# Sprint 11 — Spectrum UX port : 4 primitives + compact density global **Base** : `origin/main` (PR #12 mergée — sprint 8 + 9 + 10 en). **Branch** : `sprint/11-spectrum-ux`. **Scope** : frontend-only sprint, 5 livrables coordonnés. Pas de backend, pas de schéma, pas de nouvelle API. Pas de modif tokens couleurs/spacing/font dans tailwind.config. --- ## Contexte Analyse Spectrum (`/home/user/Documents/01_Projects/spectrum`) faite via workflow. Résultat : Spectrum n'a **pas de design system** (Tailwind raw + violet ramp + recipes inlinées 30× partout). Mimic est structurellement meilleur. On ne porte pas la philosophie Spectrum — on extrait **5 patterns isolés** qui survivent au brutalisme Mimic. ## Constraints absolues 1. **Primary `#024ad8` (Electric Blue) reste**. Pas de violet, pas de spectrum ramp. 2. **Brutalisme reste** : `rounded-none` sur containers (sauf status pills + tab count pills + avatars), zero `transition-*`, zero `shadow-*`, hairline 1px borders. 3. **Pas de modif `tailwind.config.*`** sauf si une nouvelle classe utility est strictement nécessaire (justifier dans le PR). 4. **DESIGN.md amendments additives uniquement** — pas de modif des tokens existants. 5. **Mono uniquement pour data** (IDs MITRE, dates ISO, commands, etc.) — rule sprint 7. ## Décisions binding (lockées par user) - **Q1 → B** : Tabs primitive **+ consumer EngagementDetailPage** (3 tabs : Schedule / Description / Simulations au lieu du stack vertical actuel). - **Q2 → B** : **Compact density GLOBAL** — toutes les tables passent à 32px row, pas opt-in. User accepte le tradeoff WCAG (touch target) sur Engagements list. - **Q3 → A** : AlertBanner + refactor `SimulationFormPage` (Done banner + SOC-blocked banner). --- ## Livrables ### Livrable 1 — `Tabs` primitive + `useHashTab` hook + consumer **Files** : - `frontend/src/hooks/useHashTab.ts` (NEW) — ~30 LoC. Reads `window.location.hash`, falls back to default ID, listens to `hashchange`, updates URL without reload. TypeScript pur, zéro style. - `frontend/src/components/Tabs.tsx` (NEW) — composant ``. Underline variant. - `frontend/src/styles/index.css` — nouvelles recipes : - `.tab-underline` — `text-graphite caption-bold cursor-pointer border-b-2 border-transparent hover:text-ink` (instantané, pas de transition) - `.tab-underline-active` — `text-primary border-primary` (override) - `.tab-count-pill` — `rounded-pill bg-cloud text-graphite text-[11px] px-xs py-0 font-mono` - `.tab-count-pill-active` — `bg-primary-soft text-primary` (override) - `frontend/src/pages/EngagementDetailPage.tsx` — refactor en 3 tabs : - Tab 1 "Schedule" (id `schedule`, default) — dates + statut + bouton "Edit engagement" - Tab 2 "Description" (id `description`) — texte description - Tab 3 "Simulations" (id `simulations`) — la liste actuelle de simulations - Count pills sur tab 3 = nombre de simulations - Tabs branchées sur `useHashTab('schedule')` - `DESIGN.md` — nouvelle subsection `### Navigation › Sub-page tabs` (additive) **Brutalism check** : - Tab : `border-b-2` only, pas de bg, pas de rounded, pas de transition - Pill : `rounded-pill` autorisé (exception status pill DESIGN.md L125) - Hover : `hover:text-ink` instantané ### Livrable 2 — `AlertBanner` component + 4 recipes **Files** : - `frontend/src/components/AlertBanner.tsx` (NEW) — ``. Brutalist : border-l-4 strip semantic, fill `bg-paper`, border `border-hairline` partout sauf le strip gauche, `rounded-none`. Icon Lucide à gauche (`AlertCircle` error, `AlertTriangle` warn, `CheckCircle` success, `Info` info) à `size={16}`. - `frontend/src/styles/index.css` — 4 nouvelles recipes : - `.alert-error` — `bg-paper border border-hairline border-l-4 border-l-bloom-deep px-md py-sm flex items-start gap-sm` - `.alert-warn` — same + `border-l-warn` - `.alert-success` — same + `border-l-success` - `.alert-info` — same + `border-l-primary` - `frontend/src/pages/SimulationFormPage.tsx` — refactor 2 banners hand-rollés (~L300 Done banner + ~L310 SOC-blocked banner) → `` / ``. - `DESIGN.md` — extend `### Toast Notifications` avec subsection `### Inline Banners` (additive, mêmes 4 variants). **Note** : `bloom-deep`, `warn`, `success`, `primary` sont les tokens existants — pas de nouveau token couleur. ### Livrable 3 — `BackLink` component (dédup) **Files** : - `frontend/src/components/BackLink.tsx` (NEW) — `Back to engagements`. Renders `← {children}` (ArrowLeft Lucide size=14, gap-xxs). Style : `text-graphite hover:text-primary caption-md` instantané. - Refactor consumers (3 instances hand-rollées) : - `frontend/src/pages/EngagementDetailPage.tsx` (~L35) - `frontend/src/pages/SimulationFormPage.tsx` (back-link en edit mode) - `frontend/src/pages/TemplateFormPage.tsx` (back-link en edit mode) ### Livrable 4 — `.table-compact` recipe + apply GLOBAL **Files** : - `frontend/src/styles/index.css` — nouvelle recipe (mais appliquée comme default, pas opt-in cette sprint per user decision Q2-B) : - Soit : modifier la recipe `.card-product table` existante pour passer à 32px row, `caption-md` text, `py-xxs px-xs` cells, divider `border-hairline`. - Soit : ajouter `.table-compact` puis l'appliquer à TOUTES les list pages (Engagements, Templates, Users, etc.). - **Choix builder** : pick the cleaner path. Préférer modifier le default si toutes les tables doivent suivre, sinon `.table-compact` + apply partout. - Apply sur : - `frontend/src/pages/EngagementsListPage.tsx` - `frontend/src/pages/TemplatesListPage.tsx` - `frontend/src/pages/UsersAdminPage.tsx` - `frontend/src/components/SimulationList.tsx` (utilisé dans EngagementDetailPage) - Toute autre table dans le code (grep `