Skip to content
ssntl/ui

Accessibility

Interfaces that work for the people using them. Every ssntl/ui component is accessible by default, with nothing to configure.

Respects reduced motion

  • Every animation honours prefers-reduced-motion: movement, scale and blur turn off and the change happens at once. A fade that carries no movement may stay.

  • The result still happens

    A sheet still opens, an accordion still expands, a toast still arrives.

  • Gestures that spring back, like dragging a Sheet or swiping a Toast, settle at once instead.

Nothing depends on motion alone

  • State lives in the DOM and in words: aria-expanded, aria-pressed, aria-checked, aria-invalid, aria-busy and visible text.

  • A shake comes with an error message, a shimmer with “Loading” for screen readers, and a switch fills its track, so on and off read without the motion.

Your markup keeps its semantics

  • Components render real elements

    Buttons stay <button>, headings stay headings, fields keep their <label>.

  • Decorative layers (icons, halos, shimmer beams, background patterns) are aria-hidden or drawn in pseudo-elements, so assistive technology skips them.

  • Radix primitives bring the roles, ARIA state and focus management for menus, dialogs, tabs, accordions, sliders and switches.

Keyboard and focus

  • Everything works from the keyboard, with the keys people expect: arrows in menus, tabs and sliders; Escape closes overlays; focus returns to what opened them.

  • Focus is always visible

    A 2px ring offset from the control, never removed.

  • Hit areas are 48px or more, even when the control looks smaller, like the 20px slider thumb. Dense sets, like carousel dots, are 48px tall and at least 32px wide, above WCAG's 24px.

Readable in both themes

  • Text meets 4.5:1 and controls 3:1 in light and dark. Every pair is measured in lint, so a change that breaks contrast fails the build.

Smooth by design

  • Animations stick to properties the compositor handles: translate, scale, rotate, opacity and filter.

  • Three exceptions, each because a real size has to change: Accordion and Collapsible animate height (220ms), the Carousel's current dot grows in width so its round ends stay round, and the Pill accordion grows a pill into a card (400ms). All three are off with reduced motion.

  • will-change only where a first frame stutters; looping effects like the shimmer animate translate alone.