7min.

4 astuces CSS pour bien commencer l’année !

L’année 2022 a été, pour ma part, très riche en projets Web. 🎉

Ce fut donc l’occasion rêvée d’expérimenter de nouvelles méthodologies d’intégration, de passer du côté obscur de la force en adoptant Tailwind CSS 😏, et bien évidemment, de m’amuser avec les dernières fonctionnalités CSS à la mode.

Afin de bien démarrer 2023, j’avais donc envie de vous partager 4 astuces CSS qui m’ont simplifié la vie l’an passé, et qui, je l’espère, vous seront également utiles pour vos prochains projets ! 🙂

Cet article se veut accessible au plus grand nombre, ces astuces sont rapides à mettre en place et facilement applicables en production via des cas d’usage concrets.

Non, je ne parlerai pas de la pseudo-classe :has(), ni des CSS Container Queries !

Section intitulée la-fin-du-quot-padding-hack-quot-avec-la-propriete-css-code-aspect-ratio-codeLa fin du « padding hack » avec la propriété CSS aspect-ratio

Il y a quelque temps, maintenir un ratio spécifique pour une image, une vidéo, ou une iframe, nécessitait généralement de passer par un hack à base de padding et de position: absolute.

Cette technique a été utilisée de très nombreuses années, et disons-le, elle n’était pas des plus élégantes.

Pour rappel, le CSS ressemblait à ça (exemple avec un média au format 16 / 9) :

.container {
  position: relative;
  padding-bottom: 56.25%; /* 9 / 16 * 100% */
}

.media {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

L’arrivée de la propriété CSS aspect-ratio et de sa prise en charge dans la plupart des navigateurs modernes (👋 RIP Internet Explorer) a donc véritablement changé la donne pour notre plus grand bien.

Le code précédent se simplifie maintenant de cette façon :

.media {
  aspect-ratio: 16 / 9;
}

Oui, en une seule propriété CSS ! 😍

Dernièrement, j’avais pour mission d’afficher une galerie d’images au format 16 / 9 sur grands écrans, et au format carré sur tablettes et mobiles. En complément de l’élément HTML <picture> (à utiliser pour des décisions de nature artistique), le CSS se décline simplement de cette façon :

@media screen and (min-width: 1024px) {
  .media {
    --media-aspect-ratio: 16 / 9;
  }
}

.media {
  max-width: 100%;
  height: auto;
  aspect-ratio: var(--media-aspect-ratio, 1);
  object-fit: cover;
}

La propriété CSS object-fit permet de gérer la façon dont l’image sera rognée, mise à l’échelle ou étirée en fonction des dimensions de celle-ci, elle est une très bonne alliée de la propriété CSS aspect-ratio. 👍

Pour conclure, la propriété CSS aspect-ratio permet également, à l’instar des attributs width et height propres aux éléments remplacés, d’éviter les « layout shifts » en allouant la place nécessaire avant le chargement du média, comme dans l’exemple précédent.

Section intitulée code-position-fixed-code-est-mort-vive-code-position-sticky-codeposition: fixed est mort, vive position: sticky ! 👑

Section intitulée un-rapide-etat-des-lieuxUn rapide état des lieux

Le positionnement sticky est un mélange entre le positionnement relatif et fixe. L’élément sticky est considéré comme étant relatif jusqu’à ce qu’il franchisse un seuil spécifié, au-delà duquel, il est considéré comme fixe.

Ce positionnement s’avère très pratique pour reproduire ce type d’effet :

Animation d'une barre latérale en position sticky

La structure HTML en question :

<header class="header">
  Header
</header>
<main class="container">
  <div class="content">
    Lorem ipsum...
  </div>
  <aside class="sidebar">
    Sticky sidebar...
  </aside>  
</main>

Et le CSS associé :

.container {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-items: start;
}

.content {
  grid-column: span 8;
}

.sidebar {
  position: sticky;
  top: 0;
  grid-column: span 4;
}

Plutôt facile, non ? 🥳

Pour celles et ceux qui s’en souviennent, ce comportement n’était possible, à l’époque, qu’en JavaScript. Et cette fois-ci, c’était loin d’être une partie de plaisir ! 😅

Section intitulée cas-d-ecole-un-en-tete-fixeCas d’école : Un en-tête fixe

Il y a quelques mois, j’ai découvert un autre cas d’utilisation fort pratique, en partant d’un exemple connu, illustré ci-dessous :

Animation d'un en-tête en position fixe

La méthode classique pour réaliser un en-tête fixe est d’utiliser… 🥁 le positionnement fixe !

:root {
  --header-height: 12rem;
}

.header {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 100;
  height: var(--header-height);
}

.container {
  padding-top: calc(var(--header-height) + 2.4rem);
}

À priori, ce code ne pose pas de réelles problématiques, mais j’aimerais tout de même attirer votre attention sur un point en particulier.

Petit aparté : Un élément positionné de manière fixe sort du flux normal du document, par conséquent, aucun espace n’est attribué pour cet élément.

Dans notre exemple, le conteneur (ou tout autre élément du flux), devra donc être décalé verticalement, d’une valeur correspondant à la hauteur de l’en-tête, afin de ne pas être caché par celui-ci.

Il est donc indispensable de définir explicitement la hauteur de l’en-tête, comme vous pouvez le voir à la racine du document (via la pseudo-classe :root), ce qui peut, cette fois-ci, entraîner quelques soucis : la hauteur de l’en-tête ou les espacements verticaux internes ou externes du conteneur peuvent varier en fonction de la largeur de la zone d’affichage, ou bien encore, en fonction des différentes mises en page.

Cette contrainte nécessite donc de devoir calculer en permanence la hauteur de l’en-tête, en fonction des différents scénarios évoqués précédemment, afin de décaler correctement les éléments s’affichant dans le flux normal du document.

L’idéal serait, en réalité, de pouvoir combiner les avantages du positionnement relatif (l’élément est positionné dans le flux normal du document) et du positionnement fixe. 🤔

Tada ! 🥳

.header {
  position: sticky;
  top: 0;
  z-index: 100;
}

Et oui, tout simplement ! Ce code est beaucoup plus flexible, vous ne trouvez pas ?

Section intitulée code-position-absolute-code-vs-code-display-grid-codeposition: absolute vs display: grid ⚔️

Pour cette troisième astuce, je vous propose un autre cas d’école, l’implémentation d’une carte (card) basique :

Carte basique

Commençons par la structure HTML :

<article class="card">
  <header class="card__header">
    <h2 class="card__title">
      <a href="#" class="card__link">Card title</a>
    </h2>
    <img src="https://picsum.photos/id/443/248/180" width="248" height="180" alt="Mountain landscape" class="card__img" />
  </header>
  <div class="card__content">
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    </p>
    <p>
      <strong>$99</strong>
    </p>
  </div>
</article>

Puis le CSS (version simplifiée pour l’exemple) :

.card {
  /* ui styles */  
}

.card__header {
  position: relative;
}

.card__title {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: flex-end;
}

.card__content {
  /* ui styles */  
}

Concentrons-nous plus particulièrement sur l’en-tête.

La structure HTML est assez simple, l’en-tête contient un titre, un lien et une image.

Pour la partie CSS, la solution classique afin de superposer un titre sur une image est d’utiliser le positionnement absolu position: absolute, comme montré dans l’exemple… jusque là tout va bien.

Malheureusement, une semaine après, le client formule une nouvelle demande :

Client : J’aimerais que toute la carte soit cliquable, svp, merci.

Chef de projet : Mais bien sûr ! On (Jonathan) s’en occupe tout de suite ! 👍

Moi : C’était dans les specs ? 😒

La solution la plus simple afin de créer un lien étiré (stretched link) est d’utiliser les pseudo-éléments ::before ou ::after :

.card__link::after {
  position: absolute;
  inset: 0;
  content: '';
}

Mais, quelle ne fut pas ma surprise au moment de découvrir que le lien ne s’étendait pas à l’extérieur de l’en-tête 😮 :

Carte basique (placement absolu)

(En fait, je savais pertinemment que cela ne fonctionnerait pas, parce que je suis l’auteur de cet article, et par conséquent… bref !).

Ce comportement est, en réalité, parfaitement logique : les éléments relatifs et absolus ont pour particularité d’être positionnés. Cela implique que le pseudo-élément généré de manière absolue, sera positionné par rapport à son plus proche ancêtre positionné, à savoir, le titre, lequel étant lui-même positionné de façon absolue par rapport à son en-tête, CQFD.

Cette solution ne peut donc pas fonctionner en l’état.

Heureusement, le modèle de disposition en grille (CSS Grid Layout) permet de simuler le positionnement absolu à l’aide des placements explicites.

Pour faire simple, il est possible de forcer les éléments enfants d’une grille à se superposer, grâce, notamment, aux valeurs 1 / -1 associées à la propriété CSS grid-area (propriété raccourcie pour grid-row-start, grid-column-start, grid-row-end et grid-column-end).

Modifions légèrement le CSS initial :

.card {
  position: relative;  
}

.card__header {
  display: grid;
}

.card__title {
  z-index: 10;
  display: flex;
  grid-area: 1 / -1;    
  align-items: flex-end;
}

.card__link::after {
  position: absolute;
  inset: 0;
  content: '';
}

.card__img {
  grid-area: 1 / -1;
}

.card__content {
  /* ui styles */  
}

Contrairement à tout à l’heure, le titre et l’en-tête n’étant plus positionnés, notre carte devient donc entièrement cliquable :

Carte basique (modèle de disposition en grille)

Et voilà ! 🙂

Section intitulée code-gap-code-ou-pas-code-gap-codegap ou pas gap ?

Pour conclure, j’aimerais vous présenter une petite astuce que j’utilise quotidiennement afin de gérer les espacements verticaux (applicable également aux espacements horizontaux).

Prenons cet exemple :

Liste d'éléments)

Une marge verticale doit être présente entre chaque élément de cette liste, sans affecter les marges internes de celle-ci.

Pour ce faire, deux approches sont traditionnellement employées.

La première approche consiste à réinitialiser la valeur de la marge basse du dernier élément de la liste :

.list {
  padding: 2.4rem;
}

.list__item {
  margin-bottom: 2.4rem;
}

.list__item:last-child {
  margin-bottom: 0;
}

La seconde approche consiste à utiliser la pseudo-classe de négation :not() :

.list {
  padding: 2.4rem;
}

.list__item:not(:last-child) {
  margin-bottom: 2.4rem;
}

Dans les deux cas, nous sommes contraints d’augmenter la spécificité du dernier élément de la liste, avec pour conséquence directe, de potentiellement subir des problèmes de surcharge.

Heureusement, la propriété CSS gap, applicable désormais au module de boîtes flexibles (flexbox), nous apporte une solution beaucoup plus élégante 🙂 :

.list {
  display: flex;
  flex-direction: column;
  gap: 2.4rem;
  padding: 2.4rem;
}

Section intitulée un-petit-mot-pour-la-finUn petit mot pour la fin

J’espère sincèrement, qu’à travers ces cas pratiques, ces quelques astuces CSS pourront vous aider dans votre travail quotidien.

Sur ce, je vous souhaite une très belle année 2023, et vive le CSS ! 🎉🥳

Commentaires et discussions

Nos articles sur le même sujet

Nos formations sur ce sujet

Notre expertise est aussi disponible sous forme de formations professionnelles !

Voir toutes nos formations

Ces clients ont profité de notre expertise