Salut, je m'appelle Jonathan Snook. Je suis développeur et web designer. J'ai commencé à créer des sites web en tant qu'amateur en 1994, puis j'en ai fait mon métier en 1999.

J'entretiens un blog, Snook.ca, sur lequel je partage des astuces, des conseils et des liens au sujet du développement web. Je suis aussi invité régulièrement comme orateur à des conférences et des ateliers, et je suis reconnaissant de pouvoir voyager dans le monde entier pour partager mes connaissances et ma passion.

J'ai co-écrit deux livres à ce jour : The Art and Science of CSS (disponible chez Sitepoint) et Accelerated DOM Scripting (disponible chez Apress). J'ai également écrit plusieurs articles pour les magazines en ligne A List Apart, Sitepoint.com et de nombreuses autres ressources en ligne et hors-ligne.

Après avoir travaillé sur des centaines de projets web parmi lesquels la récente refonte de Yahoo! Mail, j'ai écrit ce livre pour faire part de mon expérience dans la création de sites web de toutes tailles.

J'aimerai exprimer mon immense reconnaissance envers tous ceux de la communauté. Chacun d'entre vous me permet de continuer à apprécier et à vivre pleinement ma carrière. Un grand merci particulièrement à Kitt Hodsden pour m'avoir encouragé à écrire ce livre et à le partager. Pour finir, merci à mes fils, Hayden et Lucas qui continuent à me rendre meilleur chaque jour.

INTRODUCTION

J'ai longtemps cherché à savoir combien de sites internet j'avais créés, en vain. Vous pourriez penser qu’après en avoir construit quelques centaines, j'aurai découvert LA bonne méthode. En réalité, je ne pense pas qu'il n'y ait qu'une seule bonne méthode. Par contre, j'ai découvert des techniques qui permettent de structurer et de mieux organiser ses feuilles de style CSS, ce qui permet d'avoir un code plus facile à mettre en place et à maintenir.

J'ai analysé mes procédés et ceux des autres, pour trouver comment structurer au mieux le code pour un large panel de projets. Les concepts étaient déjà plus ou moins existants pour les petits sites sur lesquels j'avais travaillé, mais sont devenus plus concrets lorsque j'ai commencé à participer à davantage de projets complexes. Les petits sites ne présentent souvent pas les mêmes problématiques que les plus gros ou que le travail en grande équipe ; les petits sites ne sont pas aussi complexes ni ne changent aussi souvent. Cependant, ce que je décris dans ces pages est une approche valable autant pour les gros sites que pour les petits.

SMACSS (prononcé « smacks ») est plutôt un guide pour la construction des feuilles de style qu'un modèle rigide. Vous n'y trouverez aucune librairie à télécharger ou à installer. SMACSS est un moyen d'analyser vos procédés en matière de design et de transformer ces structures rigides en un processus de pensée souple et adaptable. L'objectif de ce livre est de fournir une approche pertinente au développement web avec l'utilisation des CSS. Honnêtement, qui construit son site sans CSS de nos jours ? Sentez-vous libre de considérer ce livre dans son ensemble ou simplement partiellement, en retenant uniquement ce avec quoi vous serez le plus à l'aise. Ou encore, ne l'utilisez pas du tout. Je suis bien conscient qu'il ne sera pas au goût de tout le monde. Lorsqu'il s'agit de développement web, la réponse à la plupart des questions est : « Ça dépend ».

Qu'y trouverez-vous ?

J'ai organisé mes idées autour de différents sujets concernant l'architecture CSS. Chacune d'elles est détaillée dans son propre chapitre. Lisez les chapitres les uns après les autres, ou bien dans le désordre, en choisissant ceux qui vous intéressent le plus. Ce n'est pas un livre de 1000 pages, les chapitres sont relativement courts et digestes.

Maintenant commençons !

LA CATÉGORISATION DES RÈGLES CSS

Tout projet a besoin d'une organisation. Insérer chaque nouveau style que vous créez dans un fichier unique rendra la recherche d'éléments plus difficile et compliquera la tâche des autres personnes travaillant sur le projet. Il leur sera difficile de s'y retrouver. Bien entendu, vous avez probablement déjà une organisation en place. J'espère que ce que vous lirez dans ces pages vous permettra de trouver ce qui peut fonctionner avec votre procédé actuel, et si j'ai de la chance, vous pourrez même découvrir de meilleurs moyens pour améliorer votre manière de faire.

Comment choisir entre l'utilisation des sélecteurs ID ou celle des sélecteurs de classe, ou encore, comment choisir le nombre de sélecteurs que vous avez à disposition ? De quelle manière décidez-vous à quels éléments appliquer les styles en question ? Comment faites-vous pour que l'organisation de votre site et de vos feuilles de style soit facilement compréhensible ?

Le classement par catégorie se trouve au cœur de SMACSS. En classant les règles CSS, nous à voir des modèles apparaître et pouvons ainsi définir de meilleurs procédés pour chacun de ces modèles.

Il existe cinq catégories de règles :

1. La base

2. L'agencement

3. Le module

4. L'état

5. Le thème

Nous nous retrouvons souvent à mélanger les styles de ces cinq catégories. Si nous devenons davantage conscients des éléments que nous voulons styler, nous pourrons alors éviter la complexité provoquée par l'entremêlement de ces règles.

À chaque catégorie s'appliquent certaines directives. Cette séparation succincte nous permet de nous poser les bonnes questions pendant le processus de développement. De quelle manière allons-nous coder les choses et surtout pourquoi allons-nous faire ainsi ?

L'objectif principal du classement par catégorie est de codifier des modèles – les choses qui se répètent dans votre site web. Cela permet de diminuer la quantité de code, d'avoir une maintenance plus facile et d'atteindre une plus grande uniformité dans l'expérience de l'utilisateur. Il n'y a donc que des avantages ! Quelques exceptions à la règle peuvent être intéressantes dans certains cas mais doivent toujours être justifiées.

Les règles de base sont les règles par défaut. Elles concernent presque exclusivement les sélecteurs d'éléments uniques mais peuvent également inclure des sélecteurs d'attributs, des sélecteurs de pseudo-classes, des sélecteurs d'enfant ou des sélecteurs adjacents. Un style de base dit essentiellement que, quel que soit l'endroit où se trouve l'élément sur la page, il doit porter tel style.

Exemples de styles de base

html, body, form { margin: 0; padding:0; }

input[type=text] { border: 1px solid;#999; }

a { color: #039; }

a:hover { color: #03C; }

Les règles d'agencement divisent la page en différentes sections. Les agencements maintiennent un ou plusieurs modules ensemble.

Les règles de modules sont réutilisables, ce sont les parties modulaires de votre design. Ce sont les zones de texte, les barres latérales, la liste de produits etc.

Les règles d'état sont un moyen de décrire à quoi doit ressembler tel module ou agencement dans un état particulier. Sera-t-il caché ou déroulé ? Actif ou inactif ? Elles décrivent également de quelle manière un module ou un agencement doit apparaître sur des écrans plus grands ou plus petits et à quoi ressemble un module dans un gabarit différent, comme la page d'accueil ou une des pages internes du site.

Pour finir, les règles de thème sont similaires aux règles d'état dans la mesure où elles décrivent à quoi doit ressembler un module ou un agencement. La plupart des sites n'ont pas besoin d'avoir différents thèmes mais il est bon de savoir que ces règles existent.

Nommage des règles

Lorsque l'on sépare les règles en cinq catégories, des conventions de nommages sont nécessaires pour comprendre immédiatement à quelle catégorie tel style spécifique appartient et son rôle dans l'ensemble général de la page.

J'aime utiliser un préfixe pour différencier les règles d'agencement, d'état et de module. Pour l'agencement, j'utilise l-. L'utilisation de préfixes tels que grid- permet également de clarifier la séparation entre différents styles d'agencement. Pour les règles d'état, j'aime bien utiliser is- comme dans is-hidden ou is-collapsed. Cela me permet de décrire les choses de manière très lisible.

Les modules représentent la majeure partie d'un projet. C'est la raison pour laquelle, le fait que chaque module commence par un préfixe comme .module- serait trop long et inutile. Les modules utilisent simplement le nom du module lui-même.

Exemples de classes

/* Exemple de Module */

.example { }

/* Module de texte */

.callout { }

/* Module de texte avec un état */

.callout.is-collapsed { }

/*  Module de formulaire */

.field { }

/* Agencement en ligne  */

.l-inline { }

Les éléments relatifs à un module ont pour préfixe le nom de la base. Sur ce site, l'exemple de code utilise .exm et les sous-titres s'écrivent .exm-caption. Il me suffit donc de voir la classe du sous-titre pour comprendre instantanément qu'il est relatif aux exemples de code et pour retrouver l'endroit où son style est décrit.

Les modules qui sont une variation d'un autre module devraient également avoir pour préfixe le nom du module initial. Le thème du sous-classement est abordé plus en détail dans le chapitre Les Règles de Module.

Cette convention de nommage sera utilisée tout au long du livre. Comme la plupart des choses que je vais partager, vous n'avez pas à vous conformer à ces conseils de manière rigide. Ayez une convention, documentez-la et tenez-vous en à ce que vous avez décidé.

LES RÈGLES DE BASE

Une règle de base s'applique à un élément qui utilise un sélecteur d'élément, un sélecteur descendant, ou un sélecteur d'enfant, ainsi que toute pseudo-classe. Elle ne comporte aucune classe ou sélecteur ID. Elle définit le style par défaut d'un élément pour toutes ses apparitions sur la page.

Exemples de styles de base

body, form {

margin: 0;

padding: 0;

}

a { color: #039;

}

a:hover {

color: #03F;

}

Les styles de base définissent la taille des titres, le style par défaut des liens, le style par défaut des typographies et les fonds du site. L'utilisation de !important ne devrait pas être nécessaire dans un style de base.

Je recommande fortement d'attribuer un fond au corps de page. Certains utilisateurs peuvent définir leur propre fond et choisir autre chose que du blanc. Si vous travaillez en vous attendant à ce que le fond par défaut soit blanc, vous risquez de vous retrouver avec un design qui ne ressemblera pas à ce que vous aviez en tête. Et pire, votre choix de couleur pourrait rentrer en conflit avec les paramètres utilisateurs et rendre votre site inutilisable.

Réinitialisation CSS

Une réinitialisation CSS est un ensemble de styles de base prévus pour ôter – ou réinitialiser la marge externe, la marge interne et les autres propriétés par défaut. L'objectif est de bâtir un site sur des fondations solides, qui proposera un rendu harmonieux sur les différents navigateurs.

De nombreux modèles de réinitialisation sont trop radicaux et peuvent introduire plus de problèmes qu'ils n'en résolvent. Enlever la marge externe et la marge interne des éléments pour les réintroduire par la suite duplique les efforts et augmente la quantité de code nécessaire à envoyer au client.

Beaucoup de gens trouvent la réinitialisation des styles utile dans le développement web. Assurez-vous tout de même de bien comprendre les enjeux et les inconvénients du modèle que vous souhaitez utiliser, et adaptez votre programmation en fonction.

Le fait de développer votre propre ensemble de styles par défaut, que vous réutiliserez de manière régulière d'un site à un autre, peut aussi être avantageux.

LES RÈGLES D'AGENCEMENT

L'objectif du CSS, par nature, est de disposer les éléments sur une page. Cependant, il existe une différence entre les agencements qui définissent les composants principaux et ceux qui définissent les composants secondaires. Les composants mineurs – comme les zones de texte, le formulaire d'inscription, ou le menu de navigation – sont intégrés dans les composants principaux comme l'en-tête ou le pied de page. Je vais donc utiliser le terme « Module » pour qualifier les composants secondaires, thème que j'aborderai dans le prochain chapitre. Pour parler des composants principaux, j’emploierai l'expression « Styles d'agencement ».

Les styles d'agencement peuvent eux-aussi être divisés en styles majeurs et styles mineurs, en fonction de votre fréquence de réutilisation de ces styles. Les styles majeurs d'agencement comme l'en-tête et le pied de page sont en général mis en page à l'aide des sélecteurs ID, mais prenez le temps de réfléchir aux éléments communs à tous les composants de la page et n'utilisez des sélecteurs de classe que lorsque cela est nécessaire.

Définition d'un agencement

#header, #article, #footer {

width: 960px;

margin: auto;

}

#article {

border: solid #CCC;

border-width: 1px 0 0;

}

Certains sites ont besoin d'un modèle d'agencement plus général comme par exemple, le site 960.gs. Ces styles d'agencement mineurs utilisent des noms de classe plutôt que des sélecteurs ID afin de pouvoir être utilisés plusieurs fois sur la page.

En général, un style d'agencement n'a qu'un sélecteur : un seul ID ou un seul nom de classe. Dans certains cas cependant, un agencement peut avoir besoin de s'adapter à différents paramètres. Par exemple, vous pouvez établir des agencements différents en fonction de la préférence de l'utilisateur. Cette préférence sera toujours définie comme un style d'agencement et sera utilisée en combinaison avec les autres styles d'agencement.

Utiliser un style d'agencement supérieur affectant les autres styles d'agencement

#article {

float: left;

}

#sidebar {

float: right;

}

.l-flipped #article {

float: right;

}

.l-flipped #sidebar {

float: left;

}

Dans cet exemple, la classe .l-flipped s'applique à un niveau d'élément supérieur comme l'élément body et permet d'inverser le contenu de l'article et de la barre latérale, déplaçant la barre latérale de la droite vers la gauche et vice-versa pour l'article.

Deux styles d'agencement pour faire passer un élément de fluide à fixe

#article {

width: 80%;

float: left;

}

#sidebar {

width: 20%;

float: right;

}

.l-fixed #article {

width: 600px;

}

.l-fixed #sidebar {

width: 200px;

}

Dans ce dernier exemple, la classe .l-fixed modifie le design en rendant l'agencement fixe (largeur définie en pixels) alors qu'il était fluide avant (largeur définie en pourcentage).

Une autre chose à remarquer dans cet exemple est la convention de nommage que j'ai utilisée. Les déclarations qui utilisent des sélecteurs ID prennent le nom de l'élément et ne comportent pas d'espace de nommage particulier. Les sélecteurs découlant d'une classe, en revanche, comportent un préfixe l- . Cela permet d'identifier facilement la cible de ces styles et de les distinguer des modules ou des états. Les styles d'agencement sont la seule catégorie primaire à utiliser des sélecteurs ID. Si vous souhaitez faire un espace de nommage pour votre sélecteur ID, en utilisant un préfixe, vous pouvez, mais il n'est pas forcément nécessaire de faire ainsi.

L'utilisation des sélecteurs ID

Soyons clairs, utiliser des attributs ID dans votre code HTML peut être une bonne chose, voire dans certains cas, s'avérer absolument nécessaire. Par exemple, ils constituent des points d'ancrage très efficaces pour JavaScript. Pour le langage CSS, en revanche, les sélecteurs ID ne sont pas nécessaires puisque la différence de performance entre les sélecteurs ID et les sélecteurs de classe est quasi-inexistante et peut rendre la structuration du style plus complexe en raison d'une spécificité accrue.

Exemples d'agencement

Passons maintenant à la mise en pratique pour bien comprendre le concept. Regardons un site web actuel et analysons ce qui fait partie de l'agencement et ce qui relève du module.

En regardant le site Shopify, nous voyons qu'il comporte des modèles qui se retrouvent dans la grande majorité des sites. Par exemple, il y a un en-tête, une zone de contenu principal, une barre latérale et un pied de page.

Dans votre tête, imaginez ce à quoi doit ressembler la structure HTML. Probablement un ensemble de div. Peut-être que vous utilisez HTML5 et que vous commencez à utiliser les éléments header et footer. Dans les deux cas, vous auriez probablement attribué un ID à chacun des récipients.

La structure CSS ressemblerait à cela :

#header { … }

#primarynav { … }

#maincontent { … }

<div id="header"></div>

<div id="primarynav"></div>

<div id="maincontent"></div>

C'est évident et je suis sûr que vous vous dites : « Sérieusement ? Vous me montrez comment faire ça ?! » Regardons une autre partie de cette page.

En observant la section Features, nous voyons une grille d’items, contenue dans un élément parent div, contenant lui-même une série d'enfants div. Utiliser une liste non-ordonnée peut aussi être un moyen pratique de marquer ces items, c’est la méthode que je vais utiliser dans cet exemple.

Exemple d'un code HTML pour l'agencement de la section « Featured »

<div>

<h2>Featured</h2>

<ul>

<li><a href="…">…</a></li>

<li><a href="…">…</a></li>

…

</ul>

</div>

Si nous ne prenions pas en compte l'approche SMACSS, nous pourrions être tenté d'ajouter un ID featured aux divs concernées puis, de mettre en page le contenu sur cette base.

Première approche possible pour mettre en page la liste des éléments de la section « Featured »

div#featured ul {

margin: 0;

padding: 0;

list-style-type: none;

}

div#featured li {

float: left;

height: 100px;

margin-left: 10px;

}

Avec cette approche, nous présupposons trois choses :

1. Qu'il n'y aura jamais qu'une seule section « Featured » sur la page.

2. Que les éléments de la liste sont en float left.

3. Que les éléments de la liste ont une hauteur fixe de 100 pixels.

Ces suppositions sont raisonnables. Voici un premier exemple de jusqu'où un petit site peut aller avec cette structure : elle ne changera probablement pas, et ne deviendra donc probablement pas plus complexe qu'elle ne l'est déjà. Peut-être. En revanche, les plus gros sites, ayant un taux plus élevé de changements, présentent une probabilité accrue qu'un ou plusieurs composants doivent être redéfinis dans la page, engendrant un réajustement des styles qui leur sont attribués.

Si on retourne à notre exemple, nous pouvons donc très certainement apporter des améliorations. Le sélecteur ID ne nécessitait pas l'emploi d'un sélecteur de balise et puisque la liste est un descendant direct de div, le sélecteur d'enfant (>) aurait pu être utilisé.

Voyons comment ce bout de code pourrait être réajusté dans le but de nous offrir une plus grande souplesse.

Du point de vue de l'agencement, tout ce qui compte est la manière dont chaque élément est relié aux autres. Nous ne nous soucions pas nécessairement du design des modules en eux-mêmes, ni du contexte dans lequel cet agencement s'inscrit.

Grille du module qui s'applique à une OL ou une UL.

.l-grid {

margin: 0;

padding: 0;

list-style-type: none;

}

.l-grid > li {

display: inline-block;

margin: 0 0 10px 10px;

/* hack IE7 pour imiter la déclaration inline-block sur les éléments blocs */

*display: inline;

*zoom: 1;

}

Dans cette approche, quels problèmes ont été résolus et lesquels ont été introduits ? (Il est très rare qu'une solution résolve 100% des problèmes.)

1. L'agencement de la grille peut maintenant être appliqué à n'importe quel conteneur pour créer un agencement avec un style flottant.

2. Nous avons diminué la profondeur de l'applicabilité d'un cran (Pour en savoir plus, cf. le chapitre sur la Profondeur de l'Applicabilité).

3. Nous avons réduit la spécificité des sélecteurs.

4. La hauteur fixe a été supprimée. Chaque ligne s'ajustera automatiquement à la hauteur de l'élément le plus haut de cette ligne.

Maintenant, quels sont les désavantages de cette écriture ?

1. En utilisant un sélecteur d'enfant, nous devenons incompatibles avec IE6. (Nous pourrions contourner cela en évitant le sélecteur d'enfant.)

2. Le CSS a augmenté en taille et en complexité.

L'augmentation de la taille peut être reprochée mais elle n'est que temporaire. Maintenant que nous avons ce module réutilisable, nous pouvons l'appliquer dans l'ensemble du site sans duplication de code. Il en est de même pour la complexité accrue. Nous avons dû inclure des particularités pour les navigateurs obsolètes et intégrer des hacks qui peuvent hérisser les poils de certains. Cependant, les sélecteurs sont moins complexes, ce qui nous permet d'étendre cet agencement tout en continuant à minimiser l'impact de la spécificité.

LES RÈGLES DE MODULES

Comme je l'ai déjà mentionné brièvement dans la section précédente, un module est un composant de la page moins imposant. C'est votre barre de navigation, vos carrousels, vos dialogues, vos widgets etc. Ce sont les agréments sur votre page. Les modules se trouvent à l'intérieur des composants de l'agencement. Les modules peuvent aussi parfois être intégrés dans d'autres modules. Chaque module devrait être conçu comme un composant seul et unique. En faisant ainsi, la page sera plus souple. Si votre code est bien fait, les modules pourront facilement être déplacés dans d'autres endroits de l'agencement sans rien casser.

Lorsque vous définissez la règle d'un module, évitez l'emploi de sélecteurs ID ou de sélecteurs d'éléments, tenez-vous en aux classes. Un module contient sûrement un certain nombre d'éléments, il serait donc pratique d'utiliser des sélecteurs d'enfant ou descendants pour les cibler.

Exemple de Module

.module > h2 {

padding: 5px;

}

.module span {

padding: 5px;

}

Éviter les sélecteurs d'éléments

N'utilisez des sélecteurs d'enfant ou descendants avec des sélecteurs d'éléments que si les sélecteurs d'éléments sont et resteront prévisibles. Utiliser .module span est correct si vous pouvez prévoir que span sera utilisé et mis en page de la même manière à chacune de ses occurrences dans ce module.

Exemple de Module

<div class="fld">

<span>Nom du dossier</span>

</div>

/* Module du dossier */

.fld > span {

padding-left: 20px;

background: url(icon.png);

}

Un problème se pose ici : plus un projet devient complexe, plus vous allez devoir étendre la fonctionnalité d'un composant et plus vous allez, par conséquent, être limité par le fait d'avoir utilisé cet élément générique dans votre règle.

Utiliser un élément générique pour la mise en page

<div class="fld">

<span>Nom du dossier</span>

<span>(32 items)</span>

</div>

Maintenant nous sommes dans le pétrin. Nous ne voulons pas que l'icône apparaisse sur les deux éléments du dossier de notre module. Cela me mène au point suivant :

N'incluez que des sélecteurs comportant de la sémantique. Un span ou un div n'en comportent pas. Un en-tête en a un peu. En revanche, une classe définie sur un élément en a beaucoup.

Utiliser un élément générique pour la mise en page

<div class="fld">

<span class="fld-name">Nom du dossier</span>

<span class="fld-items">(32 items)</span>

</div>

En ajoutant les classes aux éléments, nous augmentons leur sémantique et nous enlevons toute ambiguïté concernant le style qui leur est appliqué.

Si vous préférez utiliser un sélecteur d'élément, vous devez le placer au premier niveau de profondeur du sélecteur de classe. En d'autres termes, vous devez être dans la situation d'utiliser un sélecteur d'enfant, sur le modèle .class element. Autrement, vous devez être complètement sûr que l'élément en question ne pourra pas être confondu avec un autre élément.

Plus l'élément HTML est général au niveau sémantique (comme un span ou un div) plus il y a de chances que cela crée un conflit dans le déroulement du projet. Les éléments ayant une sémantique plus développée, comme les titres (h1,h2...), ont plus tendance à apparaître naturellement dans un conteneur spécifique, vous permettant alors plus facilement d'utiliser un sélecteur d'élément à bon escient.

Nouveaux contextes

Utiliser l'approche du module nous permet également de comprendre plus facilement où les changements de contexte vont se produire. Par exemple, la nécessité d'un nouveau contexte de positionnement va probablement se produire au niveau de l'agencement ou à la racine d'un module.

Sous-classement de modules

Lorsque nous avons le même module dans différentes sections, notre premier réflexe est d'utiliser l'élément parent pour designer le module différemment.

Sous-classement

.pod {

width: 100%;

}

.pod input[type=text] {

width: 50%;

}

#sidebar .pod input[type=text] {

width: 100%;

}

L'ennui dans cette approche est que vous pouvez rencontrer des problèmes spécifiques qui nécessiteront encore plus de sélecteurs ou l'utilisation de !important pour les résoudre.

Si nous allons plus loin dans cet exemple, nous voyons que nous avons un champ de saisie avec deux largeurs différentes. Dans tout le site, l'input est accompagné d'un label qui permet à la largeur de ce champ d'être réduite de moitié. Par contre, dans la barre latérale, le champ serait trop petit donc nous lui redonnons une taille 100% tout en conservant le label. Tout semble bien fonctionner. Maintenant, nous devons ajouter un nouveau composant à notre page, qui a besoin du même style que le conteneur .pod. Nous réutilisons donc cette classe. Cependant, ce conteneur .pod est particulier et a une largeur limitée quelle que soit sa position sur le site. Il doit mesurer 180 pixels de largeur.

Combat contre la spécificité

.pod {

width: 100%;

}

.pod input[type=text] {

width: 50%;

}

#sidebar .pod input[type=text] {

width: 100%;

}

.pod-callout {

width: 200px;

}

#sidebar .pod-callout input[type=text],.pod-callout input[type=text] {

width: 180px;

}

Nous avons dédoublé nos sélecteurs pour pouvoir outrepasser la spécificité du #sidebar.

Au lieu de cela, nous devrions plutôt reconnaître que l'agencement fixe de la barre latérale est une sous-classe du conteneur .pod et donc, le mettre en page en fonction.

Combat contre la spécificité

.pod {

width: 100%;

}

.pod input[type=text] {

width: 50%;

}

.pod-constrained input[type=text] {

width: 100%;

}

.pod-callout {

width: 200px;

}

.pod-callout input[type=text] {

width: 180px;

}

En sous-classant le module, les noms des classes du module de base et du sous-module sont appliqués à l'élément html.

Nom de classe d'un sous-module en HTML

<div class="pod pod-constrained">...</div>

<div class="pod pod-callout">...</div>

Essayez d'éviter le style conditionnel basé sur la localisation. Si vous voulez changer le design d'un module pour l'utiliser autre part sur la page ou le site, sous-classez plutôt le module.

Dans ce combat contre la spécificité (et si IE6 n'est pas une préoccupation), alors vous pouvez dédoubler votre nom de classe comme dans l'exemple précédant.

Sous-classer

.pod.pod-callout { }

<!-- Dans le HTML -->

<div class="pod pod-callout"> ... </div>

Vous pouvez être concerné par cela, en fonction de l'ordre de chargement. Par exemple, sur Yahoo! Mail, le code provient de différents endroits. Nous avions notre style de base pour les boutons, puis nous avions un ensemble de boutons spécifiques pour l'écran Composer. Cependant, lorsque vous cliquiez pour ajouter un contact à votre carnet d'adresse, cela chargeait un composant d'un module différent : le carnet d'adresse. (Oui, Le carnet d'adresse est un produit différent chez Yahoo!) Le carnet d'adresse chargeait ses propres styles de boutons de base, en outrepassant les styles des boutons sous-classés que nous avions.

Si l'ordre de chargement entre en jeu dans votre projet, alors faites attention à ces problèmes spécifiques.

Des composants, ayant un agencement spécifique défini grâce aux ID, pourraient très bien être utilisés pour permettre les styles spécifiques de modules. Mais le fait de sous-classer le module lui permettra d'être plus facilement déplacé dans d'autres sections du site et vous évitera d'accroître inutilement la spécificité de votre code.

LES RÈGLES D'ÉTAT

Un état est quelque chose qui grandit et qui outrepasse les autres styles. Par exemple, une section accordéon peut être soit dans l'état replié, soit dans l'état déroulé. Un message apparaît soit dans un état de succès, soit dans un état d'erreur.

Les états s'appliquent en général au même élément qu'une règle d'agencement ou au même élément qu'une classe d'un module de base.

Un état appliqué à un élément

<div id="header" class="is-collapsed">

<form>

<div class="msg is-error">

Il y a une erreur !

</div>

<label for="searchbox" class="is-hidden">Recherche</label>

<input type="search" id="searchbox">

</form>

</div>

L'élément en-tête n'a qu'un ID. C'est la raison pour laquelle nous pouvons considérer que tout style sur cet élément, s'il y en a, servira les objectifs de l'agencement et que le is-collapsed représente un état replié. Nous pouvons présumer que sans cette règle d'état, l'état par défaut est l'état déroulé.

Le module msg est assez simple et comporte un état d'erreur. Nous pourrions imaginer qu'un état de succès pourrait être attaché au message, dans le cas contraire.

Pour finir, au label du champ est appliqué un état caché, pour qu'il ne soit pas visible à l'oeil nu mais tout de même présent pour les lecteurs d'écran. Dans ce cas, nous appliquons l'état à l'élément de base et nous n'outrepassons pas un agencement ou un module.

N'est-ce pas simplement un module ?

Il y a de nombreuses similitudes entre le style d'un sous-module et le style d'un état. Ils modifient tous deux l'apparence existante d'un élément. Cependant, ils diffèrent en deux points :

1. Les styles d'état peuvent s'appliquer à l'agencement et/ou au style du module.

2. Les styles d'état sont dépendants d'un JavaScript.

C'est ce second point qui constitue la distinction la plus importante. Les styles de sous-module s'appliquent à un élément en temps voulu puis ne sont plus jamais modifiés. Les styles d'état, eux, s'appliquent aux éléments pour indiquer un changement d'état lors de son utilisation sur la machine client.

Par exemple, cliquer sur un onglet permet d'activer cet onglet. C'est la raison pour laquelle l'utilisation d'une classe is-active ou is-tab-active est appropriée. Cliquer sur le bouton de fermeture d'un dialogue cache le dialogue. C'est pourquoi, l'utilisation d'une classe is-hidden est appropriée.

L'utilisation de !important

Les états devraient être appliqués de sorte à pouvoir se suffire à eux-mêmes et sont en général composés d'un sélecteur de classe unique.

Puisque qu'un état va probablement outrepasser le style d'une règle plus complexe, l'utilisation du !important est autorisée et, si j'ose dire, recommandée. (J'avais l'habitude de dire que !important n'était jamais utile mais il s'avère que dans certains systèmes complexes, il est souvent indispensable.) En général, vous n'aurez pas deux états appliqués au même module ou deux états affectant le même type de style, les conflits de règles provenant du fait d'utiliser !important devraient donc être rares.

Cela dit, faites attention. Laissez le !important inactif tant que vous n'en avez pas réellement besoin (et vous comprendrez pourquoi dans ce prochain exemple). Souvenez-vous que l'utilisation du !important devrait être évitée pour tous les autres types de règles. Seuls les états peuvent en avoir.

Combinaison des règles d'états et des modules

Inévitablement, une règle d'état ne va pas pouvoir compter sur l'héritage pour appliquer son style au bon endroit. Parfois, un état est très spécifique à un module particulier ayant un style tout à fait unique.

Dans le cas où une règle d'état est créée pour un module spécifique, le nom de la classe d'état devrait comporter le nom du module. La règle d'état devrait également être placée avec les autres règles du module et non avec le reste des règles d'état générales.

Règles d'état pour le module

.tab {

background-color: purple;

color: white;

}

.is-tab-active {

background-color: white;

color: black;

}

Si vous faites un chargement en temps réel de votre CSS, les états génériques devraient être considérés comme faisant partie des styles de base généraux, et être chargés dès la première page. Les styles d'un module particulier n'auront pas besoin d'être chargés tant que le module en question ne l'est pas.

LES RÈGLES DE THÈME

Les règles de thème ne sont pas si fréquentes dans un projet, c'est la raison pour laquelle j’ai hésité, au départ, à les considérer comme une catégorie à part entière. Cependant, certains projets doivent y faire appel comme ce fut notre cas pour le projet Yahoo! Mail.

C'est probablement évident me direz-vous, mais un thème définit les couleurs et les images qui donnent à votre application ou à votre site son aspect et son effet ! Séparer le thème en différents styles permet de redéfinir facilement ces styles pour en faire des thèmes alternatifs. La thématisation dans un projet est nécessaire lorsque vous voulez donner à l'utilisateur la possibilité de choisir entre différentes formes du site.

Par exemple, votre site peut proposer différentes couleurs pour les différentes partie du site. Ou bien vous pouvez laisser la possibilité à l'utilisateur de choisir sa couleur en fonction de ses préférences. Ou bien vous pouvez encore avoir besoin de différents thèmes s'adaptant à différents pays ou langues.

Les thèmes

Un thème peut affecter tous les types primaires. Il peut outrepasser les styles de base et les couleurs des liens par défaut. Il peut changer les éléments des modules comme les couleurs et les bordures. Il peut affecter les agencements avec différentes variantes ainsi qu'altérer l'apparence des états.

Admettons que vous ayez un module de dialogue qui doit avoir une bordure bleue. La bordure en elle-même sera initialement définie dans le module, et le thème définira ensuite la couleur :

Intégration d'un thème dans un module

/* dans nom-du-module.css */

.mod {

border: 1px solid;

}

/* dans theme.css */

.mod {

border-color: blue;

}

En fonction de l’ampleur de la thématisation, il peut être plus facile de définir des classes spécifiques au thème. Dans le cas de Yahoo! Mail nous avons gardé la thématisation des régions spécifiques de la page. Cela a rendu la conception de nouveaux thèmes plus simple, sans pour autant toucher la conception globale, et tout en donnant à l’utilisateur une certaine personnalisation possible.

Pour une thématisation plus vaste, il faut utiliser un préfixe theme- sur les éléments spécifiques du thème. Il sera alors plus facile de les appliquer à d'autres éléments de la page.

Classes de thèmes

/* dans le fichier theme.css */

.theme-border {

border-color: purple;

}

.theme-background {

background: linear-gradient( ... );

}

Chez Yahoo! Mail, nous utilisons un Template Mustache pour nos CSS, qui permet de spécifier plusieurs valeurs de couleur, une image de fond et de créer un fichier CSS final pour la production, cela en vue de maintenir une homogénéité entre tous nos fichiers de thème – il y en a plus d'une cinquantaine.

La typographie

Dans la thématisation, il y a des moments où vous devez redéfinir les polices utilisées de manière générale sur le site, comme c'est le cas lors d'une localisation de site. Certains pays, comme la chine ou la Corée, ont des idéogrammes difficiles à lire et des tailles de police plus petites. En conséquence, nous créons des fichiers de police séparés pour chaque localisation, qui redéfinissent la taille de la police des composants.

Les règles de police affectent normalement les styles de base, des modules et de l'état. Les styles de police ne sont en général pas spécifiés au niveau de l'agencement car ceux-ci sont utilisés pour le positionnement et le placement des éléments, non pour les changements stylistiques comme la police et les couleurs.

Tout comme pour les fichiers de thème, la définition de classes pour la police, comme par exemple, font-large, ne devrait pas être nécessaire. Votre site ne devrait avoir que 3 à 6 tailles de police différentes. Si vous avez plus de 6 tailles de police dans votre projet, vos utilisateurs risquent de ne même pas le remarquer et vous rendez votre site plus complexe à maintenir.

Ce que renferme un nom

Nommer les classes de thème et de typographie est en général complexe car nous sommes dans une industrie qui les considère comme étant non-sémantiques. Les composants d'un thème, quant à eux, sont visuels et non-sémantiques de manière inhérente. Mais pour la typographie, ce n'est pas vraiment le cas. Après tout, c'est de hiérarchie visuelle dont il s'agit dans le design, votre typographie devrait donc refléter cela. C'est la raison pour laquelle la convention de nommage que vous choisissez au final devrait indiquer les différents niveaux d'importance, tout comme vous le feriez avec les niveaux de titres dans HTML.

LES CHANGEMENTS D'ÉTAT

Vous avez un fichier Photoshop devant vous et vous avez la mission de l’intégrer par la magie du html et du css (avec peut-être, et dans une bonne mesure, un peu de JavaScript).

Il semblerait évident de commencer directement la conversion du document en code. Cependant, il y a de fortes chances pour que plusieurs des éléments de votre page aient besoin d'être représentés dans différents états. Il y a l'état par défaut, décrivant l'apparence par défaut d'un composant, puis son apparence lors du changement d'état.

Qu'est-ce qu'un changement d'état ?

Les changements d'état se font par l'un des moyens ci-dessous :

1. Un nom de classe

2. Une pseudo-classe

3. Une Media Query

Un changement par nom de classe se fait grâce à JavaScript, par l'intermédiaire d'une interaction, comme le mouvement de la souris, une commande du clavier, ou un autre événement. L'élément en question se voit alors appliquer une nouvelle classe entraînant une modification de son apparence.

Un changement par pseudo-classe se fait par l'intermédiaire d'un certain nombre de pseudo-classes, et il en existe beaucoup. Dans ce cas, nous n'avons plus besoin de JavaScript pour décrire le changement d'état. Mais les pseudo-classes sont tout de même limitées, dans le sens où nous ne pouvons appliquer des changements de style qu'aux éléments descendants ou frères de l'élément auquel la pseudo-classe s'applique. Sinon, nous sommes obligés de revenir à JavaScript.

Pour finir, les Media Queries décrivent la manière dont les choses devraient changer de style en fonction de certains critères, comme par exemple, la variation de taille des viewports.

Dans un système basé sur les modules, il est important de considérer un design fondé sur l'état, à appliquer à chaque module. Lorsque vous vous demandez quel est l'état par défaut, alors vous êtes en train de penser de manière proactive à l'amélioration progressive de votre projet. C'est également une manière légèrement différente d’aborder les problèmes.

Changement par l'intermédiaire d'un nom de classe

La plupart des changements par nom de classe sont directs. Ils s'appliquent à des éléments qui changent d'état. Par exemple, un utilisateur clique sur une icône de fermeture pour faire apparaître ou cacher un élément sur la page.

Changement d'état avec JavaScript par l'intermédiaire d'un nom de classe

// avec jQuery

$('.btn-close').click(function(){

$(this).parents('.dialog').addClass('is-hidden');

})

Cet exemple jQuery ajoute un gestionnaire d'événement « click » à chaque élément comportant le nom de classe btn-close. Lorsque l'utilisateur clique sur le bouton, la source de l'évènement est prise en compte et la structure du DOM est remontée jusqu'à ce que l'élément portant la classe dialog soit retrouvé. Ensuite, il lui est appliqué l'état de classe is-hidden.

Dans d'autres cas, un changement d'état peut avoir un plus grand impact.

Un modèle de design d'interface fréquent est celui d'un bouton donnant accès à un menu. Dans ce cas, le menu évolue d'un état enroulé à un état déroulé. Quelles options s'offrent à nous pour provoquer ce changement ? Cela dépend fortement de votre structure HTML. Par exemple, sur le site Yahoo!, les menus sont chargés au moment de la requête et sont donc insérés tout en haut du DOM. Nous avons utilisé une convention de nommage pour relier les deux ensemble.

Le bouton et le menu séparés dans le même document

<div id="content">

<div class="toolbar">

<button id="btn-new" class="btn" data-action="menu">New</button>

</div>

</div>

<div id="menu-new" class="menu">

<ul> ... </ul>

</div>

L'action liée à un clic JavaScript transmet l'info « Hey, tu dois charger le menu ». Le bouton ID est nécessaire pour trouver le menu correspondant. Voici comment cela fonctionnerait avec jQuery :

Charger le menu avec jQuery

// Ajoute un gestionnaire de clic au bouton

$('#btn-new').click(function(){

// Insert jQuery au bouton cliqué

var el = $(this);

// Change l'état du bouton

el.addClass('is-pressed');

// Trouve le menu en déroulant btn- et

// l'ajoute au sélecteur de menu

$('#menu-' +
el.id.substr(4)).removeClass('is-hidden');

});

Comme illustré dans l'exemple ci-dessus, l'état change grâce à un seul élément qui est modifié sur deux éléments différents, à deux endroits différents, à l'aide de JavaScript.

Mais quand est-il si le menu est disposé juste à côté du bouton ?

Bouton et menu dans la même partie du document.

<div id="content">

<div class="toolbar">

<button id="btn-new" class="btn" data-action="menu">New</button>

<div id="menu-new" class="menu">

<ul> ... </ul>

</div>

</div>

</div>

Le code précédant fonctionnerait exactement de la même manière et n'aurait absolument pas besoin d'être modifié. Cependant, il existe des alternatives. Votre premier réflexe pourrait être d'ajouter une classe à un élément parent et de définir le style du bouton et du menu à ce niveau là.

Ajout d'une classe à un élément parent pour appliquer le style les éléments enfants

<div id="content">

<div class="toolbar is-active">

<button id="btn-new" class="btn" data-action="menu">Nouveau</button>

<div id="menu-new" class="menu">

<ul> ... </ul>

</div>

</div>

</div>

/* Le CSS pour appliquer le style */

.is-active .btn { color: #000; }

.is-active .menu { display: block; }

Le problème de cette approche est que la structure HTML ne peut plus être modifiée. Il doit y avoir un élément contenant. Le menu et le bouton doivent exister dans cet élément contenant. Espérons qu'il n'y ait aucun bouton supplémentaire à rajouter dans cette barre d'outils !

Une autre approche consiste à appliquer la classe active au bouton, comme nous l'avons fait précédemment, et à utiliser un sélecteur adjacent pour activer le menu.

Activer le menu avec un sélecteur adjacent

<div id="content">

<div class="toolbar">

<button id="btn-new" class="btn is-active" data-action="menu">Nouveau</button>

<div id="menu-new" class="menu">

<ul> ... </ul>

</div>

</div>

</div>

/* Le CSS pour pour appliquer le style */

.btn.is-active { color: #000; }

.btn.is-active + .menu { display: block; }

Je préfère cette approche à celle appliquant une classe d'état à un élément parent, car l'état est mieux combiné au module auquel il s'applique. Cependant, le menu HTML doit toujours être lié au bouton HTML : l'un doit venir immédiatement après l'autre. Si vous êtes en mesure de pouvoir établir cette homogénéité et cette cohérence dans votre projet, alors c'est une approche qui peut vous correspondre.

Pourquoi les états parents et adjacents posent-ils problème ?

La raison pour laquelle cette approche peut être plus problématique que celle consistant simplement à appliquer un état à chaque module, est que l'endroit où cette règle s'applique n'est plus vraiment clair. Le menu n'est plus un simple menu. C'est un menu de boutons. Si vous aviez besoin de modifier l'état actif de ce module, devriez-vous modifier le CSS du bouton ou celui du menu ?

Tout cela pour dire qu'appliquer un état à chaque bouton est l'approche la plus recommandée. Vous créez une meilleure séparation entre les modules, rendant ainsi votre site plus facile à tester, à développer et à faire évoluer.

Gérer le changement d'état avec les sélecteurs d'attribut

En fonction de votre support de navigateur, vous pouvez également tirer parti des sélecteurs d'attribut pour gérer les changements d'état. Cela peut être utile pour :

- Isoler les états des classes de l'agencement et du module

- Permettre des transitions plus faciles entre les états multiples

Regardons l'exemple d'un bouton pouvant avoir différents états : par défaut, pressé ou désactivé.

Vous pouvez utiliser une convention de nommage de sous-module.

Convention de nommage de sous-module

.btn { color: #333; }

.btn-pressed { color: #000; }

.btn-disabled { opacity: .5; pointer-events: none; }

Si un bouton doit pouvoir alterner entre différents états, alors il semblerait plus logique d'utiliser une convention de nommage d'état.

Convention de nommage d'état

.btn { color: #333; }

.is-pressed { color: #000; }

.is-disabled { opacity: .5; pointer-events: none; }

J'aime la comparaison entre ces deux exemples parce qu'elle montre que dans ce livre, il s'agit beaucoup de clarification et de conventions de nommage. Je serais ravi de voir l'un ou l'autre de ces exemples dans un projet. Maintenant, abordons une autre approche : les sélecteurs d'attribut.

Convention de sélecteur d'attribut

.btn[data-state=default] { color: #333;}

.btn[data-state=pressed] { color: #000;}
.btn[data-state=disabled] { opacity:
.5; pointer-events: none; }

<!-- HTML -->

<button class="btn" data-state="disabled">Désactivé</button>

Le préfixe data- sur l'attribut fait partie des spécifications du HTML5 qui vous permettent de créer vos propres noms d'attribut et de les placer dans l'espace de nommage des données afin qu'il n'y ait aucun conflit avec les futures spécifications des attributs HTML. Changer l'état d'un bouton ne nécessite pas d'enlever ou d'ajouter des classes mais simplement de changer la valeur d'un seul attribut.

Changement d'état avec jQuery

// Ajoute un gestionnaire de clic à chaque bouton

$(".btn").bind("click", function(){

// change l'état à celui de pressé

$(this).attr('data-state', 'pressed');

});

Il faut reconnaître qu’avec les bibliothèques JavaScript comme jQuery, il n'est pas compliqué de manipuler les classes pour gérer les états. jQuery a des méthodes comme hasClass, addClass, et toggleClass qui rendent le travail avec les noms de classes très facile.

Cela est suffisant pour dire que vous avez différentes possibilités pour représenter un état.

Le changement d'état basé sur les classes avec les animations CSS

Les animations sont un procédé intéressant dont certains disent qu'il définit le comportement à un niveau où il ne le devrait pas. Les CSS sont pour le style après tout. Et JavaScript est pour le comportement.

La distinction à faire ici est de comprendre que les CSS définissent un état visuel. Nous pouvons utiliser JavaScript pour changer l'état d'un élément sur notre page. JavaScript ne devrait cependant pas être utilisé pour décrire un état. C'est-à-dire qu'il ne devrait pas être utilisé pour ajouter des styles inline.

Historiquement, nous avons utilisé JavaScript pour créer des animations parce que c'était le seul moyen que nous avions à disposition (malgré HTML+TIME2).

Réfléchir en ces termes peut nous aider à formuler notre approche dans différentes situations. Par exemple, il n'est pas rare qu’un message apparaisse sur une page pendant quelques instants puis disparait ensuite.

Changement d'état avec JavaScript

function showMessage (s) {

var el = document.getElementById('message');

el.innerHTML = s;

/* établit l'état */

el.className = 'is-visible';

setTimeout(function(){

/* établit l'état d'origine */

el.className = 'is-hidden';

}, 3000);

}

L'état du message passe de caché à visible pour revenir à l'état caché. Le code JavaScript établit les changements entre ces états et c'est ensuite le CSS qui peut être utilisé pour animer ces états – en utilisant soit des transitions soit des animations.

Le CSS gérant les transitions

@keyframes fade {

0% { opacity:0; display:block; }
100% { opacity:1; display:block; }

}

.is-visible {

display: block;

animation: fade 2s;

}

.is-hidden {

display: none;

animation: fade 2s reverse;

}

Je reconnais que ce dernier exemple ne pourrait pas réellement fonctionner. Malheureusement, cette spécification d'animation et les implémentations du navigateur ne nous permettent pas de spécifier, dans notre animation, des propriétés qui ne concernent pas l'animation. Heureusement, la spécification évolue et pourrait autoriser cela au final – si les implémentations des navigateurs suivent l'évolution. En même temps, nous devons faire quelque chose comme ce qui suit.

Les animations dans les navigateurs actuels

@-webkit-keyframes fade {

0% { opacity:0;  }

100% { opacity:1; display:block; }

}

.is-visible {

opacity: 1;

animation: fade 2s;

}

.is-hidden {

opacity: 0;

animation: fade 2s reverse;

}

.is-removed {

display: none;

}

/* puis notre javascript */

function showMessage (s) {

var el = document.getElementById('message');

el.innerHTML = s;

/* définit l'état */

el.className = 'is-visible';

setTimeout(function(){

/* définit le retour à l'état */

el.className = 'is-hidden';

setTimeout(function(){

el.className = 'is-removed';

}, 2000);

}, 3000);

}

Dans ce cas, j'ai modifié le code pour qu'il fasse l'animation, mais cette-fois-ci, en utilisant JavaScript pour enlever l'élément du flux une fois que l'animation est finie.

De cette manière, nous maintenons la séparation entre le style (ou l'état) et le comportement.

Changement par l'intermédiaire de pseudo-Classes

Comme nous venons de le voir, nous pouvons utiliser des classes et des attributs pour définir les changements d'état sur un module. Cependant, les CSS nous proposent de nombreuses pseudo-classes pouvant également nous aider à gérer les états et les changements d'état.

Dans CSS2.1, les trois pseudo-classes les plus utiles sont les « dynamiques » qui réagissent à l'interaction de l'utilisateur : :hover, :focus, et :active. CSS3 ajoute plusieurs nouvelles pseudo-classes dont la plupart sont basées sur la structure HTML (comme :nth-child ou :last-child). Il y a plusieurs pseudo-classe UI (interface homme-machine) en CSS3 qui peuvent répondre à des interactions de forme, ce qui peut également être bien utile.

L'état par défaut d'un module est normalement défini sans pseudo-classe. Celle-ci sert plutôt à définir l'état secondaire d'un module.

Définition d'états par les pseudo-classes

.btn {

background-color: #333; /* gris */

}

.btn:hover {

background-color: #336; /* bleu-vert */

}

.btn:focus {

/* Ombre portée bleue */

box-shadow: 0 0 3px rgba(48,48,96,.3);

}

Comme les modules sont sous-classés, vous pouvez être amené à définir les états de pseudo-classes pour les sous-modules également, ce qui peut potentiellement devenir compliqué.

Définition d'états des sous-modules à l'aide de pseudo-classes

.btn {

background-color: #333; /* gris */

}

.btn:hover {

background-color: #336; /* bleu-vert */

}

.btn:focus {

/* Ombre portée bleue */

box-shadow: 0 0 3px rgba(48,48,96,.3);

}

/* l'état par défaut d'un bouton est le choix

* par défaut d'une sélection de boutons

*/

.btn-default {

background-color: #DEDB12; /* jaunâtre */

}

.btn-default:hover {

background-color: #B8B50B; /*jaune plus foncé */

}

/* pas besoin de définir un état focus par défaut */

Dans ce dernier exemple, nous avons essentiellement créé 5 variations pour un seul module : un module primaire, un sous-module, puis les états de pseudo-classes possibles. Cela peut devenir encore plus compliqué lorsque nous introduisons des états basés également sur la classe de chacune de ces variations.

Modules, sous-modules, états de classe et états de pseudo-classes Oh mince...

.btn { ... }

.btn:hover { ... }

.btn:focus { ... }

.btn-default { ... }

.btn-default:hover { ... }

.btn.is-pressed { ... }

.btn.is-pressed:hover { ... }

.btn-default.is-pressed { ... }

.btn-default.is-pressed:hover { ... }

Heureusement, seuls peu de modules dans votre interface vont nécessiter ce type de structure. Cependant, il est clair qu'une organisation appropriée de vos styles assurera une maintenance plus facile de votre projet.

Changement d'état par l'intermédiaire des Media Queries

Alors que les changements d'état par l'intermédiaire des classes et des pseudo-classes sont assez communs, les Media Queries sont en train de devenir une nouvelle approche pour gérer le changement d'état – une approche qui, traditionnellement, n’était possible qu’avec JavaScript. L'adaptative et le responsive design (conception adaptative d'un site web) utilisent les Media Queries pour réagir à différents critères. Les CSS pour l'impression (Print Style Sheets) étaient l'une des premières Media Queries nous permettant de définir à quoi les éléments devaient ressembler une fois imprimés sur une feuille.

Une Media Query peut être définie comme une feuille de style séparée qui utilise l'attribut media sur le lien de l'élément ou peut être définie à l'intérieur d'un bloc @media intégré à une feuille de style spécifique.

Liens entre feuilles de style

<link href="main.css" rel="stylesheet">

<link href="print.css" rel="stylesheet" media="print">

/* dans le fichier main.css */

@media screen and (max-width: 400px) {

#content { float: none; }

}

La plupart des exemples de Media Queries définissent un point de rupture et y renvoient tous les styles qui s'y rapportent.

Dans ce livre, nous essayons de garder les styles attachés à un module particulier avec le reste du module. Cela signifie qu'au lieu d'avoir un seul point de rupture, soit dans une feuille CSS principale, soit dans une feuille de style Media Query séparée, nous plaçons la Media Query proche des états du module.

Media Queries modulaires

/* default state for nav items */

.nav > li {

float: left;

}

/* Etat alterné pour les éléments nav sur petits écrans */

@media screen and (max-width: 400px) {

.nav > li { float: none; }

}

... autre part pour l'agencement ...

/* agencement par défaut*/

.content {

float: left;

width: 75%;

}

.sidebar {

float: right;

width: 25%;

}

/* Etat alterné pour les agencements sur petits écrans */

@media screen and (max-width: 400px) {

.content, .sidebar {

float: none;

width: auto;

}

}

Oui, cela signifie que la Media Query sera probablement déclarée plusieurs fois, mais cela permet à toute l'information concernant un module d'être regroupée au même endroit. Souvenez-vous que rassembler toute l'information d'un module au même endroit (particulièrement dans son propre fichier CSS) permet de tester le module de manière isolée et, en fonction de la manière dont vous construisez votre application, permet aux gabarits modularisés et aux CSS d'être chargés après le chargement de la page initiale.

C'est de l'état dont il s'agit

Ce chapitre a passé en revue les trois types de changement d'état : classe, pseudo-classe et les Media Queries. Le fait de penser à votre interface non seulement de manière modulaire, mais également comme une représentation de ces modules dans leurs différents états, simplifiera la séparation appropriée des styles et permettra la construction de sites plus simples à maintenir.

LA PROFONDEUR DE L'APPLICABILITÉ

Lorsque nous apprenons à travailler avec les CSS, nous utilisons des sélecteurs pour sélectionner les éléments HTML de la page auxquels nous voulons appliquer un style. Le code CSS s'est développé au fil des années pour nous donner encore plus de possibilités, grâce à un nombre croissant de sélecteurs. Néanmoins, chaque ensemble de règles que nous ajoutons à notre feuille de style accroît le nombre de connexions entre la CSS et le code HTML.

Analysons un bloc typique de code CSS que vous pourriez trouver sur un site internet.

La manière de relier le CSS à l'HTML

#sidebar div {

border: 1px solid #333;

}

#sidebar div h3 {

margin-top: 5px;

}

#sidebar div ul {

margin-bottom: 5px;

}

En regardant cela, vous pouvez voir à quoi doit ressembler la structure HTML. Il y a probablement une ou plusieurs sections dans une barre latérale, qui ont chacune un titre suivi d'une liste non-ordonnée. Si le site n'évolue pas très souvent, ce type de CSS est adapté. Je n'ai pas changé le design de mon blog en deux ans. Le besoin d'adaptation rapide n'est donc pas présent. Mais si j'essaie d'utiliser cette approche sur un site plus grand, qui peut évoluer plus fréquemment et avoir une plus grande variété de conditions au niveau du code, je vais rencontrer des problèmes. Je vais avoir besoin de rajouter davantage de règles comportants des sélecteurs plus complexes. Je peux me retrouver dans un cauchemar au niveau la maintenance.

Où est-ce que je me suis trompé ? Il y a deux soucis précis dans l'exemple que j'ai donné :

1. Je dépends fortement d'une structure HTML définie.

2. Le degré de profondeur du code HTML auquel s'appliquent les sélecteurs est trop élevé.

Diminuer la profondeur

Le HTML est comme la structure d'un arbre généalogique. La profondeur d'applicabilité est le nombre de générations affectées par une règle donnée. Par exemple, body.article > #main > #content > #intro > p > b a une profondeur d'applicabilité de 6 générations. Si ce sélecteur était écrit de la manière suivante : .article #intro b , la profondeur serait la même, 6 générations.

Le problème d'une telle profondeur est qu'elle engendre une plus grande dépendance à une structure HTML précise. Les composants de la page ne pourront pas être déplacés facilement. Si nous retournons à notre exemple avec la barre latérale, de quelle manière pouvons-nous recréer ce module dans une autre partie de la page comme le pied de page ? Nous devons dupliquer les règles.

Duplication des règles

#sidebar div, #footer div {

border: 1px solid #333;

}

#sidebar div h3, #footer div h3 {

margin-top: 5px;

}

#sidebar div ul, #footer div ul {

margin-bottom: 5px;

}

L'intersection d'origine se trouve au niveau du div et c'est à partir de là que nous devrions créer nos styles.

Simplification des règles

.pod {

border: 1px solid #333;

}

.pod > h3 {

margin-top: 5px;

}

.pod > ul {

margin-bottom: 5px;

}

Le conteneur .pod dépend encore d'une structure HTML précise mais qui est d'une profondeur beaucoup plus faible que ce que nous avions auparavant. En contre-partie, nous devons répéter la classe pod sur un certain nombre d'éléments sur la page, alors qu'avant, nous n'avions que deux éléments comportant des ID. Bien entendu, nous voulons éviter de retourner à l'aire où nous faisions des choses idiotes comme ajouter des noms de classe à chaque paragraphe.

L'avantage de cette approche qui diminue la profondeur d'applicabilité, est la possibilité de convertir plus facilement ces modules en gabarits pour avoir du contenu dynamique. Sur Yahoo!, par exemple, nous nous appuyons sur une structure Mustache pour la plupart des besoins de nos gabarits. Voici comment nous organiserions notre gabarit pour ces conteneurs .pod :

Un exemple de Template Mustache

<div class="pod">

<h3>{{heading}}</h3>

<ul>

{{#items}}

<li>{{item}}</li>

{{/items}}

</ul>

</div>

Nous essayons de trouver un équilibre entre la maintenance, la performance et la lisibilité. Aller trop en profondeur signifierait diminuer le nombre de classes dans votre HTML, ce qui accroîtrait le travail de maintenance et de gestion. De même, vous ne voulez pas (ou n'avez pas besoin) de mettre des classes sur tout. Rajouter des classes sur le h3 ou le ul dans cet exemple aurait été un peu inutile à moins que nous ayons eu besoin d'avoir un système encore plus souple.

Ce modèle de design va nous permettre d'aller encore plus loin dans cet exemple. C'est un contenant avec un en-tête et un corps principal. (Parfois, vous aurez également un pied de page.) Nous avons un ul dans cet exemple, mais dans les autres exemple nous auront soit un ol, soit un div à la place.

Encore une fois, nous pouvons dupliquer nos règles pour chaque variation.

Duplication de règle

.pod > ul, .pod > ol, .pod > div {

margin-bottom: 5px;

}

L'alternative est de classifier l'élément body du conteneur .pod.

Simplification avec une classe

.pod-body {

margin-bottom: 5px;

}

Avec l'approche de la règle du module, il n'est même pas nécessaire de spécifier la classe .pod. Nous pouvons voir visuellement que .pod-body est associé au module pod et, du point de vue du code, cela fonctionnera bien.

Un exemple de Template Mustache

<div class="pod">

<h3>{{heading}}</h3>

<ul class="pod-body">

{{#items}}

<li>{{item}}</li>

{{/items}}

</ul>

</div>

Grâce à ce petit changement, nous avons pu réduire la profondeur de l'applicabilité à la plus petite valeur possible. Le sélecteur simple signifie également que nous évitons ainsi des problèmes de spécificité potentiels. Nous sommes donc gagnants au final.

LA PERFORMANCE DES SÉLECTEURS

Dans mon travail, j'ai du faire pas mal d'analyses sur la performance. Nous utilisons plusieurs outils sur une application ou sur un site pour détecter les endroits où il y a des nœuds. L'un de ces outils est Google Page Speed qui fournit plusieurs recommandations quant au moyen d'améliorer notre code JavaScript et de rendre le site en question plus performant. Avant de détailler ces recommandations, nous devons comprendre un peu plus la manière dont les navigateurs évaluent les CSS.

La manière dont les CSS sont évaluées

Le style d'un élément est évalué à la création de l'élément

Nous voyons souvent nos pages comme des documents remplis et complets, pleins d'éléments et de contenu. Cependant, les navigateurs sont conçus pour gérer les documents en flux continu. Ils commencent à recevoir un document du serveur et peuvent donner son rendu avant même qu'il ne soit complètement chargé. Chaque nœud est évalué et rendu au viewport dès qu'il est réceptionné.

Un exemple de document HTML

<body>

<div id="content">

<div class="module intro">

<p>Lorem Ipsum</p>

</div>

<div class="module">

<p>Lorem Ipsum</p>

<p>Lorem Ipsum</p>

<p>Lorem Ipsum <span>Test</span></p>

</div>

</div>

</body>

Le navigateur commence sa lecture en haut et voit un élément body. À ce stade, il pense que le body est vide. Il n'a rien évalué d'autre. Puis le navigateur va déterminer les styles et les appliquer à l'élément. Quelles sont la police, la couleur, la taille de caractère ? Après avoir analysé ces informations, elles apparaissent à l'écran.

Ensuite, il voit un élément div avec un ID pour le contenu.

Encore une fois, à ce point, il pense qu'il est vide. Il n'a rien analysé d'autre. Le navigateur doit analyser le style et c'est alors qu'il peut apparaître à l'écran. Le navigateur va alors déterminer s'il doit redéfinir le body – est-ce que l'élément est devenu plus large, plus haut ? (J'imagine qu'il y a d'autres considérations mais les variations de largeur et de hauteur sont les effets des éléments enfants sur leur parents les plus fréquents.)

Ce processus continue jusqu'à ce que le navigateur ait atteint la fin du document.

Pour visualiser ce processus chez Firefox, visitez le lien suivant : http://youtu.be/ZTnIxIA5KGw

Les CSS sont évaluées de droite à gauche.

Pour déterminer si une règle CSS s'applique à un élément particulier, le navigateur la lit de droite à gauche.

Si vous avez une règle comme body div#content p { color: #003366; } alors pour chaque élément, le navigateur va d'abord chercher à savoir si c'est un paragraphe – au moment de son rendu sur la page. Si oui, alors il va remonter dans la structure du DOM pour demander et vérifier si un des parents porte un attribut ID équivalent à content puis vérifier si c'est bien un élément div qui porte l'attribut. S'il trouve ce qu'il cherche, il va continuer à remonter le DOM jusqu'à vérifier la présence du parent body.

En travaillant ainsi, de la droite vers la gauche, le navigateur peut déterminer si une règle s'applique à cet élément particulier qu'il veut faire apparaître sur le viewport le plus rapidement possible. Pour déterminer la performance d'une règle, vous devez analyser combien de nœuds doivent être évalués avant que le navigateur puisse déterminer si tel style s'applique à tel élément.

Quelles règles dominent ?