Architecture CSS pour les systèmes de conception

Nous venons de créer un système de conception pour une grande organisation et d’établir une architecture CSS qui nous plaît beaucoup. C’est l’une des premières fois que j’arrive à la ligne d’arrivée d’un projet sans souhaiter avoir fait au moins quelques choses différemment. J’ai donc pensé que ce serait génial de partager comment nous avons créé l’architecture CSS de notre système.

Pour donner un peu de contexte au projet, nous avons été chargés de créer un système de conception et un guide de style destinés aux milliers de développeurs de l’organisation, qui utilisent une vaste gamme de technologies pour créer leurs plus de 500 applications Web internes.

La grande majorité des développeurs de l’organisation ne se spécialisent pas dans le développement frontend, mais se concentrent plutôt sur la programmation d’applications, les données et la logique. Parce que ces développeurs qui ont besoin de temps pour mettre leurs applications en service rapidement, ils copient et collent souvent simplement du code frontal à partir d’autres applications ou recherchent des frameworks comme Bootstrap pour faire le travail de l’interface utilisateur. Comme vous pouvez vous y attendre, le résultat cumulatif de ces actions est un méli-mélo d’expériences Web incongrues. Bien sûr, c’est ce à quoi nous voulions remédier en construisant à l’organisation son propre système de conception d’interface utilisateur réfléchi et robuste.

Établir les principes CSS

Au début du projet, nous avons discuté avec les développeurs de leur processus et de leurs points douloureux, et nous leur avons demandé comment un système de conception d’interface pouvait leur faciliter la vie.

Nous avons parcouru et rempli mon questionnaire sur les directives frontend, ce qui a abouti à un ensemble de principes frontend qui devaient être encapsulés dans le système. Voici les principes spécifiques au CSS que nous avons établis:

  • Rendez-le modulaire. – Le système de conception est modulaire dans tous les sens, ce qui s’applique beaucoup à la façon dont CSS est écrit. Il devrait y avoir une séparation claire entre les composants.
  • La lisibilité est la clé. – Les développeurs doivent être capables de comprendre le code CSS en un coup d’œil et de comprendre le but d’un sélecteur donné.
  • La clarté l’emporte sur la concision – Le système de conception peut parfois sembler verbeux, mais il offre clarté et reslience en échange. Garder CSS lisible et évolutif signifie sacrifier une syntaxe plus courte.
  • Gardez les choses à plat – Les chaînes de sélection longues doivent être évitées dans la mesure du possible afin de garder CSS aussi indépendant du DOM et modulaire que possible.
  • Évitez les conflits – Étant donné que les composants seront déployés sur de nombreuses applications différentes, il est essentiel de s’assurer que le CSS du système de conception n’entre pas en conflit avec d’autres bibliothèques et systèmes. Ceci est accompli par l’espace de noms des noms de classes du système, décrit plus en détail ci-dessous.

À partir de là, nous avons établi des conventions et une syntaxe qui embrassaient ces principes afin de répondre aux besoins des développeurs. Voici un aperçu de la syntaxe de classe que nous avons proposée:

Espace de noms global

Toutes les classes associées au système de conception sont préfixées par un espace de noms global, qui est le nom de l’entreprise suivi d’un trait d’union:

.cn-

Si vous travaillez sur une architecture CSS qui n’est destinée qu’à un seul site ou si vous avez beaucoup de contrôle sur votre environnement, y compris un espace de noms global est probablement inutile. Mais si votre système de conception se mêle à d’autres technologies, il peut être judicieux de créer un identifiant pour un code spécifique au système. Lightning Design System utilise une approche similaire pour son système (avec le préfixe .slds-) car les développeurs tiers utilisent leur système dans des environnements que Salesforce peut ne pas contrôler. Dans notre cas, de nombreux développeurs de nos clients utilisent Angular, ils connaissent donc déjà la notion d’espace de noms, car Angular utilise ng- comme espace de noms pour du code spécifique à Angular.

Préfixes de classe

En plus d’un espace de noms global, nous avons ajouté des préfixes à chaque classe pour rendre plus évident le travail effectué par cette classe. Voici sur quels préfixes de classe nous avons atterri:

J’ai été initié à ce concept par Harry Roberts, et même si je pensais qu’ils avaient du sens, j’étais un peu sceptique au début simplement parce qu’il s’agissait de caractères supplémentaires et je pensais que les préfixes pourraient réellement diminuer la lisibilité du code. Ce n’était pas du tout le cas. Après avoir implémenté les préfixes de classe, nous les avons trouvés extrêmement utiles pour clarifier le rôle de chaque classe et faciliter le déchiffrement de la base de code d’une application en un coup d’œil. Ce type de clarté est particulièrement utile pour que les utilisateurs du système de conception puissent facilement créer des têtes ou des queues de choses.

Syntaxe BEM

BEM signifie « Modificateur d’élément de bloc », ce qui signifie:

Cette méthodologie a gagné en popularité, et la combinaison de ces concepts avec l’espace de noms global et les préfixes de classe nous a permis de créer des noms de classe encore plus explicites et encapsulés.

Tout mettre ensemble: anatomie d’une classe

La combinaison d’un espace de noms global, de préfixes de catégorie et de syntaxe BEM donne une chaîne de classe explicite (et oui, verbeuse) qui permet aux développeurs de déduire quel travail elle joue dans la construction de l’interface utilisateur.

Jetons un coup d’œil à l’exemple suivant:

.cn-c-btn--secondary
  • cn- est l’espace de noms global pour tous les styles provenant du système de conception.
  • c- est la catégorie de classe, qui dans ce cas c- signifie « composant »
  • btn est le nom du bloc (« Block » étant le « B » dans BEM)
  • --secondary est un modificateur, indiquant une variation stylistique du bloc (« Modificateur » étant le « M » dans BEM)

Voici un autre exemple:

.cn-l-grid__item
  • cn- encore une fois, l’espace de noms global du système.
  • l- est la catégorie de classe, qui dans ce cas l- signifie « mise en page »
  • grid est-ce que le nom du bloc
  • __item est un élément, indiquant qu’il s’agit d’un enfant du bloc (« Element » étant le « E » dans BEM)

Et un de plus:

.cn-c-primary-nav__submenu
  • cn- est l’espace de noms global du système.
  • c- est la catégorie de la classe, qui dans ce cas c - signifie « composant »
  • primary-nav est le nom du bloc
  • __submenu est un élément, indiquant qu’il s’agit d’un enfant du bloc (« Élément » étant le « E » dans BEM)

Encore une fois, il ne fait aucun doute que ces classes sont plus verbeuses que la plupart des autres approches, mais pour ce système spécifique, ces conventions avaient beaucoup de sens.

Autres astuces

Être explicite avec minutie

Afin d’éviter que les choses ne s’effondrent, nous avons détaillé comment gérer de nombreux détails mineurs comme les commentaires, l’espacement autour des blocs de code, les onglets et les espaces, etc. Heureusement, Harry Roberts a mis en place une ressource excellente et complète appelée Lignes directrices CSS, que nous avons utilisée comme base de référence pour ce type de conventions. Nous avons tout passé au peigne fin et signalé les zones où nous avions prévu de nous écarter de ce que Harry a énoncé.

Sélecteurs parents Sass

Un problème que j’ai toujours eu avec CSS est de savoir où mettre une règle donnée. Si j’ai un composant de navigation primaire, mais que je souhaite ajuster son alignement lorsqu’il apparaît dans un composant d’en-tête, dois-je mettre ces styles dans mon en-tête ou mon Sass de navigation primaire partiel ? Heureusement, les sélecteurs parents Sass existent, ce qui nous permet de conserver tous les styles spécifiques aux composants sous un même toit:

Cela signifie que tous mes styles de navigation principaux peuvent être trouvés dans la navigation principale partielle Sass, plutôt que de les diviser entre plusieurs fichiers.

Des règles explicites autour de l’imbrication Sass

L’imbrication dans Sass peut être très pratique, mais court le risque d’une mauvaise sortie avec des chaînes de sélection trop longues. Nous avons suivi la règle de création et n’avons jamais imbriqué Sass de plus de trois couches de profondeur.

En gardant à l’esprit le principe de planéité CSS du système de conception, nous avons voulu limiter l’imbrication aux cas d’utilisation suivants:

  1. Modificateurs d’un bloc de style
  2. Requêtes multimédia
  3. Sélecteurs parents
  4. États

1. Modificateurs de bloc de style

Pour les modificateurs, si la règle ne fait que quelques lignes, le modificateur peut être imbriqué dans le parent comme suit:

.cn-c-alert { border: 1px solid gray; color: gray; /** * Error Alert */ &--error { border-color: red; color: red; }}

Grâce au symbole &, cela se compilerait pour:

.cn-c-alert { border: 1px solid gray; color: gray;}.cn-c-alert--error { border-color: red; color: red;}

Pour les blocs de style plus longs, nous n’avons pas imbriqué le code du modificateur car cela réduisait la lisibilité du code.

2. Requêtes média

Les requêtes média spécifiques au composant doivent être imbriquées dans le bloc composant.

.cn-c-primary-nav { /* Base styles */ /** * 1) On larger displays, convert to a horizontal list */ @media all and (min-width: 40em) { display: flex; }}

Cela compile pour:

.cn-c-primary-nav { /* Base styles */}@media all and (min-width: 40em) { .cn-c-primary-nav { display: flex; }}

3. Sélecteurs parents

Le système de conception utilisera le mécanisme de sélecteur parent de Sass. Cela permet de maintenir toutes les règles pour un composant donné dans un emplacement.

Cela compilera pour:

.cn-c-header .cn-c-primary-nav { display: flex;}

Tous les styles pour cn-c-primary-nav doivent être trouvés au même endroit, plutôt que dispersés dans plusieurs fichiers partiels.

4. États

Les états d’un composant doivent être inclus en tant qu’élément imbriqué. Cela inclut les états hover, focus et active:

.cn-c-btn { background: blue; &:hover, &:focus { background: red; }}

Cela compilera pour:

.cn-c-btn { background: blue;}.cn-c-btn:hover, .cn-c-btn:focus { background: red;}

Les États peuvent également prendre la forme de classes d’utilité, telles que is- et has-:

.cn-c-accordion__panel { overflow: hidden; max-height: 0; &.cn-is-active { max-height: 40em; }}

Cela compilera pour:

.cn-c-accordion__panel { overflow: hidden; max-height: 0;}.cn-c-accordion__panel.cn-is-active { max-height: 40em;}

La mise en place de ces règles nous a donné des contraintes et des conventions auxquelles nous devions adhérer afin de créer un système solide. Lorsque nous rencontrions des cas où une convention n’était pas évidente ou où une solution pouvait être gérée de différentes manières, nous discutions de la façon de la gérer et de mettre à jour les directives si nécessaire.

Cela fonctionne-t-il pour tout le monde?

Avant de vous échauffer et de commencer à être en désaccord avec les décisions spécifiques que nous avons prises lors de la création de notre système, reconnaissez que cette architecture avait du sens pour le système sur lequel nous travaillions. Cela signifie-t-il qu’il s’agit d’une solution à l’épreuve des balles pour chaque projet? Non, et je ne propose pas ça. Les besoins spécifiques et la configuration de l’organisation devraient beaucoup influencer l’architecture CSS de votre système de conception.

Je travaille sur de nombreux projets où je peux me débrouiller avec des chaînes comme .table-of-contents li a, mais ces projets sont principalement gérés par moi. Pour les projets clients qui impliquent de travailler dans un environnement d’équipe, je m’oriente beaucoup vers des syntaxes plus verbeuses et explicites comme je l’ai décrit ci-dessus car elles offrent moins de place aux gens pour bousiller les choses. C’est formidable de voir d’autres équipes comme Sparkbox arriver à des conclusions similaires.

Après quelques semaines d’absence du projet, nous revenons pour continuer à travailler sur la version 1.1 du système de conception. J’ai hâte de revenir à cette base de code et de voir à quelle vitesse je peux m’y ré-acclimater!

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.