SPB Git forge

spb/qc-election

Public
20commits 1branches 0releases
4.9 MBsize
maindefault branch
20 days agolast push
Python 66.6% HTML 24.8% CSS 4.9% JavaScript 3.6%
8.3 KB · 146 lines markdown
Rendered Raw Blame History
1# QC Signal — design system de QC Élection Forecast23Version 4 (2026-08-30). Direction : **editorial intelligence + election command center +4scientific forecasting + identité québécoise**. La sensation recherchée : « je regarde un5instrument de mesure de l'élection ». À proscrire : dashboard SaaS générique, admin panel,6esthétique crypto.78Tout vit dans deux fichiers partagés :910- `frontend/assets/style.css` — tokens + composants (source de vérité visuelle)11- `frontend/assets/app.js` — coquille (`initShell`), composants JS, thème ECharts « qc-signal »1213---1415## 1. Tokens1617### Couleurs de surface (palettes dédiées, le dark n'est PAS une inversion)1819| Token | Dark | Light | Rôle |20|---|---|---|---|21| `--bg` | `#0a0d14` | `#eef0f4` | fond de page (bleu-noir profond / gris papier) |22| `--surface` | `#0f131c` | `#f5f6fa` | fonds encaissés (meter, pistes) |23| `--card` | `#131824` | `#ffffff` | cartes |24| `--card-2` | `#1a2030` | `#edeff5` | surfaces secondaires (champs, chips, hover) |25| `--ink` | `#edeff5` | `#161b26` | texte principal (blanc cassé / encre) |26| `--ink-2` / `--muted` | `#aab2c5` / `#7d8699` | `#414d68` / `#67718a` | texte secondaire / tertiaire |27| `--line` / `--border` | `rgba(160,178,210,.10)` | `rgba(38,54,92,.10)` | bordure discrète par défaut |28| `--border-strong` | `rgba(170,188,218,.22)` | `rgba(38,54,92,.22)` | bordure appuyée (hover, th) |29| `--accent` / `--accent-strong` | `#94a9d3` / `#5c7ec2` | `#3d5a94` / `#35507e` | bleu institutionnel **politiquement neutre** |30| `--good` / `--warn` / `--danger` | états sémantiques SEULEMENT (statut pipeline, badges) — jamais pour un mouvement de parti |3132Principe : **espace / contraste / surface avant bordure**. Les bordures sont des `rgba`33faibles; l'appui visuel vient des surfaces (`--card` vs `--bg`) et des ombres discrètes34`--shadow-sm` / `--shadow-md` (jamais de gros box-shadow).3536### Couleurs des partis — DONNÉES SEULEMENT3738`--caq #0097cf · --plq #e5484d · --pq #3f7ad6 · --qs #e06c1f · --pcq #7069d6 · --aut #ad8226`3940Validées daltonisme/contraste sur les deux surfaces. **Interdiction** de les réutiliser pour41le chrome du site, et interdiction de modifier ces valeurs. Toujours accompagnées du sigle42texte (jamais la couleur seule pour identifier un parti). Accès JS : `partyColor(p)`.4344### Radius, espacement, motion4546- Radius : `--r-8, --r-12, --r-16, --r-20, --r-24, --r-full` (8/12/16/20/24/999).47  `--radius` = 16 (cartes), `--radius-sm` = 8 (champs, boutons).48- Espacement : grille 4/8 px — `--s-1…--s-12` (4, 8, 12, 16, 20, 24, 32, 40, 48).49- Motion : `--t-fast` 130 ms, `--t-med` 200 ms, `--ease` cubic-bezier(.25,.6,.3,1).50  Aucune transition > 220 ms. `prefers-reduced-motion` coupe tout (CSS global + ECharts51  via `baseChart`).5253## 2. Typographie5455**Inter uniquement** (400–900). `font-variant-numeric: tabular-nums` sur tout chiffre56(kv, tables, metrics, deltas, strip, tooltips ECharts).5758| Classe | Usage | Taille |59|---|---|---|60| `.t-display` | titre display | clamp ~27→40 px, 800 |61| `.hero h1` / `.headline` | H1 de page / manchette éditoriale | clamp ~23→32 / ~24→40 px |62| `h2.section` | titre de section | 1.16 rem, 750 |63| `.card h3` / `.label-up` | étiquette de carte | .76 rem, uppercase, letterspacing .09 em |64| `.metric-xl` | métrique héroïque | clamp → **64 px**, 800, tabular |65| `.bignum` / `.pcard .p-main .v` | métrique de carte | 2.25 / 2.4 rem |66| corps | `.sub`, `.kv`, tables | .84–.98 rem |67| `.caption` / `.note` / `.chart-note` | légendes | .76–.78 rem |6869## 3. Coquille partagée (`initShell(active)`)7071Sur chaque page : `<header class="site">` + `<footer class="site">` vides, puis72`initShell("/route")` monte :73741. **Navbar** — fleur-de-lys SVG (`FLEUR_SVG`, courant = `--accent-strong`), wordmark75   « QC Élection Forecast », liens desktop, indicateur « Dernière MAJ hh:mm »76   (`#hdr-updated`, ≥1250 px), bascule de thème SVG (aria-label), hamburger ≤980 px77   (tiroir avec verrou de scroll, Escape, backdrop).782. **Forecast strip** (`#fstrip`) — fine barre sticky sous la nav, toutes pages.79   Alimentée par `/api/today` (repli `/api/forecast/latest`) :80   `P(PREMIER) · PQ 78 ▼9.9 · CAQ 15 ▲9.7 …` — sigle en couleur du parti, valeur =81   P(1er) **publiée**, flèche neutre = variation vs veille en points. Défilement82   horizontal en mobile, `Scrutin 5 oct. · J−n` à droite (desktop).833. **Footer** — mission de neutralité + crédits.8485Le thème est lu avant le premier paint par le one-liner en tête de chaque page86(`localStorage["qce-theme"]` → `data-theme` sur `<html>`). `toggleTheme()` recharge la87page (les canvas ECharts reprennent les couleurs).8889## 4. Grammaire des données (langage + composants JS)9091Vocabulaire uniforme : **« Chance d'arriver premier » · « Sièges attendus » ·92« Vote projeté » · « Majorité »**. Jamais « X va gagner ».9394| Helper (app.js) | Rendu | Règles |95|---|---|---|96| `deltaHTML(d, {unit, digits, color, thresh})` | `▲ 4.1 pt` / `▼ …` / `· stable` | **JAMAIS vert/rouge** : couleur du parti ou neutre (`--muted`). Une hausse d'un parti n'est pas « positive » universellement. |97| `rangeHTML({lo, hi, val, min, max, mark, color})` | `24 ───●─── 86` | intervalle visuel; `mark` = repère (majorité 64); dot = valeur centrale; aria-label complet |98| `hemicycleSVG(seats, meta)` | hémicycle 127 sièges | arc demi-cercle 6 rangées (sièges ∝ rayon), allocation entière sur p50 (reste au plus fort), marqueur pointillé « majorité 64 » à l'angle du 64ᵉ siège, hover = parti/sièges/intervalle + dim des autres, légende sigle+valeur+IC |99| `emptyStateHTML(msg)` | état vide propre | « Données temporairement indisponibles. » — jamais de `undefined`/`NaN` à l'écran |100| `avatarHTML / initialsAvatar / partyLogo` | visuels secondaires | petits, jamais porteurs d'information seuls |101102Autres composants CSS : `.outcome` (main outcome 2 colonnes), `.outcome-bar` (issues103segmentées — uniquement des probabilités du modèle : `prob_majority`,104`prob_minority_win`, complément), `.pcard` (carte parti compacte data-first),105`.meter` (baromètre sièges), `.table-scroll` (tables hautes, en-tête sticky —106`table.data th { position: sticky }`), `.skel` / `.loading` (squelettes shimmer),107`.chip` / `.badge-aux`, `.map-layout` (~75 % carte / 25 % panneau, empilé ≤980 px).108109## 5. Conventions charts (thème « qc-signal », ECharts CDN uniquement)110111Toujours partir des exports d'app.js — jamais de styles ad hoc :112113- `baseChart(el)` — Inter, animation 420 ms (0 si reduced motion), auto-resize.114- `AXIS_STYLE` — pas d'axisLine/tick; labels `MUTED` 11 px; grille `SPLIT_COLOR`115  (recessive, plus claire que le texte).116- `TOOLTIP` — fond **solide** arrondi 12 px, bordure token, ombre portée, chiffres117  tabulaires, axisPointer en croix pointillée.118- `LEGEND` — puces rondes 10 px, texte `INK2`.119- Séries : `lineGlow(color, 2–2.5)` (trait arrondi + halo de sa propre couleur),120  `areaGrad(color, .10–.28)` (dégradé vertical, jamais d'aplat), `hexA(hex, a)`.121- Couleur de série = TOUJOURS `partyColor(p)`; séries neutres : `#8fa3c8` /122  `GRID_COLOR`. Repères (majorité 64, aujourd'hui) : `markLine` pointillée `MUTED`.123- Constantes de thème : `DARKMODE, GRID_COLOR, SPLIT_COLOR, INK2, MUTED, CHART_FONT`.124125## 6. Accessibilité & robustesse126127- Focus visible global (`:focus-visible` 2 px `--accent-strong`).128- `aria-label` sur burger, bascule de thème, hémicycle, ranges; `aria-current="page"`129  sur le lien actif; drawer `role="dialog"`.130- Parti = sigle texte + couleur, jamais couleur seule; flèches de variation doublées131  du signe/valeur.132- Anti-zoom iOS : tout champ à 16 px min sous 640 px.133- `html, body { overflow-x: clip }` (le tiroir hors-écran ne crée jamais de scroll)134  et `.grid > * { min-width: 0 }` (les canvas ECharts ne verrouillent jamais une135  colonne de grille).136- Breakpoints : 1560 (nav compacte), 1250 (MAJ masquée), 1120 (nav dense), 980137  (burger + colonnes empilées), 900, 640 (mobile), 400 (cartes 1 colonne).138139## 7. Interdits140141- Toucher aux couleurs des partis, ou les utiliser hors données.142- Vert/rouge pour un mouvement de parti.143- Nouvelle dépendance JS (ECharts CDN seulement), nouvelle police.144- Réordonner le DOM entre breakpoints (`order:`, `column-reverse`).145- Modifier un contrat d'API ou recalculer une probabilité côté client.146