designmotionhq

interaction

Disabled Buttons

The button is disabled, and nobody tells you why.

Watch on Instagram

Key insights

  • A disabled button drops out of the tab order, so keyboard users skip right past it and screen readers stay silent. The block exists, but nothing announces it.
  • Pointer events are dead on a disabled element, so a tooltip meant to explain the block never fires. The reason is unreachable by design.
  • Greyed-out labels usually fail contrast. A disabled state can land near 1.9:1, well under the 4.5:1 threshold, so the text is hard to read on top of being blocked.
  • Keep the button live and validate on click instead. Light up the fields that are blocking submit, then move focus to the first one so the path forward is visible.
  • Disabled and loading are different states. During a request, hold focus, show a spinner, and report aria-busy; greying the button out throws the user's place away.

Do / Don't

  • Do: Keep the button enabled, validate on click, then flag the blocking fields and move focus to the first one.
  • Do: For async actions, use a busy state that holds focus, spins, and sets aria-busy.
  • Do: Name the blocker in reachable text, not a tooltip attached to a dead control.
  • Don't: Disable submit and leave the user to guess what is missing.
  • Don't: Rely on a tooltip to explain a disabled control, since pointer events never fire on it.
  • Don't: Treat loading as disabled; greying out mid-request drops focus and the user's place.

Want Claude to build this correctly?

UX Engine gives Claude Code the senior-designer reasoning behind every pattern.

The Design System Blueprint

An 11-page visual guide to consistent, scalable UI: colors, typography, spacing, components, motion.

Get the Blueprint — free

The PDF, plus emails about new patterns and my tools. Unsubscribe anytime.

Related patterns