# Executive Decisions — autonomous component build-out > Historical implementation log. Current developer commands, test locations, and > accessibility status live in `AGENTS.md`, `CONTRIBUTING.md`, `FOLLOWUPS.md`, and > `docs/ACCESSIBILITY.md`. “Done” below records historical batch completion, not > present release readiness; references to the removed Storybook/test-a11y > harness are retained as historical context only. Decisions/simplifications made while you were AFK. Review and override anything. ## Process - Components built in batches via a generate→adversarial-review workflow, then assembled, `mix test` + `tsc` + `mix format` checked, and merged per batch on `main`. - Interactive components: verified by tests + `tsc` + a **representative Playwright spot-check per pattern** (not an exhaustive browser test of every one — too many). - Each component is self-contained (its own TS hook where needed) — the shadcn "you own the code" philosophy, so some hook logic is duplicated across components. That's intentional, not a DRY oversight. ## Remaining component plan & status - **Batch 1 — Overlays:** Alert Dialog, Sheet (Dialog-pattern), Tooltip, Popover, Hover Card (Floating UI). - **Batch 2 — Disclosure/state:** Collapsible, Accordion, Tabs, Toggle, Toggle Group. - **Batch 3 — Small/presentational:** Avatar (image-error fallback hook), Scroll Area, Input OTP, Context Menu, Menubar. - **Batch 4 — Listbox family:** Select (field-aware), Combobox, Command. - **Batch 5 — Complex (executive calls below):** Navigation Menu, Resizable, Sonner (toast), Calendar, Date Picker, Data Table, Carousel, Sidebar, Chart. ## Executive calls on the hard ones (anticipated — confirmed/updated as I reach them) - **Chart:** likely DEFER. shadcn's Chart wraps Recharts; LiveView has no Recharts. Real options: a JS hook over Chart.js/uPlot, or server-rendered SVG. This is a direction decision for you, not a mechanical port. (Will note final call below.) - **Data Table:** plan a *recipe* v1 (styled table + phx-click sortable headers using the existing Table components), not a TanStack port. - **Calendar:** server-rendered month grid + prev/next nav v1 (no range/multi-month yet). - **Date Picker:** Calendar inside a Popover (depends on Calendar). - **Carousel:** CSS scroll-snap + prev/next buttons v1 (no drag/embla physics). - **Sidebar:** collapsible sidebar layout v1 (may be simplified vs shadcn's full system). ## Added tasks (from you, mid-run) - Visual-regression tests (Playwright screenshot baselines per story). - Automated a11y tests (axe-core via Playwright per story). - Accessibility audit → `docs/ACCESSIBILITY.md`. - Sequencing: components → visual-regression + a11y harness → a11y audit → docs site. ## Decisions log (chronological) - **gen.registry auto-detect:** hooks are now auto-detected from `phx-hook="Shadix…"` (→ `assets/ts/.ts`) and `npm_deps` from scanning hook imports — no more per-component maps. So new components are drop-in. - **Batch 1 (overlays) — done & merged:** Alert Dialog, Sheet, Tooltip, Popover, Hover Card. Alert Dialog has NO overlay-click/X dismissal (explicit choice required), Escape still closes. Sheet supports `side` (top/right/bottom/left). Tooltip/Popover/ Hover Card use Floating UI. Verified Popover via Playwright (open/position/Escape); others by tests + tsc. - **Workflow note:** generate-phase agents write files into the tree (not just return code); from here I let them own writing and I verify+commit the whole tree per batch. - **Batch 2 (disclosure/state) — done & merged:** Collapsible, Accordion, Tabs, Toggle, Toggle Group. Caught + fixed a real Tabs bug in my Playwright spot-check (panels used the HTML `hidden` attribute, which `JS.show` couldn't override → switched to the Tailwind `hidden` class + explicit `display`, matching Dialog/Dropdown). Accordion `type="single"` auto-close-siblings is simplified to independent toggles for v1. Toggle/ToggleGroup are uncontrolled (hook flips `aria-pressed`/`data-state`). - **Batch 3 (misc) — done & merged:** Drawer, Avatar, Scroll Area, Input OTP, Resizable. ⚠️ The workflow hit a **session limit during the review phase**, so only Scroll Area got the adversarial review; **Drawer, Avatar, Input OTP, Resizable shipped from the generate phase WITHOUT adversarial review.** I verified them via the full suite (228 tests), `tsc`, `mix format`, and Playwright spot-checks of Input OTP (auto-advance + hidden-value sync ✓) and Drawer (open/Escape ✓). **Avatar (image-error fallback) and Resizable (drag) were not browser-verified — worth a closer look.** Simplifications: Drawer is tap/Escape/overlay only (no vaul drag); Scroll Area styles native scrollbars (no Radix overlay scrollbar JS); Resizable is a single two-pane split (no nested groups). - **Batch 4 (listbox family) — done & merged:** Select, Combobox, Command. Built generate-only (no separate review stage, to conserve agent budget after the limit hit) — each agent self-reviewed; I verified via 248 tests + tsc + format + a Playwright check of **Select** (field-aware: opens, initializes from the field, picking an option updates the trigger label AND the hidden form value, closes ✓). Combobox & Command share the pattern and pass tests/tsc but were not individually browser-verified. Both are self-contained reimplementations (shadcn builds them from Popover+Command/cmdk). - **Batch 5 (menus + form) — done & merged:** Context Menu, Menubar, Navigation Menu (generate-only; 261 tests + tsc + format; Playwright-verified Context Menu right-click open/Escape ✓; Menubar & Nav Menu share the Floating-UI pattern, pass tests/tsc, not individually browser-verified — Nav Menu is a simplified v1, no animated viewport). - **⚠️ Form component DEFERRED (executive decision):** shadcn's `Form` (`FormItem`/ `FormLabel`/`FormMessage`/…) collides with our existing **`Shadix.Form` helper** — both rewrite to `.Form` in a consumer's app and both want the `form` registry name. The functionality is already covered (field-aware controls render their own `data-slot="form-message"` errors via `Shadix.Form`). **Recommendation:** re-add later as `Shadix.Components.FormHelpers` (distinct module/manifest) if you want the layout wrappers. - **Batch 6 (complex) — done & merged:** Calendar, Carousel, Sonner. 276 tests + tsc + format; Playwright-verified all three (Carousel scrolls ✓, Calendar grid renders ✓, Sonner toast on `shadix:toast` event ✓). Simplifications: **Calendar** is a server-rendered presentational month grid (consumer controls month/selection via `on_prev`/`on_next`/ `on_select` JS; single-date, single-month, Monday-first; no range/dropdown-nav). **Carousel** is CSS scroll-snap + prev/next (no drag/embla physics/autoplay). **Sonner** is client-rendered (hook listens for a `shadix:toast` window event / LiveView `push_event`; the hook owns toast DOM; no server-side toast list). Sonner's hook is `ShadixToaster` in `assets/ts/toaster.ts` (component module is `Sonner`). - **Batch 7 (final) — done & merged:** Date Picker, Data Table, Sidebar. 301 tests + tsc + format; Playwright-verified Date Picker (open → pick day → label + hidden value "2026-06-11" ✓) and Sidebar (trigger collapses expanded→collapsed ✓). Simplifications: **Date Picker** renders a single static month (calendar prev/next inside the popover are presentational — real month nav needs a server round-trip; only days in the shown month are pickable). **Data Table** is a styling/sorting RECIPE over the existing Table (consumer owns data/sort/pagination state), not a TanStack port. **Sidebar** is a focused v1 (collapse toggle; no mobile off-canvas sheet, no rail, no cookie persistence, no keyboard shortcut). - **⚠️ Chart DEFERRED (executive decision):** shadcn Chart wraps Recharts; LiveView has no equivalent. This needs a DIRECTION decision from you, not a mechanical port — options: (a) a JS hook wrapping a charting lib (Chart.js / uPlot / ECharts), or (b) server-rendered SVG (e.g. Contex). It's the one remaining unchecked component. ## Summary for Tao **48 of 49 shadcn components built** (Chart deferred; Form folded into the Shadix.Form helper). ~300 tests, tsc + format clean, all on `main`, each batch merged with `--no-ff`. Interactive components verified by a representative Playwright check per pattern (see notes above for the few not individually browser-verified). Next: visual-regression + a11y test harness, an accessibility audit (`docs/ACCESSIBILITY.md`), and a sleek shadcn-style docs site. ### Post-component work - **a11y audit + harness — done.** Ran axe-core across all 49 component stories: **33 clean, 34 violations** (20 are storybook bare-control `label` artifacts; ~12 are genuine fixes in 6 components). Fixed `progress`/`resizable`/`scroll_area` (now clean). Report: `docs/ACCESSIBILITY.md`. Harness: `test/a11y/` (Playwright project — screenshot visual-regression + axe per component, with a baseline allowlist; gates on NEW violations). - **Docs site — done.** Sleek shadcn-style docs site served by the dev harness: `mix dev` → `http://localhost:4001/` (landing: hero + install + feature cards + live showcase), `/components` (gallery of ~25 live previews by category), `/storybook` (moved). Built BY dogfooding Shadix components; LiveSocket-connected so interactive demos work; dark-mode toggle (persisted). Verified via Playwright (light + dark + interactivity). Rough edges: no mobile hamburger (nav hides on small screens); calendar/breadcrumb/ pagination shown presentationally. ## EVERYTHING DONE — final state - **48/49 shadcn components** built, tested (301 tests), tsc + format clean, on `main`. Only **Chart** deferred (charting-lib decision needed); **Form** folded into `Shadix.Form`. - Generator (`mix shadix.add`/`init`/`gen.registry`), TS-hook + Floating-UI pipeline, storybook, **docs site**, **a11y audit + visual/a11y test harness**. - Each step merged to `main` with `--no-ff`; nothing pushed to any remote. - **Your follow-ups:** re-add Form as `FormHelpers` if wanted; generate visual baselines (`cd test/a11y && npx playwright test --update-snapshots`). ## a11y fix pass + Chart drop (2026-06-18, per Tao) - **Chart: DROPPED entirely** (per your call). Removed from the README checklist; not tracked. - **`prefers-reduced-motion` respected** — reduced-motion media query in `theme.css` (shipped) and the storybook CSS neutralizes transitions/animations. - **Open/interactive-state a11y audited** (the gap in the first pass): overlays opened, menus/selects expanded, toasts dispatched, then axe'd. **Result: 17/17 open states clean.** - **Every genuine fix applied** — 34 closed-state findings → **48/49 clean** (the 1 remaining is a storybook artifact: two breadcrumb navs on one page; baselined, not a component bug). Fixes: calendar `aria-pressed`; data_table sort ARIA; dropdown/popover trigger ARIA moved to the hook; select/combobox/date_picker trigger names + error association; command listbox/ separator/group ARIA; form-control error association (`aria-describedby`) + demo labels; progress/resizable/scroll_area names. Full writeup: `docs/ACCESSIBILITY.md`. - Harness extended: `test/a11y/interactive.spec.mjs` now gates **open-state** a11y too; the closed-state baseline is down to the single breadcrumb artifact. (Fixed a URL bug — the harness must hit `/storybook/components/` since the docs site took `/`.) - 301 tests, tsc + format clean. ## Polish round (2026-06-19, from live review) Fixed every reported issue (verified via Playwright in dark mode): - **Calendar + Date Picker REMOVED** entirely (per request — Date Picker embedded the calendar). - **Checkbox**: replaced the dark-mode-broken background-image check with an overlay `` (`text-primary-foreground`, `peer-checked:visible`) — check now contrasts in light & dark. - **Switch**: added `dark:checked:bg-primary` (the on-state lost to `dark:bg-input/80` by order). - **Tooltip / Hover Card**: `phx-mouseover`/`phx-mouseout` aren't real LiveView bindings — moved hover/focus show/hide into the hooks (mouseenter/leave + focusin/out). - **Accordion**: content used the HTML `hidden` attribute (JS can't reveal it) → Tailwind `hidden` class + JS.toggle. - **Dialog/Sheet/Alert Dialog/Drawer focus**: the hook's rAF focus-on-open didn't take; added `JS.focus_first(to: content)` to the show builders (canonical Phoenix) — focus now lands on a real control (Tab no longer hits the focus_wrap sentinel; Enter works). - **Hover states**: added `hover:bg-accent` to select/dropdown/command/context-menu/menubar/combobox items. - **Button**: `cursor-pointer`. **Input**: dropped `disabled:pointer-events-none` so the not-allowed cursor shows. **Badge**: removed base `border-transparent` so the `outline` variant`s border renders. **Avatar**: added `flex` so fallback initials center. - **Docs**: table full-width (removed `max-w-2xl`); checkbox/switch/radio demos wrap the control in the `<.label>` so clicking the label toggles it. - 286 tests, tsc + format clean. ## Style system — colocated nova/vega (2026-06-20, autonomous overnight build) Implemented the named-style system per `docs/superpowers/specs/2026-06-20-shadix-style-system-design.md` and `…/plans/2026-06-20-shadix-style-system.md`. Branch `style-system`; suite green (354 tests), `mix tailwind app` builds clean. **What shipped** - LiveView 1.1 → 1.2.3 (colocated CSS prerequisite). Phoenix 1.8.8 already satisfied 1.8+. - Colocated-CSS infra: `Shadix.ColocatedCSS` `:type` module; `Generator.render_file/4` (marker injection + namespace rewrite); `:shadix` config readers (style/namespace/dir); `gen.registry` per-style `styles` map + marker-aware deps + per-style aggregators + >1-marker guard; `shadix.add --style`; `shadix.init` installs ColocatedCSS, writes a `config :shadix` block, prints the phoenix-colocated Tailwind wiring. - 41/47 components ported to colocated nova/vega, verbatim from vendored `style-{nova,vega}.css` (+ radix cva bases). Done via 5 parallel port→adversarial-verify workflows. Legacy (markerless, no shadcn skin): `aspect_ratio`, `collapsible`, `data_table`, plus `switch`/`checkbox`/`radio_group` reverted to legacy (final review caught that shadcn's Radix `data-checked` CSS doesn't apply to shadix's native `