card-08-orb-chain-total
chart-03-accent-column-callout
Histogramme à colonne accentuée et info-bulle
Donner la forme d'une série en quelques colonnes flottantes sans axe ni graduation, et n'en chiffrer qu'une seule — la courante — par une info-bulle sombre ancrée à sa colonne.
chartslideweb700×238px11 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
Une série courte (4 à 7 périodes) sert de contexte à UNE valeur qui, elle, doit être lue exactement : mois en cours, dernier trimestre, pic à commenter.
Éviter quand
Plusieurs valeurs doivent être lues précisément (il faudrait autant d'info-bulles, et l'accent perd tout sens), ou les écarts entre colonnes sont faibles : sans axe, un écart de 5 % ne se voit pas.
Notes
Deux bornes protègent l'accent, et elles tirent en sens inverse : la colonne accentuée doit être la plus haute (sinon l'œil accentue la mauvaise), et son info-bulle — ancrée à la COLONNE, donc libre de sortir de la carte — doit rester dans le cadre et sous l'en-tête. C'est ce couple qui rend le pattern rejouable sur d'autres valeurs : dès que la série change, l'un des deux casse avant le rendu. Le rayon est mesuré en BAS : c'est le seul endroit où l'on voit si les colonnes flottent ou si elles sont plantées sur une ligne de base absente. L'info-bulle monte au niveau de l'en-tête : c'est voulu, la colonne accentuée est haute et la bulle la surmonte. Ce qui est mesuré n'est donc pas un dégagement vertical (il n'y en a pas) mais le dégagement HORIZONTAL avec ses deux voisins de ligne, titre à gauche et pilule de période à droite.
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs chart-03-accent-column-callout : le pattern se prouve, il ne se croit pas.
| assertion | mesure | attendu |
|---|---|---|
| une seule colonne accentuée | document.querySelectorAll('.vl-callout__col--on').length | 1 ± 0 |
| une seule info-bulle, et elle est portée par la colonne accentuée | document.querySelectorAll('.vl-callout__tip').length + (document.querySelector('.vl-callout__col--on .vl-callout__tip') ? 0 : 10) | 1 ± 0 |
| la colonne accentuée est la plus haute de la série | box('.vl-callout__col--on .vl-callout__bar').h - Math.max(...[...document.querySelectorAll('.vl-callout__col:not(.vl-callout__col--on) .vl-callout__bar')].map((e) => e.getBoundingClientRect().height)) | ≥ 20 |
| le pas vaut environ le double de la largeur de colonne (le vide fait lire l'accent) | (box('.vl-callout__col:nth-child(2)').left - box('.vl-callout__col:nth-child(1)').left) / box('.vl-callout__col').w | 2.22 ± 0.3 |
| les colonnes flottent : le rayon vaut ≈ 0,43 × la largeur, en haut ET en bas | num('.vl-callout__bar','borderBottomLeftRadius') / box('.vl-callout__bar').w | 0.4219 ± 0.06 |
| la série remplit sa carte : la plus haute colonne dépasse 55 % de la hauteur | box('.vl-callout__col--on .vl-callout__bar').h / H | ≥ 0.55 |
| l'info-bulle reste dans la carte (ancrée à la colonne, elle peut en sortir) | box('.vl-callout__box').right - W | ≤ 0 |
| l'info-bulle partage la ligne d'en-tête sans percuter le titre ni la pilule de période | Math.min(box('.vl-callout__box').left - box('.vl-callout__title').right, box('.vl-callout__pill').left - box('.vl-callout__box').right) | ≥ 8 |
| contraste du texte de l'info-bulle sur son aplat ≥ 7 | contrast('.vl-callout__box') | ≥ 7 |
| contraste des légendes de période ≥ 4,5 malgré leur petit corps | contrast('.vl-callout__cap') | ≥ 4.5 |
| aucun débordement du contenu hors de la carte | overflow('.vl-callout') | ≤ 0 |
Le code
patterns/chart-03-accent-column-callout.html
Télécharger<!-- chart-03-accent-column-callout — fragment autonome. Couleurs via --vl-* uniquement, aucune valeur en dur. -->
<div class="vl-callout">
<div class="vl-callout__head">
<h2 class="vl-callout__title">Expense statistic</h2>
<span class="vl-callout__pill">Monthly</span>
</div>
<div class="vl-callout__plot">
<span class="vl-callout__col">
<span class="vl-callout__bar" style="height:77px"></span>
<span class="vl-callout__cap">MAY</span>
</span>
<span class="vl-callout__col">
<span class="vl-callout__bar" style="height:63px"></span>
<span class="vl-callout__cap">JUN</span>
</span>
<span class="vl-callout__col vl-callout__col--on">
<span class="vl-callout__bar" style="height:140px">
<span class="vl-callout__tip">
<span class="vl-callout__dot"></span>
<span class="vl-callout__box">$45k</span>
</span>
</span>
<span class="vl-callout__cap">JUL</span>
</span>
<span class="vl-callout__col">
<span class="vl-callout__bar" style="height:87px"></span>
<span class="vl-callout__cap">AUG</span>
</span>
<span class="vl-callout__col">
<span class="vl-callout__bar" style="height:73px"></span>
<span class="vl-callout__cap">SEP</span>
</span>
</div>
</div>
<style>
/* Un histogramme où UNE seule colonne est accentuée — la courante — et où l'info-bulle ne
commente qu'elle. Ni axe, ni graduation, ni valeur sur les autres colonnes : la série donne
une forme, l'accent donne le chiffre. Les colonnes sont arrondies EN HAUT ET EN BAS (elles
flottent, elles ne sont pas plantées sur une ligne de base), et le pas est à peu près le
double de la largeur de colonne — c'est ce vide qui rend l'accent lisible sans le grossir.
L'info-bulle est ancrée à la colonne, pas à la carte : elle suit sa valeur quand elle change. */
.vl-callout {
position: relative; width: 700px; height: 238px; padding: 24px 28px;
display: flex; flex-direction: column;
border-radius: var(--vl-radius-card);
background-color: var(--vl-surface-glass);
backdrop-filter: blur(var(--vl-blur));
box-shadow: inset 0 1px 0 var(--vl-sheen);
font-family: Inter, -apple-system, "Helvetica Neue", Arial, sans-serif;
color: var(--vl-ink);
}
.vl-callout__head { display: flex; align-items: center; justify-content: space-between; }
.vl-callout__title { margin: 0; font-size: 15px; font-weight: 500; letter-spacing: -0.01em; }
.vl-callout__pill {
display: inline-flex; align-items: center; padding: 7px 15px; border-radius: 999px;
font-size: 11px; font-weight: 500;
background-color: var(--vl-surface-glass-strong);
box-shadow: inset 0 1px 0 var(--vl-sheen);
}
.vl-callout__plot { flex: 1; display: flex; align-items: flex-end; justify-content: space-between; padding: 8px 6px 0; }
.vl-callout__col { width: 64px; display: inline-flex; flex-direction: column; align-items: center; }
.vl-callout__bar {
position: relative; width: 64px; border-radius: 27px;
background-color: var(--vl-surface-glass-strong);
box-shadow: inset 0 1px 0 var(--vl-sheen);
}
.vl-callout__col--on .vl-callout__bar {
background-color: var(--vl-blue);
background-image: linear-gradient(180deg, var(--vl-blue-light) 0%, var(--vl-blue) 100%);
box-shadow: none;
}
.vl-callout__cap { margin-top: 10px; font-size: 10px; font-weight: 500; letter-spacing: 0.04em; color: var(--vl-ink-muted); }
.vl-callout__col--on .vl-callout__cap { color: var(--vl-ink); font-weight: 600; }
.vl-callout__tip { position: absolute; left: 10px; top: -34px; display: flex; align-items: center; gap: 8px; }
.vl-callout__dot { width: 9px; height: 9px; border-radius: 50%; background-color: var(--vl-surface-dark); }
.vl-callout__box {
padding: 6px 12px; border-radius: 999px; white-space: nowrap;
background-color: var(--vl-surface-dark); color: var(--vl-ink-invert);
font-size: 11px; font-weight: 500;
}
</style>
systems/ref-13-glass-fintech-dashboard.json
Télécharger:root {
--vl-bg: #B4BEDD;
--vl-grad-1: #A3B0DA;
--vl-grad-2: #98A9D6;
--vl-grad-3: #C8CEDA;
--vl-surface-glass: rgba(255, 255, 255, 0.45);
--vl-surface-glass-strong: rgba(255, 255, 255, 0.68);
--vl-surface-glass-soft: rgba(255, 255, 255, 0.24);
--vl-surface-dark: #101625;
--vl-sheen: rgba(255, 255, 255, 0.45);
--vl-blue: #4F7CF7;
--vl-blue-light: #8FB6FA;
--vl-violet: #8B5CF6;
--vl-violet-deep: #5B7CF0;
--vl-violet-halo: rgba(123, 106, 240, 0.42);
--vl-ink: #1A1F2E;
--vl-ink-muted: #565C72;
--vl-ink-invert: #FFFFFF;
--vl-ink-invert-muted: rgba(255, 255, 255, 0.72);
--vl-radius-card: 28px;
--vl-radius-sub: 18px;
--vl-gap: 22px;
--vl-margin: 44px;
--vl-blur: 18px;
}
les métadonnées — patterns/chart-03-accent-column-callout.json (télécharger)
{
"name": "Histogramme à colonne accentuée et info-bulle",
"family": "chart",
"ref": "ref-13-glass-fintech-dashboard",
"intent": "Donner la forme d'une série en quelques colonnes flottantes sans axe ni graduation, et n'en chiffrer qu'une seule — la courante — par une info-bulle sombre ancrée à sa colonne.",
"when_to_use": "Une série courte (4 à 7 périodes) sert de contexte à UNE valeur qui, elle, doit être lue exactement : mois en cours, dernier trimestre, pic à commenter.",
"avoid_when": "Plusieurs valeurs doivent être lues précisément (il faudrait autant d'info-bulles, et l'accent perd tout sens), ou les écarts entre colonnes sont faibles : sans axe, un écart de 5 % ne se voit pas.",
"media": [
"slide",
"web"
],
"tags": [
"histogramme",
"barres",
"colonnes",
"accent",
"info-bulle",
"tooltip",
"serie",
"periode",
"dashboard",
"sans-axe",
"verre"
],
"vars": [
{
"name": "--vl-surface-glass",
"role": "verre de la carte",
"default": "rgba(255, 255, 255, 0.45)"
},
{
"name": "--vl-surface-glass-strong",
"role": "colonnes neutres et pilule de période",
"default": "rgba(255, 255, 255, 0.68)"
},
{
"name": "--vl-blue",
"role": "pied du dégradé de la colonne accentuée",
"default": "#4F7CF7"
},
{
"name": "--vl-blue-light",
"role": "sommet du dégradé de la colonne accentuée",
"default": "#8FB6FA"
},
{
"name": "--vl-surface-dark",
"role": "aplat de l'info-bulle et de son point de repère",
"default": "#101625"
}
],
"slots": [
"title",
"period",
"columns",
"callout"
],
"geometry": {
"root": ".vl-callout",
"frame": [
700,
238
],
"ratios": {
"col_over_width": 0.0914,
"pitch_over_col": 2.219,
"bar_radius_over_col": 0.4219,
"tall_over_height": 0.5882
},
"type_px": {
"title": 15,
"pill": 11,
"cap": 10,
"tip": 11
},
"pad_ratio": {
"top": 0.0343,
"x": 0.04,
"bottom": 0.0343
}
},
"benchmarks": [
{
"name": "une seule colonne accentuée",
"measure": "document.querySelectorAll('.vl-callout__col--on').length",
"expect": 1,
"tol": 0
},
{
"name": "une seule info-bulle, et elle est portée par la colonne accentuée",
"measure": "document.querySelectorAll('.vl-callout__tip').length + (document.querySelector('.vl-callout__col--on .vl-callout__tip') ? 0 : 10)",
"expect": 1,
"tol": 0
},
{
"name": "la colonne accentuée est la plus haute de la série",
"measure": "box('.vl-callout__col--on .vl-callout__bar').h - Math.max(...[...document.querySelectorAll('.vl-callout__col:not(.vl-callout__col--on) .vl-callout__bar')].map((e) => e.getBoundingClientRect().height))",
"min": 20
},
{
"name": "le pas vaut environ le double de la largeur de colonne (le vide fait lire l'accent)",
"measure": "(box('.vl-callout__col:nth-child(2)').left - box('.vl-callout__col:nth-child(1)').left) / box('.vl-callout__col').w",
"expect": 2.22,
"tol": 0.3
},
{
"name": "les colonnes flottent : le rayon vaut ≈ 0,43 × la largeur, en haut ET en bas",
"measure": "num('.vl-callout__bar','borderBottomLeftRadius') / box('.vl-callout__bar').w",
"expect": 0.4219,
"tol": 0.06
},
{
"name": "la série remplit sa carte : la plus haute colonne dépasse 55 % de la hauteur",
"measure": "box('.vl-callout__col--on .vl-callout__bar').h / H",
"min": 0.55
},
{
"name": "l'info-bulle reste dans la carte (ancrée à la colonne, elle peut en sortir)",
"measure": "box('.vl-callout__box').right - W",
"max": 0
},
{
"name": "l'info-bulle partage la ligne d'en-tête sans percuter le titre ni la pilule de période",
"measure": "Math.min(box('.vl-callout__box').left - box('.vl-callout__title').right, box('.vl-callout__pill').left - box('.vl-callout__box').right)",
"min": 8
},
{
"name": "contraste du texte de l'info-bulle sur son aplat ≥ 7",
"measure": "contrast('.vl-callout__box')",
"min": 7
},
{
"name": "contraste des légendes de période ≥ 4,5 malgré leur petit corps",
"measure": "contrast('.vl-callout__cap')",
"min": 4.5
},
{
"name": "aucun débordement du contenu hors de la carte",
"measure": "overflow('.vl-callout')",
"max": 0
}
],
"notes": "Deux bornes protègent l'accent, et elles tirent en sens inverse : la colonne accentuée doit être la plus haute (sinon l'œil accentue la mauvaise), et son info-bulle — ancrée à la COLONNE, donc libre de sortir de la carte — doit rester dans le cadre et sous l'en-tête. C'est ce couple qui rend le pattern rejouable sur d'autres valeurs : dès que la série change, l'un des deux casse avant le rendu. Le rayon est mesuré en BAS : c'est le seul endroit où l'on voit si les colonnes flottent ou si elles sont plantées sur une ligne de base absente. L'info-bulle monte au niveau de l'en-tête : c'est voulu, la colonne accentuée est haute et la bulle la surmonte. Ce qui est mesuré n'est donc pas un dégagement vertical (il n'y en a pas) mais le dégagement HORIZONTAL avec ses deux voisins de ligne, titre à gauche et pilule de période à droite.",
"id": "chart-03-accent-column-callout"
}
À côté
card-09-gradient-metric-curve
KPI sur aplat coloré à courbe annotée
chart-01-stadium-bars
Barres à sommet arrondi
chart-02-isotype
Isotype à taux de remplissage
chart-04-unit-textured-bar
Barre à unité déclarée
chart-05-tile-heatmap