CSS gradient button generator

Gradient buttons with a hover that actually animates, a visible focus ring, a pressed state and an automatic check that the label is readable.

Hover, tab to and press the button to see every state.

#b3005f
#e0470b

Label colour #ffffff — worst contrast 4.14:1 (large/bold text only)

.btn-gradient {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 14px 28px;
  border: 0;
  border-radius: 999px;
  color: #ffffff;
  font-weight: 700;
  background-color: #b3005f;
  background-image: linear-gradient(90deg, #b3005f 0%, #e0470b 50%, #e0470b 50%, #b3005f 100%);
  background-size: 200% 100%;
  cursor: pointer;
  transition: background-position .4s ease, box-shadow .2s ease, transform .15s ease;
}
.btn-gradient:hover {
  background-position: 100% 0;
}
.btn-gradient:active { transform: translateY(1px); }
.btn-gradient:focus-visible {
  outline: 3px solid #b3005f;
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .btn-gradient { transition: none; }
}

Questions

How do I make a gradient button in CSS?

Put the gradient in background-image and keep a solid background-color as fallback. For a smooth hover, make the background 200% wide and shift background-position on :hover — gradients themselves can’t be transitioned directly without @property.

Why doesn’t my gradient transition on hover?

Browsers can’t interpolate between two background-image values, so the change is instant. Either animate background-position on an oversized gradient (what this generator does), fade an overlay pseudo-element, or register custom properties with @property and transition those.

What text colour should a gradient button use?

Whichever passes 4.5:1 against every part of the gradient. The generator checks white and near-black text against the whole gradient and tells you which works.

Do I need a focus style?

Yes. WCAG 2.2 requires a visible focus indicator. Use :focus-visible with a 2–3px outline offset from the button so it shows over any gradient.