learn.colinkim.dev

Accessible focus, contrast, and motion preferences

Learn keyboard-visible focus, contrast, reduced motion, forced colors, hover and pointer media features, and color-independent cues.

Accessible CSS makes interface state perceivable. It does not replace semantic HTML, but it can preserve or destroy usability.

Focus must be visible

Every interactive element should have a visible focus state:

:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}

For custom colors:

.button:focus-visible {
  outline: 3px solid color-mix(in oklab, royalblue, white 35%);
  outline-offset: 3px;
}

Never remove outlines globally:

*:focus {
  outline: none;
}

That makes keyboard navigation hard or impossible to track.

Contrast

Text needs enough contrast against its background:

.button {
  background: rgb(20 90 190);
  color: white;
}

Check real combinations: normal text, large text, disabled text, placeholder text, borders, icons, and focus rings.

Do not rely on color alone

Bad:

.error {
  color: red;
}

Better:

.error {
  color: rgb(160 30 30);
  border-inline-start: 4px solid currentColor;
  padding-inline-start: 0.75rem;
}

The border communicates state even if color perception differs.

Reduced motion

Some users prefer less motion:

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    scroll-behavior: auto;
    transition-duration: 0.01ms;
  }
}

For more careful designs, replace motion with a non-motion cue:

@media (prefers-reduced-motion: reduce) {
  .toast {
    transform: none;
  }
}

Forced colors

Some users use high-contrast or forced-color modes. Let system colors work:

@media (forced-colors: active) {
  .button {
    border: 1px solid ButtonText;
  }
}

Avoid using background images or box shadows as the only state indicators. They may disappear or change in forced-color modes.

Pointer and hover capability

Do not assume every device has hover:

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-2px);
  }
}

Touch users still need visible controls and states without hover.

What to carry forward

  • visible focus is required for keyboard use
  • contrast must be checked for every meaningful state
  • color should not be the only cue
  • prefers-reduced-motion should reduce or replace motion
  • forced colors may change backgrounds, shadows, and colors
  • hover styles should be enhancements, not required instructions

The next lesson returns to the cascade with architecture tools: layers, resets, components, and overrides.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.