JSN UI built-in skill

JSN UI Accessibility

ARIA roles, keyboard nav, prefers-reduced-motion, and the a11y checklist.

.skills/jsn-ui-a11y.md1100 charsa11y
.skills/jsn-ui-a11y.md
GitHub
# JSN UI — Accessibility

Every JSN UI component is built with accessibility in mind. This is
the checklist we follow; the user should be able to drop any
component in and ship an accessible UI.

## What JSN UI does for you

- All interactive components use real `<button>`, `<a>`, `<input>` elements
- `forwardRef` + `displayName` on every component
- `aria-invalid`, `aria-disabled`, `aria-hidden` set where appropriate
- Focus rings via the `--ring` token (visible on `:focus-visible`)
- `Escape` closes dialogs, drawers, popovers, sheets
- Click-outside closes popovers
- Tab order is always natural DOM order
- `prefers-reduced-motion` is respected in every motion element

## What you need to do

### Labels

Every form control needs a label.

```tsx
// ✅ Good — explicit label
<div>
  <Label htmlFor="email">Email</Label>
  <Input id="email" type="email" />
</div>

// ✅ Good — sr-only label for icon-only inputs
<Label htmlFor="search" className="sr-only">Search</Label>
<Input id="search" placeholder="Search…" />
```

### Icon-only buttons

```tsx
<Button size="icon" aria-label="Open menu">
  <Menu />
</Button>
```

### Touch targets

Icon-only buttons should be at least 44×44px. Use the `TouchTarget`
component to enforce this:

```tsx
import { TouchTarget } from "@/components/mobile/touch-target";

<TouchTarget asChild>
  <button aria-label="Settings">
    <Settings size={16} />
  </button>
</TouchTarget>
```

### Headings

Use exactly one `<h1>` per page. Don't skip levels.

```tsx
<h1>Page title</h1>
<section>
  <h2>Section title</h2>  // h2, not h3
</section>
```

### Live regions

For dynamic content like toasts, use `aria-live`:

```tsx
<div role="status" aria-live="polite">
  Saved.
</div>
```

### Color contrast

The 6 OKLCH presets in `themes.ts` are pre-tuned to meet WCAG AA
contrast in both light and dark mode. Don't override individual
tokens without re-checking contrast.

## Testing

Before shipping:
- Tab through every page. Focus must be visible.
- Use VoiceOver / NVDA on a complex page.
- Run Lighthouse in Chrome — target 100 on accessibility.
- Run `axe DevTools` in Chrome DevTools — fix any "serious" or "critical" issues.

## Common mistakes to flag

- Buttons that don't have a visible label
- Clicking on a `<div>` instead of a `<button>` (use the right element)
- Forgetting `aria-current="page"` on the active nav link
- Using color alone to convey state (add an icon or text too)

How to use this skill

These files live in the .skills/ directory of the JSN UI repo. Open Design–compatible agents (Claude Code, Cursor, Cline, etc.) auto-detect them. You can also reference them directly:

# in your agent's config
- name: jsn-ui
  source: https://github.com/javashn/jsn-ui/tree/main/.skills