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

@@ -112,20 +112,43 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
</Fragment>
<section class="archi-page" data-archi-page>
<div class="archi-hero" data-archi-hero>
<div class="archi-hero glossary-page-hero" data-archi-hero>
<p class="archi-kicker">Topologie archicratique</p>
<h1>Archicrations</h1>
<p class="archi-intro">
Cette page rassemble les principales formes darchicration distinguées
dans le glossaire. Elle propose une vue densemble des grands régimes de
co-viabilité à partir desquels un collectif se stabilise, se transmet,
se transforme ou se recompose.
</p>
<p class="archi-intro">
Les catégories proposées ci-dessous ne valent pas comme cases closes,
mais comme repères de lecture permettant de situer les différentes
topologies de régulation et leurs articulations.
</p>
<div class="archi-hero-collapsible">
<div
class="archi-hero-more"
id="archi-hero-more"
data-archi-more
aria-hidden="false"
>
<p class="archi-intro">
Les catégories proposées ci-dessous ne valent pas comme cases closes,
mais comme repères de lecture permettant de situer les différentes
topologies de régulation et leurs articulations.
</p>
</div>
<button
class="archi-hero-toggle"
id="archi-hero-toggle"
data-archi-more-toggle
type="button"
aria-controls="archi-hero-more"
aria-expanded="false"
hidden
>
lire la suite
</button>
</div>
</div>
{sections.map((section) => (
@@ -181,12 +204,20 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-archi-hero]");
const follow = document.getElementById("reading-follow");
const heroMore = document.getElementById("archi-hero-more");
const heroToggle = document.getElementById("archi-hero-toggle");
if (!body || !root || !hero) return;
if (!body || !root || !hero || !follow) return;
const BODY_CLASS = "is-archicrations-page";
const FOLLOW_ON_CLASS = "archi-follow-on";
const EXPANDED_CLASS = "archi-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);
@@ -200,6 +231,12 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
});
};
const computeFollowOn = () =>
!mqMobile.matches &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const applyLocalStickyHeight = () => {
const h = mqMobile.matches ? 0 : heroHeight();
@@ -211,22 +248,121 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
};
const syncFollowState = () => {
const follow = document.getElementById("reading-follow");
const on = computeFollowOn();
body.classList.toggle(FOLLOW_ON_CLASS, on);
return on;
};
const followOn =
!mqMobile.matches &&
!!follow &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const collapseHero = () => {
if (!body.classList.contains(EXPANDED_CLASS)) return;
body.classList.toggle(FOLLOW_ON_CLASS, followOn);
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();
applyLocalStickyHeight();
syncFollowState();
syncHeroState();
applyLocalStickyHeight();
};
let raf = 0;
@@ -238,13 +374,17 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
});
};
const follow = document.getElementById("reading-follow");
const followObserver =
follow
? new MutationObserver(schedule)
: null;
heroToggle?.addEventListener("click", () => {
expandHero();
});
followObserver?.observe(follow, {
const onScroll = () => {
maybeAutoCollapseOnScroll();
schedule();
};
const followObserver = new MutationObserver(schedule);
followObserver.observe(follow, {
attributes: true,
attributeFilter: ["class", "style", "aria-hidden"],
subtree: false,
@@ -257,6 +397,7 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
heroResizeObserver?.observe(hero);
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", schedule);
window.addEventListener("pageshow", schedule);
@@ -294,30 +435,23 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
margin: 0 0 24px;
padding: 18px 18px 20px;
border: 1px solid rgba(127,127,127,0.18);
border-radius: 28px 28px 28px 28px;
border-radius: 28px;
background:
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
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;
}
:global(body.is-archicrations-page:not(.archi-follow-on) .archi-hero){
margin-bottom: 24px;
border-radius: 28px 28px 28px 28px;
}
:global(body.is-archicrations-page.archi-follow-on .archi-hero){
margin-bottom: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
margin-bottom 180ms ease,
border-radius 180ms ease,
padding 180ms ease,
row-gap 180ms ease;
}
.archi-kicker{
margin: 0 0 10px;
margin: 0;
font-size: 12px;
letter-spacing: .08em;
text-transform: uppercase;
@@ -325,23 +459,75 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
}
.archi-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;
}
.archi-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;
}
.archi-intro + .archi-intro{
margin-top: 14px;
.archi-hero-collapsible{
display: grid;
row-gap: 6px;
}
.archi-hero-more{
display: grid;
row-gap: 14px;
max-height: 18rem;
overflow: hidden;
opacity: 1;
transition:
max-height 220ms ease,
opacity 180ms ease;
}
.archi-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;
}
.archi-hero-toggle:hover{
opacity: .84;
}
.archi-hero-toggle:focus-visible{
outline: 2px solid rgba(0,217,255,0.24);
outline-offset: 4px;
border-radius: 4px;
}
.archi-hero-toggle[hidden]{
display: none !important;
}
.archi-section{
@@ -486,10 +672,33 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
:global(body.is-archicrations-page.archi-follow-on .archi-hero){
margin-bottom: 0;
padding: 12px 16px 14px;
row-gap: 10px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
:global(body.is-archicrations-page.archi-follow-on .archi-hero h1){
font-size: clamp(1.9rem, 3.2vw, 2.55rem);
}
:global(body.is-archicrations-page.archi-follow-on .archi-intro){
max-width: 68ch;
font-size: .98rem;
line-height: 1.48;
}
:global(body.is-archicrations-page.archi-follow-on:not(.archi-hero-expanded) .archi-hero-more){
max-height: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
:global(body.is-archicrations-page.archi-follow-on:not(.archi-hero-expanded) .archi-hero-toggle){
display: inline-flex;
}
:global(body.is-archicrations-page.archi-follow-on #reading-follow .reading-follow__inner){
border-top-left-radius: 0;
border-top-right-radius: 0;
@@ -513,11 +722,29 @@ const totalCount = sections.reduce((sum, section) => sum + section.items.length,
position: static;
border-radius: 22px;
margin-bottom: 20px;
padding: 14px 14px 16px;
row-gap: 12px;
}
.archi-intro{
max-width: none;
}
.archi-hero-more{
max-height: none;
opacity: 1;
overflow: visible;
}
.archi-hero-toggle{
display: none !important;
}
:global(body.is-archicrations-page.archi-follow-on .archi-hero){
border-radius: 22px;
margin-bottom: 20px;
padding: 14px 14px 16px;
row-gap: 12px;
}
}