Compare commits

...

8 Commits

Author SHA1 Message Date
c9ed43c9e0 Polish glossary complete index compact sticky behavior
All checks were successful
SMOKE / smoke (push) Successful in 9s
CI / build-and-anchors (push) Successful in 49s
CI / build-and-anchors (pull_request) Successful in 42s
2026-05-07 19:18:25 +02:00
3439e2aaf9 Merge pull request 'Polish glossary home sticky hero on mobile and tablet' (#358) from polish/glossary-home-hero-sticky-mobile into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 21s
CI / build-and-anchors (push) Successful in 36s
SMOKE / smoke (push) Successful in 5s
Deploy staging+live (annotations) / deploy (push) Successful in 10m53s
Reviewed-on: #358
2026-05-07 12:56:01 +00:00
75fd6de293 Polish glossary home sticky hero on mobile and tablet
All checks were successful
SMOKE / smoke (push) Successful in 11s
CI / build-and-anchors (push) Successful in 42s
CI / build-and-anchors (pull_request) Successful in 40s
2026-05-07 14:52:12 +02:00
7551c91f37 Merge pull request 'Polish glossary home responsive aside and map density' (#357) from polish/glossary-home-mobile-and-map-density into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 26s
CI / build-and-anchors (push) Successful in 48s
SMOKE / smoke (push) Successful in 5s
Deploy staging+live (annotations) / deploy (push) Successful in 10m59s
Reviewed-on: #357
2026-05-07 12:04:59 +00:00
40ab10b8e8 Polish glossary home responsive aside and map density
All checks were successful
SMOKE / smoke (push) Successful in 15s
CI / build-and-anchors (push) Successful in 43s
CI / build-and-anchors (pull_request) Successful in 48s
2026-05-07 13:59:05 +02:00
4bab188df7 Merge pull request 'Fix glossary home hero follow truncation' (#356) from fix/glossary-home-hero-follow-no-truncation into main
All checks were successful
CI / build-and-anchors (push) Successful in 44s
Proposer Apply (Queue) / apply-proposer (push) Successful in 26s
SMOKE / smoke (push) Successful in 11s
Deploy staging+live (annotations) / deploy (push) Successful in 9m16s
Reviewed-on: #356
2026-05-06 11:50:48 +00:00
aaed642cec Fix glossary home hero follow truncation
All checks were successful
SMOKE / smoke (push) Successful in 4s
CI / build-and-anchors (push) Successful in 38s
CI / build-and-anchors (pull_request) Successful in 40s
2026-05-06 13:48:23 +02:00
b4f2de438e Merge pull request 'Stabilize commencer editorial reveal spacing' (#355) from fix/commencer-reveal-css-stabilization into main
All checks were successful
Proposer Apply (Queue) / apply-proposer (push) Successful in 24s
CI / build-and-anchors (push) Successful in 42s
SMOKE / smoke (push) Successful in 8s
Deploy staging+live (annotations) / deploy (push) Successful in 9m26s
Reviewed-on: #355
2026-05-06 08:26:22 +00:00
6 changed files with 1395 additions and 205 deletions

View File

@@ -192,120 +192,47 @@ const {
word-break: break-word;
}
@media (max-width: 860px){
.glossary-home-aside{
gap: 10px;
}
.glossary-home-aside__block{
border-radius: 14px;
}
.glossary-home-aside__block--intro{
padding: 12px;
}
.glossary-home-aside__title{
font-size: 19px;
line-height: 1.18;
}
.glossary-home-aside__meta{
margin-top: 6px;
font-size: 12px;
line-height: 1.32;
}
.glossary-home-aside__pills{
gap: 6px;
margin-top: 9px;
}
.glossary-home-aside__pill{
padding: 4px 9px;
font-size: 12px;
line-height: 1.28;
}
.glossary-home-aside__summary{
padding: 12px;
}
.glossary-home-aside__heading{
font-size: 17px;
line-height: 1.2;
}
.glossary-home-aside__panel{
padding: 0 12px 12px;
}
.glossary-home-aside__list li{
margin: 5px 0;
}
.glossary-home-aside__list a{
font-size: 14px;
line-height: 1.34;
}
.glossary-home-aside__disclosure:not([open]) .glossary-home-aside__panel{
display: none;
}
}
@media (max-width: 860px){
.glossary-home-aside__disclosure{
background: rgba(127,127,127,0.045);
}
.glossary-home-aside__disclosure[open] .glossary-home-aside__summary{
border-bottom: 1px solid rgba(127,127,127,0.12);
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
@media (max-width: 980px){
.glossary-home-aside{
gap: 8px;
margin-bottom: 12px;
}
.glossary-home-aside__block{
border-radius: 12px;
border-radius: 13px;
}
.glossary-home-aside__block--intro{
padding: 10px 11px;
padding: 10px 12px;
}
.glossary-home-aside__title{
font-size: 16px;
line-height: 1.14;
font-size: 15px;
line-height: 1.15;
}
.glossary-home-aside__meta{
font-size: 11px;
line-height: 1.26;
margin-top: 5px;
display: none;
}
.glossary-home-aside__pills{
gap: 5px;
margin-top: 8px;
margin-top: 7px;
}
.glossary-home-aside__pill{
padding: 3px 8px;
font-size: 11px;
line-height: 1.2;
line-height: 1.18;
}
.glossary-home-aside__summary{
padding: 10px 11px;
padding: 9px 11px;
}
.glossary-home-aside__heading{
font-size: 15px;
line-height: 1.16;
font-size: 14px;
line-height: 1.15;
}
.glossary-home-aside__panel{
@@ -320,9 +247,80 @@ const {
font-size: 13px;
line-height: 1.28;
}
.glossary-home-aside__disclosure:not([open]) .glossary-home-aside__panel{
display: none;
}
}
@media (min-width: 861px){
@media (max-width: 980px){
.glossary-home-aside__disclosure{
background: rgba(127,127,127,0.045);
}
.glossary-home-aside__disclosure[open] .glossary-home-aside__summary{
border-bottom: 1px solid rgba(127,127,127,0.12);
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
.glossary-home-aside{
gap: 7px;
margin-bottom: 10px;
}
.glossary-home-aside__block{
border-radius: 12px;
}
.glossary-home-aside__block--intro{
padding: 9px 10px;
}
.glossary-home-aside__title{
font-size: 14px;
line-height: 1.12;
}
.glossary-home-aside__meta{
display: none;
}
.glossary-home-aside__pills{
gap: 4px;
margin-top: 6px;
}
.glossary-home-aside__pill{
padding: 2px 7px;
font-size: 10.5px;
line-height: 1.16;
}
.glossary-home-aside__summary{
padding: 8px 10px;
}
.glossary-home-aside__heading{
font-size: 13px;
line-height: 1.12;
}
.glossary-home-aside__panel{
padding: 0 10px 9px;
}
.glossary-home-aside__list li{
margin: 3px 0;
}
.glossary-home-aside__list a{
font-size: 12px;
line-height: 1.22;
}
}
@media (min-width: 981px){
.glossary-home-aside__summary{
cursor: default;
}
@@ -346,28 +344,32 @@ const {
<script is:inline>
(() => {
let wasCompact = null;
const syncMobileDisclosure = () => {
const mobile = window.matchMedia("(max-width: 860px)").matches;
const stackedLayout = window.matchMedia("(max-width: 980px)").matches;
const smallLandscape = window.matchMedia(
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
).matches;
const compact = mobile || smallLandscape;
const compact = stackedLayout || smallLandscape;
const enteringCompact = compact && wasCompact !== true;
document
.querySelectorAll(".glossary-home-aside__disclosure")
.forEach((el, index) => {
.forEach((el) => {
if (!(el instanceof HTMLDetailsElement)) return;
if (compact) {
if (!el.dataset.mobileInit) {
el.open = index === 0;
el.dataset.mobileInit = "true";
if (enteringCompact) {
el.open = false;
}
} else {
el.open = true;
}
});
wasCompact = compact;
};
if (document.readyState === "loading") {

View File

@@ -457,4 +457,292 @@ const {
line-height: 1.08 !important;
}
}
/* =========================================================
Glossaire home — états du hero sticky
========================================================= */
/*
Principe :
- le follow peut respirer sans ellipsis brutal ;
- lintro reste strictement clampée en mode collapsed ;
- lintro ne redevient complète quen mode expanded ;
- mobile/tablette <= 860px reste neutralisé plus haut.
*/
.glossary-hero-follow{
height: auto;
max-height: none;
max-width: min(100%, 34ch);
overflow: visible;
white-space: normal;
text-overflow: clip;
line-height: 1.08;
}
:global(body.glossary-home-follow-on) .glossary-hero{
min-height: auto;
height: auto;
}
:global(body.glossary-home-follow-on) .glossary-hero h1{
white-space: normal;
overflow: visible;
text-overflow: clip;
}
/*
État collapsed :
lintro DOIT rester compactée. Cette règle doit gagner contre
les anciennes règles anti-troncature du follow.
*/
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
max-height: calc(2 * 1.34em);
overflow: hidden;
white-space: normal;
text-overflow: clip;
}
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero__toggle{
display: inline-flex;
margin-top: 2px;
align-self: start;
}
/*
État expanded :
lutilisateur a explicitement demandé à lire la suite,
donc lintro redevient complète.
*/
:global(body.glossary-home-hero-expanded) .glossary-hero p#glossary-hero-intro{
display: block;
-webkit-line-clamp: unset;
line-clamp: unset;
-webkit-box-orient: unset;
max-height: none;
overflow: visible;
white-space: normal;
text-overflow: clip;
}
:global(body.glossary-home-hero-expanded) .glossary-hero__toggle{
display: none !important;
}
@media (min-width: 861px) and (max-width: 1240px){
.glossary-hero-follow{
max-width: min(100%, 36ch);
font-size: clamp(1.55rem, 3.1vw, 2.05rem);
line-height: 1.08;
}
}
/* =========================================================
Glossaire home — sticky compact mobile/tablette avec H2 local
========================================================= */
@media (max-width: 980px){
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero{
position: sticky !important;
top: calc(var(--sticky-header-h, 0px) + 8px) !important;
z-index: 8 !important;
transform: none !important;
overflow: hidden !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
padding: 8px 10px 9px !important;
row-gap: 4px !important;
border-radius: 16px !important;
margin-bottom: 10px !important;
box-shadow: 0 12px 30px rgba(0,0,0,.22) !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-kicker{
font-size: 9px !important;
line-height: 1.05 !important;
letter-spacing: .11em !important;
opacity: .72 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero h1{
font-size: clamp(1.35rem, 5.1vw, 1.72rem) !important;
line-height: 1.02 !important;
letter-spacing: -.03em !important;
white-space: normal !important;
overflow: visible !important;
text-overflow: clip !important;
max-width: 100% !important;
margin: 0 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
display: -webkit-box !important;
-webkit-box-orient: vertical !important;
-webkit-line-clamp: 2 !important;
line-clamp: 2 !important;
max-height: calc(2 * 1.12em) !important;
overflow: hidden !important;
white-space: normal !important;
text-overflow: clip !important;
font-size: .72rem !important;
line-height: 1.12 !important;
opacity: .78 !important;
margin: 0 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero__toggle{
display: none !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero-follow{
display: none !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-height: 0 !important;
margin-top: 4px !important;
padding-top: 6px !important;
border-top: 1px solid rgba(127,127,127,.18) !important;
opacity: .98 !important;
transform: none !important;
filter: none !important;
white-space: normal !important;
overflow: hidden !important;
text-overflow: clip !important;
color: inherit !important;
}
}
@media (min-width: 761px) and (max-width: 980px){
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
padding: 10px 14px 11px !important;
row-gap: 5px !important;
border-radius: 18px !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero h1{
font-size: clamp(1.65rem, 3.4vw, 2.1rem) !important;
line-height: 1.02 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
font-size: .82rem !important;
line-height: 1.18 !important;
max-height: calc(2 * 1.18em) !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
margin-top: 5px !important;
padding-top: 7px !important;
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
top: calc(var(--sticky-header-h, 0px) + 6px) !important;
padding: 6px 9px 7px !important;
row-gap: 3px !important;
border-radius: 13px !important;
margin-bottom: 8px !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-kicker{
font-size: 8px !important;
line-height: 1 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero h1{
font-size: clamp(1.08rem, 3.2vw, 1.34rem) !important;
line-height: 1 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
-webkit-line-clamp: 1 !important;
line-clamp: 1 !important;
max-height: 1.08em !important;
font-size: .64rem !important;
line-height: 1.08 !important;
opacity: .72 !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
margin-top: 3px !important;
padding-top: 4px !important;
font-size: .86rem !important;
line-height: 1.04 !important;
}
}
/* =========================================================
Glossaire home — polish premium fluidité sticky
========================================================= */
@media (max-width: 980px){
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero{
transition:
padding 180ms ease,
border-radius 180ms ease,
box-shadow 180ms ease,
background 180ms ease,
margin-bottom 180ms ease;
will-change: padding, border-radius, box-shadow;
backface-visibility: hidden;
transform: translateZ(0) !important;
background: rgba(0,0,0,.92) !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero h1,
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-kicker,
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero p#glossary-hero-intro{
transition:
font-size 180ms ease,
line-height 180ms ease,
opacity 180ms ease,
max-height 180ms ease;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero-follow{
display: block !important;
max-height: 0 !important;
margin-top: 0 !important;
padding-top: 0 !important;
border-top: 0 !important;
opacity: 0 !important;
visibility: hidden !important;
overflow: hidden !important;
transform: translateY(-4px) !important;
transition:
max-height 180ms ease,
opacity 180ms ease,
transform 180ms ease,
padding-top 180ms ease,
margin-top 180ms ease;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
max-height: 3.2em !important;
opacity: .98 !important;
visibility: visible !important;
transform: translateY(0) !important;
border-top: 1px solid rgba(127,127,127,.18) !important;
}
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
background: rgba(0,0,0,.96) !important;
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
max-height: 2.4em !important;
}
}
</style>

View File

@@ -5,6 +5,7 @@ interface Props {
sectionHeadSelector?: string;
mobileBreakpoint?: number;
autoCollapseDelta?: number;
compactSticky?: boolean;
}
const {
@@ -13,12 +14,13 @@ const {
sectionHeadSelector = ".glossary-portal-section__head",
mobileBreakpoint = 860,
autoCollapseDelta = 160,
compactSticky = false,
} = Astro.props;
---
<script
is:inline
define:vars={{ heroMoreId, heroToggleId, sectionHeadSelector, mobileBreakpoint, autoCollapseDelta }}
define:vars={{ heroMoreId, heroToggleId, sectionHeadSelector, mobileBreakpoint, autoCollapseDelta, compactSticky }}
>
(() => {
const boot = () => {
@@ -53,6 +55,14 @@ const {
const isCompactViewport = () =>
mqMobile.matches || mqSmallLandscape.matches;
const keepPortalPremiumOnCompact = () =>
body.classList.contains("is-index-complet-page");
const compactShouldDisablePortal = () =>
isCompactViewport() && !keepPortalPremiumOnCompact();
const compactStickyEnabled = () => Boolean(compactSticky);
const stripLocalSticky = () => {
document.querySelectorAll(sectionHeadSelector).forEach((el) => {
el.classList.remove("is-sticky");
@@ -69,13 +79,13 @@ const {
};
const computeFollowOn = () =>
!isCompactViewport() &&
(!isCompactViewport() || compactStickyEnabled()) &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const computeCondensed = () => {
if (isCompactViewport()) return false;
if (isCompactViewport() && !compactStickyEnabled()) return false;
const heroRect = hero.getBoundingClientRect();
const stickyTop = readStickyTop();
@@ -89,7 +99,7 @@ const {
const PIN_EPS = 3;
const isHeroPinned = () => {
if (isCompactViewport()) return false;
if (isCompactViewport() && !compactStickyEnabled()) return false;
const rect = hero.getBoundingClientRect();
const stickyTop = readStickyTop();
@@ -104,7 +114,16 @@ const {
};
const applyLocalStickyHeight = () => {
const h = isHeroPinned() ? measureHeroHeight() : 0;
/*
Stabilisation premium :
une fois le hero condensé, on réserve sa hauteur réelle.
Cela évite les oscillations dun frame où isHeroPinned()
peut repasser brièvement à false pendant le scroll.
*/
const shouldReserveHero =
body.classList.contains(CONDENSED_CLASS) || isHeroPinned();
const h = shouldReserveHero ? measureHeroHeight() : 0;
if (h === lastHeroHeight) return;
lastHeroHeight = h;
@@ -183,7 +202,7 @@ const {
const expanded = body.classList.contains(EXPANDED_CLASS);
const collapsed = condensed && !expanded;
if (isCompactViewport() || !condensed) {
if ((isCompactViewport() && !compactStickyEnabled()) || !condensed) {
body.classList.remove(EXPANDED_CLASS);
expandedAtY = null;
@@ -210,7 +229,7 @@ const {
};
const maybeAutoCollapseOnScroll = () => {
if (isCompactViewport()) {
if (isCompactViewport() && !compactStickyEnabled()) {
lastScrollY = window.scrollY || 0;
return;
}
@@ -239,7 +258,7 @@ const {
const syncAll = () => {
stripLocalSticky();
if (isCompactViewport()) {
if (isCompactViewport() && !compactStickyEnabled()) {
body.classList.remove(FOLLOW_ON_CLASS);
body.classList.remove(CONDENSED_CLASS);
body.classList.remove(EXPANDED_CLASS);
@@ -422,66 +441,134 @@ const {
}
@media (max-width: 860px){
:global(body.is-glossary-portal-page #reading-follow),
:global(body.is-glossary-portal-page #reading-follow .reading-follow__inner){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow),
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
:global(body.is-glossary-portal-page){
:global(body.is-glossary-portal-page:not(.is-index-complet-page)){
--followbar-h: 0px !important;
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
}
:global(body.is-glossary-portal-page .glossary-portal-hero){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero){
margin-bottom: var(--portal-hero-margin-bottom, 18px);
border-radius: 20px !important;
box-shadow: none !important;
}
:global(body.is-glossary-portal-page .glossary-portal-hero__more){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__more){
max-height: none !important;
opacity: 1 !important;
overflow: visible !important;
pointer-events: auto !important;
}
:global(body.is-glossary-portal-page .glossary-portal-hero__toggle){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__toggle){
display: none !important;
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
:global(body.is-glossary-portal-page #reading-follow),
:global(body.is-glossary-portal-page #reading-follow .reading-follow__inner){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow),
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
:global(body.is-glossary-portal-page){
:global(body.is-glossary-portal-page:not(.is-index-complet-page)){
--followbar-h: 0px !important;
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
}
:global(body.is-glossary-portal-page .glossary-portal-hero){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero){
margin-bottom: var(--portal-hero-margin-bottom, 12px);
border-radius: 16px !important;
box-shadow: none !important;
}
:global(body.is-glossary-portal-page .glossary-portal-hero__more){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__more){
max-height: none !important;
opacity: 1 !important;
overflow: visible !important;
pointer-events: auto !important;
}
:global(body.is-glossary-portal-page .glossary-portal-hero__toggle){
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__toggle){
display: none !important;
}
}
/* =========================================================
Index complet — exception premium compacte portal
---------------------------------------------------------
Les portails ordinaires restent neutralisés en compact.
Lindex complet, lui, conserve la pile :
hero sticky → reading-follow → lettres flottantes.
========================================================= */
@media (max-width: 980px){
:global(body.is-index-complet-page[data-edition-key="glossaire"][data-sticky-mode="glossary-portal"]) .glossary-portal-hero,
:global(body.is-index-complet-page[data-edition-key="glossaire"][data-sticky-mode="glossary-portal"]) .glossary-page-hero{
position: sticky !important;
top: var(--glossary-sticky-top, calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))) !important;
z-index: 72 !important;
margin-bottom: 0 !important;
overflow: hidden !important;
transform: translateZ(0) !important;
backface-visibility: hidden !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
:global(body.is-index-complet-page #reading-follow){
display: block !important;
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(var(--glossary-sticky-top, var(--sticky-header-h, 0px)) + var(--glossary-local-sticky-h, 0px) - var(--followbar-h, 0px) - 1px) !important;
z-index: 71 !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
}
:global(body.is-index-complet-page.glossary-portal-follow-on #reading-follow){
visibility: visible !important;
opacity: 1 !important;
}
:global(body.is-index-complet-page #reading-follow .reading-follow__inner){
display: block !important;
visibility: visible !important;
opacity: 1 !important;
max-height: none !important;
margin-top: -1px !important;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
:global(body.is-index-complet-page #gic-follow-letters){
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(var(--glossary-sticky-top, var(--sticky-header-h, 0px)) + var(--gic-premium-hero-h, 0px) + var(--gic-premium-follow-h, 0px) - 1px) !important;
z-index: 70 !important;
}
:global(body.is-index-complet-page.gic-letters-docked #gic-follow-letters){
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
}
}
</style>

View File

@@ -931,14 +931,14 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
min-width: 0 !important;
}
:global(body[data-edition-key="glossaire"]) .glossary-hero,
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-home"]):not([data-sticky-mode="glossary-portal"])) .glossary-hero,
:global(body[data-edition-key="glossaire"]) .glossary-home,
:global(body[data-edition-key="glossaire"]) .glossary-map,
:global(body[data-edition-key="glossaire"]) .glossary-map-block,
:global(body[data-edition-key="glossaire"]) .glossary-map-section,
:global(body[data-edition-key="glossaire"]) .glossary-page-hero,
:global(body[data-edition-key="glossaire"]) .glossary-portal-hero,
:global(body[data-edition-key="glossaire"]) .scene-hero{
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) .glossary-page-hero,
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) .glossary-portal-hero,
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) .scene-hero{
position: static !important;
top: auto !important;
left: auto !important;
@@ -953,7 +953,7 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
transform: none !important;
}
:global(body[data-edition-key="glossaire"]) #reading-follow{
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) #reading-follow{
display: none !important;
}
}

View File

@@ -126,12 +126,6 @@ const prolongerLinks = [
<span class="gic-aside__pill">
{doctrinesCount} doctrine{doctrinesCount > 1 ? "s" : ""}
</span>
<span class="gic-aside__pill">
{verbesCount} verbe{verbesCount > 1 ? "s" : ""}
</span>
<span class="gic-aside__pill">
{casIaCount} entrée{casIaCount > 1 ? "s" : ""} cas IA
</span>
</div>
<p class="gic-aside__note">
@@ -232,6 +226,8 @@ const prolongerLinks = [
<GlossaryPortalStickySync
heroMoreId="gic-hero-more"
heroToggleId="gic-hero-toggle"
mobileBreakpoint={980}
compactSticky={true}
/>
<script is:inline>
@@ -265,30 +261,39 @@ const prolongerLinks = [
body.classList.add(BODY_CLASS);
const syncPremiumStickyMetrics = () => {
const hero = document.querySelector("[data-glossary-portal-hero]");
const followInner = follow.querySelector(".reading-follow__inner");
const heroH = hero ? Math.round(hero.getBoundingClientRect().height || 0) : 0;
const followOn =
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
const followH = followOn && followInner
? Math.round(followInner.getBoundingClientRect().height || 0)
: 0;
const lettersH =
!lettersFollow.hidden && lettersFollow.getAttribute("aria-hidden") === "false"
? Math.round(lettersFollow.getBoundingClientRect().height || 0)
: 0;
root.style.setProperty("--gic-premium-hero-h", `${heroH}px`);
root.style.setProperty("--gic-premium-follow-h", `${followH}px`);
root.style.setProperty("--gic-premium-letters-h", `${lettersH}px`);
};
const forceHideGlobalFollowOnCompactViewport = () => {
if (!isCompactViewport()) {
follow.style.display = "";
return;
}
follow.classList.remove("is-on");
follow.setAttribute("aria-hidden", "true");
follow.style.display = "none";
follow.innerHTML = "";
root.style.setProperty("--followbar-h", "0px");
root.style.setProperty(
"--sticky-offset-px",
"calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))"
);
setAnchorOffset(0);
follow.style.display = "";
syncPremiumStickyMetrics();
};
const isCompactViewport = () =>
mqMobile.matches || mqSmallLandscape.matches;
const computeFollowOn = () =>
!isCompactViewport() &&
follow.classList.contains("is-on") &&
follow.style.display !== "none" &&
follow.getAttribute("aria-hidden") !== "true";
@@ -320,7 +325,7 @@ const prolongerLinks = [
};
const syncLettersDockState = () => {
if (mqMobile.matches || !computeFollowOn()) {
if (!computeFollowOn()) {
body.classList.remove(LETTERS_DOCKED_CLASS);
lettersFollow.hidden = true;
lettersFollow.setAttribute("aria-hidden", "true");
@@ -503,6 +508,7 @@ const prolongerLinks = [
forceHideGlobalFollowOnCompactViewport();
syncFollowLettersTop();
syncLettersDockState();
syncPremiumStickyMetrics();
syncActiveLetter();
});
};
@@ -565,6 +571,777 @@ const prolongerLinks = [
}
})();
</script>
<script is:inline>
/*
INDEX COMPLET — STACK PREMIUM MOBILE/TABLETTE V2
Principe :
- uniquement sur /glossaire/index-complet/ ;
- rail vertical calculé en pixels réels ;
- cockpit compact :
header → hero condensé → follow H2 compact → lettres horizontales.
*/
(() => {
const boot = () => {
const body = document.body;
const root = document.documentElement;
const hero = document.querySelector("[data-glossary-portal-hero]");
const follow = document.getElementById("reading-follow");
const inner = follow?.querySelector(".reading-follow__inner");
const lettersSource = document.getElementById("gic-letters-source");
const lettersFollow = document.getElementById("gic-follow-letters");
if (!body || !root || !hero || !follow || !inner || !lettersSource || !lettersFollow) return;
const mqCompact = window.matchMedia("(max-width: 980px)");
const mqSmallLandscape = window.matchMedia(
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
);
const isCompact = () => mqCompact.matches || mqSmallLandscape.matches;
const pxVar = (name, fallback = 0) => {
const raw = getComputedStyle(root).getPropertyValue(name).trim();
const n = Number.parseFloat(raw);
return Number.isFinite(n) ? n : fallback;
};
const stackTop = () => {
const glossaryTop = pxVar("--glossary-sticky-top", NaN);
if (Number.isFinite(glossaryTop) && glossaryTop > 0) return glossaryTop;
const headerH = pxVar("--sticky-header-h", 0);
const gap = pxVar("--page-gap", 12);
return Math.max(0, Math.round(headerH + gap));
};
const heads = Array.from(
document.querySelectorAll(".glossary-portal-section__head, .gic-section__head")
);
const titleOf = (head) => {
const h = head?.querySelector("h2, h3");
return (h?.textContent || head?.textContent || "")
.replace(/\s+/g, " ")
.trim();
};
const writeFollow = (title) => {
const h1 = inner.querySelector(".rf-h1");
const h2 = inner.querySelector(".rf-h2");
const h3 = inner.querySelector(".rf-h3");
if (h1) h1.textContent = "Index complet";
if (h2) h2.textContent = title || "Index alphabétique";
if (h3) h3.textContent = "";
follow.classList.add("is-on");
follow.setAttribute("aria-hidden", "false");
follow.style.setProperty("display", "block", "important");
inner.style.setProperty("display", "block", "important");
inner.style.setProperty("visibility", "visible", "important");
inner.style.setProperty("opacity", "1", "important");
body.classList.add("glossary-portal-follow-on", "gic-premium-follow-on");
};
const clearFollow = () => {
follow.classList.remove("is-on");
follow.setAttribute("aria-hidden", "true");
body.classList.remove(
"glossary-portal-follow-on",
"gic-premium-follow-on",
"gic-letters-docked",
"gic-premium-letters-on"
);
lettersFollow.hidden = true;
lettersFollow.setAttribute("aria-hidden", "true");
root.style.setProperty("--gic-premium-follow-h", "0px");
root.style.setProperty("--gic-premium-letters-h", "0px");
root.style.setProperty("--gic-stack-h", "0px");
root.style.setProperty("--gic-follow-letters-offset", "0px");
};
const currentHead = () => {
const top = stackTop();
const heroH = pxVar("--gic-premium-hero-h", 0);
const followH = pxVar("--gic-premium-follow-h", 0);
const lettersH = pxVar("--gic-premium-letters-h", 0);
const threshold = top + heroH + followH + lettersH + 18;
let current = null;
for (const head of heads) {
const r = head.getBoundingClientRect();
if (r.top <= threshold) current = head;
else break;
}
return current;
};
const measure = () => {
const heroH = body.classList.contains("glossary-portal-hero-condensed")
? Math.max(0, Math.round(hero.getBoundingClientRect().height || 0))
: 0;
const followH = body.classList.contains("gic-premium-follow-on")
? Math.max(0, Math.round(inner.getBoundingClientRect().height || 0))
: 0;
const lettersH = body.classList.contains("gic-premium-letters-on")
? Math.max(0, Math.round(lettersFollow.getBoundingClientRect().height || 0))
: 0;
const stackH = heroH + followH + lettersH;
root.style.setProperty("--gic-premium-top", `${stackTop()}px`);
root.style.setProperty("--gic-premium-hero-h", `${heroH}px`);
root.style.setProperty("--gic-premium-follow-h", `${followH}px`);
root.style.setProperty("--gic-premium-letters-h", `${lettersH}px`);
root.style.setProperty("--gic-stack-h", `${stackH}px`);
root.style.setProperty("--glossary-local-sticky-h", `${stackH}px`);
root.style.setProperty("--followbar-h", `${followH}px`);
root.style.setProperty("--sticky-offset-px", `${stackTop() + stackH + 12}px`);
};
const syncLetters = () => {
if (!body.classList.contains("gic-premium-follow-on")) {
body.classList.remove("gic-letters-docked", "gic-premium-letters-on");
lettersFollow.hidden = true;
lettersFollow.setAttribute("aria-hidden", "true");
root.style.setProperty("--gic-follow-letters-offset", "0px");
return;
}
const sourceRect = lettersSource.getBoundingClientRect();
const followBottom = stackTop() + pxVar("--gic-premium-hero-h", 0) + pxVar("--gic-premium-follow-h", 0);
const shouldDock = sourceRect.top <= followBottom + 8;
body.classList.toggle("gic-letters-docked", shouldDock);
body.classList.toggle("gic-premium-letters-on", shouldDock);
lettersFollow.hidden = !shouldDock;
lettersFollow.setAttribute("aria-hidden", shouldDock ? "false" : "true");
if (shouldDock) {
lettersFollow.style.setProperty("display", "flex", "important");
root.style.setProperty(
"--gic-follow-letters-offset",
`${Math.max(0, Math.round(lettersFollow.getBoundingClientRect().height || 0)) + 12}px`
);
} else {
root.style.setProperty("--gic-follow-letters-offset", "0px");
}
};
let raf = 0;
const sync = () => {
raf = 0;
body.classList.add("is-index-complet-page", "is-glossary-portal-page");
if (!isCompact()) {
body.classList.remove(
"gic-premium-compact",
"gic-premium-follow-on",
"gic-premium-letters-on"
);
hero.style.removeProperty("position");
hero.style.removeProperty("top");
hero.style.removeProperty("z-index");
follow.style.removeProperty("display");
follow.style.removeProperty("top");
follow.style.removeProperty("left");
follow.style.removeProperty("width");
lettersFollow.style.removeProperty("display");
lettersFollow.style.removeProperty("top");
lettersFollow.style.removeProperty("left");
lettersFollow.style.removeProperty("width");
return;
}
body.classList.add("gic-premium-compact");
const top = stackTop();
const scrollY = window.scrollY || window.pageYOffset || 0;
const heroRect = hero.getBoundingClientRect();
const condensed = scrollY > 8 && heroRect.top <= top + 8;
body.classList.toggle("glossary-portal-hero-condensed", condensed);
hero.style.setProperty("position", "sticky", "important");
hero.style.setProperty("top", `${top}px`, "important");
hero.style.setProperty("z-index", "72", "important");
if (!condensed) {
clearFollow();
measure();
return;
}
measure();
const head = currentHead();
const rawTitle = titleOf(head) || "Index alphabétique";
const title = /^[A-ZÀÂÄÇÉÈÊËÎÏÔÖÙÛÜŸ]$/i.test(rawTitle)
? "Index alphabétique"
: rawTitle;
writeFollow(title);
measure();
syncLetters();
measure();
};
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(sync);
};
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 (mqCompact.addEventListener) mqCompact.addEventListener("change", schedule);
else if (mqCompact.addListener) mqCompact.addListener(schedule);
if (mqSmallLandscape.addEventListener) mqSmallLandscape.addEventListener("change", schedule);
else if (mqSmallLandscape.addListener) mqSmallLandscape.addListener(schedule);
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
<style is:global>
/* INDEX COMPLET — STACK PREMIUM MOBILE/TABLETTE V2 */
@media (max-width: 980px){
body.is-index-complet-page.gic-premium-compact{
scroll-padding-top: calc(var(--gic-premium-top, 0px) + var(--gic-stack-h, 0px) + 16px);
}
body.is-index-complet-page.gic-premium-compact .glossary-portal-hero,
body.is-index-complet-page.gic-premium-compact .glossary-page-hero{
position: sticky !important;
top: var(--gic-premium-top, var(--glossary-sticky-top, 0px)) !important;
z-index: 72 !important;
overflow: hidden !important;
margin-bottom: 12px !important;
transform: translateZ(0) !important;
backface-visibility: hidden !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero,
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-page-hero{
padding: 7px 10px 7px !important;
row-gap: 3px !important;
border-radius: 14px 14px 0 0 !important;
margin-bottom: 0 !important;
box-shadow: 0 12px 30px rgba(0,0,0,.24) !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero__kicker{
font-size: 8px !important;
line-height: 1 !important;
letter-spacing: .105em !important;
opacity: .66 !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero h1{
font-size: clamp(1.18rem, 4.8vw, 1.48rem) !important;
line-height: 1 !important;
letter-spacing: -.028em !important;
margin: 0 !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro{
display: -webkit-box !important;
-webkit-box-orient: vertical !important;
-webkit-line-clamp: 1 !important;
line-clamp: 1 !important;
max-height: 1.12em !important;
overflow: hidden !important;
font-size: .66rem !important;
line-height: 1.12 !important;
opacity: .72 !important;
margin: 0 !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__more{
max-height: 0 !important;
opacity: 0 !important;
overflow: hidden !important;
pointer-events: none !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero__toggle{
display: none !important;
}
body.is-index-complet-page.gic-premium-compact #reading-follow{
display: block !important;
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(var(--gic-premium-top, 0px) + var(--gic-premium-hero-h, 0px) - 1px) !important;
z-index: 71 !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
body.is-index-complet-page.gic-premium-compact.gic-premium-follow-on #reading-follow{
opacity: 1 !important;
visibility: visible !important;
}
body.is-index-complet-page.gic-premium-compact #reading-follow .reading-follow__inner{
display: block !important;
visibility: visible !important;
opacity: 1 !important;
max-height: none !important;
min-height: 0 !important;
margin-top: 0 !important;
padding: 5px 56px 5px 9px !important;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
box-shadow: 0 8px 18px rgba(0,0,0,.16) !important;
}
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h1,
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h3,
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-actions{
display: none !important;
}
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h2{
display: block !important;
font-size: .78rem !important;
line-height: 1.08 !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
letter-spacing: -.012em !important;
}
body.is-index-complet-page.gic-premium-compact #gic-follow-letters{
position: fixed !important;
left: var(--reading-left) !important;
width: var(--reading-width) !important;
top: calc(
var(--gic-premium-top, 0px)
+ var(--gic-premium-hero-h, 0px)
+ var(--gic-premium-follow-h, 0px)
- 1px
) !important;
z-index: 70 !important;
display: none !important;
flex-wrap: nowrap !important;
gap: 6px !important;
overflow-x: auto !important;
overflow-y: hidden !important;
scrollbar-width: none !important;
-webkit-overflow-scrolling: touch !important;
margin: 0 !important;
padding: 6px 8px 7px !important;
border-radius: 0 0 12px 12px !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
body.is-index-complet-page.gic-premium-compact #gic-follow-letters::-webkit-scrollbar{
display: none !important;
}
body.is-index-complet-page.gic-premium-compact #gic-follow-letters a{
flex: 0 0 auto !important;
min-width: 28px !important;
height: 28px !important;
padding: 0 9px !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
body.is-index-complet-page.gic-premium-compact.gic-premium-letters-on #gic-follow-letters{
display: flex !important;
opacity: 1 !important;
visibility: visible !important;
pointer-events: auto !important;
}
body.is-index-complet-page.gic-premium-compact .gic-section,
body.is-index-complet-page.gic-premium-compact .gic-group,
body.is-index-complet-page.gic-premium-compact .glossary-portal-section{
scroll-margin-top: calc(var(--gic-premium-top, 0px) + var(--gic-stack-h, 0px) + 18px) !important;
}
}
@media (min-width: 761px) and (max-width: 980px){
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero,
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-page-hero{
padding: 8px 12px 8px !important;
border-radius: 16px 16px 0 0 !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero h1{
font-size: clamp(1.48rem, 3vw, 1.82rem) !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro{
font-size: .76rem !important;
line-height: 1.14 !important;
}
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h2{
font-size: .86rem !important;
}
}
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero,
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-page-hero{
padding: 5px 8px 5px !important;
row-gap: 2px !important;
border-radius: 12px 12px 0 0 !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero h1{
font-size: clamp(1rem, 3vw, 1.22rem) !important;
}
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro{
display: none !important;
}
body.is-index-complet-page.gic-premium-compact #reading-follow .reading-follow__inner{
padding-top: 4px !important;
padding-bottom: 4px !important;
}
body.is-index-complet-page.gic-premium-compact #gic-follow-letters{
padding-top: 4px !important;
padding-bottom: 5px !important;
}
body.is-index-complet-page.gic-premium-compact #gic-follow-letters a{
min-width: 24px !important;
height: 24px !important;
padding: 0 7px !important;
}
}
</style>
<script is:inline>
/*
INDEX COMPLET — FOLLOW LABEL ET STABILITÉ DESKTOP
Sur cette page, les sections alphabétiques A/B/C… ne doivent pas
devenir le titre du reading-follow : la lettre active est déjà
portée par la barre alphabétique.
*/
(() => {
const boot = () => {
const body = document.body;
const follow = document.getElementById("reading-follow");
const inner = follow?.querySelector(".reading-follow__inner");
if (!body || !follow || !inner) return;
const LETTER_RE = /^[A-ZÀÂÄÇÉÈÊËÎÏÔÖÙÛÜŸ]$/i;
const normalize = () => {
if (!body.classList.contains("is-index-complet-page")) return;
const h1 = inner.querySelector(".rf-h1");
const h2 = inner.querySelector(".rf-h2");
const h3 = inner.querySelector(".rf-h3");
const txt = (h2?.textContent || "").replace(/\s+/g, " ").trim();
if (
h2 &&
(
LETTER_RE.test(txt) ||
body.classList.contains("gic-letters-docked") ||
body.classList.contains("gic-premium-letters-on")
)
) {
if (h1) h1.textContent = "Index complet du glossaire";
h2.textContent = "Index alphabétique";
if (h3) h3.textContent = "";
}
};
let raf = 0;
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
normalize();
});
};
const observer = new MutationObserver(schedule);
observer.observe(body, {
attributes: true,
attributeFilter: ["class"],
});
observer.observe(inner, {
childList: true,
subtree: true,
characterData: true,
});
window.addEventListener("scroll", schedule, { passive: true });
window.addEventListener("resize", schedule);
window.addEventListener("pageshow", schedule);
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
<style is:global>
/* INDEX COMPLET — FOLLOW LABEL ET STABILITÉ DESKTOP */
@media (min-width: 981px){
body.is-index-complet-page.glossary-portal-follow-on #reading-follow{
top: calc(
var(--glossary-sticky-top, var(--sticky-header-h, 0px))
+ var(--glossary-local-sticky-h, 0px)
- 1px
) !important;
z-index: 71 !important;
}
body.is-index-complet-page.gic-letters-docked #gic-follow-letters{
top: calc(
var(--glossary-sticky-top, var(--sticky-header-h, 0px))
+ var(--glossary-local-sticky-h, 0px)
+ var(--followbar-h, 0px)
- 1px
) !important;
z-index: 70 !important;
}
body.is-index-complet-page.gic-letters-docked #reading-follow .rf-h2{
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
}
</style>
<script is:inline>
/*
INDEX COMPLET — DESKTOP FOLLOW GATE
Le reading-follow ne doit pas apparaître en haut de page.
Il devient visible seulement lorsque le hero est effectivement condensé.
*/
(() => {
const boot = () => {
const body = document.body;
const hero = document.querySelector("[data-glossary-portal-hero]");
const follow = document.getElementById("reading-follow");
if (!body || !hero || !follow) return;
const mqDesktop = window.matchMedia("(min-width: 981px)");
const sync = () => {
if (!mqDesktop.matches) {
body.classList.remove("gic-desktop-follow-gated");
return;
}
const condensed = body.classList.contains("glossary-portal-hero-condensed");
body.classList.toggle("gic-desktop-follow-gated", !condensed);
if (!condensed) {
follow.classList.remove("is-on");
follow.setAttribute("aria-hidden", "true");
}
};
let raf = 0;
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
sync();
});
};
const observer = new MutationObserver(schedule);
observer.observe(body, {
attributes: true,
attributeFilter: ["class"],
});
window.addEventListener("scroll", schedule, { passive: true });
window.addEventListener("resize", schedule);
window.addEventListener("pageshow", schedule);
if (mqDesktop.addEventListener) mqDesktop.addEventListener("change", schedule);
else if (mqDesktop.addListener) mqDesktop.addListener(schedule);
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
<style is:global>
/* INDEX COMPLET — DESKTOP FOLLOW GATE */
@media (min-width: 981px){
body.is-index-complet-page.gic-desktop-follow-gated #reading-follow,
body.is-index-complet-page.gic-desktop-follow-gated #reading-follow .reading-follow__inner{
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
body.is-index-complet-page.gic-desktop-follow-gated #gic-follow-letters{
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
}
</style>
<script is:inline>
/*
INDEX COMPLET — AUTO-CENTER ACTIVE LETTER
Sur mobile, la barre alphabétique horizontale doit suivre le lecteur :
la lettre active est automatiquement recentrée dans gic-follow-letters.
*/
(() => {
const boot = () => {
const body = document.body;
const lettersFollow = document.getElementById("gic-follow-letters");
if (!body || !lettersFollow) return;
const mqCompact = window.matchMedia("(max-width: 760px)");
let lastHref = "";
let raf = 0;
const centerActiveLetter = () => {
raf = 0;
if (!mqCompact.matches) return;
if (!body.classList.contains("is-index-complet-page")) return;
if (!body.classList.contains("gic-premium-letters-on")) return;
const active =
lettersFollow.querySelector('a[aria-current="true"]') ||
lettersFollow.querySelector("a.is-active");
if (!active) return;
const href = active.getAttribute("href") || "";
const force = href !== lastHref;
lastHref = href;
const containerRect = lettersFollow.getBoundingClientRect();
const activeRect = active.getBoundingClientRect();
const activeCenter =
activeRect.left - containerRect.left + lettersFollow.scrollLeft + activeRect.width / 2;
const target =
activeCenter - lettersFollow.clientWidth / 2;
const max =
lettersFollow.scrollWidth - lettersFollow.clientWidth;
const next = Math.max(0, Math.min(max, Math.round(target)));
if (force || Math.abs(lettersFollow.scrollLeft - next) > 6) {
lettersFollow.scrollTo({
left: next,
behavior: "smooth",
});
}
};
const schedule = () => {
if (raf) return;
raf = requestAnimationFrame(centerActiveLetter);
};
const observer = new MutationObserver(schedule);
observer.observe(lettersFollow, {
subtree: true,
attributes: true,
attributeFilter: ["class", "aria-current"],
});
observer.observe(body, {
attributes: true,
attributeFilter: ["class"],
});
window.addEventListener("scroll", schedule, { passive: true });
window.addEventListener("resize", schedule);
window.addEventListener("pageshow", schedule);
if (mqCompact.addEventListener) mqCompact.addEventListener("change", schedule);
else if (mqCompact.addListener) mqCompact.addListener(schedule);
schedule();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();
</script>
</GlossaryLayout>
<style>
@@ -1041,4 +1818,5 @@ const prolongerLinks = [
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
}
}
</style>

View File

@@ -351,6 +351,7 @@ const approfondirPortalItems = [
let raf = 0;
let activeHeading = null;
let clearTimer = 0;
let homeFollowOn = false;
function syncStickyTop() {
const headerHeight = header.getBoundingClientRect().height || 0;
@@ -368,8 +369,15 @@ const approfondirPortalItems = [
Number.parseFloat(cs.lineHeight) ||
Math.round(sourceFontSize * 1.06 * 100) / 100;
const scaledFontSize = Math.max(28, sourceFontSize * 0.9);
const scaledLineHeight = Math.max(scaledFontSize * 1.04, sourceLineHeight * 0.92);
const compactHomeSticky = window.matchMedia("(max-width: 980px)").matches;
const scaledFontSize = compactHomeSticky
? Math.min(20, Math.max(14, sourceFontSize * 0.56))
: Math.max(28, sourceFontSize * 0.9);
const scaledLineHeight = compactHomeSticky
? Math.max(scaledFontSize * 1.08, 16)
: Math.max(scaledFontSize * 1.04, sourceLineHeight * 0.92);
toEl.style.fontSize = `${scaledFontSize}px`;
toEl.style.lineHeight = `${scaledLineHeight}px`;
@@ -469,6 +477,7 @@ const approfondirPortalItems = [
function updateFollow() {
syncStickyTop();
const compactHomeSticky = window.matchMedia("(max-width: 980px)").matches;
const heroRect = hero.getBoundingClientRect();
const active = getCurrentHeading();
@@ -478,9 +487,23 @@ const approfondirPortalItems = [
.getPropertyValue("--glossary-sticky-top")
) || 64;
const hasStartedScrolling = (window.scrollY || window.pageYOffset || 0) > 8;
const heroDocked = Math.abs(heroRect.top - stickyTop) <= 6;
const heroOut = hasStartedScrolling && heroDocked;
const scrollY = window.scrollY || window.pageYOffset || 0;
const hasStartedScrolling = scrollY > 8;
const heroDocked = compactHomeSticky
? heroRect.top <= stickyTop + 8
: Math.abs(heroRect.top - stickyTop) <= 6;
const enterFollow = hasStartedScrolling && heroDocked;
const exitFollow = !hasStartedScrolling || heroRect.top > stickyTop + (compactHomeSticky ? 28 : 12);
if (enterFollow) {
homeFollowOn = true;
} else if (exitFollow) {
homeFollowOn = false;
}
const heroOut = homeFollowOn;
document.body.classList.toggle("glossary-home-follow-on", heroOut);
@@ -569,7 +592,7 @@ const approfondirPortalItems = [
}
.glossary-map-block{
padding: 18px 18px 20px;
padding: 16px 18px 18px;
border: 1px solid var(--glossary-border);
border-radius: 24px;
background: rgba(127,127,127,0.04);
@@ -577,9 +600,9 @@ const approfondirPortalItems = [
.glossary-map-block__head p{
max-width: 76ch;
margin: 12px 0 0;
font-size: 1rem;
line-height: 1.55;
margin: 9px 0 0;
font-size: .98rem;
line-height: 1.46;
opacity: .94;
text-wrap: pretty;
}
@@ -587,28 +610,56 @@ const approfondirPortalItems = [
.glossary-map{
display: grid;
justify-items: center;
gap: 10px;
margin-top: 18px;
gap: 6px;
margin-top: 14px;
}
.glossary-map__stage{
width: min(580px, 100%);
display: grid;
justify-items: center;
gap: 10px;
gap: 6px;
}
.glossary-map__title{
width: 100%;
text-align: center;
font-size: 1.08rem;
line-height: 1.25;
font-size: .98rem;
line-height: 1.16;
font-weight: 800;
letter-spacing: -.01em;
opacity: .96;
text-wrap: balance;
}
.glossary-map__node{
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 42px;
padding: 8px 14px;
border: 1px solid var(--glossary-border-strong);
border-radius: 999px;
background: var(--glossary-bg-soft);
color: var(--glossary-accent);
text-decoration: none;
text-align: center;
font-size: .94rem;
font-weight: 800;
letter-spacing: .04em;
line-height: 1.16;
transition:
transform 120ms ease,
background 120ms ease,
border-color 120ms ease;
}
.glossary-map__arrow{
font-size: 1.18rem;
line-height: .9;
opacity: .68;
}
.glossary-map__roots{
width: 100%;
display: grid;
@@ -616,28 +667,6 @@ const approfondirPortalItems = [
gap: 8px;
}
.glossary-map__node{
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 50px;
padding: 10px 14px;
border: 1px solid var(--glossary-border-strong);
border-radius: 999px;
background: var(--glossary-bg-soft);
color: var(--glossary-accent);
text-decoration: none;
text-align: center;
font-size: .99rem;
font-weight: 800;
letter-spacing: .04em;
line-height: 1.2;
transition:
transform 120ms ease,
background 120ms ease,
border-color 120ms ease;
}
.glossary-map__node:hover{
background: var(--glossary-bg-soft-strong);
border-color: rgba(0,217,255,0.22);
@@ -650,12 +679,6 @@ const approfondirPortalItems = [
max-width: 100%;
}
.glossary-map__arrow{
font-size: 1.45rem;
line-height: 1;
opacity: .72;
}
.glossary-portal-card strong{
color: var(--glossary-accent);
font-size: 1.08rem;
@@ -720,11 +743,17 @@ const approfondirPortalItems = [
.glossary-map-block__head h2,
.glossary-section h2{
font-size: clamp(1.5rem, 7vw, 1.95rem);
line-height: 1.04;
font-size: clamp(1.42rem, 6.3vw, 1.82rem);
line-height: 1.03;
letter-spacing: -.022em;
}
.glossary-map-block__head h2{
hyphens: none;
word-break: normal;
overflow-wrap: normal;
}
.glossary-map-block__head p,
.glossary-intro{
font-size: .9rem;
@@ -742,35 +771,36 @@ const approfondirPortalItems = [
}
.glossary-map{
gap: 7px;
margin-top: 12px;
gap: 4px;
margin-top: 10px;
}
.glossary-map__stage{
gap: 7px;
gap: 4px;
width: 100%;
}
.glossary-map__title{
font-size: .9rem;
line-height: 1.2;
font-size: .82rem;
line-height: 1.12;
}
.glossary-map__roots{
grid-template-columns: 1fr;
gap: 6px;
gap: 5px;
}
.glossary-map__node{
min-height: 38px;
padding: 8px 10px;
font-size: .84rem;
line-height: 1.15;
min-height: 32px;
padding: 6px 10px;
font-size: .78rem;
line-height: 1.12;
}
.glossary-map__arrow{
font-size: 1rem;
opacity: .62;
font-size: .92rem;
line-height: .8;
opacity: .6;
}
}
@@ -804,23 +834,28 @@ const approfondirPortalItems = [
}
.glossary-map{
gap: 5px;
margin-top: 10px;
gap: 3px;
margin-top: 8px;
}
.glossary-map__stage{
gap: 5px;
gap: 3px;
}
.glossary-map__title{
font-size: .8rem;
line-height: 1.12;
font-size: .74rem;
line-height: 1.08;
}
.glossary-map__node{
min-height: 32px;
padding: 6px 8px;
font-size: .74rem;
min-height: 28px;
padding: 5px 8px;
font-size: .7rem;
}
.glossary-map__arrow{
font-size: .82rem;
line-height: .75;
}
.glossary-home .glossary-card,