+```
+
+### MINOR — Verbose label
+
+```markdown
+### [MINOR] Button label is unnecessarily verbose
+
+**What:** Label includes redundant information
+**Where:** `src/ui/controls/play-button.ts:8`
+**Why:** Screen reader users hear repetitive content
+**WCAG:** 2.4.6 Headings and Labels
+**Fix:** Shorten to essential information
+
+
+
+aria-label="Click this button to play the video"
+
+
+
+aria-label="Play"
+```
+
+---
+
+## Report Template
+
+```markdown
+# Accessibility Review: [filename or component]
+
+## Score: X/100
+
+| Severity | Count | Points |
+| -------- | ----- | ------ |
+| Critical | X | -X |
+| Major | X | -X |
+| Minor | X | -X |
+| Nit | X | -X |
+
+## Critical Issues
+
+[List all CRITICAL issues using full format above]
+
+---
+
+## Major Issues
+
+[List all MAJOR issues using full format above]
+
+---
+
+## Minor Issues
+
+| Severity | Location | Issue | WCAG | Fix |
+| -------- | ------------ | ------------------- | ----- | --------------- |
+| MINOR | `file.ts:12` | Verbose label | 2.4.6 | Shorten |
+| NIT | `file.ts:34` | Missing describedby | 1.3.1 | Add description |
+
+---
+
+## Good Patterns Found
+
+- [What's working well]
+- [Worth preserving]
+
+---
+
+## Summary
+
+[2-3 paragraph assessment: overall accessibility posture, priority fixes, recommendations]
+
+---
+
+## Recommendations
+
+### Before Release
+
+1. [Critical fixes]
+
+### Next Release
+
+1. [Major improvements]
+
+### Future
+
+1. [Minor enhancements]
+```
+
+---
+
+## PR Review Template
+
+For reviewing accessibility changes in pull requests:
+
+```markdown
+# PR Accessibility Review: #[number]
+
+## Changed Files
+
+| File | Type | Review |
+| ------------------ | ------------- | ----------- |
+| `src/ui/menu.ts` | New component | Full review |
+| `src/ui/button.ts` | Modified | Diff review |
+
+## New Components
+
+### src/ui/menu.ts
+
+[Full review using standard template]
+
+## Modified Components
+
+### src/ui/button.ts
+
+**Changed:** Lines 45-60
+
+[Review of changed accessibility surface only]
+
+## Checklist
+
+- [ ] New interactive elements have accessible names
+- [ ] Keyboard navigation works correctly
+- [ ] Focus management handles new UI flows
+- [ ] ARIA attributes reflect component state
+- [ ] Live regions announce dynamic changes
+- [ ] No new accessibility regressions
+```
+
+---
+
+## Console Output Format
+
+For quick terminal-based reviews:
+
+```
+═══════════════════════════════════════════════════
+A11Y REVIEW: [filename]
+═══════════════════════════════════════════════════
+
+CRITICAL (X issues)
+───────────────────
+[A11Y] Line X: Issue title
+ code snippet
+ Fix: recommended fix
+ WCAG: criterion
+
+MAJOR (X issues)
+───────────────────
+[A11Y] Line X: Issue title
+ code snippet
+ Fix: recommended fix
+ WCAG: criterion
+
+MINOR (X issues)
+───────────────────
+[A11Y] Line X: Issue title
+ code snippet
+ Fix: recommended fix
+ WCAG: criterion
+
+═══════════════════════════════════════════════════
+SUMMARY: X critical, X major, X minor
+Score: X/100
+═══════════════════════════════════════════════════
+```
diff --git a/.claude/skills/aria/review/workflow.md b/.claude/skills/aria/review/workflow.md
new file mode 100644
index 00000000..36d7178d
--- /dev/null
+++ b/.claude/skills/aria/review/workflow.md
@@ -0,0 +1,92 @@
+# Accessibility Review Workflow
+
+Review components and code for accessibility following WAI-ARIA and WCAG 2.1.
+
+## Process
+
+```
+┌─────────────────────────────────────────────────────┐
+│ Gather Context │
+│ Load file(s) or PR diff to review │
+└──────────────────────┬──────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ Load References │
+│ Based on component type (media, menu, form) │
+└──────────────────────┬──────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ Run Checklist │
+│ review/checklist.md by section │
+└──────────────────────┬──────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ Format Report │
+│ Use templates.md for output │
+└─────────────────────────────────────────────────────┘
+```
+
+### 1. Gather Context
+
+Identify what to review:
+
+- **Single file:** `path/to/component.ts`
+- **Package:** `packages/html/src/ui/`
+- **PR diff:** Changed accessibility surface
+
+### 2. Load References
+
+Based on component type, load relevant references:
+
+| Component Type | Load |
+| ---------------- | ----------------------------------------------- |
+| Media player | `references/media.md` |
+| Any interactive | `references/keyboard.md`, `references/focus.md` |
+| Custom widgets | `references/aria.md` |
+| React components | `references/react.md` |
+| Common mistakes | `references/anti-patterns.md` |
+
+### 3. Run Checklist
+
+Use [checklist.md](checklist.md) systematically:
+
+1. Start with **All Interactive Elements** section
+2. Check component-specific sections (Buttons, Menus, Dialogs, Sliders, etc.)
+3. Review Color and Contrast
+4. Check Motion and Animation
+5. Verify Document Structure if applicable
+
+### 4. Format Report
+
+Use [templates.md](templates.md) for consistent output.
+
+## Quick Review
+
+For fast reviews without full checklist:
+
+- [ ] All interactive elements have accessible names
+- [ ] Keyboard navigation works (Tab, Enter, Space, Arrows, Escape)
+- [ ] Focus indicator visible on all focusable elements
+- [ ] Custom controls have appropriate ARIA roles
+- [ ] State changes announced (aria-pressed, aria-expanded, live regions)
+- [ ] No mouse-only interactions
+
+## Severity Levels
+
+See [templates.md](templates.md) for severity definitions and scoring.
+
+## References
+
+| File | Contents |
+| ---------------------------------------------------------------- | ------------------------------ |
+| [checklist.md](checklist.md) | Comprehensive review checklist |
+| [templates.md](templates.md) | Issue format, report template |
+| [../references/keyboard.md](../references/keyboard.md) | Keyboard navigation patterns |
+| [../references/focus.md](../references/focus.md) | Focus management |
+| [../references/aria.md](../references/aria.md) | ARIA roles and states |
+| [../references/media.md](../references/media.md) | Media player accessibility |
+| [../references/react.md](../references/react.md) | React-specific patterns |
+| [../references/anti-patterns.md](../references/anti-patterns.md) | Common mistakes |
diff --git a/.claude/skills/component/SKILL.md b/.claude/skills/component/SKILL.md
new file mode 100644
index 00000000..61761c92
--- /dev/null
+++ b/.claude/skills/component/SKILL.md
@@ -0,0 +1,215 @@
+---
+name: component
+description: >-
+ Build accessible, headless UI components with modern architecture patterns.
+ Use when creating component libraries, design systems, or reusable UI primitives.
+ Handles compound components, state management, accessibility, styling hooks, and API design.
+ Includes Lit (controllers, ReactiveElement) and React (hooks, context) patterns.
+---
+
+# Component Architecture Patterns
+
+Build accessible, headless UI components using proven patterns from Base UI, Radix, and Ark UI. These patterns are **framework-agnostic** — core concepts apply across React, Vue, Svelte, Solid, and vanilla JS.
+
+**Primary sources:**
+
+- [Base UI Handbook](https://base-ui.com/react/handbook/overview)
+- [Ark UI](https://ark-ui.com/) — Cross-framework implementation
+- [Zag.js](https://zagjs.com/) — State machines for UI components
+
+**Framework-specific:** [react.md](references/react.md) | [lit.md](references/lit.md)
+
+---
+
+## Core Principles
+
+1. **Headless over styled** — Separate behavior from presentation
+2. **Compound over monolithic** — Small composable parts over config-heavy megacomponents
+3. **Controlled + uncontrolled** — Support both state ownership models
+4. **Accessible by default** — ARIA, keyboard nav, focus management built-in
+5. **State via attributes** — Expose state through `data-*` for framework-agnostic styling
+
+---
+
+## Pattern 1: Compound Components
+
+**What:** Components as related parts sharing state through context, each mapping 1:1 to DOM elements.
+
+**Why:**
+
+- Declarative — assemble like building blocks, reorder/omit parts freely
+- Each part is an independent styling target
+- DOM structure maps directly to ARIA roles
+
+**Standard hierarchies:**
+
+| Type | Parts |
+| ----------- | ---------------------------------------------------- |
+| Popups | Root → Trigger → Portal → Positioner → Popup → Arrow |
+| Collections | Root → List → Trigger + Panel |
+| Forms | Root → Label → Control → Description → Error |
+
+**Ref:** [Base UI Composition](https://base-ui.com/react/handbook/composition)
+
+---
+
+## Pattern 2: Controlled & Uncontrolled State
+
+**What:** Support external state control OR internal state with consistent prop naming.
+
+**Why:**
+
+- Flexibility for simple and complex use cases
+- Predictable API across components
+- Change details enable fine-grained control (cancel changes, track reasons)
+
+**Convention:**
+
+| State | Uncontrolled | Controlled | Handler |
+| ------- | ---------------- | ---------- | ----------------------------------- |
+| Open | `defaultOpen` | `open` | `onOpenChange(open, details)` |
+| Value | `defaultValue` | `value` | `onValueChange(value, details)` |
+| Checked | `defaultChecked` | `checked` | `onCheckedChange(checked, details)` |
+
+**Change details:** `{ reason, event, cancel() }`
+
+**Ref:** [Base UI Customization](https://base-ui.com/react/handbook/customization)
+
+---
+
+## Pattern 3: Prop Getters
+
+**What:** Functions returning HTML attributes for DOM elements, abstracting logic from rendering.
+
+**Why:**
+
+- Portable across frameworks (React, Vue, Svelte, Solid)
+- Clean separation of concerns
+- Composable via `mergeProps()`
+
+**Example:** `getTriggerProps()` returns `{ aria-expanded, aria-haspopup, onClick, onKeyDown }`
+
+**Ref:** [Zag.js](https://zagjs.com/), [Downshift](https://www.downshift-js.com/)
+
+---
+
+## Pattern 4: State via Data Attributes
+
+**What:** Expose state through `data-*` attributes for CSS targeting.
+
+**Why:**
+
+- Framework-agnostic styling
+- No JS needed for state-based styles
+- Inspectable in DevTools
+
+**Standard attributes:**
+
+- `data-open` / `data-closed` — Visibility
+- `data-checked` / `data-unchecked` — Toggle state
+- `data-highlighted` — Focus within group
+- `data-disabled`, `data-valid`, `data-invalid`
+- `data-side`, `data-align` — Positioning
+
+**CSS variables:** `--available-height`, `--anchor-width`, `--transform-origin`
+
+**Ref:** [Base UI Styling](https://base-ui.com/react/handbook/styling)
+
+---
+
+## Pattern 5: Accessibility
+
+**What:** ARIA, keyboard navigation, focus management built into architecture.
+
+**Why:**
+
+- Accessibility is structural, not decorative
+- Users expect standard keyboard interactions
+- Consistent patterns reduce errors
+
+**Key concerns:**
+
+- **ARIA attributes** — Auto-managed from state
+- **Focus trapping** — Modals trap focus within
+- **Roving tabindex** — One tabbable item, arrows navigate
+- **Virtual focus** — `aria-activedescendant` for long lists
+- **Typeahead** — A-Z jumps to matches
+
+**Ref:** [Base UI Accessibility](https://base-ui.com/react/overview/accessibility), [WAI-ARIA Practices](https://www.w3.org/WAI/ARIA/apg/patterns/)
+
+For detailed accessibility patterns, load the `aria` skill.
+
+---
+
+## Pattern 6: Floating Positioning
+
+**What:** Position popups relative to triggers with collision detection.
+
+**Why:** Handles viewport boundaries, scroll, resize automatically.
+
+**Config:** `side`, `align`, `sideOffset`, `collision` (flip/shift), `trackAnchor`
+
+**Ref:** [Floating UI](https://floating-ui.com/)
+
+---
+
+## API Conventions
+
+| Category | Props |
+| ----------- | -------------------------------------------------------------- |
+| Interaction | `disabled`, `required`, `readOnly` |
+| Collections | `multiple`, `loopFocus`, `orientation` |
+| Popups | `modal`, `closeOnEscape`, `closeOnOutsideClick`, `keepMounted` |
+| Positioning | `side`, `align`, `sideOffset`, `collision` |
+
+**Imperative actions:** `actionsRef` exposing `open()`, `close()`, `toggle()`
+
+See [props.md](references/props.md) for naming conventions.
+
+---
+
+## Reference Files
+
+| File | Contents |
+| ----------------------------------------------- | ------------------------------------ |
+| [lit.md](references/lit.md) | Lit controllers, mixins, context |
+| [react.md](references/react.md) | React hooks, context, refs |
+| [props.md](references/props.md) | Prop naming, conventions, defaults |
+| [styling.md](references/styling.md) | Data attributes, CSS variables |
+| [animation.md](references/animation.md) | CSS transitions, JS animation libs |
+| [polymorphism.md](references/polymorphism.md) | render vs asChild patterns |
+| [collection.md](references/collection.md) | Collections, portals, virtualization |
+| [anti-patterns.md](references/anti-patterns.md) | Common component mistakes |
+
+For accessibility patterns (ARIA, keyboard, focus), load the `aria` skill.
+
+## Review
+
+For structured component reviews, load the review workflow:
+
+| File | Contents |
+| ----------------------------------- | --------------------------- |
+| [workflow.md](review/workflow.md) | Review process and severity |
+| [checklist.md](review/checklist.md) | Component review checklist |
+| [templates.md](review/templates.md) | Issue and report formats |
+
+---
+
+## Implementation Sources
+
+| Resource | Use For |
+| ------------------------------------------------------------------------------- | --------------------------------- |
+| [Base UI Source](https://github.com/mui/base-ui/tree/master/packages/react/src) | React reference implementations |
+| [Radix Primitives](https://github.com/radix-ui/primitives) | Alternative approach |
+| [Zag.js](https://github.com/chakra-ui/zag) | Framework-agnostic state machines |
+| [Floating UI](https://floating-ui.com/docs/getting-started) | Positioning |
+
+---
+
+## Related Skills
+
+| Need | Use |
+| ---------------------- | ------------ |
+| Accessibility patterns | `aria` skill |
+| API design principles | `api` skill |
+| Documentation patterns | `docs` skill |
diff --git a/.claude/skills/component/references/animation.md b/.claude/skills/component/references/animation.md
new file mode 100644
index 00000000..59d2ccf6
--- /dev/null
+++ b/.claude/skills/component/references/animation.md
@@ -0,0 +1,238 @@
+# Animation Patterns
+
+Animate component state changes with CSS or JavaScript libraries.
+
+**Reference:** [Base UI Animation Handbook](https://base-ui.com/react/handbook/animation)
+
+---
+
+## CSS Transitions (Preferred)
+
+Use `data-starting-style` / `data-ending-style` for smooth transitions:
+
+```css
+.popup {
+ transform-origin: var(--transform-origin);
+ transition:
+ transform 150ms,
+ opacity 150ms;
+}
+
+.popup[data-starting-style],
+.popup[data-ending-style] {
+ opacity: 0;
+ transform: scale(0.9);
+}
+```
+
+**Why transitions over animations:** Transitions can be cancelled midway. If user closes a popup before it finishes opening, it smoothly animates to closed without abrupt changes.
+
+---
+
+## CSS Animations
+
+Use `data-open` / `data-closed` for keyframe animations:
+
+```css
+.popup[data-open] {
+ animation: scaleIn 200ms ease-out;
+}
+
+.popup[data-closed] {
+ animation: scaleOut 200ms ease-in;
+}
+
+@keyframes scaleIn {
+ from {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes scaleOut {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+}
+```
+
+---
+
+## JavaScript Animation Libraries
+
+### Unmounted Components (Dialog, Popover, Menu)
+
+Components unmounted from DOM when closed need special handling for exit animations.
+
+**Pattern:** Controlled `open` + `keepMounted` on Portal + AnimatePresence
+
+```tsx
+function AnimatedPopover() {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+ Open
+
+ {open && (
+
+
+
+ }
+ >
+ Content
+
+
+
+ )}
+
+
+ );
+}
+```
+
+### Kept Mounted Components
+
+Components with `keepMounted` stay in DOM when closed — use state-based animation:
+
+```tsx
+
(
+
+ )}
+>
+ Content
+
+```
+
+### Manual Unmount Control
+
+Use `actionsRef` for full lifecycle control:
+
+```tsx
+function ManualUnmount() {
+ const [open, setOpen] = useState(false);
+ const actionsRef = useRef(null);
+
+ return (
+
+ Open
+
+ {open && (
+
+ {
+ if (!open) actionsRef.current.unmount();
+ }}
+ />
+ }
+ />
+
+ )}
+
+
+ );
+}
+```
+
+---
+
+## Animation Detection
+
+Base UI uses `element.getAnimations()` to detect when animations finish before unmounting.
+
+**Important:** For animations without opacity (e.g., translating drawer), include `opacity: 0.9999` so detection works:
+
+```tsx
+
+```
+
+---
+
+## Height Animation (Accordion)
+
+Animating `height: auto` requires measurement.
+
+**Steps:**
+
+1. On open: measure `scrollHeight`, animate 0 → measured
+2. After animation: set to `auto` (allows content resize)
+3. On close: set explicit height, animate to 0
+
+```css
+.accordion-content {
+ overflow: hidden;
+ transition: height 200ms ease-out;
+}
+
+.accordion-content[data-open] {
+ animation: slideDown 200ms ease-out;
+}
+
+@keyframes slideDown {
+ from {
+ height: 0;
+ }
+ to {
+ height: var(--accordion-content-height);
+ }
+}
+```
+
+**Double-rAF trick:** When closing, set explicit height before animating to 0. Browser needs a frame to register height before transitioning.
+
+> **Reference:** [Radix Collapsible](https://www.radix-ui.com/primitives/docs/components/collapsible)
+
+---
+
+## Reduced Motion
+
+Respect user preferences:
+
+```css
+@media (prefers-reduced-motion: reduce) {
+ .popup {
+ transition: none;
+ animation: none;
+ }
+}
+```
+
+---
+
+## See Also
+
+- [Styling](styling.md) — data attributes for state
+- [Collection](collection.md) — exit animations in lists
diff --git a/.claude/skills/component/references/anti-patterns.md b/.claude/skills/component/references/anti-patterns.md
new file mode 100644
index 00000000..5878d13e
--- /dev/null
+++ b/.claude/skills/component/references/anti-patterns.md
@@ -0,0 +1,288 @@
+# Component Anti-Patterns
+
+Common mistakes when building UI components.
+
+## Prop Explosion
+
+```tsx
+// BAD: 30+ props on one component
+
+
+// GOOD: Compound components
+
+
+
+ Title
+ Close
+
+
+```
+
+**Why it fails:** Inflexible, hard to style parts independently, poor TypeScript experience.
+
+---
+
+## Inline Styles for State
+
+```tsx
+// BAD: Forces JS styling, fights theming
+
+```
+
+The `as` prop requires complex generic types that slow down the TypeScript language server.
+
+---
+
+## When to Use Each
+
+| Scenario | Pattern |
+| --------------------- | ---------------------------- |
+| Simple element swap | `asChild` acceptable |
+| State-based rendering | `render` (function form) |
+| Complex prop merging | `render` (explicit control) |
+| Debugging issues | `render` (visible prop flow) |
+| Maximum type safety | `render` |
+
+---
+
+## See Also
+
+- [Progressive Disclosure](../../api-design/principles/progressive-disclosure.md) — layered complexity
+- [Anti-Patterns](anti-patterns.md) — polymorphism pitfalls
diff --git a/.claude/skills/component/references/props.md b/.claude/skills/component/references/props.md
new file mode 100644
index 00000000..3b17abc6
--- /dev/null
+++ b/.claude/skills/component/references/props.md
@@ -0,0 +1,235 @@
+# API Design Conventions
+
+Prop naming, defaults, and documentation patterns for consistent component APIs.
+
+**Reference implementations:** [Base UI](https://base-ui.com/), [Radix](https://www.radix-ui.com/), [Ark UI](https://ark-ui.com/)
+
+---
+
+## Prop Naming
+
+### Boolean Props
+
+Use positive adjectives, avoid `is`/`has` prefixes:
+
+| ✅ Good | ❌ Avoid |
+|---------|----------|
+| `disabled` | `isDisabled` |
+| `required` | `isRequired` |
+| `open` | `isOpen` |
+| `loading` | `isLoading` |
+
+---
+
+### State Props
+
+| State | Uncontrolled | Controlled | Handler |
+|-------|--------------|------------|---------|
+| Open | `defaultOpen` | `open` | `onOpenChange` |
+| Value | `defaultValue` | `value` | `onValueChange` |
+| Checked | `defaultChecked` | `checked` | `onCheckedChange` |
+| Selected | `defaultSelected` | `selected` | `onSelectedChange` |
+
+---
+
+### Event Handlers
+
+Pattern: `on` + Noun + Verb
+
+| ✅ Good | ❌ Avoid |
+|---------|----------|
+| `onOpenChange` | `handleOpen`, `setOpen` |
+| `onValueChange` | `onChange` (too generic) |
+| `onSelect` | `onItemSelected` |
+| `onDismiss` | `onClose` (ambiguous) |
+
+---
+
+## Standard Props by Category
+
+### Interaction
+
+```typescript
+disabled?: boolean;
+required?: boolean;
+readOnly?: boolean;
+autoFocus?: boolean;
+```
+
+### Collections
+
+```typescript
+multiple?: boolean; // Allow multiple selection
+loopFocus?: boolean; // Arrow keys loop at ends
+orientation?: 'horizontal' | 'vertical';
+typeahead?: boolean; // A-Z navigation
+```
+
+### Popups
+
+```typescript
+modal?: boolean | 'trap-focus';
+closeOnEscape?: boolean;
+closeOnOutsideClick?: boolean;
+keepMounted?: boolean; // Keep in DOM when closed
+```
+
+### Positioning
+
+```typescript
+side?: 'top' | 'bottom' | 'left' | 'right';
+align?: 'start' | 'center' | 'end';
+sideOffset?: number;
+alignOffset?: number;
+collision?: 'flip' | 'shift' | 'none';
+```
+
+---
+
+## Change Event Details
+
+**What:** Rich context passed to change handlers.
+
+**Why:** Enables conditional logic — prevent changes, track analytics, debug.
+
+```typescript
+interface ChangeDetails {
+ reason: string; // 'click' | 'keyboard' | 'blur' | 'escape' | 'outside-click'
+ event?: Event; // Original DOM event
+ cancel(): void; // Prevent internal state change
+}
+```
+
+**Usage:** `onOpenChange={(open, details) => { if (details.reason === 'outside-click') details.cancel(); }}`
+
+---
+
+## Imperative Actions
+
+**What:** Expose actions via `actionsRef` for programmatic control.
+
+**Common actions:**
+- `open()`, `close()`, `toggle()` — Popups
+- `focus()` — Focus management
+- `scrollToIndex(i)` — Virtualized lists
+
+**Pattern:** `
` → `ref.current.open()`
+
+---
+
+## Render Delegation
+
+### The `render` Prop
+
+**What:** Replace default element while preserving behavior.
+
+**Forms:**
+- Element: `render={}` — Props merged onto element
+- Function: `render={(props, state) => ...}` — Full control
+
+### `className` / `style` as Function
+
+**What:** State-aware styling without external state.
+
+**Pattern:** `className={(state) => state.checked ? 'on' : 'off'}`
+
+---
+
+## Defaults
+
+### Sensible Defaults (80% case)
+
+| Prop | Default | Rationale |
+|------|---------|-----------|
+| `side` | `'bottom'` | Most common popup position |
+| `align` | `'center'` | Visually balanced |
+| `sideOffset` | `8` | Standard spacing |
+| `closeOnEscape` | `true` | Expected behavior |
+| `loopFocus` | `true` | Better keyboard UX |
+
+### Require Explicit Opt-in
+
+| Prop | Default | Rationale |
+|------|---------|-----------|
+| `autoFocus` | `false` | Can be disorienting |
+| `modal` | `false` | Has side effects (scroll lock) |
+| `keepMounted` | `false` | Performance |
+
+---
+
+## Escape Hatches
+
+| Need | Solution |
+|------|----------|
+| DOM access | Forward refs to root element |
+| Custom attributes | Spread `...props` |
+| Custom portal target | `container` prop on Portal |
+| Override handlers | Spread after internal handlers |
+
+---
+
+## Documentation Pattern
+
+### Props Table
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `open` | `boolean` | — | Controlled open state |
+| `defaultOpen` | `boolean` | `false` | Initial open state |
+| `onOpenChange` | `(open, details) => void` | — | Called on state change |
+
+### Data Attributes Table
+
+| Attribute | When Present |
+|-----------|--------------|
+| `data-open` | Component is open |
+| `data-disabled` | Component is disabled |
+
+### Anatomy Section
+
+Show component structure with all parts.
+
+### Examples Section
+
+Basic usage, controlled, with custom trigger, etc.
+
+---
+
+## Versioning
+
+| Change | Breaking? |
+|--------|-----------|
+| Add optional prop | No |
+| Change default value | **Yes** |
+| Remove prop | **Yes** |
+| Add required prop | **Yes** |
+
+### Deprecation Pattern
+
+1. Add new prop alongside old
+2. Log warning when old prop used
+3. Remove old prop in next major
+
+---
+
+## Checklist for New Components
+
+- [ ] Boolean props use positive adjectives
+- [ ] State props follow `value`/`defaultValue`/`onValueChange`
+- [ ] Change handlers receive `details` with `reason` and `cancel()`
+- [ ] Ref forwarded to root DOM element
+- [ ] `render` prop for element polymorphism
+- [ ] `className` accepts function
+- [ ] State exposed via `data-*` attributes
+- [ ] Additional HTML attributes spread
+- [ ] Types exported (`Component.Props`, `Component.State`)
+
+---
+
+## References
+
+| Library | API Style |
+|---------|-----------|
+| [Base UI API](https://base-ui.com/react/components/dialog) | Canonical reference |
+| [Radix API](https://www.radix-ui.com/primitives/docs/components/dialog) | Alternative conventions |
+| [Ark UI API](https://ark-ui.com/react/docs/components/dialog) | Cross-framework |
diff --git a/.claude/skills/component/references/react.md b/.claude/skills/component/references/react.md
new file mode 100644
index 00000000..79a6769a
--- /dev/null
+++ b/.claude/skills/component/references/react.md
@@ -0,0 +1,316 @@
+# React Component Patterns
+
+React-specific implementation details for compound components. For framework-agnostic patterns, see [SKILL.md](../SKILL.md).
+
+---
+
+## Context Architecture
+
+**What:** Compound components share state via React Context without prop drilling.
+
+**Why:**
+
+- Implicit state sharing between parts (Root → Trigger → Content)
+- Clean consumer API — no manual wiring
+- Nested contexts for multi-level components (Accordion → Item → Trigger)
+
+**Pattern:**
+
+- Create context with `undefined` default
+- Consumer hook throws if used outside provider
+- Root provides state, children consume
+
+**Ref:** [Base UI Dialog Source](https://github.com/mui/base-ui/tree/master/packages/react/src/dialog)
+
+---
+
+## Essential Hooks
+
+### `useControlledState`
+
+**What:** Unifies controlled/uncontrolled state patterns.
+
+**Why:** Single implementation handles both modes with consistent API.
+
+**Behavior:**
+
+- If `value` provided → controlled (external state)
+- If only `defaultValue` → uncontrolled (internal state)
+- Calls `onChange` in both modes
+
+**Ref:** [Radix useControllableState](https://github.com/radix-ui/primitives/blob/main/packages/react/use-controllable-state/src/useControllableState.tsx)
+
+---
+
+### `useId`
+
+**What:** Generate unique IDs for ARIA relationships.
+
+**Why:** Labels, descriptions, and controls need matching IDs for accessibility.
+
+**Note:** Built into React 18+. For earlier versions, use `@reach/auto-id`.
+
+---
+
+### `useFocusTrap`
+
+**What:** Trap focus within a container (modal dialogs).
+
+**Why:** Modal accessibility requires focus stays within dialog until closed.
+
+**Behavior:**
+
+- Tab at last element → first element
+- Shift+Tab at first → last element
+- Returns focus to trigger on close
+
+**Ref:** [focus-trap](https://github.com/focus-trap/focus-trap) library
+
+---
+
+### `useRovingFocus`
+
+**What:** Arrow key navigation within groups with single Tab stop.
+
+**Why:** Standard keyboard pattern for menus, tablists, toolbars.
+
+**Behavior:**
+
+- Only focused item has `tabIndex={0}`
+- Others have `tabIndex={-1}`
+- Arrows move focus, optionally loops
+
+**Ref:** [Radix RovingFocus](https://github.com/radix-ui/primitives/tree/main/packages/react/roving-focus)
+
+---
+
+### `useFloating`
+
+**What:** Position floating elements relative to anchors.
+
+**Why:** Popups need collision detection, scroll tracking, arrow positioning.
+
+**Use:** Wrap `@floating-ui/react` with component-specific defaults.
+
+**Ref:** [Floating UI React](https://floating-ui.com/docs/react)
+
+---
+
+## Ref Patterns
+
+### Forward Refs on All Parts
+
+**What:** Every compound component part forwards refs to its DOM element.
+
+**Why:** Consumers need DOM access for focus management, measurements, animations.
+
+**Pattern:** `forwardRef((props, ref) => ...)`
+
+---
+
+### `useImperativeHandle` for Actions
+
+**What:** Expose component actions through ref.
+
+**Why:** Programmatic control — `dialogRef.current.open()`
+
+**Pattern:**
+
+```tsx
+interface Actions {
+ open(): void;
+ close(): void;
+}
+useImperativeHandle(actionsRef, () => ({ open, close }));
+```
+
+**Ref:** [React useImperativeHandle](https://react.dev/reference/react/useImperativeHandle)
+
+---
+
+### `useMergeRefs` / `composeRefs`
+
+**What:** Combine multiple refs pointing to same element.
+
+**Why:** Compound components often need both:
+
+- Internal ref (for positioning, focus management, measurements)
+- Forwarded ref (for consumer access)
+- Floating UI ref (for anchor positioning)
+
+**Use cases:**
+
+- Trigger needs internal ref + forwarded ref + floating anchor ref
+- Popup needs internal ref + forwarded ref + floating ref
+- Any part using `useFloating` alongside `forwardRef`
+
+**Ref:** [Floating UI useMergeRefs](https://floating-ui.com/docs/react#usemergerefs), [Radix composeRefs](https://github.com/radix-ui/primitives/blob/main/packages/react/compose-refs/src/composeRefs.tsx)
+
+---
+
+## Render Prop Implementation
+
+**What:** The `render` prop replaces default element with custom element or component.
+
+**Key pieces:**
+
+- Accept `ReactElement` or `(props, state) => ReactElement`
+- Use `cloneElement` for element form
+- Use `mergeProps` to combine internal + external props
+
+**`mergeProps` behavior:**
+
+- Event handlers — chain (both called)
+- className — concatenate
+- style — shallow merge
+- Other props — override
+
+**Ref:** [Base UI useRender](https://github.com/mui/base-ui/blob/master/packages/react/src/use-render/useRender.ts)
+
+---
+
+## Render Delegation
+
+**What:** Replace default rendered element while preserving component behavior.
+
+**Why:**
+
+- Element polymorphism (button → link)
+- Integrate with existing component libraries
+- Conditional rendering based on internal state
+
+### Approaches
+
+| Pattern | Library | Usage |
+| -------------- | ------- | --------------------------------------------------------------- |
+| `render` prop | Base UI | `render={}` or `render={(props, state) => ...}` |
+| `asChild` prop | Radix | `Link` |
+| `as` prop | Various | `` — simpler, less flexible |
+
+### Key Utility: `mergeProps`
+
+**What:** Safely combines props from component internals + consumer.
+
+**Behavior:**
+
+- Event handlers → chained (both called)
+- `className` → concatenated
+- `style` → shallow merged
+- Other props → consumer overrides
+
+**Ref:** [Base UI mergeProps](https://github.com/mui/base-ui/blob/master/packages/react/src/merge-props/mergeProps.ts), [Radix Slot](https://github.com/radix-ui/primitives/tree/main/packages/react/slot)
+
+---
+
+## Portal
+
+**What:** Render children into `document.body` (or custom container).
+
+**Why:** Popups need to escape parent overflow/stacking contexts.
+
+**Implementation:** `createPortal(children, container)` after mount.
+
+**Ref:** [React createPortal](https://react.dev/reference/react-dom/createPortal)
+
+---
+
+## Server Components
+
+**What:** Compound components are Client Components (use hooks, events).
+
+**Why:** Interactive components can't be Server Components.
+
+**Pattern:** Mark with `'use client'` directive. Server Components can compose them.
+
+---
+
+## TypeScript Patterns
+
+### Namespaced Types
+
+**What:** Export types under component namespace.
+
+**Why:** Clean imports — `Dialog.RootProps`, `Dialog.TriggerProps`
+
+**Pattern:**
+
+```tsx
+export namespace Dialog {
+ export interface RootProps { ... }
+ export interface TriggerProps { ... }
+}
+```
+
+---
+
+### Generic Collections
+
+**What:** Collection components generic over item type.
+
+**Why:** Type-safe `value` and `onValueChange` for any item type.
+
+**Pattern:** `function Select({ value, onValueChange }: SelectProps)`
+
+---
+
+### Polymorphic Components
+
+**What:** Components accepting `as` prop with full type inference.
+
+**Why:** `` with correct HTML attributes.
+
+**Ref:** [Radix Polymorphic](https://github.com/radix-ui/primitives/tree/main/packages/react/polymorphic)
+
+---
+
+## Performance
+
+### Context Splitting
+
+**What:** Separate contexts by update frequency.
+
+**Why:** Prevent unnecessary re-renders — highlight changes shouldn't re-render entire tree.
+
+**Pattern:** `MenuStateContext` (stable) vs `MenuHighlightContext` (frequent updates)
+
+---
+
+### Memoization
+
+**What:** `useMemo` for context values, `memo` for parts.
+
+**Why:** Stable references prevent child re-renders.
+
+---
+
+## Export Pattern
+
+**What:** Named exports as namespace object.
+
+**Pattern:**
+
+```tsx
+// dialog/index.ts
+export { DialogRoot as Root } from './root';
+export { DialogTrigger as Trigger } from './trigger';
+// Usage: import * as Dialog from './dialog';
+```
+
+---
+
+## Implementation Sources
+
+| Pattern | Reference |
+| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
+| Context + Compound | [Base UI Dialog](https://github.com/mui/base-ui/tree/master/packages/react/src/dialog) |
+| Controlled State | [Radix useControllableState](https://github.com/radix-ui/primitives/blob/main/packages/react/use-controllable-state) |
+| Focus Trap | [focus-trap-react](https://github.com/focus-trap/focus-trap-react) |
+| Roving Focus | [Radix RovingFocus](https://github.com/radix-ui/primitives/tree/main/packages/react/roving-focus) |
+| Floating | [Floating UI React](https://floating-ui.com/docs/react) |
+| Merge Props | [Base UI mergeProps](https://github.com/mui/base-ui/blob/master/packages/react/src/merge-props/mergeProps.ts) |
+
+---
+
+## See Also
+
+- [aria/react.md](../../aria/references/react.md) — React accessibility patterns (focus scope, announcements, a11y testing)
diff --git a/.claude/skills/component/references/styling.md b/.claude/skills/component/references/styling.md
new file mode 100644
index 00000000..a8f5e425
--- /dev/null
+++ b/.claude/skills/component/references/styling.md
@@ -0,0 +1,191 @@
+# Styling Patterns
+
+Style headless components with CSS classes, data attributes, and CSS variables.
+
+**Reference:** [Base UI Styling Handbook](https://base-ui.com/react/handbook/styling)
+
+---
+
+## Style Hooks
+
+### className as Function
+
+Access component state for conditional classes:
+
+```tsx
+ (state.checked ? 'checked' : 'unchecked')} />
+```
+
+### style as Function
+
+Access component state for conditional inline styles:
+
+```tsx
+ ({
+ transform: state.checked ? 'translateX(20px)' : 'translateX(0)',
+ })}
+/>
+```
+
+---
+
+## Data Attributes
+
+Target component states with CSS selectors.
+
+### Standard Attributes
+
+| Attribute | Values | Purpose |
+| --------------------------------- | --------------------------------------------------------- | ----------------------- |
+| `data-state` | `open`, `closed`, `checked`, `unchecked`, `indeterminate` | Primary state |
+| `data-open` / `data-closed` | present/absent | Visibility state |
+| `data-checked` / `data-unchecked` | present/absent | Toggle state |
+| `data-disabled` | present/absent | Disabled state |
+| `data-highlighted` | present/absent | Focus/hover in groups |
+| `data-popup-open` | present/absent | Trigger when popup open |
+| `data-side` | `top`, `bottom`, `left`, `right` | Popup positioning |
+| `data-align` | `start`, `center`, `end` | Popup alignment |
+| `data-orientation` | `horizontal`, `vertical` | Layout direction |
+| `data-starting-style` | present/absent | Enter transition state |
+| `data-ending-style` | present/absent | Exit transition state |
+
+### CSS Usage
+
+```css
+.switch[data-checked] {
+ background-color: var(--color-primary);
+}
+
+.switch[data-disabled] {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+[data-highlighted] {
+ background: var(--color-highlight);
+}
+```
+
+### Attribute Rules
+
+- Boolean: present = true, absent = false
+- Use `undefined` to omit (not `false`)
+- Consistent naming across components
+
+---
+
+## CSS Variables
+
+Dynamic values exposed for positioning and sizing.
+
+### Common Variables
+
+| Variable | Purpose |
+| -------------------- | ------------------------------- |
+| `--available-height` | Max height before viewport edge |
+| `--available-width` | Max width before viewport edge |
+| `--anchor-width` | Width of anchor element |
+| `--anchor-height` | Height of anchor element |
+| `--transform-origin` | Calculated transform origin |
+
+### Component-Specific Variables
+
+| Component | Variable | Purpose |
+| --------- | ---------------------------- | ---------------------- |
+| Accordion | `--accordion-content-height` | Animated height |
+| Select | `--select-trigger-width` | Match popup to trigger |
+| Slider | `--slider-thumb-transform` | Thumb position |
+
+### Usage
+
+```css
+/* Constrain popup to viewport */
+.popup {
+ max-height: var(--available-height);
+ overflow-y: auto;
+}
+
+/* Match popup width to trigger */
+.select-content {
+ width: var(--anchor-width);
+}
+
+/* Scale from anchor */
+.popup {
+ transform-origin: var(--transform-origin);
+}
+```
+
+---
+
+## Framework Integration
+
+### Tailwind CSS
+
+Data attributes work with Tailwind's `data-*` variants:
+
+```tsx
+
+ Copy
+
+```
+
+### CSS Modules
+
+```tsx
+import styles from './menu.module.css';
+
+Copy;
+```
+
+```css
+/* menu.module.css */
+.item[data-highlighted] {
+ background-color: var(--blue-100);
+}
+```
+
+### CSS-in-JS
+
+Wrap component parts with styled():
+
+```tsx
+const StyledMenuItem = styled(Menu.Item)`
+ &[data-highlighted] {
+ background-color: var(--blue-100);
+ }
+`;
+```
+
+---
+
+## Guidelines
+
+| Use | For |
+| ------------------ | ------------------------------------------------ |
+| Data attributes | Discrete states (open/closed, checked, disabled) |
+| CSS variables | Continuous values (heights, widths, positions) |
+| className function | State-dependent class composition |
+| style function | State-dependent inline styles |
+
+**Avoid:**
+
+- Inline styles for state (fights theming)
+- Shipping CSS in core (specificity conflicts)
+- Non-standard attribute names
+
+---
+
+## See Also
+
+- [Animation](animation.md) — animating state transitions
+- [Anti-Patterns](anti-patterns.md) — styling mistakes
diff --git a/.claude/skills/component/review/checklist.md b/.claude/skills/component/review/checklist.md
new file mode 100644
index 00000000..5714e8ea
--- /dev/null
+++ b/.claude/skills/component/review/checklist.md
@@ -0,0 +1,334 @@
+# Component Review Checklist
+
+Comprehensive checklist for reviewing UI components against architecture patterns and conventions.
+
+---
+
+## Architecture
+
+### Compound Components
+
+- [ ] Component uses compound structure (Root, Trigger, Content, etc.)
+- [ ] Each part maps 1:1 to a DOM element
+- [ ] Parts can be reordered or omitted freely
+- [ ] No prop explosion (>10 props suggests need for decomposition)
+
+**Detection:** Single component with many configuration props
+
+```tsx
+// BAD: Prop explosion
+
+
+// GOOD: Compound
+
+
+
+ ...
+
+
+
+```
+
+### Standard Hierarchies
+
+| Type | Expected Parts |
+| ----------- | ---------------------------------------------------- |
+| Popups | Root → Trigger → Portal → Positioner → Popup → Arrow |
+| Collections | Root → List → Trigger + Panel |
+| Forms | Root → Label → Control → Description → Error |
+
+### Context Scoping
+
+- [ ] Each Root creates isolated context
+- [ ] Nested instances don't interfere
+
+**Detection:** Nested components share unintended state
+
+---
+
+## State Management
+
+### Controlled & Uncontrolled Support
+
+- [ ] Both modes supported: `value` (controlled) and `defaultValue` (uncontrolled)
+- [ ] Works correctly in either mode
+- [ ] No state desync between modes
+
+**Detection:** Only `defaultValue` or only `value` supported
+
+| State | Uncontrolled | Controlled | Handler |
+| ------- | ---------------- | ---------- | ----------------- |
+| Open | `defaultOpen` | `open` | `onOpenChange` |
+| Value | `defaultValue` | `value` | `onValueChange` |
+| Checked | `defaultChecked` | `checked` | `onCheckedChange` |
+
+### Change Event Details
+
+- [ ] Handler receives value and details object
+- [ ] Details includes `reason` (click, keyboard, blur, escape, etc.)
+- [ ] Details includes `event` (original DOM event)
+- [ ] Details includes `cancel()` for preventing change
+
+**Detection:** Handler receives only value, no context
+
+```typescript
+// BAD
+onOpenChange?: (open: boolean) => void;
+
+// GOOD
+onOpenChange?: (open: boolean, details: ChangeDetails) => void;
+```
+
+### Imperative Actions
+
+- [ ] `actionsRef` prop exposes imperative methods where needed
+- [ ] Common actions: `open()`, `close()`, `toggle()`, `focus()`
+
+---
+
+## Props & API
+
+### Boolean Props
+
+- [ ] Use positive adjectives: `disabled`, `required`, `open`
+- [ ] Avoid `is`/`has` prefixes: not `isDisabled`, `isOpen`
+
+| Good | Avoid |
+| ---------- | ------------ |
+| `disabled` | `isDisabled` |
+| `open` | `isOpen` |
+| `loading` | `isLoading` |
+
+### Event Handler Naming
+
+- [ ] Pattern: `on` + Noun + Verb
+- [ ] Specific names over generic: `onValueChange` not `onChange`
+
+| Good | Avoid |
+| --------------- | ----------------------- |
+| `onOpenChange` | `handleOpen`, `setOpen` |
+| `onValueChange` | `onChange` |
+| `onSelect` | `onItemSelected` |
+
+### Standard Props by Category
+
+**Interaction:**
+
+- [ ] `disabled?: boolean`
+- [ ] `required?: boolean`
+- [ ] `readOnly?: boolean`
+
+**Collections:**
+
+- [ ] `multiple?: boolean`
+- [ ] `loopFocus?: boolean`
+- [ ] `orientation?: 'horizontal' | 'vertical'`
+
+**Popups:**
+
+- [ ] `modal?: boolean`
+- [ ] `closeOnEscape?: boolean`
+- [ ] `closeOnOutsideClick?: boolean`
+- [ ] `keepMounted?: boolean`
+
+### Refs
+
+- [ ] Ref forwarded to root DOM element
+- [ ] Parent can access DOM for focus, measurement
+
+**Detection:** `forwardRef` not used
+
+```tsx
+// BAD
+const Button = ({ children }) => {children};
+
+// GOOD
+const Button = forwardRef(({ children, ...props }, ref) => (
+
+ {children}
+
+));
+```
+
+### Polymorphism
+
+- [ ] Uses `render` prop or `asChild`, not `as` prop
+- [ ] `render` function receives props and state
+- [ ] State accessible for conditional rendering
+
+**Detection:** Component has `as` prop
+
+```tsx
+// BAD: TypeScript performance issues
+
+
+// GOOD: render prop
+}>
+
+// GOOD: asChild
+
+ Link
+
+```
+
+---
+
+## Data Attributes & Styling
+
+### State via Data Attributes
+
+- [ ] State exposed via `data-*` attributes
+- [ ] Enables CSS-only styling without JS
+
+**Detection:** Inline styles for state, no data attributes
+
+| Attribute | When Present |
+| ------------------ | --------------------------- |
+| `data-open` | Component is open |
+| `data-closed` | Component is closed |
+| `data-checked` | Toggle is checked |
+| `data-disabled` | Component is disabled |
+| `data-highlighted` | Item has focus within group |
+| `data-side` | Popup position side |
+| `data-align` | Popup alignment |
+
+```tsx
+// BAD
+
+
+// GOOD
+
+```
+
+### CSS Variables
+
+- [ ] CSS variables documented for customization
+- [ ] Standard variables: `--available-height`, `--anchor-width`, `--transform-origin`
+
+### No Shipped CSS
+
+- [ ] Component is headless — no CSS imported
+- [ ] User brings their own styles
+
+**Detection:** `import 'component/styles.css'` in component
+
+---
+
+## Animation Support
+
+### Exit Animations
+
+- [ ] Exit animation possible (element not immediately unmounted)
+- [ ] `data-state="open"` / `data-state="closed"` for CSS transitions
+- [ ] `keepMounted` option for JS animation libraries
+
+**Detection:** `{open && }` pattern without animation support
+
+```tsx
+// BAD: No exit animation possible
+{open && ...}
+
+// GOOD: Data attributes for CSS
+
+
+// GOOD: keepMounted for JS animation
+
+
+ {open && }
+
+
+```
+
+---
+
+## SSR Safety
+
+- [ ] No `document` or `window` at module scope
+- [ ] Portals handle SSR (render fallback or wait for mount)
+- [ ] IDs generated safely (no Math.random at module level)
+
+**Detection:** `document.body` reference outside effect
+
+```tsx
+// BAD
+function Portal({ children }) {
+ return createPortal(children, document.body);
+}
+
+// GOOD
+function Portal({ children }) {
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => setMounted(true), []);
+ if (!mounted) return <>{children}>;
+ return createPortal(children, document.body);
+}
+```
+
+---
+
+## Accessibility
+
+For full accessibility review, load the `aria` skill and run `review/workflow.md`.
+
+Quick checks:
+
+- [ ] All interactive elements have accessible names
+- [ ] Keyboard navigation works
+- [ ] Focus managed for modals (trapped, restored)
+- [ ] ARIA attributes reflect component state
+
+---
+
+## Anti-Pattern Summary
+
+| Anti-Pattern | Detection | Fix |
+| ------------------- | ------------------------------------- | ----------------------------- |
+| Prop explosion | >10 props on one component | Use compound components |
+| Inline state styles | `style={{ opacity: disabled ? ... }}` | Use data attributes |
+| Shipped CSS | `import 'styles.css'` in component | Headless, user brings styles |
+| `as` prop | `` | Use `render` or `asChild` |
+| Missing controlled | Only `defaultValue` | Add `value` + `onValueChange` |
+| Context collision | Nested instances share state | Scope contexts per Root |
+| No exit animation | `{open && ...}` without `keepMounted` | Add animation support |
+| SSR unsafe | `document.body` at module scope | Guard with mount check |
+| No ref forwarding | Missing `forwardRef` | Forward ref to DOM |
+
+---
+
+## Severity Guide
+
+### Critical
+
+| Issue | Why |
+| ---------------------------- | ----------------------------------- |
+| Missing controlled support | Can't integrate with external state |
+| Context collision in nesting | Breaks composition |
+| SSR crash | Breaks server rendering |
+| Memory leak (no cleanup) | Production issue |
+
+### Major
+
+| Issue | Why |
+| ------------------------- | -------------------- |
+| Prop explosion | Poor DX, inflexible |
+| Missing data attributes | Can't style with CSS |
+| No exit animation support | Poor UX |
+| Boolean trap | Confusing API |
+| Missing ref forwarding | Can't access DOM |
+
+### Minor
+
+| Issue | Why |
+| --------------------- | ------------------- |
+| Inconsistent naming | API inconsistency |
+| Missing CSS variables | Harder to customize |
+| Verbose handler names | Minor DX issue |
+
+---
+
+## See Also
+
+- [Anti-Patterns](../references/anti-patterns.md) — Full anti-pattern reference
+- [Props](../references/props.md) — Prop naming conventions
+- [Styling](../references/styling.md) — Data attributes and CSS variables
+- [Accessibility Checklist](../../aria/review/checklist.md) — Full a11y checklist
diff --git a/.claude/skills/component/review/templates.md b/.claude/skills/component/review/templates.md
new file mode 100644
index 00000000..8b8bf392
--- /dev/null
+++ b/.claude/skills/component/review/templates.md
@@ -0,0 +1,232 @@
+# Review Templates
+
+Issue format and report templates for component reviews.
+
+## Issue Format
+
+```markdown
+### [SEVERITY] Issue title
+
+**What:** Brief description
+**Where:** `path/to/file.ts:42`
+**Why:** Impact on users/developers
+**Pattern:** Which pattern violated
+**Fix:** Concrete suggestion
+
+// Before
+problematic()
+
+// After
+improved()
+```
+
+## Severity Levels
+
+| Level | Meaning | Examples |
+| ---------- | ---------------------------- | ---------------------------------------------------------- |
+| `CRITICAL` | Breaks users, blocks release | Wrong state model, missing controlled support, memory leak |
+| `MAJOR` | Significant DX issue | Boolean trap, inconsistent naming, missing data attributes |
+| `MINOR` | Improvement opportunity | Verbose API, missing CSS variable, could use prop getter |
+| `NIT` | Polish, optional | Naming consistency, minor ergonomics |
+
+---
+
+## Issue Examples
+
+### CRITICAL — Missing controlled support
+
+```markdown
+### [CRITICAL] Component only supports uncontrolled state
+
+**What:** No `value` prop, only `defaultValue`
+**Where:** `src/ui/slider/slider.ts:15`
+**Why:** Cannot integrate with external state management
+**Pattern:** Controlled & Uncontrolled State (SKILL.md Pattern 2)
+**Fix:** Add controlled state support
+
+// Before
+interface SliderProps {
+defaultValue?: number;
+onChange?: (value: number) => void;
+}
+
+// After
+interface SliderProps {
+value?: number;
+defaultValue?: number;
+onValueChange?: (value: number, details: ChangeDetails) => void;
+}
+```
+
+### MAJOR — Boolean trap
+
+```markdown
+### [MAJOR] Boolean parameter with unclear meaning
+
+**What:** Positional boolean in component API
+**Where:** `src/ui/menu/menu.ts:8`
+**Why:** `