improved responsive framework

This commit is contained in:
2026-10-20 20:45:21 +02:00
parent 15ae4466e7
commit 07bcb7a6c2
10 changed files with 91 additions and 85 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7 -7
View File
@@ -44,13 +44,13 @@ gsap.registerPlugin(ScrollTrigger);
console.log('gsap_mm', gsap_mm);
// Frontière mobile ↔ tablette : source unique = _breakpoints.scss,
// publiée en variable CSS --bp-mobile par _base.scss. Plus de littéral
// ici — le couple mq(mobile)/mq-above(mobile) du CSS est exclusif par
// construction, le JS doit l'être aussi (bug « à 768px pile », review #94).
const bpMobile = getComputedStyle(document.documentElement)
.getPropertyValue('--bp-mobile').trim() || '767px';
// Condition desktop STRICTEMENT au-dessus de la zone mobile, complémentaire du CSS.
const desktopQuery = `(min-width: calc(${bpMobile} + 1px))`;
// publiée en variable CSS --bp-tablette (début inclusif de la zone
// tablette) par _base.scss. Plus de littéral ici — même requête que
// $desktop-et-tablette, complément de $mobile (max-width: 767.98px) :
// le JS est exclusif du CSS mobile (bug « à 768px pile », review #94).
const bpTablette = getComputedStyle(document.documentElement)
.getPropertyValue('--bp-tablette').trim() || '768px';
const desktopQuery = `(min-width: ${bpTablette})`;
// Instance Lenis partagée : initPageFull en a besoin pour resynchroniser la
// plage de scroll après avoir gonflé le spacer (voir ensureScrollForTitle).
@@ -8,19 +8,26 @@
// Aucun CSS émis. LA seule maison des seuils de largeur du thème.
//
// ─── Modèle ──────────────────────────────────────────────────────────────────
// Thème desktop-first. Trois familles de variables :
// Thème desktop-first. Chaque valeur de $bp est le DÉBUT d'une zone (sa
// première largeur, inclusive). Une zone finit à « début suivant − 0.02px »
// ($fin) : syntaxe min-/max-width classique, compatible partout (la syntaxe
// d'intervalle « width < X » exige Safari ≥ 16.4). Le 0.02px (convention
// Bootstrap) réduit le trou entre deux zones aux largeurs fractionnaires
// (zoom, DPR non entier) à l'intervalle ]767.98, 768[ — en pratique jamais.
//
// narrow < 432 ≤ mobile < 768 ≤ tablette < 1024 ≤ desktop
//
// 1. Zones imbriquées vers le bas — « cette zone et tout ce qui est
// plus étroit » :
// @media #{$tablette}{ ... } ≤ 1023 (contient mobile)
// @media #{$mobile}{ ... } ≤ 767 (contient narrow)
// @media #{$narrow}{ ... } ≤ 431
// @media #{$tablette}{ ... } < 1024 (contient mobile)
// @media #{$mobile}{ ... } < 768 (contient narrow)
// @media #{$narrow}{ ... } < 432
//
// 2. Le grand format — desktop + tablette ensemble (≥ 768) :
// @media #{$desktop-et-tablette}{ ... }
// C'est le complément EXACT de $mobile (aucune largeur ne matche les
// deux) : pour les composants écrits en deux blocs explicites
// (grand format / mobile).
// C'est le complément de $mobile (aucune largeur ne matche les deux) :
// pour les composants écrits en deux blocs explicites (grand format /
// mobile).
//
// 3. Formats seuls — exactement une zone, ni plus ni moins :
// @media #{$seulement-desktop}{ ... } ≥ 1024
@@ -29,41 +36,46 @@
// (seulement-narrow ≡ $narrow : rien en dessous.)
//
// ─── Disciplines ────────────────────────────────────────────────────────────
// 1. Jamais de nombre brut égal à un seuil : interpoler une variable de zone.
// 1. Jamais de nombre brut égal à un seuil : interpoler une requête nommée.
// 2. Une valeur découverte pendant le pass responsive s'écrit brute avec un
// commentaire et se consolide ICI quand 2+ composants convergent dessus.
// 3. Le JS consomme ces seuils par les variables CSS --bp-* (posées dans
// _base.scss) : plus jamais de littéral dans main.js. Le bug « à 768px
// pile » de la review venait d'un chevauchement CSS/JS — les zones sont
// exclusives par construction, le JS doit l'être aussi.
// pile » de la review (#94) venait d'un chevauchement CSS/JS — les zones
// sont exclusives par construction, le JS doit l'être aussi.
// 4. Un futur leshed.breakpoints.yml (images responsives) doit reprendre
// exactement ces valeurs.
// 5. L'ancrage du layout (1112px) n'est pas un seuil : il est calculé dans
// _variables.scss ($layout-anchor).
@use "sass:map";
$bp: (
// Ancre du layout (contenu + padding proportionnels) — référencée par _fluid.
layout: 1112px,
// Frontière tablette ↔ desktop : la zone desktop commence À 1024 (inclusive).
// Début de la zone mobile ; en dessous : narrow (accueil) — découverte
// à re-valider au pass 1 (432 = l'iPhone Pro Max à 430 reste narrow).
mobile: 432px,
// Début de la zone tablette = fin du mobile. 768 pile est tablette :
// c'est la frontière CSS/JS du bug #94.
tablette: 768px,
// Début de la zone desktop.
desktop: 1024px,
// Frontière mobile ↔ tablette : la zone mobile finit À 767 (inclusive).
// 767 et pas 768 : à 768 pile, l'ancien couple max-width:768/min-width:769
// laissait la requête mobile cohabiter avec le JS desktop — review #94.
mobile: 767px,
// Zone étroit (accueil) — découverte à re-valider au pass 1.
narrow: 431px,
);
// Dernière largeur d'une zone : juste avant le début de la suivante.
@function fin($zone-suivante) {
@return map.get($bp, $zone-suivante) - 0.02px;
}
// ─── Zones imbriquées vers le bas ────────────────────────────────────────────
// Dérivées de $bp : changer un seuil recalcule tout (CSS, --bp-*, JS).
$tablette: "(max-width: calc(#{map.get($bp, desktop)} - 1px))";
$mobile: "(max-width: #{map.get($bp, mobile)})";
$narrow: "(max-width: #{map.get($bp, narrow)})";
$tablette: "(max-width: #{fin(desktop)})";
$mobile: "(max-width: #{fin(tablette)})";
$narrow: "(max-width: #{fin(mobile)})";
// Le grand format : desktop + tablette — complément exact de $mobile.
$desktop-et-tablette: "(min-width: calc(#{map.get($bp, mobile)} + 1px))";
// Le grand format : desktop + tablette — complément de $mobile.
$desktop-et-tablette: "(min-width: #{map.get($bp, tablette)})";
// ─── Formats seuls ───────────────────────────────────────────────────────────
$seulement-desktop: "(min-width: #{map.get($bp, desktop)})";
$seulement-tablette: "(min-width: calc(#{map.get($bp, mobile)} + 1px)) and (max-width: calc(#{map.get($bp, desktop)} - 1px))";
$seulement-mobile: "(min-width: calc(#{map.get($bp, narrow)} + 1px)) and (max-width: #{map.get($bp, mobile)})";
$seulement-tablette: "(min-width: #{map.get($bp, tablette)}) and (max-width: #{fin(desktop)})";
$seulement-mobile: "(min-width: #{map.get($bp, mobile)}) and (max-width: #{fin(tablette)})";
@@ -11,7 +11,7 @@
header{
.field_dates{
@include event-date-time(5em);
@include event-date-time(3.5em);
span.date-sep{
display: block;
width: 1em;
@@ -1,9 +1,8 @@
@use "sass:math";
@use "fluid" as *;
@use "../abstracts/breakpoints" as *;
@mixin event-date-time($ref: 10em){
@mixin event-date-time($ref: 7em){
display: flex;
position: relative;
// Métriques propres aux dates : elles dépendaient avant du bloc de masquage
@@ -16,9 +15,6 @@
// dérivent de --ref : le bloc entier rétrécit en lock-step, sans désaligner
// la grille par rapport à la typo. NB : les em sont convertis via px() sur
// hypothèse d'un contexte de 16px.
@media #{$mobile}{
$ref:calc($ref * 0.7);
}
--ref: #{fluid-clamp(px($ref), $min: math.div(px($ref), 1.5))};
time{
position: relative;
@@ -11,13 +11,13 @@
// → la taille vaut 5rem tant que le viewport tient le layout complet
// (contenu + padding latéral), suit la largeur du viewport en dessous,
// plancher $min.
// Le point de passage est dérivé de $layout-anchor : recalage
// automatique si le plafond du layout ou son padding changent.
// Le point de passage est dérivé de $layout-anchor (_variables.scss) :
// recalage automatique si le plafond du layout ou son padding changent.
// Les bornes sont émises en rem : elles suivent la taille de police
// choisie par l'utilisateur (identiques au px près avec <html> à 16px).
@use "sass:math";
@use "sass:map";
@use "variables" as *;
@use "breakpoints" as *;
$root: 16px; // taille de police de <html>
@@ -32,20 +32,20 @@ $root: 16px; // taille de police de <html>
@error "px() : px, rem ou em attendu, reçu #{$size}";
}
// Largeur d'ancrage des clamps : le viewport auquel le layout est plein —
// 1080px de contenu + 2 × 16px de padding = 1112px. Vit dans $bp.layout
// (_breakpoints.scss, la seule maison des largeurs) — alias local pour les
// signatures de fonctions ci-dessous.
$layout-anchor: map.get($bp, layout);
// Convertit px, rem ou em en rem (même hypothèse de base 16px que px()).
@function to-rem($size) {
@return math.div(px($size), $root) * 1rem;
}
// La valeur vw qui vaut exactement $size quand le viewport fait $at.
// La valeur vw qui vaut exactement $size quand le viewport fait $at
// ($layout-anchor par défaut : 1080 de contenu + 2 × padding = 1112px).
@function vw-at($size, $at: $layout-anchor) {
@return math.div(px($size), $at) * 100 * 1vw;
}
// $size au-dessus de $at, fluide en dessous, plancher $min.
@function fluid-clamp($size, $at: $layout-anchor, $min: 0px) {
@return clamp(#{px($min)}, #{vw-at($size, $at)}, #{px($size)});
@return clamp(#{to-rem($min)}, #{vw-at($size, $at)}, #{to-rem($size)});
}
// Tokens dérivés : tailles de référence passées en fluide. Le plancher est
@@ -1,10 +1,22 @@
@use "sass:math";
$layout_width: 1080px;
// Padding et gap proportionnels : sous l'ancrage du layout (voir _fluid.scss,
// $layout-anchor), TOUT rétrécit ensemble (typo fluide, gouttières, colonnes) —
// les clamps vw restent exacts à toutes les largeurs. Les vw sont calés pour
// valoir exactement les bornes hautes à l'ancrage (1112px) : au-dessus, le
// design desktop est identique à l'ancien 1em/2em fixe.
$layout_padding: clamp(0.75rem, 1.4388vw, 1rem);
$default_fwght: 200;
$header_height: 70px;
$colsgap: clamp(1.5rem, 2.8777vw, 2rem);
// Bornes hautes du padding latéral et de la gouttière : leur valeur au-dessus
// de l'ancrage du layout (design desktop = l'ancien 1em/2em fixe).
$layout_padding_max: 1rem;
$colsgap_max: 2rem;
// Ancrage du layout : le viewport auquel le layout est plein — contenu +
// 2 × padding latéral (1080 + 2 × 16 = 1112px). Calculé, pas écrit : changer
// $layout_width ou $layout_padding_max recale tous les clamps vw (padding,
// gouttière, typo fluide de _fluid.scss). Hypothèse : <html> à 16px.
$layout-anchor: $layout_width + 2 * math.div($layout_padding_max, 1rem) * 16px;
// Padding et gap proportionnels : sous l'ancrage, TOUT rétrécit ensemble
// (typo fluide, gouttières, colonnes) — les clamps vw restent exacts à toutes
// les largeurs. Le vw vaut exactement la borne haute à l'ancrage.
$layout_padding: clamp(0.75rem, math.div(math.div($layout_padding_max, 1rem) * 16px, $layout-anchor) * 100vw, $layout_padding_max);
$colsgap: clamp(1.5rem, math.div(math.div($colsgap_max, 1rem) * 16px, $layout-anchor) * 100vw, $colsgap_max);
@@ -9,12 +9,12 @@
@use "../abstracts/breakpoints" as *;
// Seuils publiés en variables CSS : le JS (gsap.matchMedia) les lit au lieu
// de littéraux — contrat de valeur unique, voir _breakpoints.scss.
// de littéraux — contrat de valeur unique, voir _breakpoints.scss. Chaque
// valeur est le début (inclusif) de sa zone.
:root{
--bp-layout: #{map.get($bp, layout)};
--bp-desktop: #{map.get($bp, desktop)};
--bp-mobile: #{map.get($bp, mobile)};
--bp-narrow: #{map.get($bp, narrow)};
--bp-tablette: #{map.get($bp, tablette)};
--bp-desktop: #{map.get($bp, desktop)};
}
@@ -11,9 +11,14 @@
div.views-archives-projets{
display: flex;
flex-direction: row;
flex-wrap: wrap;
// Design : 4 colonnes ou 2, jamais 3 — grille explicite qui bascule au
// seuil mobile. Une dernière rangée incomplète ne s'étire pas.
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: $colsgap;
@media #{$mobile}{
grid-template-columns: repeat(2, minmax(0, 1fr));
}
article.node-type-projet.view-mode-tableau_liste{
@@ -27,25 +32,6 @@ div.views-archives-projets{
}
// DESKTOP + TABLETTE (≥ 768) — gouttière + rangées de 4.
@media #{$desktop-et-tablette}{
gap: $colsgap;
div.views-row{
flex: 1 0 calc(25% - $colsgap);
width: calc(25% - $colsgap);
}
}
// MOBILE (≤ 767) : 2 par ligne.
@media #{$mobile}{
gap: $colsgap;
div.views-row{
flex: 1 0 calc(50% - $colsgap);
width: calc(50% - $colsgap);
}
}
}
form#views-exposed-form-archives-page-1{
div.bef-links{