CSS et design continu

Pourquoi un site responsif sans media queries?

Je n’aime pas le mode plein écran. Les écrans sont horizontaux, le contenu des sites web est vertical, c’est stupide d’élargir sa fenêtre au maximum et d’avoir de l’espace vide sur les deux tiers. Je suis du genre à ajuster et réajuster la taille de mes fenêtres au cours de la journée. Pour cette raison, je n’aime pas les sites qui basculent de la mise en page « bureau » à la mise en page « mobile » dès que la fenêtre fait moins de 1400 pixels de large. En particulier, je n’aime pas le fait de basculer brutalement d’un mode à l’autre, en changeant complètement la mise en page.

lemonde.fr, 1024px / 1023px de large (cliquez pour basculer) 1

Je préfère garder la même structure entre le design « large » et le design « étroit ». Les éléments restent à la même place; les marges se réduisent ou s’agrandissent, le texte revient à la ligne plus souvent, mais la mise en page reste la même. C’est ce qu’on a fait pour le redesign de notre site.

Selon la largeur du viewport, trois propriétés de mise en page sont modifiées:

Comment ça marche?

Pas de breakpoints, mais une zone de transition.

Les breakpoints sont habituellement une notion importante du design responsif: à quelle largeur bascule-t-on d’un design à l’autre?

@media (width > 1200px) {
  /* CSS rules for desktop wide screens */
}

Au lieu de deux jeux de règles distinctes, on a mis en place un seul design avec un changement graduel entre les deux extrêmes.

Par simple interpolation, on va calculer une valeur entre O et 1, que j’appelle --lerp-px 2 dans le code. À partir de ce ratio, on va pouvoir écrire les propriétés css qu’on veut faire varier. Par exemple, pour le padding latéral (remplissage, en français selon MDN.):

body {
  padding-inline: calc(16px + 80 * var(--lerp-px));  
}

Il vaut donc:

C’est aussi simple que ça. On peut faire la même chose pour les marges verticales:

main article + article {
  margin-block-start: calc(16px + 48*var(--lerp-px));
}

Et l’interligne des titres:

h1, h2, h3 {
  line-height: calc(1.3em + 10*var(--lerp-px));
}

Comment est calculé --lerp-px?

On se base sur la largeur de la fenêtre, autrement dit le viewport, autrement dit 100vw3:

:root {
  --clamped-width: clamp(800px, 100vw, 1000px);
  --lerp-px: calc((var(--clamped-width) - 800px) / (1000 - 800));
}

Ça a l’air simple comme ça, mais ça a été un peu pénible à débugguer! J’ai écrit un mini script pour observer en direct les variables CSS qui utilise ResizeObserver sur un div dont la taille dépend de la variable à débugguer. Il est chargé sur cette page, vous pouvez ouvrir la console, taper observeCssVariable("--clamped-width") ou observeCssVariable("--lerp-px"), et jouer avec la largeur de la fenêtre.

Une dernière remarque technique: pourquoi --lerp-px et pas --lerp tout court?

Puisqu’elle est calculée à partir de <length>4 css, 100vw et 800px, la valeur est elle-même une <length> css, et pas un <number>5. Il n’y pas de moyen simple de convertir une <length> ou une <dimension> en <number> sans unité. J’ai le sentiment que la spec indique que calc(1px/1px) devrait donner un <number>, mais ce n’est pas ce que j’obtiens.

Je suis preneur d’explications! Peut-être qu’il y aurait une solution en utilisant les @property. J’ai déjà passé beaucoup de temps à ces quelques pixels, ça devra attendre.

C’est tout pour le moment, merci d’avoir lu jusque là! N’hésitez pas à nous dire ce que vous en pensez, par mail, sur Mastodon ou sur le dépot.


  1. Je précise que les énormes marges blanches de la mise en page « mobile » ne sont pas dues à mon bloqueur de pub. La bannière grise sur la mise en page « bureau », oui. ↩︎

  2. Wikipedia: “In computer graphics jargon Linear Interpolation is sometimes called a lerp.” ↩︎

  3. https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_viewport ↩︎

  4. https://developer.mozilla.org/en-US/docs/Web/CSS/length ↩︎

  5. https://developer.mozilla.org/en-US/docs/Web/CSS/number ↩︎