Sprint 11 — Spectrum UX port : analyse du projet /home/user/Documents/01_Projects/spectrum faite via workflow (4 lecteurs parallèles + synthèse adversariale). Verdict : Spectrum n'a pas de design system (Tailwind raw + violet ramp, recipes inlinées 30× partout). Mimic est structurellement meilleur — on n'absorbe pas la philosophie Spectrum, on extrait 4 patterns isolés qui survivent au brutalisme.
Sortie : 4 nouveaux composants/hooks (Tabs, useHashTab, AlertBanner, BackLink), 1 page refactorée en 3 tabs (EngagementDetailPage), 1 changement de densité table global (32px row), 4 amendments DESIGN.md additives, CHANGELOG.md entrée.
Brutalisme strict : zéro transition-*, zéro shadow-*, rounded-none partout sauf status pills + .tab-count-pill (exception documentée). Hairline 1px borders only. Primary #024ad8 Electric Blue intouchée.
WCAG SC 2.5.5 tradeoff explicite : tables passent de 44px à 32px row min-height. Justifié dans DESIGN.md (Mimic est un BAS operator tool, densité > touch target compliance). Action buttons restent ≥ 24px icon + padding pour préserver le hit area.
Sécurité a11y : Tabs wired complet WAI-ARIA (role="tablist" + role="tab" + aria-selected + aria-controls + aria-labelledby + arrow-key nav). AlertBannerrole="alert" (error/warn) vs role="status" (success/info) — aria-live correct par implication.
useHashTab stable F5 + non-polluant historique : history.replaceState (pas de pushState, pas d'anchor-jump scroll).
Test plan
Frontend : 236 / 236 vitest (baseline 212 + 24 new tests across 4 new specs + 2 new arrow-key + 1 new aria-controls assertion).
Reviews : code-review APPROVED (1 MINOR + 3 NIT, all patched), design-review APPROVED (2 MINOR + 1 NIT, all patched).
Comment tester en local
# Sprint 11 est purement frontend. Pas de migration, pas d'env var.
git checkout sprint/11-spectrum-ux
make build && make start
# http://127.0.0.1:5000
Scénarios :
Tabs sur EngagementDetailPage — ouvre un engagement en lecture. 3 tabs : Schedule (par défaut), Description, Simulations. Count pill sur "Simulations" = nombre exact de simulations. Click un tab → URL devient …#description (sans push d'history entry — Back doit toujours mener à la liste d'engagements en 1 clic).
Hash sync F5 — sur …#simulations, refresh la page → tab Simulations reste actif. Colle …#nonsense → tu vois rien actif (silent fallback), idéalement default fallback dans une future itération.
Arrow-key nav — focus un tab, ArrowRight / ArrowLeft cyclent entre les 3 tabs avec wrap-around.
Screen reader — chaque tab a role="tab" + aria-selected, le panel actif a role="tabpanel" + aria-labelledby pointant vers le tab actif.
AlertBanner — SimulationFormPage — sur une simulation done, le bandeau "Simulation complétée…" est désormais un <AlertBanner variant="success">. Sur une simulation SOC-blocked en mode RT, le bandeau "Cette simulation est en cours de review SOC…" est un <AlertBanner variant="warn">. Plus de markup bg-fog/bg-cloud hand-rollé.
BackLink — sur Engagement detail, Simulation edit, Template edit : tu vois ← Back to engagements / ← Back to {engagement name} / ← Back to templates. Tous les 3 sont des <BackLink> (caption-md text-graphite, hover-instant text-primary).
Compact density — Engagements list, Templates list, Users admin, Simulations list (inside Engagement) : toutes les tables sont en density 32px row, headers en text-[12px] uppercase, dividers hairline. Action buttons (edit/delete) restent confortables à cliquer.
Fichiers livrés
Fichier
Type
Notes
frontend/src/hooks/useHashTab.ts
NEW (~30 LoC)
Hash-stable active tab, history.replaceState, cleanup listener
frontend/src/components/Tabs.tsx
NEW
<Tabs items activeId onChange> complet WAI-ARIA + arrow-key nav
frontend/src/components/AlertBanner.tsx
NEW
4 variants, Lucide icons, ARIA correct par variant
border-l-4 semantic strip, bg-paper, Lucide icons at size=16.
ARIA role="alert" for error/warn, role="status" for success/info.
No shadow, no radius, no transition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add table-compact class to all 4 list surfaces: EngagementsListPage,
TemplatesListPage, UsersAdminPage, SimulationList. Remove inline
px-xl py-md from th/td — recipe handles padding. Row border-b moved
to recipe. WCAG SC 2.5.5 tradeoff accepted (BAS operator tool).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 new spec files adding 21 tests (7+6+4+4). Each component spec
includes brutalism invariant assertions (no rounded-md, no transition-*,
no shadow-*).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 1 (A): move row height constraint from min-height on tbody tr
(CSS no-op on table-row) to height: 32px on td (works on table-cell).
Fix 2 (A): table header text-[11px] -> text-[12px] to align with
documented caption scale.
Fix 3: add aria-controls + id to Tabs buttons; wrap active tab content
in role="tabpanel" + aria-labelledby in EngagementDetailPage.
Test: add aria-controls/id assertion to Tabs.test.tsx (234 total).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 4: useHashTab navigate() uses history.replaceState instead of
window.location.hash assignment — no spurious history entries, no
anchor-jump scroll side-effect.
Fix 5: Tabs ArrowLeft/ArrowRight keyboard nav (WAI-ARIA tabs pattern).
Fix 6: TabId union type in EngagementDetailPage, cast from string for
type-safe switch on activeTab without breaking Tabs.onChange signature.
Fix 7: intentional comment on UsersAdminPage reset-password aerated row.
Tests: 236/236 (+2 arrow-key assertions in Tabs.test.tsx).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
caption-bold and caption-md are fontSize tokens — Tailwind generates
text-caption-bold / text-caption-md utilities, not bare-name utilities.
Missing text- prefix caused @apply resolution failure in vite build
(PostCSS step), while vitest passed because it does not resolve @apply.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
/home/user/Documents/01_Projects/spectrumfaite via workflow (4 lecteurs parallèles + synthèse adversariale). Verdict : Spectrum n'a pas de design system (Tailwind raw + violet ramp, recipes inlinées 30× partout). Mimic est structurellement meilleur — on n'absorbe pas la philosophie Spectrum, on extrait 4 patterns isolés qui survivent au brutalisme.Tabs,useHashTab,AlertBanner,BackLink), 1 page refactorée en 3 tabs (EngagementDetailPage), 1 changement de densité table global (32px row), 4 amendments DESIGN.md additives, CHANGELOG.md entrée.transition-*, zéroshadow-*,rounded-nonepartout sauf status pills +.tab-count-pill(exception documentée). Hairline 1px borders only. Primary#024ad8Electric Blue intouchée.Tabswired complet WAI-ARIA (role="tablist"+role="tab"+aria-selected+aria-controls+aria-labelledby+ arrow-key nav).AlertBannerrole="alert"(error/warn) vsrole="status"(success/info) —aria-livecorrect par implication.history.replaceState(pas de pushState, pas d'anchor-jump scroll).Test plan
tsc --noEmitclean.eslint --max-warnings=0clean.Comment tester en local
Scénarios :
Schedule(par défaut),Description,Simulations. Count pill sur "Simulations" = nombre exact de simulations. Click un tab → URL devient…#description(sans push d'history entry — Back doit toujours mener à la liste d'engagements en 1 clic).…#simulations, refresh la page → tab Simulations reste actif. Colle…#nonsense→ tu vois rien actif (silent fallback), idéalement default fallback dans une future itération.role="tab"+aria-selected, le panel actif arole="tabpanel"+aria-labelledbypointant vers le tab actif.done, le bandeau "Simulation complétée…" est désormais un<AlertBanner variant="success">. Sur une simulation SOC-blocked en mode RT, le bandeau "Cette simulation est en cours de review SOC…" est un<AlertBanner variant="warn">. Plus de markupbg-fog/bg-cloudhand-rollé.← Back to engagements/← Back to {engagement name}/← Back to templates. Tous les 3 sont des<BackLink>(caption-md text-graphite, hover-instant text-primary).Fichiers livrés
frontend/src/hooks/useHashTab.tshistory.replaceState, cleanup listenerfrontend/src/components/Tabs.tsx<Tabs items activeId onChange>complet WAI-ARIA + arrow-key navfrontend/src/components/AlertBanner.tsxfrontend/src/components/BackLink.tsx<Link>React Router + ArrowLeft Lucide size=14frontend/src/styles/index.cssfrontend/src/pages/EngagementDetailPage.tsxTabIdunion typed) + BackLinkfrontend/src/pages/SimulationFormPage.tsx<AlertBanner>+ back-link →<BackLink>frontend/src/pages/TemplateFormPage.tsx<BackLink>frontend/src/pages/EngagementsListPage.tsx.table-compactappliedfrontend/src/pages/TemplatesListPage.tsx.table-compactappliedfrontend/src/pages/UsersAdminPage.tsx.table-compactapplied + commentaire intention sur reset-password rowfrontend/src/components/SimulationList.tsx.table-compactappliedfrontend/tests/hooks/useHashTab.test.tsxfrontend/tests/components/Tabs.test.tsxfrontend/tests/components/AlertBanner.test.tsxfrontend/tests/components/BackLink.test.tsxDESIGN.mdCHANGELOG.mdDécisions verrouillées
Patterns explicitement rejetés depuis Spectrum
spectrum-700(contrainte hard : primary stays#024ad8)shadow-md/lg/xlsur cards/modals (Mimic forbid)rounded/rounded-lg/rounded-fullsur chrome (Mimic =rounded-nonepartout sauf pills + avatars)transition-*(Mimic = hover instantané)hover:-translate-y-0.5 hover:shadow-lg)Commits
🤖 Generated with Claude Code
Tabs: Schedule (default) / Description / Simulations, wired via useHashTab('schedule'). Count pill on Simulations tab. BackLink replaces hand-rolled Link. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>