Skip to content

Accessibility

prfct targets WCAG 2.2 AA by construction — in its palette, its primitives and its defaults — and is verified by automated tests on every page.

Accessibility in prfct isn't a checklist at the end. It's built into the layers everything else sits on: a palette whose contrast is solved mathematically, primitives from Base UI that implement WAI-ARIA patterns, and component defaults that make the accessible choice the easy one.

What you get for free

AreaHow prfct handles it
ContrastText steps are solved for ≥ 4.5:1 on their surfaces; control boundaries meet 3:1 (WCAG 1.4.11). See Color.
KeyboardEvery interactive component is operable with the keyboard, with roving focus, typeahead and Esc where the ARIA pattern expects it.
FocusA visible 2px focus indicator that meets 3:1 against every surface, drawn with outline so it survives forced-colors mode.
SemanticsBase UI renders native elements and correct roles, states and relationships (aria-expanded, aria-controls, aria-describedby…).
OverlaysDialogs trap and return focus, make the background inert, and require a title.
Target sizeControls from sm up are at least 24×24px (WCAG 2.2, 2.5.8); small checkboxes extend their hit area invisibly.
Motionprefers-reduced-motion collapses all transitions; nothing depends on animation.
Contrast preferencesprefers-contrast: more strengthens borders and secondary text.
Zoom & text sizeType is set in rem; layouts reflow at 400% zoom without horizontal scrolling.
LanguageInter covers Latin Extended-A, so Polish, Czech, Turkish and other languages render with real glyphs.

Focus

Click here, then press Tab to move through the controls.

prfct uses two focus styles:

  • Discrete controls — buttons, checkboxes, switches, tabs — show a 2px ring offset 2px from the edge.
  • Text fields show a brand border and a 3px halo, because a ring outside a bordered field reads as a second border.

Both follow :focus-visible: controls show their ring only when focus comes from the keyboard, so mouse users don't see rings on click. Text fields show their halo whenever they're focused, however focus arrived — a focused field is where your typing goes, so it should always say so.

Your responsibilities

Components can't know everything about your content. These are yours:

  • Label everything. Every input needs a visible <Label> or FieldLabel; icon-only buttons need aria-label.
  • Write meaningful text. Link and button labels must make sense out of context: Download invoice, not Click here.
  • Describe errors in words. Pair aria-invalid with a FieldError that says what went wrong and how to fix it.
  • Keep the heading outline. One h1 per page, and no skipped levels.
  • Give images alternatives. Meaningful images need alt; decorative ones need alt="".
  • Don't use color alone. Status needs an icon or text, too.
  • Announce async results. Use a toast or a live region for results that appear without a page change.

Testing

Test your product the way prfct's own site is tested:

  1. Automated — scan every page with axe-core in both modes. prfct's test suite does this for every page of this site, in light and dark mode (pnpm test:a11y), and tabs through its most interactive pages checking that every stop shows a focus indicator.
  2. Keyboard — walk every flow with the keyboard only: focus order, visible focus, no traps, Esc closes overlays.
  3. Screen readers — check key flows with VoiceOver (macOS, iOS) and NVDA or JAWS (Windows).
  4. Zoom and preferences — 200% and 400% zoom, reduced motion, increased contrast and forced colors.
Automated tools catch a fraction of issues
axe and Lighthouse are a floor, not a finish line. Missing labels and low contrast are easy to detect; confusing focus order, unclear errors and unannounced updates are not. Always test with a keyboard and a screen reader.