Good button design is about making the most-touched part of an interface feel reliable. A button is not just a colored rectangle; it is a control that feels clickable, signals its state clearly, and works with a keyboard too. In this article we build a solid button around the triangle of state, size and contrast, and arrive at a framework you can apply both in Figma and in real CSS.
Why a button is a hard component
Because it looks small, the button is often left to the end of a design — yet it is the element users interact with most. When someone looks at a button, they want to answer three questions in seconds: Is it clickable? What state is it in right now? What will it do? If those answers are unclear, people hesitate, misclick, or do not click at all. So button design is not decoration; it is a matter of communication.
A good button has these core qualities:
- Affordance: it stands out from its surroundings and looks "pressable" through fill, shadow or border.
- State feedback: it looks different on hover, when pressed, when focused, and when disabled.
- Hierarchy: the primary action clearly stands out from secondary and tertiary ones.
- Accessibility: contrast is sufficient, and it works with keyboard and screen readers.
States: a button's hidden half
A static button is only half the job. The real challenge is how the button behaves across its states. In practice the states you need to design are:
- Default — the resting state.
- Hover — pointer over the control (pointer devices only).
- Active / Pressed — the moment of the click, usually a slight darkening or a 1px downward shift.
- Focus — a visible ring while navigating with the keyboard.
- Disabled — closed to interaction, low contrast.
- Loading — the action is in progress; a spinner or a text change.
Building these in Figma as a Component with variants makes it easy to match development one-to-one. On the CSS side, each state maps to a pseudo-class:
.btn {
--bg: #2563eb;
background: var(--bg);
color: #fff;
border: none;
border-radius: 8px;
padding: 10px 16px;
font: 600 15px/1 system-ui, sans-serif;
cursor: pointer;
transition: background .15s ease, transform .05s ease;
}
.btn:hover { --bg: #1d4ed8; }
.btn:active { transform: translateY(1px); }
.btn:focus-visible {
outline: 3px solid #93c5fd;
outline-offset: 2px;
}
.btn:disabled {
--bg: #cbd5e1;
color: #f1f5f9;
cursor: not-allowed;
}
Note: using :focus-visible gives keyboard users a clear focus mark without showing an unnecessary ring to people clicking with a mouse. Never remove the focus ring entirely; that is the single most common accessibility-breaking mistake.
Size, hit area and touch targets
No matter how large a button looks visually, what truly matters is the size of its clickable area. On mobile especially, targets must be wide enough to tap accurately with a finger. Widely accepted guidelines:
- Minimum touch target ~44×44 px (Apple) / ~48×48 dp (Google Material).
- Padding, not text height, defines the button's real hit area.
- Leave at least 8px between adjacent buttons to reduce mis-taps.
For small icon buttons, you can grow the hit area with transparent padding even when the visible box is small. Think of size as a system too: three steps such as sm, md, lg are enough for most products, scaling padding and font-size together.
Contrast and hierarchy
When several buttons share a screen, the user must instantly understand which one is the main action. You establish this through color saturation and fill:
- Primary — filled, high contrast, usually one per screen.
- Secondary — outlined or lightly filled, calmer.
- Tertiary / Ghost — text only, the lowest weight.
For accessibility, target the WCAG text contrast ratios: at least 4.5:1 for normal text and 3:1 for large/bold text. White text on a blue fill is usually safe; but avoid light grey text on a light grey fill. Do not rely on color alone: support the disabled state with an extra cue (such as a cursor change) rather than just dimming it, because colorblind users may not be able to distinguish hue alone.
Label, icon and microcopy
A button's text matters as much as its design. Good labels are action-oriented and state the outcome: "Save changes" is often clearer than "Save", and "Send message" clearer than "Send". A few practical rules:
- Start with a verb and keep it short (1–3 words).
- If you use an icon, let it reinforce the same meaning as the text, not contradict it.
- Always give an icon-only button an
aria-label.
<button class="btn" aria-label="Add to cart">
<svg aria-hidden="true" focusable="false" ...></svg>
</button>
Here aria-hidden="true" hides the icon from screen readers, while aria-label supplies the meaning as text. During loading, setting the button to disabled and changing the text to something like "Saving…" also prevents double clicks.
Building a button system
Instead of drawing buttons one by one, build a token-based system. Keep color, radius, spacing and typography values in variables, and derive variants from them. That way, when your brand changes you update from a single place and every button stays consistent. In Figma you do this with components + variants + variables; in CSS with custom properties. Consistency means users never have to relearn each button — which directly raises perceived quality.
Frequently Asked Questions
What color should the primary button be?
Your brand accent color is a sensible starting point, as long as it gives at least 4.5:1 contrast with its label. Keep a single primary button per screen; two "main actions" make the user's decision harder.
Can I remove the focus ring for the sake of the design?
No. Removing the focus ring makes the button unusable for keyboard and screen reader users. If you want it to look cleaner, customize the outline color and outline-offset, but always leave a visible mark via :focus-visible.
Should I disable the button or show an error message?
In most cases, keeping the button active and showing a message that explains what is missing when clicked is better than silently disabling it, because a disabled button never tells the user why it cannot be clicked, and they get stuck.
Do your buttons still look like they "can't be clicked"? I can help you rework your interface for state, contrast and accessibility. Get in touch and let's make your buttons both beautiful and functional.