cssgridgen

CSS Button Generator

Every state, contrast-checked. CSS or Tailwind.

4.49:1 passes AA for large text only — below 4.5:1 for normal button labels.

All five states
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  color: #ffffff;
  background: #d8401f;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s, transform 0.1s, box-shadow 0.15s, filter 0.15s;
}

.btn:hover:not(:disabled) {
  filter: brightness(0.92);
}

.btn:active:not(:disabled) {
  transform: translateY(1px);
  filter: brightness(0.88);
}

.btn:focus-visible {
  outline: 2px solid #d8401f;
  outline-offset: 2px;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}
Free tool

The four states most button CSS forgets

Most button snippets give you a default state and leave you to invent the rest. A button is five states, and the ones that get skipped — focus and disabled — are exactly the ones that decide whether the control is usable by keyboard or legible when inactive. All five are written here whether you look at them or not.

How to use it

  1. Pick a variant

    Solid, Outline, Ghost, Dark, Pill or Block. Each is a complete starting point with its own sensible hover behaviour.

  2. Shape it

    Padding, font size, weight, corner radius and border width. The preview updates with the exact values the CSS will contain.

  3. Check every state

    Switch between default, hover, active, focus and disabled in the preview. All five are written into the output whether you look at them or not.

  4. Watch the contrast reading

    The label contrast ratio is shown live and flags anything below WCAG AA before you ship it.

  5. Copy the code

    Full CSS with :hover, :active, :focus-visible and :disabled rules, or the equivalent Tailwind class string.

Disabled is an attribute, not a class

Style :disabled rather than .is-disabled. The pseudo-class follows the actual HTML attribute, which means the styling can never drift out of sync with whether the button really is inert. And remember to guard your hover rule with :not(:disabled), or a dead button will still respond to the cursor.

Never delete the focus ring

outline: none with nothing to replace it is the single most common accessibility failure in button CSS. The reason people reach for it is that plain :focus shows the ring on mouse clicks too, which looks wrong. :focus-visible solves that properly: the ring appears for keyboard users and stays out of the way for everyone else.

Contrast, checked as you go

A button label needs 4.5:1 against its background to pass WCAG AA at normal sizes. That is easy to fail with a mid-tone brand colour and white text, and almost impossible to judge by eye. The ratio above the preview updates with every change and tells you which side of the line you are on.

Laying buttons out

A row of buttons is a flexbox problem, not a grid one — use the flexbox generator for the container and let each button size itself.

Frequently asked questions

How do I style a disabled button in CSS?
Target the :disabled pseudo-class: .btn:disabled { opacity: 0.5; cursor: not-allowed; }. Use the pseudo-class rather than a class name so the styling can never drift out of sync with whether the button really is inert. Also guard your hover rule with :not(:disabled).
Why does cursor: not-allowed not work on a disabled button?
Because pointer-events: none is also set, which stops the element receiving any pointer interaction including the cursor change. Remove it — the disabled attribute already blocks clicks on its own.
How do I make an outline button in CSS?
Transparent background, a coloured border, and the same colour for the label. On hover, fill the button with the border colour and flip the label to white — darkening a transparent background does nothing visible, which is why so many outline buttons feel dead.
Should I use :focus or :focus-visible?
Use :focus-visible. Plain :focus also shows the ring when you click with a mouse, which is why people delete outlines entirely and break keyboard navigation. :focus-visible shows the indicator only when the browser judges one is needed.
How do I make a close button in CSS?
A square button with equal padding, no border, a transparent background and a × character or icon. The important part is the accessible name: the glyph means nothing to a screen reader, so add aria-label="Close".
What contrast ratio does a button need?
4.5:1 between the label and the button background for normal text, or 3:1 if the label is 18px bold or larger. The button edge against the page needs 3:1 so the control is identifiable. The reading above the preview updates as you change colours.