---
trigger: glob
globs: 'app/components/**/*.tsx,app/pages/**/*.tsx'
---
# Accessibility & Responsive Design

**WCAG Level AAA enforced: 44×44px touch targets, 14px+ text.**

## Touch Targets (WCAG 2.5.5)

**Min: 44×44px** | **Recommended: 48×48px** | **Spacing: 8px between**

### Buttons

**✅ Correct:** All sizes meet 44px minimum

```tsx
<Button size="icon">            {/* h-11 w-11 (44px) → h-12 w-12 (48px) */}
  <Icon className="h-6 w-6" />  {/* 24px icon = 54% ratio */}
</Button>

<Button size="md">              {/* h-11 (44px) → h-12 (48px) */}
  <Sparkles className="h-6 w-6" />
  Discover
</Button>
```

**❌ Wrong:** Too small

```tsx
<button className="h-8 w-8">   {/* 32px - fails WCAG */}
<button className="h-10 w-10"> {/* 40px - still too small */}
```

### Other Interactive Elements

**✅ Correct:** Radio/Checkbox in clickable labels

```tsx
<input type="radio" className="h-5 w-5 sm:h-6 sm:w-6" />
```

**✅ Correct:** Slider touch targets

```tsx
<Slider />  {/* Glass: 44-48px track + thumb */}
<Slider />  {/* Paper: 10px visual + 34px padding = 44px hit area */}
```

**✅ Correct:** Spacing between targets

```tsx
<div className="flex gap-2">  {/* 8px minimum between targets */}
```

## Typography

**Min: 14px (text-sm)** | **Recommended: 16px (text-base)**

### Text Sizing

**✅ Correct:** Body text (readable)

```tsx
<p className="text-sm sm:text-base">Content...</p>
<Input className="text-sm md:text-base" />
<label className="text-sm sm:text-base">Label</label>
```

**✅ Correct:** Headings (hierarchy)

```tsx
<h1 className="text-3xl sm:text-4xl">Title</h1>
<h2 className="text-xl sm:text-2xl">Subtitle</h2>
```

**⚠️ Decorative only (12px):**

```tsx
<span className="text-xs" aria-hidden="true">
  2h ago
</span>
```

**❌ Wrong:** Never for body/interactive

```tsx
<p className="text-xs">Important text...</p>
<label className="text-xs">Field name</label>
```

### Responsive Pattern

**✅ Correct:** Text scales UP on larger screens

```tsx
className = "text-sm sm:text-base md:text-lg";
```

**❌ Wrong:** Text shrinks on mobile (backwards!)

```tsx
className = "text-base sm:text-sm";
```

## Icon Sizing

**In buttons: 24×24px (h-6 w-6)** for 44-48px buttons (54% ratio)

**✅ Correct:** Optimal

```tsx
<Button size="icon">
  <Settings className="h-6 w-6" /> {/* 24px */}
</Button>
```

**⚠️ Minimum acceptable:**

```tsx
<Button size="icon">
  <Settings className="h-5 w-5" /> {/* 20px */}
</Button>
```

**❌ Wrong:** Too small

```tsx
<Button size="icon">
  <Settings className="h-4 w-4" /> {/* 16px - hard to see */}
</Button>
```

## Icon-Only Buttons

**All icon-only buttons require `aria-label`:**

**✅ Correct:** Accessible

```tsx
<Button size="icon" aria-label="Settings">
  <Settings className="h-6 w-6" />
</Button>
```

**❌ Wrong:** No label (screen readers can't describe button)

```tsx
<Button size="icon">
  <Settings className="h-6 w-6" />
</Button>
```

## Keyboard Navigation

**All interactive components must support keyboard navigation:**

- **Focus visible**: `focus-visible:ring-2 focus-visible:ring-brand-primary`
- **Tab order**: Logical, follows visual order
- **Escape**: Closes overlays/modals
- **Arrow keys**: Navigate within composite widgets (SegmentedControl, RadioGroup)

**✅ Correct:** Use components with built-in keyboard support

```tsx
<SegmentedControl value={view} onChange={setView} options={options} />
```

**❌ Wrong:** Manual buttons without keyboard navigation

```tsx
<div className="flex">
  <button onClick={() => setView("a")}>A</button>
  <button onClick={() => setView("b")}>B</button>
</div>
```

## Reduced Motion

**Respect `prefers-reduced-motion`:**

**✅ Correct:** CSS handles reduced motion

```tsx
className = "animate-in duration-moderate motion-reduce:animate-none";
```

**❌ Wrong:** No reduced motion consideration

```tsx
className = "animate-bounce";
```

## Interactive Element Requirements

All clickable/interactive elements must include:

- ✅ `cursor-pointer` on all clickable elements
- ✅ `select-none` on non-selectable text
- ✅ Semantic text colors (`text-content-primary`)
- ✅ Hover states with transitions
- ✅ Focus indicators (`focus-visible:ring-2`)

## Mobile Hover Visibility

**Hover-reveal patterns don't work on touch devices.** Always ensure interactive elements are visible by default on mobile.

**✅ Correct:** Visible on mobile, hover-reveal on desktop

```tsx
<Button className="sm:opacity-0 sm:group-hover:opacity-100">
  <RotateCcw />
</Button>
```

**❌ Wrong:** Hidden on all devices until hover (invisible on mobile)

```tsx
<Button className="opacity-0 group-hover:opacity-100">
  <RotateCcw />
</Button>
```

**Pattern:** Use `sm:` prefix for opacity hiding so mobile (<640px) shows the element by default.

## Responsive Philosophy

**On smaller devices:**

- ✅ Touch targets: SAME or LARGER (44px always)
- ✅ Text: SAME or LARGER (14px minimum)
- ✅ Icons in buttons: SAME (24px)
- ✅ Spacing/padding: REDUCES
- ✅ Layout: SIMPLIFIES (multi-column → single)

**✅ Correct:** Responsive pattern

```tsx
<Button className="h-11 sm:h-12">        {/* Larger on desktop */}
  <Icon className="h-6 w-6" />            {/* Consistent */}
  <span className="text-sm sm:text-base"> {/* Larger on desktop */}
</Button>
<Container className="p-4 sm:p-6">       {/* Padding reduces on mobile */}
```

## Quick Reference

### Size Classes

**Touch Targets:**

- ✅ `h-11` (44px) minimum, `h-12` (48px) recommended
- ❌ `h-1` through `h-10` all too small

**Icons in Buttons:**

- ✅ `h-6 w-6` (24px) optimal
- ⚠️ `h-5 w-5` (20px) minimum
- ❌ `h-4 w-4` (16px) too small

**Body Text:**

- ✅ `text-sm` (14px) minimum, `text-base` (16px) recommended
- ⚠️ `text-xs` (12px) decorative only with `aria-hidden`
- ❌ Smaller sizes never

**Spacing:**

- ✅ `gap-2` (8px) minimum between touch targets
- ❌ `gap-1` (4px) too tight

## Component Standards

**Button:** All variants 44px+ - see @app/components/ui/Button/Button.tsx
**Input:** text-sm minimum - see @app/components/ui/Input/Input.tsx
**Slider:** Glass (44-48px track + thumb), Paper (10px visual + 34px padding = 44px hit area) - see @app/components/ui/Slider/Slider.tsx
**Select:** 44px triggers, text-sm items - see @app/components/ui/Select/Select.tsx

## Verification

```bash
npm run dev  # Test at 375px (mobile) and 768px (tablet)
```

Use Chrome DevTools Device Toolbar to measure button sizes - all interactive elements must be ≥44×44px.
