Most button generators style one state and hand back one block of CSS. A button on a real page has at least four, and three of them decide whether the control works for people who never touch a mouse. That is the reason the state row sits above the preview instead of at the end of the output.
The four states, and what each one is for
- Rest
- The button sitting there doing nothing. Its job is to look clickable without shouting over the rest of the page.
- Hover
- Pointer confirmation. It fires on mouse and trackpad only, so it is the least important of the four despite getting the most attention.
- Focus
- Keyboard position. Tab lands here. Without a visible ring, anyone navigating by keyboard loses their place entirely.
- Disabled
- Not available yet. Usually dimmed with a changed cursor, and it should still read clearly enough that people know the control exists.
Click through the four tabs above and the preview locks to that state, so you see the focus ring and the disabled treatment without tabbing into the page or spinning up a test file.
Why the focus rule uses :focus-visible
The output includes a rule you did not configure:
.btn-primary:focus-visible {outline: 3px solid #2563eb;outline-offset: 2px;}Browsers draw a default outline on focus. Designers remove it with outline: none because it appears after a mouse click too, which looks like a bug. Deleting it breaks keyboard navigation across the whole site.
:focus-visible settles the argument. The browser decides: click with a mouse and no ring appears, arrive with Tab and the ring shows. Support has been in every current browser since 2022, so there is no fallback branch to write. The generator adds the rule with the ring color derived from your background, and lightens it automatically when the background is too pale for the ring to register.
One thing the generator will not do is delete that rule for you. If your design system already handles focus globally, drop those three lines when you paste. Removing them and replacing them with nothing is the part worth avoiding.
Reading the contrast number
The figure under the preview is the WCAG contrast ratio between your text color and your background color, from 1:1 (identical) to 21:1 (black on white). Buttons fail this check constantly, usually white text on a mid-tone brand color that looked fine on the designer's monitor.
| Ratio | Normal text | Large text |
|---|---|---|
| Under 3:1 | Fails | Fails |
| 3:1 to 4.49:1 | Fails | Passes AA |
| 4.5:1 to 6.99:1 | Passes AA | Passes AA and AAA |
| 7:1 and above | Passes AA and AAA | Passes AA and AAA |
Large text means 24px at any weight, or 18.66px at bold. The readout applies that rule to whatever size and weight you set, so raising the font weight to 700 sometimes flips a failing label to passing without touching the colors.
Two limits worth knowing. The check reads your two flat colors, so a semi-transparent background or a gradient behind the text gives a number that does not reflect what renders. And the outline and quiet presets put dark text on white, which passes easily while the button edge against the page stays a separate question the ratio never covers.
Where this generator stops
It writes a single class with four states. It does not produce size variants, icon spacing, loading spinners, gradient fills, or a dark mode pair. For gradient backgrounds, build the value in the CSS gradient generator and paste it over the background-color line. For a layered drop shadow past the single hover shadow here, the box shadow generator handles multiple shadows on one element.
Padding is set in pixels because that is what people check against a design file. If your project scales type with rem, divide by your root font size after pasting, or run the numbers through the px to rem converter.
Style the rest state, check the other three, then paste all four.
