Buttons

Accessible Focus Ring

A keyboard-only focus ring using :focus-visible, so mouse clicks stay clean.

Edit this in the studio

Opens with this animation loaded. Nothing is uploaded — the scene travels in the link.

animation.css
.button-focus {
  transform: translate3d(165px, 137px, 0);
  width: 150px;
  height: 46px;
  background: #1c1e2a;
  border-radius: 10px;
  color: #e7e9ee;
  font-size: 15px;
  font-weight: 600;
  transition: background 140ms ease-out, box-shadow 140ms ease-out, opacity 140ms ease-out;
}

.button-focus:hover {
  background: #262a3a;
}

.button-focus:focus-visible {
  box-shadow: 0px 0px 0px 3px #22d3eecc;
}

.button-focus:disabled {
  opacity: 0.4;
}

@media (prefers-reduced-motion: reduce) {
  .button-focus {
    animation: none;
    transition: none;
  }
}

Why it is built this way

Uses :focus-visible rather than :focus, so the ring appears for keyboard users and not on mouse click — which is the reason people used to remove focus outlines entirely and break keyboard navigation. The ring is a box-shadow spread rather than an outline so it follows the border radius.

What it animates

No keyframes at all — this one is built from interaction states, so the browser only does anything when the visitor does.

What it costs to run

Nothing here runs on a timer. The motion lives entirely in :hover, :focus-visible and :disabled, which compiles to a transition — so it costs nothing until the visitor does something, and it interrupts and reverses cleanly halfway through.

Accessibility

A reader who never triggers :hover, :focus-visible and :disabled never sees this, so it carries no risk on load. Because it answers to :focus-visible as well, a keyboard user gets the same feedback a mouse user does — the part hand-written hover CSS usually forgets.

Related animations

focusaccessibilityfocus-visiblea11ybutton