Sémantique HTML et architecture côté client

Par Nicolas Gallagher

Voici quelques réflexions, quelques histoires et idées que j’aime bien et sur lesquelles je travaille depuis un an. Pour le HTML, cela recouvre la sémantique, les composants et différentes approches de l’architecture côté client (en front-end), mais je parle aussi des systèmes de nommage des classes, ainsi que de la compression HTTP.

We shall not cease from exploration
And the end of all our exploring
Will be to arrive where we started
And know the place for the first time.

Nous ne cesserons jamais d’explorer
Et la fin de toutes nos explorations
Sera de parvenir à notre point de départ
Et d’enfin savoir où nous sommes.

T.S. Eliot, Little Gidding

De la sémantique

La sémantique se définit par l’étude des relations entre les signes et symboles et ce qu’ils représentent. En linguistique, il s’agit essentiellement de l’étude de la signification des signes (comme les mots, les phrases ou les sons) dans le langage. Dans un contexte de développement web côté client, la sémantique concerne surtout le sens communément donné aux éléments, attributs et valeurs des attributs HTML (y compris des extensions comme les microdonnées).

Cette sémantique commune, qui est généralement formalisée à travers des spécifications, peut s’employer pour aider des programmes (et par conséquent des êtres humains) à mieux comprendre certains aspects de l’information contenue dans un site web. Néanmoins, même après cette formalisation, la sémantique des éléments, attributs et valeurs d’attributs reste l’objet d’adaptations et de choix de la part des développeurs. Cela peut mener à des modifications de la sémantique communément admise (c’est même un principe de base du design HTML).

Savoir distinguer différents types de sémantique HTML

L’une des bases du développement côté client moderne et professionnel réside dans l’écriture d’un « HTML sémantique ». L’essentiel de la sémantique renvoie à des aspects de la nature du contenu existant ou attendu (par ex. un élément h1, un attribut lang, l’adresse mail spécifiée grâce à l’attribut type, ou des microdonnées). Néanmoins, toute la sémantique n’a pas à être dérivée du contenu. Les noms de classes ne peuvent pas être « non-sémantiques ». Quels que soient les noms qu’on emploie, ils ont un sens, ils servent un objectif.

La sémantique des noms de classes peut être différente de celle des éléments HTML. Il est possible de profiter de la sémantique commune « globale » des éléments HTML, de certains attributs HTML, des microdonnées etc, sans pour autant confondre leur portée avec celle de la sémantique « locale » spécifique aux sites web et aux applications, comme on la trouve généralement dans les valeurs d’attributs comme class.
Malgré la section de spécification HTML5 sur les classes, qui pose comme une « bonne pratique » le fait que...

« … les auteurs sont encouragés à utiliser les valeurs des attributs de classe qui décrivent la nature du contenu, plutôt que des valeurs qui décrivent la présentation souhaitée du contenu. »

… en réalité, il n’y a pas de raison fondamentale de suivre cette indication. C’est même souvent une entrave lorsqu’on travaille sur des sites ou des applications de grande ampleur.

Prenons cet exemple très simple :

<div class="infos">
   <h2>Infos</h2>
   [contenu des infos]
</div>

Le nom de classe « infos » ne vous dit rien d’autre que ce que le contexte indique déjà clairement. Il ne vous donne aucune information quant à la structure architecturale du composant, et ne peut plus être employé avec un contenu qui ne serait pas « des infos ». En rattachant la sémantique de votre nom de classe à la nature du contenu, vous limitez d’emblée les capacités de redimensionnement de votre architecture, ou la possibilité que celle-ci soit facilement exploitée par d’autres développeurs.

Des noms de classes indépendants du contenu

Une alternative consiste à dériver la sémantique des noms de classes à partir de schémas structurels et fonctionnels qui se répètent dans un design. Les composants les plus faciles à réutiliser sont ceux dont les noms de classes sont indépendants du contexte.

Il ne faut pas craindre de rendre claires et explicites les connexions entre les différents couches, plutôt que d’avoir des noms de classes qui reflètent strictement un contenu spécifique. En faisant cela, vous n’en faites pas des classes « non sémantiques », cela signifie simplement que la sémantique n’est pas dérivée du contenu. Il ne faut pas craindre d’ajouter des éléments HTML supplémentaires s’ils vous permettent de créer des éléments plus robustes, plus souples et réutilisables. Cette approche ne rend pas le HTML « non sémantique », mais cela signifie simplement que vous employez un peu plus d’éléments que le strict minimum nécessaire pour baliser le contenu.

L’architecture côté client

L’objectif d’une architecture orientée composant/template/objet est d’être capable de développer un nombre limité de composants réutilisables pouvant contenir une série de types de contenus différents. L’important pour la sémantique des noms de classe, dans les applications complexes, c’est qu’elle soit pragmatique et serve son objectif premier : fournir des marqueurs de présentation (ou de comportement) signifiants, flexibles et réutilisables afin que ce soit bel et bien les développeurs qui puissent s’en emparer.

Des composants réutilisables et combinables

Les HTML/CSS extensibles doivent très largement se baser sur des classes au sein du HTML pour permettre la création de composants réutilisables. Un composant souple et réutilisable ne doit ni dépendre de son existence à un point particulier de l’arbre DOM, ni exiger l’emploi de types d’éléments spécifiques. Il doit pouvoir s’adapter à différents conteneurs, et être facile à personnaliser. Si nécessaire, des éléments HTML supplémentaires (au-delà de ceux qui s’imposent pour simplement baliser le contenu) peuvent être employés pour rendre le composant plus robuste.

Un bon exemple peut être observé dans ce que Nicole Sullivan appelle l’objet média.
Les composants qui peuvent être facilement combinés vont tirer parti d’une préférence pour les classes, plutôt que pour les sélecteurs de type. L’exemple qui suit montre un évitement de la combinaison entre le composant btn (pour les boutons) et le composant uilist (pour les listes d’interface utilisateur). Il y a en effet deux difficultés : la spécificité de .btn est inférieure à celle de .uilist a (qui va prendre le pas sur n’importe quelle propriété partagée) ; et le composant uilist réclame des ancres en tant que nœuds enfants.

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist a { /* styles */ }
<nav class="uilist">
   <a href="#">Index</a>
   <a href="#">À Propos</a>
   <a class="btn" href="#">S’identifier</a>
</nav>

Pour pouvoir plus facilement combiner uilist avec d’autres composants, il est préférable d’utiliser des classes pour styler les éléments enfants du DOM. Cela permet de réduire la spécificité de la règle, mais surtout cela laisse la possibilité d’appliquer les styles structurels à n’importe quel type de nœud enfant.

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist-element { /* styles */ }
<nav class="uilist">
   <a class="uilist-element" href="#">Index</a>
   <a class="uilist-element" href="#">À Propos</a>
   <span class="uilist-element">
       <a class="btn" href="#">S’identifier</a>
   </span>
</nav>

Des classes spécifiques au Javascript

En employant des classes spécifiques au Javascript, il est possible de réduire le risque que des changements thématiques ou structurels sur les composants viennent détruire le Javascript qui s’y applique aussi. L’approche qui me semble la plus utile consiste à employer certains noms de classes uniquement pour les marqueurs du Javascript (js-*) et sans y associer le moindre effet de présentation.

<a href="/identification" class="btn btn-primaire js-identification"></a>

C’est ainsi qu’on réduit le risque qu’un changement dans la structure ou le thème des composants n’affecte par inadvertance tel ou tel comportement Javascript mettant en œuvre une fonctionnalité complexe.

Les modificateurs de composants

Les composants offrent souvent des variantes avec des présentations légèrement différentes du composant de base : par exemple, avec un fond ou une bordure différente. Il existe principalement deux systèmes pour créer ces variantes de composants. Je les appellerai ici « système à classe unique » ou bien « système multi-classe ».

Le système « à classe unique »

.btn, .btn-primaire { /* styles de base des boutons */ }
.btn-primaire { /* styles spécifiques au bouton Valider */ }

<button class="btn">Bouton Lambda</button>
<button class="btn-primaire">S’identifier</button>

Le système « multi-classe »

.btn { /*styles de base des boutons */ }
.btn-primaire { /* styles spécifiques au bouton primaire */ }

<button class="btn">Bouton Lambda</button>
<button class="btn btn-primaire">S’identifier</button>

Si vous utilisez un pré-processeur, vous employez peut-être la fonctionnalité @extend de Sass, afin de réduire le travail de maintenance qu’implique l’usage du système « à classe unique ». Néanmoins, et même avec l’aide d’un pré-processeur, ma préférence va à l’emploi d’un système « multi-classe », en ajoutant des classes modificatrices au sein du HTML.

En fait, je trouve que c’est un système plus modulable. Prenez par exemple le composant btn de base, et ajoutez-y 5 nouveaux types de boutons et 3 tailles différentes. Avec un système « multi-classe », vous allez vous retrouver avec 9 classes qui peuvent être mélangées et réarrangées entre elles. Avec un système « à classe unique », vous vous retrouvez avec 24 classes.

C’est aussi plus facile d’ajouter de petites modifications contextuelles à un composant, en cas de nécessité absolue. Vous pourriez ainsi avoir besoin de faire de petits réglages portant sur n’importe quel btn qui apparaît à l’intérieur d’un autre composant.

/* réajustement multi-classe */
.truc .btn { /* réajustements*/ }

/* réajustement classe unique */
.truc .btn,
.truc .btn-primaire,
.truc .btn-alerte,
.truc .btn-etc { /* réajustements */ }

Avec un système « multi-classe », vous n’avez besoin que d’un seul sélecteur interne au composant pour cibler n’importe quel type d’élément stylé en btn au sein de ce composant. Si vous aviez un système « à classe unique », cela vous obligerait à recenser absolument tous les types de boutons employés, et à modifier le sélecteur à chaque fois qu’une nouvelle variante de bouton est introduite.

Des noms de classes structurés

Lorsqu’on crée des composants (et des « thèmes » qui sont construits à partir de cette base), certaines classes sont employées pour établir des frontières entre composants, d’autres pour modifier des composants, et d’autres enfin pour fondre un ensemble de nœuds du DOM dans un composant de présentation abstrait et plus vaste. Il devient alors difficile de comprendre la relation entre btn (composant), btn-primaire (modificateur), btn-groupe (composant) et btn-element-du-groupe (sous-objet du composant) parce que leurs noms n’identifient pas vraiment l’objectif de ces classes. Il n’y a pas de schéma cohérent.

En 2011, j’ai testé différents types de systèmes de nommage, qui m’aident à comprendre plus rapidement la relation qui est établie (en termes de présentation) entre différents nœuds d’un morceau du DOM, plutôt que d’essayer d’établir l’architecture du site en faisant d’incessants aller-retours entre les fichiers HTML, CSS et JS. Je suis arrivé à un système qui est prioritairement influencé par l’approche sémantique du système BEM, mais adapté sous une forme que je trouve plus facile à lire.

Après que j’ai écrit ce billet, c’est une approche qui a été retenue par plusieurs autres équipes et structures. Entretemps, MontageJS a modifié cette notation en lui donnant un style que je préfère, et que j’emploie actuellement dans le kit d’outils SUIT :

/* Utilitaire*/
.u-nomUtilitaire {}

/* Utilitaire d'état */
.u-isNomEtat {}

/* Composant */
.NomComposant {}

/* Modificateur de composant */
.NomComposant--nomModificateur {}

/* Composant descendant */
.NomComposant-descendant {}

/* Modificateur de composant descendant */
.NomComposant-descendant--nomModificateur {}

/* État du composant (focalisation sur le composant) */
.NomComposant.is-etatDuComposant {}

/* Mixin de composant (dépendances des styles ancêtres) */
.with-NomComposant {}

Il ne s’agit là que d’un système de nommage que je trouve pratique aujourd’hui. Il pourrait prendre d’autres formes. Mais son intérêt réside dans le fait qu’il ôte toute ambiguïté par rapport aux noms de classes qui se baseraient uniquement sur les tirets hauts (uniques), les tirets bas underscore ou encore le CamelCase (Ndt : assemblage de mots qui se distinguent les uns des autres par des majuscules initiales, comme PlayStation).

Un mot sur la taille des fichiers bruts et sur la compression HTTP

Dès qu’on parle des CSS modulaires / scalaires, c’est qu’il y a des inquiétudes à propos de la taille des fichiers et du « grossissement ». Nicole Sullivan évoque souvent tout le poids qui a été gagné (et toutes les améliorations obtenues en termes de maintenance) lorsque des entreprises comme Facebook ont adopté ce type d’approche. Au-delà de tout cela, je voudrais partager quelques anecdotes sur les effets de la compression HTTP sur le rendu des pré-processeurs et sur l’usage extensif de classes HTML.

Lorsque Twitter Bootstrap est sorti, j’ai ré-écrit le CSS compilé pour mieux montrer comment je le traiterais manuellement, et pour comparer les tailles de fichiers. Après avoir miniaturisé les deux fichiers, il est apparu que le CSS fait manuellement pesait environ 10% de moins que celui issu du pré-processeur. Mais après avoir compressé les deux fichiers avec gzip, le résultat du pré-processeur était environ 5% plus léger que le CSS manuel.

Ceci montre à quel point il est important de comparer la taille des fichiers après compression HTTP, parce que la taille des fichiers qu’on a miniaturisés ne nous en dit pas suffisamment. Cela suggère que les développeurs CSS expérimentés qui utilisent des pré-processeurs ne doivent pas trop s’inquiéter s’il y a un certain effet de redondance dans le CSS compilé, puisque celui-ci se prête bien à une réduction de la taille finale après compression HTTP. Les avantages d’un code « CSS » plus facile à maintenir, produit via des pré-processeurs, dépassent les inquiétudes portant sur l’esthétique ou sur la taille d’un CSS brut et miniaturisé.

Dans une autre expérience, j’ai retiré absolument tous les attributs de classes que j’ai pu trouver dans un fichier HTML de 60Ko extrait d’un véritable site web (et qui était déjà composé de nombreux composants réutilisables). Cette action a réduit la taille du fichier à 25Ko. Après avoir gzippé le fichier original et le fichier épuré, leurs poids s’élevaient respectivement à 7,6Ko et 6Ko : une différence de 1,6Ko. Inutile, donc, de s’inquiéter des conséquences d’un usage généreux des classes en termes de taille des fichiers.

Le jour où j’ai cessé de m’inquiéter...

Au fil de l’expérience et des années, beaucoup de développeurs de talent ont fait évoluer la façon dont sont développés les sites et les applications à grande échelle. Malgré tout, lorsqu’on a été nourri d’une idéologie où les mots « HTML sémantique » signifient l’emploi de noms de classes dérivés du contenu (et encore, en tout dernier ressort), on ne devient pleinement conscient des limites pratiques de cette approche que lorsqu’on a été confronté à un travail sur des applications de grande taille. Il faut être prêt à se départir d’idées anciennes, à envisager des alternatives, et même à revisiter des méthodes que l’on a pu rejeter par le passé.

Une fois que vous avez commencé à écrire des sites web et des applications d’importance qui doivent être maintenus (voire activement itérés) par vous-même ou et par d’autres, vous réalisez vite qu’en dépit de vos plus gros efforts, votre code devient de plus en plus difficile à maintenir. Il ne faudrait pas hésiter à prendre le temps de découvrir le travail de quelques personnes qui ont proposé leur approches personnelles de ces problèmes : le blog de Nicole et son projet CSS orienté objet ; les CSS pour une architecture modulaire adaptable de Jonathan Snook ; et la méthode bloc, élément, modificateur (BEM) développée par Yandex.

Si vous choisissez de créer en HTML et CSS tout en essayant de réduire le temps passé à écrire et éditer les CSS, alors cela implique que vous acceptiez en retour de consacrer plus de temps à changer les classes HTML sur les éléments si vous voulez modifier leur style. Le résultat est franchement opérationnel, que ce soit pour les développeurs en front ou en back-end : car même si n’importe qui peut réarranger des briques de Lego pré-construites, il n’existe encore pas d’alchimiste des CSS.

Fiche technique

À propos de l'auteur

Développeur front-end pour Twitter, contributeur pour HTML5 boilerplate et co-créateur de Normalize.css

Articles similaires

Voici la liste des dix articles les plus récents en rapport avec cet article :

Architecture de l’information

HTML

Technique

© 2001-2016 Pompage Magazine et les auteurs originaux - Hébergé chez Nursit.com ! - RSS / ATOM - About this site