Button
Overview
Buttons are interactive elements that trigger actions or events when clicked or interacted with.
Use Cases
When to use:
- To trigger an immediate action or event (e.g., “Save”, “Delete”, “Send”)
- To submit a form
- To open or close interactive elements (modals, dialogs, menus)
- To toggle between states (e.g., play/pause, show/hide)
- To download files or content
- To navigate between steps in a multi-step process
When not to use:
- For navigation between pages (use links instead)
- When the action isn’t immediately clear to users
- For text-only content that doesn’t trigger an action
- When the interaction would be better served by a different component (e.g., checkbox, toggle switch)
- When multiple conflicting actions are grouped together
- When the action requires additional context that isn’t immediately available
Common scenarios and examples
- Primary actions: “Submit”, “Save”, “Continue”
- Secondary actions: “Cancel”, “Back”, “Reset”
- Destructive actions: “Delete”, “Remove”, “Clear All”
- Toggle actions: “Show More”, “Expand/Collapse”, “Play/Pause”
- Process actions: “Upload”, “Download”, “Export”
- Social actions: “Share”, “Follow”, “Like”
Benefits
- Provides clear, actionable interactions for users to accomplish tasks
- Maintains consistency in user interface interactions across the application
- Offers visual feedback and states (hover, focus, active, disabled) to enhance usability
- Supports accessibility through keyboard navigation and screen reader compatibility
- Reduces cognitive load by making actions immediately recognizable
Anatomy
Component Structure
- Container
- The root button element that wraps all other components
- Handles click events and keyboard interactions
- Manages focus states and accessibility attributes
- Contains variants for different button styles (default, destructive, outline, etc.)
- Label Text
- The button label communicates the action that will occur
- Should be clear, concise, and action-oriented
- Must maintain proper contrast ratio with the background
- Icon (Optional)
- Icons can be added to clarify an action
- Should be the same color as the text
- Placed on the left side of the label by default
- Size should be proportional to text (default 16x16px)
- Should include proper ARIA labels when used alone
- Loading State (Optional)
- Visual indicator for async operations
- Replaces or overlays the button content
- Should maintain the button’s original width
- Prevents multiple clicks during loading
- Typically uses a spinner or progress indicator
- Add loading text alongside spinners (e.g., “Saving…” vs just a spinner)
- Visual States
- Default: Normal state
- Hover: Visual feedback on mouse over
- Focus: Keyboard navigation indicator
- Active: Pressed state
- Disabled: Indicates non-interactive state
- Loading: Shows processing state
Best Practices
Content
Do’s ✅
- Use action verbs that describe what the button does (eg: “Save” instead of “Submit”)
- Keep button labels concise and clear
- Be consistent with button labeling across the application
- Include loading text when appropriate (“Saving…” vs “Save”)
- Use sentence case for button labels (e.g., “Save Changes”)
Don’ts ❌
- Don’t use vague labels like “Click Here” or “Submit”
- Don’t use inconsistent terminology
- Don’t write overly long button labels
- Don’t use technical jargon in button labels
- Don’t mix different cases in button labels
Accessibility & UX
Do’s ✅
- Use descriptive labels that clearly communicate the action 1
- Provide visual feedback for all interactive states
- Ensure keyboard navigation works properly
- Include loading states for asynchronous actions
- Don’t pass the
disabled
attribute to buttons, instead usearia-disabled
. You would risk disrupting the keyboard navigation flow as this would reset focus on the button. - Use descriptive loading states with both text and visual indicators
Don’ts ❌
- Don’t rely solely on color to communicate button states
- Don’t disable browser focus indicators without providing alternatives
- Don’t use button elements for navigation (use links instead)
- Don’t auto-focus destructive actions
- Don’t remove focus styles
Visual Design
Do’s ✅
- Use the same button style for the same action throughout the application
- Make buttons finger-friendly for mobile users (minimum 44x44px touch target)
- Use appropriate visual hierarchy (primary, secondary, tertiary buttons)
- Maintain consistent spacing between button groups
- Use appropriate color contrast ratios (WCAG 2.1 AA compliance)
- Include hover and focus states for interactive feedback
- Keep icon and text alignment consistent
- Maintain consistent padding and height across similar buttons
- Scale button size appropriately for different viewport sizes
Don’ts ❌
- Don’t have more than one primary action button on the screen at a time
- Don’t use a dark pattern when you don’t want users to take a certain action
- Don’t mix different button styles for the same action type
- Don’t use colors that conflict with your application’s color scheme
- Don’t make disabled buttons look interactive
- Don’t use inconsistent corner radius within button groups
- Avoid having buttons looking like links or vice versa
- Don’t use spinners without descriptive text
Mobile & Touch Considerations
Do’s ✅
- Place primary actions within thumb-friendly zones
- Use full-width buttons for important actions on mobile
- Implement haptic feedback for touch interactions
- Maintain sufficient spacing between touch targets (minimum 8px)
Don’ts ❌
- Don’t place critical actions in hard-to-reach corners
- Don’t rely solely on hover states for mobile feedback
- Don’t make touch targets smaller than 44x44px
- Don’t crowd multiple buttons together on mobile views
Layout & Positioning
Do’s ✅
- Place primary action buttons in prominent, easily accessible locations
- Align buttons consistently within forms and dialogs
- Group related buttons together
- Consider mobile touch targets (minimum 44x44px)
Don’ts ❌
- Don’t position buttons where they might be accidentally clicked
- Don’t hide important actions behind dropdown menus
- Don’t place buttons in unexpected locations (e.g., footer)
Code Examples
Basic Implementation
<!-- Small Button -->
<button type="button" class="button-sm">Small</button>
<!-- Default Size Button -->
<button type="button">Default</button>
<!-- Large Button -->
<button type="button" class="button-lg">Large</button>
<!-- Icon-only Button -->
<button type="button" aria-label="Settings">
<svg aria-hidden="true">
<use href="#icon-settings" />
</svg>
</button>
Form Submit Button
<form>
<!-- Form fields here -->
<button type="submit" class="button button-primary" data-loading="false">
<span class="button-text">Submit Form</span>
<svg class="icon spinner hidden" aria-hidden="true">
<use href="#icon-loading" />
</svg>
</button>
</form>
Accessibility
ARIA Attributes
Required ARIA attributes:
- Use
aria-label
for icon-only buttons - Use
aria-pressed
for toggle buttons - Use
aria-expanded
for buttons that control expandable content - Use
aria-disabled="true"
instead of thedisabled
attribute - Use
aria-describedby
to associate additional descriptive text
Screen Reader Support
Implementation example:
<!-- Icon-only button with proper ARIA -->
<button type="button" aria-label="Close dialog">
<svg aria-hidden="true">
<use href="#icon-close" />
</svg>
</button>
<!-- Toggle button with ARIA pressed state -->
<button type="button" aria-pressed="false"">
<span>Dark mode</span>
</button>
<!-- Expandable content button -->
<button type="button" aria-expanded="false" aria-controls="content-1">
<span>Show more</span>
</button>
Keyboard Navigation
- Buttons must be focusable and activated with both Enter and Space keys
- Focus order should follow a logical sequence
- Focus states must be clearly visible
- Avoid removing focus outlines without providing alternatives
- Maintain focus after interactions (e.g., after closing a modal)
Testing Guidelines
Functional Testing
Should ✓
- Show a loader when the button is submitting a form
- Disable the button during form submission to prevent double submissions
- Handle click events and trigger the appropriate action
- Maintain proper visual states (hover, focus, active, disabled)
- Support keyboard interaction (Enter and Space keys)
- Preserve button width when switching between normal and loading states
- Reset to initial state after operation completion
Accessibility Testing
Should ✓
- Be focusable and have visible focus indicators
- Have proper ARIA labels, especially for icon-only buttons
- Maintain sufficient color contrast ratios (WCAG 2.1 AA)
- Support screen reader announcements of button state changes
- Be operable with keyboard navigation
- Communicate loading states to assistive technologies
- Have appropriate touch target sizes (minimum 44x44px)
Visual Testing
Should ✓
- Maintain consistent styling across different variants (primary, secondary, destructive)
- Display icons with correct alignment and spacing
- Show proper visual feedback for all interactive states
- Render correctly across different viewport sizes
- Handle text overflow appropriately
- Display loading spinners centered within the button
- Maintain proper padding and margins in all states
Performance Testing
Should ✓
- Render without layout shifts
- Handle rapid click events appropriately
- Maintain smooth transitions between states
- Load icons and spinners efficiently
- Function without JavaScript (progressive enhancement)
Testing Tools
- Accessibility: WAVE, aXe, or Lighthouse
- SEO: Google’s Rich Results Test
- Performance: Chrome DevTools Performance panel
- Visual Regression: Percy or Chromatic
- Cross-browser: BrowserStack or Sauce Labs
Browser Support
Design Tokens
These design tokens follow the Design Tokens Format specification and can be used with various token transformation tools to generate platform-specific variables.
Button Tokens in DTF Format
{
"$schema": "https://design-tokens.org/schema.json",
"button": {
"sizing": {
"height": {
"sm": { "value": "2.25rem", "type": "dimension" },
"md": { "value": "2.5rem", "type": "dimension" },
"lg": { "value": "2.75rem", "type": "dimension" }
},
"paddingX": {
"sm": { "value": "0.75rem", "type": "dimension" },
"md": { "value": "1rem", "type": "dimension" },
"lg": { "value": "2rem", "type": "dimension" }
},
"iconSize": { "value": "1rem", "type": "dimension" },
"iconGap": { "value": "0.5rem", "type": "dimension" },
"touchTarget": {
"value": "2.75rem",
"type": "dimension",
"description": "44px minimum touch target"
}
},
"typography": {
"fontFamily": { "value": "{font.family.sans}", "type": "fontFamily" },
"fontWeight": { "value": "600", "type": "fontWeight" },
"fontSize": {
"sm": { "value": "0.875rem", "type": "dimension" },
"md": { "value": "1rem", "type": "dimension" },
"lg": { "value": "1.125rem", "type": "dimension" }
}
},
"border": {
"radius": {
"sm": { "value": "0.375rem", "type": "dimension" },
"md": { "value": "0.5rem", "type": "dimension" },
"lg": { "value": "0.75rem", "type": "dimension" },
"pill": { "value": "999px", "type": "dimension" }
},
"width": { "value": "1px", "type": "dimension" }
},
"variants": {
"primary": {
"background": {
"default": { "value": "{color.primary.600}", "type": "color" },
"hover": { "value": "{color.primary.700}", "type": "color" },
"active": { "value": "{color.primary.800}", "type": "color" },
"disabled": { "value": "{color.primary.200}", "type": "color" }
},
"text": {
"default": { "value": "{color.white}", "type": "color" },
"disabled": { "value": "{color.primary.100}", "type": "color" }
}
},
"secondary": {
"background": {
"default": { "value": "{color.gray.100}", "type": "color" },
"hover": { "value": "{color.gray.200}", "type": "color" },
"active": { "value": "{color.gray.300}", "type": "color" },
"disabled": { "value": "{color.gray.50}", "type": "color" }
},
"text": {
"default": { "value": "{color.gray.900}", "type": "color" },
"disabled": { "value": "{color.gray.400}", "type": "color" }
}
},
"destructive": {
"background": {
"default": { "value": "{color.red.600}", "type": "color" },
"hover": { "value": "{color.red.700}", "type": "color" },
"active": { "value": "{color.red.800}", "type": "color" },
"disabled": { "value": "{color.red.200}", "type": "color" }
},
"text": {
"default": { "value": "{color.white}", "type": "color" },
"disabled": { "value": "{color.red.100}", "type": "color" }
}
},
"outline": {
"border": {
"default": { "value": "{color.gray.300}", "type": "color" },
"hover": { "value": "{color.gray.400}", "type": "color" },
"active": { "value": "{color.gray.500}", "type": "color" },
"disabled": { "value": "{color.gray.200}", "type": "color" }
},
"text": {
"default": { "value": "{color.gray.900}", "type": "color" },
"disabled": { "value": "{color.gray.400}", "type": "color" }
}
}
},
"states": {
"focusRing": {
"width": { "value": "2px", "type": "dimension" },
"offset": { "value": "2px", "type": "dimension" },
"color": { "value": "{color.primary.200}", "type": "color" }
},
"loadingSpinner": {
"size": { "value": "1rem", "type": "dimension" },
"borderWidth": { "value": "2px", "type": "dimension" }
}
}
}
}
Resources
Articles
-
I’ve been doing buttons wrong! Have you? by Adham Dannaway
-
A comprehensive guide to buttons by UX Planet
-
A comprehensive guide to designing UX buttons by InVision
-
Building Accessible Buttons with ARIA: A11y Support Series by Deque
Design systems
Footnotes
Footnotes
Got a pattern request?
Let us know, and we'll add it!
Send SuggestionLast updated on