From 9d8326573159fa66e816290008677163eb3bbae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20SAILLANT?= <108685187+electron-rare@users.noreply.github.com> Date: Sat, 14 Mar 2026 17:59:43 +0000 Subject: [PATCH] Clean white-contrast rollout and sync tracking checks --- docs/da-01-implementation-backlog.md | 9 +- docs/project-master-todos.md | 7 +- scripts/validate-tracking.mjs | 119 +++++++++++++++------- src/styles/global.css | 147 +-------------------------- 4 files changed, 97 insertions(+), 185 deletions(-) diff --git a/docs/da-01-implementation-backlog.md b/docs/da-01-implementation-backlog.md index 30e0f9c..ade0f5c 100644 --- a/docs/da-01-implementation-backlog.md +++ b/docs/da-01-implementation-backlog.md @@ -94,18 +94,19 @@ Consolider la DA actuelle en version claire, contraste blanc, sans reintroduire - contact direct ## Backlog P0 -- [ ] Forcer la cascade sur la DA blanche dans `BaseLayout.astro`, `site.ts`, `global.css`, `home-workbench.css`. -- [ ] Relever les derniers blocs sombres restants: `hero-bg-photo`, `photo-strip`, `video-strip`, `contact-minitel`, FAQ focus. +- [x] Forcer la cascade sur la DA blanche dans `BaseLayout.astro`, `site.ts`, `global.css`, `home-workbench.css`. +- [x] Relever les derniers blocs sombres restants: `hero-bg-photo`, `photo-strip`, `video-strip`, `contact-minitel`, FAQ focus. - [ ] Verifier la lisibilite des CTA sur blanc. -- [ ] Verifier que la home et `formation` partagent bien le meme socle visuel. +- [ ] Verifier que la home et `formation` partagent bien le meme socle visuel en preview live. ## Backlog P1 - [ ] Supprimer les references documentaires a `#projets`, Malt, GitHub Pages static prod, dual-rail hero, contrast toggle. - [ ] Verifier les events `data-track` sur les CTA actifs uniquement. +- [x] Recaler `tracking:check` sur le contrat Astro actif pour sortir des faux positifs legacy. - [ ] Valider preview OVH puis prod. ## Backlog P2 -- [ ] Supprimer les reliquats `.site-contrast-toggle` du CSS global. +- [x] Supprimer les reliquats `.site-contrast-toggle` du CSS global. - [ ] Evaluer si un vrai theme secondaire doit exister plus tard ou si le mode blanc reste la seule version publique. - [ ] Consolider une archive explicite des anciens artefacts DA pour eviter toute reintroduction. diff --git a/docs/project-master-todos.md b/docs/project-master-todos.md index 21ef665..683db40 100644 --- a/docs/project-master-todos.md +++ b/docs/project-master-todos.md @@ -22,11 +22,14 @@ Source of truth: this file for execution priorities, plus the current code in `s - [x] Ancienne variante `studio/figma` sortie du runtime actif. - [x] Bouton contraste retire du markup. - [x] Theme blanc contraste force par defaut dans le layout source. +- [x] Reliquats CSS `.site-contrast-toggle` supprimes du source actif. +- [x] Build preview local `PUBLIC_SITE_URL=/preview/` repasse propre. +- [x] Validation tracking recalee sur le contrat Astro actif. - [ ] White-contrast pass verifiee visuellement sur preview. - [ ] White-contrast pass verifiee visuellement sur production. ## P0 - Bloc de sortie immediate -- [ ] Builder le preview avec le theme blanc contraste (`PUBLIC_SITE_URL=https://www.lelectronrare.fr/preview/ npm run build:external`). +- [x] Builder le preview avec le theme blanc contraste (`PUBLIC_SITE_URL=https://www.lelectronrare.fr/preview/ npm run build:external`). - [ ] Verifier le rendu de la home: nav, hero, approche, cas clients, missions, FAQ, footer. - [ ] Verifier les surfaces medias: hero photo, photo strip, video strip. - [ ] Verifier la section contact en version claire: tabs, textarea, formulaire, focus clavier, messages d'etat. @@ -55,7 +58,7 @@ Source of truth: this file for execution priorities, plus the current code in `s - [ ] Soumettre ou recontroler Search Console sur la base de la prod actuelle OVH. ## P3 - Nettoyage et consolidation -- [ ] Supprimer les blocs CSS morts `.site-contrast-toggle` encore presents dans `src/styles/global.css`. +- [x] Supprimer les blocs CSS morts `.site-contrast-toggle` encore presents dans `src/styles/global.css`. - [ ] Nettoyer les docs qui parlent encore de GitHub Pages static root, `#projets`, Malt, ou du toggle contraste. - [ ] Mesurer LCP et budget media de la variante blanche en conditions reelles. - [ ] Archiver les plans anterieurs devenus purement historiques. diff --git a/scripts/validate-tracking.mjs b/scripts/validate-tracking.mjs index e96802a..0f3b913 100644 --- a/scripts/validate-tracking.mjs +++ b/scripts/validate-tracking.mjs @@ -1,45 +1,96 @@ import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; -const htmlPath = new URL('../index.html', import.meta.url); -const html = fs.readFileSync(htmlPath, 'utf8'); - -const expectedPrimary = [ - 'cta_hero_projets', - 'cta_hero_contact', - 'cta_hero_profile', - 'outbound_linkedin_contact', - 'outbound_malt_contact', - 'outbound_bandcamp_contact', - 'outbound_github_contact', - 'outbound_github_project_rtc_bl_phone', - 'outbound_github_project_zacus', - 'outbound_github_project_site', - 'outbound_github_lab_more', - 'cta_lab_interactif_open' +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(__dirname, '..'); +const trackingFile = path.join(repoRoot, 'src/lib/tracking.ts'); +const sourceRoots = [ + path.join(repoRoot, 'src/pages'), + path.join(repoRoot, 'src/components'), + path.join(repoRoot, 'src/content') ]; -const expectedLegacy = ['outbound_github_project', 'outbound_github_contact']; +function walk(dir) { + return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + return walk(fullPath); + } + if (/\.(astro|tsx|ts|jsx|js)$/.test(entry.name)) { + return [fullPath]; + } + return []; + }); +} -const dataTrackMatches = [...html.matchAll(/data-track="([^"]+)"/g)].map((m) => m[1]); -const dataLegacyMatches = [...html.matchAll(/data-track-legacy="([^"]+)"/g)].map((m) => m[1]); - -const uniqueDataTrack = [...new Set(dataTrackMatches)]; -const uniqueLegacyTrack = [...new Set(dataLegacyMatches)]; - -const missingPrimary = expectedPrimary.filter((eventName) => !uniqueDataTrack.includes(eventName)); -const missingLegacy = expectedLegacy.filter((eventName) => !uniqueLegacyTrack.includes(eventName)); - -if (missingPrimary.length || missingLegacy.length) { - console.error('Tracking contract validation failed.'); - if (missingPrimary.length) { - console.error(`Missing primary events: ${missingPrimary.join(', ')}`); +function readTrackEvents() { + const source = fs.readFileSync(trackingFile, 'utf8'); + const objectMatch = source.match(/TRACK_EVENTS\s*=\s*\{([\s\S]*?)\}\s*as const/); + if (!objectMatch) { + throw new Error('Unable to parse TRACK_EVENTS from src/lib/tracking.ts'); } - if (missingLegacy.length) { - console.error(`Missing legacy events: ${missingLegacy.join(', ')}`); + + const entries = [...objectMatch[1].matchAll(/(\w+)\s*:\s*'([^']+)'/g)].map((match) => ({ + key: match[1], + value: match[2] + })); + + return new Map(entries.map(({ key, value }) => [key, value])); +} + +const knownEvents = readTrackEvents(); +const usedEventKeys = new Set(); +const usedRawEvents = new Set(); +const legacyAttrs = []; + +for (const sourceRoot of sourceRoots) { + for (const filePath of walk(sourceRoot)) { + const content = fs.readFileSync(filePath, 'utf8'); + + for (const match of content.matchAll(/TRACK_EVENTS\.(\w+)/g)) { + usedEventKeys.add(match[1]); + } + + for (const match of content.matchAll(/data-track="([^"]+)"/g)) { + usedRawEvents.add(match[1]); + } + + if (content.includes('data-track-legacy=')) { + legacyAttrs.push(path.relative(repoRoot, filePath)); + } + } +} + +const usedEvents = new Set( + [...usedEventKeys] + .filter((key) => knownEvents.has(key)) + .map((key) => knownEvents.get(key)) + .concat([...usedRawEvents]) +); + +const missingEvents = [...knownEvents.entries()] + .filter(([_, value]) => !usedEvents.has(value)) + .map(([key, value]) => `${key}=${value}`); + +const unexpectedEvents = [...usedEvents].filter( + (value) => ![...knownEvents.values()].includes(value) +); + +if (legacyAttrs.length || missingEvents.length || unexpectedEvents.length) { + console.error('Tracking contract validation failed.'); + if (legacyAttrs.length) { + console.error(`Legacy tracking attrs found in: ${legacyAttrs.join(', ')}`); + } + if (missingEvents.length) { + console.error(`Known events missing from active source: ${missingEvents.join(', ')}`); + } + if (unexpectedEvents.length) { + console.error(`Unknown tracking events found in active source: ${unexpectedEvents.join(', ')}`); } process.exit(1); } console.log('Tracking contract validation passed.'); -console.log(`Primary events found (${uniqueDataTrack.length}): ${uniqueDataTrack.join(', ')}`); -console.log(`Legacy events found (${uniqueLegacyTrack.length}): ${uniqueLegacyTrack.join(', ')}`); +console.log(`Known events (${knownEvents.size}): ${[...knownEvents.values()].join(', ')}`); +console.log(`Active events used (${usedEvents.size}): ${[...usedEvents].join(', ')}`); diff --git a/src/styles/global.css b/src/styles/global.css index b0b1d12..bc92466 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -786,32 +786,6 @@ h3 { color: color-mix(in oklab, var(--electric) 82%, var(--text) 18%); } - .site-contrast-toggle { - border: 1px solid color-mix(in oklab, var(--trace-cyan) 48%, transparent); - } - - .site-contrast-toggle { - background: color-mix(in oklab, var(--surface-soft) 74%, transparent); - } - - .site-contrast-toggle { - color: color-mix(in oklab, var(--electric) 90%, var(--text) 10%); - } - - .site-contrast-toggle:hover, - .site-contrast-toggle:focus-visible { - border-color: color-mix(in oklab, var(--electric) 70%, transparent); - } - - .site-contrast-toggle:hover, - .site-contrast-toggle:focus-visible { - background: color-mix(in oklab, var(--electric) 12%, transparent); - } - - .site-contrast-toggle:before { - border: 1px solid color-mix(in oklab, var(--text) 70%, transparent); - } - .projects-timeline-item:before { border: 1px solid color-mix(in oklab, var(--trace-cyan) 44%, transparent); } @@ -999,57 +973,6 @@ h3 { .studio-muted { color: var(--text-muted); } -.site-contrast-toggle { - border: 1px solid var(--trace-cyan); - border-radius: 999px; - align-items: center; - gap: 0.45rem; - min-height: 2rem; - padding: 0 0.88rem; - display: inline-flex; -} -.site-contrast-toggle { - background: var(--surface-soft); -} -.site-contrast-toggle { - color: var(--electric); -} -.site-contrast-toggle { - letter-spacing: 0.04em; - white-space: nowrap; - cursor: pointer; - font-size: 0.78rem; - font-weight: 600; - transition: - background 0.2s, - border-color 0.2s, - transform 0.2s; -} -.site-contrast-toggle:hover, -.site-contrast-toggle:focus-visible { - border-color: var(--electric); -} -.site-contrast-toggle:hover, -.site-contrast-toggle:focus-visible { - background: var(--electric); -} -.site-contrast-toggle:active { - transform: translateY(1px); -} -.site-contrast-toggle:before { - content: ""; - border: 1px solid var(--text); - border-radius: 999px; - width: 0.55rem; - height: 0.55rem; -} -.site-contrast-toggle:before { - background: radial-gradient( - circle at 35% 35%, - var(--trace-cyan), - transparent 58% - ); -} .projects-timeline-item { grid-template-columns: 4.5rem 1fr; gap: 0.32rem; @@ -1224,11 +1147,6 @@ button:focus-visible { flex-wrap: wrap; justify-content: center; } - .site-contrast-toggle { - justify-content: center; - width: 100%; - max-width: 14rem; - } .site-shell-title { max-width: 12ch; } @@ -1426,7 +1344,6 @@ li { sans-serif; } .site-navlink, -.site-contrast-toggle, .lab-kicker, .lab-pill, .studio-chip { @@ -1572,16 +1489,6 @@ li { border-radius: 0.3rem; font-size: 0.61rem; } -.site-contrast-toggle { - letter-spacing: 0.1em; - text-transform: uppercase; - border-radius: 0.28rem; - min-height: 1.95rem; - font-size: 0.66rem; -} -.site-contrast-toggle:before { - border-radius: 1px; -} .circuit-board, .lab-panel, .studio-panel { @@ -3178,25 +3085,6 @@ footer:before { color: #3a2e22; } -/* HIGH-CONTRAST: contrast toggle */ -:root[data-da-variant="v12"][data-theme="high-contrast"] .site-contrast-toggle { - background: #ffffff; - border-color: #333; - color: #1a1a1a; - box-shadow: none; -} - -:root[data-da-variant="v12"][data-theme="high-contrast"] .site-contrast-toggle:hover, -:root[data-da-variant="v12"][data-theme="high-contrast"] .site-contrast-toggle:focus-visible { - background: #f0e8dd; - border-color: #1a1a1a; - box-shadow: 0 0 0 2px #1a1a1a; -} - -:root[data-da-variant="v12"][data-theme="high-contrast"] .site-contrast-toggle::before { - background: none; -} - /* HIGH-CONTRAST: pills / CTAs */ :root[data-da-variant="v12"][data-theme="high-contrast"] .figma-lab-pill--secondary, :root[data-da-variant="v12"][data-theme="high-contrast"] .graphic-sprints-cta { @@ -3481,25 +3369,6 @@ footer:before { display: none; } -:root[data-da-variant="v12"] .site-contrast-toggle { - min-height: 2.5rem; - border-color: rgba(120, 94, 73, 0.18); - background: rgba(255, 250, 243, 0.86); - color: #4d3a30; - box-shadow: 0 8px 20px rgba(99, 74, 54, 0.08); -} - -:root[data-da-variant="v12"] .site-contrast-toggle:hover, -:root[data-da-variant="v12"] .site-contrast-toggle:focus-visible { - background: rgba(255, 253, 249, 0.98); - border-color: rgba(120, 94, 73, 0.28); - box-shadow: 0 12px 24px rgba(99, 74, 54, 0.12); -} - -:root[data-da-variant="v12"] .site-contrast-toggle::before { - background: linear-gradient(90deg, rgba(183, 86, 44, 0.18), rgba(47, 104, 118, 0.18)); -} - :root[data-da-variant="v12"] .figma-lab-nav-row { min-height: 4.25rem; gap: 1.2rem; @@ -4351,8 +4220,7 @@ details[open] > .faq-question .faq-chevron::after { gap: 0.5rem; } -:root[data-da-variant="v12"] .figma-lab-nav-link, -:root[data-da-variant="v12"] .site-contrast-toggle { +:root[data-da-variant="v12"] .figma-lab-nav-link { min-height: 2.2rem; display: inline-flex; align-items: center; @@ -4376,9 +4244,7 @@ details[open] > .faq-question .faq-chevron::after { :root[data-da-variant="v12"] .figma-lab-nav-link:hover, :root[data-da-variant="v12"] .figma-lab-nav-link:focus-visible, :root[data-da-variant="v12"] .figma-lab-nav-link.is-active, -:root[data-da-variant="v12"] .figma-lab-nav-link[aria-current="location"], -:root[data-da-variant="v12"] .site-contrast-toggle:hover, -:root[data-da-variant="v12"] .site-contrast-toggle:focus-visible { +:root[data-da-variant="v12"] .figma-lab-nav-link[aria-current="location"] { color: #cbffd0; border-color: rgba(51, 255, 255, 0.3); background: @@ -4390,10 +4256,6 @@ details[open] > .faq-question .faq-chevron::after { text-shadow: 0 0 10px rgba(51, 255, 51, 0.22); } -:root[data-da-variant="v12"] .site-contrast-toggle::before { - background: linear-gradient(90deg, rgba(51, 255, 255, 0.16), rgba(51, 255, 51, 0.16)); -} - :root[data-da-variant="v12"] .figma-lab-left, :root[data-da-variant="v12"] .figma-lab-right, :root[data-da-variant="v12"] .circuit-board, @@ -4737,7 +4599,6 @@ details[open] > .faq-question .faq-chevron::after { @media (max-width: 767px) { :root[data-da-variant="v12"] .figma-lab-nav-link, -:root[data-da-variant="v12"] .site-contrast-toggle, :root[data-da-variant="v12"] .figma-lab-pill, :root[data-da-variant="v12"] .graphic-sprints-cta { width: 100%; @@ -4758,10 +4619,6 @@ details[open] > .faq-question .faq-chevron::after { align-items: center; } -.site-contrast-toggle { - min-height: 2.75rem; /* 44px */ -} - /* About section photo integration */ .about-intro { display: flex;