CSS Button Generator

Set padding, color, edges, and motion, then click through rest, hover, focus, and disabled to see what each one looks like. The generated CSS carries all four states, not only the one you styled.

Text on background8.59:1

Passes AA and AAA at this size.

Label and selector

Box and type

Color

Edges

Motion

Starting points

buttons.css
markup

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.

RatioNormal textLarge text
Under 3:1FailsFails
3:1 to 4.49:1FailsPasses AA
4.5:1 to 6.99:1Passes AAPasses AA and AAA
7:1 and abovePasses AA and AAAPasses 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.

Button styling questions

Why does the CSS include a :disabled rule I never set up?

Any button wired to a form or an async action ends up disabled at some point. Without a rule it stays looking clickable while doing nothing, which reads as a broken page. The generated block dims the button and switches the cursor, and you are free to replace those two declarations with your own.

The hover effect does nothing on my phone. Is that a bug?

No. Touch screens have no hover state, so the browser either skips it or leaves it stuck after a tap. Treat hover as an extra for pointer users and keep the rest state legible on its own. The active press state is what carries feedback on touch.

Can I paste the CSS onto an anchor tag instead of a button?

Yes, and the class works the same way on both. Add text-decoration: none for anchors, since links carry an underline the button element does not. Use a link when it navigates somewhere and a button when it triggers an action, because that choice affects screen readers and middle-click.

Why did my contrast reading change when I only touched the font size?

WCAG applies a looser threshold to large text, defined as 24px at any weight or 18.66px at bold. Crossing that line moves the requirement from 4.5:1 down to 3:1, so the same two colors flip from failing to passing.

Does the swap hover effect work with the outline preset?

It does, and that pairing is the classic outline button: transparent fill at rest, filled on hover. Pick the outline preset first, then set hover behaviour to swap. The border color stays put so the button keeps its footprint instead of shifting the layout.

Is the generated CSS safe next to Bootstrap or Tailwind?

Rename the class to something your framework does not claim. btn-primary collides with Bootstrap directly, and whichever stylesheet loads last wins. Any name outside the framework prefix avoids the fight.