On this page
CSS Buttons: Variants, States, Sizes and Accessible Button Design
What is a CSS button?
A CSS button is a clickable control styled with CSS so users can clearly see what it does and whether it is available. Buttons trigger actions such as submit, save, open a menu or delete. Good button design is one of the most important parts of a UI component library, because buttons appear on almost every page.
Focus keyword: CSS buttons. Related trending keywords: CSS button hover effects, accessible buttons, button focus-visible, color-mix() hover color, design tokens, CSS custom properties components, loading button CSS, icon button.
Use the right element: button vs link
<button>: performs an action on the current page (submit, toggle, open dialog). It is focusable and works with Enter and Space by default.<a href>: navigates somewhere. Screen readers announce it as a link. You can style a link to look like a button, but keep the link semantics.- Never build a button from a
<div>or<span>. You would have to recreate keyboard support, focus and roles yourself. - Inside a form, set
type='button'for non-submit buttons, otherwise they submit the form by default.
Reset the browser defaults first
Buttons do not inherit fonts by default. A solid base rule is: font: inherit; cursor: pointer; border: 0; background: none; and then add your own padding, radius and colors. Add line-height and text-decoration: none when styling links as buttons.
Button variants
- Primary: the main action on a page. Use only one per view.
- Secondary: a less important action, often a lighter fill.
- Outline: border only. Good for secondary choices.
- Ghost or text: no border or fill until hover. Good for toolbars.
- Danger: destructive actions like delete. Use red and a confirmation step.
Drive variants with CSS custom properties, for example --btn-bg and --btn-fg, so one base class supports many variants.
Sizes and touch targets
Offer small, medium and large sizes using padding and font size. For touch, aim for a target of at least 44 by 44 CSS pixels (WCAG 2.5.5 Level AAA) and never below 24 by 24 CSS pixels (WCAG 2.5.8 Level AA in WCAG 2.2). Use min-height and min-width instead of fixed sizes.
States every button needs
- Default, hover (
:hover), active (:active, pressed), focus (:focus-visible), disabled (:disabled) and loading. - Use
:focus-visibleto show a clear outline for keyboard users without showing it on every mouse click. Never remove the outline without a replacement that has enough contrast. - The native
disabledattribute removes the button from tab order. If users need to discover why it is disabled, usearia-disabled='true'and block the action in JavaScript instead. - For loading, set
aria-busy='true', keep the label, show a spinner and prevent double submits.
Modern CSS helpers
color-mix(in srgb, var(--btn-bg) 85%, black)creates hover and active shades from a single base color.:where()keeps specificity low so variants are easy to override.transitiononbackground-color,transformandbox-shadowgives smooth feedback. Respectprefers-reduced-motion.:has()lets a button adapt when it contains an icon only, for examplebutton:has(svg):not(:has(span)).
Icon buttons
A button with only an icon needs an accessible name, such as aria-label='Close' or visually hidden text. Mark decorative SVG icons with aria-hidden='true' and focusable='false'.
Button groups
Group related buttons with flex and a small gap. For a toggle button, use aria-pressed='true|false' and update it in JavaScript. For a group with one selected option, radio inputs styled as buttons are often better.
Best practices
- Use action-oriented text: Save changes, Add to cart. Avoid Click here.
- Keep color contrast at least 4.5:1 for text and 3:1 for the focus indicator and boundaries.
- Do not rely on color alone to show state.
- Keep consistent sizes, radius and spacing across the product.
Quick summary
Use semantic elements, a small set of variants built on custom properties, clear states, visible focus and accessible names. That makes CSS buttons reusable and inclusive.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Create a .btn base class that uses --bg and --fg custom properties. Add .success and .warning variants by only changing those properties. Add a hover color with color-mix(), a clear :focus-visible outline and a disabled style. The minimum height must be 44px.
Press Run to execute.
Show expected output
Three colored buttons with darker hover shades, a visible outline on keyboard focus, and a gray disabled button. All at least 44px tall.This is a self-check — compare your result with the expected output above.
Was this page helpful?