Aller au contenu Aller au pied de page

00 Référence vivante

Notre repère commun.
Tout ce qui compte y est gravé.

Le fond de cette page est celui du site — même canvas, même repli CSS, même grain, même barre et même pied de page. Il tourne ici sur aurora, la nuit en réserve, que son contraste réserve aux fonds sans texte courant. La section champ animé la fait passer dans les autres mondes.

0 · Lire ce document

Trois règles, et tout le reste en découle.

  1. 1. Le sombre est le défaut.

    Blanc sur nuit bleue, partout. Une section n'écrit rien pour être dans le ton — elle hérite. Elle pose un data-theme uniquement pour en sortir, et l'attribut peint alors le fond et le texte tout seul. La PAGE, elle, peut traverser jusqu'à cinq mondes du haut au bas — voir la traversée.

  2. 2. Une seule classe de mise en page.

    .layout-wrapper tient le cadre de la page ET la grille : une colonne sur téléphone, douze de md à lg, vingt-quatre à partir de xl. La marge est une COLONNE, la première et la dernière : le contenu vit entre les deux, et un enfant qui ne déclare rien les occupe toutes. La gouttière est nulle, donc l'air entre deux blocs est une colonne sautée — voir la gouttière.

  3. 3. Les jetons, pas les variables.

    Une couleur ou une taille absente des jetons échoue au build. En revanche, les espacements, les rayons et les durées n'ont PAS de jeton : Tailwind les fournit déjà — p-8, rounded-2xl, duration-150. Rien de tout cela ne s'écrit en var() dans le HTML.

Où vivent les choses

css/theme.css
Les jetons Tailwind : couleurs, familles, échelle de texte, ombres, courbe.
css/themes.css
Les douze mondes, les dégradés, le fondu de l'encre. Le défaut est sombre.
js/page-theme.js
La traversée : les huit variables interpolées entre les mondes de la page.
js/gradient-field.js
Le fond animé : les mêmes mondes, en WebGL, et les effets du curseur.
css/base.css
Réinitialisation, anneau de focus, mouvement réduit.
css/grid.css
.layout-wrapper et l'inspecteur de grille.
css/utilities.css
Les utilitaires maison : section-y, measure, eyebrow, grain, les dégradés.
css/prose.css
Le HTML généré depuis le Markdown — .prose-rich.
css/forms.css
Les champs — .field, et les encarts d'erreur.
views/components/ui/
Les cinq composants, appelables depuis Blade comme depuis Antlers.

La marque LN

Noir ou blanc, jamais autre chose. Ni bleu marine, ni teinte, jamais sur une pastille de couleur.

Linectis tone="ivory"
Linectis tone="ink"

Le logotype

La marque et le mot, approche 0.16em. Le mot est en Nimbus 700.

Linectis Linectis
<x-ui.wordmark />
<x-ui.wordmark tone="ink" size="sm" :name="false" />

2 · Type

Familles

Nimbus Sans étendu pour les titres et tout ce qui est cliquable, Söhne pour le texte courant.

font-display · NimbusSansDOT

Aa Gg 01 — Titre de démonstration

300 · 400 · 700

font-body · Söhne

Aa Gg 01 — Paragraphe de démonstration

400 · 600 · 700

font-mono

Aa Gg 01 — SFMono, Menlo, Monaco

Échelle display

Les héros. Poids 300, approche −0.02em. La plus grande est pour un titre qui n'a rien au-dessus de lui, la feuille de contact, qui n'a pas de titre de page pour la présenter.

Texte display

text-display-lg · clamp(40px, 5.95vw + 14.3px, 100px)

Texte display

text-display · clamp(44px, 4vw + 26px, 88px)

Texte display

text-display-sm · clamp(28px, 1.6vw + 20px, 44px)

Échelle de page

Tout titre est en poids 400. Une seule ligne de clamp dit le minimum, la pente et le maximum.

H1 — Titre de niveau 1

text-h1 · clamp(36px, 3vw + 18px, 72px)

H2 — Titre de niveau 2

text-h2 · clamp(24px, 1.2vw + 16px, 40px)

H3 — Titre de niveau 3

text-h3 · clamp(20px, 0.4vw + 17px, 24px)

Corps de texte

Söhne, interligne 1.65, mesure de 56 caractères avec l'utilitaire .measure.

Ceci est un paragraphe d'introduction de démonstration. Il sert à mettre en avant le message principal de la section.

text-lead · 18px

Ceci est un paragraphe de texte courant. Il sert à illustrer la taille, l'interligne et la lisibilité par défaut du contenu.

text-body · 16px

Ceci est un exemple de petit texte utilisé pour les légendes ou les mentions discrètes.

text-md · 14px

Libellés

Le surtitre en capitales, et la taille partagée par tout ce qui est cliquable.

Libellé générique

.eyebrow · 0.75rem · approche 0.18em · poids 400

Un utilitaire, pas un composant : il se pose sur un <h2> ou un <dt> qui existe déjà.

Libellé d'action

text-md · 14px · Nimbus 700

Une seule taille pour le bouton et le lien fléché : posés côte à côte, ils doivent se lire au même niveau.

<h2 class="eyebrow">Libellé générique</h2>

3 · Couleur

Encres

Le noir radical, puis la descente vers les bleus de nuit.

  • black

    #000000

  • night

    #080817

  • ink

    #101828

  • ink-soft

    #171720

Bleus de marque

Le bleu électrique est réservé à l'unique action de conversion. Les autres portent les liens.

  • blue

    #5243ff

  • blue-deep

    #0047b9

  • periwinkle

    #9bb6ed

  • mist

    #d0d5dd

Braises

Le thème chaud — le seul ajout à la palette d'origine, qui était noire, blanche et bleue.

  • ember

    #0b0603

  • rust

    #7c2d12

  • flare

    #f97316

  • amber

    #fbbf24

Neutres

Gris froids. Papier, filets, textes discrets.

  • white

    #ffffff

  • paper

    #f3f3f3

  • slate

    #344054

  • slate-mid

    #525d6e

  • slate-soft

    #667085

Statuts

Livrés, mais rares. Un site d'agence n'a presque jamais à dire « erreur ».

  • success-light

    #ecfdf3

  • success

    #039855

  • success-dark

    #027a48

  • danger

    #b42318

Les mondes et leurs variables

Chaque monde redéfinit les mêmes onze variables. Un composant posé dedans n'a rien à savoir. Montré ici sur les trois mondes de contrôle — un sombre froid, un sombre chaud, le clair.

data-theme="night"

Aa

Nuit — Le défaut du site — blanc sur nuit bleue

bg-surface
text-fg
text-muted
text-link
border-line
border-line-soft
border-control

data-theme="ember"

Aa

Braises — Le sombre chaud — les braises

bg-surface
text-fg
text-muted
text-link
border-line
border-line-soft
border-control

data-theme="paper"

Aa

Papier — Le clair — pour une section de lecture

bg-surface
text-fg
text-muted
text-link
border-line
border-line-soft
border-control
Rien à écrire : la section hérite du thème sombre par défaut.
<section class="section-y">

Pour en sortir, un attribut suffit — il peint le fond et le texte.
<section data-theme="paper" class="section-y">

Filets et contours

Trois traits, trois métiers. Un filet sépare, un séparateur interne se devine, un contour de commande désigne une cible cliquable.

Nuit — Le défaut du site — blanc sur nuit bleue

border-line

le trait qu'on voit

border-line-soft

entre deux lignes d'une liste

border-control

le bord d'une commande

Braises — Le sombre chaud — les braises

border-line

le trait qu'on voit

border-line-soft

entre deux lignes d'une liste

border-control

le bord d'une commande

Papier — Le clair — pour une section de lecture

border-line

le trait qu'on voit

border-line-soft

entre deux lignes d'une liste

border-control

le bord d'une commande

Le filet du monde clair valait #e4e7ec, soit 1,12:1 contre sa propre surface, là où les mondes sombres tenaient 2,39:1 — sept fois plus faible. Il est désormais de l'ENCRE À OPACITÉ, comme les mondes sombres sont du BLANC à opacité : le filet d'un monde se fait avec la couleur de son texte, jamais avec une teinte fixe qui ne sait pas où elle est posée. Les trois mondes mesurent maintenant 2,36 à 2,39:1.

⚠️ Ne jamais écrire border-line/40. Le jeton porte déjà son opacité ; la redoubler au point d'appel donnait 1,29:1 sur du sombre — par chance — et 1,04:1 sur du clair, c'est-à-dire rien. C'est ce que border-line-soft remplace.

border-control vaut le filet dans les mondes sombres et le pervenche #9bb6ed sur le papier, où le survol le fait basculer sur l'encre des titres. Mesuré, ce pervenche vaut 1,84:1 sur #f3f3f3, sous les 3:1 que les WCAG 1.4.11 demandent au contour d'une commande : c'est un choix assumé, l'anneau de focus et le libellé portant, eux, le contraste. #6382c4 donnerait 3,43:1.

La traversée de la page

Une page ne vit pas forcément dans un seul monde : elle en traverse jusqu'à cinq, du haut au bas, et le fond animé fait le même chemin.

Un thème posé sur une section est une BASCULE — la section en sort, le reste du document n'en sait rien. La traversée, elle, est CONTINUE : les huit variables sont interpolées entre les arrêts au fil de la lecture et écrites sur <html>, d'où tout le site en hérite. Le fond WebGL lit la même liste au même rythme.

Le défaut, partout
Nuit au début, braises à la fin — y compris sur la page 404, qui n'a pas de front matter à consulter. Une page n'écrit page_themes que pour en sortir.
Les surfaces coulent, l'encre bascule
Les surfaces suivent le scroll image par image. Les couleurs d'encre, non : elles basculent d'un coup, à l'endroit exact où le blanc et l'encre ont le même contraste sur le fond, et c'est le CSS qui les fond en 320 ms. Les interpoler au scroll ferait passer le texte par la couleur de son propre fond — 1,00:1 mesuré — et un visiteur pourrait s'y arrêter.
Une section garde le dernier mot
data-theme sur une section reprend la main sur son sous-arbre, traversée ou pas. C'est ce qui permet à la section claire des solutions de vivre au milieu d'une page de nuit.
# Le front matter de l'entrée. Un à cinq mondes, dans l'ordre.
page_themes:
  - night
  - ember

# Rien du tout = le défaut du site, qui dit exactement la même chose.
# Une page claire de bout en bout :
page_themes:
  - paper

# Trois arrêts : la page passe par la nuit à mi-hauteur.
page_themes:
  - ember
  - night
  - ember

# Cinq, le maximum : les arrêts se répartissent sur la hauteur du document.
page_themes:
  - night
  - abyss
  - verdigris
  - plum
  - ember

Les dégradés

Un par monde, même nom. Ce sont les replis CSS du champ animé : impression, e-mail, absence de WebGL.

gradient-dusk

--gradient-dusk

5,29:1

gradient-midnight

--gradient-midnight

7,39:1

gradient-abyss

--gradient-abyss

5,29:1

gradient-glacier

--gradient-glacier

5,31:1

gradient-orchid

--gradient-orchid

5,29:1

gradient-aurora

--gradient-aurora

2,58:1

⚠ Lueur — arête claire, sous AA

gradient-tide

--gradient-tide

5,27:1

gradient-verdigris

--gradient-verdigris

5,27:1

gradient-jade

--gradient-jade

5,27:1

gradient-ember

--gradient-ember

5,32:1

gradient-gold

--gradient-gold

2,58:1

⚠ Lueur — arête claire, sous AA

gradient-dawn

--gradient-dawn

5,14:1

Le chiffre est le contraste du pixel le plus clair que la composition peut produire — un quart de bande, trois quarts d'arête, plus le tramage — et non celui des couleurs prises séparément, qui passent toutes. C'est le seul qui dise si un monde peut porter du texte blanc. Les mondes AA tombent tous sur le même point, à quatre centièmes près : celui de la nuit. Minuit vise 7,4:1, soit AAA. Et les six de la famille à arête CLAIRE — Aurora et Or — tombent, eux, sur le point d'aurora : 2,58:1, sous AA. C'est ce qui fait leur beauté et ce qu'elle coûte.

⚠️ L'alpha de l'arête est calculé, pas choisi. Il pose le centre de la radiale à la luminance de ce pixel. À 0.55, la valeur d'avant, le repli rendait 9,6:1 là où le champ rend 5,3 — deux fois trop sombre, donc une autre image que celle qu'il remplace. La géométrie des quatre couches est la même partout, pour la même raison : le shader compose la même bande quel que soit le monde.

Le catalogue des mondes

Douze, groupés par COULEUR en quatre familles. Dix tiennent AA sous du texte blanc et se calibrent sur le pixel de la nuit ; Aurora et Or portent une arête CLAIRE en bas à droite et se calibrent sur celui d'aurora, à 2,58:1.

Bleu

Nuit

data-theme="0"

Le défaut du site — blanc sur nuit bleue

fond · brume · bande · arête
#080817 · #0047b9 · #2c34ad · #6066ea
pixel le plus clair
5,29:1

Minuit (AAA)

data-theme="1"

La nuit en plus profond — AAA, pour un texte dense

fond · brume · bande · arête
#010514 · #03112f · #2512b0 · #513aef
pixel le plus clair
7,39:1

Abysse

data-theme="2"

L'océan — blanc sur bleu de grand fond

fond · brume · bande · arête
#051220 · #0b2943 · #105e7c · #217a86
pixel le plus clair
5,29:1

Glacier

data-theme="3"

La glace — un bleu qui tire vers le cyan

fond · brume · bande · arête
#030e15 · #0c2a3c · #10606e · #217b78
pixel le plus clair
5,31:1

Orchidée

data-theme="4"

La fleur — du magenta au bleu, dans ce sens

fond · brume · bande · arête
#170416 · #4e134b · #7e19d0 · #5165f1
pixel le plus clair
5,29:1

Aurora (2,6:1)

data-theme="5"

La référence — bleu de marque, arête pervenche

fond · brume · bande · arête
#080817 · #0047b9 · #5243ff · #9bb6ed
pixel le plus clair
2,58:1

Vert

Marée

data-theme="0"

Le large — du bleu profond au vert d’eau

fond · brume · bande · arête
#030c1d · #0a254d · #106166 · #217e5f
pixel le plus clair
5,27:1

Vert-de-gris

data-theme="1"

Le cuivre oxydé — un bleu qui a viré au jade

fond · brume · bande · arête
#04130f · #0c2a36 · #10625f · #217e5c
pixel le plus clair
5,27:1

Jade

data-theme="2"

La pierre — un vert franc, le plus vif des verts

fond · brume · bande · arête
#030e10 · #0c2b2f · #10634d · #20803a
pixel le plus clair
5,27:1

Chaud

Braises

data-theme="0"

Le sombre chaud — les braises

fond · brume · bande · arête
#0b0603 · #54120e · #963e0e · #99631e
pixel le plus clair
5,32:1

Or (2,6:1)

data-theme="1"

L'or franc — brume de bronze, lueur de crème

fond · brume · bande · arête
#0e0b04 · #4f401a · #7c6712 · #bbb287
pixel le plus clair
2,58:1

Clair

Papier

data-theme="0"

Le clair — pour une section de lecture

fond · brume · bande · arête
#f3f3f3 · #ccd3df · #9bb6ed · #ffffff
pixel le plus clair
5,14:1
# Poser un monde sur une page — dans son front matter, comme les autres.
page_themes:
  - abyss

# Ou en traversée, exactement comme le site fait nuit → braises.
page_themes:
  - abyss
  - plum

Le champ animé

Le vrai fond de la marque : un canvas WebGL dont la bande ondule. Ce n'est pas une vignette — c'est le fond de CETTE page, et les boutons le font changer de monde.

Le repli CSS du monde courant, grain compris. Le canvas, lui, est derrière toute la page — regardez au-dessus du titre.

Un seul monde

Une traversée — puis défilez

Les boutons réécrivent data-page-themes sur l'hôte du champ, puis émettent un événement que site.js écoute. Le fond ET le texte sont relus depuis le même attribut, exactement comme au chargement d'une page : c'est le chemin de code réel qui est exercé ici, pas un second écrit pour la démonstration.

Le CURSEUR répond tout de suite — le halo s'étire dans le sens du geste et la matière du fond se réfracte autour de lui. Trois effets, dosés pour se voir du coin de l'œil. Aucun au clic : un fond qui ondule pendant qu'on remplit un formulaire distrait au lieu de séduire.

Sans WebGL, le canvas ne se monte pas et le dégradé CSS reste seul — la page ne casse pas, elle perd son ondulation. Sous prefers-reduced-motion, une seule image est rendue et le curseur n'est plus suivi.

Grain

La signature tactile. Texture argentique en fondu soft-light, qui saute huit fois par seconde comme une émulsion. Sur les dégradés comme sur les fonds pleins.

sans grain
.grain · soft-light, 8 sauts/1,2 s

4 · Composants

Fil d'Ariane

Deux niveaux pour une page d'information, trois pour une page de service. L'accueil est toujours le premier pas et n'est pas paramétrable.

night

ember

paper

Il vivait en dur dans l'ouverture des pages d'information, et n'existait donc que là. Les pages de service sont pourtant plus profondes qu'elles.

⚠️ Ses paramètres sont préfixés, contrairement aux cinq composants d'UI : il est appelé depuis des gabarits d'ENTRÉE, où title, url et parent sont des champs de Statamic qui écraseraient un paramètre de même nom, en silence.

Sa couleur est posée sur ses ITEMS et non sur sa liste : .eyebrow porte la typographie et --muted, or ce fil vit souvent sur le champ animé. Deux éléments différents, aucun duel d'utilitaires.

<x-ui.breadcrumb crumb_current="Page courante" />
<x-ui.breadcrumb crumb_section="Section parente" crumb_section_url="/section" crumb_current="Sous-page courante" />

{{ partial:components/ui/breadcrumb crumb_section="Section parente" crumb_section_url="/section" :crumb_current="title" }}

Boutons

La pilule pleine est l'unique action de conversion de la page. S'il y en a deux, il n'y en a plus aucune.

night

ember

paper

<x-ui.button href="/contact">Bouton principal</x-ui.button>
<x-ui.button variant="outline" size="sm" href="/projets">Bouton secondaire</x-ui.button>

{{ partial:components/ui/button variant="outline" label="Bouton secondaire" }}

Badges

Bascule de langue, compteurs, étiquettes. La puce pleine s'inverse par relative à son thème.

night

Tag Badge 1 / 6 — Catégorie

ember

Tag Badge 1 / 6 — Catégorie

paper

Tag Badge 1 / 6 — Catégorie

Champs de formulaire

Un seul style pour input, select et textarea. Le libellé est en Nimbus gras 14px — c'est une étiquette d'interface, au même titre qu'un bouton ; la saisie reste en Söhne, parce qu'un message de cinq lignes est du texte courant.

night

Texte d'aide sous le champ.
Message d'erreur contextuel à afficher.

2 champs à revoir

  • Le message d'erreur du premier champ.
  • Celui du second.

ember

Texte d'aide sous le champ.
Message d'erreur contextuel à afficher.

2 champs à revoir

  • Le message d'erreur du premier champ.
  • Celui du second.

paper

Texte d'aide sous le champ.
Message d'erreur contextuel à afficher.

2 champs à revoir

  • Le message d'erreur du premier champ.
  • Celui du second.

Rayons

Ceux de Tailwind, sans jeton maison. Rien n'a d'angle vif : le plus petit rayon employé est rounded-md, 6px.

  • rounded-full

  • rounded-3xl

  • rounded-2xl

  • rounded-xl

  • rounded-md

Ombres

Trois, et pas une de plus. Toute autre ombre est une erreur.

  • shadow-media

    Sous une image en cadre

  • shadow-card

    Sous une carte

  • shadow-pill

    Le halo du bouton de conversion

5 · Mouvement

Courbes et durées

Une seule courbe pour tout le site. Les durées sont celles de Tailwind, écrites en clair.

ease-out
cubic-bezier(0.22, 1, 0.36, 1) — la courbe de la marque, réécrite dans theme.css
duration-150
couleur, opacité, bordure
duration-300
déplacements — la flèche qui glisse
duration-500
zoom d'image, et l'anneau qui se dessine autour du bouton secondaire

Il n'y a pas de jeton de durée, et il n'y en aura pas : une classe duration-150 se lit et se change sur place, là où duration-[var(--dur-fast)] obligeait à ouvrir un autre fichier pour savoir de combien de millisecondes on parlait. Sous prefers-reduced-motion, toutes les transitions tombent à 0.01ms — la règle vit dans base.css.

États

Survol, appui, focus. Passez la souris, puis refaites-le au clavier.

Lien fléché
survol
La flèche glisse de 8px vers la droite, le fond passe au ton de survol. Aucun mouvement sur l'axe vertical — c'est ce qui distingue une interface calme d'une interface qui sautille.
appui
active:scale-[0.985], sans changement de couleur.
focus
Anneau de 1.5px en bleu électrique, décollé de 2px. Déclaré une seule fois dans css/base.css — aucun composant ne le redéfinit, aucun ne le supprime.

Révélations au défilement

Un état caché posé par le CSS, levé par GSAP. Sans script, rien ne se cache.

data-reveal
Le bloc part à opacity: 0 et remonte de quelques pixels. À poser sur le bloc lui-même, jamais sur chacun de ses enfants — c'est le décalage de la chronologie qui fait le rythme, pas la multiplication des cibles.
data-reveal-line
La ligne monte derrière un masque. Le parent porte overflow-hidden, la ligne porte son rembourrage de jambages en em : les deux hauteurs restent égales, et la translation vide toujours le masque.

Les deux règles vivent dans base.css, sous html.js — la classe que le gabarit pose dans l'en-tête. Sans JavaScript elle n'existe pas, donc rien ne se cache et la page reste entière. Sous prefers-reduced-motion, l'état caché est annulé des DEUX côtés : le CSS relève l'opacité et gsap.matchMedia() n'ouvre aucune chronologie. Ce qui n'est pas animé ne doit jamais être caché.

6 · Grille

Une classe, trois paliers

Le même .layout-wrapper porte le cadre de la page et ses colonnes. Une seule variable dit combien : 1, puis 12, puis 24.

Redimensionnez la fenêtre : ces cellules sont dans un .layout-wrapper, elles suivent donc les pistes du site et non un dessin recopié. Les deux plus foncées sont les marges.

--layout-columns
Le nombre de colonnes, écrit une seule fois dans grid.css : 1 sur téléphone, 12 de md à lg, 24 à partir de xl. Les vingt-quatre n'ouvrent pas à lg : à 1024 une piste vaudrait 39px, trop fine pour qu'une portée veuille dire quelque chose.
la marge
C'est une colonne, la première et la dernière. Le contenu vit entre les deux : colonnes 2 à 11 sur douze, 2 à 23 sur vingt-quatre. Sur téléphone il n'y a qu'une piste, donc pas de colonne à sacrifier : là, et là seulement, c'est le rembourrage du conteneur qui fait la marge.
enfant sans portée
Tout le cadre (grid-column: 2 / -2). C'est le cas le plus fréquent, et il ne s'écrit pas.
col-span-full
Ce qui traverse : la barre de navigation et les bandes du pied de page vont de la colonne 1 à la 24. Leurs filets sont l'assise d'architecture du site.
⌘X · Ctrl+X
Affiche la grille par-dessus la page, marges teintées. Le calque porte la même classe et lit la même variable : il ne peut pas mentir. Le réglage survit au rechargement et aux navigations.
.section-y
Le rythme vertical : clamp(80px, 11vw, 180px).
<section class="section-y">
    <div class="layout-wrapper">
        <h2 class="xl:col-start-2 xl:col-span-13">…</h2>
        <p  class="xl:col-start-17 xl:col-span-7">…</p>
    </div>
</section>

Raccourci ⌘X / Ctrl+X : la grille s'affiche par-dessus la page.

La gouttière est nulle, l'air est une colonne

Les colonnes se touchent : un visuel qui couvre six colonnes les couvre bord à bord. Ce qui sépare deux blocs, c'est une colonne laissée vide.

xl:col-start-2 xl:col-span-13 puis xl:col-start-17 xl:col-span-7 : les colonnes 15 et 16 restent à l'air, et cet air est fluide. ⚠️ Chaque bloc porte son col-start : une portée seule est placée automatiquement, donc à partir de la ligne 1, c'est-à-dire dans la marge.

titre + lien
2 span 13 · 17 span 7
média + texte
2 span 12 · 16 span 8
moitiés
2 span 10 · 14 span 10
étiquette + valeur
2 span 8 · 12 span 12
rangée répétée
Une galerie deux par deux, trois cartes, une liste de questions : grid grid-cols-N avec son propre écart, dans le cadre. Ses bords tombent sur les colonnes 2 et 23, sa découpe intérieure est la sienne.
flex · block
⚠️ Posés sur un .layout-wrapper, ils ANNULENT sa grille en silence : un utilitaire bat toujours la couche components. Seule la barre de navigation en use, exprès.
Une rangée répétée n'est pas une composition de page :
sa grille est la sienne, dans le cadre du contenu.

<ol class="grid gap-y-12 lg:grid-cols-3 lg:gap-x-8">
    <li>…</li>
    <li>…</li>
    <li>…</li>
</ol>

L'ouverture d'une page

Sept pages, un seul rythme. Ce qui change d'une ouverture à l'autre, c'est ce qu'on y met, jamais l'espacement.

Barre · 80px, en fixed
Fil d'Ariane ou surtitre Le titre Le chapô, quand la page en a un.
La ligne de pied, sur son filet.
.hero-y
Le rythme. Le haut vaut la hauteur de la barre PLUS l'air, parce qu'elle est en fixed et ne réserve rien. C'est la seule raison pour laquelle il ne s'écrit pas en une classe Tailwind.
Zone A
Les mots, en colonne gap-8 : fil d'Ariane ou surtitre, titre, chapô. Colonnes 2 à 14 quand il y a une zone B, le cadre entier sinon — les mots ne s'arrêtent pas à la colonne 14 pour rien.
Zone B
Le complément, en pile gap-8 : actions, fiche technique, compteur. Colonnes 17 à 23, calée sur la ligne de base par xl:items-end. ⚠️ Elle ne prend sa colonne qu'à xl : en dessous elle s'empile, et il n'y a rien à défaire.
Zone C
La ligne de pied, à mt-16 lg:mt-20 sur son filet. Un .layout-wrapper imbriqué : le trait traverse le cadre, le texte retrouve la colonne 2.
Zone D
Le média, au même écart. L'accueil et les pages de projet sont les seules à en avoir un.
h1
text-display et text-balance, sur les sept pages. À cette taille, une phrase de cinquante signes tient sur trois lignes et la dernière porterait un mot seul.

⚠️ Les écarts internes d'une ouverture sont portés par le gap-8 de sa colonne, jamais par un mt-* posé sur une ligne. Une page de service en empilait quatre par-dessus le rembourrage de sa section, et son titre tombait à 370px du haut de la fenêtre contre 194 sur les services.

7 · Interop

Blade et Antlers, un seul fichier

Les deux pilules ci-dessous sortent du même composant, appelé de deux façons différentes.

Appel Blade

Rendu comme vue simple

L'appel Antlers réel tourne en production dans plan-du-site.antlers.html, qui rend components/ui/link-row dans sa boucle ; la page 404 rend le même fichier en Blade.

Depuis Blade ou Livewire
<x-ui.button variant="outline" class="w-full">Bouton Blade</x-ui.button>

Depuis un gabarit Antlers — Statamic rend n'importe quelle vue en
partial, .blade.php compris
{{ partial:components/ui/button variant="outline" label="Bouton Antlers" }}

Tout ce qui n'est pas un paramètre déclaré passe par le sac d'attributs : class, wire:click, x-on:click, target, aria-*. C'est ce qui rend un composant adaptable sans y ajouter un paramètre à chaque cas particulier.