card-14-numbered-steps
layout-16-header-tripartite
Header tri-parti à filet, centre sur l'axe
Poser le bandeau d'appareil qui court en tête de chaque page d'un document — émetteur à gauche, mention centrale, pile et jeton de bord à droite, un filet sous les trois — de façon que la zone centrale soit sur l'AXE du conteneur et n'en bouge plus, quels que soient les libellés de ses voisines. Deux rails `minmax(0, 1fr)` qui se font face l'y tiennent par construction. Et parce qu'un appareil n'est pas un titre, il n'a qu'UN corps et DEUX niveaux d'encre : la hiérarchie s'y lit à l'encre et à la position, jamais à la taille.
layoutslidewebprint1456×55px20 benchmarks
Le fragment RÉEL, dans son propre document, sur le sol de sa charte — pas une image. Ce que vous voyez est exactement ce que vous collerez.
Employer quand
Le bandeau répété d'un deck, d'un rapport, d'une proposition ou d'une page de documentation : trois informations de nature différente (qui parle, à qui / quand, de quoi) qui doivent se lire comme une réglure et non comme du texte, et rester STRICTEMENT identiques d'une page à l'autre. C'est aussi la bonne construction dès qu'un centre doit tenir son axe entre deux voisins de longueurs libres.
Éviter quand
Une des trois zones porte un objet et non du texte (logotype large, bouton, avatar) : au-delà d'un tiers de la bande, la zone latérale mord sur le centre et le pattern n'a plus de centre à défendre — `layout-12-nav-three-zone` traite ce cas. Écarter aussi quand la bande doit hiérarchiser par la TAILLE (un titre de page dans le header) : cela dément la loi du corps unique, et il faut alors un titre sous le bandeau, pas dedans.
Notes
Le pattern tient dans une construction qu'on ne voit pas et dans une loi qu'on voit sans la nommer. LA CONSTRUCTION : `minmax(0, 1fr) auto minmax(0, 1fr)`. Les deux rails latéraux ont la même largeur par définition de `1fr`, donc la colonne `auto` du milieu est exactement sur l'axe du conteneur — et elle y reste quels que soient les libellés. Un `space-between` à trois enfants produit un balisage d'apparence identique qui dérive : ici l'encre de gauche fait 0,4 fois celle de droite, et le milieu de leurs deux bords tombe à 5,5 % de la largeur à gauche de l'axe. Deux benchmarks tiennent la règle des deux côtés — le centre est sur l'axe, ET le milieu de ses voisins ne l'est pas. Le second est le seul qui empêche quelqu'un de « simplifier » le pattern : sans lui, une réécriture fautive resterait verte tant que les libellés de démonstration sont équilibrés. Le `minmax(0, …)` fait le reste du travail, et c'est la part la moins évidente — au point que la contre-épreuve a d'abord été écrite trop faible. Avec un `1fr` nu, la piste a une taille MINIMALE automatique, qui est le min-content de ce qu'elle porte : un rail dont le contenu ne peut pas se replier sous la largeur de piste S'ÉLARGIT, et pousse le centre. Or un libellé long mais SÉCABLE (« ©northbeam consulting partners international ») se replie tout seul : son min-content est celui de son mot le plus long, et l'axe ne bouge pas — mesuré, `1fr` et `minmax(0, 1fr)` sont alors indiscernables, et la contre-épreuve sortait verte sur la construction fautive. Ce qui les sépare est un libellé INSÉCABLE plus large que son rail : une adresse électronique, un nom d'enseigne d'un seul tenant. La contre-épreuve en pose un et remesure l'axe — `minmax(0, 1fr)` le tient à 0, `1fr` le déplace de 3,7 % de la largeur, soit 54 px. C'est la forme générale du piège : une contre-épreuve doit reparamétrer là où les deux constructions DIVERGENT, pas là où l'une des deux est simplement plus confortable. LA LOI : un appareil n'est pas un titre. UN SEUL corps dans tout le bandeau, DEUX niveaux d'encre, et rien d'autre — le jeton de droite, qui est ce qu'on retient de la page, a exactement le corps et la graisse de la mention atténuée qui le précède. Il n'en diffère que par l'encre et par sa position, poussé au bord extrême. C'est la construction inverse d'un titre, et c'est ce qui fait qu'un bandeau répété huit fois se lit comme une réglure au lieu de redemander l'attention à chaque page. Les deux `Set` qui comptent les corps et les encres sont la façon la plus courte de l'asserter : dès qu'un troisième niveau d'encre apparaît — l'envie de « souligner » une zone —, le bandeau redevient du texte. Les capitales sont pistées à 0,12 em. Ce n'est pas un effet : à 13 px, des capitales sans interlettrage se referment et la bande devient un bloc gris. C'est la seule casse capitale de la charte d'origine, et elle est réservée à l'appareil. Ce pattern n'a AUCUN accent, volontairement. La charte dont il vient tient sur « neutre + un accent fluo » : un bandeau qui court sur les huit pages et qui poserait l'accent le dépenserait huit fois, c'est-à-dire jamais. Le budget de couleur reste entier pour le contenu. Différence avec `layout-12-nav-three-zone`, qui résout le même problème d'axe pour un autre objet : là-bas la zone centrale sort du flux, parce qu'elle porte un logotype qui a le droit de passer sous ses voisins et parce que la rangée porte un bouton d'action — un objet, pas du texte. Ici les trois zones sont du texte de même corps et le centre reste DANS le flux : la grille suffit, et elle a l'avantage de garantir en plus que rien ne se chevauche jamais.
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs layout-16-header-tripartite : le pattern se prouve, il ne se croit pas.
| assertion | mesure | attendu |
|---|---|---|
| LA ZONE CENTRALE EST SUR L'AXE DU CONTENEUR | Math.abs((box('.vl-htri__zone--c').left + box('.vl-htri__zone--c').right) / 2 - W / 2) / W | ≤ 0.003 |
| …et c'est un MÉCANISME : les deux rails latéraux ont exactement la même largeur | box('.vl-htri__zone--l').w / box('.vl-htri__zone--r').w | 1 ± 0.005 |
| …alors que ce qu'ils PORTENT n'a pas du tout la même largeur d'encre | (() => { const ink = (s) => { const r = document.createRange(); r.selectNodeContents(document.querySelector(s)); return r.getBoundingClientRect().width; }; return ink('.vl-htri__zone--l') / ink('.vl-htri__zone--r'); })() | ≤ 0.75 |
| CONTRE-ÉPREUVE DU PIÈGE — un centrage entre les deux VOISINS, lui, dériverait de plus de 2 % de la largeur | (() => { const O = document.querySelector('.vl-htri').getBoundingClientRect(); const ink = (s) => { const r = document.createRange(); r.selectNodeContents(document.querySelector(s)); const b = r.getBoundingClientRect(); return { left: b.left - O.left, right: b.right - O.left }; }; return Math.abs((ink('.vl-htri__zone--l').right + ink('.vl-htri__zone--r').left) / 2 - W / 2) / W; })() | ≥ 0.02 |
| CONTRE-ÉPREUVE DE LA CONSTRUCTION — un libellé de gauche PLUS LARGE QUE SON RAIL, et insécable, ne déplace toujours pas l'axe du centre | (() => { const el = document.querySelector('.vl-htri__mark'); const old = el.textContent; el.textContent = '©northbeamconsultingpartnersinternationalgroupholdingsworldwide'; const O = document.querySelector('.vl-htri').getBoundingClientRect(); const c = document.querySelector('.vl-htri__zone--c').getBoundingClientRect(); const out = Math.abs((c.left + c.right) / 2 - O.left - W / 2) / W; el.textContent = old; return out; })() | ≤ 0.004 |
| le jeton de droite est poussé au bord EXTRÊME de la bande | (() => { const O = document.querySelector('.vl-htri').getBoundingClientRect(); const r = document.createRange(); r.selectNodeContents(document.querySelector('.vl-htri__edge')); return (O.right - r.getBoundingClientRect().right) / W; })() | ≤ 0.003 |
| …et la marque touche le bord gauche, symétriquement | (() => { const O = document.querySelector('.vl-htri').getBoundingClientRect(); const r = document.createRange(); r.selectNodeContents(document.querySelector('.vl-htri__mark')); return (r.getBoundingClientRect().left - O.left) / W; })() | ≤ 0.003 |
| UN SEUL CORPS dans tout le bandeau — c'est ce qui le fait lire comme une réglure | new Set([...document.querySelectorAll('.vl-htri, .vl-htri *')].map((e) => getComputedStyle(e).fontSize)).size | 1 ± 0 |
| DEUX niveaux d'encre, et pas trois | new Set([...document.querySelectorAll('.vl-htri, .vl-htri *')].map((e) => getComputedStyle(e).color)).size | 2 ± 0 |
| LA HIÉRARCHIE NE PASSE PAS PAR LA TAILLE : le jeton primaire a le corps ET la graisse de la mention atténuée qui le précède | num('.vl-htri__edge','fontSize') / num('.vl-htri__pair','fontSize') + (cs('.vl-htri__edge','fontWeight') === cs('.vl-htri__pair','fontWeight') ? 0 : 1) | 1 ± 0.01 |
| CAPITALES PISTÉES — la seule casse d'appareil de la charte : au moins 0,1 em d'interlettrage | cs('.vl-htri','textTransform') === 'uppercase' ? num('.vl-htri','letterSpacing') / num('.vl-htri','fontSize') : 0 | ≥ 0.1 |
| RAIL DU HAUT — les trois zones pendent du même plafond | (Math.max(...['--l','--c','--r'].map((z) => box('.vl-htri__zone' + z).top)) - Math.min(...['--l','--c','--r'].map((z) => box('.vl-htri__zone' + z).top))) / H | 0 ± 0.01 |
| le filet court sous les TROIS zones, d'un bord à l'autre | num('.vl-htri','borderBottomWidth') | ≥ 1 |
| …et il respire : le blanc entre la dernière ligne et le filet vaut 1,3 % de la largeur | (box('.vl-htri').bottom - num('.vl-htri','borderBottomWidth') - Math.max(...['--l','--c','--r'].map((z) => box('.vl-htri__zone' + z).bottom))) / W | 0.0124 ± 0.003 |
| les zones ne se chevauchent jamais : du blanc de part et d'autre du centre | (() => { const ink = (s) => { const r = document.createRange(); r.selectNodeContents(document.querySelector(s)); return r.getBoundingClientRect(); }; return Math.min(ink('.vl-htri__zone--c').left - ink('.vl-htri__zone--l').right, ink('.vl-htri__zone--r').left - ink('.vl-htri__zone--c').right) / W; })() | ≥ 0.03 |
| APPAREIL, PAS TITRE : la bande reste sous 4 % de sa largeur en hauteur | H / W | ≤ 0.04 |
| contraste du rang atténué — c'est lui qui porte le plus de texte, et c'est lui qui échoue d'habitude | contrast('.vl-htri__zone--c div') | ≥ 4.5 |
| contraste du rang primaire | contrast('.vl-htri__edge') | ≥ 7 |
| rien ne déborde de la bande | overflow('.vl-htri') | 0 ± 0 |
| AUTONOMIE TYPOGRAPHIQUE — un serif imposé à la page hôte ne déplace pas d'un pixel le bord d'encre du jeton : le fragment épingle sa famille | (() => { const ink = () => { const r = document.createRange(); r.selectNodeContents(document.querySelector('.vl-htri__edge')); return r.getBoundingClientRect().width; }; const a = ink(); const h = document.documentElement, old = h.style.fontFamily; h.style.fontFamily = 'Georgia, "Times New Roman", serif'; const b = ink(); h.style.fontFamily = old; return b / a; })() | 1 ± 0.01 |
Le code
patterns/layout-16-header-tripartite.html
Télécharger<!-- layout-16-header-tripartite — le bandeau d'appareil qui court en tête de chaque page d'un
document : trois zones, un filet qui les souligne toutes, et rien d'autre.
CE QUI EN FAIT UN PATTERN, ce n'est pas « trois zones » — c'est que la zone du milieu est
sur l'AXE DU CONTENEUR et n'en bouge plus, quoi qu'on écrive à ses côtés. Les deux rails
latéraux sont deux `minmax(0, 1fr)` qui se font face : ils ont la même largeur PAR
CONSTRUCTION, donc la colonne `auto` du centre est exactement au milieu. Écrit en
`space-between`, le même balisage paraît identique et dérive : ici l'encre de gauche fait
0,4 fois celle de droite, et le milieu de leurs deux bords tombe à 5,5 % de la largeur à
gauche de l'axe. Rien ne le signale, et sur une suite de pages le centre se met à flotter
d'une page à l'autre au gré des libellés.
LA SECONDE LOI, et c'est elle qui fait lire le bandeau comme une RÉGLURE et non comme du
texte : UN SEUL CORPS dans tout le header, et DEUX niveaux d'encre. La hiérarchie ne passe
jamais par la taille — le jeton de droite, qui est ce qu'on retient de la page, a
exactement le corps de la mention atténuée qui le précède. Il n'en diffère que par l'encre
et par sa position, poussé au bord extrême. C'est la construction inverse d'un titre.
Le `minmax(0, …)` n'est pas une précaution de style. Un `1fr` nu a une taille minimale
automatique — le min-content de ce qu'il porte : un rail qui ne peut pas se replier sous
sa largeur de piste S'ÉLARGIT et POUSSE le centre. Un libellé long mais SÉCABLE se replie
tout seul et ne prouve donc rien ; c'est un libellé INSÉCABLE plus large que son rail (une
adresse électronique, un nom d'un seul tenant) qui sépare les deux constructions —
mesuré, `1fr` déplace alors l'axe de 54 px, `minmax(0, 1fr)` de zéro. -->
<header class="vl-htri">
<div class="vl-htri__zone vl-htri__zone--l">
<span class="vl-htri__key vl-htri__mark">©northbeam</span>
</div>
<div class="vl-htri__zone vl-htri__zone--c">
<div>Our Company</div>
<div>March 28th, 2045</div>
</div>
<div class="vl-htri__zone vl-htri__zone--r">
<div class="vl-htri__pair">
<div>Business Proposal</div>
<div>Presentation</div>
</div>
<span class="vl-htri__key vl-htri__edge">2045</span>
</div>
</header>
<style>
.vl-htri {
--vl-htri-w: 1456px;
--vl-htri-size: 13px;
--vl-htri-lh: 1.4;
--vl-htri-track: 0.12em;
--vl-htri-gap: 56px; /* le blanc minimal de part et d'autre du centre */
--vl-htri-split: 64px; /* ce qui sépare la pile de droite de son jeton de bord */
--vl-htri-pad: 18px; /* du bas de la dernière ligne au filet */
--vl-htri-ink: var(--vl-black);
--vl-htri-muted: var(--vl-ink-muted);
--vl-htri-rule: var(--vl-hairline);
/* La police est ÉPINGLÉE : le bandeau se cale sur des bords d'encre (marque à gauche,
jeton à droite), donc ses cotes dépendent d'une chasse — un fragment dans ce cas ne
peut pas hériter la police de la page qui le colle. */
font-family: var(--vl-font-body, "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif);
width: var(--vl-htri-w);
box-sizing: border-box;
/* LE MÉCANISME : deux rails égaux qui se font face, le centre en `auto` entre eux.
`minmax(0, 1fr)` et non `1fr` — voir l'en-tête du fragment. */
display: grid;
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
align-items: start;
column-gap: var(--vl-htri-gap);
padding-bottom: var(--vl-htri-pad);
border-bottom: 1px solid var(--vl-htri-rule);
font-size: var(--vl-htri-size);
line-height: var(--vl-htri-lh);
font-weight: 700;
letter-spacing: var(--vl-htri-track);
text-transform: uppercase;
color: var(--vl-htri-muted);
}
/* Le rang PRIMAIRE ne change que l'encre. Ni corps, ni graisse, ni casse. */
.vl-htri__key { color: var(--vl-htri-ink); }
.vl-htri__zone--c { text-align: center; }
/* La zone de droite pousse son dernier enfant au bord extrême de la bande. */
.vl-htri__zone--r {
display: flex;
justify-content: flex-end;
gap: var(--vl-htri-split);
}
</style>
systems/ref-05-proposal-acid-yellow.json
Télécharger:root {
--vl-bg: #EFEFED;
--vl-board: #DCDCD8;
--vl-white: #FFFFFF;
--vl-black: #111111;
--vl-acid: #EAFF00;
--vl-ink-muted: #6C6C6C;
--vl-ink-muted-invert: #9A9A9A;
--vl-hairline: #11111118;
--vl-hairline-invert: #FFFFFF24;
--vl-blob-light: #E8E8E6;
--vl-blob-dark: #171717;
--vl-margin: 72px;
--vl-gap: 24px;
--vl-gap-block: 32px;
--vl-r-media: 19px;
--vl-r-list: 22px;
--vl-r-band: 26px;
--vl-r-step: 32px;
}
les métadonnées — patterns/layout-16-header-tripartite.json (télécharger)
{
"name": "Header tri-parti à filet, centre sur l'axe",
"family": "layout",
"ref": "ref-05-proposal-acid-yellow",
"intent": "Poser le bandeau d'appareil qui court en tête de chaque page d'un document — émetteur à gauche, mention centrale, pile et jeton de bord à droite, un filet sous les trois — de façon que la zone centrale soit sur l'AXE du conteneur et n'en bouge plus, quels que soient les libellés de ses voisines. Deux rails `minmax(0, 1fr)` qui se font face l'y tiennent par construction. Et parce qu'un appareil n'est pas un titre, il n'a qu'UN corps et DEUX niveaux d'encre : la hiérarchie s'y lit à l'encre et à la position, jamais à la taille.",
"when_to_use": "Le bandeau répété d'un deck, d'un rapport, d'une proposition ou d'une page de documentation : trois informations de nature différente (qui parle, à qui / quand, de quoi) qui doivent se lire comme une réglure et non comme du texte, et rester STRICTEMENT identiques d'une page à l'autre. C'est aussi la bonne construction dès qu'un centre doit tenir son axe entre deux voisins de longueurs libres.",
"avoid_when": "Une des trois zones porte un objet et non du texte (logotype large, bouton, avatar) : au-delà d'un tiers de la bande, la zone latérale mord sur le centre et le pattern n'a plus de centre à défendre — `layout-12-nav-three-zone` traite ce cas. Écarter aussi quand la bande doit hiérarchiser par la TAILLE (un titre de page dans le header) : cela dément la loi du corps unique, et il faut alors un titre sous le bandeau, pas dedans.",
"media": [
"slide",
"web",
"print"
],
"tags": [
"header",
"bandeau",
"en-tete",
"trois-zones",
"tripartite",
"filet",
"hairline",
"centrage",
"axe",
"micro-caps",
"capitales",
"appareil",
"pied-de-page",
"grid"
],
"vars": [
{
"name": "--vl-htri-w",
"role": "la largeur de la bande — celle de la zone de contenu de la page, marges comprises",
"default": "1456px"
},
{
"name": "--vl-htri-size",
"role": "le corps UNIQUE du bandeau : toutes les zones l'ont, primaires comprises",
"default": "13px"
},
{
"name": "--vl-htri-lh",
"role": "l'interligne des piles de deux lignes",
"default": "1.4"
},
{
"name": "--vl-htri-track",
"role": "l'interlettrage des capitales — sans lui, un micro-corps en capitales se referme",
"default": "0.12em"
},
{
"name": "--vl-htri-gap",
"role": "le blanc minimal de part et d'autre de la zone centrale",
"default": "56px"
},
{
"name": "--vl-htri-split",
"role": "ce qui sépare la pile de droite de son jeton de bord",
"default": "64px"
},
{
"name": "--vl-htri-pad",
"role": "du bas de la dernière ligne au filet",
"default": "18px"
},
{
"name": "--vl-htri-ink",
"role": "l'encre du rang PRIMAIRE — l'un des deux seuls niveaux du bandeau",
"default": "var(--vl-black)"
},
{
"name": "--vl-htri-muted",
"role": "l'encre du rang atténué, qui est celle de la bande par défaut",
"default": "var(--vl-ink-muted)"
},
{
"name": "--vl-htri-rule",
"role": "le filet qui souligne les trois zones d'un bord à l'autre",
"default": "var(--vl-hairline)"
}
],
"slots": [
"left",
"center",
"right",
"edge"
],
"geometry": {
"root": ".vl-htri",
"frame": [
1456,
55
],
"ratios": {
"height_over_width": 0.038,
"center_axis_over_width": 0.5,
"rule_gap_over_width": 0.013,
"left_ink_over_right_ink": 0.4
},
"type_px": {
"micro": 13
},
"pad_ratio": {
"top": 0,
"x": 0,
"bottom": 0.012
}
},
"benchmarks": [
{
"name": "LA ZONE CENTRALE EST SUR L'AXE DU CONTENEUR",
"measure": "Math.abs((box('.vl-htri__zone--c').left + box('.vl-htri__zone--c').right) / 2 - W / 2) / W",
"max": 0.003
},
{
"name": "…et c'est un MÉCANISME : les deux rails latéraux ont exactement la même largeur",
"measure": "box('.vl-htri__zone--l').w / box('.vl-htri__zone--r').w",
"expect": 1,
"tol": 0.005
},
{
"name": "…alors que ce qu'ils PORTENT n'a pas du tout la même largeur d'encre",
"measure": "(() => { const ink = (s) => { const r = document.createRange(); r.selectNodeContents(document.querySelector(s)); return r.getBoundingClientRect().width; }; return ink('.vl-htri__zone--l') / ink('.vl-htri__zone--r'); })()",
"max": 0.75
},
{
"name": "CONTRE-ÉPREUVE DU PIÈGE — un centrage entre les deux VOISINS, lui, dériverait de plus de 2 % de la largeur",
"measure": "(() => { const O = document.querySelector('.vl-htri').getBoundingClientRect(); const ink = (s) => { const r = document.createRange(); r.selectNodeContents(document.querySelector(s)); const b = r.getBoundingClientRect(); return { left: b.left - O.left, right: b.right - O.left }; }; return Math.abs((ink('.vl-htri__zone--l').right + ink('.vl-htri__zone--r').left) / 2 - W / 2) / W; })()",
"min": 0.02
},
{
"name": "CONTRE-ÉPREUVE DE LA CONSTRUCTION — un libellé de gauche PLUS LARGE QUE SON RAIL, et insécable, ne déplace toujours pas l'axe du centre",
"measure": "(() => { const el = document.querySelector('.vl-htri__mark'); const old = el.textContent; el.textContent = '©northbeamconsultingpartnersinternationalgroupholdingsworldwide'; const O = document.querySelector('.vl-htri').getBoundingClientRect(); const c = document.querySelector('.vl-htri__zone--c').getBoundingClientRect(); const out = Math.abs((c.left + c.right) / 2 - O.left - W / 2) / W; el.textContent = old; return out; })()",
"max": 0.004
},
{
"name": "le jeton de droite est poussé au bord EXTRÊME de la bande",
"measure": "(() => { const O = document.querySelector('.vl-htri').getBoundingClientRect(); const r = document.createRange(); r.selectNodeContents(document.querySelector('.vl-htri__edge')); return (O.right - r.getBoundingClientRect().right) / W; })()",
"max": 0.003
},
{
"name": "…et la marque touche le bord gauche, symétriquement",
"measure": "(() => { const O = document.querySelector('.vl-htri').getBoundingClientRect(); const r = document.createRange(); r.selectNodeContents(document.querySelector('.vl-htri__mark')); return (r.getBoundingClientRect().left - O.left) / W; })()",
"max": 0.003
},
{
"name": "UN SEUL CORPS dans tout le bandeau — c'est ce qui le fait lire comme une réglure",
"measure": "new Set([...document.querySelectorAll('.vl-htri, .vl-htri *')].map((e) => getComputedStyle(e).fontSize)).size",
"expect": 1,
"tol": 0
},
{
"name": "DEUX niveaux d'encre, et pas trois",
"measure": "new Set([...document.querySelectorAll('.vl-htri, .vl-htri *')].map((e) => getComputedStyle(e).color)).size",
"expect": 2,
"tol": 0
},
{
"name": "LA HIÉRARCHIE NE PASSE PAS PAR LA TAILLE : le jeton primaire a le corps ET la graisse de la mention atténuée qui le précède",
"measure": "num('.vl-htri__edge','fontSize') / num('.vl-htri__pair','fontSize') + (cs('.vl-htri__edge','fontWeight') === cs('.vl-htri__pair','fontWeight') ? 0 : 1)",
"expect": 1,
"tol": 0.01
},
{
"name": "CAPITALES PISTÉES — la seule casse d'appareil de la charte : au moins 0,1 em d'interlettrage",
"measure": "cs('.vl-htri','textTransform') === 'uppercase' ? num('.vl-htri','letterSpacing') / num('.vl-htri','fontSize') : 0",
"min": 0.1
},
{
"name": "RAIL DU HAUT — les trois zones pendent du même plafond",
"measure": "(Math.max(...['--l','--c','--r'].map((z) => box('.vl-htri__zone' + z).top)) - Math.min(...['--l','--c','--r'].map((z) => box('.vl-htri__zone' + z).top))) / H",
"expect": 0,
"tol": 0.01
},
{
"name": "le filet court sous les TROIS zones, d'un bord à l'autre",
"measure": "num('.vl-htri','borderBottomWidth')",
"min": 1
},
{
"name": "…et il respire : le blanc entre la dernière ligne et le filet vaut 1,3 % de la largeur",
"measure": "(box('.vl-htri').bottom - num('.vl-htri','borderBottomWidth') - Math.max(...['--l','--c','--r'].map((z) => box('.vl-htri__zone' + z).bottom))) / W",
"expect": 0.0124,
"tol": 0.003
},
{
"name": "les zones ne se chevauchent jamais : du blanc de part et d'autre du centre",
"measure": "(() => { const ink = (s) => { const r = document.createRange(); r.selectNodeContents(document.querySelector(s)); return r.getBoundingClientRect(); }; return Math.min(ink('.vl-htri__zone--c').left - ink('.vl-htri__zone--l').right, ink('.vl-htri__zone--r').left - ink('.vl-htri__zone--c').right) / W; })()",
"min": 0.03
},
{
"name": "APPAREIL, PAS TITRE : la bande reste sous 4 % de sa largeur en hauteur",
"measure": "H / W",
"max": 0.04
},
{
"name": "contraste du rang atténué — c'est lui qui porte le plus de texte, et c'est lui qui échoue d'habitude",
"measure": "contrast('.vl-htri__zone--c div')",
"min": 4.5
},
{
"name": "contraste du rang primaire",
"measure": "contrast('.vl-htri__edge')",
"min": 7
},
{
"name": "rien ne déborde de la bande",
"measure": "overflow('.vl-htri')",
"expect": 0,
"tol": 0
},
{
"name": "AUTONOMIE TYPOGRAPHIQUE — un serif imposé à la page hôte ne déplace pas d'un pixel le bord d'encre du jeton : le fragment épingle sa famille",
"measure": "(() => { const ink = () => { const r = document.createRange(); r.selectNodeContents(document.querySelector('.vl-htri__edge')); return r.getBoundingClientRect().width; }; const a = ink(); const h = document.documentElement, old = h.style.fontFamily; h.style.fontFamily = 'Georgia, \"Times New Roman\", serif'; const b = ink(); h.style.fontFamily = old; return b / a; })()",
"expect": 1,
"tol": 0.01
}
],
"notes": "Le pattern tient dans une construction qu'on ne voit pas et dans une loi qu'on voit sans la nommer.\n\nLA CONSTRUCTION : `minmax(0, 1fr) auto minmax(0, 1fr)`. Les deux rails latéraux ont la même largeur par définition de `1fr`, donc la colonne `auto` du milieu est exactement sur l'axe du conteneur — et elle y reste quels que soient les libellés. Un `space-between` à trois enfants produit un balisage d'apparence identique qui dérive : ici l'encre de gauche fait 0,4 fois celle de droite, et le milieu de leurs deux bords tombe à 5,5 % de la largeur à gauche de l'axe. Deux benchmarks tiennent la règle des deux côtés — le centre est sur l'axe, ET le milieu de ses voisins ne l'est pas. Le second est le seul qui empêche quelqu'un de « simplifier » le pattern : sans lui, une réécriture fautive resterait verte tant que les libellés de démonstration sont équilibrés.\n\nLe `minmax(0, …)` fait le reste du travail, et c'est la part la moins évidente — au point que la contre-épreuve a d'abord été écrite trop faible. Avec un `1fr` nu, la piste a une taille MINIMALE automatique, qui est le min-content de ce qu'elle porte : un rail dont le contenu ne peut pas se replier sous la largeur de piste S'ÉLARGIT, et pousse le centre. Or un libellé long mais SÉCABLE (« ©northbeam consulting partners international ») se replie tout seul : son min-content est celui de son mot le plus long, et l'axe ne bouge pas — mesuré, `1fr` et `minmax(0, 1fr)` sont alors indiscernables, et la contre-épreuve sortait verte sur la construction fautive. Ce qui les sépare est un libellé INSÉCABLE plus large que son rail : une adresse électronique, un nom d'enseigne d'un seul tenant. La contre-épreuve en pose un et remesure l'axe — `minmax(0, 1fr)` le tient à 0, `1fr` le déplace de 3,7 % de la largeur, soit 54 px. C'est la forme générale du piège : une contre-épreuve doit reparamétrer là où les deux constructions DIVERGENT, pas là où l'une des deux est simplement plus confortable.\n\nLA LOI : un appareil n'est pas un titre. UN SEUL corps dans tout le bandeau, DEUX niveaux d'encre, et rien d'autre — le jeton de droite, qui est ce qu'on retient de la page, a exactement le corps et la graisse de la mention atténuée qui le précède. Il n'en diffère que par l'encre et par sa position, poussé au bord extrême. C'est la construction inverse d'un titre, et c'est ce qui fait qu'un bandeau répété huit fois se lit comme une réglure au lieu de redemander l'attention à chaque page. Les deux `Set` qui comptent les corps et les encres sont la façon la plus courte de l'asserter : dès qu'un troisième niveau d'encre apparaît — l'envie de « souligner » une zone —, le bandeau redevient du texte.\n\nLes capitales sont pistées à 0,12 em. Ce n'est pas un effet : à 13 px, des capitales sans interlettrage se referment et la bande devient un bloc gris. C'est la seule casse capitale de la charte d'origine, et elle est réservée à l'appareil.\n\nCe pattern n'a AUCUN accent, volontairement. La charte dont il vient tient sur « neutre + un accent fluo » : un bandeau qui court sur les huit pages et qui poserait l'accent le dépenserait huit fois, c'est-à-dire jamais. Le budget de couleur reste entier pour le contenu.\n\nDifférence avec `layout-12-nav-three-zone`, qui résout le même problème d'axe pour un autre objet : là-bas la zone centrale sort du flux, parce qu'elle porte un logotype qui a le droit de passer sous ses voisins et parce que la rangée porte un bouton d'action — un objet, pas du texte. Ici les trois zones sont du texte de même corps et le centre reste DANS le flux : la grille suffit, et elle a l'avantage de garantir en plus que rien ne se chevauche jamais.",
"id": "layout-16-header-tripartite"
}
À côté
layout-01-nested-bento
Bento à cartes imbriquées
layout-02-swiss-frame
Cadre suisse header / footer
layout-03-glass-board
Écran en verre dépoli — fond dégradé + modules
layout-04-graph-paper-cells
Papier millimétré et ses cases
layout-05-nav-pill-bar