Journal complet des 6 bugs résolus avec cause exacte et fix appliqué. Guide de debug par symptôme. Tests manuels à passer avant chaque déploiement. Erreurs console courantes et leur signification.
Logo TRUvector absent dans la navbar sur toutes les pages hors racine. Aussi : CSS non chargé, JS non chargé, footer absent. La page est entièrement cassée visuellement.
Cause exacte
Formule de calcul du root path incorrecte dans components.js. L'ancienne V2 comptait les slashes dans le pathname et utilisait depth - 1 pour calculer le nombre de ../ à générer. Pour /legal/page.html (2 slashes → depth=1), le résultat était '../'.repeat(0) = '' au lieu de '../'. Pour /products/ironlock/x.html (3 slashes → depth=2), le résultat était '../' au lieu de '../../'.
Bug #2 — Lien actif navbar jamais surligné sur sous-pages
✓ RÉSOLU — V3.0
Fichier : assets/js/main.js
Sévérité : MINEUR
Symptôme
Le lien de navigation correspondant à la page actuelle n'est jamais surligné (classe .active) sur les pages en sous-dossier (legal/, blog/, products/ironlock/).
Cause exacte
L'ancienne logique utilisait path.endsWith(href) où href était un chemin relatif comme '../index.html'. window.location.pathname.endsWith('../index.html') est structurellement impossible — les navigateurs ne mettent jamais .. dans le pathname résolu.
Fix appliqué
// AVANT (bugué)const path = window.location.pathname.replace(/\/$/, '');
if (path.endsWith(href)) a.classList.add('active');
// APRÈS (corrigé) — résolution en URL absolueconst currentPath = window.location.pathname
.replace(/\/index\.html$/, '/').replace(/\/$/, '') || '/';
const absPath = new URL(a.href, window.location.href).pathname
.replace(/\/index\.html$/, '/').replace(/\/$/, '') || '/';
if (currentPath === absPath) a.classList.add('active');
Bug #3 — SyntaxError : apostrophe dans searchIndex
✓ RÉSOLU — V3.1
Fichier : assets/js/components.js ligne 155
Sévérité : CRITIQUE
Symptôme : Toute la navbar disparaît
Symptôme
Console Firefox : Uncaught SyntaxError: missing } after property list [components.js:155:84]. La navbar ne s'injecte pas du tout, la page s'affiche sans navigation.
Cause exacte
La chaîne 'Guide complet d'installation' dans le tableau searchIndex contient une apostrophe directe à l'intérieur d'une string délimitée par des guillemets simples. Le parser JS interprète le ' de d'installation comme la fermeture de la string.
Fix appliqué
// AVANT (bugué) — apostrophe directe
{ excerpt: 'Guide complet d'installation...' }
// APRÈS (corrigé) — Unicode escape
{ excerpt: "Guide complet d\u2019installation..." }
// Règle : utiliser guillemets DOUBLES dans searchIndex// + Unicode escapes pour tous caractères spéciaux :// \u2019 = ' | \u2014 = — | \u00e9 = é | \u00e8 = è
Bug #4 — RangeError: repeat count must be non-negative
✓ RÉSOLU — V3.1
Fichier : assets/js/components.js ligne 20
Sévérité : CRITIQUE
Pages affectées : / (pathname = '/')
Symptôme
Console : Uncaught RangeError: repeat count must be non-negative [components.js:20]. Navbar absente sur la page d'accueil quand le serveur sert directement / (sans /index.html dans le pathname).
Cause exacte
Quand pathname = '/', split('/').filter(Boolean) retourne [] (tableau vide). Donc segments.length = 0, dirs = 0 - 1 = -1, et '../'.repeat(-1) lève un RangeError (JavaScript n'accepte pas un count négatif dans repeat()).
Fix appliqué
// AVANT (bugué)const dirs = segments.length - 1; // → -1 quand segments=[]// APRÈS (corrigé) — Math.max clamp à 0const dirs = Math.max(0, segments.length - 1); // → 0 minimum// Vérification de tous les cas :// pathname='/' → segments=[] → dirs=0 → root='./' ✅// pathname='/index.html' → segments=[index…] → dirs=0 → root='./' ✅// pathname='/legal/x' → segments=[legal, x] → dirs=1 → root='../' ✅
Console Firefox : Le chargement de truvector-promo dans un cadre est refusé par la directive X-Frame-Options définie à DENY. La vidéo promo sur la page d'accueil affiche un cadre vide.
Cause exacte
X-Frame-Options: DENY dans _headers interdit à n'importe quelle page d'être chargée dans une iframe, y compris depuis le même domaine. Hors truvector-promo.html est chargée en <iframe> sur index.html.
Fix appliqué
# _headers — AVANT
/*
X-Frame-Options: DENY
# _headers — APRÈS
/*
X-Frame-Options: SAMEORIGIN ← iframes internes OK, externes bloquées
Bug #6 — Navbar invisible en thème Light
✓ RÉSOLU — V3.2
Fichier : assets/css/truvector.css
Sévérité : CRITIQUE
Conditions : Firefox avec prefers-color-scheme: light
Symptôme
En thème Light, la navbar semble absente — en réalité elle est là mais les textes sont blanc cassé sur fond blanc. Capture : logo visible mais liens navbar et boutons FR/EN/thème invisibles.
Cause exacte
La variable --textd: #6a7f96 était utilisée pour colorer les liens de navigation. En thème dark c'est lisible (sur fond très sombre). En thème light, --nav-bg: rgba(244,246,249,.96) est quasi blanc — le contraste de #6a7f96 sur fond blanc est insuffisant (ratio ≈ 3:1, minimum WCAG = 4.5:1). De plus aucune surcharge [data-theme="light"] n'existait pour les éléments navbar.
Fix appliqué
/* truvector.css — Fixes thème light navbar *//* 1. Fond navbar bien opaque et visible */
[data-theme="light"] { --nav-bg: rgba(255,255,255,.98); }
/* 2. Liens navbar foncés sur fond clair */
.nav-links a { color: var(--textb); }
[data-theme="light"] .nav-links a { color: var(--textb); }
/* 3. Boutons FR/EN lisibles */
.lang-btn { color: var(--textb); }
/* 4. Boutons thème lisibles */
.theme-btn { color: var(--textb); }
/* 5. Hamburger mobile visible */
.nav-toggle span { background: var(--textb); }
/* Ratio contraste final : #0f1e2e sur white = 15:1 (WCAG AAA) */
02
🔴 Symptôme — Navbar invisible ou absente
Vérification
Action
Ouvrir la console (F12) → des erreurs JS ?
Si SyntaxError → apostrophe dans searchIndex (Bug #3). Si RangeError → Math.max manquant (Bug #4). Voir historique bugs.
<div id="nav-placeholder"> présent dans le HTML ?
Inspecter le DOM. Si absent → ajouter en première ligne après <body>. Sans lui, components.js ne peut pas injecter la navbar.
components.js bien chargé ?
Onglet Network (F12) → filtrer "components" → vérifier statut 200. Si 404 → le chemin src est incorrect (mauvais root path).
Navbar présente mais invisible (thème light) ?
Inspecter le nav#navbar → background blanc, couleur texte blanc aussi. Bug #6. Vérifier que le CSS contient les surcharges [data-theme="light"] .nav-links a { color: var(--textb); }.
Ouverture directe avec file:// ?
Ne JAMAIS ouvrir avec file://. Le fetch des JSON i18n est bloqué. Utiliser python -m http.server 8080.
03
🔴 Symptôme — Logo cassé (image manquante)
Sur quelle page ? Racine ou sous-page ?
Si la racine (index.html) est OK mais les sous-pages non → problème de root path. Bug #1 pattern.
Onglet Network → logo.webp → quelle URL est demandée ?
Si l'URL contient /products/assets/ au lieu de /assets/ → root mal calculé. Vérifier que la formule Math.max(0, segments.length-1) est bien en place dans components.js.
Le fichier logo.webp existe-t-il ?
Vérifier que assets/img/logo.webp est bien committé et poussé sur GitHub. Cloudflare Pages ne sert que les fichiers trackés par Git.
Logo dans le <head> (favicon, OG) cassé ?
Ces chemins sont dans le HTML statique de chaque page, pas dans components.js. Vérifier que href="../../assets/img/favicon.ico" utilise le bon nombre de ../ pour la profondeur de la page.
04
🔴 Symptôme — JavaScript ne charge pas
SyntaxError dans la console ?
Valider la syntaxe : node --check assets/js/components.js. Chercher les apostrophes directes dans les strings du searchIndex.
404 sur le script JS ?
Le <script src> utilise un root incorrect. Pour une page en products/ironlock/, le src doit être ../../assets/js/components.js.
main.js chargé avant components.js ?
main.js dépend d'éléments injectés par components.js (nav-toggle, etc.). Toujours charger components.js EN PREMIER. Vérifier l'ordre dans le HTML.
Cache navigateur sur un ancien fichier JS ?
Ctrl+Shift+R (hard reload) ou vider le cache. En prod, le header Cache-Control: max-age=31536000 sur /assets/* peut servir une ancienne version.
05
🟡 Symptôme — Thème ne se souvient pas / flash blanc
Flash blanc au chargement (FOUC) ?
Le script anti-FOUC est absent du <head>. Ajouter le script inline juste avant </head> sur la page concernée. Vérifier dans les 14 fichiers HTML.
Thème light mais site en dark après refresh ?
Vérifier localStorage : F12 → Application → Local Storage → trv-theme. Si absent, le script détecte prefers-color-scheme. Si Firefox est en mode dark système, le site sera dark même si on clique sur light et qu'on refresh (localStorage devrait persister).
Boutons thème ne répondent pas ?
components.js non chargé ou erreur JS avant l'event listener. Vérifier la console. Les boutons sont générés dynamiquement par components.js dans la navbar.
Couleurs incohérentes en thème light ?
Une couleur est hardcodée en hex au lieu d'utiliser var(--xxx). Inspecter l'élément → trouver les propriétés CSS non-variables → remplacer par la variable correspondante.
06
🟡 Symptôme — Traductions absentes ou clés affichées
La clé s'affiche ("nav.home") au lieu du texte ?
La clé n'existe pas dans le fichier JSON de la langue active. Vérifier assets/i18n/fr.json et en.json. La clé doit être identique dans les deux fichiers.
Onglet Network → erreur 404 sur fr.json ?
Le root path est incorrect → le fetch échoue → fallback sur les clés brutes. Vérifier le calcul root dans components.js.
Texte FR affiché même après switch EN ?
Vérifier localStorage → trv-lang. Si la clé existe dans fr.json mais pas dans en.json → le fallback s'applique et affiche le texte HTML initial (FR). Ajouter la traduction EN manquante.
data-i18n présent mais texte non traduit ?
La traduction est async — si la page est inspectée immédiatement après chargement, le DOM peut ne pas encore avoir été mis à jour. Attendre 200-500ms ou inspecter après que la page soit complètement chargée.
07
🟡 Symptôme — Recherche Ctrl+K ne s'ouvre pas ou résultats vides
Ctrl+K ne fait rien ?
components.js non chargé ou erreur JS. Vérifier la console. L'event listener Ctrl+K est dans components.js, à la fin du fichier.
Modal s'ouvre mais 0 résultats même pour "iron" ?
Le tableau searchIndex est vide ou les chaînes utilisent des caractères qui ne matchent pas la requête. Vérifier que les titres et excerpts sont bien en texte clair (pas que des Unicode escapes).
SyntaxError dans components.js après ajout d'une entrée ?
Apostrophe directe dans une string de searchIndex. Utiliser node --check assets/js/components.js pour localiser l'erreur. Remplacer par \u2019.
Normal — l'action du formulaire est encore "#". Configurer Formspree et remplacer action="#" par l'URL réelle. Voir doc-configuration.html section Formspree.
Erreur 422 Unprocessable Entity de Formspree ?
Un champ required est vide ou l'email est invalide. Vérifier la validation HTML5 (attribut required sur les champs).
Erreur CORS sur le fetch Formspree ?
Le header Accept: application/json est requis par Formspree pour les soumissions AJAX. Vérifier qu'il est bien présent dans le fetch de main.js.
Formulaire soumis mais pas d'email reçu ?
Vérifier le spam folder. Dans Formspree Dashboard → Form → Submissions → vérifier si la soumission est arrivée. Si oui, configurer l'email de notification dans Form Settings.
09
⚪ Erreurs console normales — À ignorer
Erreur console
Cause
Action requise
beacon.min.js CORS / sha512 integrity mismatch
Script analytics Cloudflare Web Analytics bloqué par Firefox Enhanced Tracking Protection. Non bloquant.
Aucune — comportement normal avec ETP activé
babel.min.js.map 404
Source map manquante dans truvector-promo.html (page promo avec JSX Babel in-browser).
Cosmétique — ne pas modifier sauf refonte de la promo
truvector-cube.png 404
Image déclarative dans truvector-promo.html mais absente du repo.
Ajouter l'image dans assets/ ou retirer la référence dans truvector-promo.html
You are using the in-browser Babel transformer
truvector-promo.html utilise Babel en mode navigateur pour du JSX/React. Avertissement de performance.
Non critique pour la prod — la promo est dans un iframe isolé
[TRV] i18n load failed for en — falling back to FR
fetch de en.json a échoué (réseau, 404, CORS). Le fallback FR s'applique automatiquement.
Vérifier que assets/i18n/en.json est committé et accessible
10
Tests manuels avant déploiement
ℹ
Ces tests doivent être effectués localement (python -m http.server 8080) avant chaque git push sur main.
Tests prioritaires (toujours)
□ Logo visible sur index.html
□ Logo visible sur legal/mentions-legales.html
□ Logo visible sur products/ironlock/index.html
□ Thème dark → navbar textes visibles
□ Thème light → navbar textes visibles
□ Thème contrast → contraste maximal OK
□ Thème persisté après F5
□ Switch FR → textes changent
□ Switch EN → textes changent
□ Langue persistée après F5
□ Ctrl+K → modal s'ouvre
□ Taper "iron" → résultats apparaissent
□ Escape → modal se ferme
Tests mobile et formulaires
□ DevTools mobile (< 768px) → hamburger visible
□ Hamburger → menu s'ouvre
□ Clic lien dans menu → menu se ferme
□ Lien actif correct sur chaque page visitée
□ contact.html → formulaire avec action="#" → message d'avertissement Formspree apparaît
□ pricing.html → formulaire → idem
□ downloads/index.html → glisser un fichier → SHA-256 calculé
□ blog/index.html → bouton filtre "Crypto" → seuls articles crypto visibles
□ Scroll reveal → cards apparaissent progressivement
□ Boutons "Copier" sur les blocs code → feedback ✓ Copié !
11
Commandes de vérification
JavaScript
# Syntaxe components.js
node --check assets/js/components.js
# Syntaxe main.js
node --check assets/js/main.js
# Test calcul root path pour toutes les profondeurs
node -e "
['/','index.html','/about.html',
'/legal/x.html','/blog/x.html',
'/products/ironlock/x.html'].forEach(p => {
const s = p.split('/').filter(Boolean);
const d = Math.max(0, s.length - 1);
const r = d === 0 ? './' : '../'.repeat(d);
console.log(p.padEnd(32), '->', JSON.stringify(r));
});
"
CSS & HTML
# Accolades CSS équilibrées
python3 -c "
c = open('assets/css/truvector.css').read()
o, cl = c.count('{'), c.count('}')
print(f'{{ = {o}, }} = {cl}')
print('OK' if o == cl else 'ERREUR DESEQUILIBRE')
"
# Vérifier que toutes les pages HTML ont# les éléments obligatoires
python3 -c "
import os
root = '.'
checks = ['data-theme','trv-theme','nav-placeholder',
'footer-placeholder','skip-link','components.js']
for d,_,fs in os.walk(root):
for f in fs:
if not f.endswith('.html'): continue
p = os.path.join(d, f)
c = open(p).read()
missing = [k for k in checks if k not in c]
if missing:
print(f'[{p}] manque: {missing}')
print('Scan terminé.')
"
Vérification des apostrophes dans searchIndex
# Détecter les apostrophes directes dans le searchIndex
python3 -c "
import re
content = open('assets/js/components.js').read()
# Chercher les lignes du searchIndex avec apostrophes suspectes
idx_start = content.find('const searchIndex')
idx_end = content.find('];', idx_start)
idx_block = content[idx_start:idx_end]
lines = idx_block.split('\n')
for i, line in enumerate(lines):
if \"'\" in line and 'd\\'in line or \"l\\'\" in line or \"n\\'\" in line:
print(f'Ligne {i}: {line.strip()[:80]}')
print('Scan terminé.')
"
12
Maintenance courante
Mettre à jour le copyright
Le copyright est géré dynamiquement dans components.js via new Date().getFullYear(). Il se met à jour automatiquement chaque année. Aucune action nécessaire.
Mettre à jour le sitemap
Après ajout d'une page : ajouter l'URL dans sitemap.xml, mettre à jour le lastmod sur les pages modifiées. Resoumettre dans Google Search Console si modifications majeures.
Vérifier les liens morts
Mensuel : utiliser un outil comme deadlinkchecker.com sur https://truvector.dev. Corriger les liens 404 dans les pages HTML concernées.
Renouveler le domaine
Vérifier la date d'expiration de truvector.dev dans le registrar. Cloudflare Registrar propose le renouvellement automatique. Activer si ce n'est pas fait.
Audit Lighthouse
Trimestriel : lancer Lighthouse (Chrome DevTools → Lighthouse) sur index.html et products/ironlock/index.html. Objectif : Performance ≥ 90, SEO = 100, A11y ≥ 90, Best Practices = 100.
Vérifier Core Web Vitals
Dashboard Cloudflare → Pages → truvector → Analytics → Web Vitals. Vérifier LCP < 2.5s, CLS < 0.1, FID < 100ms. Si dégradation → investiguer les nouveaux assets lourds.