card-01-ghost-icon
card-13-email-figure-band
Bande de trois chiffres pour un mail — colonnes en cellules, filets d'un pixel
Poser trois indicateurs de même nature en une bande dans un mail, sans aplat de carte ni encadrement : les colonnes sont des cellules à largeur en pourcentage sur une table à disposition fixe — la seule répartition en parts égales qu'Outlook respecte — et les séparateurs sont des filets de cellule, pas des colonnes intercalaires. La couleur ne fait qu'une chose : elle porte les chiffres.
cardemail520×88px17 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
Ouvrir ou clore une lettre d'information par trois valeurs comparables (un encours, une variation, un horizon) que le lecteur doit pouvoir saisir sans lire le corps. Le chiffre est court — six signes au plus — et son libellé tient sur une ligne.
Éviter quand
Les valeurs ne sont pas comparables (la bande impose une lecture de gauche à droite qui n'existe pas), il y en a plus de trois (au-delà, les colonnes tombent sous 120 px et les chiffres se coupent sur mobile), ou la lettre est déjà dense en chiffres — une bande de chiffres au milieu d'un texte de chiffres ne hiérarchise plus rien.
Notes
Se pose dans le slot `body` de layout-09-email-envelope, dont il reprend exactement la largeur utile. Le détail qui rend ce pattern non trivial : sur une table à disposition fixe, la largeur déclarée d'une cellule englobe son padding ET son filet. C'est ce qui permet de poser un séparateur sur deux colonnes sur trois sans décaler la répartition — dans un modèle en boîtes, le même geste aurait élargi deux colonnes d'un pixel chacune. Le benchmark d'égalité des largeurs est là précisément pour attraper le jour où quelqu'un remplacera la table par des `<div>`. La bande ne porte aucun aplat de carte, contrairement à son équivalent de slide. Ce n'est pas une simplification : la charte ref-04 n'admet le bleu qu'en surface entière ou en chiffre, et un aplat de carte par colonne aurait introduit la valeur intermédiaire que ce système refuse. Le comptage des éléments chromatiques verrouille la règle. Écrit nativement pour le canal : `node bin/emit.mjs card-13-email-figure-band --target email` sort sans bloquant.
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs card-13-email-figure-band : 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 |
| TROIS COLONNES DE MÊME LARGEUR — un filet de cellule ne doit pas décaler la répartition | Math.max(...[...document.querySelectorAll('.vl-emband__cell')].map((e) => e.getBoundingClientRect().width)) - Math.min(...[...document.querySelectorAll('.vl-emband__cell')].map((e) => e.getBoundingClientRect().width)) | ≤ 1.5 |
| chaque colonne vaut un tiers de la bande | box('.vl-emband__cell').w / W | 0.3333 ± 0.01 |
| la bande reste une bande : sa hauteur ne dépasse pas le quart de sa largeur | H / W | ≤ 0.25 |
| les trois colonnes partagent leur ligne haute et leur ligne basse | [...document.querySelectorAll('.vl-emband__cell')].reduce((a, e, i, t) => a + Math.abs(e.getBoundingClientRect().top - t[0].getBoundingClientRect().top) + Math.abs(e.getBoundingClientRect().bottom - t[0].getBoundingClientRect().bottom), 0) | ≤ 1.5 |
| DEUX séparateurs pour trois colonnes : la bande n'est pas encadrée | [...document.querySelectorAll('.vl-emband__cell')].filter((e) => parseFloat(getComputedStyle(e).borderLeftWidth) > 0).length | 2 ± 0 |
| un séparateur est un filet, pas un trait : un pixel | num('.vl-emband__cell--split','borderLeftWidth') | 1 ± 0 |
| aucun rayon — angles vifs, et zéro dégradation dans Outlook | ['.vl-emband','.vl-emband__cell'].reduce((a, s) => a + parseFloat(getComputedStyle(document.querySelector(s)).borderTopLeftRadius), 0) | 0 ± 0 |
| saut typographique chiffre / libellé ≥ 3 — la charte n'a pas de taille intermédiaire | num('.vl-emband__fig','fontSize') / num('.vl-emband__label','fontSize') | ≥ 3 |
| CHAQUE LIBELLÉ ET CHAQUE NOTE TIENNENT SUR UNE LIGNE — au-delà, les colonnes cessent d'avoir la même assise | Math.max(...[...document.querySelectorAll('.vl-emband__note, .vl-emband__label')].map((e) => e.getBoundingClientRect().height / parseFloat(getComputedStyle(e).lineHeight))) | ≤ 1.05 |
| LE CHIFFRE EST LE SEUL ÉLÉMENT CHROMATIQUE — trois, pas un de plus | [...document.querySelectorAll('.vl-emband *')].filter((e) => { const c = parseRgb(getComputedStyle(e).color); return Math.max(...c) - Math.min(...c) > 40; }).length | 3 ± 0 |
| … et il est vraiment coloré : la couleur du chiffre n'est pas un gris | (() => { const c = parseRgb(cs('.vl-emband__fig','color')); return Math.max(...c) - Math.min(...c); })() | ≥ 40 |
| contraste du chiffre ≥ 4,5 | contrast('.vl-emband__fig') | ≥ 4.5 |
| contraste du libellé ≥ 4,5 — c'est le gris qui plafonne, pas le chiffre | contrast('.vl-emband__label') | ≥ 4.5 |
| contraste de la note ≥ 4,5 | contrast('.vl-emband__note') | ≥ 4.5 |
| plancher de lisibilité : aucun corps sous 10 px | Math.min(...[...document.querySelectorAll('.vl-emband *')].filter((e) => !e.children.length && e.textContent.trim()).map((e) => parseFloat(getComputedStyle(e).fontSize))) | ≥ 10 |
| aucun débordement | overflow('.vl-emband') | ≤ 0 |
Le code
patterns/card-13-email-figure-band.html
Télécharger<!-- card-13-email-figure-band — trois chiffres de même nature en une bande, dans un mail.
Écrit NATIVEMENT pour le canal. Ce que ça change par rapport à la rangée de KPI d'une
slide : les colonnes sont des CELLULES à largeur en pourcentage sur une table
`table-layout: fixed` — la seule répartition en trois parts égales qu'Outlook respecte —
et les séparateurs sont des `border-left` de cellule, pas des éléments intercalaires : une
colonne de gouttière de plus rendrait la bande impossible à empiler sur mobile.
Aucun aplat de carte, aucun rayon, aucune ombre : la charte ref-04 ne connaît que des
angles vifs, et c'est aussi ce qu'un client mail rend sans dégradation. Le bleu ne fait
qu'une chose — il colore les chiffres.
520 px = le slot `body` de layout-09-email-envelope (600 − 2 × 40).
Couleurs via --vl-* uniquement. -->
<table class="vl-emband" role="presentation" cellpadding="0" cellspacing="0" border="0" width="520">
<tr>
<td class="vl-emband__cell" valign="top">
<div class="vl-emband__label">ENCOURS SUIVIS</div>
<div class="vl-emband__fig">1 240</div>
<div class="vl-emband__note">AU 31 JUILLET</div>
</td>
<td class="vl-emband__cell vl-emband__cell--split" valign="top">
<div class="vl-emband__label">ÉCART TYPE</div>
<div class="vl-emband__fig">+18,4 %</div>
<div class="vl-emband__note">SUR DOUZE MOIS</div>
</td>
<td class="vl-emband__cell vl-emband__cell--split" valign="top">
<div class="vl-emband__label">HORIZON MÉDIAN</div>
<div class="vl-emband__fig">9 ans</div>
<div class="vl-emband__note">TOUTES POCHES</div>
</td>
</tr>
</table>
<style>
.vl-emband {
width: 520px;
border-collapse: collapse;
background: var(--vl-white);
color: var(--vl-black);
font-family: "Helvetica Neue", Inter, Arial, sans-serif;
}
/* Trois parts égales par le POURCENTAGE et non par une grille : sur une table à disposition
fixe, la largeur déclarée d'une cellule englobe son padding et son filet, donc les trois
colonnes restent d'aplomb même quand deux d'entre elles portent un séparateur. */
.vl-emband__cell {
width: 33.33%;
padding: 2px 16px 2px 0;
}
.vl-emband__cell--split {
border-left: 1px solid var(--vl-black);
padding-left: 18px;
}
.vl-emband__label {
margin: 0 0 12px;
color: var(--vl-ink-muted);
font-size: 10px; line-height: 13px; font-weight: 600; letter-spacing: 0.14em;
}
/* Le seul élément chromatique de la bande. Deux corps et rien entre les deux : le chiffre
pèse plus de trois fois le libellé, et c'est ce vide qui tient la hiérarchie sans couleur
de fond ni encadrement. */
.vl-emband__fig {
margin: 0 0 10px;
color: var(--vl-blue);
font-size: 34px; line-height: 34px; font-weight: 800; letter-spacing: -0.03em;
}
.vl-emband__note {
margin: 0;
color: var(--vl-ink-muted);
font-size: 10px; line-height: 15px; font-weight: 500; letter-spacing: 0.06em;
}
</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/card-13-email-figure-band.json (télécharger)
{
"name": "Bande de trois chiffres pour un mail — colonnes en cellules, filets d'un pixel",
"family": "card",
"ref": "ref-04-swiss-investor-blue",
"intent": "Poser trois indicateurs de même nature en une bande dans un mail, sans aplat de carte ni encadrement : les colonnes sont des cellules à largeur en pourcentage sur une table à disposition fixe — la seule répartition en parts égales qu'Outlook respecte — et les séparateurs sont des filets de cellule, pas des colonnes intercalaires. La couleur ne fait qu'une chose : elle porte les chiffres.",
"when_to_use": "Ouvrir ou clore une lettre d'information par trois valeurs comparables (un encours, une variation, un horizon) que le lecteur doit pouvoir saisir sans lire le corps. Le chiffre est court — six signes au plus — et son libellé tient sur une ligne.",
"avoid_when": "Les valeurs ne sont pas comparables (la bande impose une lecture de gauche à droite qui n'existe pas), il y en a plus de trois (au-delà, les colonnes tombent sous 120 px et les chiffres se coupent sur mobile), ou la lettre est déjà dense en chiffres — une bande de chiffres au milieu d'un texte de chiffres ne hiérarchise plus rien.",
"media": [
"email"
],
"tags": [
"email",
"mailing",
"newsletter",
"chiffre",
"kpi",
"indicateur",
"bande",
"colonnes",
"filet",
"outlook",
"table",
"stat",
"resume"
],
"vars": [
{
"name": "--vl-white",
"role": "aplat du bloc — plein, jamais translucide",
"default": "#FFFFFF"
},
{
"name": "--vl-black",
"role": "filets verticaux de séparation",
"default": "#0D0D0D"
},
{
"name": "--vl-blue",
"role": "les chiffres — le SEUL élément chromatique de la bande",
"default": "#1B44D8"
},
{
"name": "--vl-ink-muted",
"role": "libellé au-dessus et note en dessous",
"default": "#6E6E6E"
}
],
"slots": [
"label",
"fig",
"note"
],
"geometry": {
"root": ".vl-emband",
"frame": [
520,
88
],
"ratios": {
"column_over_width": 0.3333,
"fig_over_label": 3.4,
"height_over_width": 0.169,
"rule_px": 1
},
"type_px": {
"fig": 34,
"label": 10,
"note": 10
},
"pad_ratio": {
"top": 0.004,
"x": 0.031,
"bottom": 0.004
}
},
"benchmarks": [
{
"name": "520 px — le slot exact de layout-09-email-envelope (600 − 2 × 40)",
"measure": "W",
"expect": 520,
"tol": 0
},
{
"name": "TROIS COLONNES DE MÊME LARGEUR — un filet de cellule ne doit pas décaler la répartition",
"measure": "Math.max(...[...document.querySelectorAll('.vl-emband__cell')].map((e) => e.getBoundingClientRect().width)) - Math.min(...[...document.querySelectorAll('.vl-emband__cell')].map((e) => e.getBoundingClientRect().width))",
"max": 1.5
},
{
"name": "chaque colonne vaut un tiers de la bande",
"measure": "box('.vl-emband__cell').w / W",
"expect": 0.3333,
"tol": 0.01
},
{
"name": "la bande reste une bande : sa hauteur ne dépasse pas le quart de sa largeur",
"measure": "H / W",
"max": 0.25
},
{
"name": "les trois colonnes partagent leur ligne haute et leur ligne basse",
"measure": "[...document.querySelectorAll('.vl-emband__cell')].reduce((a, e, i, t) => a + Math.abs(e.getBoundingClientRect().top - t[0].getBoundingClientRect().top) + Math.abs(e.getBoundingClientRect().bottom - t[0].getBoundingClientRect().bottom), 0)",
"max": 1.5
},
{
"name": "DEUX séparateurs pour trois colonnes : la bande n'est pas encadrée",
"measure": "[...document.querySelectorAll('.vl-emband__cell')].filter((e) => parseFloat(getComputedStyle(e).borderLeftWidth) > 0).length",
"expect": 2,
"tol": 0
},
{
"name": "un séparateur est un filet, pas un trait : un pixel",
"measure": "num('.vl-emband__cell--split','borderLeftWidth')",
"expect": 1,
"tol": 0
},
{
"name": "aucun rayon — angles vifs, et zéro dégradation dans Outlook",
"measure": "['.vl-emband','.vl-emband__cell'].reduce((a, s) => a + parseFloat(getComputedStyle(document.querySelector(s)).borderTopLeftRadius), 0)",
"expect": 0,
"tol": 0
},
{
"name": "saut typographique chiffre / libellé ≥ 3 — la charte n'a pas de taille intermédiaire",
"measure": "num('.vl-emband__fig','fontSize') / num('.vl-emband__label','fontSize')",
"min": 3
},
{
"name": "CHAQUE LIBELLÉ ET CHAQUE NOTE TIENNENT SUR UNE LIGNE — au-delà, les colonnes cessent d'avoir la même assise",
"measure": "Math.max(...[...document.querySelectorAll('.vl-emband__note, .vl-emband__label')].map((e) => e.getBoundingClientRect().height / parseFloat(getComputedStyle(e).lineHeight)))",
"max": 1.05
},
{
"name": "LE CHIFFRE EST LE SEUL ÉLÉMENT CHROMATIQUE — trois, pas un de plus",
"measure": "[...document.querySelectorAll('.vl-emband *')].filter((e) => { const c = parseRgb(getComputedStyle(e).color); return Math.max(...c) - Math.min(...c) > 40; }).length",
"expect": 3,
"tol": 0
},
{
"name": "… et il est vraiment coloré : la couleur du chiffre n'est pas un gris",
"measure": "(() => { const c = parseRgb(cs('.vl-emband__fig','color')); return Math.max(...c) - Math.min(...c); })()",
"min": 40
},
{
"name": "contraste du chiffre ≥ 4,5",
"measure": "contrast('.vl-emband__fig')",
"min": 4.5
},
{
"name": "contraste du libellé ≥ 4,5 — c'est le gris qui plafonne, pas le chiffre",
"measure": "contrast('.vl-emband__label')",
"min": 4.5
},
{
"name": "contraste de la note ≥ 4,5",
"measure": "contrast('.vl-emband__note')",
"min": 4.5
},
{
"name": "plancher de lisibilité : aucun corps sous 10 px",
"measure": "Math.min(...[...document.querySelectorAll('.vl-emband *')].filter((e) => !e.children.length && e.textContent.trim()).map((e) => parseFloat(getComputedStyle(e).fontSize)))",
"min": 10
},
{
"name": "aucun débordement",
"measure": "overflow('.vl-emband')",
"max": 0
}
],
"notes": "Se pose dans le slot `body` de layout-09-email-envelope, dont il reprend exactement la largeur utile.\n\nLe détail qui rend ce pattern non trivial : sur une table à disposition fixe, la largeur déclarée d'une cellule englobe son padding ET son filet. C'est ce qui permet de poser un séparateur sur deux colonnes sur trois sans décaler la répartition — dans un modèle en boîtes, le même geste aurait élargi deux colonnes d'un pixel chacune. Le benchmark d'égalité des largeurs est là précisément pour attraper le jour où quelqu'un remplacera la table par des `<div>`.\n\nLa bande ne porte aucun aplat de carte, contrairement à son équivalent de slide. Ce n'est pas une simplification : la charte ref-04 n'admet le bleu qu'en surface entière ou en chiffre, et un aplat de carte par colonne aurait introduit la valeur intermédiaire que ce système refuse. Le comptage des éléments chromatiques verrouille la règle.\n\nÉcrit nativement pour le canal : `node bin/emit.mjs card-13-email-figure-band --target email` sort sans bloquant.",
"id": "card-13-email-figure-band"
}
À côté
card-02-notched-brief
Carte chanfreinée de second rang
card-03-stat-accent
Vignette statistique sur accent
card-04-stat-barchart
Carte statistique à barres empilées
card-05-balance-gauge
Carte solde à jauge compteur
card-06-forecast-timeline