Home / Snippets / UI Components /
Primary button
Every state covered: hover, focus-visible, active, disabled.
Quick implementation
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.65rem 1.5rem;
font: 600 0.9rem/1.2 system-ui, sans-serif;
color: white;
background: oklch(0.52 0.22 265);
border: none;
border-radius: 0.5rem;
cursor: pointer;
transition:
background 0.2s ease-out,
box-shadow 0.2s ease-out,
transform 0.1s ease-out;
box-shadow: 0 1px 3px oklch(0 0 0 / 0.12);
}
.btn-primary:hover {
background: oklch(0.48 0.22 265);
box-shadow: 0 2px 8px oklch(0 0 0 / 0.18);
}
.btn-primary:active {
transform: scale(0.97);
}
.btn-primary:focus-visible {
outline: 2px solid oklch(0.52 0.22 265);
outline-offset: 3px;
}
.btn-primary:disabled {
opacity: 0.45;
cursor: not-allowed;
pointer-events: none;
}
Prompt this to your LLM
Includes role, constraints, state coverage, and variants.
You are a senior frontend engineer building a button component.
Goal: A production-ready primary button covering all interactive states.
Technical constraints:
- Use oklch() for background color — oklch(0.52 0.22 265) as base.
- Hover: darken by reducing lightness (oklch(0.48 ...)).
- Active: scale(0.97) for tactile press feedback.
- Focus-visible: 2px solid outline with 3px offset — NOT visible on mouse click.
- Disabled: opacity 0.45, cursor: not-allowed, pointer-events: none.
- Use display: inline-flex + align-items/justify-content for icon alignment.
- Transition: background 0.2s ease-out, box-shadow 0.2s ease-out.
- Layered box-shadow for subtle depth.
Variants to include:
A) Primary (filled).
B) Ghost (transparent background, border, text-colored).
C) Small and large size variants.
Return CSS. No JavaScript.
Why this matters
Buttons are the most interacted-with element on any page, yet most are missing states. A production button needs five states: default, hover, active (pressed), focus-visible (keyboard), and disabled. Skipping any of these creates a broken experience for some users. This snippet covers them all in ~30 lines.
The logic
The oklch() color space makes state variations trivial — just reduce lightness for hover. scale(0.97) on :active gives tactile feedback without layout shift. :focus-visible shows the outline only for keyboard users, not mouse clicks. pointer-events: none on disabled prevents all interaction including tooltips from parent wrappers.
Accessibility & performance
The white-on-accent contrast ratio should be at least 4.5:1 for WCAG AA. oklch(0.52 0.22 265) on white text passes. The :focus-visible outline must be visible — use a contrasting color with offset. Never remove the outline without replacing it. Transitions use compositor-friendly properties for smooth 60fps interaction.