identité : le kit du studio DA intégré, les 61 oiseaux posés sur la page

Copie du kit kit-site (empreinte vérifiée) : logotype dans l'en-tête, signe §,
favicons et icônes, og-default (?v=3), frise du pied de page, 61 oiseaux.
Les shadoks positionnés à la main (HomeShadoks et les règles des 10 pages)
cèdent la place à UiOiseauPose : chaque oiseau se pose sur une arête réelle
(perché, marge gauche ou droite, derrière une carte, libre), currentColor,
largeur 72 px au moins, opacité 0,18-0,28 près du texte et 0,10-0,14 derrière,
jamais deux fois sur une page, calligraphe et typographe jamais en miroir.
Sous 768 px, un oiseau par page au plus ; seuls ceux derrière une carte
dérivent un peu, et plus du tout en mouvement réduit.

Consignes d'Yvv du 03/11 inscrites dans docs/consignes-yvv.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-03 06:39:34 +01:00
co-authored by Claude Opus 5.5
parent cdd3fa57de
commit d2bc0d9e46
99 changed files with 889 additions and 3413 deletions
+142
View File
@@ -0,0 +1,142 @@
<script setup lang="ts">
// A bird of the librodrome placed on its block (composition of 03/11, docs/consignes-yvv.md):
// it stands on a real edge of the page, next to the content its trade is about.
// The host block is the positioning context: position relative, overflow visible.
//
// Poses
// perche on the block's top edge (bottom: 100%), x with left / right
// marge-g beside the block, on its left; feet at the block's bottom, raised by `bottom`
// marge-d the same, on its right
// derriere large and pale, behind the block (z-index -1): only what sticks out shows.
// Needs a stacking context above the block (isolation: isolate) and a block
// that creates none itself (no z-index, transform, opacity, filter).
// libre placed only by left / right / top / bottom (an empty area next to its block)
//
// Birds in the margin, behind a block or free need room around the column: they show from
// 1200 px wide, the perched ones from 768 px (`seuil` overrides); under 768 px, only a bird
// marked `mobile`.
//
// Drawing rules of the studio DA (kit README, §2.5): one colour (currentColor), proportions
// intact and never cropped, 72 px wide at least, opacity 0.18-0.28 next to text and 0.10-0.14
// large behind a card, mirror allowed except for the calligraphe and the typographe.
// Birds standing on an edge do not move; only the one behind a card drifts a little (Yvv, 03/11).
import { OISEAUX } from '~/utils/oiseaux'
type Pose = 'perche' | 'marge-g' | 'marge-d' | 'derriere' | 'libre'
const props = withDefaults(defineProps<{
nom: string
pose?: Pose
/** Width in px: S 72-90, M 108-156, L 200-250 */
w?: number
left?: string
right?: string
top?: string
bottom?: string
/** Gap between the block and a bird in the margin */
ecart?: string
retourne?: boolean
/** Shown under 768 px (one bird per page at most) */
mobile?: boolean
/** Shown under 768 px only: the phone place of a bird placed elsewhere on wider screens */
mobileSeul?: boolean
/** Narrowest window (px) where the bird shows: 1200 by default for the margin, behind
* and free poses, 768 for a perched bird */
seuil?: 768 | 1200
ton?: 'primary' | 'accent'
op?: number
}>(), { pose: 'perche', w: 80, ecart: '2.5rem', ton: 'primary' })
/** These two write and compose from left to right: never mirrored */
const NO_MIRROR = new Set(['calligraphe', 'typographe'])
const sol = computed(() => OISEAUX[props.nom]?.sol ?? 5)
const style = computed(() => {
const s: Record<string, string> = { 'width': `${Math.max(72, props.w)}px`, '--sol': `${sol.value}%` }
if (props.pose === 'perche') s.bottom = '100%'
if (props.pose === 'marge-g') s.right = `calc(100% + ${props.ecart})`
if (props.pose === 'marge-d') s.left = `calc(100% + ${props.ecart})`
if (props.pose === 'marge-g' || props.pose === 'marge-d') s.bottom = '0px'
for (const k of ['left', 'right', 'top', 'bottom'] as const) {
if (props[k] !== undefined) s[k] = props[k]!
}
if (props.op !== undefined) s.opacity = String(props.op)
return s
})
const classes = computed(() => [
'oiseau-pose',
`oiseau-pose--${props.pose}`,
{
'oiseau-pose--retourne': props.retourne && !NO_MIRROR.has(props.nom),
'oiseau-pose--mobile': props.mobile || props.mobileSeul,
'oiseau-pose--mobile-seul': props.mobileSeul,
'oiseau-pose--large': (props.seuil ?? (props.pose === 'perche' ? 768 : 1200)) >= 1200,
'oiseau-pose--accent': props.ton === 'accent',
},
])
</script>
<template>
<UiOiseau :nom="nom" :class="classes" :style="style" :data-oiseau="nom" :data-pose="pose" />
</template>
<style scoped>
.oiseau-pose {
position: absolute;
z-index: 1;
pointer-events: none;
color: hsl(var(--color-primary));
opacity: 0.24;
/* The drawing's ground line lands on the edge */
transform: translateY(var(--sol));
}
.oiseau-pose--accent {
color: hsl(var(--color-accent));
}
.oiseau-pose--perche {
opacity: 0.28;
}
.oiseau-pose--derriere {
z-index: -1;
opacity: 0.12;
animation: oiseau-derive 12s ease-in-out infinite alternate;
}
.oiseau-pose--retourne {
scale: -1 1;
}
@keyframes oiseau-derive {
from { translate: 0 0; }
to { translate: 0 -6px; }
}
@media (max-width: 1199px) {
.oiseau-pose--large {
display: none;
}
}
@media (min-width: 768px) {
.oiseau-pose--mobile-seul {
display: none;
}
}
@media (max-width: 767px) {
.oiseau-pose:not(.oiseau-pose--mobile) {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.oiseau-pose--derriere {
animation: none;
}
}
</style>