331 lines
10 KiB
CSS
331 lines
10 KiB
CSS
/* ==========================================================================
|
|
layout.css — Structure de page
|
|
--------------------------------------------------------------------------
|
|
Conteneurs, en-tête collant, navigation, sections, grilles, pied de page.
|
|
Ce fichier positionne ; components.css habille.
|
|
========================================================================== */
|
|
|
|
@layer layout {
|
|
|
|
/* ======================================================================
|
|
CONTENEUR
|
|
Un seul conteneur réutilisé partout : la largeur de contenu est décidée
|
|
à un seul endroit.
|
|
====================================================================== */
|
|
|
|
.container {
|
|
inline-size: 100%;
|
|
max-inline-size: var(--width-content);
|
|
margin-inline: auto;
|
|
padding-inline: var(--gutter);
|
|
}
|
|
|
|
/* ======================================================================
|
|
LIEN D'ÉVITEMENT
|
|
Premier élément focusable de la page. Permet à un utilisateur au
|
|
clavier ou au lecteur d'écran de sauter la navigation pour atteindre
|
|
directement le contenu. Critère WCAG 2.4.1 (Contourner des blocs).
|
|
Masqué visuellement hors focus, mais jamais display:none — sinon il
|
|
ne serait pas focusable.
|
|
====================================================================== */
|
|
|
|
.skip-link {
|
|
position: fixed;
|
|
inset-block-start: var(--space-xs);
|
|
inset-inline-start: var(--space-xs);
|
|
z-index: var(--z-skip-link);
|
|
padding: var(--space-xs) var(--space-md);
|
|
background-color: var(--color-ink);
|
|
color: var(--color-ink-inverse);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--text-sm);
|
|
text-decoration: none;
|
|
border-radius: var(--radius-sm);
|
|
transform: translateY(-200%);
|
|
transition: transform var(--transition-fast);
|
|
}
|
|
|
|
.skip-link:focus {
|
|
transform: translateY(0);
|
|
color: var(--color-ink-inverse);
|
|
}
|
|
|
|
/* ======================================================================
|
|
EN-TÊTE COLLANT
|
|
====================================================================== */
|
|
|
|
.site-header {
|
|
position: sticky;
|
|
inset-block-start: 0;
|
|
z-index: var(--z-sticky);
|
|
background-color: var(--color-bg-translucent);
|
|
border-block-end: 1px solid var(--color-line);
|
|
|
|
/* Flou d'arrière-plan : le contenu qui défile dessous reste deviné
|
|
sans nuire à la lisibilité du menu. Dégradation propre si non
|
|
supporté (le fond translucide suffit). */
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.site-header__inner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-md);
|
|
min-block-size: var(--header-height);
|
|
}
|
|
|
|
.site-header__brand {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--text-sm);
|
|
font-weight: var(--weight-bold);
|
|
color: var(--color-ink);
|
|
text-decoration: none;
|
|
letter-spacing: -0.01em;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.site-header__brand:hover { color: var(--color-ink); }
|
|
|
|
.site-header__logo {
|
|
inline-size: 1.6rem;
|
|
block-size: 1.6rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* ======================================================================
|
|
NAVIGATION PRINCIPALE
|
|
====================================================================== */
|
|
|
|
.site-nav__list {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-lg);
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.site-nav__link {
|
|
display: inline-block;
|
|
font-family: var(--font-mono);
|
|
font-size: var(--text-sm);
|
|
color: var(--color-ink-faint);
|
|
text-decoration: none;
|
|
padding-block: var(--space-2xs);
|
|
border-block-end: 2px solid transparent;
|
|
transition: color var(--transition-fast), border-color var(--transition-fast);
|
|
}
|
|
|
|
.site-nav__link:hover { color: var(--color-ink); }
|
|
|
|
/* Section courante. aria-current n'est pas qu'un sélecteur pratique :
|
|
il annonce la position aux lecteurs d'écran. Le repère visuel repose
|
|
sur la couleur ET sur une bordure — jamais sur la couleur seule
|
|
(WCAG 1.4.1, information non véhiculée par la couleur uniquement). */
|
|
.site-nav__link[aria-current="true"] {
|
|
color: var(--color-ink);
|
|
border-block-end-color: var(--color-accent);
|
|
}
|
|
|
|
/* ======================================================================
|
|
BOUTON MENU (mobile)
|
|
====================================================================== */
|
|
|
|
.nav-toggle {
|
|
display: none; /* Affiché uniquement sous le point de rupture */
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
min-block-size: var(--tap-target);
|
|
padding-inline: var(--space-xs);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--text-sm);
|
|
color: var(--color-ink-soft);
|
|
}
|
|
|
|
.nav-toggle__icon {
|
|
inline-size: 1.15rem;
|
|
block-size: 1.15rem;
|
|
stroke: currentcolor;
|
|
stroke-width: 2;
|
|
fill: none;
|
|
}
|
|
|
|
/* Les deux tracés de l'icône se transforment en croix à l'ouverture */
|
|
.nav-toggle__line {
|
|
transform-origin: center;
|
|
transition: transform var(--transition-base), opacity var(--transition-fast);
|
|
}
|
|
|
|
.nav-toggle[aria-expanded="true"] .nav-toggle__line--top { transform: translateY(5px) rotate(45deg); }
|
|
.nav-toggle[aria-expanded="true"] .nav-toggle__line--middle { opacity: 0; }
|
|
.nav-toggle[aria-expanded="true"] .nav-toggle__line--bottom { transform: translateY(-5px) rotate(-45deg); }
|
|
|
|
/* ======================================================================
|
|
RÉGIONS DE CONTENU
|
|
====================================================================== */
|
|
|
|
.masthead {
|
|
padding-block: var(--space-3xl) var(--space-2xl);
|
|
border-block-end: 1px solid var(--color-line);
|
|
}
|
|
|
|
.section {
|
|
padding-block: var(--space-3xl);
|
|
border-block-start: 1px solid var(--color-line);
|
|
}
|
|
|
|
/* ======================================================================
|
|
GRILLE AUTO-ADAPTATIVE
|
|
auto-fit + minmax : le nombre de colonnes s'ajuste seul à la largeur
|
|
disponible, sans media query. Le min() interne évite le débordement
|
|
sur les très petits écrans (une colonne ne peut pas être plus large
|
|
que son conteneur).
|
|
====================================================================== */
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(min(17.5rem, 100%), 1fr));
|
|
gap: var(--space-md);
|
|
}
|
|
|
|
.grid--wide {
|
|
grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
|
|
}
|
|
|
|
/* Empilement vertical régulier */
|
|
.stack > * + * { margin-block-start: var(--space-md); }
|
|
|
|
/* ======================================================================
|
|
PIED DE PAGE
|
|
====================================================================== */
|
|
|
|
.site-footer {
|
|
border-block-start: 1px solid var(--color-line);
|
|
padding-block: var(--space-xl) var(--space-2xl);
|
|
}
|
|
|
|
.site-footer__inner {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: var(--space-sm) var(--space-lg);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--text-sm);
|
|
color: var(--color-ink-faint);
|
|
}
|
|
|
|
.site-footer__links {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-md);
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
/* ======================================================================
|
|
BOUTON RETOUR EN HAUT
|
|
====================================================================== */
|
|
|
|
.to-top {
|
|
position: fixed;
|
|
inset-block-end: var(--space-lg);
|
|
inset-inline-end: var(--space-lg);
|
|
z-index: var(--z-overlay);
|
|
display: grid;
|
|
place-items: center;
|
|
inline-size: var(--tap-target);
|
|
block-size: var(--tap-target);
|
|
background-color: var(--color-surface);
|
|
border: 1px solid var(--color-line-strong);
|
|
border-radius: var(--radius-full);
|
|
box-shadow: var(--shadow-md);
|
|
color: var(--color-ink-soft);
|
|
|
|
/* Masqué par défaut : révélé par JS après défilement.
|
|
visibility (et non display) pour permettre la transition. */
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transform: translateY(0.5rem);
|
|
transition: opacity var(--transition-base),
|
|
transform var(--transition-base),
|
|
visibility var(--transition-base);
|
|
}
|
|
|
|
.to-top[data-visible="true"] {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.to-top:hover {
|
|
color: var(--color-accent-dark);
|
|
border-color: var(--color-accent);
|
|
}
|
|
|
|
/* ======================================================================
|
|
POINT DE RUPTURE MOBILE
|
|
Une seule media query pour toute la mise en page : le reste est fluide.
|
|
Écrite en syntaxe de plage (width <= 46rem), lisible et sans le
|
|
traditionnel « max-width: 45.999rem ».
|
|
====================================================================== */
|
|
|
|
@media (width <= 46rem) {
|
|
|
|
.nav-toggle { display: inline-flex; }
|
|
|
|
.site-header__inner { position: relative; }
|
|
|
|
/* Le menu devient un panneau déroulant sous l'en-tête */
|
|
.site-nav {
|
|
position: absolute;
|
|
inset-block-start: 100%;
|
|
inset-inline: calc(var(--gutter) * -1);
|
|
display: none;
|
|
background-color: var(--color-surface);
|
|
border-block-end: 1px solid var(--color-line);
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.site-nav[data-open="true"] { display: block; }
|
|
|
|
.site-nav__list {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 0;
|
|
padding: var(--space-xs) var(--gutter) var(--space-md);
|
|
}
|
|
|
|
.site-nav__link {
|
|
/* Hauteur de cible tactile conforme (44px minimum) */
|
|
display: flex;
|
|
align-items: center;
|
|
min-block-size: var(--tap-target);
|
|
padding-inline-start: var(--space-xs);
|
|
border-block-end: 1px solid var(--color-line);
|
|
border-inline-start: 2px solid transparent;
|
|
}
|
|
|
|
/* Le repère de section active passe du bas au côté */
|
|
.site-nav__link[aria-current="true"] {
|
|
border-block-end-color: var(--color-line);
|
|
border-inline-start-color: var(--color-accent);
|
|
background-color: var(--color-accent-glow);
|
|
}
|
|
|
|
.masthead { padding-block: var(--space-2xl) var(--space-xl); }
|
|
.section { padding-block: var(--space-2xl); }
|
|
|
|
.to-top {
|
|
inset-block-end: var(--space-md);
|
|
inset-inline-end: var(--space-md);
|
|
}
|
|
}
|
|
}
|