Correction d'une affirmation fausse écrite plus tôt dans la journée et reprise dans la maquette, le plan et une décision validée : « tablet-app n'a aucun i18n ». Il en a un — lib/Helpers/translations.dart, table de 10 langues (FR EN NL DE IT ES PL CN AR UK), ~150 entrées, lue par TranslationHelper.getFromLocale à 19 endroits, avec déjà une clé back. Ce n'est pas de l'ARB : chercher l10n/ ou AppLocalizations ne le trouve pas, alors que le CLAUDE.md du repo le décrit. Ce que ça change : le motif donné pour « hors dates, aucune prose » — un bandeau serait français pour un néerlandophone — était faux. Un libellé traduit coûte une clé × 10 langues. La règle reste défendable (rien à traduire vaut mieux que quelque chose à traduire) mais devient un choix, pas une contrainte. À reconfirmer. Corollaire d'implémentation : le bouton Retour des deux écrans passe par la clé back existante, il ne s'écrit pas en dur. Ajouté en V2 — portrait et paysage sur toute la borne. tablet-app n'a aucune stratégie d'orientation : chaque écran est écrit pour la forme qu'on lui a vue, et la maquette du 12/08 ne tranche que le paysage, pour deux types. Une borne se monte pourtant aussi bien en totem qu'en comptoir, et le choix appartient au lieu. Le chantier n'est pas « ajouter le portrait » mais rendre les treize types indifférents à l'orientation — repasse UI transversale, même famille que la repasse globale de manager-app. La maquette est compatible (le rail qui tombe sous le texte est le comportement portrait) mais ne le spécifie pas. Kanban : carte V2 ajoutée, Planifié 21 -> 22 (colonne et bandeau). Le motif « et tablet-app ne compile même plus » de la carte Kiosk web est corrigé — il est tombé avec K1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
997 lines
42 KiB
HTML
997 lines
42 KiB
HTML
<title>Borne en paysage — Article & Événement</title>
|
||
|
||
<style>
|
||
:root {
|
||
--ground: #F7F8F9;
|
||
--surface: #FFFFFF;
|
||
--surface-2: #EDF0F2;
|
||
--ink: #16202A;
|
||
--ink-2: #4A5966;
|
||
--ink-3: #7C8B98;
|
||
--line: #DCE2E7;
|
||
--line-soft: #E8ECEF;
|
||
--accent: #2F4858;
|
||
--accent-ink: #FFFFFF;
|
||
--accent-soft: #E3EBF0;
|
||
--live: #C4622D;
|
||
--live-soft: #F6E7DC;
|
||
|
||
--display: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
|
||
--body: ui-sans-serif, "Segoe UI", system-ui, -apple-system, sans-serif;
|
||
--mono: ui-monospace, "Cascadia Mono", Consolas, "SF Mono", monospace;
|
||
|
||
--measure: 66ch;
|
||
--s1: 0.4rem; --s2: 0.75rem; --s3: 1.15rem;
|
||
--s4: 1.9rem; --s5: 3rem; --s6: 4.75rem;
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
:root:not([data-theme="light"]) {
|
||
--ground: #10161C;
|
||
--surface: #172029;
|
||
--surface-2: #1F2A34;
|
||
--ink: #E6ECF1;
|
||
--ink-2: #A9B7C2;
|
||
--ink-3: #74838F;
|
||
--line: #2A3742;
|
||
--line-soft: #212D37;
|
||
--accent: #7FB0C8;
|
||
--accent-ink: #0E141A;
|
||
--accent-soft: #1E2C36;
|
||
--live: #E08A4F;
|
||
--live-soft: #2E2118;
|
||
}
|
||
}
|
||
|
||
:root[data-theme="dark"] {
|
||
--ground: #10161C;
|
||
--surface: #172029;
|
||
--surface-2: #1F2A34;
|
||
--ink: #E6ECF1;
|
||
--ink-2: #A9B7C2;
|
||
--ink-3: #74838F;
|
||
--line: #2A3742;
|
||
--line-soft: #212D37;
|
||
--accent: #7FB0C8;
|
||
--accent-ink: #0E141A;
|
||
--accent-soft: #1E2C36;
|
||
--live: #E08A4F;
|
||
--live-soft: #2E2118;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
|
||
body {
|
||
background: var(--ground);
|
||
color: var(--ink);
|
||
font-family: var(--body);
|
||
font-size: 17px;
|
||
line-height: 1.65;
|
||
margin: 0;
|
||
padding: var(--s5) var(--s3) var(--s6);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
.page { max-width: 68rem; margin: 0 auto; display: flex; flex-direction: column; gap: var(--s6); }
|
||
|
||
/* ── Header ─────────────────────────────────────────── */
|
||
|
||
.masthead { display: flex; flex-direction: column; gap: var(--s3); }
|
||
|
||
.eyebrow {
|
||
font-family: var(--mono);
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.13em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-3);
|
||
display: flex; flex-wrap: wrap; gap: var(--s2) var(--s3);
|
||
}
|
||
|
||
h1 {
|
||
font-family: var(--display);
|
||
font-size: clamp(2.1rem, 5.2vw, 3.4rem);
|
||
line-height: 1.08;
|
||
font-weight: 600;
|
||
letter-spacing: -0.015em;
|
||
text-wrap: balance;
|
||
margin: 0;
|
||
}
|
||
|
||
.standfirst {
|
||
font-size: 1.2rem;
|
||
line-height: 1.55;
|
||
color: var(--ink-2);
|
||
max-width: var(--measure);
|
||
margin: 0;
|
||
text-wrap: pretty;
|
||
}
|
||
|
||
.rule { height: 1px; background: var(--line); border: 0; margin: 0; }
|
||
|
||
/* ── Sections ───────────────────────────────────────── */
|
||
|
||
section { display: flex; flex-direction: column; gap: var(--s4); }
|
||
|
||
h2 {
|
||
font-family: var(--display);
|
||
font-size: clamp(1.5rem, 3vw, 2rem);
|
||
font-weight: 600;
|
||
line-height: 1.15;
|
||
letter-spacing: -0.01em;
|
||
margin: 0;
|
||
text-wrap: balance;
|
||
}
|
||
|
||
h3 {
|
||
font-size: 1.02rem;
|
||
font-weight: 650;
|
||
line-height: 1.3;
|
||
margin: 0;
|
||
letter-spacing: -0.005em;
|
||
}
|
||
|
||
p { margin: 0; max-width: var(--measure); text-wrap: pretty; }
|
||
|
||
.lede { color: var(--ink-2); }
|
||
|
||
code {
|
||
font-family: var(--mono);
|
||
font-size: 0.87em;
|
||
background: var(--surface-2);
|
||
padding: 0.1em 0.36em;
|
||
border-radius: 3px;
|
||
color: var(--ink);
|
||
}
|
||
|
||
strong { font-weight: 650; }
|
||
|
||
/* ── Kiosk mockup frame ─────────────────────────────── */
|
||
|
||
.kiosk {
|
||
background: var(--surface);
|
||
border: 1px solid var(--line);
|
||
border-radius: 10px;
|
||
padding: var(--s2);
|
||
box-shadow: 0 1px 2px rgba(22, 32, 42, 0.05);
|
||
}
|
||
|
||
.kiosk-label {
|
||
font-family: var(--mono);
|
||
font-size: 0.68rem;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-3);
|
||
padding: 0 var(--s1) var(--s2);
|
||
display: flex; justify-content: space-between; gap: var(--s2);
|
||
}
|
||
|
||
.screen {
|
||
aspect-ratio: 16 / 10;
|
||
background: var(--surface-2);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
container-type: inline-size;
|
||
display: flex; flex-direction: column;
|
||
}
|
||
|
||
/* mockup internals scale with the frame width */
|
||
/* La dalle est peinte en clair en dur, quel que soit le thème du lecteur :
|
||
tablet-app est clair (kBackgroundColor #FFFFFF, kBackgroundLight #F3F3F3)
|
||
et une maquette qui bascule avec le thème du lecteur ne montre plus l'app. */
|
||
.screen {
|
||
--u: 1cqw;
|
||
--surface: #FFFFFF;
|
||
--surface-2: #F3F3F3;
|
||
--ink: #16202A;
|
||
--ink-2: #4A5966;
|
||
--ink-3: #7C8B98;
|
||
--line: #DCE2E7;
|
||
--line-soft: #E8ECEF;
|
||
--accent: #2F4858;
|
||
--accent-ink: #FFFFFF;
|
||
--accent-soft: #E3EBF0;
|
||
--live: #C4622D;
|
||
--live-soft: #F6E7DC;
|
||
background: var(--surface-2);
|
||
color: var(--ink);
|
||
}
|
||
.screen, .screen * { font-size: calc(1.35 * var(--u)); line-height: 1.4; }
|
||
|
||
.zone {
|
||
position: relative;
|
||
background: var(--surface);
|
||
border: 1px dashed var(--line);
|
||
}
|
||
|
||
.ztag {
|
||
position: absolute; top: calc(0.6 * var(--u)); left: calc(0.6 * var(--u));
|
||
font-family: var(--mono);
|
||
font-size: calc(1.15 * var(--u));
|
||
font-weight: 700;
|
||
color: var(--accent-ink);
|
||
background: var(--accent);
|
||
width: calc(2.4 * var(--u)); height: calc(2.4 * var(--u));
|
||
display: grid; place-items: center;
|
||
border-radius: 2px;
|
||
z-index: 3;
|
||
}
|
||
|
||
/* generic mockup atoms */
|
||
.bar { background: var(--ink-3); opacity: 0.28; border-radius: 1px; height: calc(1.1 * var(--u)); }
|
||
.bar.t { opacity: 0.5; }
|
||
.stack { display: flex; flex-direction: column; gap: calc(0.9 * var(--u)); }
|
||
.imgblock {
|
||
background: linear-gradient(135deg, var(--accent-soft), var(--surface-2));
|
||
border: 1px solid var(--line-soft);
|
||
display: grid; place-items: center;
|
||
color: var(--ink-3);
|
||
font-family: var(--mono);
|
||
font-size: calc(1.1 * var(--u));
|
||
}
|
||
|
||
/* ── Article mockup ─────────────────────────────────── */
|
||
|
||
.m-article { display: grid; grid-template-columns: 38fr 62fr; height: 100%; }
|
||
|
||
.m-media { padding: calc(2 * var(--u)); display: flex; flex-direction: column; gap: calc(1.4 * var(--u)); }
|
||
.m-media .imgblock { flex: 1; border-radius: calc(0.6 * var(--u)); }
|
||
|
||
.m-thumbs { display: grid; grid-template-columns: repeat(4, 1fr); gap: calc(0.8 * var(--u)); height: calc(7 * var(--u)); }
|
||
.m-thumbs .imgblock { border-radius: calc(0.4 * var(--u)); font-size: calc(0.9 * var(--u)); }
|
||
|
||
.m-audio {
|
||
background: var(--accent);
|
||
color: var(--accent-ink);
|
||
border-radius: calc(0.6 * var(--u));
|
||
padding: calc(1.2 * var(--u)) calc(1.4 * var(--u));
|
||
display: flex; align-items: center; gap: calc(1.2 * var(--u));
|
||
height: calc(8.5 * var(--u));
|
||
}
|
||
.m-audio .play {
|
||
width: calc(5 * var(--u)); height: calc(5 * var(--u));
|
||
border-radius: 50%;
|
||
background: var(--accent-ink); opacity: 0.9;
|
||
display: grid; place-items: center;
|
||
color: var(--accent);
|
||
font-size: calc(1.8 * var(--u));
|
||
flex: none;
|
||
}
|
||
.m-audio .track { flex: 1; height: calc(0.9 * var(--u)); background: currentColor; opacity: 0.35; border-radius: 1px; }
|
||
|
||
.m-text { padding: calc(2.4 * var(--u)) calc(2.4 * var(--u)) 0; display: flex; flex-direction: column; overflow: hidden; }
|
||
.m-title { font-family: var(--display); font-size: calc(3.1 * var(--u)) !important; font-weight: 600; line-height: 1.1; color: var(--ink); }
|
||
.m-kicker { font-family: var(--mono); font-size: calc(1 * var(--u)) !important; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); }
|
||
.m-measure { max-width: calc(52 * var(--u)); }
|
||
|
||
.m-footer {
|
||
border-top: 1px solid var(--line);
|
||
padding: calc(1.2 * var(--u)) calc(2.4 * var(--u));
|
||
display: flex; align-items: center; justify-content: flex-end;
|
||
background: var(--surface-2);
|
||
height: calc(8 * var(--u));
|
||
flex: none;
|
||
}
|
||
.btn-back {
|
||
background: var(--accent); color: var(--accent-ink);
|
||
border-radius: calc(0.4 * var(--u));
|
||
padding: calc(0.8 * var(--u)) calc(1.8 * var(--u));
|
||
font-size: calc(1.3 * var(--u)) !important;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* ── Event mockup ───────────────────────────────────── */
|
||
|
||
.m-event { display: flex; flex-direction: column; height: 100%; }
|
||
|
||
.m-hero {
|
||
height: 26%;
|
||
background: linear-gradient(135deg, var(--accent), var(--accent-soft));
|
||
padding: calc(2 * var(--u)) calc(2.4 * var(--u));
|
||
display: flex; align-items: flex-end; justify-content: space-between; gap: calc(2 * var(--u));
|
||
flex: none;
|
||
position: relative;
|
||
}
|
||
.m-hero-title { font-family: var(--display); font-size: calc(3 * var(--u)) !important; font-weight: 600; color: #fff; line-height: 1.1; }
|
||
.m-hero-date {
|
||
background: rgba(255,255,255,0.9); color: var(--accent);
|
||
border-radius: calc(2 * var(--u));
|
||
padding: calc(0.6 * var(--u)) calc(1.4 * var(--u));
|
||
font-size: calc(1.2 * var(--u)) !important;
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.m-body { display: grid; grid-template-columns: 44fr 56fr; flex: 1; min-height: 0; }
|
||
|
||
.m-prog { padding: calc(1.8 * var(--u)); display: flex; flex-direction: column; gap: calc(1 * var(--u)); overflow: hidden; }
|
||
.m-sechead {
|
||
font-family: var(--mono); font-size: calc(1.05 * var(--u)) !important;
|
||
letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);
|
||
}
|
||
.m-block {
|
||
border: 1px solid var(--line);
|
||
border-radius: calc(0.5 * var(--u));
|
||
padding: calc(1.1 * var(--u)) calc(1.3 * var(--u));
|
||
display: flex; align-items: center; gap: calc(1.2 * var(--u));
|
||
background: var(--surface);
|
||
}
|
||
.m-block .time { font-family: var(--mono); font-size: calc(1.15 * var(--u)) !important; color: var(--ink-3); flex: none; font-variant-numeric: tabular-nums; }
|
||
.m-block .name { flex: 1; color: var(--ink-2); font-size: calc(1.3 * var(--u)) !important; }
|
||
.m-block.live { background: var(--live-soft); border-color: var(--live); }
|
||
.m-block.live .name { color: var(--ink); font-weight: 650; }
|
||
.m-block.live .time { color: var(--live); font-weight: 700; }
|
||
.pill-live {
|
||
background: var(--live); color: #fff;
|
||
border-radius: calc(2 * var(--u));
|
||
padding: calc(0.35 * var(--u)) calc(1 * var(--u));
|
||
font-size: calc(1 * var(--u)) !important;
|
||
font-weight: 700; letter-spacing: 0.04em;
|
||
flex: none;
|
||
}
|
||
|
||
.m-map { position: relative; border-left: 1px solid var(--line); background: var(--surface-2); overflow: hidden; }
|
||
.m-map-grid {
|
||
position: absolute; inset: 0;
|
||
background-image:
|
||
linear-gradient(var(--line-soft) 1px, transparent 1px),
|
||
linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
|
||
background-size: calc(6 * var(--u)) calc(6 * var(--u));
|
||
}
|
||
.pin {
|
||
position: absolute;
|
||
width: calc(2.2 * var(--u)); height: calc(2.2 * var(--u));
|
||
border-radius: 50% 50% 50% 0;
|
||
transform: rotate(-45deg);
|
||
background: var(--accent);
|
||
border: 1px solid var(--surface);
|
||
}
|
||
.pin.live { background: var(--live); width: calc(2.8 * var(--u)); height: calc(2.8 * var(--u)); }
|
||
.m-map-cap {
|
||
position: absolute; left: calc(1.4 * var(--u)); bottom: calc(1.4 * var(--u));
|
||
background: var(--surface); border: 1px solid var(--line);
|
||
border-radius: calc(0.4 * var(--u));
|
||
padding: calc(0.6 * var(--u)) calc(1.1 * var(--u));
|
||
font-family: var(--mono); font-size: calc(1 * var(--u)) !important;
|
||
color: var(--ink-2);
|
||
}
|
||
|
||
/* ── Annotations ────────────────────────────────────── */
|
||
|
||
.notes { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--s3); }
|
||
|
||
/* ── Variants (états incomplets) ────────────────────── */
|
||
|
||
.variants { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: var(--s3); }
|
||
.variant { display: flex; flex-direction: column; gap: var(--s2); }
|
||
.variant .kiosk { padding: calc(var(--s1) * 1.2); }
|
||
.variant h3 { font-size: 0.94rem; }
|
||
.variant p { font-size: 0.9rem; color: var(--ink-2); max-width: none; }
|
||
.variant .cond {
|
||
font-family: var(--mono); font-size: 0.72rem; color: var(--ink-3);
|
||
display: block; margin-top: var(--s1);
|
||
}
|
||
|
||
.note {
|
||
display: grid;
|
||
grid-template-columns: 1.6rem 1fr;
|
||
gap: var(--s2);
|
||
align-items: start;
|
||
}
|
||
.note .key {
|
||
font-family: var(--mono); font-size: 0.72rem; font-weight: 700;
|
||
color: var(--accent-ink); background: var(--accent);
|
||
width: 1.6rem; height: 1.6rem;
|
||
display: grid; place-items: center; border-radius: 2px;
|
||
margin-top: 0.12rem;
|
||
}
|
||
.note p { font-size: 0.94rem; line-height: 1.55; color: var(--ink-2); max-width: none; }
|
||
.note strong { color: var(--ink); }
|
||
|
||
/* ── Rules list ─────────────────────────────────────── */
|
||
|
||
.rules { display: flex; flex-direction: column; gap: 0; border-top: 1px solid var(--line); }
|
||
|
||
.ruleitem {
|
||
display: grid;
|
||
grid-template-columns: minmax(9rem, 15rem) 1fr;
|
||
gap: var(--s2) var(--s4);
|
||
padding: var(--s3) 0;
|
||
border-bottom: 1px solid var(--line);
|
||
align-items: start;
|
||
}
|
||
.ruleitem h3 { padding-top: 0.1rem; }
|
||
.ruleitem p { font-size: 0.96rem; color: var(--ink-2); }
|
||
.ruleitem .before {
|
||
display: block;
|
||
font-family: var(--mono);
|
||
font-size: 0.75rem;
|
||
color: var(--ink-3);
|
||
margin-top: var(--s1);
|
||
}
|
||
|
||
/* ── Callout ────────────────────────────────────────── */
|
||
|
||
.callout {
|
||
background: var(--surface);
|
||
border: 1px solid var(--line);
|
||
border-left: 3px solid var(--accent);
|
||
border-radius: 0 6px 6px 0;
|
||
padding: var(--s3);
|
||
display: flex; flex-direction: column; gap: var(--s2);
|
||
}
|
||
.callout h3 { font-size: 0.96rem; }
|
||
.callout p { font-size: 0.94rem; color: var(--ink-2); }
|
||
|
||
footer {
|
||
font-family: var(--mono); font-size: 0.72rem;
|
||
color: var(--ink-3); letter-spacing: 0.04em;
|
||
border-top: 1px solid var(--line);
|
||
padding-top: var(--s3);
|
||
display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4);
|
||
}
|
||
|
||
@media (max-width: 44rem) {
|
||
.ruleitem { grid-template-columns: 1fr; }
|
||
body { font-size: 16px; }
|
||
}
|
||
</style>
|
||
|
||
<div class="page">
|
||
|
||
<header class="masthead">
|
||
<div class="eyebrow">
|
||
<span>tablet-app · lot K</span>
|
||
<span>K3 — SectionEvent</span>
|
||
<span>K7 — SectionArticle</span>
|
||
<span>12 août 2026</span>
|
||
</div>
|
||
<h1>Deux écrans à faire passer du téléphone debout à la borne couchée</h1>
|
||
<p class="standfirst">
|
||
Le code se recopie depuis <code>mymuseum-visitapp</code> — ce n'est pas là qu'est le travail.
|
||
Ces écrans sont dessinés pour un téléphone tenu à la verticale, à trente centimètres des yeux.
|
||
Une borne fait 1280 px de large, elle est fixe, et on la lit debout à un mètre.
|
||
Cette page arrête la mise en page des deux écrans <em>ensemble</em>, parce que les trancher
|
||
séparément donnerait deux grammaires différentes sur la même dalle.
|
||
<strong>Validée le 12 août</strong> — les quatre décisions de fin de page sont prises.
|
||
</p>
|
||
</header>
|
||
|
||
<hr class="rule">
|
||
|
||
<!-- ── RÈGLES COMMUNES ────────────────────────────── -->
|
||
|
||
<section>
|
||
<h2>Ce qui change, pour les deux</h2>
|
||
<p class="lede">
|
||
Six décisions valables sur toute la borne. Elles ne sortent pas d'une préférence :
|
||
chacune corrige un idiome de téléphone qui devient faux sur une dalle large et fixe.
|
||
</p>
|
||
|
||
<div class="rules">
|
||
|
||
<div class="ruleitem">
|
||
<h3>Deux colonnes, jamais une</h3>
|
||
<p>
|
||
La largeur est la ressource qu'on gagne, et une colonne unique la gaspille intégralement.
|
||
Chaque écran se découpe en un <strong>ancrage</strong> qui ne bouge pas et un
|
||
<strong>flux</strong> qui défile. Sur l'article, le média ancre et le texte défile ; sur
|
||
l'événement, la carte ancre et le programme défile.
|
||
<span class="before">mobile — une colonne, tout défile ensemble</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Le texte reste étroit</h3>
|
||
<p>
|
||
Élargir la colonne de texte à 1280 px la rend illisible : l'œil perd la ligne au retour.
|
||
Le corps de l'article est plafonné autour de <strong>65 caractères</strong> et le reste de
|
||
la largeur va au média, pas au texte. C'est la règle qui justifie à elle seule les deux colonnes.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Le héros est une bande, pas un écran</h3>
|
||
<p>
|
||
Sur mobile, l'événement ouvre sur une image qui occupe <strong>52 % de la hauteur</strong>,
|
||
et le parallaxe récompense le défilement. Debout devant une borne, personne ne défile pour
|
||
découvrir qu'il y a un programme dessous. Le héros tombe à <strong>26 %</strong> : il situe,
|
||
il n'accueille pas.
|
||
<span class="before">mobile — SliverAppBar, expandedHeight = 0.52 × hauteur</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Pas de feuille modale</h3>
|
||
<p>
|
||
<code>showModalBottomSheet</code> est une réponse à l'étroitesse : sur un téléphone il n'y a
|
||
pas de place à côté, alors on empile. Sur une borne il y a la place à côté. Le détail d'un
|
||
bloc de programme s'ouvre <strong>dans la colonne de droite</strong>, sous la carte, sans
|
||
masquer ce qu'on regardait.
|
||
<span class="before">mobile — _showBlockDetail() en bottom sheet</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Rien à agrandir</h3>
|
||
<p>
|
||
La carte de l'événement est un aperçu de 220 px qu'il faut toucher pour ouvrir une page
|
||
pleine. Sur une borne d'accueil, la carte <em>est</em> la raison pour laquelle on s'arrête :
|
||
elle est affichée à sa taille utile d'emblée, et <code>EventMapFullPage</code> ne se porte pas.
|
||
<span class="before">mobile — aperçu 220 px + bouton « Agrandir »</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Les couleurs viennent de la configuration</h3>
|
||
<p>
|
||
<code>mymuseum-visitapp</code> code ses couleurs en dur par flavor client — rouge MDLF, bleu
|
||
Fort. <code>tablet-app</code> n'a pas de flavor : un seul APK sert tous les clients et la
|
||
charte arrive avec la configuration choisie au pincode. Tout accent dérive de
|
||
<code>configuration.primaryColor</code>, <strong>comme l'écran Game livré en K4</strong>.
|
||
Le bleu ardoise de cette page est le repli <code>kTestSecondColor</code>, pas une proposition
|
||
de charte.
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ── ARTICLE ────────────────────────────────────── -->
|
||
|
||
<section>
|
||
<h2>Article — le média ancre, le texte défile</h2>
|
||
<p class="lede">
|
||
Un article porte un corps HTML, souvent un audio, et parfois une série d'images.
|
||
Sur mobile les trois s'empilent et l'audio finit en lecteur flottant par-dessus le texte.
|
||
En paysage, chacun a sa place et le lecteur audio n'a plus besoin de flotter.
|
||
</p>
|
||
|
||
<div class="kiosk">
|
||
<div class="kiosk-label">
|
||
<span>Article — 1280 × 800</span>
|
||
<span>MDLF · « Ruche 7 »</span>
|
||
</div>
|
||
<div class="screen">
|
||
<div class="m-article">
|
||
|
||
<div class="zone m-media">
|
||
<span class="ztag">A</span>
|
||
<div class="imgblock">image principale</div>
|
||
<div class="m-thumbs">
|
||
<div class="imgblock">2</div>
|
||
<div class="imgblock">3</div>
|
||
<div class="imgblock">4</div>
|
||
<div class="imgblock">5</div>
|
||
</div>
|
||
<div class="m-audio">
|
||
<span class="play">▶</span>
|
||
<span class="track"></span>
|
||
<span style="font-family: var(--mono); font-variant-numeric: tabular-nums;">3:12</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="display: flex; flex-direction: column; min-width: 0;">
|
||
<div class="zone m-text" style="flex: 1;">
|
||
<span class="ztag">B</span>
|
||
<div class="stack m-measure" style="margin-top: calc(2.6 * var(--u));">
|
||
<div class="m-kicker">Rucher pédagogique</div>
|
||
<div class="m-title">Ruche 7</div>
|
||
<div class="stack" style="margin-top: calc(1.4 * var(--u));">
|
||
<div class="bar" style="width: 100%;"></div>
|
||
<div class="bar" style="width: 97%;"></div>
|
||
<div class="bar" style="width: 99%;"></div>
|
||
<div class="bar" style="width: 62%;"></div>
|
||
<div class="bar t" style="width: 44%; margin-top: calc(1.2 * var(--u));"></div>
|
||
<div class="bar" style="width: 98%;"></div>
|
||
<div class="bar" style="width: 95%;"></div>
|
||
<div class="bar" style="width: 71%;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="zone m-footer" style="border-style: dashed dashed none none;">
|
||
<span class="ztag" style="top: calc(-1.4 * var(--u));">C</span>
|
||
<span class="btn-back">Retour</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="notes">
|
||
<div class="note">
|
||
<span class="key">A</span>
|
||
<p>
|
||
<strong>Rail média, 38 %.</strong> L'image principale prend la hauteur disponible, les
|
||
autres ressources deviennent une bande de vignettes — c'est <code>SliderImages</code> à plat
|
||
plutôt qu'en carrousel, parce qu'une borne n'a pas de raison de cacher quatre images
|
||
derrière un geste. <strong>L'audio est docké en bas du rail</strong> : sur mobile,
|
||
<code>audio_player_floating</code> flotte au-dessus du texte faute de place. Ici il a une
|
||
place, donc il ne flotte plus.
|
||
</p>
|
||
</div>
|
||
<div class="note">
|
||
<span class="key">B</span>
|
||
<p>
|
||
<strong>Colonne de lecture, 62 %, texte plafonné.</strong> Le rendu HTML
|
||
(<code>flutter_widget_from_html</code>) se recopie tel quel ; ce qui change est le
|
||
<code>maxWidth</code> qui l'entoure. Le titre passe en display, avec l'étiquette de section
|
||
au-dessus — sur une borne, un visiteur arrive au milieu d'un parcours et doit savoir
|
||
<em>où</em> il est avant <em>quoi</em> il lit.
|
||
</p>
|
||
</div>
|
||
<div class="note">
|
||
<span class="key">C</span>
|
||
<p>
|
||
<strong>Barre de pied, hauteur de doigt.</strong> Un seul bouton, cible à
|
||
<strong>56 px minimum</strong> : on tape debout, parfois avec un enfant au bout du bras.
|
||
<strong>Pas de sélecteur de langue</strong> — elle est choisie en amont, la réafficher ici
|
||
rouvrirait une décision déjà prise. Et rien d'autre : <code>SectionPageDetail</code> ne
|
||
reçoit qu'<em>une</em> section et un booléen <code>isFromMenu</code>, jamais la liste de ses
|
||
voisines. Un repère du type « 3 / 8 » n'est donc adossé à aucune donnée — il faudrait
|
||
descendre la liste jusqu'ici pour le calculer.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<h2 style="font-size: 1.25rem; margin-top: var(--s3);">Quand il manque l'audio, les photos, ou les deux</h2>
|
||
<p class="lede">
|
||
Les trois cas sont possibles en configuration, et le rail média de 38 % ne survit pas tel quel
|
||
à la disparition de son contenu. La règle qui les couvre tous :
|
||
<strong>le rail n'existe que s'il a quelque chose à ancrer</strong>. Sinon la colonne de lecture
|
||
se centre — ce qui ne contredit pas la règle des deux colonnes, dont l'objet est d'empêcher une
|
||
ligne de texte de s'étirer sur 1280 px, pas d'imposer une colonne vide.
|
||
</p>
|
||
|
||
<div class="variants">
|
||
|
||
<div class="variant">
|
||
<div class="kiosk">
|
||
<div class="screen">
|
||
<div class="m-article">
|
||
<div class="zone m-media">
|
||
<div class="imgblock">image principale</div>
|
||
<div class="m-thumbs">
|
||
<div class="imgblock">2</div>
|
||
<div class="imgblock">3</div>
|
||
<div class="imgblock">4</div>
|
||
<div class="imgblock">5</div>
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; flex-direction: column; min-width: 0;">
|
||
<div class="zone m-text" style="flex: 1;">
|
||
<div class="stack m-measure" style="margin-top: calc(2.6 * var(--u));">
|
||
<div class="m-kicker">Rucher pédagogique</div>
|
||
<div class="m-title">Ruche 7</div>
|
||
<div class="stack" style="margin-top: calc(1.4 * var(--u));">
|
||
<div class="bar" style="width: 100%;"></div>
|
||
<div class="bar" style="width: 96%;"></div>
|
||
<div class="bar" style="width: 99%;"></div>
|
||
<div class="bar" style="width: 58%;"></div>
|
||
<div class="bar t" style="width: 44%; margin-top: calc(1.2 * var(--u));"></div>
|
||
<div class="bar" style="width: 97%;"></div>
|
||
<div class="bar" style="width: 74%;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="zone m-footer" style="border-style: dashed none none none;">
|
||
<span class="btn-back">Retour</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<h3>Sans audio</h3>
|
||
<p>
|
||
Le dock disparaît, l'image reprend la hauteur libérée. <strong>Pas de lecteur grisé, pas de
|
||
repli sur une autre langue</strong> — le lecteur n'existe pas pour ce visiteur.
|
||
<span class="cond">pas d'entrée audioIds pour la langue choisie</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="variant">
|
||
<div class="kiosk">
|
||
<div class="screen">
|
||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||
<div class="zone m-text" style="flex: 1; align-items: center;">
|
||
<div class="stack m-measure" style="margin-top: calc(3.2 * var(--u)); width: calc(52 * var(--u));">
|
||
<div class="m-kicker">Rucher pédagogique</div>
|
||
<div class="m-title">Ruche 7</div>
|
||
<div class="m-audio" style="margin-top: calc(1.6 * var(--u)); height: calc(7.5 * var(--u));">
|
||
<span class="play">▶</span>
|
||
<span class="track"></span>
|
||
<span style="font-family: var(--mono);">3:12</span>
|
||
</div>
|
||
<div class="stack" style="margin-top: calc(1.6 * var(--u));">
|
||
<div class="bar" style="width: 100%;"></div>
|
||
<div class="bar" style="width: 97%;"></div>
|
||
<div class="bar" style="width: 99%;"></div>
|
||
<div class="bar" style="width: 63%;"></div>
|
||
<div class="bar t" style="width: 44%; margin-top: calc(1.2 * var(--u));"></div>
|
||
<div class="bar" style="width: 96%;"></div>
|
||
<div class="bar" style="width: 81%;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="zone m-footer" style="border-style: dashed none none none;">
|
||
<span class="btn-back">Retour</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<h3>Sans photo, avec audio</h3>
|
||
<p>
|
||
Le rail tombe — une colonne de 38 % contenant un seul lecteur serait un trou, pas une mise
|
||
en page. L'audio passe <strong>en tête de la colonne de lecture</strong>, à la largeur du
|
||
texte, juste sous le titre : c'est là qu'on le cherche quand il est la pièce principale.
|
||
<span class="cond">imageSource nul et contents vide</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="variant">
|
||
<div class="kiosk">
|
||
<div class="screen">
|
||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||
<div class="zone m-text" style="flex: 1; align-items: center;">
|
||
<div class="stack m-measure" style="margin-top: calc(4.5 * var(--u)); width: calc(52 * var(--u));">
|
||
<div class="m-kicker">Rucher pédagogique</div>
|
||
<div class="m-title">Ruche 7</div>
|
||
<div class="stack" style="margin-top: calc(1.8 * var(--u));">
|
||
<div class="bar" style="width: 100%;"></div>
|
||
<div class="bar" style="width: 98%;"></div>
|
||
<div class="bar" style="width: 96%;"></div>
|
||
<div class="bar" style="width: 99%;"></div>
|
||
<div class="bar" style="width: 57%;"></div>
|
||
<div class="bar t" style="width: 44%; margin-top: calc(1.4 * var(--u));"></div>
|
||
<div class="bar" style="width: 97%;"></div>
|
||
<div class="bar" style="width: 99%;"></div>
|
||
<div class="bar" style="width: 68%;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="zone m-footer" style="border-style: dashed none none none;">
|
||
<span class="btn-back">Retour</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<h3>Texte seul</h3>
|
||
<p>
|
||
Une seule colonne, centrée, toujours plafonnée à 65 caractères. Le vide de part et d'autre
|
||
est <strong>voulu</strong> : c'est ce qui garde le texte lisible. La tentation d'étirer la
|
||
ligne pour « remplir » est précisément ce que la règle interdit.
|
||
<span class="cond">ni image, ni contents, ni audio</span>
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="callout">
|
||
<h3>Deux choses vérifiées dans <code>ArticleDTO</code>, pas supposées</h3>
|
||
<p>
|
||
<strong><code>audioIds</code> est une <code>List<TranslationDTO></code>, pas un id.</strong>
|
||
L'audio est donc <em>par langue</em> : un article peut avoir sa version française et pas sa
|
||
néerlandaise. La règle est donc stricte — <strong>pas d'entrée pour la langue choisie, pas de
|
||
lecteur du tout.</strong> Ni lecteur grisé, ni repli sur une autre langue : un visiteur
|
||
néerlandophone à qui la borne servirait l'audio français serait plus mal servi que par le
|
||
silence. « Sans audio » est un état <strong>du visiteur</strong>, pas de l'article.
|
||
</p>
|
||
<p>
|
||
<strong><code>isContentTop</code> existe déjà</strong> et dit si le texte passe avant le média.
|
||
En portrait, « avant » veut dire au-dessus ; en paysage il n'y a plus de dessus, donc
|
||
<strong>le drapeau devient le côté</strong> :
|
||
<code>isContentTop == true</code> → <strong>texte à gauche, rail média à droite</strong> ;
|
||
sinon la disposition par défaut des maquettes, média à gauche. Le rail garde sa composition
|
||
dans les deux cas — image, vignettes, audio docké — il change de bord, pas de contenu. À
|
||
câbler, sinon un réglage déjà offert dans <code>manager-app</code> reste sans effet sur la borne.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="callout">
|
||
<h3>Les statistiques sont déjà là — vérifié dans le code</h3>
|
||
<p>
|
||
Rien à câbler. <code>section_page_detail.dart</code> émet <code>sectionView</code> à
|
||
l'ouverture et <code>sectionLeave</code> avec la durée au <code>dispose</code>, et il
|
||
<strong>enveloppe les treize types</strong> : l'article sera compté dès que son <code>case</code>
|
||
entre dans le <code>switch</code>. <code>statisticsService</code> est câblé à sept endroits de
|
||
l'app. L'événement <code>articleRead</code> de mymuseum, plus spécifique, n'existe pas ici —
|
||
c'est un ajout facultatif, pas un manque.
|
||
</p>
|
||
<p>
|
||
En revanche, le <strong>marquage « déjà lu » par visiteur</strong>
|
||
(<code>articleRead</code> en base locale, <code>DatabaseHelper</code>) ne se recopie pas : sur
|
||
un appareil partagé, le lecteur suivant n'est pas le précédent.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ── ÉVÉNEMENT ──────────────────────────────────── -->
|
||
|
||
<section>
|
||
<h2>Événement — la carte ancre, le programme défile</h2>
|
||
<p class="lede">
|
||
C'est l'écran du cas E du test-plan : la borne d'accueil d'un carnaval ou d'un festival, qui
|
||
répond à deux questions et deux seulement — <em>qu'est-ce qui se passe maintenant</em> et
|
||
<em>où est-ce que ça se passe</em>. La mise en page les met côte à côte, sans geste entre elles.
|
||
</p>
|
||
|
||
<div class="kiosk">
|
||
<div class="kiosk-label">
|
||
<span>Événement — 1280 × 800</span>
|
||
<span>Cas E · carnaval</span>
|
||
</div>
|
||
<div class="screen">
|
||
<div class="m-event">
|
||
|
||
<div class="zone m-hero" style="border-style: none none dashed none;">
|
||
<span class="ztag">D</span>
|
||
<div class="m-hero-title">Carnaval des Légendes</div>
|
||
<span class="m-hero-date">14/03/2026 → 16/03/2026</span>
|
||
</div>
|
||
|
||
<div class="m-body">
|
||
|
||
<div class="zone m-prog">
|
||
<span class="ztag">E</span>
|
||
<div class="m-sechead" style="margin-top: calc(2.4 * var(--u));">Programme du jour</div>
|
||
<div class="m-block">
|
||
<span class="time">10:00</span>
|
||
<span class="name">Ouverture des portes</span>
|
||
</div>
|
||
<div class="m-block live">
|
||
<span class="time">14:00</span>
|
||
<span class="name">Grand cortège — départ place du Marché</span>
|
||
<span class="pill-live">EN COURS</span>
|
||
</div>
|
||
<div class="m-block">
|
||
<span class="time">16:30</span>
|
||
<span class="name">Remise des prix des chars</span>
|
||
</div>
|
||
<div class="m-block">
|
||
<span class="time">18:00</span>
|
||
<span class="name">Brûlage du Bonhomme Hiver</span>
|
||
</div>
|
||
<div class="m-block">
|
||
<span class="time">20:00</span>
|
||
<span class="name">Feu d'artifice</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="zone m-map">
|
||
<span class="ztag">F</span>
|
||
<div class="m-map-grid"></div>
|
||
<div class="pin live" style="left: 44%; top: 46%;"></div>
|
||
<div class="pin" style="left: 22%; top: 28%;"></div>
|
||
<div class="pin" style="left: 68%; top: 34%;"></div>
|
||
<div class="pin" style="left: 58%; top: 66%;"></div>
|
||
<div class="pin" style="left: 30%; top: 72%;"></div>
|
||
<span class="m-map-cap">5 points · le point orange suit le bloc en cours</span>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="notes">
|
||
<div class="note">
|
||
<span class="key">D</span>
|
||
<p>
|
||
<strong>Bande héros, 26 %.</strong> Assez pour porter l'image, le titre et les dates ;
|
||
pas assez pour repousser le programme sous la ligne de flottaison. Le dégradé de repli
|
||
dérive de <code>primaryColor</code> — c'est exactement le calcul déjà écrit dans l'écran
|
||
Game de K4, à réutiliser plutôt qu'à refaire.
|
||
</p>
|
||
</div>
|
||
<div class="note">
|
||
<span class="key">E</span>
|
||
<p>
|
||
<strong>Programme, 44 %.</strong> La frise verticale à pastilles et trait de liaison de
|
||
mymuseum tombe : elle coûte 56 px de gouttière pour exprimer une chronologie que l'heure
|
||
en tête de ligne dit déjà. Le bloc actif garde son fond plein et son pastillage
|
||
<strong>« EN COURS »</strong> — c'est l'information la plus demandée de l'écran, elle doit
|
||
se voir à trois mètres.
|
||
</p>
|
||
</div>
|
||
<div class="note">
|
||
<span class="key">F</span>
|
||
<p>
|
||
<strong>Carte, 56 %, toujours vive.</strong> Les annotations globales sont dans l'accent,
|
||
celles du bloc en cours dans l'orange sémantique — la même paire de couleurs que le
|
||
programme, pour que l'œil relie une ligne à un point sans légende. Défilement et zoom
|
||
restent actifs ; <code>InteractiveFlag.none</code> était une contrainte d'aperçu.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="callout">
|
||
<h3>Le piège à ne pas déplacer</h3>
|
||
<p>
|
||
Les coordonnées de <code>MapAnnotationDTO.geometry.coordinates</code> sont en
|
||
<strong><code>[lng, lat]</code></strong> — GeoJSON. Celles de <code>GuidedStep</code> sont en
|
||
<code>[lat, lng]</code>. Les deux ordres sont en production, et une confusion
|
||
<strong>ne lève aucune erreur</strong> : elle déplace les points sur une carte qui reste
|
||
plausible. La lecture de mymuseum (<code>coords[1]</code> = lat, <code>coords[0]</code> = lng)
|
||
se recopie telle quelle, sans passer par un helper venu d'un autre contexte. Le volet parcours
|
||
étant exclu de la borne, cet écran ne voit qu'une seule des deux conventions — c'est une raison
|
||
de plus de ne pas y introduire l'autre.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ── QUESTIONS ──────────────────────────────────── -->
|
||
|
||
<section>
|
||
<h2>Tranché le 12 août</h2>
|
||
|
||
<div class="rules">
|
||
|
||
<div class="ruleitem">
|
||
<h3>Fond clair</h3>
|
||
<p>
|
||
La nouvelle section ne doit pas dénoter dans l'app. <code>tablet-app</code> est clair —
|
||
<code>kBackgroundColor</code> vaut <code>#FFFFFF</code> et <code>kBackgroundLight</code>
|
||
<code>#F3F3F3</code> — et les maquettes ci-dessus emploient <strong>ces valeurs-là</strong>,
|
||
pas une palette inventée. Le <code>#111111</code> en dur de l'écran Événement de mymuseum ne
|
||
se recopie donc pas : il vient d'un téléphone consulté en extérieur.
|
||
<span class="before">mymuseum — Scaffold backgroundColor: Color(0xFF111111)</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Hors dates, aucune prose</h3>
|
||
<p>
|
||
Le carnaval dure trois jours, la borne tourne les 362 autres. Le programme reste affiché,
|
||
<strong>sans pastille « EN COURS »</strong> et avec les blocs en gris — le surlignage est
|
||
calculé sur <code>DateTime.now()</code>, il s'éteint tout seul. La bande héros continue de
|
||
porter les dates, qui sont <strong>numériques donc sans traduction</strong>, et suffisent à
|
||
dire qu'on est avant ou après.
|
||
<span class="before">état passé = les mêmes blocs, désaturés — pas un écran différent</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>L'i18n existe — corrigé le 12/08</h3>
|
||
<p>
|
||
⛔ <strong>Une affirmation de cette page était fausse et a été retirée</strong> :
|
||
« <code>tablet-app</code> n'a aucun i18n ». Il en a un —
|
||
<code>lib/Helpers/translations.dart</code>, une table de <strong>10 langues</strong>
|
||
(FR, EN, NL, DE, IT, ES, PL, CN, AR, UK) lue par
|
||
<code>TranslationHelper.getFromLocale</code> à <strong>19 endroits</strong>, avec déjà une
|
||
clé <code>back</code>. Ce n'est pas de l'ARB, c'est une table à la main — le même pattern que
|
||
mymuseum. Chercher <code>l10n/</code> et <code>AppLocalizations</code> ne le trouve pas.
|
||
</p>
|
||
<p style="margin-top: var(--s2);">
|
||
Ce que ça change : un libellé traduit coûte <strong>une clé × 10 langues</strong>, pas un
|
||
chantier. La règle « aucune prose » ci-dessus reste défendable — les dates suffisent, et
|
||
rien à traduire vaut mieux que quelque chose à traduire — mais elle devient un
|
||
<strong>choix</strong> et non une contrainte. <strong>À confirmer.</strong> Et le bouton
|
||
« Retour » des maquettes doit passer par la clé <code>back</code> existante, pas être écrit
|
||
en dur.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="ruleitem">
|
||
<h3>Retour à l'accueil après 5 min</h3>
|
||
<p>
|
||
Adopté, et <strong>écrit une seule fois pour les treize types</strong> :
|
||
<code>section_page_detail</code> enveloppe déjà toutes les sections et porte déjà les crochets
|
||
<code>initState</code> / <code>dispose</code> des statistiques. Le minuteur d'inactivité va
|
||
là, pas dans les deux écrans de cette page. Bénéfice de bord : un retour automatique passe
|
||
par le même <code>dispose</code>, donc il émet un <code>sectionLeave</code> avec sa durée
|
||
réelle — la statistique reste juste au lieu de compter une consultation ouverte jusqu'au
|
||
prochain visiteur.
|
||
<span class="before">hors périmètre K3/K7 — à ouvrir comme chantier propre</span>
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<footer>
|
||
<span>DOCS/claude design/kiosk-paysage-article-event.html</span>
|
||
<span>source de vérité — l'artifact ne sert qu'à la diffusion (DB0)</span>
|
||
</footer>
|
||
|
||
</div>
|