Skip to content

Accessibility

WCAG posture

No stated compliance level. No claim of WCAG A / AA / AAA in the repo's docs.

Automated checks

  • eslint-plugin-jsx-a11y is not configured in eslint.config.js. There is no a11y lint rule enforcement.

Recommendation: add eslint-plugin-jsx-a11y. Default rule set catches the common stuff (missing alt, htmlFor on labels, anchors without href, etc.). Maybe 10 lines of config; turns into a low-grade nag in the IDE.

Manual testing

No documented keyboard-only or screen-reader testing has been performed (none of the canonical-repo docs mention it).

Foundations the dependency tree provides

Using shadcn/ui + Radix UI gives accessibility for free in many primitives:

  • Dialog traps focus, restores on close, has Esc to close, has aria-modal and aria-labelledby
  • DropdownMenu, Select, Combobox, Popover follow ARIA Authoring Practices (keyboard navigation, screen-reader announcements)
  • Tooltip, HoverCard support keyboard focus
  • Form (with react-hook-form integration) wires <label htmlFor> + aria-describedby + aria-invalid automatically

This is a strong starting point. The risk is custom components on top of Radix that don't preserve the accessibility properties.

Color contrast

Tailwind palette via HSL CSS variables. Contrast depends on chosen values: - Verify --background / --foreground pair meets WCAG AA (4.5:1) - Verify --primary / --primary-foreground pair - Verify --muted-foreground against --background (often the failing pair in shadcn defaults)

A 5-minute pass with WebAIM Contrast Checker on the live colours would close this.

Focus management

  • shadcn Dialog and Sheet handle focus trapping
  • Routing changes do not shift focus by default (react-router-dom doesn't do this) — a screen-reader user navigating to a new page won't have focus reset. Add a focus reset in a route-change effect.
  • No "skip to content" link — for keyboard users hitting Tab, traversing the sidebar nav before reaching main content is friction. Add a <a href="#main" className="sr-only focus:not-sr-only ...">Skip to main content</a> at the top of AppLayout.

Semantic HTML

shadcn primitives render semantic elements (<button>, <dialog>, <nav>). Custom pages should preserve this — avoid wrapping native elements in <div role="button"> constructs.

ARIA usage

Mostly handled by shadcn / Radix. Custom code should not invent ARIA — use the lowest-level Radix primitive that fits.

Form labels

<Form> + <FormLabel htmlFor> handles this. Audit: every page with a form must use shadcn's <Form> (not raw <input>). Verify with grep -rE "<input " src/pages/ — if there are unwrapped <input> tags, those probably lack labels.

Image alt text

  • <img src="/someli_logo.png" alt="Someli Logo" />
  • <img src="/dashboard.jpg" alt="Dashboard Illustration" />
  • Verify every other <img> has alt (audit with grep -rE "<img " src/)

Known issues

None tracked at audit time (no ticket queue inspected).

Recommendations (prioritised)

  1. Add eslint-plugin-jsx-a11y to ESLint config. Low cost, ongoing benefit.
  2. Add "skip to main content" link at top of AppLayout.
  3. Add focus reset on route change in AppLayout.
  4. Run a color contrast pass on the design tokens.
  5. Document a WCAG target (e.g., "this app aims for WCAG 2.1 AA") in the README so future work has a yardstick.
  6. Test with axe-core in CI or via the browser plugin.