card-11-corner-arrow-tile
layout-05-nav-pill-bar
Barre de menu en pilule flottante
Poser la navigation d'une page comme un OBJET posé dans la marge — un aplat arrondi qui flotte au lieu d'un bandeau collé au bord — et n'y employer qu'une seule ressource de hiérarchie : le rang de blanc, le badge de marque et le bouton d'action étant plus CLAIRS que la barre qui les porte.
layoutweb1326×69px16 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
En-tête d'une landing ou d'une application dont le fond est déjà clair, quand la navigation ne doit pas se lire comme un chrome de navigateur : la barre reste dans la marge de page, à la même distance des bords que les modules qui la suivent, et le premier module commence juste dessous.
Éviter quand
La page défile sous un en-tête collant (une pilule flottante qui se fige au bord perd exactement ce qui la justifie), le fond est sombre ou photographique (le rang de blanc n'a plus de sens : le blanc devient la couleur la plus forte de l'écran), ou la navigation compte plus de cinq entrées — au-delà, les liens rattrapent le bouton et la respiration qui fait l'objet disparaît.
Notes
Le geste qui fait ce pattern n'est ni le rayon ni la pilule : c'est le RANG DE BLANC. La barre est un gris de module, le badge et le bouton sont d'un blanc plus clair que la PAGE elle-même. Deux benchmarks le mesurent en luminance relative parce que c'est la seule formulation qui survive à un changement de charte — dire « le bouton est blanc » serait faux dès qu'on thème le pattern en sombre, dire « le bouton est plus clair que la barre » reste vrai partout. Le rayon est tenu entre 18 % et 45 % de la hauteur, et cette borne haute est le vrai contrôle : à 50 % la barre devient une pilule pleine, ce qui est une autre forme et un autre système (les liens semblent alors flotter dans un tube). Le bouton, lui, est bien une pilule — la dissymétrie entre les deux est délibérée. L'assertion « l'écart du badge au bord gauche égale son écart au bord haut » vaut mieux qu'une mesure de padding : elle attrape aussi le cas où le badge est centré verticalement mais collé à gauche, qui passe tous les contrôles de padding et casse pourtant la silhouette. L'encre des liens est posée à #5F5F5F et non au #6E6E6E relevé sur la source : sur le gris de module #EDEDED, le relevé plafonne à 4,09:1. Écart invisible à l'œil, faute de contraste évitée.
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs layout-05-nav-pill-bar : le pattern se prouve, il ne se croit pas.
| assertion | mesure | attendu |
|---|---|---|
| barre basse : sa hauteur vaut ≈ 5 % de sa largeur | H / W | 0.052 ± 0.012 |
| un RAYON, pas une pilule : le rayon reste sous 45 % de la hauteur | num('.vl-navbar','borderTopLeftRadius') / H | 0.18 → 0.45 |
| le badge, LUI, est un disque parfait | box('.vl-navbar__badge').w / box('.vl-navbar__badge').h | 1 ± 0.02 |
| badge ≈ 70 % de la hauteur de barre — il loge dedans, il ne la déborde pas | box('.vl-navbar__badge').h / H | 0.696 ± 0.06 |
| gouttière constante : l'écart du badge au bord gauche égale son écart au bord haut | Math.abs(box('.vl-navbar__badge').left - box('.vl-navbar__badge').top) | ≤ 1.5 |
| le bouton est PLUS CLAIR que la barre — c'est le seul geste de hiérarchie du système | lum(parseRgb(cs('.vl-navbar__cta','backgroundColor'))) - lum(parseRgb(cs('.vl-navbar','backgroundColor'))) | ≥ 0.01 |
| le badge aussi est plus clair que la barre | lum(parseRgb(cs('.vl-navbar__badge','backgroundColor'))) - lum(parseRgb(cs('.vl-navbar','backgroundColor'))) | ≥ 0.01 |
| le bouton EST une pilule : son rayon vaut au moins sa demi-hauteur | num('.vl-navbar__cta','borderTopLeftRadius') / box('.vl-navbar__cta').h | ≥ 0.5 |
| le bloc de droite colle la marge intérieure de la barre | W - num('.vl-navbar','paddingRight') - box('.vl-navbar__cta').right | 0 ± 1.5 |
| le wordmark et les liens ne se touchent jamais : au moins deux hauteurs de barre les séparent | (box('.vl-navbar__links').left - box('.vl-navbar__mark').right) / H | ≥ 2 |
| saut typographique wordmark / lien ≥ 1,7 | num('.vl-navbar__mark','fontSize') / num('.vl-navbar__links','fontSize') | ≥ 1.7 |
| aucun liseré : la barre se détache par son aplat, pas par un trait | num('.vl-navbar','borderTopWidth') | 0 ± 0 |
| aucune ombre portée | cs('.vl-navbar','boxShadow') === 'none' ? 1 : 0 | 1 |
| contraste des liens ≥ 4,5 sur l'aplat de la barre | contrast('.vl-navbar__links') | ≥ 4.5 |
| contraste du libellé de bouton ≥ 4,5 sur son blanc | contrast('.vl-navbar__cta') | ≥ 4.5 |
| aucun débordement du contenu hors de la barre | overflow('.vl-navbar') | ≤ 0 |
Le code
patterns/layout-05-nav-pill-bar.html
Télécharger<!-- layout-05-nav-pill-bar — la navigation comme OBJET posé dans la marge, pas comme bandeau.
Couleurs via --vl-* uniquement. Trois zones dans un ordre FIXE : badge + wordmark à
gauche, liens poussés à droite par `margin-left:auto`, bouton en bout de ligne.
Le badge et le bouton sont d'un blanc PLUS CLAIR que la barre : c'est la seule ressource
de hiérarchie du système, et elle ne fonctionne que dans ce sens-là. -->
<header class="vl-navbar">
<span class="vl-navbar__badge" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round">
<path d="M4 10v4M8 6.5v11M12 3.5v17M16 6.5v11M20 10v4"/>
</svg>
</span>
<span class="vl-navbar__mark">northbeam</span>
<nav class="vl-navbar__links">
<span>Services
<svg class="vl-navbar__caret" viewBox="0 0 12 8" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M1 1.5 6 6.5l5-5"/></svg>
</span>
<span>Case work</span>
<span class="vl-navbar__phone">+33 1 84 80 44 55</span>
</nav>
<a class="vl-navbar__cta" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><circle cx="12" cy="8" r="3.4"/><path d="M4.8 20c.7-3.6 3.6-5.6 7.2-5.6s6.5 2 7.2 5.6"/></svg>
Client area
</a>
</header>
<style>
.vl-navbar {
width: 1326px; height: 69px;
box-sizing: border-box;
/* La marge intérieure vaut la moitié de l'écart vertical du badge : c'est ce qui fait
lire le badge comme LOGÉ dans la barre, et non posé dessus. */
padding: 0 10px;
display: flex; align-items: center;
background: var(--vl-module);
/* Un rayon franc, jamais 50 % : à demi-hauteur la barre devient un tube et les liens
semblent y flotter. La borne haute est le vrai contrôle de ce pattern. */
border-radius: var(--vl-radius-bar, 20px);
font-family: Inter, "Helvetica Neue", Arial, sans-serif;
color: var(--vl-ink);
}
.vl-navbar__badge {
flex: 0 0 auto;
width: 48px; height: 48px;
border-radius: var(--vl-radius-pill, 999px);
background: var(--vl-object);
color: var(--vl-ink);
display: flex; align-items: center; justify-content: center;
}
.vl-navbar__badge svg { width: 22px; height: 22px; }
.vl-navbar__mark {
margin-left: 16px;
font-size: 30px; font-weight: 700; letter-spacing: -0.02em;
}
/* `margin-left:auto` et rien d'autre : c'est le vide qui sépare la marque de la navigation,
pas un filet ni un changement de fond. */
.vl-navbar__links {
margin-left: auto;
display: flex; align-items: center; gap: 44px;
font-size: 16px; font-weight: 500;
color: var(--vl-ink-soft);
}
.vl-navbar__links > span { display: inline-flex; align-items: center; gap: 7px; }
.vl-navbar__caret { width: 9px; height: 6px; }
/* Le numéro n'est pas un lien : il passe à l'encre pleine. Une seule information de la
navigation a le droit à ce rang, sinon le rang ne veut plus rien dire. */
.vl-navbar__phone { color: var(--vl-ink); }
.vl-navbar__cta {
margin-left: 44px;
height: 49px; padding: 0 22px;
display: inline-flex; align-items: center; gap: 10px;
background: var(--vl-object);
color: var(--vl-ink);
border-radius: var(--vl-radius-pill, 999px);
font-size: 15px; font-weight: 500; text-decoration: none;
}
.vl-navbar__cta svg { width: 15px; height: 15px; }
</style>
systems/ref-17-mono-bento-legal.json
Télécharger:root {
--vl-page: #FEFEFE;
--vl-module: #EDEDED;
--vl-object: #FFFFFF;
--vl-invert: #000000;
--vl-ink: #111111;
--vl-ink-soft: #5F5F5F;
--vl-on-invert: #FFFFFF;
--vl-on-invert-soft: rgba(255,255,255,0.62);
--vl-line: rgba(17,17,17,0.55);
--vl-radius-page: 38px;
--vl-radius-module: 24px;
--vl-radius-bar: 20px;
--vl-radius-pill: 999px;
--vl-gutter: 26px;
--vl-margin: 57px;
--vl-bar-h: 69px;
--vl-col-1: 343fr;
--vl-col-2: 466fr;
--vl-col-3: 464fr;
--vl-row-1: 302fr;
--vl-row-2: 232fr;
--vl-row-3: 243fr;
}
les métadonnées — patterns/layout-05-nav-pill-bar.json (télécharger)
{
"name": "Barre de menu en pilule flottante",
"family": "layout",
"ref": "ref-17-mono-bento-legal",
"intent": "Poser la navigation d'une page comme un OBJET posé dans la marge — un aplat arrondi qui flotte au lieu d'un bandeau collé au bord — et n'y employer qu'une seule ressource de hiérarchie : le rang de blanc, le badge de marque et le bouton d'action étant plus CLAIRS que la barre qui les porte.",
"when_to_use": "En-tête d'une landing ou d'une application dont le fond est déjà clair, quand la navigation ne doit pas se lire comme un chrome de navigateur : la barre reste dans la marge de page, à la même distance des bords que les modules qui la suivent, et le premier module commence juste dessous.",
"avoid_when": "La page défile sous un en-tête collant (une pilule flottante qui se fige au bord perd exactement ce qui la justifie), le fond est sombre ou photographique (le rang de blanc n'a plus de sens : le blanc devient la couleur la plus forte de l'écran), ou la navigation compte plus de cinq entrées — au-delà, les liens rattrapent le bouton et la respiration qui fait l'objet disparaît.",
"media": [
"web"
],
"tags": [
"navigation",
"menu",
"barre",
"header",
"entete",
"pilule",
"landing",
"logo",
"wordmark",
"bouton",
"cta",
"monochrome",
"web",
"app"
],
"vars": [
{
"name": "--vl-module",
"role": "aplat de la barre",
"default": "#EDEDED"
},
{
"name": "--vl-object",
"role": "blanc des objets posés DANS la barre (badge, bouton) — plus clair que la page",
"default": "#FFFFFF"
},
{
"name": "--vl-ink",
"role": "wordmark, téléphone, libellé du bouton",
"default": "#111111"
},
{
"name": "--vl-ink-soft",
"role": "liens de menu (relevé #6E6E6E, posé #5F5F5F pour tenir 4,5:1)",
"default": "#5F5F5F"
},
{
"name": "--vl-radius-bar",
"role": "rayon de la barre — un rayon franc, JAMAIS une demi-hauteur",
"default": "20px"
},
{
"name": "--vl-radius-pill",
"role": "rayon du bouton et du badge, eux vraiment en pilule",
"default": "999px"
}
],
"slots": [
"badge",
"mark",
"links",
"cta"
],
"geometry": {
"root": ".vl-navbar",
"frame": [
1326,
69
],
"ratios": {
"height_over_width": 0.052,
"radius_over_height": 0.29,
"badge_over_height": 0.696,
"cta_over_height": 0.71,
"inset_over_height": 0.145,
"link_gap_over_height": 0.638
},
"type_px": {
"mark": 30,
"link": 16,
"cta": 15
},
"pad_ratio": {
"top": 0,
"x": 0.0075,
"bottom": 0
}
},
"benchmarks": [
{
"name": "barre basse : sa hauteur vaut ≈ 5 % de sa largeur",
"measure": "H / W",
"expect": 0.052,
"tol": 0.012
},
{
"name": "un RAYON, pas une pilule : le rayon reste sous 45 % de la hauteur",
"measure": "num('.vl-navbar','borderTopLeftRadius') / H",
"min": 0.18,
"max": 0.45
},
{
"name": "le badge, LUI, est un disque parfait",
"measure": "box('.vl-navbar__badge').w / box('.vl-navbar__badge').h",
"expect": 1,
"tol": 0.02
},
{
"name": "badge ≈ 70 % de la hauteur de barre — il loge dedans, il ne la déborde pas",
"measure": "box('.vl-navbar__badge').h / H",
"expect": 0.696,
"tol": 0.06
},
{
"name": "gouttière constante : l'écart du badge au bord gauche égale son écart au bord haut",
"measure": "Math.abs(box('.vl-navbar__badge').left - box('.vl-navbar__badge').top)",
"max": 1.5
},
{
"name": "le bouton est PLUS CLAIR que la barre — c'est le seul geste de hiérarchie du système",
"measure": "lum(parseRgb(cs('.vl-navbar__cta','backgroundColor'))) - lum(parseRgb(cs('.vl-navbar','backgroundColor')))",
"min": 0.01
},
{
"name": "le badge aussi est plus clair que la barre",
"measure": "lum(parseRgb(cs('.vl-navbar__badge','backgroundColor'))) - lum(parseRgb(cs('.vl-navbar','backgroundColor')))",
"min": 0.01
},
{
"name": "le bouton EST une pilule : son rayon vaut au moins sa demi-hauteur",
"measure": "num('.vl-navbar__cta','borderTopLeftRadius') / box('.vl-navbar__cta').h",
"min": 0.5
},
{
"name": "le bloc de droite colle la marge intérieure de la barre",
"measure": "W - num('.vl-navbar','paddingRight') - box('.vl-navbar__cta').right",
"expect": 0,
"tol": 1.5
},
{
"name": "le wordmark et les liens ne se touchent jamais : au moins deux hauteurs de barre les séparent",
"measure": "(box('.vl-navbar__links').left - box('.vl-navbar__mark').right) / H",
"min": 2
},
{
"name": "saut typographique wordmark / lien ≥ 1,7",
"measure": "num('.vl-navbar__mark','fontSize') / num('.vl-navbar__links','fontSize')",
"min": 1.7
},
{
"name": "aucun liseré : la barre se détache par son aplat, pas par un trait",
"measure": "num('.vl-navbar','borderTopWidth')",
"expect": 0,
"tol": 0
},
{
"name": "aucune ombre portée",
"measure": "cs('.vl-navbar','boxShadow') === 'none' ? 1 : 0",
"expect": 1
},
{
"name": "contraste des liens ≥ 4,5 sur l'aplat de la barre",
"measure": "contrast('.vl-navbar__links')",
"min": 4.5
},
{
"name": "contraste du libellé de bouton ≥ 4,5 sur son blanc",
"measure": "contrast('.vl-navbar__cta')",
"min": 4.5
},
{
"name": "aucun débordement du contenu hors de la barre",
"measure": "overflow('.vl-navbar')",
"max": 0
}
],
"notes": "Le geste qui fait ce pattern n'est ni le rayon ni la pilule : c'est le RANG DE BLANC. La barre est un gris de module, le badge et le bouton sont d'un blanc plus clair que la PAGE elle-même. Deux benchmarks le mesurent en luminance relative parce que c'est la seule formulation qui survive à un changement de charte — dire « le bouton est blanc » serait faux dès qu'on thème le pattern en sombre, dire « le bouton est plus clair que la barre » reste vrai partout.\n\nLe rayon est tenu entre 18 % et 45 % de la hauteur, et cette borne haute est le vrai contrôle : à 50 % la barre devient une pilule pleine, ce qui est une autre forme et un autre système (les liens semblent alors flotter dans un tube). Le bouton, lui, est bien une pilule — la dissymétrie entre les deux est délibérée.\n\nL'assertion « l'écart du badge au bord gauche égale son écart au bord haut » vaut mieux qu'une mesure de padding : elle attrape aussi le cas où le badge est centré verticalement mais collé à gauche, qui passe tous les contrôles de padding et casse pourtant la silhouette.\n\nL'encre des liens est posée à #5F5F5F et non au #6E6E6E relevé sur la source : sur le gris de module #EDEDED, le relevé plafonne à 4,09:1. Écart invisible à l'œil, faute de contraste évitée.",
"id": "layout-05-nav-pill-bar"
}
À 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-06-bento-l-span