# Audit de `src/app/globals.css` et de la base `html` / `body` — 2026-08

Base : `dev` @ 029283d. Vérifications : `bun run build` (Next 16.2.9, Turbopack),
lecture du CSS produit (`.next/static/chunks/*.css`), sondes Playwright/Chromium
sur un serveur `next start` local. Les tailles et occurrences citées viennent de
ce build, pas d'une lecture à l'œil.

Convention de la colonne **Décision** :

- **Fait** — appliqué dans le commit `refactor(theme): tidy globals.css` ;
  strictement sans effet sur le rendu, preuve à l'appui.
- **Reco** — changerait un rendu, un comportement ou l'API du design system :
  laissé ici, pas dans le commit.
- **RAS** — soupçon levé, rien à faire.

---

## 1. Structure Tailwind v4

| #   | Constat                                                                                                                                                                                                                                                                                                                       | Décision                                         |
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| 1.1 | L'ordre est bien celui d'un projet v4 : `@import "tailwindcss"` en tête, puis `@custom-variant`, `@theme`, `@theme inline`, `:root` / `.dark` **hors** de tout `@layer`, `@layer base`, `@layer components`. La règle du skill `tailwind-v4-shadcn` « ne jamais mettre `:root` ou `.dark` dans `@layer base` » est respectée. | RAS                                              |
| 1.2 | Les deux `@utility` (`container`, `shell`) étaient insérés **entre** `@theme` et `@theme inline`, coupant en deux la déclaration du thème. L'ordre des `@utility` est sans effet (Tailwind trie les utilities lui-même).                                                                                                      | Fait — déplacés au-dessus de `@layer components` |
| 1.3 | Les couleurs sont écrites en hexadécimal, pas en `hsl()` comme le montre le skill. Le skill impose surtout de **ne pas double-emballer** (`hsl(var(--x))`) : ici `@layer base` et `@theme inline` référencent `var(--x)` nu. Aucun double emballage.                                                                          | RAS                                              |
| 1.4 | Trois commentaires commencent par « Modern Editorial » (accents or dans `@theme`, thème clair avant `:root`, motion avant les keyframes). Ce sont trois sections distinctes, pas un doublon.                                                                                                                                  | RAS                                              |

## 2. Les trois `@import`

| #   | Constat                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Décision                               |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| 2.1 | `@import "shadcn/tailwind.css"` était soupçonné mort (rapport de phase 1 : « `node_modules/shadcn` ne livre pas ce CSS »). **Faux.** Le paquet l'expose via la condition `style` de ses `exports` (`"./tailwind.css": { "style": "./dist/tailwind.css" }`) et le fichier existe (666 o). Il livre les variants `data-open` / `data-closed` / `data-checked` / `data-unchecked` / `data-selected` / `data-disabled` / `data-active` / `data-horizontal` / `data-vertical`, les keyframes accordion et l'utility `no-scrollbar`. Preuve : 24 occurrences de `data-checked` dans le CSS produit ; `select.tsx`, `field.tsx`, `separator` etc. les consomment (17 usages dans `src`). **Supprimer cet import casserait les composants shadcn.** | RAS — import vivant, à ne plus toucher |
| 2.2 | Corollaire de 2.1 : `.no-scrollbar` était déclaré **deux fois** — par `@utility` dans `shadcn/tailwind.css` et par `@layer components` dans `globals.css`, avec des déclarations identiques. Les deux blocs sortaient dans le CSS produit (4 sélecteurs au lieu de 2).                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Fait — le doublon local est supprimé   |
| 2.3 | `@import "tw-animate-css"` : paquet présent (v1.4.0). Consommé par `src/components/ui/select.tsx` (`data-open:animate-in`, `fade-in-0`, `zoom-in-95`, `slide-in-from-*`, `data-closed:animate-out`…).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | RAS — vivant                           |
| 2.4 | Les keyframes `accordion-down` / `accordion-up` de `shadcn/tailwind.css` ne sortent pas dans le CSS produit : aucun composant accordion n'est installé. Tailwind les élague tout seul.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | RAS                                    |

## 3. Palette sombre et `color-scheme`

| #   | Constat                                                                                                                                                                                                                                                                                                                                                                                                        | Décision                                                                                                                                                                                                                                        |
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 3.1 | Le bloc `.dark` (26 tokens, dont `--background: #1c1b1b`) n'est **jamais** activé : aucun `ThemeProvider`, aucun `next-themes`, aucun `classList.toggle` dans `src` ; `viewport.colorScheme = "light"` dans `src/app/[locale]/layout.tsx` émet `<meta name="color-scheme" content="light">`. Il pèse quand même dans le CSS livré (47 occurrences de `.dark`, variants `dark:` des composants shadcn compris). | **Reco** — le supprimer allégerait le CSS mais retirerait le filet des `dark:` que shadcn embarque dans `button.tsx`, `input.tsx`, `select.tsx`, `field.tsx`. Décision produit (le site aura-t-il un jour un thème sombre ?), pas un nettoyage. |
| 3.2 | `@custom-variant dark (&:is(.dark *))` n'a de sens que tant que 3.1 reste. Il définit `dark:` sur les **descendants** de `.dark`, pas sur `.dark` lui-même — c'est la valeur par défaut de shadcn, pas un bug.                                                                                                                                                                                                 | RAS — lié à 3.1                                                                                                                                                                                                                                 |
| 3.3 | `color-scheme` n'est **pas** déclaré en CSS ; il vient de l'export `viewport`. Une seule source, c'est bien. C'est aussi ce qui rend le canvas UA blanc, d'où le bug corrigé au commit précédent.                                                                                                                                                                                                              | RAS                                                                                                                                                                                                                                             |

## 4. La déclaration suspecte des lignes 283-287

| #   | Constat                                                                                                                                                                                                      | Décision                                                                   |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| 4.1 | Ce n'est pas une règle `html`, c'est `::selection { background: #e9c176; color: #261900 }` — la couleur de sélection de texte, légitime. `#e9c176` est **exactement** `--color-gold-bright`, recopié en dur. | Fait — remplacé par `var(--color-gold-bright)` (valeur calculée identique) |
| 4.2 | `#261900` (brun très sombre, texte posé sur la sélection) n'a aucun token. Le sortir en token serait un ajout à l'API du design system, pas un nettoyage.                                                    | **Reco** — commenté sur place en attendant                                 |

## 5. Keyframes et utilities de motion

| #   | Constat                                                                                                                                                                                                                                                      | Décision |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| 5.1 | `editorial-bounce` et `editorial-pulse` étaient soupçonnés morts. **Faux** : `.bounce-slow` est utilisé par `production/ProcessHero.tsx`, `.pulse-slow` par `production/ProcessXray.tsx`, `.float-slow` par `ui/RjcHallmark.tsx` et `home/AboutSection.tsx`. | RAS      |
| 5.2 | `reveal-in`, `rule-grow-in`, `.reveal`, `.reveal-stagger`, `.rule-grow` : tous consommés (`ui/Rule.tsx`, `ui/NumberedCardGrid.tsx`, 6 sections).                                                                                                             | RAS      |
| 5.3 | `@media (prefers-reduced-motion: reduce)` neutralise bien `.float-slow` / `.bounce-slow` / `.pulse-slow`, et le bloc `@supports` des reveals est déjà gardé par `prefers-reduced-motion: no-preference`.                                                     | RAS      |

## 6. `@layer base`, `html` et `body`

| #   | Constat                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Décision                                                                |
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| 6.1 | Le lissage de police n'est déclaré **qu'une** fois : `antialiased` sur `<html>` dans `layout.tsx`. `globals.css` ne pose ni `-webkit-font-smoothing` ni `-moz-osx-font-smoothing`. Le template Tailwind les met dans `@layer base` ; ici c'est l'autre source, mais il n'y en a qu'une.                                                                                                                                                                           | RAS                                                                     |
| 6.2 | `font-sans` était en revanche déclaré **deux** fois : `@layer base { html { @apply font-sans } }` et l'utility `font-sans` sur `<body>`. Mesuré avant changement : `htmlFont === bodyFont`, donc l'une des deux ne sert à rien. La règle `html` est gardée (le `<html>` porte désormais aussi le fond de canvas, et `::selection` / la barre de défilement héritent de lui).                                                                                      | Fait — `font-sans` retiré de `<body>`                                   |
| 6.3 | Le template Tailwind propose `html.h-full` + `body.h-full.flex.flex-col` ; le projet avait `html.h-full` + `body.flex.min-h-dvh.flex-col`. `min-h-dvh` est **meilleur** que `h-full` sur `body` : il suit la barre d'URL mobile et laisse le contenu dépasser, là où `h-full` fige la hauteur à celle du viewport. Et `h-full` sur `html` était activement nuisible (voir 6.4). Le couple retenu est donc `html` (hauteur auto) + `body.min-h-dvh.flex.flex-col`. | Fait (commit précédent)                                                 |
| 6.4 | `h-full` sur `<html>` bornait la boîte racine à la hauteur du viewport — 664 px mesurés pour un document de 10 485 px. La zone de positionnement du fond de canvas n'atteignait donc jamais le bas de page.                                                                                                                                                                                                                                                       | Fait (commit précédent)                                                 |
| 6.5 | `scroll-smooth` est posé sans condition sur `<html>`. Un utilisateur `prefers-reduced-motion: reduce` subit quand même le défilement animé des ancres. `motion-safe:scroll-smooth` réglerait le point (WCAG 2.3.3 AAA, mais surtout attendu par les utilisateurs concernés).                                                                                                                                                                                      | **Reco** — change un comportement, hors périmètre « sans effet visuel » |
| 6.6 | `* { @apply border-border outline-ring/50 }` est le reset shadcn standard. Le sélecteur universel a un coût, mais c'est la convention du kit et l'enlever changerait les bordures par défaut.                                                                                                                                                                                                                                                                     | RAS                                                                     |

## 7. Tokens

| #   | Constat                                                                                                                                                                                                                                                                                                                                                                                               | Décision                                                                                  |
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| 7.1 | Tokens `@theme` **sans aucun consommateur** dans `src` : `--color-surface-bright`, `--color-machined-silver`, `--color-industrial-grid`, `--color-tertiary-container`, `--color-on-tertiary-container`. Tailwind v4 les élague : aucun n'apparaît dans le CSS produit. Les supprimer n'allégerait donc rien, et `machined-silver` / `industrial-grid` sont nommés dans `docs/brand.md` (§ Metallics). | **Reco** — à trancher avec `docs/brand.md`, pas à supprimer mécaniquement                 |
| 7.2 | `--color-surface` (#f9f9f7) et `--color-on-surface` (#1a1c1b) répètent mot pour mot `--background` et `--foreground`. Deux vocabulaires (Material 3 et shadcn) pour la même couleur : `bg-surface` et `bg-background` sont interchangeables sans que rien ne le dise.                                                                                                                                 | **Reco** — unifier vers un seul vocabulaire est un chantier de refactor, pas un nettoyage |
| 7.3 | `--radius: 0px` met bien à zéro les sept rayons dérivés (`--radius-sm` → `--radius-4xl`). Les `rounded-*` du kit shadcn sont donc inertes, conformément au « strictly Sharp (0px) » de `docs/brand.md`. Les `rounded-full` restants sont listés dans `docs/brand.md`.                                                                                                                                 | RAS                                                                                       |
| 7.4 | `--color-slate-850` (`hsl(222 47% 16%)`, un bleu ardoise) est vivant : `PageLayout.tsx` en fait le fond des pages `error` et `not-found`. Il n'appartient à aucune famille de `docs/brand.md` — les pages d'erreur sont donc bleu nuit là où tout le reste du site est charbon `#1a1c1b`.                                                                                                             | **Reco** — écart de charte à trancher côté design                                         |
| 7.5 | `@utility container` était soupçonné inutile : il est consommé par `PageLayout.tsx` (`className="container relative …"`), donc par `error.tsx` et `NotFoundPage.tsx`.                                                                                                                                                                                                                                 | RAS                                                                                       |
| 7.6 | `--container-lead`, `--spacing-hero`, `--spacing-monogram-lg`, `--blur-header-compact`, `--shadow-header` : tous consommés une fois chacun (`MaterialsSection`, `HeroHome`, `LogoMonogram`, `HeaderShell`). Tokens à consommateur unique, mais documentés dans `docs/brand.md`.                                                                                                                       | RAS                                                                                       |

## 8. Hors `globals.css` mais découvert en route

| #   | Constat                                                                                                                                                                                                                                                                                                                                                                                                      | Décision                                                                 |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| 8.1 | Tailwind v4 détecte ses sources tout seul, et ratisse `docs/`. L'utility `hover:border-outline-strong` est présente dans le CSS livré alors qu'**aucun** composant ne l'utilise : elle vient de la prose de `docs/audits/home-best-practices-2026-08/04-value-prop-materials.md`. Toute classe citée dans une doc devient du CSS livré. Un `@source` explicite (ou `@source not "docs"`) réglerait le point. | **Reco** — modifie le CSS produit, donc hors du périmètre « sans effet » |

---

## Ce que le commit contient

1. `.no-scrollbar` en double supprimé de `@layer components` (2.2).
2. `::selection` : `#e9c176` → `var(--color-gold-bright)` (4.1), `#261900` commenté (4.2).
3. `@utility container` / `@utility shell` déplacés au-dessus de `@layer components`, pour que `@theme` et `@theme inline` soient contigus (1.2).
4. `font-sans` retiré de `<body>` : une seule source, la règle `html` de `@layer base` (6.2).
5. Commentaires sur les trois `@import`, pour qu'on ne re-soupçonne pas `shadcn/tailwind.css` d'être mort (2.1).

Preuve d'absence d'effet visuel : captures pleine page de `/en`, `/fr`,
`/en/contact-us`, `/en/blog` et `/en/gallery` en 390 et 1440, md5 identiques
avant/après. `bun run build`, `bun run lint`, `bunx tsc --noEmit` et `knip` verts.
