card-13-email-figure-band
list-06-email-digest
Digest de newsletter — entrées numérotées séparées par un filet
Aligner les trois à six entrées d'une lettre d'information dans un mail, chacune tenant en rubrique, titre, chapô et lien, sans qu'aucune ne se lise comme un encadré : la colonne du numéro est une CELLULE de largeur fixe et la séparation un filet posé en haut de chaque entrée — retiré sur la première, pour que le filet sépare au lieu d'encadrer.
listemail520×594px16 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 corps d'une lettre d'information périodique : plusieurs sujets de même rang, chacun résumé en deux lignes et renvoyant vers un article. Le numéro donne un ordre de lecture explicite, ce qui vaut mieux qu'un tri implicite quand tous les titres pèsent le même poids.
Éviter quand
Une seule entrée (le numéro et le filet n'ont plus rien à séparer, une simple accroche suffit), plus de six (la lettre devient un sommaire et personne ne descend), ou les entrées ne sont pas de même nature — un digest suggère une lecture de haut en bas qui devient fausse dès qu'une entrée est d'un autre genre que les autres.
Notes
Se pose dans le slot `body` de layout-09-email-envelope, dont il reprend exactement la largeur utile. Deux choix d'écriture sont dictés par le canal, et ce sont eux qui distinguent ce pattern de son équivalent web. Le filet du premier item est retiré par une CLASSE (`--first`) et non par `:first-child` : un pseudo-classe n'a pas d'équivalent en attribut `style`, donc elle disparaît à l'inlining que réclame un client mail — `bin/emit.mjs` la refuse d'ailleurs comme sélecteur non inlinable. La colonne du numéro est une cellule à largeur déclarée sur une table à disposition fixe, pas une colonne de grille : c'est la seule mise en colonnes qu'Outlook respecte. Le benchmark qui compte les éléments chromatiques est écrit en SATURATION et non sur une classe : ajouter plus tard un lien bleu ou une rubrique colorée est exactement la régression qu'un contrôle par classe laisserait passer, et c'est celle qui casse la règle de la charte (« le bleu occupe la surface entière ou il colore un chiffre, rien entre les deux »).
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs list-06-email-digest : le pattern se prouve, il ne se croit pas.
| assertion | mesure | attendu |
|---|---|---|
| 520 px — le slot exact de layout-09-email-envelope (600 − 2 × 40) | W | 520 ± 0 |
| colonne du numéro ≈ 10,8 % de la largeur (56 px : le chiffre plus sa gouttière) | box('.vl-emdig__num').w / W | 0.1077 ± 0.012 |
| LE FILET SÉPARE, IL N'ENCADRE PAS : la première entrée n'a pas de filet haut | num('.vl-emdig__item--first','borderTopWidth') | 0 ± 0 |
| … et les deux suivantes en portent un, d'un pixel | [...document.querySelectorAll('.vl-emdig__item')].filter((e) => parseFloat(getComputedStyle(e).borderTopWidth) === 1).length | 2 ± 0 |
| aucune bordure basse : le bloc ne se referme pas sur lui-même | [...document.querySelectorAll('.vl-emdig__item')].reduce((a, e) => a + parseFloat(getComputedStyle(e).borderBottomWidth), 0) | 0 ± 0 |
| LE BLEU NE COLORE QUE LES NUMÉROS — trois éléments chromatiques, pas un de plus | [...document.querySelectorAll('.vl-emdig *')].filter((e) => { const c = parseRgb(getComputedStyle(e).color); return Math.max(...c) - Math.min(...c) > 40; }).length | 3 ± 0 |
| le numéro pèse plus que le titre qu'il ordonne | num('.vl-emdig__num','fontSize') / num('.vl-emdig__title','fontSize') | ≥ 1.25 |
| saut typographique titre / chapô ≥ 1,2 | num('.vl-emdig__title','fontSize') / num('.vl-emdig__lede','fontSize') | ≥ 1.2 |
| les entrées respirent autant qu'elles se séparent : l'air au-dessus du filet vaut au moins un corps de titre | num('.vl-emdig__item','paddingBottom') / num('.vl-emdig__title','fontSize') | ≥ 1 |
| le numéro et sa rubrique partagent leur ligne haute | Math.abs(box('.vl-emdig__num').top - box('.vl-emdig__kicker').top) | ≤ 4 |
| contraste du chapô gris ≥ 4,5 — le piège payé trois lots de suite | contrast('.vl-emdig__lede') | ≥ 4.5 |
| contraste de la rubrique ≥ 4,5 : c'est le plus petit corps du bloc | contrast('.vl-emdig__kicker') | ≥ 4.5 |
| contraste du titre ≥ 7 | contrast('.vl-emdig__title') | ≥ 7 |
| contraste du numéro bleu ≥ 4,5 | contrast('.vl-emdig__num') | ≥ 4.5 |
| le lien reste identifiable sans la couleur : il est souligné | cs('.vl-emdig__link','textDecorationLine').includes('underline') ? 1 : 0 | 1 ± 0 |
| aucun débordement | overflow('.vl-emdig') | ≤ 0 |
Le code
patterns/list-06-email-digest.html
Télécharger<!-- list-06-email-digest — le corps d'une newsletter : N entrées numérotées, séparées par un
filet, chacune tenant en rubrique / titre / chapô / lien.
Écrit NATIVEMENT pour le canal mail. Deux conséquences visibles dans le code :
— la colonne du numéro est une CELLULE de largeur fixe (`table-layout: fixed`), pas une
colonne de grille ni un élément positionné : c'est la seule mise en colonnes que le
moteur Word rende à l'identique ;
— le filet de séparation est un `border-top` posé sur la cellule de l'entrée, et retiré sur
la PREMIÈRE par une classe — pas un `::before`, pas un `:first-child` : ni l'un ni l'autre
ne survit à l'inlining que réclame un client mail.
520 px de large = 600 − 2 × 40 : la mesure exacte du slot `body` de layout-09-email-envelope.
Couleurs via --vl-* uniquement. Le bleu ne sert QU'À colorer les numéros — c'est la règle de
ref-04 (« il occupe la surface entière ou il colore un chiffre, rien entre les deux »). -->
<table class="vl-emdig" role="presentation" cellpadding="0" cellspacing="0" border="0" width="520">
<tr>
<td class="vl-emdig__item vl-emdig__item--first">
<table class="vl-emdig__grid" role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td class="vl-emdig__num" valign="top">01</td>
<td class="vl-emdig__txt" valign="top">
<div class="vl-emdig__kicker">MARCHÉS</div>
<div class="vl-emdig__title">La pente à dix ans se redresse pour la première fois depuis mars</div>
<p class="vl-emdig__lede">Le mouvement est lent et il est général : douze des quinze
courbes suivies ont bougé dans le même sens sur la semaine.</p>
<a class="vl-emdig__link" href="https://northbeam.invalid/1">LIRE →</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="vl-emdig__item">
<table class="vl-emdig__grid" role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td class="vl-emdig__num" valign="top">02</td>
<td class="vl-emdig__txt" valign="top">
<div class="vl-emdig__kicker">RÉGLEMENTAIRE</div>
<div class="vl-emdig__title">Ce que la révision de norme change pour les contrats longs</div>
<p class="vl-emdig__lede">Une seule ligne du texte déplace la frontière entre deux
modèles d'évaluation. Elle s'applique aux exercices ouverts en janvier.</p>
<a class="vl-emdig__link" href="https://northbeam.invalid/2">LIRE →</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="vl-emdig__item">
<table class="vl-emdig__grid" role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td class="vl-emdig__num" valign="top">03</td>
<td class="vl-emdig__txt" valign="top">
<div class="vl-emdig__kicker">ALLOCATION</div>
<div class="vl-emdig__title">Trois portefeuilles types, et le seul arbitrage qui les sépare</div>
<p class="vl-emdig__lede">À horizon dix ans, l'écart de performance tient à une
décision unique — prise une fois, jamais rejouée.</p>
<a class="vl-emdig__link" href="https://northbeam.invalid/3">LIRE →</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<style>
.vl-emdig {
width: 520px;
border-collapse: collapse;
background: var(--vl-white);
color: var(--vl-black);
font-family: "Helvetica Neue", Inter, Arial, sans-serif;
}
/* Le filet SÉPARE, il n'encadre pas : posé en haut de chaque entrée, retiré sur la première.
Un filet complet ferait une boîte, et une boîte dans un mail se lit comme une pièce jointe. */
.vl-emdig__item { padding: 26px 0 24px; border-top: 1px solid var(--vl-black); }
.vl-emdig__item--first { padding-top: 0; border-top-width: 0; }
.vl-emdig__grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
/* 56 px = le chiffre (30) + sa gouttière (26). Une largeur de CELLULE, donc respectée par
Outlook ; une gouttière `gap` ne le serait pas. */
.vl-emdig__num {
width: 56px;
color: var(--vl-blue);
font-size: 26px; line-height: 26px; font-weight: 800; letter-spacing: -0.02em;
}
.vl-emdig__txt { color: var(--vl-black); }
.vl-emdig__kicker {
margin: 0 0 8px;
color: var(--vl-ink-muted);
font-size: 10px; line-height: 13px; font-weight: 600; letter-spacing: 0.14em;
}
.vl-emdig__title {
margin: 0 0 8px;
color: var(--vl-black);
font-size: 19px; line-height: 25px; font-weight: 700; letter-spacing: -0.01em;
}
.vl-emdig__lede {
margin: 0 0 12px;
color: var(--vl-ink-muted);
font-size: 15px; line-height: 23px; font-weight: 400;
}
/* Souligné, et pas seulement coloré : dans un client qui force sa propre couleur de lien,
le soulignement est ce qui reste. */
.vl-emdig__link {
color: var(--vl-black);
font-size: 12px; line-height: 16px; font-weight: 700; letter-spacing: 0.1em;
text-decoration: underline;
}
</style>
systems/ref-04-swiss-investor-blue.json
Télécharger:root {
--vl-bg: #D9D2C7;
--vl-board: #D9D2C7;
--vl-blue: #1B44D8;
--vl-black: #0D0D0D;
--vl-white: #FFFFFF;
--vl-ink-muted: #6E6E6E;
--vl-ink-muted-invert: #D3D3D3;
--vl-hairline: #00000018;
--vl-hairline-invert: #FFFFFF33;
--vl-ph-a: #C9C9C9;
--vl-ph-b: #5A5A5A;
--vl-margin: 48px;
}
les métadonnées — patterns/list-06-email-digest.json (télécharger)
{
"name": "Digest de newsletter — entrées numérotées séparées par un filet",
"family": "list",
"ref": "ref-04-swiss-investor-blue",
"intent": "Aligner les trois à six entrées d'une lettre d'information dans un mail, chacune tenant en rubrique, titre, chapô et lien, sans qu'aucune ne se lise comme un encadré : la colonne du numéro est une CELLULE de largeur fixe et la séparation un filet posé en haut de chaque entrée — retiré sur la première, pour que le filet sépare au lieu d'encadrer.",
"when_to_use": "Le corps d'une lettre d'information périodique : plusieurs sujets de même rang, chacun résumé en deux lignes et renvoyant vers un article. Le numéro donne un ordre de lecture explicite, ce qui vaut mieux qu'un tri implicite quand tous les titres pèsent le même poids.",
"avoid_when": "Une seule entrée (le numéro et le filet n'ont plus rien à séparer, une simple accroche suffit), plus de six (la lettre devient un sommaire et personne ne descend), ou les entrées ne sont pas de même nature — un digest suggère une lecture de haut en bas qui devient fausse dès qu'une entrée est d'un autre genre que les autres.",
"media": [
"email"
],
"tags": [
"email",
"mailing",
"newsletter",
"digest",
"sommaire",
"entrees",
"articles",
"numerote",
"filet",
"separateur",
"chapo",
"lien",
"outlook",
"table"
],
"vars": [
{
"name": "--vl-white",
"role": "aplat du bloc — plein, jamais translucide",
"default": "#FFFFFF"
},
{
"name": "--vl-black",
"role": "titres, filets de séparation et lien",
"default": "#0D0D0D"
},
{
"name": "--vl-blue",
"role": "le numéro d'entrée — le SEUL élément chromatique du bloc",
"default": "#1B44D8"
},
{
"name": "--vl-ink-muted",
"role": "rubrique et chapô",
"default": "#6E6E6E"
}
],
"slots": [
"num",
"kicker",
"title",
"lede",
"link"
],
"geometry": {
"root": ".vl-emdig",
"frame": [
520,
594
],
"ratios": {
"num_col_over_width": 0.1077,
"title_over_lede": 1.27,
"num_over_title": 1.37,
"item_pad_top_over_width": 0.05
},
"type_px": {
"num": 26,
"title": 19,
"lede": 15,
"kicker": 10,
"link": 12
},
"pad_ratio": {
"top": 0,
"x": 0,
"bottom": 0.046
}
},
"benchmarks": [
{
"name": "520 px — le slot exact de layout-09-email-envelope (600 − 2 × 40)",
"measure": "W",
"expect": 520,
"tol": 0
},
{
"name": "colonne du numéro ≈ 10,8 % de la largeur (56 px : le chiffre plus sa gouttière)",
"measure": "box('.vl-emdig__num').w / W",
"expect": 0.1077,
"tol": 0.012
},
{
"name": "LE FILET SÉPARE, IL N'ENCADRE PAS : la première entrée n'a pas de filet haut",
"measure": "num('.vl-emdig__item--first','borderTopWidth')",
"expect": 0,
"tol": 0
},
{
"name": "… et les deux suivantes en portent un, d'un pixel",
"measure": "[...document.querySelectorAll('.vl-emdig__item')].filter((e) => parseFloat(getComputedStyle(e).borderTopWidth) === 1).length",
"expect": 2,
"tol": 0
},
{
"name": "aucune bordure basse : le bloc ne se referme pas sur lui-même",
"measure": "[...document.querySelectorAll('.vl-emdig__item')].reduce((a, e) => a + parseFloat(getComputedStyle(e).borderBottomWidth), 0)",
"expect": 0,
"tol": 0
},
{
"name": "LE BLEU NE COLORE QUE LES NUMÉROS — trois éléments chromatiques, pas un de plus",
"measure": "[...document.querySelectorAll('.vl-emdig *')].filter((e) => { const c = parseRgb(getComputedStyle(e).color); return Math.max(...c) - Math.min(...c) > 40; }).length",
"expect": 3,
"tol": 0
},
{
"name": "le numéro pèse plus que le titre qu'il ordonne",
"measure": "num('.vl-emdig__num','fontSize') / num('.vl-emdig__title','fontSize')",
"min": 1.25
},
{
"name": "saut typographique titre / chapô ≥ 1,2",
"measure": "num('.vl-emdig__title','fontSize') / num('.vl-emdig__lede','fontSize')",
"min": 1.2
},
{
"name": "les entrées respirent autant qu'elles se séparent : l'air au-dessus du filet vaut au moins un corps de titre",
"measure": "num('.vl-emdig__item','paddingBottom') / num('.vl-emdig__title','fontSize')",
"min": 1
},
{
"name": "le numéro et sa rubrique partagent leur ligne haute",
"measure": "Math.abs(box('.vl-emdig__num').top - box('.vl-emdig__kicker').top)",
"max": 4
},
{
"name": "contraste du chapô gris ≥ 4,5 — le piège payé trois lots de suite",
"measure": "contrast('.vl-emdig__lede')",
"min": 4.5
},
{
"name": "contraste de la rubrique ≥ 4,5 : c'est le plus petit corps du bloc",
"measure": "contrast('.vl-emdig__kicker')",
"min": 4.5
},
{
"name": "contraste du titre ≥ 7",
"measure": "contrast('.vl-emdig__title')",
"min": 7
},
{
"name": "contraste du numéro bleu ≥ 4,5",
"measure": "contrast('.vl-emdig__num')",
"min": 4.5
},
{
"name": "le lien reste identifiable sans la couleur : il est souligné",
"measure": "cs('.vl-emdig__link','textDecorationLine').includes('underline') ? 1 : 0",
"expect": 1,
"tol": 0
},
{
"name": "aucun débordement",
"measure": "overflow('.vl-emdig')",
"max": 0
}
],
"notes": "Se pose dans le slot `body` de layout-09-email-envelope, dont il reprend exactement la largeur utile.\n\nDeux choix d'écriture sont dictés par le canal, et ce sont eux qui distinguent ce pattern de son équivalent web. Le filet du premier item est retiré par une CLASSE (`--first`) et non par `:first-child` : un pseudo-classe n'a pas d'équivalent en attribut `style`, donc elle disparaît à l'inlining que réclame un client mail — `bin/emit.mjs` la refuse d'ailleurs comme sélecteur non inlinable. La colonne du numéro est une cellule à largeur déclarée sur une table à disposition fixe, pas une colonne de grille : c'est la seule mise en colonnes qu'Outlook respecte.\n\nLe benchmark qui compte les éléments chromatiques est écrit en SATURATION et non sur une classe : ajouter plus tard un lien bleu ou une rubrique colorée est exactement la régression qu'un contrôle par classe laisserait passer, et c'est celle qui casse la règle de la charte (« le bleu occupe la surface entière ou il colore un chiffre, rien entre les deux »).",
"id": "list-06-email-digest"
}
À côté
layout-02-swiss-frame
Cadre suisse header / footer
layout-09-email-envelope
Enveloppe de mailing — garde, colonne de 600, trois aplats pleins
list-01-giant-numbers
Colonnes à numéro géant
list-02-ruled-index
Sommaire à filets numérotés
list-03-two-column-toc