From c38c2c56e92b6bf102dd4a5e55554b70c0f8a7f4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 23:17:11 +0000 Subject: [PATCH] =?UTF-8?q?SEO=20lot=204=20:=20titres,=20description=20des?= =?UTF-8?q?=20=C3=A9tudes=20de=20cas,=20favicon.ico?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Titre de l'accueil (PROFILE.seo.title, repris par og:title et twitter:title) : « Antoine Berthaud · Senior Product Manager Growth à Nantes » (57 caractères) / « Antoine Berthaud · Senior Growth Product Manager, Nantes » (56), au lieu de 66 / 65. - Description des études de cas (results.metaDescription, reprise par og:, twitter: et le JSON-LD CollectionPage) : 153 / 152 caractères au lieu de 174 / 177. - /favicon.ico (404 jusqu'ici) : icône 16, 32 et 48 px rendue depuis assets/favicon.svg ; balises inchangées. - undefined/formation-finale.png supprimé : capture d'écran committée par erreur dans la PR #67 et servie sur le domaine. - check-seo.mjs : critères du lot 4 (textes dans le HTML brut et le JSON-LD, longueurs, favicon). Trois textes que la mission ne fournit pas dépassent la limite et sont signalés sans faire échouer : titre des pages projet (65 / 63) et description française de l'accueil (163). - CLAUDE.md et README à jour ; le plan de travail sort de docs/, la mission étant terminée. check-seo.mjs : 167 critères au vert, captures identiques au pixel aux références du lot 0, générateur stable. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011xf6atLum6g68MUw2hi81e --- CLAUDE.md | 22 +++-- README.md | 5 +- docs/plan-seo-2026-09.md | 158 --------------------------------- en/index.html | 6 +- en/results.html | 8 +- favicon.ico | Bin 0 -> 4021 bytes index.html | 6 +- js/data.js | 11 +-- js/i18n.js | 5 +- results.html | 8 +- scripts/check-seo.mjs | 65 ++++++++++++++ undefined/formation-finale.png | Bin 70409 -> 0 bytes 12 files changed, 104 insertions(+), 190 deletions(-) delete mode 100644 docs/plan-seo-2026-09.md create mode 100644 favicon.ico delete mode 100644 undefined/formation-finale.png diff --git a/CLAUDE.md b/CLAUDE.md index fbe494d..89cb52f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,13 +5,16 @@ Lu automatiquement par Claude Code au démarrage d'une session dans ce dossier. ## Ce que c'est -**Dernier grand chantier** : la quatrième revue externe (7 sept. 2026), livrée -en huit PR — expériences rééquilibrées, hero à quatre chiffres, compétences -réduites et sans filtres, ordre des sections revu avec une section « Études de -cas », PDF court par défaut, quatrième étude sans chiffre. Chaque arbitrage est -dans « Décisions déjà prises » ci-dessous ; le plan de travail a été retiré une -fois terminé. `docs/` reste exclu du domaine dans `_config.yml` pour le -prochain document de travail. +**Dernier grand chantier** : la mission SEO du 24 sept. 2026 (audit de la +production), livrée en cinq PR (#71 à #75, dont un correctif du lien FR/EN) : +pages anglaises statiques sous `/en/`, pages projet statiques +sous `/projets/` et `/en/projects/`, données structurées complètes par page et +par langue, `lastmod` et `dateModified` tenus par le générateur, titres et +descriptions raccourcis, `favicon.ico`. `scripts/check-seo.mjs` vérifie le +tout et sert de test de non-régression ; les arbitrages sont dans « Décisions +déjà prises » ci-dessous. Avant elle, la quatrième revue externe (7 sept. +2026). `docs/` reste exclu du domaine dans `_config.yml` pour le prochain +document de travail. Site CV statique bilingue (FR/EN), HTML/CSS/JS vanilla, hébergé sur GitHub Pages avec domaine personnalisé `cv.antoine.berthaud.me`. Piloté par la @@ -368,7 +371,10 @@ plein ont tous les deux été essayés et jugés trop nets avant ça. régénérées laissait le `lastmod` figé (06/09 alors que la page avait changé le 07/09). Les PDF portent des métadonnées (titre, auteur, langue) posées par - `scripts/generate-pdf.js` via `pdf-lib`. + `scripts/generate-pdf.js` via `pdf-lib`. Titres ≤ 60 caractères, + descriptions ≤ 160 (Google coupe au-delà ; textes de la mission SEO du + 24/09/2026 dans `PROFILE.seo` et `results.metaDescription`) ; `favicon.ico` + multi-tailles à la racine, rendu depuis `assets/favicon.svg`. - **Site considéré fonctionnellement complet** (sept. 2026) : bilingue, PDF, Fit-Checker IA, hero cliquable + études de cas STAR, side project avec étude de cas, CI de génération/surveillance des PDF. Ne pas diff --git a/README.md b/README.md index e8197d6..38fd0cf 100644 --- a/README.md +++ b/README.md @@ -276,11 +276,10 @@ Je l'ai déjà appliqué sur tes expériences les plus récentes (AB Tasty, Ever ## 7. Favicon et aperçu de partage (Open Graph) -- **Favicon** : icône violette avec tes initiales "AB", déjà en place (`assets/favicon.svg` + fallbacks PNG pour les navigateurs/appareils plus anciens). Rien à faire. +- **Favicon** : icône violette avec tes initiales "AB", déjà en place (`assets/favicon.svg` + fallbacks PNG pour les navigateurs/appareils plus anciens). `/favicon.ico` à la racine (16, 32 et 48 px, rendu depuis le SVG) répond aux navigateurs et robots qui le demandent directement, sans lire les balises `` — il renvoyait une 404 jusqu'en septembre 2026. Rien à faire. - **Image de partage** (ce qui s'affiche quand tu partages le lien du CV sur LinkedIn, Slack, etc.) : déjà générée dans `assets/og-image.png` à partir de ta photo et de tes infos actuelles. -- **⚠️ À faire une fois déployé** : dans `index.html`, remplace les deux occurrences de `https://REMPLACE-PAR-TON-DOMAINE.example` (balises `og:image`, `og:url`, `twitter:image`) par ta vraie URL, ex. `https://tonpseudo.github.io/cv`. Sans ça, l'aperçu ne s'affichera pas correctement quand tu partageras le lien. - Si tu changes significativement ton nom/rôle/photo plus tard, dis-le-moi et je régénère `og-image.png` en conséquence (c'est une image statique, pas générée dynamiquement). -- Ces balises restent toujours en français : les robots des réseaux sociaux n'exécutent pas le JavaScript, donc ils ne voient jamais la bascule EN. C'est normal et sans conséquence pour le site lui-même. +- Titres et descriptions des aperçus sont ceux de la page, dans sa langue : partager `/en/` donne un aperçu en anglais (après passage dans le Post Inspector de LinkedIn, qui garde longtemps l'ancien aperçu en cache). Longueurs : 60 caractères au plus pour un titre, 160 pour une description (Google coupe au-delà) ; `check-seo.mjs` le vérifie. ## 8. Analytics respectueux de la vie privée (GoatCounter) diff --git a/docs/plan-seo-2026-09.md b/docs/plan-seo-2026-09.md deleted file mode 100644 index d00f977..0000000 --- a/docs/plan-seo-2026-09.md +++ /dev/null @@ -1,158 +0,0 @@ -# Mission SEO de septembre 2026 — état des lieux et plan (lot 0) - -Document de travail (exclu du domaine par `_config.yml`), à retirer une fois -la mission terminée, comme le plan de la quatrième revue. - -## 1. Le constat, point par point - -Mesuré le 24/09/2026 sur la production et dans le code. - -| Point du constat | Verdict | Où | -|---|---|---| -| `/?lang=en` = même HTML que `/` (49 746 octets, même empreinte), `lang="fr"`, canonical `/` | ✅ confirmé | `curl` des deux URL : 49 746 octets, md5 identique | -| `app.js` passe `lang` en `en`, réécrit titre, description, og:/twitter:, canonical `/?lang=en` | ✅ confirmé | `js/app.js` 500-530 (`applyStaticTranslations`) | -| Script `lang-pending` qui masque la page sur `?lang=en` | ✅ confirmé | `` des trois pages + `css/style.css` | -| `results.html?lang=en` : anglais par JS, canonical `results.html`, pas de hreflang | ✅ confirmé | voulu jusqu'ici (CLAUDE.md, « seule l'accueil a une version EN indexable ») : **la mission revient sur cette décision** | -| Liens internes via `window.i18n.langSuffix()`, y compris `project-detail.html?slug=…` | ✅ confirmé | `app.js`, `results.js`, `project-detail.js` | -| Page projet : HTML pré-rendu de Tour de Growth quel que soit le slug, canonical statique `?slug=tour-de-growth` | ✅ confirmé | `generate-static.js` (`PAGES[2]`), `project-detail.html` | -| Slug inconnu : « Projet introuvable » + `noindex` posé par JS | ✅ confirmé | `project-detail.js` (`renderNotFound`) | -| Description de la page projet : 97 caractères, sans Antoine ni l'outil ; pas de JSON-LD | ✅ confirmé | description = `tagline` de `PROJECT_DETAILS` | -| Accueil : `ProfilePage` → `Person` `#person`, `sameAs` avec tourdegrowth.com, pas de `dateModified` | ✅ confirmé | **écrit à la main** dans `index.html` (pas généré) | -| `results.html` et page projet sans JSON-LD | ✅ confirmé | | -| Titre de l'accueil 66 car. (FR), 65 (EN) | ✅ confirmé | `PROFILE.seo.title` | -| Description de `results.html` 174 caractères | ✅ confirmé | `results.metaDescription` (`i18n.js`) | -| `/favicon.ico` en 404 | ✅ confirmé | | -| Ce qui est bien fait (robots, sitemap, HTTPS, OG, h1, alt, logos, PDF, mobile, noindex, GoatCounter) | ✅ confirmé | `scripts/check-seo.mjs --lot 0` : 26/26 | - -**Où se décide quoi aujourd'hui** - -- Langue au chargement : `initLangFromUrl()` (`app.js`), `initLang()` (`results.js`, - `project-detail.js`) — `?lang=en` sinon français ; jamais le navigateur. -- `?lang=en` produit par `langSuffix()` et `syncUrl()` (`i18n.js`), lu par les trois - scripts de page, par le script `lang-pending` et par la fonction GoatCounter du ``. -- Canonical réécrit par `app.js` (accueil) et `project-detail.js` (slug) ; titre, - description, og:/twitter: réécrits par les trois scripts. -- JSON-LD : écrit à la main dans `index.html`. -- `` : le workflow `generate-pdf.yml` les met à la date du jour pour chaque - fichier **que la CI elle-même a modifié** ; les pages générées sont committées - (par la CI sur `main`, ou dans la PR quand on régénère en local). - -## 2. Écarts et points à trancher (aucun ne contredit le constat) - -1. **`lastmod` figés** : `/` et `/results.html` sont au 06/09 alors que `index.html` - et `results.html` ont changé le 07/09. Quand une PR arrive avec ses pages déjà - régénérées, la CI ne voit aucun écart et ne touche pas au sitemap. Conséquence - pour le lot 3 : `dateModified` = `lastmod` hériterait d'une date fausse. - Proposition (lot 3) : c'est le générateur qui pose `lastmod` et `dateModified`, - à la date du jour quand la page générée change, idempotent sinon. -2. **Clone superficiel en CI** : `actions/checkout@v5` récupère un seul commit, donc - `git log --reverse -- index.html` y rendrait la date du dernier commit. Pour - `dateCreated` (lot 3), passer `fetch-depth: 0` dans `generate-pdf.yml` et - `pr-checks.yml`, et faire échouer le générateur sur un clone superficiel plutôt - que d'écrire une date fausse. Premier commit de `index.html` : 22/08/2026. -3. **Liens vers Tour de Growth au lot 1** : « garde leur fonctionnement actuel » - (donc `&lang=en` sur les pages anglaises) et le critère 3 (« plus aucun - `lang=en` ») se contredisent pour ces liens-là. Je garde le fonctionnement - (un visiteur anglais arrive sur la page projet en anglais) et `check-seo.mjs` - tolère ces liens jusqu'au lot 2, comme au critère 8. -4. **JSON-LD de `/en/results.html`** : le critère 2 renvoie aux vérifications du - critère 1, dont le JSON-LD ; le JSON-LD des études de cas n'est défini qu'au - lot 3. Vérifié au lot 3. -5. **`