diagram-01-layer-stack
diagram-03-time-scrubber
Ruban temporel à fenêtre de sélection
Donner à un écran sa position de lecture dans un axe plus long que lui : l'année entière tient dans une pilule claire — mois écrits, événements en pastilles rondes, comptage en exposant acide —, et une FENÊTRE SOMBRE À POIGNÉES posée dessus marque l'intervalle affiché au-dessus. La fenêtre reprend les mêmes pastilles en inverse : c'est ce qui dit qu'on regarde le même axe, et pas un second objet.
diagramweb1330×74px18 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
Sous une vue détaillée qui ne montre qu'une tranche d'un historique long : dossier, journal, calendrier de production. L'axe entier reste lisible, la tranche courante est désignée, et le lecteur sait toujours où il est sans changer d'écran.
Éviter quand
L'axe tient entièrement dans la vue (le ruban ne désigne alors plus rien), les événements sont trop nombreux pour être des pastilles distinctes (il faut une densité, pas des objets), ou la sélection n'est pas déplaçable — des poignées qui ne se saisissent pas sont un mensonge d'interface.
Notes
C'est le seul objet du corpus où un module donne la POSITION DE LECTURE d'un autre. Ce qui le rend lisible sans légende n'est pas la fenêtre sombre — c'est que la fenêtre reprend exactement les mêmes pastilles, à la même taille, seulement inversées. Deux benchmarks tiennent ce couple : taille identique (tolérance 2 %) et luminance franchement différente (écart ≥ 0,3). Changer l'une des deux casse la lecture — des pastilles plus petites dans la fenêtre feraient croire à un second objet, des pastilles de même valeur la feraient disparaître. LE COMPTAGE EST UN EXPOSANT, ET CE N'EST PAS UNE FAÇON DE PARLER : la pastille acide déborde de son événement en HAUT et à DROITE, ce que le benchmark vérifie sur les deux axes. Centrée, elle deviendrait un badge d'état ; posée dessous, une légende. Elle porte l'accent parce qu'elle compte — l'alerte, dans ce système, est un rouge saumon et n'apparaît jamais ici. L'assertion « le repère aujourd'hui est le seul objet sombre hors de la fenêtre » protège le contraste de rôle : deux aplats sombres sur l'axe, et on ne sait plus lequel désigne la position courante.
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs diagram-03-time-scrubber : le pattern se prouve, il ne se croit pas.
| assertion | mesure | attendu |
|---|---|---|
| le ruban est une pilule pleine | num('.vl-scrub','borderTopLeftRadius') / H | ≥ 0.5 |
| ruban bas : sa hauteur vaut au plus 8 % de sa largeur | H / W | ≤ 0.08 |
| LA FENÊTRE EST SOMBRE là où le ruban est clair | lum(parseRgb(cs('.vl-scrub','backgroundColor'))) / lum(parseRgb(cs('.vl-scrub__window','backgroundColor'))) | ≥ 6 |
| … et c'est une pilule, comme le ruban qui la porte | num('.vl-scrub__window','borderTopLeftRadius') / box('.vl-scrub__window').h | ≥ 0.5 |
| la fenêtre est une TRANCHE : entre 10 % et 45 % de l'axe | box('.vl-scrub__window').w / W | 0.1 → 0.45 |
| la fenêtre tient DANS le ruban, jamais à côté | Math.min(box('.vl-scrub__window').left, W - box('.vl-scrub__window').right) | ≥ 0 |
| DEUX poignées, une à chaque extrémité de la fenêtre | document.querySelectorAll('.vl-scrub__grip').length | 2 ± 0 |
| les poignées sont AUX BORDS de la fenêtre, pas au milieu | (() => { const w = box('.vl-scrub__window'); const g = [...document.querySelectorAll('.vl-scrub__grip')].map((e) => e.getBoundingClientRect()); const r = $(window.__VL_ROOT__).getBoundingClientRect(); const xs = g.map((b) => b.left + b.width / 2 - r.left); return Math.max(Math.abs(Math.min(...xs) - w.left), Math.abs(Math.max(...xs) - w.right)); })() | ≤ 24 |
| les pastilles de la fenêtre ont la MÊME taille que celles du ruban — c'est le même axe | box('.vl-scrub__window .vl-scrub__ev').h / box('.vl-scrub__scale .vl-scrub__ev').h | 1 ± 0.02 |
| … mais elles sont INVERSÉES : plus sombres que celles du ruban | lum(bgOf($('.vl-scrub__scale .vl-scrub__ev'))) - lum(bgOf($('.vl-scrub__window .vl-scrub__ev'))) | ≥ 0.3 |
| une pastille d'événement est un disque | box('.vl-scrub__ev').w / box('.vl-scrub__ev').h | 1 ± 0.02 |
| LE COMPTAGE EST EN EXPOSANT : la pastille de comptage déborde en haut ET à droite de son événement | (box('.vl-scrub__ev').top - box('.vl-scrub__n').top > 0 && box('.vl-scrub__n').right - box('.vl-scrub__ev').right > 0) ? 1 : 0 | 1 |
| le comptage porte l'accent : il compte, il n'alerte pas | (() => { const c = parseRgb(cs('.vl-scrub__n','backgroundColor')); return Math.max(...c) - Math.min(...c); })() | ≥ 60 |
| … et il reste minuscule : au plus la moitié de sa pastille | box('.vl-scrub__n').h / box('.vl-scrub__ev').h | ≤ 0.55 |
| le repère « aujourd'hui » est le seul objet sombre HORS de la fenêtre | [...document.querySelectorAll('.vl-scrub__scale *, .vl-scrub__now')].filter((e) => { const p = (getComputedStyle(e).backgroundColor.match(/[\d.]+/g) || []).map(Number); const a = p.length === 4 ? p[3] : (p.length === 3 ? 1 : 0); return a > 0.9 && lum(p.slice(0, 3)) < 0.2; }).length | 1 ± 0 |
| contraste d'un libellé de mois hors fenêtre ≥ 4,5 | contrast('.vl-scrub__scale .vl-scrub__month') | ≥ 4.5 |
| contraste d'un libellé de mois DANS la fenêtre ≥ 4,5 | contrast('.vl-scrub__window .vl-scrub__month') | ≥ 4.5 |
| aucun débordement du ruban | overflow('.vl-scrub') | ≤ 0 |
Le code
patterns/diagram-03-time-scrubber.html
Télécharger<!-- diagram-03-time-scrubber — l'axe entier dans une pilule, la tranche affichée en surbrillance.
Couleurs via --vl-* uniquement.
CE QUI REND L'OBJET LISIBLE SANS LÉGENDE n'est pas la fenêtre sombre : c'est qu'elle
reprend EXACTEMENT les mêmes pastilles, à la même taille, seulement inversées. Plus
petites, elles feraient croire à un second objet ; de même valeur, la fenêtre
disparaîtrait. Les deux benchmarks tiennent ce couple ensemble.
LE COMPTAGE EST UN EXPOSANT : la pastille acide déborde en HAUT et à DROITE de son
événement. Centrée, elle deviendrait un badge d'état ; posée dessous, une légende. -->
<div class="vl-scrub">
<span class="vl-scrub__now" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"><rect x="4" y="5" width="16" height="15" rx="2.6"/><path d="M4 10h16M8.5 2.8v4M15.5 2.8v4"/></svg>
</span>
<div class="vl-scrub__scale">
<span class="vl-scrub__month">Jan<b>2026</b></span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><rect x="6" y="3.5" width="12" height="17" rx="2.2"/><path d="M9.5 8h5M9.5 12h5"/></svg></span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3.4" y="8.6" width="17" height="7" rx="3.5" transform="rotate(-45 12 12)"/></svg><span class="vl-scrub__n">6</span></span>
<span class="vl-scrub__dots">···</span>
<span class="vl-scrub__month">Feb</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M10 3.5v6L5.5 18a2 2 0 0 0 1.8 3h9.4a2 2 0 0 0 1.8-3L14 9.5v-6"/></svg></span>
<span class="vl-scrub__month">Mar</span>
<span class="vl-scrub__dots">···</span>
<span class="vl-scrub__month">Apr</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><rect x="6" y="3.5" width="12" height="17" rx="2.2"/><path d="M9.5 8h5M9.5 12h5"/></svg></span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3.4" y="8.6" width="17" height="7" rx="3.5" transform="rotate(-45 12 12)"/></svg><span class="vl-scrub__n">2</span></span>
<span class="vl-scrub__month">May</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><rect x="6" y="3.5" width="12" height="17" rx="2.2"/><path d="M9.5 8h5M9.5 12h5"/></svg></span>
<span class="vl-scrub__dots">···</span>
<span class="vl-scrub__month">Jun</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M10 3.5v6L5.5 18a2 2 0 0 0 1.8 3h9.4a2 2 0 0 0 1.8-3L14 9.5v-6"/></svg></span>
<span class="vl-scrub__month">Jul</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3.4" y="8.6" width="17" height="7" rx="3.5" transform="rotate(-45 12 12)"/></svg><span class="vl-scrub__n">6</span></span>
</div>
<div class="vl-scrub__window">
<span class="vl-scrub__grip"></span>
<span class="vl-scrub__month">Aug</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><rect x="6" y="3.5" width="12" height="17" rx="2.2"/><path d="M9.5 8h5M9.5 12h5"/></svg></span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3.4" y="8.6" width="17" height="7" rx="3.5" transform="rotate(-45 12 12)"/></svg></span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M10 3.5v6L5.5 18a2 2 0 0 0 1.8 3h9.4a2 2 0 0 0 1.8-3L14 9.5v-6"/></svg><span class="vl-scrub__n">4</span></span>
<span class="vl-scrub__month">Sep</span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><rect x="6" y="3.5" width="12" height="17" rx="2.2"/><path d="M9.5 8h5M9.5 12h5"/></svg></span>
<span class="vl-scrub__ev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3.4" y="8.6" width="17" height="7" rx="3.5" transform="rotate(-45 12 12)"/></svg><span class="vl-scrub__n">2</span></span>
<span class="vl-scrub__grip"></span>
</div>
</div>
<style>
.vl-scrub {
width: 1330px; height: 74px;
box-sizing: border-box;
position: relative;
padding: 0 8px;
background: var(--vl-module-hi);
color: var(--vl-ink);
border-radius: var(--vl-radius-pill, 999px);
display: flex; align-items: center; gap: 10px;
font-family: Montserrat, Poppins, Inter, "Helvetica Neue", Arial, sans-serif;
}
/* Le SEUL objet sombre hors de la fenêtre. Deux aplats sombres sur l'axe, et on ne sait plus
lequel désigne la position courante. */
.vl-scrub__now {
flex: 0 0 auto;
width: 58px; height: 58px; border-radius: 50%;
background: var(--vl-veil-deep); color: var(--vl-on-veil);
display: flex; align-items: center; justify-content: center;
}
.vl-scrub__now svg { width: 24px; height: 24px; }
.vl-scrub__scale {
flex: 1 1 auto; min-width: 0;
display: flex; align-items: center; gap: 8px;
padding-right: 410px; /* la place que la fenêtre occupe par-dessus */
}
.vl-scrub__month { font-size: 14px; color: var(--vl-ink); padding: 0 4px; white-space: nowrap; }
.vl-scrub__month b { display: block; font-size: 12px; font-weight: 500; color: var(--vl-ink-soft); }
.vl-scrub__ev {
position: relative; flex: 0 0 auto;
width: 38px; height: 38px; border-radius: 50%;
background: var(--vl-module);
display: flex; align-items: center; justify-content: center;
}
.vl-scrub__ev svg { width: 17px; height: 17px; }
/* EXPOSANT : la pastille de comptage déborde en haut ET à droite. Centrée elle deviendrait un
badge d'état, posée dessous une légende — dans les deux cas elle cesserait de compter. */
.vl-scrub__n {
position: absolute; top: -6px; right: -6px;
min-width: 18px; height: 18px; padding: 0 5px;
border-radius: 50%;
background: var(--vl-accent); color: var(--vl-ink);
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 600;
}
.vl-scrub__dots { color: var(--vl-ink-soft); opacity: 0.55; font-size: 15px; letter-spacing: 2px; }
/* LA FENÊTRE. Posée PAR-DESSUS l'axe (position absolue), elle n'est pas un morceau de la
liste : elle la recouvre. Ses pastilles ont la même taille que celles du ruban, seulement
inversées — c'est ce qui dit qu'on regarde le même axe. */
.vl-scrub__window {
position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
height: 58px; padding: 0 14px;
border-radius: var(--vl-radius-pill, 999px);
background: var(--vl-veil-deep); color: var(--vl-on-veil);
display: flex; align-items: center; gap: 8px;
}
.vl-scrub__window .vl-scrub__month { color: var(--vl-on-veil); }
.vl-scrub__window .vl-scrub__ev { background: rgba(255,255,255,0.14); color: var(--vl-on-veil); }
.vl-scrub__grip { width: 3px; height: 26px; border-radius: 2px; background: var(--vl-on-veil); opacity: 0.8; }
</style>
systems/ref-19-acid-clinical-timeline.json
Télécharger:root {
--vl-veil: #4A4A48;
--vl-veil-deep: #1B1D1D;
--vl-sheet: #DFDFD7;
--vl-module: #F0EFEB;
--vl-module-hi: #FFFFFF;
--vl-accent: #F5FE49;
--vl-alert: #E8756B;
--vl-recover: #7FA88C;
--vl-ink: #262C2C;
--vl-ink-soft: #5F615E;
--vl-on-veil: #FFFFFF;
--vl-on-veil-soft: rgba(255,255,255,0.72);
--vl-radius-sheet: 34px;
--vl-radius-module: 22px;
--vl-radius-pill: 999px;
--vl-notch: 26px;
--vl-bleed: 20px;
--vl-tabbar-h: 106px;
--vl-ribbon-h: 74px;
--vl-gutter: 18px;
--vl-margin: 34px;
}
les métadonnées — patterns/diagram-03-time-scrubber.json (télécharger)
{
"name": "Ruban temporel à fenêtre de sélection",
"family": "diagram",
"ref": "ref-19-acid-clinical-timeline",
"intent": "Donner à un écran sa position de lecture dans un axe plus long que lui : l'année entière tient dans une pilule claire — mois écrits, événements en pastilles rondes, comptage en exposant acide —, et une FENÊTRE SOMBRE À POIGNÉES posée dessus marque l'intervalle affiché au-dessus. La fenêtre reprend les mêmes pastilles en inverse : c'est ce qui dit qu'on regarde le même axe, et pas un second objet.",
"when_to_use": "Sous une vue détaillée qui ne montre qu'une tranche d'un historique long : dossier, journal, calendrier de production. L'axe entier reste lisible, la tranche courante est désignée, et le lecteur sait toujours où il est sans changer d'écran.",
"avoid_when": "L'axe tient entièrement dans la vue (le ruban ne désigne alors plus rien), les événements sont trop nombreux pour être des pastilles distinctes (il faut une densité, pas des objets), ou la sélection n'est pas déplaçable — des poignées qui ne se saisissent pas sont un mensonge d'interface.",
"media": [
"web"
],
"tags": [
"timeline",
"ruban",
"scrubber",
"brush",
"navigateur",
"selection",
"fenetre",
"axe",
"historique",
"mois",
"evenement",
"poignee",
"minimap"
],
"vars": [
{
"name": "--vl-module-hi",
"role": "aplat du ruban",
"default": "#FFFFFF"
},
{
"name": "--vl-module",
"role": "aplat d'une pastille d'événement, hors fenêtre",
"default": "#F0EFEB"
},
{
"name": "--vl-veil-deep",
"role": "aplat de la fenêtre de sélection ET du repère « aujourd'hui »",
"default": "#1B1D1D"
},
{
"name": "--vl-accent",
"role": "pastille de COMPTAGE, en exposant — jamais une alerte",
"default": "#F5FE49"
},
{
"name": "--vl-ink",
"role": "libellés de mois hors fenêtre",
"default": "#262C2C"
},
{
"name": "--vl-ink-soft",
"role": "millésime, séparateurs",
"default": "#5F615E"
},
{
"name": "--vl-on-veil",
"role": "libellés dans la fenêtre",
"default": "#FFFFFF"
}
],
"slots": [
"now",
"scale",
"window"
],
"geometry": {
"root": ".vl-scrub",
"frame": [
1330,
74
],
"ratios": {
"now_over_height": 0.784,
"event_over_height": 0.514,
"badge_over_event": 0.474,
"window_over_width": 0.301,
"grip_over_height": 0.351
},
"type_px": {
"month": 14,
"badge": 11
},
"pad_ratio": {
"top": 0,
"x": 0.006,
"bottom": 0
}
},
"benchmarks": [
{
"name": "le ruban est une pilule pleine",
"measure": "num('.vl-scrub','borderTopLeftRadius') / H",
"min": 0.5
},
{
"name": "ruban bas : sa hauteur vaut au plus 8 % de sa largeur",
"measure": "H / W",
"max": 0.08
},
{
"name": "LA FENÊTRE EST SOMBRE là où le ruban est clair",
"measure": "lum(parseRgb(cs('.vl-scrub','backgroundColor'))) / lum(parseRgb(cs('.vl-scrub__window','backgroundColor')))",
"min": 6
},
{
"name": "… et c'est une pilule, comme le ruban qui la porte",
"measure": "num('.vl-scrub__window','borderTopLeftRadius') / box('.vl-scrub__window').h",
"min": 0.5
},
{
"name": "la fenêtre est une TRANCHE : entre 10 % et 45 % de l'axe",
"measure": "box('.vl-scrub__window').w / W",
"min": 0.1,
"max": 0.45
},
{
"name": "la fenêtre tient DANS le ruban, jamais à côté",
"measure": "Math.min(box('.vl-scrub__window').left, W - box('.vl-scrub__window').right)",
"min": 0
},
{
"name": "DEUX poignées, une à chaque extrémité de la fenêtre",
"measure": "document.querySelectorAll('.vl-scrub__grip').length",
"expect": 2,
"tol": 0
},
{
"name": "les poignées sont AUX BORDS de la fenêtre, pas au milieu",
"measure": "(() => { const w = box('.vl-scrub__window'); const g = [...document.querySelectorAll('.vl-scrub__grip')].map((e) => e.getBoundingClientRect()); const r = $(window.__VL_ROOT__).getBoundingClientRect(); const xs = g.map((b) => b.left + b.width / 2 - r.left); return Math.max(Math.abs(Math.min(...xs) - w.left), Math.abs(Math.max(...xs) - w.right)); })()",
"max": 24
},
{
"name": "les pastilles de la fenêtre ont la MÊME taille que celles du ruban — c'est le même axe",
"measure": "box('.vl-scrub__window .vl-scrub__ev').h / box('.vl-scrub__scale .vl-scrub__ev').h",
"expect": 1,
"tol": 0.02
},
{
"name": "… mais elles sont INVERSÉES : plus sombres que celles du ruban",
"measure": "lum(bgOf($('.vl-scrub__scale .vl-scrub__ev'))) - lum(bgOf($('.vl-scrub__window .vl-scrub__ev')))",
"min": 0.3
},
{
"name": "une pastille d'événement est un disque",
"measure": "box('.vl-scrub__ev').w / box('.vl-scrub__ev').h",
"expect": 1,
"tol": 0.02
},
{
"name": "LE COMPTAGE EST EN EXPOSANT : la pastille de comptage déborde en haut ET à droite de son événement",
"measure": "(box('.vl-scrub__ev').top - box('.vl-scrub__n').top > 0 && box('.vl-scrub__n').right - box('.vl-scrub__ev').right > 0) ? 1 : 0",
"expect": 1
},
{
"name": "le comptage porte l'accent : il compte, il n'alerte pas",
"measure": "(() => { const c = parseRgb(cs('.vl-scrub__n','backgroundColor')); return Math.max(...c) - Math.min(...c); })()",
"min": 60
},
{
"name": "… et il reste minuscule : au plus la moitié de sa pastille",
"measure": "box('.vl-scrub__n').h / box('.vl-scrub__ev').h",
"max": 0.55
},
{
"name": "le repère « aujourd'hui » est le seul objet sombre HORS de la fenêtre",
"measure": "[...document.querySelectorAll('.vl-scrub__scale *, .vl-scrub__now')].filter((e) => { const p = (getComputedStyle(e).backgroundColor.match(/[\\d.]+/g) || []).map(Number); const a = p.length === 4 ? p[3] : (p.length === 3 ? 1 : 0); return a > 0.9 && lum(p.slice(0, 3)) < 0.2; }).length",
"expect": 1,
"tol": 0
},
{
"name": "contraste d'un libellé de mois hors fenêtre ≥ 4,5",
"measure": "contrast('.vl-scrub__scale .vl-scrub__month')",
"min": 4.5
},
{
"name": "contraste d'un libellé de mois DANS la fenêtre ≥ 4,5",
"measure": "contrast('.vl-scrub__window .vl-scrub__month')",
"min": 4.5
},
{
"name": "aucun débordement du ruban",
"measure": "overflow('.vl-scrub')",
"max": 0
}
],
"notes": "C'est le seul objet du corpus où un module donne la POSITION DE LECTURE d'un autre. Ce qui le rend lisible sans légende n'est pas la fenêtre sombre — c'est que la fenêtre reprend exactement les mêmes pastilles, à la même taille, seulement inversées. Deux benchmarks tiennent ce couple : taille identique (tolérance 2 %) et luminance franchement différente (écart ≥ 0,3). Changer l'une des deux casse la lecture — des pastilles plus petites dans la fenêtre feraient croire à un second objet, des pastilles de même valeur la feraient disparaître.\n\nLE COMPTAGE EST UN EXPOSANT, ET CE N'EST PAS UNE FAÇON DE PARLER : la pastille acide déborde de son événement en HAUT et à DROITE, ce que le benchmark vérifie sur les deux axes. Centrée, elle deviendrait un badge d'état ; posée dessous, une légende. Elle porte l'accent parce qu'elle compte — l'alerte, dans ce système, est un rouge saumon et n'apparaît jamais ici.\n\nL'assertion « le repère aujourd'hui est le seul objet sombre hors de la fenêtre » protège le contraste de rôle : deux aplats sombres sur l'axe, et on ne sait plus lequel désigne la position courante.",
"id": "diagram-03-time-scrubber"
}
À côté
diagram-02-branch-timeline
Timeline à branches en équerre
layout-08-sheet-handle-tab
Feuille modale à poignée soudée par congés concaves
list-05-vitals-strip