Achatsolutions travaille constamment à la maintenance de ses plateformes web et, plus particulièrement, sur ses interfaces utilisateurs. Le CSS est une des briques principales de la conception graphique des sites web.
Rappel – Problèmes et limitations de CSS
CSS est un langage déclaratif avec une syntaxe et une méthode d’évaluation simple.
Il est basé autour du concept de sélecteurs (classe, id, élément) avec un « scope » et des propriétés avec une notion de redéfinition (en cascade).
En soit le design du langage est cohérent et bon, il est compréhensible par des non programmeurs (designers), et permet une application performante des styles par le navigateur.
Il est également flexible, car malgré 30 ans d’histoire, sa syntaxe n’a que très peu évolué.
Mais…, car il y a malheureusement un « mais», les propriétés fournies et le choix des comportements associés sont problématiques…
Les bases du langage ont été posées par des personnes compétentes telles que Pei-Yuan Wei de Berkeley, cependant l’ajout de fonctionnalités (au travers de règles et propriétés) au langage s’est faite au fil de l’eau de manière chaotique dans un contexte de guerre des navigateurs.
CSS 2.1 et CSS 3 ont tenté de rectifier le tir, cependant le mal était déjà fait. En effet, les propriétés et comportements problématiques existent toujours afin d’assurer la rétrocompatibilité. Il est alors du devoir des programmeurs de se fixer des règles strictes pour éviter une complexité de maintenance trop grande et des affichages inattendus.
Problèmes et limitations :
- Le système de layout de CSS 1 a été pensé à une époque où les pages web avaient la mise en page de documents « papier », d’où la notion de « flow layout ». Ainsi, on travaille avec des éléments qui sont des boîtes rectangulaires avec une « width », « height », « line-height » que l’on place en mode « block », « inline », ou « inline-block », que l’on peut aligner horizontalement « text-align », « float », ou verticalement dans le flux via « vertical-align ». Cela va à contrecourant des méthodes de layout par contraintes « container » traditionnelles.
- La gestion des unités de mesure est trop complexe à cause d’ajouts incessants peu justifiés. Et on peut se demander à partir de combien d’unités, le CSS se transforme en jeu Pokémon, parce que là on en est déjà à une quinzaine ! C’est dommage lorsque l’on sait que la plupart des frameworks graphique en offre deux à trois pour des applications similaires ! On remarque aussi que le mélange d’unités absolues et relatives via des opérateurs tels que « calc(…) » n’est apparu que dans la version 3…
- Méthode de calcul hasardeuses des contraintes de tailles et positionnement. Simple mais mal pensée au départ (padding/margin), elle s’est complexifiée avec le temps. Il n’y a désormais plus de règle bien définie pour savoir si la contrainte de taille parente l’emporte sur celles des éléments enfants, c’est au cas par cas, sans parler des comportements de contraintes CSS qui peuvent entrer en conflit avec celles propres à certains élément HTML comme les tables. Pas évident, surtout alors que nous sommes dans une époque où l’on souhaite être « responsive ».
- De manière générale, beaucoup de propriétés/valeurs font double emploi. Il est par exemple possible de créer des « margin » visuellement avec du « padding », mais qui entraineront des différences de calcul lors des redimensionnements, il sera alors peut-être nécessaire de changer le « box-sizing » qui lui impactera à son tour d’autres paramètres.
- CSS est un langage qui fait du graphique, seulement il aura fallu attendre CSS 2 et 3 pour avoir accès aux transformations affines de base, présentes dans tous les frameworks graphiques telles que les translations, rotations, homothéties.
- Il y a plusieurs manières de penser le code avec CSS, et rien n’est imposé (cela ne veut pas dire « c’est cool, c’est flexible », cela veut dire « c’est un cauchemar entre de mauvaises mains ou à plusieurs… »).
Exemple :
- Appliquer son style « inline » directement sur les éléments d’un fichier HTML.
- Utiliser des sélecteurs sur des éléments HTML dans un fichier CSS.
- Exploiter des sélecteurs par ID.
- Se servir des sélecteurs classes et là encore plusieurs possibilités :
- Multiplication de petites classes telles que « px-3 » ou « mx-auto » de Bootstrap qui ont une utilité réduite (c’est presque de l’équivalent d’un « margin inline » à ce stade), mais qui sont facilement réutilisables.
- Ajoute de classes spécifiques telles que « main-banner-right-button » qui ne sont pas réutilisables (dans ce cas il vaudrait mieux un ID).
- Création de classes spécifiques pour des composants qui se veulent réutilisables telles que « card-panel ».
- Il manque certaines fonctionnalités pratiques (qui ont mené à l’idée aberrante de devoir utiliser des préprocesseurs dans un cadre web) :
- Jusqu’à peu, pas possibilité de réutiliser de valeur via des variables, mais le problème est résolu depuis peu via « var(…) ».
- Un moyen de faire un reset complet de style sur un élément ou une classe. Ce qui est fâcheux lorsque l’on souhaite mélanger plusieurs bibliothèques graphiques, chacune avec leurs styles appliqués sur les éléments HTML.
- L’impossibilité de sélectionner un élément parent par sélecteur. C’est une contrainte qui peut se comprendre d’un point de vue performance d’application des classes. Cependant elle n’en reste pas moins gênante.
Bonne pratique – Conventions CSS
Conventions nécessaires :
- Se fixer un type de casse : La convention usuelle veut que tout soit nommé en minuscule et que le séparateur de mot « – » soit utilisé. Il est cependant possible comme beaucoup de framework CSS de se fixer d’autres conventions, l’important est de s’y tenir.
- Préfixer ses classes : CSS ne propose pas de moyen d’éviter les collisions de noms, et il faut donc décider un préfixe pour limiter les possibilités de collision avec d’autres framework CSS.
- Trier les propriétés de manière homogène : Il est important de déterminer un ordre pour s’y retrouver dans les propriétés d’un scope, dans le cas contraire cela devient rapidement l’anarchie.
On peut par exemple décider d’organiser par type de propriété de cette manière :
- position : « position », « top », « left », …
- affichage : « display », « box-sizing », « visibility », …
- tailles et espacement : « width », « height », « line-height », « margin », …
- alignement : « text-align », « vertical-align », …
- police : « font-family », « font-size », « font-weight », …
- bordure : « border », « border-radius », « box-shadow », …
- couleur/image : « color », « background-color », « background-image », …
- …
Exemple :
- Ne pas faire d’approximations : Un pixel en moins ou en trop cela ne peut pas fonctionner, cela va forcément créer un décalage quelque part. Une interface qui bouge lors d’un redimensionnement etc… Il faut toujours calculer et positionner aux pixel près. Et on ne peut pas se plaindre, de nos jours les navigateurs nous montrent exactement le calcul de tailles des éléments et quelle propriété est responsable en temps réel. A l’époque il fallait être rigoureux et faire des suppositions puis tester pour débugger.
Donc prendre en compte, les « margins », « paddings », « borders », et centrer les éléments à l’aide de calcul précis, c’est facilité de nos jours avec « calc(…) ».
- Ne pas créer un layout basé sur des positions fixes : Il faut proscrire l’utilisation généralisée de « position » du type « absolute », « fixed », « static » ou « sticky ». Ce type de positionnement est à utiliser avec parcimonie pour des cas bien précis. On s’évite au passage un enfer de gestion au niveau des « z-index ».
- Utiliser des valeurs homogènes et simples : Pour les couleurs hexadécimales, définir une casse et un nombre de caractères à respecter (ex : éviter d’alterner entre « #fff, #FFF, white, #FFFFFFFF, …). Pour les marges choisir des multiple (ex : 5px, 10px, 15px, 20px, ou 8px, 16px, 24px et non 2px, 5px, 14px).
- Choisir une approche générale d’utilisation des classes : Plusieurs petites classes très réutilisables comme ce que peut proposer Bootstrap. Le partage de classe entre plusieurs éléments empêchent de refondre graphiquement toute une IHM. Ces éléments n’ont potentiellement pas de rapport les uns avec les autres. Ou inversement des classes très spécifiques peu réutilisables mais qui permettent de changer facilement l’IHM.
Un bon compromis étant la création de classes de composants réutilisables avec une convention type BEM et des classes/ids spécifiques pour ce qui n’est pas composant.
Conventions souhaitables :
- Fixer la taille des polices en unités relatives : Les polices doivent avoir une taille relative à une police « root » fixe. Ou alors en laisser à la charge du navigateur (permet de bénéficier du zoom). On peut exprimer ces tailles relatives en « % » ou « rem ».
- Avoir un layout basé sur des styles et non des tags HTML :
Au début du web, le CSS ne servait que peu, le layout était effectué en HTML. Désormais il est essentiel de faire le layout 100% en CSS. Il faut proscrire l’utilisation de tags tels que « table ». Le principal bloc de construction du layout doit être l’élément « div » avec un « display » approprié.
- Faciliter la réflexion avec les espacements : Il est préférable d’utiliser des « margins » pour gérer les espacements dans un conteneur. L’espace interne d’un composant doit être le « padding ». Utiliser « box-sizing : border-box ». Il faut aussi garder des marges homogènes dans toute l’application. Pourquoi un champ de formulaire pourrait avoir une marge différente avec son libellé ?
- Faire une IHM naturellement « responsive » : Utiliser les unités de mesure relative pour la taille des composants. Avec possiblement des contraintes « min/max-width et min/max-height » en pixels. Il faut utiliser des pixels pour les marges ou padding afin qui ne varient pas.
Utilisation de styles plutôt que de pseudo-classes pour les événements : Il existe des pseudo-classes pour certains événements/états liés à l’utilisateur telles que « :hover », « :enabled », cependant la liste peut ne pas être suffisante lors de la création de composants haut niveau, il faudra alors ajouter des classes d’évènements du type « is-selected », on aura alors une asymétrie entre les évènements représentés par des classes et ceux représentés par des pseudo-classes, il peut alors être intéressant de préférer ne pas utiliser les pseudo-classes et généraliser les classes.