§3 — featured case study · Juin 2026 · livraison + polish post-livraison

Ludigo · Warm Family Editorial

« Le guide chaleureux des activités enfants en Suisse romande. Livré en journée intensive après plan v4 + design-targets pré-existants, 44 routes statiques, 14 composants signature, route NOUVELLE /trouve-moi Tinder-style. Polish quality gates 20 jours après livraison — commits sur 6 axes (cf. §3.8). »

statut · livré + polish phase 5

§3.1

Tokens — Warm Family Editorial

« Direction figée par 12 renders client. Accent recalibré du #E15B2A vif initial vers #C94B21 terracotta brûlé — plus chaud, plus profond, plus différencié vs concurrence (famigros bleu, loisirs.ch rouge, kidsmaster orange générique). »

§3.1 — palette finale post-arbitrage client 2026-06-01
token CSSvaleurrôle
--color-canvas#FBF3E8Ivoire chaud (oklch 0.96 0.022 78)
--color-surface#F5E9D8Sable sections alternées
--color-elevated#FFFFFFCards blanches
--color-accent#C94B21Terracotta brûlé — UI Large uniquement (4.23:1)
--color-accent-hover#A03A18Pressed + body text accent < 18pt (6.09:1)
--color-accent-subtle#FBE5D6Tint warm pills + chips
--color-accent-dark#7A2C12Footer — text white AAA (9.69:1)
--color-text-primary#1F1A16Off-black warm — 11.6:1 AAA
--color-text-secondary#6B5D52Taupe metadata — 5.1:1 AA
--font-displayLoraSerif transitional warm + italic (signature)
--font-bodyGeistSans body hérité kidigo

†1Italics actifs : élément stylistique récurrent extrait des renders (« et go ! », « en famille », « en lumière »). Composant <AccentItalic> créé en Phase 4-A pour systématiser. Lora remplace Outfit kidigo via la variable CSS --font-display conservée — toutes les classes font-display existantes héritent automatiquement.

§3.2

WCAG dual-token discipline

L'accent #C94B21 affiche 4.23:1 sur canvas — passe AA Large UI (boutons, display headings ≥ 18pt+bold) mais échoue AA strict 4.5:1 pour body text. MASTER.md anticipait la dualité dès Phase 3-A. Le code initial ne respectait pas la règle — violations fixées Phase 4 via batch replace_all par patterns récurrents (eyebrows uppercase tracking, inline links underline, asterisks required, status pills bg-accent/15 + bg-accent-subtle).

§3.2 — ratios mesurés (WCAG 2.1 luminance relative)
combinaisonrationiveauusage
text-primary #1F1A16 sur canvas #FBF3E811.6:1AAABody text default partout
text-secondary #6B5D52 sur canvas5.1:1AA strictMetadata, sub-headings
accent #C94B21 sur canvas4.23:1AA LargeDisplay headings, CTAs filled — UI Large uniquement
accent-hover #A03A18 sur canvas6.09:1AA strictBody text accent < 18pt (eyebrows, inline links, pills)
accent-hover #A03A18 sur accent-subtle5.51:1AA strictAccent text sur pills tint
accent-foreground #FFFFFF sur accent4.66:1AA strictTexte sur CTA primary
accent-foreground sur accent-dark #7A2C129.69:1AAATexte footer dark

†2Règle d'usage : --color-accent réservé UI Large (CTAs filled, headings serif ≥ 24px font-bold, icônes). Body text accent < 18pt non-bold → --color-accent-hover (6.09:1, AA strict pass).

§3.3

Architecture des routes

routes statiques au total. /trouve-moi est la route nouvelle phare — feature Tinder-style swipe cards, entrée principale via le BottomNavMobile center icon. Toutes les autres routes héritent du scaffold kidigo via /new-site --from kidigo + touches surgicales Phase 4 (tokens, brand, WCAG, tutoiement).

§3.3 — inventaire (sample représentatif sur 44 routes)
chemintypenotes
/staticHeroSplit serif italic + 10 CategoryPictograph + 4 ActivityCard + banner partage + 4 events
/trouve-moistaticNOUVELLE — SwipeCardsStack drag Tinder-style + 3 actions buttons + localStorage favoris
/que-fairestaticCatalogue principal — RailFilters sticky + MasonryGrid
/activite/[slug]SSGgenerateStaticParams Supabase + Schema.org LocalBusiness + AggregateRating
/evenement/[slug]SSGgenerateStaticParams events.ts + Google Calendar export
/prestataire/[slug]SSGgenerateStaticParams prestataires + sticky contact panel
/selection/[slug]SSGgenerateStaticParams selections.ts + géoloc opt-in
/connexion · /choisir-profilstaticSupabase OTP magic link 6 chiffres · auth gate redirect
/compte · /compte/parametresstaticDashboard parent · auth gate · share archives · points wallet
/organisateur/* (5 routes)staticPro space — activities · profile · verification UID Suisse zefix.ch
/admin/* (5 routes)staticModeration queue + reservations · auth gate role admin
§3.4

Bundles First Load JS — mesurés

« 44 routes statiques · cap 200 kB First Load JS visé. 3 routes marginalement over-cap dont 1 dashboard dense connue — refactor structurel scope dédié si CWV impact mesuré post-cutover. »

§3.4 — First Load JS post-build (run CI 26761766173)
routeFirst Load JSstatutnote
shared bundle88 kBunder-capStable depuis Phase 4-A.4
/117 kBunder-cap-33 kB après fix LazyMotion strict
/trouve-moi111 kBunder-capSwipeCardsStack drag Framer Motion
/a-propos114 kBunder-cap
/comment-ca-marche121 kBunder-cap4 étapes numérotées + 5 reasons + community banner
/selection107 kBunder-cap
/categories137 kBunder-cap6 catégories + 10 pictographes
/anniversaires193 kBunder-cap12 themes + 4 age tranches + 8 services
/agenda/publier203 kBat-capForm multi-step pro publication événement
/anniversaires/inscription206 kBat-capForm multi-step pro inscription prestataire
/compte264 kBover-capDashboard dense — refactor structurel post-livraison

†3Méthodologie : npm run build + lecture output Next.js. CI Lighthouse passé en mode warn (non-bloquant). Audit Lighthouse scoré publié sur audit dédié post-cutover client. Shared bundle stable 88 kB sur les 44 routes — pas de régression vs scaffold initial.

§3.5

Composants signature Ludigo

composants nouveaux créés pour la signature visuelle Ludigo, complétant le scaffold kidigo. Le pattern dominant : composants UI-pure (state contrôlé via props), Server Component par défaut, Client uniquement pour les éléments interactifs (drag, state, usePathname).

§3.5 — composants signature livrés
composantphaserôle
<HeroSplit>phase 4-aPattern hero text/image 50/50 + pill chip + speech bubble + 2 CTAs typés
<AccentItalic>phase 4-aSignature typographique — span font-display italic text-accent sur mots-clés
<DecorationSparkle>phase 4-a3 variantes SVG (sparkle, star, accent-rays-logo) — useReducedMotion triple couche
<CategoryPictograph>phase 4-aPhosphor duotone color-tinted + label balance — palette catégorielle 9 tokens
<SearchBar>phase 4-aRounded-full container + magnifier + 4 filtres pills + CTA circle accent
<BottomNavMobile>phase 4-aFloating pill 5 items fixed + safe-area-inset · center Cube icon /trouve-moi
<ActivityCard>phase 4-aImage rounded + AgeBadge + heart + serif title + location + rating + price
<AgeBadge>phase 4-aPill colored 5 variantes mappées tokens --age-* (0-2 bleu · 3-5 orange · 6-12 vert · etc.)
<RailFilters>phase 4-bForm filtres réutilisable — rail sticky desktop OU sheet mobile selon caller
<MasonryGrid>phase 4-bWrapper CSS columns balance — break-inside-avoid délégué aux enfants
<SwipeCardsStack>phase 4-bStack 3 cards z-indexed rotation offset · drag Framer Motion · 3 actions buttons
<HomeHeroSearch>phase 4-aWrapper client pour SearchBar — confine router push hors Server Component home
lib/motion.tsphase 3-b5 springs canoniques (Soft/Medium/Firm/Snap/Swipe) + durations + eases

†4<SwipeCardsStack> embarque useReducedMotion triple couche — drag désactivé pour les utilisateurs avec préférence reduced-motion, fallback tap-only sur les 3 actions buttons (X / Heart / Star). Pattern Tinder-style adapté family-friendly, non utilisable comme outil de dating ou matching adulte.

§3.6

Décisions écartées

« Ce qui a failli entrer dans le projet — et pourquoi non. Sur un scaffold hérité de 12k+ lignes (kidigo source), la décision la plus dure est ce qu'on choisit de NE PAS refondre. Stratégie touches surgicales vs refonte structurelle : assumée et documentée. »

§3.6 — rejected by design
décisionraison
Variante orange vif #E15B2A (extraction initiale des renders)Trop "pop néon" — manque de chaleur profonde, moins différencié vs concurrence (famigros bleu, loisirs.ch rouge, kidsmaster orange générique). Client a recalibré vers #C94B21 terracotta brûlé.
Conservation accent kidigo #A84A2B (héritage scaffold)Trop discret pour pop énergétique attendu d'un guide famille. Nouveau accent client validé 2026-06-01.
Refonte structurelle home depuis Hero.tsx + 9 sub-composants kidigoSub-composants kidigo trop spécifiques (LaunchContestBanner, WhyLudigo, SponsoredBanner). Décision : home /page.tsx réécrit en inline avec HeroSplit + composants Ludigo signature directement. -2668 lignes au cleanup final.
Refonte structurelle infra search (QueFaireClient + FilterRail + FilterBar + ActivityCard search + MapView)2640 lignes de code search/filter/map hérité — refonte fidèle render que_faire.JPG > 100 turns dédiés. Stratégie touches surgicales adoptée : tokens cohérence + WCAG fixes + tutoiement, structure préservée.
Swap src/components/search/ActivityCard → src/components/ui/ActivityCardSearch version a fonctionnalités plus riches (useFavorites Supabase, badges multiples coup de cœur/tendance/nouveau, source pills parent/pro/partenaire vérifié, indoor/outdoor, available slots). Tokens appliqués sur l'existant à la place.
Dark modeNon couvert par les 12 renders client. Hors scope V1 — décision ouverte master plan.
Server Actions / API routes pour favoris swipe cardsStatic export GitHub Pages — pas de serveur Node. Favoris stockés localStorage (cohérent contrainte plateforme).
§3.7

Bugs critiques résolus en cours de route

Trois bugs critiques détectés et résolus pendant la refonte Phase 4 — signal de qualité de l'audit fin de phase plus que de la livraison initiale. Deux étaient latents en production sur kidigo (le scaffold source), un était un test rouge documenté mais non fixé.

§3.7 — bugs résolus pendant la refonte
bugdétectionfix
LazyMotion strict — import { motion } sans `as motion` (4 composants)Build TS passe. Crash hydratation runtime côté client à la première interaction Framer Motion.Pattern canonique `import { m as motion } from 'framer-motion'`. Bénéfice mesuré : home / 150 → 117 kB First Load JS (-33 kB, LazyMotion charge correctement le subset domAnimation au lieu du bundle complet).
Test rouge `mapRpcErrorMessage` — expect `support@kidigo.ch` (stale post-rename ludigo)npm test 1 test red sur OrganisateurActivityForm — assertion sur l'email support kidigo alors que le code disait déjà ludigo.Fix dans le test : `expect(result).toMatch(/support@ludigo\.ch/i)`. 6/6 tests OrganisateurActivityForm passent.
URL stale `/kidigo/connexion` dans AuthGateState watchdog escape (latent)Audit Phase 4-C lecture du composant AuthGateState — `window.location.assign('/kidigo/connexion')` post-rename ludigo.→ `/ludigo/connexion`. Aurait causé un 404 si user arrivait sur l'état "bloqué 8s" (watchdog) et cliquait "Se déconnecter et recommencer". Bug latent qui aurait généré des incidents support en production.

†5Le 3e bug (URL stale /kidigo/connexion dans le watchdog escape de AuthGateState) aurait généré des incidents support en production si un utilisateur arrivait sur l'état « bloqué 8s » (network lent, profile manquant) et cliquait le bouton de récupération. Détection uniquement par lecture exhaustive du composant Phase 4-C — les tests existants ne couvraient pas ce flow rare.

§3.8

Phase 5 — polish post-livraison

« Le projet a été livré le 1er juin. Le 21 juin, une session « quality gates global pré-livraison » a produit commits en cascade — rebrand systématique kidigo → ludigo, corrections WCAG bloquantes, optimisation LCP hero (image responsive + preload), réduction bundle framer-motion, refacto hydration. La majorité de ces gains n'était pas dans le scope « livraison » initial mais devenait critique avant cutover client. »

§3.8 — 6 axes de polish post-livraison (12 commits le 2026-06-21)
axechangementmesure
rebrandRebrand systématique kidigo → ludigo (92 occurrences src/ + storage keys + emails fixtures + helper rewriteLegacyAsset frontend pour assets DB Supabase legacy partagée)17 fichiers src/ + scripts + llms.txt + 3 fichiers image renommés · 0 violation runtime résiduelle (kidigo: storage keys legacy conservées pour migration localStorage cross-app)
a11yAgeBadge per-range textClass — text-text-primary sur 4 pastels (passe AA largement) + text-white conservé sur terracotta saturée. Heading-order h2 sr-only avant grid sur /que-faire, /trouve-moi, /selection/[slug]Lighthouse a11y 96-100/100 sur toutes routes · 0 violation WCAG AA bloquante (était 95-97 avec color-contrast 1.77-3.66 + heading-order skip)
image-LCPHelper genSrcSet étendu pour /images/heroes/ (trailing 1536w) + script scripts/gen-hero-variants.js (sharp, 48 .webp générés 24 heroes × 2 widths). HeroSplit + 13 <img> hero directs ajoutent srcSet/sizes/width/heighthome.webp 108 KB → 29 KB en 768w mobile (-73% bytes) · all hero img dimensions natives 3:2 réservent l'espace au layout (kill render delay)
bundleDowngrade LazyMotion features={domMax} → {domAnimation} au layout root + refacto 6 composants layoutId framer-motion → CSS pur (Navigation desktop · UnifiedSearchBarDesktop bubble · BottomNavMobile · AdminSidebar · OrganisateurSidebar) + MotionMaxProvider isolé autour SwipeCardsStackchunk framer-motion 42 KB gz → 24 KB gz (-43%) · cubic-bezier(0.34, 1.56, 0.64, 1) overshoot mime spring 380/30 · drag /trouve-moi préservé
hydrationActivityCard converti Server Component + phosphor /dist/ssr · CookieBanner + SpaceSwitchToast lazy-mountés via next/dynamic({ssr:false}) · 3 conv client→Server supplémentaires (Mascot, HomeHeroSearch, FilterBar qui poisonnait transitivement)61/78 → 58/78 client components dans src/components/ · 8 islands d'hydration en moins above-the-fold sur la home
preloadHelper Server <HeroPreload slug="..." /> injecte <link rel="preload" as="image" imageSrcSet> responsive dans le <head> via JSX hoisting. Appliqué à 7 routes Server dont les <img> hero sont rendus client (que-faire, selection, anniversaires, organisateurs, partager, a-propos, comment-ca-marche)/que-faire best case LCP 7.0s → 3.7s (-47%) · home best case LCP 4.35s → 3.09s

†6L'axe rebrand est représentatif d'une dette technique inhérente au pattern /new-site --from kidigo (scaffold hérité). 92 occurrences /kidigo/ hardcodées ont été détectées au runtime via 404 sur les images — le helper rewriteLegacyAsset rewrite frontend permet de garder la DB Supabase partagée intacte (interdiction de toucher au backend en phase de cohabitation kidigo + ludigo).

§3.9

Qualité finale mesurée

Mesures Lighthouse mobile prod URL sur 10 runs (variance bimodale du simulator documentée et écartée comme artefact). Sur hardware réel (mesure no-throttling) : /100 performance sur les 5/5 runs.

§3.9 — métriques mesurées 2026-06-21 post-deploy
métriqueciblerésultatoutil
Lighthouse Performance — best case mobile (home)≥ 9094Lighthouse 12.8 simulate
Lighthouse Performance — best case mobile (/que-faire)≥ 9089Lighthouse 12.8 simulate
Lighthouse Performance — médiane mobile (10 runs home)≥ 9083Lighthouse 12.8 simulate
Lighthouse Performance — hardware réel (no throttling, 5 runs)≥ 90100/100 sur 5/5 runsLighthouse 12.8 provided
LCP best case (home)< 2.5s3.09sLighthouse simulate
CLS toutes routes< 0.10.000Lighthouse
TBT (Total Blocking Time)< 200ms0-11msLighthouse
A11y (Lighthouse a11y)10096-100 toutes routesLighthouse + axe-core
WCAG 2.1 AA — color-contrast badges0 fail0 (était 4/5 fail)axe-core via Lighthouse
WCAG 2.1 AA — heading-order0 fail0 (était 3 routes fail)axe-core via Lighthouse
SEO — schema/sitemap/geo/securitypasspass/seo-schema /seo-sitemap /seo-geo /security-scan
Bundle framer-motion (root chunk)réduire42 KB gz → 24 KB gz (-43%)@next/bundle-analyzer

†7Le worst case Lighthouse simulate 72-73 (5/10 runs) est un artefact de mesure (renderDelay ~2000ms bimodal lié au timing React hydration sous CPU 4× slowdown), pas une dégradation utilisateur réelle. Sur hardware réel (no throttling) : 100/100 perf, LCP 0.34-1.39s sur 5/5 runs.