Navigated to /docs/ui/accessibility

Accessibility

Combine component-provided keyboard and ARIA behavior with application-owned labels, semantics, state, and testing.

Know what the component can guarantee

Tavo.js UI owns reusable mechanics such as dialog focus trapping, escape handling, tab keyboard navigation, menu movement, field message wiring, and status semantics. The application still owns meaningful labels, heading order, controlled open state, validation messages, and task completion flow.

  • Give icon-only controls an accessible name.

  • Associate every field with a visible label or an intentional accessible label.

  • Keep focus visible and restore it after overlays close.

  • Use semantic Text and Box roots through as instead of making generic containers interactive.

  • Test the complete flow with keyboard and a screen reader, not only isolated component markup.

Connect fields to feedback

Field, FormLabel, FormControl, FormMessage, Fieldset, and Legend provide the structural pieces for understandable forms. Pair asynchronous feedback with visible status text or a Toast so the result remains clear without moving focus.

TSX
tsximport { Field, TextInput } from "@tavojs/ui";

export const EmailField = ({ error }: { error?: string }) => {
  return (
    <Field label="Email" error={error} required>
      <TextInput name="email" type="email" required />
    </Field>
  );
};

Audit theme contrast

Theme auditing checks important foreground and background contrast pairs. Use the programmatic helper or the web CLI in continuous integration, then supplement it with rendered accessibility tests.

TS
tsimport { auditThemeA11y } from "@tavojs/ui/a11y";

const result = auditThemeA11y({
  color: { light: { primary: "#7C5CFF" } },
  accessibility: { contrast: "AA" }
});
BASH
bashnpx tavo-ui web audit --config tavo-ui.config.json

Focus and announcement helpers

  • FocusTrap defaults active to true and cycles Tab or Shift+Tab within its descendants. It does not create dialog semantics or restore focus by itself.

  • Spinner defaults size=md, tone=primary, and label=Loading; it exposes status semantics.

  • Toast defaults tone=info and closeLabel=Dismiss notification. ToastStack only positions a group; application state owns its queue and removal.

  • Overlay defaults open=true, scrim=soft, and center=true. It is a visual primitive, not a modal or focus manager.

Review component behavior before shipping

  • Test keyboard behavior, focus order, and visible focus for every interactive composition.

  • Verify labels, descriptions, errors, current-page state, and dynamic announcements with assistive technology.

  • Test controlled components when values are missing, stale, empty, or outside the available item set.

  • Check long labels, localized content, reduced motion, high zoom, narrow viewports, and both color modes.

  • Use public props and compound members only. Treat internal classes and element nesting as implementation details.

Let Field own label and message wiring

Field derives an input ID, labels the first child VNode, and connects one generated message through aria-describedby. Message priority is error, warning, success, then hint.

  • Pass hint, error, warning, or success to Field instead of adding an unconnected FormMessage manually.

  • Put the form control first. Field applies ID and ARIA wiring to only the first child VNode.

  • Field required displays the required marker but does not set the native input attribute. Pass required to the control too.

  • An explicit child ID wins over the Field ID and becomes the label target.

  • Existing aria-describedby values are preserved and combined with the generated message ID.

TSX
tsximport { Field, TextInput } from "@tavojs/ui";

export const EmailField = ({ error }: { error?: string }) => {
  return (
    <Field
      label="Email"
      hint="Used for account notices"
      error={error}
      required
    >
      <TextInput name="email" type="email" required />
    </Field>
  );
};

Read theme audit results precisely

  • Theme auditing checks light and dark token results. AA uses 4.5:1 and AAA uses 7:1.

  • failOnViolation makes token generation throw; auditThemeA11y catches those violations and returns error-severity issues.

  • Without failOnViolation, contrast findings are warnings and passed remains true.

  • The result describes theme-token contrast only; it does not audit component metadata or rendered application UI.

TS
tsimport { auditThemeA11y } from "@tavojs/ui/a11y";

type A11yAuditIssue = {
  id: string;
  severity: "error" | "warning" | "info";
  message: string;
  target?: string;
};

type A11yAuditResult = {
  passed: boolean;
  issues: A11yAuditIssue[];
};

const result = auditThemeA11y(config);

Know what automated UI audits do not prove

Theme auditing checks six generated foreground/background pairs: normal text, muted text, headings, primary actions, secondary actions, and neutral actions. It skips non-hex values because their contrast cannot be resolved statically.

  • The audit does not inspect rendered DOM, accessible names, heading order, landmarks, or duplicate IDs.

  • It does not exercise keyboard navigation, focus restoration, overlay stacking, or task completion flow.

  • It does not test browser zoom, forced colors, reduced motion, localization expansion, or screen-reader output.

  • It does not validate whether component documentation contains accessibility guidance.

  • Combine the theme check with browser automation, an accessibility engine, keyboard testing, and representative screen-reader testing.