feat(glossaire): polish sticky entry flow and aside navigation
All checks were successful
SMOKE / smoke (push) Successful in 18s
CI / build-and-anchors (push) Successful in 48s
CI / build-and-anchors (pull_request) Successful in 48s

This commit is contained in:
2026-03-24 00:29:39 +01:00
parent e39a0c547d
commit 87955adf5d
18 changed files with 3639 additions and 584 deletions

View File

@@ -118,9 +118,10 @@ const constellationCount = relatedEntries.length;
</Fragment>
<section class="dyna-page" data-dyna-page>
<div class="dyna-hero" data-dyna-hero>
<div class="dyna-hero glossary-page-hero" data-dyna-hero>
<p class="dyna-kicker">Parcours du glossaire</p>
<h1>Dynamiques archicratiques</h1>
<p class="dyna-intro">
Les dynamiques archicratiques désignent les processus par lesquels une
régulation se déplace, se ferme, se rigidifie ou se soustrait à sa
@@ -128,12 +129,34 @@ const constellationCount = relatedEntries.length;
dun régime, mais aussi ses dérives, ses opacifications et ses
pathologies.
</p>
<p class="dyna-intro">
Elles décrivent ainsi le versant processuel de larchicratie : non plus
seulement ce qui tient, mais la manière dont cela se transforme,
saltère, sautonomise ou devient de moins en moins exposable à
lépreuve collective.
</p>
<div class="dyna-hero-collapsible">
<div
class="dyna-hero-more"
id="dyna-hero-more"
data-dyna-more
aria-hidden="false"
>
<p class="dyna-intro">
Elles décrivent ainsi le versant processuel de larchicratie : non plus
seulement ce qui tient, mais la manière dont cela se transforme,
saltère, sautonomise ou devient de moins en moins exposable à
lépreuve collective.
</p>
</div>
<button
class="dyna-hero-toggle"
id="dyna-hero-toggle"
data-dyna-more-toggle
type="button"
aria-controls="dyna-hero-more"
aria-expanded="false"
hidden
>
lire la suite
</button>
</div>
</div>
<section class="dyna-section">
@@ -239,7 +262,7 @@ const constellationCount = relatedEntries.length;
{otherEntries.length > 0 && (
<div class="dyna-block">
<h3>Notions et diagnostics liés</h3>
<h3 id="notions-et-diagnostics-lies">Notions et diagnostics liés</h3>
<div class="dyna-cards">
{otherEntries.map((entry) => (
<a class="dyna-card" href={hrefOf(entry)}>
@@ -259,7 +282,7 @@ const constellationCount = relatedEntries.length;
{paradigmEntries.length > 0 && (
<div class="dyna-block">
<h3>Paradigmes mobilisés</h3>
<h3 id="paradigmes-mobilises">Paradigmes mobilisés</h3>
<div class="dyna-cards">
{paradigmEntries.map((entry) => (
<a class="dyna-card" href={hrefOf(entry)}>
@@ -340,95 +363,225 @@ const constellationCount = relatedEntries.length;
<script is:inline>
(() => {
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-dyna-hero]");
if (!body || !root || !hero) return;
const BODY_CLASS = "is-dynamiques-archicratiques-page";
const FOLLOW_ON_CLASS = "dyna-follow-on";
const mqMobile = window.matchMedia("(max-width: 860px)");
body.classList.add(BODY_CLASS);
const setRootVar = (name, value) => {
root.style.setProperty(name, value);
};
const heroHeight = () =>
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
const stripLocalSticky = () => {
document.querySelectorAll(".dyna-section__head").forEach((el) => {
el.classList.remove("is-sticky");
el.removeAttribute("data-sticky-active");
});
};
const applyLocalStickyHeight = () => {
const h = mqMobile.matches ? 0 : heroHeight();
if (typeof window.__archiSetLocalStickyHeight === "function") {
window.__archiSetLocalStickyHeight(h);
} else {
setRootVar("--glossary-local-sticky-h", `${h}px`);
}
};
const syncFollowState = () => {
const boot = () => {
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-dyna-hero]");
const follow = document.getElementById("reading-follow");
if (!follow) {
body.classList.remove(FOLLOW_ON_CLASS);
return;
}
const heroMore = document.getElementById("dyna-hero-more");
const heroToggle = document.getElementById("dyna-hero-toggle");
const followOn =
if (!body || !root || !hero || !follow) return;
const BODY_CLASS = "is-dynamiques-archicratiques-page";
const FOLLOW_ON_CLASS = "dyna-follow-on";
const EXPANDED_CLASS = "dyna-hero-expanded";
const mqMobile = window.matchMedia("(max-width: 860px)");
const AUTO_COLLAPSE_DELTA = 160;
let expandedAtY = null;
let lastScrollY = window.scrollY || 0;
body.classList.add(BODY_CLASS);
const heroHeight = () =>
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
const stripLocalSticky = () => {
document.querySelectorAll(".dyna-section__head").forEach((el) => {
el.classList.remove("is-sticky");
el.removeAttribute("data-sticky-active");
});
};
const computeFollowOn = () =>
!mqMobile.matches &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
body.classList.toggle(FOLLOW_ON_CLASS, followOn);
};
const applyLocalStickyHeight = () => {
const h = mqMobile.matches ? 0 : heroHeight();
const syncAll = () => {
stripLocalSticky();
applyLocalStickyHeight();
syncFollowState();
};
if (typeof window.__archiSetLocalStickyHeight === "function") {
window.__archiSetLocalStickyHeight(h);
} else {
root.style.setProperty("--glossary-local-sticky-h", `${h}px`);
}
};
let raf = 0;
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
requestAnimationFrame(syncAll);
const syncFollowState = () => {
const on = computeFollowOn();
body.classList.toggle(FOLLOW_ON_CLASS, on);
return on;
};
const collapseHero = () => {
if (!body.classList.contains(EXPANDED_CLASS)) return;
body.classList.remove(EXPANDED_CLASS);
expandedAtY = null;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "true");
}
if (heroToggle) {
heroToggle.hidden = false;
heroToggle.setAttribute("aria-expanded", "false");
}
try {
window.__archiUpdateFollow?.();
} catch {}
schedule();
};
const expandHero = () => {
body.classList.add(EXPANDED_CLASS);
expandedAtY = window.scrollY || 0;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "false");
}
if (heroToggle) {
heroToggle.hidden = true;
heroToggle.setAttribute("aria-expanded", "true");
}
try {
window.__archiUpdateFollow?.();
} catch {}
schedule();
};
const syncHeroState = () => {
const followOn = computeFollowOn();
const expanded = body.classList.contains(EXPANDED_CLASS);
const collapsed = followOn && !expanded;
if (!followOn || mqMobile.matches) {
body.classList.remove(EXPANDED_CLASS);
expandedAtY = null;
if (heroMore) {
heroMore.setAttribute("aria-hidden", "false");
}
if (heroToggle) {
heroToggle.hidden = true;
heroToggle.setAttribute("aria-expanded", "false");
}
return;
}
if (heroMore) {
heroMore.setAttribute("aria-hidden", collapsed ? "true" : "false");
}
if (heroToggle) {
heroToggle.hidden = !collapsed;
heroToggle.setAttribute("aria-expanded", expanded ? "true" : "false");
}
};
const maybeAutoCollapseOnScroll = () => {
if (mqMobile.matches) {
lastScrollY = window.scrollY || 0;
return;
}
if (!computeFollowOn()) {
lastScrollY = window.scrollY || 0;
return;
}
if (!body.classList.contains(EXPANDED_CLASS)) {
lastScrollY = window.scrollY || 0;
return;
}
if (expandedAtY == null) {
lastScrollY = window.scrollY || 0;
return;
}
const currentY = window.scrollY || 0;
const scrollingDown = currentY > lastScrollY;
const delta = currentY - expandedAtY;
if (scrollingDown && delta >= AUTO_COLLAPSE_DELTA) {
collapseHero();
}
lastScrollY = currentY;
};
const syncAll = () => {
stripLocalSticky();
syncFollowState();
syncHeroState();
applyLocalStickyHeight();
};
let raf = 0;
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
requestAnimationFrame(syncAll);
});
};
heroToggle?.addEventListener("click", () => {
expandHero();
});
const onScroll = () => {
maybeAutoCollapseOnScroll();
schedule();
};
const followObserver = new MutationObserver(schedule);
followObserver.observe(follow, {
attributes: true,
attributeFilter: ["class", "style", "aria-hidden"],
subtree: false,
});
const heroResizeObserver =
typeof ResizeObserver !== "undefined"
? new ResizeObserver(schedule)
: null;
heroResizeObserver?.observe(hero);
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", schedule);
window.addEventListener("pageshow", schedule);
if (document.fonts?.ready) {
document.fonts.ready.then(schedule).catch(() => {});
}
if (mqMobile.addEventListener) {
mqMobile.addEventListener("change", schedule);
} else if (mqMobile.addListener) {
mqMobile.addListener(schedule);
}
schedule();
};
const heroResizeObserver =
typeof ResizeObserver !== "undefined"
? new ResizeObserver(schedule)
: null;
heroResizeObserver?.observe(hero);
window.addEventListener("scroll", schedule, { passive: true });
window.addEventListener("resize", schedule);
window.addEventListener("pageshow", schedule);
if (document.fonts?.ready) {
document.fonts.ready.then(schedule).catch(() => {});
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
if (mqMobile.addEventListener) {
mqMobile.addEventListener("change", schedule);
} else if (mqMobile.addListener) {
mqMobile.addListener(schedule);
}
schedule();
})();
</script>
</GlossaryLayout>
@@ -451,13 +604,17 @@ const constellationCount = relatedEntries.length;
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: grid;
row-gap: 14px;
transition:
margin-bottom 180ms ease,
border-radius 180ms ease;
border-radius 180ms ease,
padding 180ms ease,
row-gap 180ms ease;
}
.dyna-kicker{
margin: 0 0 10px;
margin: 0;
font-size: 12px;
letter-spacing: .08em;
text-transform: uppercase;
@@ -465,23 +622,75 @@ const constellationCount = relatedEntries.length;
}
.dyna-hero h1{
margin: 0 0 14px;
margin: 0;
font-size: clamp(2.2rem, 4vw, 3.15rem);
line-height: 1.02;
letter-spacing: -.04em;
font-weight: 850;
transition: font-size 180ms ease;
}
.dyna-intro{
max-width: 76ch;
margin: 0;
max-width: 72ch;
font-size: 1.04rem;
line-height: 1.55;
opacity: .94;
transition:
font-size 180ms ease,
line-height 180ms ease,
max-width 180ms ease;
}
.dyna-intro + .dyna-intro{
margin-top: 14px;
.dyna-hero-collapsible{
display: grid;
row-gap: 6px;
}
.dyna-hero-more{
display: grid;
row-gap: 14px;
max-height: 18rem;
overflow: hidden;
opacity: 1;
transition:
max-height 220ms ease,
opacity 180ms ease;
}
.dyna-hero-toggle{
display: none;
align-self: flex-start;
width: fit-content;
margin: 0;
padding: 0;
border: 0;
background: transparent;
color: inherit;
font: inherit;
font-size: 11px;
line-height: 1.2;
letter-spacing: .01em;
text-transform: none;
opacity: .56;
cursor: pointer;
text-decoration: underline;
text-underline-offset: .12em;
text-decoration-thickness: 1px;
}
.dyna-hero-toggle:hover{
opacity: .84;
}
.dyna-hero-toggle:focus-visible{
outline: 2px solid rgba(0,217,255,0.24);
outline-offset: 4px;
border-radius: 4px;
}
.dyna-hero-toggle[hidden]{
display: none !important;
}
.dyna-section{
@@ -699,10 +908,33 @@ const constellationCount = relatedEntries.length;
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on .dyna-hero){
margin-bottom: 0;
padding: 12px 16px 14px;
row-gap: 10px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on .dyna-hero h1){
font-size: clamp(1.9rem, 3.2vw, 2.55rem);
}
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on .dyna-intro){
max-width: 68ch;
font-size: .98rem;
line-height: 1.48;
}
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on:not(.dyna-hero-expanded) .dyna-hero-more){
max-height: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on:not(.dyna-hero-expanded) .dyna-hero-toggle){
display: inline-flex;
}
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on #reading-follow .reading-follow__inner){
border-top-left-radius: 0;
border-top-right-radius: 0;
@@ -726,11 +958,29 @@ const constellationCount = relatedEntries.length;
position: static;
border-radius: 22px;
margin-bottom: 20px;
padding: 14px 14px 16px;
row-gap: 12px;
}
.dyna-intro{
max-width: none;
}
.dyna-hero-more{
max-height: none;
opacity: 1;
overflow: visible;
}
.dyna-hero-toggle{
display: none !important;
}
:global(body.is-dynamiques-archicratiques-page.dyna-follow-on .dyna-hero){
border-radius: 22px;
margin-bottom: 20px;
padding: 14px 14px 16px;
row-gap: 12px;
}
}