CSS « fonctionnel » et responsive design

J’ai lu aujourd’hui un article très intéressant qui soulignait les bénéfices du CSS dit « fonctionnel » (on entend par fonctionnel l’utilisation généralisée de classes utilitaires à but unique). Le hic, c’est qu’il néglige également de parler d’un inconvénient majeur et de la manière de le traiter.


Un exemple pour commencer…

Avant de rentrer dans le vif du sujet il peut être utile de faire un rappel de ce qu’est le CSS fonctionnel, cf. cette définition tirée de CSS-Tricks :

Le CSS atomique (fonctionnel) est l’approche, en architecture CSS, qui favorise les petites classes à but unique avec des noms basés sur leurs fonctions visuelles.

Voici un exemple simple, mais volontairement verbeux pour plus de clarté :

See the Pen GvmOzV by Ronan Levesque (@eskiiss) on CodePen.

L’avantage saute immédiatement aux yeux; cette manière de faire permet de gagner en maintenabilité et en temps de développement. Cependant l’article élude un sujet capital et auquel la plupart des frameworks CSS qui suivent cette méthode peinent à offrir une solution pertinente : la transformabilité des dites classes.

J’entends par là, la capacité d’une classe à faire évoluer ses styles (via media queries).

Reprenons l’exemple ci-dessus : nos classes utilitaires vont très bien marcher pour une taille d’écran standard, mais qu’en est-il si on souhaite ajuster le bloc pour des petits / grands écrans ? Si on décide de maintenir une valeur par classe, alors on se retrouve avec un code assez horrible :

See the Pen BdRYyJ by Ronan Levesque (@eskiiss) on CodePen.

Dans cet exemple, il n’y a que 3 breakpoints. Mais on imagine aisément l’usine à gaz que cela va devenir si on doit en gérer 5, voire plus (cas fréquents lorsque l’on fait du responsive design).


Pour avoir expérimenté le CSS fonctionnel dans le passé, je vois les avantages qu’on peut tirer de cette méthodologie. Mais je reste mitigé car elle risque d’induire un niveau de complexité supplémentaire dans la gestion des classes. En inspectant le code de Tailwind CSS, créé par Adam Wathan (auteur de l’article), je me trouve par exemple face à ce genre de chose :

<h1 class="text-2xl sm:text-3xl text-thin text-dark-soft constrain-sm mx-auto leading-tight mb-8">A Utility-First CSS Framework for Rapid UI Development</h1>

Et j’ai du mal à voir en quoi ça serait plus maintenable ou lisible que :

<h1 class="main-title">A Utility-First CSS Framework for Rapid UI Development</h1>

Ou alors, il faut envisager une approche différente.

Utilitaires + media queries

À la lumière de ces différents exemples, on comprendra sans peine pourquoi il est sans doute préférable de traiter d’une autre manière les utilitaires contenant une valeur susceptible de changer selon les breakpoints (margin, padding, font-size etc.)…

Reprenons notre exemple de random block. On sait que les valeurs de padding et de font-size doivent évoluer d’un breakpoint à un autre. Mais on veut garder cette notion de classe utilitaire, réutilisable ailleurs. On peut alors imaginer une approche identique à ceci :

See the Pen ZJXyMY by Ronan Levesque (@eskiiss) on CodePen.

Rien ne nous empêche par la suite de créer des classes telles que padding-small et padding-large, text-small et text-large etc., qui seront toutes en charge de traiter leurs propres breakpoints.

Ainsi, on abstrait les notions de transformabilité au sein de la classe utilitaire, qui garde son côté réutilisable. Cela permet également de renforcer la consistance de nos styles.

CSS fonctionnel et composants

Adam précise dans son article qu’il n’est pas en faveur d’une approche 100% fonctionnelle car selon lui certains cas nécessitent d’utiliser des composants; il prend notamment un exemple tiré de Tachyons utilisé pour créer un bouton et démontre qu’il serait plus simple d’en faire un composant réutilisable.

En fait, toute la difficulté de l’exercice revient à savoir quand utiliser un utilitaire et quand utiliser un composant.

D’ailleurs, on pourrait même arriver à la conclusion qu’un utilitaire est en fait simplement un composant à but unique. 🤔