UI Audit — refonte « QC Signal » (2026-08-30)
État des lieux avant la refonte du frontend (design v3 « sharp ») et dettes retirées par
la migration vers le design system QC Signal (voir docs/DESIGN_SYSTEM.md).
Refonte UI/UX pure : aucun changement statistique, aucun contrat d'API modifié,
backend intact.
État avant
- Design v3 « sharp » : bon socle (thème dark/light, palette partis validée CVD,
drawer mobile correct), mais tokens incomplets (pas d'échelle de radius/spacing
formalisée, ombres composites lourdes
--shadow-lift), deux polices (Inter + JetBrains Mono), logo = carré dégradé bleu/violet avec ⚜ emoji et ombre portée. - Accueil : liste de cartes partis chargées (gros portraits + logos), baromètre
« meter » comme seul visuel de sièges, aucun hemicycle, aucune hiérarchie
éditoriale (pas de verdict principal), intervalles
[a–b]en texte brut partout. - « Le point du jour » : deltas verts/rouges (
delta-up/delta-down) — une hausse du PQ affichée « positive » universellement, en contradiction avec la neutralité de la plateforme. - Intelligence / simulateur : momentum et « vs référence » également en
vert (
--good) / rouge (--plq!) — le rouge d'un parti utilisé comme sémantique négative. - Pas d'indicateur global du forecast hors de l'accueil (aucun « strip »).
- Tables longues (127 circonscriptions, ~2000 sondages) sans en-tête sticky ni hauteur bornée.
- /carte : carte à ~66 % de largeur, hauteur fixe 560 px, panneau 340 px figé.
- Emojis décoratifs dans les cartes de couches (🗳️✅🏛️📰📈) et libellés d'état.
- Bascule de thème = emoji ☀️/🌙.
- États vides hétérogènes (
<span style="color:var(--muted)">,.loadingpartout).
Dettes retirées
| Dette | Correction |
|---|---|
| Deltas vert/rouge (aujourdhui, intelligence, simulateur) | helper unique deltaHTML : flèche + valeur, couleur du parti ou neutre — jamais de valence universelle |
var(--plq) (rouge PLQ) utilisé comme couleur d'erreur (admin, intelligence, simulateur) |
--danger sémantique (admin) ou couleur du parti (données) |
Intervalles texte [24–86] sans lecture visuelle |
composant rangeHTML (barre lo─●─hi avec repère majorité) sur accueil + cartes partis |
| Aucune vue de composition des sièges | hemicycleSVG réutilisable (127 sièges, marqueur majorité 64, hover parti/sièges/IC) |
| Pas de résumé du forecast inter-pages | forecast strip sticky sous la nav sur les 11 pages (via initShell, /api/today + repli) |
| Logo pastille dégradée + emoji ⚜ | fleur-de-lys SVG nette monochrome accent, wordmark simple |
| Emoji ☀️/🌙 et emojis de couches | icônes SVG soleil/lune; couches numérotées 1-5 |
| 2 webfonts | Inter seul (code → pile monospace système) |
| Ombres/gradients lourds sur cartes | surfaces planes + --shadow-sm/--shadow-md discrets, bordures rgba faibles |
| Tables longues illisibles | .table-scroll (max-height 70 vh) + th sticky (circonscriptions, sondages) |
| /carte : carte à l'étroit | .map-layout ~75/25, hauteur min(72vh, 700px) |
| Débordement horizontal mobile (390 px) : tiroir hors-écran comptait dans scrollWidth; canvas ECharts verrouillait la colonne de grille (/intelligence à 824 px); table marchés sans conteneur | html,body{overflow-x:clip}, .grid>*{min-width:0}, overflow-wrap:anywhere sur les verbatims, conteneur défilant sur la table marchés — scrollWidth = 390 sur les 11 pages |
| Étiquette « majorité 64 » de l'hémicycle coupée hors viewBox | marge haute du SVG (+52 px) |
| Nav qui tronquait « Méthodologie » à 1440 px | paliers 1560/1250/1120 px (densité, masquage MAJ, masquage « Forecast ») |
États vides ad hoc / risque de undefined |
emptyStateHTML(...) uniforme; audit Playwright vérifie l'absence de undefined/NaN/[object Object] dans le texte rendu |
| Animations non bornées | ≤220 ms, prefers-reduced-motion respecté aussi par ECharts (baseChart) |
Langage uniformisé
« Chance d'arriver premier » · « Sièges attendus » · « Vote projeté » · « Majorité » — appliqué à l'accueil, au point du jour, au strip et aux cartes partis.
Validation
~/ka-screenshots/qcsignal-audit.mjs : 11 pages × {1440, 1024, 768, 390} px,
collecte pageerror + console.error, détection de texte undefined/NaN,
captures /tmp/qcsignal_*.png (accueil aussi en clair 1440 + 390).
Résultat final : zéro erreur JS, zéro débordement horizontal, aucun texte cassé
sur les 44 combinaisons.
Contrats préservés (fragiles — ne pas renommer)
- IDs DOM consommés par les scripts de page (inchangés partout) : notamment
hdr-updated,drawer-updated,nav-burger,fstrip(coquille);party-cards,trend-chart,seat-hist,prob-hist,nowfc,events,latest-polls,beyond-*(accueil); tous les IDs de carte/circonscriptions/ sondages/signaux/simulateur/intelligence/backtest/admin inchangés. - Helpers globaux d'app.js réutilisés par toutes les pages :
initShell,getJSON,API,partyColor/partyName/partyLeader,avatarHTML,initialsAvatar,partyLogo,fmtPct/fmtDate/sortParties,baseChart,AXIS_STYLE/TOOLTIP/LEGEND,areaGrad/lineGlow/hexA,DARKMODE/GRID_COLOR/SPLIT_COLOR/MUTED/INK2/CHART_FONT— signatures intactes. - La probabilité « premier » affichée reste la valeur publiée
(
seats.ensemble.blendedavec repliprob_most) — aucune probabilité recalculée côté client; l'outcome bar n'utilise queprob_majority,prob_minority_winet leur complément.