docs(design): add tabs / inline banners / compact-table-density to DESIGN.md

Adds:
- Navigation > Sub-page tabs subsection (tab-underline recipes, count pill, ARIA)
- Data Tables: row min-height 44px -> 32px + WCAG SC 2.5.5 rationale
- Toast Notifications > Inline Banners subsection (4 variants table)
Also adds Sprint 11 entry to CHANGELOG.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-06-21 22:11:38 +02:00
parent 5d34a5e171
commit 1324a0c8ce
2 changed files with 56 additions and 1 deletions

View File

@@ -6,6 +6,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
## [Unreleased] ## [Unreleased]
### Added — Sprint 11 (Spectrum UX port: 4 primitives + compact density global)
**Frontend only** (233 vitest passing — baseline 212 + 21 new tests across 4 new specs)
- `frontend/src/hooks/useHashTab.ts` — NEW. Pure-TS hook that reads `window.location.hash`, falls back to a default tab id, and listens to `hashchange` (cleanup on unmount). No style dependency.
- `frontend/src/components/Tabs.tsx` — NEW. `<Tabs items activeId onChange>` component. Underline variant: `tab-underline` / `tab-underline-active` CSS recipes. Count pill: `tab-count-pill` / `tab-count-pill-active` (`rounded-pill` exception per DESIGN.md). `role="tablist"` + `aria-selected` on tabs. Zero transitions.
- `frontend/src/components/AlertBanner.tsx` — NEW. `<AlertBanner variant="error|warn|success|info" title? children>`. Border-l-4 semantic strip, `bg-paper`, Lucide icon at `size={16}`. ARIA: `role="alert"` for error/warn, `role="status"` for success/info. No shadow, no radius, no transition.
- `frontend/src/components/BackLink.tsx` — NEW. `<BackLink to children>` renders `ArrowLeft` (Lucide, 14px) + text with `caption-md text-graphite hover:text-primary`. Replaces 3 hand-rolled back-link instances.
- `frontend/src/styles/index.css` — Added recipes: `.tab-underline`, `.tab-underline-active`, `.tab-count-pill`, `.tab-count-pill-active`, `.alert-error`, `.alert-warn`, `.alert-success`, `.alert-info`, `.table-compact` (32px row density, global).
- `frontend/src/pages/EngagementDetailPage.tsx` — Refactored to 3-tab layout (Schedule / Description / Simulations) wired via `useHashTab('schedule')`. Count pill on Simulations tab. BackLink replaces hand-rolled link.
- `frontend/src/pages/SimulationFormPage.tsx` — Done banner and SOC-blocked banner migrated to `<AlertBanner>`. Both back-link instances replaced with `<BackLink>`.
- `frontend/src/pages/TemplateFormPage.tsx` — Back-link replaced with `<BackLink>`.
- `frontend/src/pages/EngagementsListPage.tsx` — Table marked `table-compact`, inline `px-xl py-md` padding removed from th/td (recipe handles it).
- `frontend/src/pages/TemplatesListPage.tsx` — Same compact table treatment.
- `frontend/src/pages/UsersAdminPage.tsx` — Same compact table treatment.
- `frontend/src/components/SimulationList.tsx` — Same compact table treatment.
- `DESIGN.md` — Added `### Navigation Sub-page tabs` subsection; updated `### Data Tables` row min-height 44px → 32px with WCAG SC 2.5.5 rationale; added `### Inline Banners` subsection under `### Toast Notifications`.
**Tests added** (21 new assertions across 4 new spec files):
- `tests/hooks/useHashTab.test.tsx` — 4 tests (default, hash read on mount, navigate(), empty-hash fallback)
- `tests/components/Tabs.test.tsx` — 7 tests (render, aria-selected, active class, count pill, count-pill-active, onChange, brutalism)
- `tests/components/AlertBanner.test.tsx` — 6 tests (4 variants × role + class, title prop, brutalism)
- `tests/components/BackLink.test.tsx` — 4 tests (render, href, brutalism, svg icon)
**No backend changes.** No DB schema change. No migration.
### Changed — Sprint 7 (Terminal-SOC design refresh) ### Changed — Sprint 7 (Terminal-SOC design refresh)
**Frontend** (136 vitest passing — unchanged count, 3 assertions updated for new token names) **Frontend** (136 vitest passing — unchanged count, 3 assertions updated for new token names)

View File

@@ -216,11 +216,23 @@ Shadows (`box-shadow`) are not used anywhere.
- Text `{colors.slab-text}`, caption-md, `rounded-none` - Text `{colors.slab-text}`, caption-md, `rounded-none`
- Active state: 2px primary bottom border - Active state: 2px primary bottom border
#### Sub-page tabs
Used inside pages that need content partitioning without a URL change (e.g. EngagementDetailPage: Schedule / Description / Simulations).
- **Container**: `flex items-end border-b border-hairline gap-xs` — sits on the `{colors.hairline}` bottom line.
- **`.tab-underline`**: `text-graphite caption-bold cursor-pointer border-b-2 border-transparent hover:text-ink px-xs`. Hover is **instant** — no `transition-*`.
- **`.tab-underline-active`**: `text-primary border-primary` — 2px primary underline, primary text.
- **Count pill** (optional, e.g. simulation count): `.tab-count-pill``rounded-pill bg-cloud text-graphite text-[11px] px-xs py-0 font-mono`. `rounded-pill` is the documented exception.
- **Active count pill**: `.tab-count-pill-active``bg-primary-soft text-primary`.
- ARIA: `role="tablist"` on container; `role="tab"` + `aria-selected` on each button.
### Data Tables ### Data Tables
- `table-layout: fixed`, `word-break: break-word` - `table-layout: fixed`, `word-break: break-word`
- Header row: background `{colors.cloud}`, `body-emphasis` text, 1px `{colors.hairline}` bottom border - Header row: background `{colors.cloud}`, `body-emphasis` text, 1px `{colors.hairline}` bottom border
- Data cells: 44px min-height, 1px `{colors.hairline}` bottom border - Data cells: **32px min-height** (compact density, all surfaces). Class: `.table-compact`.
- **WCAG SC 2.5.5 tradeoff**: the recommended 44×44px touch target is intentionally not met on data rows. Mimic is a BAS operator console used at desktop on mouse/keyboard — density and scanability take priority over touch target size. Action buttons within rows retain icon ≥ 24px with adequate padding to preserve a usable hit area.
- ID / date / technique columns: `font-mono` - ID / date / technique columns: `font-mono`
- Zebra striping optional — use `{colors.cloud}` for odd rows if table is wide - Zebra striping optional — use `{colors.cloud}` for odd rows if table is wide
@@ -229,6 +241,23 @@ Shadows (`box-shadow`) are not used anywhere.
- Angular (`rounded-none`), 4px left border strip in semantic color (success/warn/bloom-deep/primary) - Angular (`rounded-none`), 4px left border strip in semantic color (success/warn/bloom-deep/primary)
- Background `{colors.paper}`, 1px `{colors.hairline}` border - Background `{colors.paper}`, 1px `{colors.hairline}` border
#### Inline Banners
For persistent in-page status messages (not ephemeral toasts). Use `<AlertBanner variant="...">` component.
Four variants — all share: `bg-paper border border-hairline border-l-4 rounded-none px-md py-sm flex items-start gap-sm`.
| Variant | Left strip color | ARIA role | Lucide icon |
|---------|-----------------|-----------|-------------|
| `error` | `border-l-bloom-deep` | `role="alert"` | `AlertCircle` |
| `warn` | `border-l-warn` | `role="alert"` | `AlertTriangle` |
| `success` | `border-l-success` | `role="status"` | `CheckCircle` |
| `info` | `border-l-primary` | `role="status"` | `Info` |
- Icon: Lucide at `size={16}`, `aria-hidden`, `mt-[2px] shrink-0` for baseline alignment.
- No shadow, no rounded corners, no transition.
- Optional `title` prop renders as `font-medium text-ink` above the message body.
--- ---
## Do's and Don'ts ## Do's and Don'ts