identité 4 : plus de paire ni de colonne en marge, chaque oiseau sur une ligne visible ; la console arrête un curseur qui descendait

Relecture de fb8c71f..16d6607 (03/11 13h10, relayée par la session orch) :
- /economique : la balance sort de derrière le bloc du livre à mi-hauteur, loin du sol de la
  maraîchère ;
- authentification-wot : la toile de confiance, petite, en marge sur la dernière ligne des étapes
  (le bloc n'a pas de fond) ;
- chapitres : le filet au-dessus de précédent / suivant est tracé (border-t seul n'a pas de style
  ici) ; le cartonnier s'y pose ;
- /evenement : l'acrobate passe au bout de la ligne du leitmotiv, le régisseur reste seul près de
  « Format & lieu » ;
- sommaire du livre : empileur plus loin, illustratrice moins sortie : plus de bande commune.
Trouvés par le contrôle étendu : le débogueur (logiciel-libre) sur la dernière ligne des étapes,
la vigneronne (monnaie-libre) derrière le bloc de texte, qui a un fond.

Console, décision d'orch du 03/11 à 13h10 : au premier geste, un curseur non touché que la
rotation faisait descendre s'arrête là où il est ; un curseur qui montait finit sa montée.
docs/consignes-yvv.md (« Ce que fait le code ») et docs/da-oiseaux.md suivent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-03 13:52:44 +01:00
co-authored by Claude Opus 5.5
parent 16d66072b9
commit b6143c5cf7
9 changed files with 86 additions and 24 deletions
+30 -12
View File
@@ -30,7 +30,7 @@
type="button" type="button"
role="tab" role="tab"
class="fader" class="fader"
:class="{ 'fader--active': axis.key === active, 'fader--held': held.has(axis.key), 'fader--rising': rising.has(axis.key) }" :class="{ 'fader--active': axis.key === active, 'fader--held': held.has(axis.key), 'fader--rising': rising.has(axis.key), 'fader--stopped': stopped.has(axis.key) }"
:style="{ '--hue': `var(${axis.hue})`, '--level': levels[axis.key] ?? 0, '--i': i }" :style="{ '--hue': `var(${axis.hue})`, '--level': levels[axis.key] ?? 0, '--i': i }"
:data-axis="axis.key" :data-axis="axis.key"
:aria-selected="axis.key === active" :aria-selected="axis.key === active"
@@ -142,10 +142,11 @@ const current = computed(() => props.axes.find(a => a.key === active.value) ?? p
// The visitor's hand takes over for good (03/11): from the first hover, // The visitor's hand takes over for good (03/11): from the first hover,
// keyboard focus or tap on a cursor, the rotation stops. The touched cursor // keyboard focus or tap on a cursor, the rotation stops. The touched cursor
// replays its rise, from the bottom to its own graduation, and its panel // replays its rise, from the bottom to its own graduation, and its panel
// shows. The others are never lowered by the hand: a cursor already touched // shows. The others are never lowered by the hand (orch decision, 03/11
// stays at its graduation; a cursor never touched finishes, gently, the move // 13h10): a cursor already touched stays at its graduation; a cursor never
// the rotation had begun (up to its graduation, or down to its low level), // touched that the rotation was taking down stops where it is at the first
// then stays there. None stays frozen mid-course. // gesture; one it was taking up finishes, gently, its rise to the level the
// rotation gave it, then stays there.
const BEAT_MS = 2600 const BEAT_MS = 2600
const BEATS_PER_AXIS = 3 const BEATS_PER_AXIS = 3
/** Graduation of each axis, from asservissement (0) to émancipation (1) */ /** Graduation of each axis, from asservissement (0) to émancipation (1) */
@@ -158,6 +159,8 @@ const levels = reactive<Partial<Record<AxisKey, number>>>({})
const held = reactive(new Set<AxisKey>()) const held = reactive(new Set<AxisKey>())
/** Cursors replaying their rise (fast transition) */ /** Cursors replaying their rise (fast transition) */
const rising = reactive(new Set<AxisKey>()) const rising = reactive(new Set<AxisKey>())
/** Untouched cursors the first gesture stopped on their way down */
const stopped = reactive(new Set<AxisKey>())
const reduced = ref(false) const reduced = ref(false)
const visible = ref(true) const visible = ref(true)
const engaged = ref(false) const engaged = ref(false)
@@ -188,17 +191,30 @@ function faderEl(key: AxisKey) {
return root.value?.querySelector<HTMLElement>(`[data-axis="${key}"]`) ?? null return root.value?.querySelector<HTMLElement>(`[data-axis="${key}"]`) ?? null
} }
/** First gesture: the rotation stops for good. The untouched cursors keep the target the /** First gesture: the rotation stops for good. An untouched cursor on its way down stops
* rotation gave them and finish their glide to it (no freeze mid-course) */ * where it is drawn; one on its way up finishes its glide to the target the rotation gave it.
function engage() { * `touched` is the cursor of this gesture: it replays its rise anyway */
function engage(touched?: AxisKey) {
if (engaged.value) return if (engaged.value) return
engaged.value = true engaged.value = true
clearInterval(timer) clearInterval(timer)
for (const axis of props.axes) {
const key = axis.key
if (key === touched || held.has(key)) continue
const el = faderEl(key)
const drawn = el ? Number.parseFloat(getComputedStyle(el).getPropertyValue('--level')) : Number.NaN
const target = levels[key] ?? 0
if (Number.isFinite(drawn) && drawn > target) {
stopped.add(key)
levels[key] = drawn
}
}
} }
/** The touched cursor replays its rise: back to the bottom, then up to its graduation */ /** The touched cursor replays its rise: back to the bottom, then up to its graduation */
async function rise(key: AxisKey) { async function rise(key: AxisKey) {
held.add(key) held.add(key)
stopped.delete(key)
const target = GRADUATION[key] ?? 0.75 const target = GRADUATION[key] ?? 0.75
if (reduced.value) { if (reduced.value) {
levels[key] = target levels[key] = target
@@ -220,14 +236,14 @@ async function rise(key: AxisKey) {
/** A cursor under the visitor's hand: it rises and its panel shows. Every rollover replays /** A cursor under the visitor's hand: it rises and its panel shows. Every rollover replays
* the rise, however soon after the previous one (Yvv, 03/11) */ * the rise, however soon after the previous one (Yvv, 03/11) */
function touch(key: AxisKey) { function touch(key: AxisKey) {
engage() engage(key)
active.value = key active.value = key
rise(key) rise(key)
} }
/** The panel of a cursor already raised by this same gesture: no second rise */ /** The panel of a cursor already raised by this same gesture: no second rise */
function select(key: AxisKey) { function select(key: AxisKey) {
engage() engage(key)
active.value = key active.value = key
} }
@@ -404,8 +420,10 @@ onMounted(() => {
} }
/* A cursor the hand has touched no longer glides: it drops to the bottom at once, then /* A cursor the hand has touched no longer glides: it drops to the bottom at once, then
replays its rise. The untouched ones finish their glide, then the rotation is over */ replays its rise. At the first gesture, an untouched cursor on its way down stops where it
.fader--held { is; one on its way up finishes its glide, then the rotation is over */
.fader--held,
.fader--stopped {
transition: none; transition: none;
} }
+4 -2
View File
@@ -43,8 +43,10 @@
<!-- Modèle économique — bloc livre --> <!-- Modèle économique — bloc livre -->
<div class="book-block"> <div class="book-block">
<!-- The balance (the measure of the gift) peeks out from behind the book --> <!-- The balance (the measure of the gift) peeks out from behind the book, on the left and
<UiOiseauPose nom="la-balance" pose="derriere" left="-150px" bottom="-20px" :w="240" /> halfway up: the market gardener stands low on the right, never at its height, and the
four birds of the page do not alternate at an even step -->
<UiOiseauPose nom="la-balance" pose="derriere" left="-150px" bottom="calc(100% - 240px)" :w="240" />
<!-- Not by the Productions card: with the beekeeper, a pair at the same height --> <!-- Not by the Productions card: with the beekeeper, a pair at the same height -->
<UiOiseauPose nom="maraichere" pose="marge-d" ecart="46px" :w="108" retourne /> <UiOiseauPose nom="maraichere" pose="marge-d" ecart="46px" :w="108" retourne />
<HomeBookSection <HomeBookSection
+3 -2
View File
@@ -14,8 +14,9 @@
<BookChapterContent :content="chapter" /> <BookChapterContent :content="chapter" />
<!-- Prev / Next navigation --> <!-- Prev / Next navigation. The rule above it is drawn (border-t alone sets no style here):
<nav class="relative mt-16 flex items-center justify-between border-t border-white/8 pt-8"> the box maker stands on it -->
<nav class="relative mt-16 flex items-center justify-between border-t border-t-solid border-white/8 pt-8">
<UiOiseauPose nom="cartonnier" pose="perche" left="calc(50% - 38px)" :w="76" /> <UiOiseauPose nom="cartonnier" pose="perche" left="calc(50% - 38px)" :w="76" />
<NuxtLink <NuxtLink
v-if="prevChapter" v-if="prevChapter"
+2 -2
View File
@@ -78,8 +78,8 @@ const OISEAUX_CHAPITRE: Record<string, OiseauPlace[]> = {
'01-introduction': [{ nom: 'conteuse', pose: 'marge-g', ecart: '50px', w: 122 }], '01-introduction': [{ nom: 'conteuse', pose: 'marge-g', ecart: '50px', w: 122 }],
'03-mesure': [{ nom: 'la-balance', pose: 'marge-d', ecart: '40px', w: 112, retourne: true }], '03-mesure': [{ nom: 'la-balance', pose: 'marge-d', ecart: '40px', w: 112, retourne: true }],
'07-echanger': [{ nom: 'colporteur', pose: 'marge-g', ecart: '44px', w: 116 }], '07-echanger': [{ nom: 'colporteur', pose: 'marge-g', ecart: '44px', w: 116 }],
'08-institution': [{ nom: 'empileur', pose: 'marge-d', ecart: '48px', w: 128, retourne: true }], '08-institution': [{ nom: 'empileur', pose: 'marge-d', ecart: '90px', w: 104, retourne: true }],
'11-annexes': [{ nom: 'illustratrice', pose: 'derriere', left: 'calc(100% - 90px)', bottom: '-40px', w: 220 }], '11-annexes': [{ nom: 'illustratrice', pose: 'derriere', left: 'calc(100% - 110px)', bottom: '-40px', w: 220 }],
} }
definePageMeta({ definePageMeta({
+2 -1
View File
@@ -65,6 +65,8 @@
class="prose-block relative mb-8 max-w-2xl mx-auto section-anchor" class="prose-block relative mb-8 max-w-2xl mx-auto section-anchor"
> >
<UiOiseauPose nom="boulangere" pose="marge-g" ecart="56px" :w="96" /> <UiOiseauPose nom="boulangere" pose="marge-g" ecart="56px" :w="96" />
<!-- Behind the top of the text block, which has a background (the links stand apart) -->
<UiOiseauPose nom="vigneronne" pose="derriere" left="-140px" bottom="calc(100% - 220px)" :w="210" />
<p class="leading-relaxed whitespace-pre-line" style="color: hsl(var(--color-text-muted))"> <p class="leading-relaxed whitespace-pre-line" style="color: hsl(var(--color-text-muted))">
{{ content.content }} {{ content.content }}
</p> </p>
@@ -76,7 +78,6 @@
id="section-links" id="section-links"
class="relative flex flex-col gap-3 mb-10 max-w-2xl mx-auto section-anchor" class="relative flex flex-col gap-3 mb-10 max-w-2xl mx-auto section-anchor"
> >
<UiOiseauPose nom="vigneronne" pose="derriere" left="-140px" bottom="-24px" :w="210" />
<a <a
v-for="link in content.links" v-for="link in content.links"
:key="link.href" :key="link.href"
+4 -2
View File
@@ -26,8 +26,11 @@
<!-- Leitmotiv --> <!-- Leitmotiv -->
<div v-if="evtContent?.leitmotiv" class="mt-8 flex flex-col items-center gap-3"> <div v-if="evtContent?.leitmotiv" class="mt-8 flex flex-col items-center gap-3">
<p class="font-display text-xl font-bold" style="color: hsl(var(--color-text))"> <p class="relative font-display text-xl font-bold" style="color: hsl(var(--color-text))">
« {{ evtContent.leitmotiv }} » « {{ evtContent.leitmotiv }} »
<!-- The acrobat on the line of the leitmotiv, before it: by « Format & lieu », the stage
manager stands alone (two birds there would stand at the same height) -->
<UiOiseauPose nom="acrobate" pose="perche" right="calc(100% + 14px)" bottom="0px" :w="72" />
</p> </p>
<p v-if="evtContent?.tagline" class="font-mono text-sm tracking-widest uppercase" style="color: hsl(var(--color-primary))"> <p v-if="evtContent?.tagline" class="font-mono text-sm tracking-widest uppercase" style="color: hsl(var(--color-primary))">
{{ evtContent.tagline }} {{ evtContent.tagline }}
@@ -129,7 +132,6 @@ const evtContent = computed(() => content.value as Record<string, any> | null)
const OISEAUX_CARTE: Record<string, OiseauPlace[]> = { const OISEAUX_CARTE: Record<string, OiseauPlace[]> = {
'Économique': [{ nom: 'jongleur', pose: 'perche', right: '22px', w: 76 }], 'Économique': [{ nom: 'jongleur', pose: 'perche', right: '22px', w: 76 }],
'Ğ(marché)': [{ nom: 'cracheur-de-feu', pose: 'perche', right: '18px', w: 76, ton: 'accent', seuil: 1200 }], 'Ğ(marché)': [{ nom: 'cracheur-de-feu', pose: 'perche', right: '18px', w: 76, ton: 'accent', seuil: 1200 }],
'100 à 200 personnes': [{ nom: 'acrobate', pose: 'perche', right: '14px', w: 72 }],
} }
useSeoPage({ useSeoPage({
+5 -2
View File
@@ -439,7 +439,8 @@ const OISEAUX_PAGE: Record<string, OiseauxPage> = {
'logiciel-libre': { 'logiciel-libre': {
projet: [{ nom: 'cafeine', pose: 'derriere', left: '-130px', bottom: '-30px', w: 200 }], projet: [{ nom: 'cafeine', pose: 'derriere', left: '-130px', bottom: '-30px', w: 200 }],
sections: { sections: {
1: [{ nom: 'debogueur', pose: 'marge-g', ecart: '24px', w: 132 }], // feet on the last line of the steps: the block ends with the step's padding (16 px)
1: [{ nom: 'debogueur', pose: 'marge-g', ecart: '24px', bottom: '16px', w: 132 }],
// sections 2 (and cloud-libre 1 and 5) are wide: 152 px of margin at 1200 px // sections 2 (and cloud-libre 1 and 5) are wide: 152 px of margin at 1200 px
2: [{ nom: 'le-manchot', pose: 'marge-g', ecart: '40px', w: 84 }], 2: [{ nom: 'le-manchot', pose: 'marge-g', ecart: '40px', w: 84 }],
3: [{ nom: 'canard-en-plastique', pose: 'marge-g', ecart: '110px', w: 76 }], 3: [{ nom: 'canard-en-plastique', pose: 'marge-g', ecart: '110px', w: 76 }],
@@ -448,7 +449,9 @@ const OISEAUX_PAGE: Record<string, OiseauxPage> = {
'authentification-wot': { 'authentification-wot': {
projet: [{ nom: 'porteur-de-serveur', pose: 'derriere', left: '-130px', bottom: '-30px', w: 210 }], projet: [{ nom: 'porteur-de-serveur', pose: 'derriere', left: '-130px', bottom: '-30px', w: 210 }],
sections: { sections: {
1: [{ nom: 'toile-de-confiance', pose: 'derriere', left: '-120px', bottom: '-30px', w: 220 }], // the steps have no background: in the margin, on their last line, small and close to the
// text, out of the band shared by the server carrier and the pump
1: [{ nom: 'toile-de-confiance', pose: 'marge-g', ecart: '24px', bottom: '16px', w: 76 }],
// not right under the server carrier and the web of trust: no column of three // not right under the server carrier and the web of trust: no column of three
2: [{ nom: 'cryptographe', pose: 'marge-g', ecart: '96px', w: 84 }], 2: [{ nom: 'cryptographe', pose: 'marge-g', ecart: '96px', w: 84 }],
4: [{ nom: 'pompeur-de-g1', pose: 'marge-g', ecart: '72px', w: 120 }], 4: [{ nom: 'pompeur-de-g1', pose: 'marge-g', ecart: '72px', w: 120 }],
+8 -1
View File
@@ -41,7 +41,14 @@ Ce que fait le code :
- Le curseur touché repart d'en bas et monte en 1,1 s à son cran : numérique 0,75, économique 0,50, - Le curseur touché repart d'en bas et monte en 1,1 s à son cran : numérique 0,75, économique 0,50,
citoyenne 0,667. Il y reste. citoyenne 0,667. Il y reste.
- Le panneau de l'axe s'affiche au même geste. - Le panneau de l'axe s'affiche au même geste.
- Les autres curseurs ne bougent pas. Un curseur jamais touché reste là où il était. - La main ne fait descendre aucun autre curseur :
- un curseur déjà touché reste à son cran ;
- au premier geste, un curseur jamais touché que la rotation faisait descendre s'arrête là où il
est ;
- un curseur jamais touché que la rotation faisait monter finit sa montée, au niveau que la
rotation lui donnait (le cran pour l'axe mis en avant), puis il y reste.
Décision d'orch du 03/11 à 13h10, qui découle de « ne fait pas descendre les autres ».
- Un nouveau survol rejoue la montée. - Un nouveau survol rejoue la montée.
- Avant tout geste, l'animation du 29/09 continue : la rotation et la dérive des curseurs bas. L'axe - Avant tout geste, l'animation du 29/09 continue : la rotation et la dérive des curseurs bas. L'axe
mis en avant monte à son cran, et non plus vers 0,9. C'est une décision d'orch du 03/11 à 05h51, qui mis en avant monte à son cran, et non plus vers 0,9. C'est une décision d'orch du 03/11 à 05h51, qui
+28
View File
@@ -55,3 +55,31 @@ Ce que fait le code depuis :
- Commande : un seul côté par carte. Le cycliste est à gauche de la première, le libraire en grand - Commande : un seul côté par carte. Le cycliste est à gauche de la première, le libraire en grand
derrière la seconde, à droite. La lectrice se tient sur le sac de l'icône. Le cartonnier est parti derrière la seconde, à droite. La lectrice se tient sur le sac de l'icône. Le cartonnier est parti
sur la ligne qui clôt chaque chapitre, l'empileur sur le sommaire du livre. sur la ligne qui clôt chaque chapitre, l'empileur sur le sommaire du livre.
### 03/11/2026, 13h10 : relecture (relayée par la session orch, ce ne sont pas les mots d'Yvv)
La relecture de fb8c71f à 16d6607 conclut « à reprendre », avec cinq constats :
- /economique : la maraîchère et la balance font une paire, aux sols presque à la même hauteur ;
- /numerique/authentification-wot : la toile de confiance est « derrière » un bloc sans fond, et du
texte la traverse ;
- les chapitres : le cartonnier est posé sur un filet que rien ne trace ;
- /evenement : le régisseur et l'acrobate font une paire ;
- le sommaire du livre : trois oiseaux dans une même bande de la marge droite.
Trois règles en découlent :
- pas deux oiseaux de part et d'autre dont les sols sont à moins de 40 px l'un de l'autre ;
- une ligne visible, c'est une bordure, un fond différent de ce qu'il y a dessous, le bas d'une
carte qui a un fond, ou une ligne de texte ;
- « derrière une carte » demande une carte au fond opaque.
Ce que fait le code depuis :
- /economique : la balance sort de derrière le bloc du livre, à gauche, à mi-hauteur.
- Authentification : la toile de confiance est petite, en marge, sur la dernière ligne des étapes.
- Logiciel libre : le débogueur se tient aussi sur la dernière ligne des étapes.
- Chapitres : le filet au-dessus de précédent et suivant est tracé ; le cartonnier s'y pose.
- /evenement : le régisseur reste seul près de « Format & lieu ». L'acrobate se tient au bout de la
ligne du leitmotiv, dans l'en-tête.
- Sommaire du livre : l'empileur est plus loin dans la marge, l'illustratrice dépasse moins de sa
carte.
- Monnaie libre : la vigneronne sort de derrière le haut du bloc de texte, qui a un fond.