Vous êtes sur la page 1sur 410

Résumé

Vingt ans après sa conception, le langage CSS n’en est plus à ses balbutiements et
n’est plus optionnel en ce qui concerne la conception web moderne. Sans le moindre
concurrent en vue, CSS a encore de belles années devant lui. Et pour cause, il est
toujours en perpétuelle évolution !
Ce livre n’a pas pour prétention d’être le guide ultime de l’intégrateur dans la mesure où il
ne reprend pas les bases. Il offre simplement une mise à niveau en levant le voile sur tous
les modules CSS, afi n d’offrir dès aujourd’hui les connaissances nécessaires à la
réalisation de sites et d’applications web. En effet, les enjeux comme les objectifs ne sont
plus les mêmes qu’il y a quelques années, aussi est-il important que les intégrateurs,
designers et développeurs s’arment face aux nouvelles problématiques que sont le
Responsive Web Design, le rétrécissement de l’écart entre le Web et le natif, et la course à
la performance.
Qu’il s’agisse de mise en page avec Flexbox ou Grid Layout, d’embellissement des
interfaces, d’élaboration d’animations ou même de design fl uide avec les Media Queries,
vous devriez être capable de maîtriser tous ces sujets au sortir de votre lecture.
Au-delà de l’aspect purement didactique de l’ouvrage, vous trouverez un grand nombre
d’exemples et de mises en pratique, ainsi que tout ce que vous devez savoir vis-à-vis du
support des fonctionnalités par les navigateurs. Pour fi nir, vous découvrirez dans les
annexes la liste des valeurs par défaut des propriétés CSS, celle des propriétés que l’on
peut animer et une bibliographie pour aller plus loin.
À qui s’adresse cet ouvrage ?
– Aux intégrateurs désireux d’aller plus loin avec CSS
– Aux designers souhaitant se mettre au design in the browser
– À tous les concepteurs de sites et d’applications voulant se mettre à niveau vis-à-vis des
nouveautés du langage

Au sommaire
L’état actuel du W3C et des standards CSS • Une évolution implacable • Un tour d’horizon des navigateurs
d’aujourd’hui • L’état actuel des standards • L’aventure des préfixes constructeurs • La standardisation des CSS •
Les sélecteurs : l’accès au DOM • Opérateurs • Sélecteurs d’attribut • Pseudo-classes de position • Pseudo-classes
de contexte • Pseudo-classes de formulaire • Pseudo-éléments • Positionnement et layout : les nouvelles
techniques de mise en page • Modèle de boîte et box-sizing • Multicolonne • Flexbox • Grid Layout • Position «
sticky » • Régions • Masques de formes • Interfaces graphiques et amélioration visuelle • Couleurs • Opacité •
Bords arrondis • Ombres avec box-shadow • Ombres de texte avec text-shadow • Dégradés • Meilleur contrôle des
arrière-plans • Filtres CSS • Pointer-events • Images comme bordures • De nouvelles unités et valeurs • calc • rem •
ch • Unités relatives au viewport • Dimensions intrinsèques • Contrôle du texte • Gestion des débordements avec
overflow-wrap • Gestion des espaces avec white-space • Débordements de texte et text-overflow • Césures avec
hyphens • Césures agressives avec word-break • Gestion des tabulations avec tab-size • Ponctuation plus élégante
avec hanging-punctuation • Meilleurs alignements avec text-align-last • Restriction de caractères avec unicode-range
• Variables natives • Comment ça marche ? • La syntaxe • Les variables invalides • Les valeurs de recours en cas
d’invalidité • Cas particuliers et clarifications • Styles conditionnels • Feature Queries • Media Queries •
Transformations : un nouveau monde en 2D et en 3D • À quoi servent les transformations CSS ? • Les
transformations 2D • L’origine de transformation • L’ordre des transformations • Les transformations 3D •
Animations et transitions : pour des interfaces moins statiques • À quoi servent les animations ? • Animation ou
transition ? • À propos de l’accélération matérielle • JavaScript ou CSS ? • Les transitions • Les animations.

Biographie auteur
H. Giraudel
Développeur front-end passionné par CSS et auteur du site Browserhacks, Hugo
Giraudel fait part de son expertise sur son propre blog ainsi que sur les sites
SitePoint, CSS-Tricks, The Sass Way et Tuts+, entre autres. Enthousiasmé par le
préprocesseur Sass, il a su s’imposer comme référence mondiale sur le sujet. Il est
d’ailleurs l’auteur de SassDoc, un outil de documentation pour Sass.
R. Goetter
Webdesigner et gérant d’une agence web strasbourgeoise, Raphaël Goetter partage
ses connaissances à travers son site Alsacréations.com, et s’intéresse de près aux
domaines des normes du Web et de l’accessibilité. Il fait partie du collectif
Openweb.eu.org, référence francophone en matière de standards du Web.
www.editions-eyrolles.com
CSS3
Pratique du design web
Hugo Giraudel
Raphaël Goetter
Préface de Chris Coyier
ÉDITIONS EYROLLES
61, bd Saint-Germain
75240 Paris Cedex 05
www.editions-eyrolles.com
En application de la loi du 11 mars 1957, il est interdit de reproduire intégralement ou partiellement le présent ouvrage,
sur quelque support que ce soit, sans l’autorisation de l’Éditeur ou du Centre Français d’exploitation du droit de copie,
20, rue des Grands Augustins, 75006 Paris.
© Groupe Eyrolles, 2015, ISBN : 978-2-212-14023-1
Crédits photographiques : © Alexandra Lucas
DANS LA MÊME COLLECTION
C. DELANNOY. – Le guide complet du langage C.
N°14012, 2014, 844 pages.
K. AYARI. – Scripting avancé avec Windows PowerShell.
N°13788, 2013, 358 pages.
W. BORIES, O. MIRIAL, S. PAPP. – Déploiement et migration Windows 8.
N°13645, 2013, 480 pages.
W. BORIES, A. LAACHIR, D. THIBLEMONT, P. LAFEIL, F.-X. VITRANT. – Virtualisation du poste de travail Windows 7
et 8 avec Windows Server 2012.
N°13644, 2013, 218 pages.
J.-M. DEFRANCE. – jQuery-Ajax avec PHP.
N°13720, 4e édition, 2013, 488 pages.
L.G. MORAND, L. VO VAN, A. ZANCHETTA. – Développement Windows 8 - Créer des applications pour le Windows
Store.
N°13643, 2013, 284 pages.
Y. GABORY, N. FERRARI, T. PETILLON. – Django avancé.
N°13415, 2013, 402 pages.
P. ROQUES. – Modélisation de systèmes complexes avec SysML.
N°13641, 2013, 188 pages.

SUR LE MÊME THÈME


R. RIMELÉ, R. GOETTER. – HTML 5 – Une référence pour le développeur web.
N°13638, 2e édition, 2013, 752 pages.
C. SCHILLINGER. – Intégration web – Les bonnes pratiques.
N°13370, 2012, 390 pages.
S. POLLET-VILLARD. – Créer un seul site pour toutes les plates-formes.
N°13986, 2014, 144 pages.
E. MARCOTTE. – Responsive web design.
N°13331, 2011, 160 pages.
F. DRAILLARD. – Premiers pas en CSS 3 et HTML 5.
N°13944, 6e édition, 2015, 472 pages.
M. KABAB, R. GOETTER. – Sass et Compass avancé.
N°13677, 2013, 280 pages.
Préface

CSS occupe une place étrange dans le monde du développement. Il y a des développeurs
qui dédaignent ce langage, ne le jugeant pas digne de leur temps ou de leurs compétences.
Et, en même temps, ils en ont peut-être peur. Voyez-vous, CSS est à la fois très simple et
très compliqué. Après tout, c’est essentiellement une suite de déclarations composées de
paires de clés/valeurs : color: red, width: 50%, display: inline-block. Rien de bien compliqué
en soi. Mais la façon dont ces déclarations interagissent les unes avec les autres peut être
aussi complexe et casse-tête que n’importe quel autre langage de programmation. Ajoutez
à cela les problèmes d’incompatibilité entre les navigateurs, et vous pourriez presque en
venir à dire que le rôle d’intégrateur est l’un des plus difficiles qui soit !
Il y a aussi le fait que CSS évolue à une vitesse sans précédent. Ce que nous connaissons
sous le terme de « CSS 3 » est à la fois amusant et puissant, mais rend aussi les choses
plus complexes. Il existe désormais de nouveaux systèmes de mise en page comme
Flexbox, des possibilités d’animation, et même de la 3D entièrement réalisée avec CSS !
Êtes-vous parfaitement au fait de position: sticky ? Qu’en est-il des unités de
dimensionnement vis-à-vis du viewport ? Savez-vous que l’on peut tester le support des
propriétés directement au sein des feuilles de styles ? C’est un nouveau monde plein de
possibilités qui s'offre à vous !
Hugo Giraudel est la personne idéale pour vous présenter ces nouveaux concepts. Il
parvient en effet à appréhender ces idées, puis à les expliquer de manière pédagogique et
compréhensible. Je peux vous l’affirmer, non seulement parce que c’est ce qu’il fait dans
ce livre, mais aussi parce que je lis ses articles techniques depuis des années. Il est parfait
quand il s’agit d’expliquer des concepts bien spécifiques, et parvient à couvrir tous les
détails nécessaires à la compréhension de ceux-ci. J’ai même fait appel à ses services pour
m’aider à rechercher et à écrire des contenus pour mon propre site web (CSS-Tricks) !
C’est une chance que d’avoir Hugo à la tête de ce livre, réunissant autant d’informations
intéressantes et pertinentes en un même ouvrage.
Chris Coyier,
auteur du site CSS-Tricks,
développeur en chef à CodePen,
podcasteur à Shop Talk Show
Table des matières

Avant-propos
CSS, un langage en perpétuelle évolution
Pourquoi cet ouvrage ?
À qui s’adresse cet ouvrage ?
La structure de l’ouvrage
Compléments vidéo à consulter en ligne
Remerciements
CHAPITRE 1
L’état actuel du W3C et des standards CSS
Une évolution implacable
Un tour d’horizon des navigateurs d’aujourd’hui
L’état actuel des standards
L’aventure des préfixes constructeurs
La standardisation des CSS
Étape 1. Trouver une idée
Étape 2. Editor’s Draft (ED)
Étape 3. Working Draft (WD)
Étape 4. Candidate Recommendation (CR)
Étape 5. Recommendation (REC)
CHAPITRE 2
Les sélecteurs : l’accès au DOM
Les opérateurs
Opérateur d’adjacence directe (CSS 2)
Compatibilité des navigateurs pour l’opérateur d’adjacence directe
Opérateur d’adjacence générale
Compatibilité des navigateurs pour l’opérateur d’adjacence générale
Les sélecteurs d’attribut
Sélecteur d’attribut simple (CSS 2)
Compatibilité des navigateurs pour les sélecteurs d’attribut
Sélecteur d’attribut modulé
Attribut dont la valeur est comprise dans une liste séparée par des
espaces avec [attr~=“value”] (CSS 2)
Attribut dont la valeur est exacte ou démarre par une chaîne précédée
du caractère - avec [attr|=“value”] (CSS 2)
Attribut dont la valeur débute par une chaîne avec [attr^=“value”]
Attribut dont la valeur termine par une chaîne avec [attr$=“value”]
Attribut dont la valeur contient une chaîne avec [attr*=“value”]
Les pseudo-classes de position
Premier et dernier enfant avec :first-child & :last-child
Compatibilité des navigateurs pour :first-child
Compatibilité des navigateurs pour :last-child
Énièmes enfants avec :nth-child et :nth-last-child
Styler en fonction du nombre d’éléments dans le parent
Compatibilité des navigateurs pour :nth-child et :nth-last-child
Enfant unique avec :only-child
Compatibilité des navigateurs pour :only-child
Propriétés *-of-type
Compatibilité des navigateurs pour les pseudo-classes :*-of-type
Les pseudo-classes de contexte
Ciblage par ancre avec :target
Compatibilité des navigateurs pour :target
Éléments vides avec :empty et :blank
Compatibilité des navigateurs pour :empty
Gestion de la langue avec :lang (CSS 2)
Compatibilité des navigateurs pour :lang
Élément racine avec :root
Compatibilité des navigateurs pour :root
Négation avec :not
Compatibilité des navigateurs pour :not
Simplification des sélecteurs avec :matches
Compatibilité des navigateurs pour :matches
Les pseudo-classes de formulaire
Focus avec :focus (en toute simplicité)
Compatibilité des navigateurs pour :focus
État des champs de formulaire avec :enabled et :disabled
Compatibilité des navigateurs pour :enabled et :disabled
Modes d’écriture avec :read-write et :read-only
Compatibilité des navigateurs pour :read-write et :read-only
Validité des champs de formulaire avec :valid et :invalid
Compatibilité des navigateurs pour :valid et :invalid
Statut des champs de formulaire avec :optional et :required
Compatibilité des navigateurs pour :optional et :required
Précisions sur les checkboxes et les boutons radio avec :checked
et :indeterminate
Compatibilité des navigateurs pour :checked
Compatibilité des navigateurs pour :indeterminate
Valeur par défaut des boutons radio avec :default
Compatibilité des navigateurs pour :default
Gestion de l’amplitude des champs de type number avec :in-range et :out-of-
range
Compatibilité des navigateurs pour :in-range et :out-of-range
Les pseudo-éléments
Évolution de la syntaxe
Contrôle de la sélection du texte avec ::selection
Compatibilité des navigateurs pour ::selection
CHAPITRE 3
Positionnement et layout : les nouvelles techniques de mise en page
Le modèle de boîte : retour aux sources avec box-sizing
Cas pratique : simplifier les calculs de dimensions
Compatibilité des navigateurs pour box-sizing
Le multicolonne
Comment ça marche ?
Syntaxe
Largeur des colonnes avec column-width
Nombre de colonnes avec column-count
Déclaration raccourcie avec columns
Gestion de la gouttière avec column-gap
Séparation des colonnes avec column-rule
Interruption des colonnes avec break-*
Envahissement des colonnes avec column-span
Équilibrage des colonnes avec column-fill
Cas pratique : alléger une liste chargée
Cas pratique : utiliser les colonnes comme grille
Compatibilité des navigateurs pour le multicolonne
Les modèles de boîtes flexibles avec Flexbox
Comment ça marche ?
Syntaxe
Initialisation avec display: flex | inline-flex
Direction générale avec flex-direction
Gestion des retours à la ligne avec flex-wrap
Déclaration raccourcie avec flex-flow
Justification avec justify-content
Alignement du contenu avec align-items
Répartition des lignes avec align-content
Gestion de l’ordre d’apparition avec order
Accroissement avec flex-grow
Rétrécissement avec flex-shrink
Dimensions par défaut avec flex-basis
Déclaration raccourcie avec flex
Alignement particulier avec align-self
Cas pratique : un menu responsive
Cas pratique : un layout mobile first
Cas pratique : centrage absolu
Cas pratique : un formulaire fluide
Compatibilité des navigateurs pour Flexbox
Mettre en place une solution pour tous les navigateurs
Un préprocesseur pour simplifier les préfixes (Sass)
Le Grid Layout
Comment ça marche ?
Une introduction par l’exemple
Initialiser une grille
Définir la grille
Simplifier les définitions avec la fonction repeat()
Fractions de l’espace restant avec l’unité fr
Nommage
Nommer les lignes
Nommer les zones
Placer les éléments
Le placement avec grid-row-start, grid-row-end, grid-column-start et
grid-column-end
Le positionnement simplifié avec grid-row et grid-column
Le positionnement encore plus simplifié avec grid-area
Placement automatique des éléments avec grid-auto-flow
Gestion des erreurs de placement
Le terme subgrid
Cas pratique : réaliser une galerie d’images
Simplifier les choses avec un préprocesseur (Sass)
Cas pratique : créer un layout simple
Compatibilité des navigateurs pour Grid Layout
La position « sticky »
Cas pratique : un header fixe
Compatibilité des navigateurs pour position: sticky
Une solution de repli en JavaScript
Les régions
Terminologie
Comment ça marche ?
Injecter du contenu dans un flux
Réclamer le contenu d’un flux
Gérer la fin d’un flux
Quelques informations complémentaires
Boucles infinies
Arborescence HTML spécifique
Cas pratique : une gestion des encarts publicitaires différente selon la taille de
l’écran
Compatibilité des navigateurs pour les régions CSS
Les masques de forme en CSS
Comment ça marche ?
Zone de flottement
Formes de base
circle
ellipse
inset
polygon
Syntaxe
Déclaration d’une forme avec shape-outside
Seuil d’opacité avec shape-image-threshold
Marge avec shape-margin
Cas pratique : une bio avec un avatar circulaire
Compatibilité des navigateurs pour les masques de forme
CHAPITRE 4
Interfaces graphiques et amélioration visuelle
Des couleurs plus colorées
De nouvelles couleurs
La notation rgba
Compatibilité des navigateurs pour rgba
La notation hsla
Compatibilité des navigateurs pour hsla
L’opacité
opacity et l’héritage
opacity et les contextes d’empilement
Compatibilité des navigateurs pour opacity
Émuler le support sur Internet Explorer grâce aux filtres propriétaires de
Microsoft
Des bords arrondis en CSS
La version simple
Dissocier les axes X et Y
Bordures et bords arrondis
Compatibilité des navigateurs pour border-radius
Des ombres avec box-shadow
À propos des performances
Cas pratique : donner du sens aux interfaces
Compatibilité des navigateurs pour box-shadow
Des ombres de texte avec text-shadow
Compatibilité des navigateurs pour text-shadow
Des dégradés
Syntaxe
Dégradés linéaires
Dégradés radiaux
Les dégradés au quotidien
Compatibilité des navigateurs pour les dégradés
Un meilleur contrôle des arrière-plans
Plusieurs arrière-plans sur un même élément
Compatibilité des navigateurs pour les arrière-plans multiples
De nouvelles valeurs pour background-repeat
Cas pratique : placeholders dans une galerie d’images
Compatibilité des navigateurs pour background-repeat: space et
background-repeat: round
De nouvelles valeurs pour background-size
Compatibilité des navigateurs pour background-size: cover et
background-size: contain
Émuler le support sur Internet Explorer grâce aux filtres propriétaires
de Microsoft
Un nouveau système de valeurs pour background-position
Compatibilité des navigateurs pour le nouveau système de background-
position
Origine d’arrière-plan avec background-origin
Compatibilité des navigateurs pour background-origin
Fixation de l’arrière-plan avec background-attachment: local
Cas pratique : effet d’ombre sur un élément scrollable
Compatibilité des navigateurs pour background-attachment: local
Zone d’arrière-plan avec background-clip
Cas pratique : pop-up et bordure semi-transparente
Compatibilité des navigateurs pour background-clip
Les filtres CSS
Comment fonctionnent-ils ?
Syntaxe
blur
brightness
contrast
drop-shadow
grayscale
hue-rotate
invert
opacity
saturate
sepia
Cas pratique : différents coloris d’image
Compatibilité des navigateurs pour les filtres CSS
Pointer-events
Cas pratique : améliorer les performances durant le scroll
Compatibilité des navigateurs pour les pointer-events
Des images comme bordures
Comment ça marche ?
Syntaxe
border-image-source
border-image-slice
border-image-width
border-image-outset
border-image-repeat
Compatibilité des navigateurs pour border-image
CHAPITRE 5
De nouvelles unités et valeurs
Le Saint-Graal des calculs : calc
À propos de la syntaxe
Pourquoi pas un préprocesseur ?
Cas pratique : utiliser calc pour le layout
Cas pratique : position de l’arrière-plan
Cas pratique : centrage absolu
Compatibilité des navigateurs pour calc
Root em : l’évolution de l’unité em
Pourquoi rem et pas simplement em ?
Cas pratique : une grille typographique
Compatibilité des navigateurs pour l’unité rem
Une solution de repli avec un préprocesseur (Sass)
Démystification : à propos de 62.5 %
Une unité pour la largeur d’un caractère : ch
Compatibilité des navigateurs pour l’unité ch
Les unités relatives au viewport
Pourcentage de la largeur/hauteur avec vw et vh
Cas pratique : limiter la hauteur des images à la hauteur du viewport
Cas pratique : typographie responsive
Cas pratique : typographie responsive et rem
Cas pratique : conserver un ratio relatif au viewport
Compatibilité des navigateurs pour les unités vw et vh
Pourcentage de la largeur/hauteur avec vmin et vmax
Cas pratique : ratio 16:9 occupant toute la largeur du viewport
Compatibilité des navigateurs pour les unités vmin et vmax
Les dimensions intrinsèques
Largeur minimale avec min-content
Cas pratique : figure dimensionnée selon la largeur de l’image
Largeur maximale avec max-content
Comportement de type block avec fill
Largeur optimale avec fit-content
Cas pratique : liste centrée mais dimensionnée selon son contenu
Compatibilité des navigateurs pour les dimensions intrinsèques
Faciliter l’utilisation des valeurs intrinsèques avec un préprocesseur
(Sass)
CHAPITRE 6
Contrôle du texte
La gestion des débordements avec overflow-wrap
Compatibilité des navigateurs pour overflow-wrap
La gestion des espaces avec white-space
Cas pratique : affichage de code
Compatibilité des navigateurs pour white-space
Les débordements de texte et text-overflow
Cas pratique : des lignes de tableau de même hauteur
Compatibilité des navigateurs pour text-overflow
Les césures avec hyphens
Syntaxe
none
manual (valeur initiale)
auto
Compatibilité des navigateurs pour hyphens
Les césures agressives avec word-break
Compatibilité des navigateurs pour word-break
La gestion des tabulations avec tab-size
Compatibilité des navigateurs pour tab-size
Une ponctuation plus élégante avec hanging-punctuation
Compatibilité des navigateurs pour hanging-punctuation
De meilleurs alignements avec text-align-last
Compatibilité des navigateurs pour text-align-last
La restriction de caractères avec unicode-range
Cas pratique : embellir les esperluettes
Compatibilité des navigateurs pour unicode-range
Cas pratique : des blocs de code qui donnent envie
CHAPITRE 7
Variables natives
Comment ça marche ?
La syntaxe
Les variables invalides
Les valeurs de recours en cas d’invalidité
Cas particuliers et clarifications
Variables qui se référencent mutuellement
Propriété all et propriétés personnalisées
Animations
La compatibilité des navigateurs pour les variables natives
CHAPITRE 8
Styles conditionnels
Feature Queries
De la notion de « support »
Syntaxe
API JavaScript
@supports et préfixes constructeurs
Cas pratique : header fixe à compter d’une certaine position avec position:
sticky
Cas pratique : carrousel de Bootstrap 3
Compatibilité des navigateurs pour les Feature Queries
Media Queries
Syntaxe
Conjonction
Condition
Négation
Restriction
Que peut-on détecter ?
Les dimensions : width et height
L’orientation de l’appareil
Le ratio hauteur/largeur de l’écran
La résolution de l’écran
Futur : davantage de contrôle
Présence de JavaScript
Luminosité ambiante
Système de pointage
Capacité de survol
Fréquence de mise à jour
Gestion des débordements
Futur : Media Queries et variables
Cas pratique : le design Mobile First
Compatibilité des navigateurs pour les Media Queries
CHAPITRE 9
Transformations : un nouveau monde en 2D et en 3D
À quoi servent les transformations CSS ?
Les transformations 2D
Rotation
Cas pratique : réaliser des flèches
Émulation de rotation avec les filtres propriétaires
Conversion d’angle avec un préprocesseur (Sass)
Translation
À propos des offsets
Cas pratique : centrage absolu
Mise à l’échelle
Cas pratique : agrandissement au survol
Émulation de mise à l’échelle avec zoom sur Internet Explorer 8
Bug d’Android 2.3
Inclinaison
Cas pratique : un menu incliné
matrix
Compatibilité des navigateurs pour les transformations 2D
L’origine de transformation
Tester et débugger transform-origin
L’ordre des transformations
Les transformations 3D
Compatibilité des navigateurs pour les transformations 3D
Environnement 3D avec perspective
Origine de perspective
En résumé
Contexte et transform-style
Notion de faces avec backface-visibility
Cas pratique : effet card flip
CHAPITRE 10
Animations et transitions : pour des interfaces moins statiques
À quoi servent les animations ?
Animation ou transition ?
À propos de l’accélération matérielle
Compatibilité des navigateurs pour will-change
JavaScript ou CSS ?
Les transitions
« Quoi ? » avec transition-property
« Comment ? » avec transition-timing-function
« Combien de temps ? » avec transition-duration
« Quand ? » avec transition-delay
Utilisation
Quelques informations complémentaires
Cas pratique : une sidebar plus légère
Cas pratique : changement de vue sur mobile
Cas pratique : animation d’une pop-up
Compatibilité des navigateurs pour les transitions
Transitions et pseudo-éléments
Les animations
« Quoi ? » avec animation-name
« Comment ? » avec animation-timing-function
« Combien de temps ? » avec animation-duration
« Combien de fois ? » avec animation-iteration-count
« Dans quel sens ? » avec animation-direction
« Quel état ? » avec animation-play-state
« Quand ? » avec animation-delay
« Quel impact ? » avec animation-fill-mode
La définition d’une animation : @keyframes
Animations et performance
Cas pratique : animation d’un loader
Cas pratique : animation d’un carrousel
Cas pratique : animation de l’affichage d’une galerie d’images
Faciliter la génération des styles avec un préprocesseur (Sass)
Cas pratique : animations comme feedbacks
Cas pratique : mise en place d’un système anti-spoiler
Compatibilité des navigateurs pour les animations
ANNEXE A
Liste des propriétés CSS et de leur valeur par défaut
ANNEXE B
Liste des propriétés CSS qui peuvent être animées
Notes
Ce que l’on aimerait pouvoir animer
ANNEXE C
Ressources et liens
Sites francophones
Sites anglophones
Newsletters
Comptes Twitter
Bibliographie
Index
Avant-propos

CSS, un langage en perpétuelle évolution


Au cours de cette dernière décennie, peu de langages peuvent se vanter d’avoir connu une
révolution aussi exceptionnelle que celle vécue par CSS. Pour ceux d’entre vous qui
réalisaient déjà des sites web au début des années 2000, vous n’êtes pas sans vous
souvenir, peut-être avec nostalgie, que la mise en page en tableaux et les spacer.gif ne sont
pas si lointains.
Aujourd’hui, tout cela est terminé (du moins je l’espère…). CSS est en effet le langage
destiné à l’habillage des pages web, et cela n’est pas près de changer. D’autant plus qu’il
sert aux sites web, aux applications mais aussi aux livres. Autrement dit, apprendre CSS
n’est plus une option, c’est même une nécessité, tout du moins pour les personnes qui ont
vocation à travailler dans le domaine du développement web.
Heureusement, les ressources ne se font pas aussi rares qu’il y a dix ans, y compris celles
francophones. Aussi apprendre CSS, ou plutôt améliorer la qualité de ses feuilles de
styles, est-il tout à fait envisageable, et j’espère que ce livre vous aidera dans cette tâche.
Pourquoi cet ouvrage ?
Si vous êtes un lecteur assidu des ouvrages des éditions Eyrolles, vous avez peut-être (ou
avez eu) dans votre bibliothèque le livre intitulé CSS 2 – Pratique du design web, écrit par
Raphaël Goetter. Sachez que l’ouvrage que vous avez présentement entre vos mains en est
son digne successeur, du moins je l’espère !
La première édition de son aïeul date de juin 2005, soit il y a presque dix ans. Inutile de
vous dire à quel point CSS a changé depuis. C’est d’ailleurs pour cette raison que Raphaël
l’a mis à jour à trois reprises.
Seulement aujourd’hui, la mise à jour ne suffit plus. L’ouvrage initial étant trop vieux et
pour l’essentiel dépassé, il a été décidé de le reprendre depuis ses fondations, afin de le
mettre au goût du jour pour de bon et de présenter un livre dédié aux nouveautés dans le
domaine des CSS.
Comme son fier ancêtre, cet ouvrage n’a pas de prétentions audacieuses ; il ne cherche
qu’à apporter des éléments de réponse et des bases solides pour que vous soyez en mesure
d’attaquer vos projets courants et futurs avec les technologies actuelles.
À qui s’adresse cet ouvrage ?
Quand nous avons lancé le projet d’écriture de ce livre, il a rapidement été question de
déterminer sa cible. Or, comme beaucoup de langages, CSS couvre un spectre très large de
niveaux de compétences. Il existe en effet des développeurs absolument inexpérimentés,
des instruits, des novices, des confirmés, des experts, des légendes, et peut-être même que
vous ne figurez dans aucune de ces catégories ! Autant dire qu’il est difficile de contenter
tout le monde et, fort heureusement, ce n’est pas la prétention de cet ouvrage.
Nous allons aborder de nombreuses notions, certaines relativement simples comme les
sélecteurs, d’autres beaucoup plus complexes comme les nouveaux modules de mise en
page. Quoi qu’il en soit, nous ne reprendrons pas les bases, sans quoi ce livre ferait
quelques milliers de pages !
Aussi dirais-je que cet ouvrage s’adresse à des développeurs qui ont déjà utilisé CSS à
maintes reprises et qui sont en parfaite mesure de s’en sortir avec les bases qu’ils ont
apprises, mais pour qui « s’en sortir » ne suffit plus ; en d’autres termes, ceux qui désirent
aller plus loin. De fait, si vous êtes un fanatique de CSS, utilisateur de
post/préprocesseurs, mordu de veille techno et rock star du DevTools, vous risquez de
sortir de cet ouvrage tel que vous vous y êtes plongé, et possiblement un peu déçu.
Ce livre est destiné aux développeurs désireux d’apprendre et d’aller de l’avant, et non aux
néophytes les plus complets, ni aux experts en la profession.
Idéalement, j’aimerais donc que vous terminiez la lecture de cet ouvrage en étant plus
instruit, et pourquoi pas un brin inspiré !
La structure de l’ouvrage
Ce livre a pour vocation de faire un tour d’horizon des modules CSS émergents ; autant
dire qu’il y a de quoi faire ! Du coup, il n’a pas été simple de définir un ordre logique à
tout cela. Et pourtant, il y en a bien un !
Tout d’abord, Raphaël nous régalera d’une introduction sur l’état actuel des CSS, des
standards et du marché des navigateurs. On y abordera la formidable épopée des préfixes
constructeurs, l’arrivée astucieuse des flags et le chemin parsemé d’embûches
qu’emprunte une fonctionnalité avant d’être standardisée.
Ensuite, nous débuterons en douceur avec un chapitre intégralement dédié aux nouvelles
façons d’accéder au DOM (Document Object Model), c’est-à-dire aux éléments HTML.
Effectivement, avant de pouvoir appliquer des styles aux éléments, il faut pouvoir les
cibler !
Une fois que vous serez en pleine mesure de cibler n’importe quel élément dans le DOM
grâce aux sélecteurs avancés, il sera temps de s’attaquer au plat de résistance avec les
nouvelles techniques de mise en page : Flexbox, Grid Layout, multicolonne… Il y a
beaucoup de choses à voir, et ce ne sont pas les plus simples.
La structure mise en place, on pourra alors se pencher sur l’habillage des documents. Il
s’avère que CSS 3 apporte énormément de nouveautés en la matière : ombres, bords
arrondis, dégradés, manipulation des arrière-plans…
Après quoi, nous irons faire un tour du côté des nouvelles unités et fonctions, qui peuvent
s’avérer très pratiques en association avec les notions abordées dans les deux chapitres
précédents.
L’un des aspects souvent bien méconnus des CSS concerne le niveau de contrôle que l’on
peut avoir sur les contenus textuels. Il s’avère que nous avons à notre disposition de plus
en plus d’outils pour s’approcher de la typographie « print », à défaut d’être en mesure de
véritablement rivaliser. Ce sera justement le sujet de notre sixième chapitre.
Ensuite, j’ai souhaité que vous vous familiarisiez avec les variables natives qui sont tout
ce qu’il y a d’imminentes. Vu qu’il s’agit d’un élément incontournable du futur de CSS,
j’ai jugé bon de lui dédier un chapitre, somme toute assez court.
Le chapitre suivant ne sera sûrement pas une totale découverte pour vous puisqu’il traitera
des styles conditionnels : les Feature Queries (via la directive @supports) et les fameuses
Media Queries. Nous en profiterons même pour faire un tour d’horizon du futur de ces
dernières et, qui sait, peut-être serez-vous surpris ?!
Pour la fin de l’ouvrage, j’ai décidé d’aborder des notions un peu plus originales, avec
notamment les transformations CSS, y compris celles 3D, et la façon dont vous pouvez en
tirer parti dans vos créations. Seront également traitées les animations et comment les
utiliser à bon escient pour améliorer vos interfaces.
Dans les annexes, vous trouverez un certain nombre d’informations supplémentaires pour
aller plus loin, notamment une bibliographie ou encore la liste des propriétés CSS pouvant
faire l’objet d’une animation.
Compléments vidéo à consulter en ligne
Cet ouvrage a été conçu pour être aussi interactif que possible. C’est pourquoi, en plus du
livre que vous tenez entre les mains, nous vous invitons à consulter des vidéos de
formation associées que j’ai réalisées et accessibles à l’adresse suivante :
http://www.editions-eyrolles.com/go/videoslivreCSS3.
Remerciements
Avant tout, je souhaite remercier infiniment ma compagne, Alexandra, pour avoir fait
preuve d’une patience à toute épreuve et d’un soutien sans faille au cours de ces longs
mois d’écriture à l’humeur parfois difficile, et aussi pour ses belles photographies qui
illustrent ce livre.
Un grand merci également aux personnes que j’ai sollicitées au cours de la rédaction de
cet ouvrage pour des avis et des questions techniques : Rachel Andrew, Tab Atkins Jr.,
Dave DeSandro, Paul Irish, Paul Lewis, Rachel Nabors, Sara Soueidan et Ana Tudor, ainsi
que Chris Coyier qui nous fait l’honneur de sa préface.
Bien évidemment, je souhaite remercier chaleureusement les relecteurs techniques
Geoffrey Crofte et Thomas Zilliox, et surtout Raphaël Goetter qui a su m’accompagner et
me faire part de son expérience au cours de cette aventure.
Enfin, merci à toute l’équipe des éditions Eyrolles, notamment Alexandre Habian et
Muriel Shan Sei Fan qui ont su m’aiguiller dans la rédaction de ce livre, mais aussi tous
les relecteurs qui sont parvenus à corriger mes tournures pas toujours heureuses, de sorte
que ce livre soit agréable à lire.
1
L’état actuel du W3C et des standards
CSS

Inventé au début des années 1990, CSS a connu une route longue et parsemée d’embûches
pour arriver au langage de styles tel qu’on le connaît aujourd’hui. Débutons avec un état
des lieux et une mise en situation.
Une évolution implacable
En 2005, lors de la sortie du livre CSS 2 – Pratique du design web, la conception de sites
en CSS n’en était qu’à ses balbutiements et la mise en page en tableaux HTML était
monnaie courante. Pour faire simple, « passer aux standards » demeurait à cette époque
une mission pour le moins périlleuse.
En ces temps troubles, les doctypes XHTML, les framesets, spacer.gif et Internet Explorer
6 avaient le vent en poupe : imaginez-vous dans un monde où Internet Explorer 7 et
Google Chrome n’existaient pas encore ! Si, comme moi, vous avez connu cette époque
de la « bidouille », vous ne pouvez que vous réjouir de sa disparition et constater le
chemin parcouru en près de dix années.
Si je devais résumer la période web actuelle en seulement quatre mots-clés, je citerais :
• HTML5CSS3 (d’accord, j’ai triché !) ;
• Web mobile ;
• industrialisation ;
• stabilisation.
HTML 5 et CSS 3, véritables fleurons du Web moderne, apportent de nouvelles couches et
améliorations considérables aux versions précédentes. Voici les principales innovations
apportées par ces technologies :
• de multiples possibilités graphiques (arrondis, ombrages, opacité, dégradés, arrière-plans
multiples, pour ne citer que les plus connus) ;
• de nouveaux types de positionnements (Flexbox, Grid Layout, multicolonne) ;
• de nombreuses façons de cibler les éléments ;
• des transformations (rotation, zoom, déformation, translation) ;
• enfin, des animations pour que les interfaces deviennent un peu moins figées.
Et je ne viens d’évoquer que des fonctionnalités CSS 3 ! En vérité, ce sont tous les
domaines du Web qui ont bénéficié de spectaculaires avancées techniques : HTML 5, mais
aussi WebGL, SVG, Canvas, etc.
En parallèle, cette décennie marque l’avènement d’un monde « mobile ». En 2007 en
effet, l’iPhone d’Apple débarque et provoque un raz-de-marée : il est enfin possible (et
pratique) de se servir d’un téléphone portable pour surfer sur le Net. Le smartphone est né.
Avec le Web mobile s’est ouvert un formidable nouveau marché à conquérir, avec son lot
de combats de titans sans merci dans lesquels certains, tels que Google et Apple, en sont
sortis grandis et où d’autres, tels que BlackBerry, Motorola et Nokia, y ont laissé des
plumes. Ou pire…
Notre activité professionnelle se doit d’accompagner cette révolution mobile :
« Responsive Web Design » a été élu mot-clé buzz de l’année 2013, il en découle que les
notions de performance et de temps d’affichage deviennent essentielles et, enfin, une
remise en question radicale de l’ergonomie d’un site web est nécessaire dans un monde
connecté que l’on peut tenir dans sa main.
L’industrialisation apparaît elle aussi telle une évidence depuis plusieurs années. Le métier
d’intégrateur s’est en effet radicalement transformé en peu de temps, agrégeant des
contraintes jusqu’alors inédites. Aujourd’hui, il ne suffit plus de rédiger du code HTML et
CSS valide, mais bien de réaliser de nombreuses tâches simultanément :
• produire du code lisible et maintenable dans la durée ;
• tester sur plusieurs périphériques et formats différents ;
• ajouter les préfixes nécessaires aux propriétés CSS 3 non finalisées ;
• versionner ses fichiers de travail ;
• gagner en performance en compressant les images ainsi que CSS et JavaScript ;
• ne pas réinventer la roue à chaque projet et automatiser le maximum de tâches.
Autant dire que les outils mis à notre disposition pour pallier les lacunes de CSS ou
accélérer notre méthode de production sont très rapidement devenus une béquille
indispensable à notre quotidien. Je pense notamment aux préprocesseurs, dont Sass est
l’un des plus célèbres porte-parole, ou encore aux planificateurs de tâches (task runners)
tels que Grunt ou Gulp. Et, chaque jour, on peut assister à la naissance (ou la mort) de l’un
de ces outils.
La bonne nouvelle est que tout cela s’accompagne d’une relative mais générale
stabilisation du côté des standards qui régissent le Web. La présence de nouveaux acteurs
dans le monde des navigateurs web, notamment Chrome de Google né en 2008, ainsi que
la fin de l’hégémonie d’Internet Explorer et la mort programmée de Windows XP
favorisent en réalité l’action menée par des organismes mondiaux régulateurs tels que le
W3C ou le WHATWG. Les standards, quoi.
D’une manière générale, nous pouvons observer que certains consensus s’établissent plus
facilement qu’auparavant entre les forces en présence (par exemple, l’idée de laisser
tomber les préfixes constructeurs au profit des flags – nous y reviendrons un peu plus
loin).
Autre point extrêmement positif dans ce constat : le « dinosaure » Internet Explorer
rattrape rapidement son retard et offre enfin la possibilité de concevoir des pages web sans
bricolages farfelus et aléatoires. La preuve en est qu’Internet Explorer a désormais un
developer channel public.
Le design web est dorénavant une affaire de règles HTML et CSS établies, qui
s’appliquent à la grande majorité des navigateurs actuels.
Un tour d’horizon des navigateurs d’aujourd’hui
Dans les années 2000, la suprématie d’Internet Explorer était alors établie depuis
longtemps et rien ne paraissait pouvoir la faire fléchir. À peine quelques années plus tard,
Mozilla Firefox, promu par une communauté libre exceptionnelle, commençait déjà à faire
de l’ombre au mastodonte de Microsoft.
Puis Google Chrome débarque en 2008. Aujourd’hui, il emporte tout sur son passage.
Rapidement propulsé au troisième rang des navigateurs, il occupe actuellement la tête du
classement sans que son hégémonie ne puisse être discutée.
Il ne reste plus guère de place pour les autres navigateurs « alternatifs », ce qui n’empêche
pas certains méconnus de s’illustrer de belle manière, par exemple Maxthon (basé sur
WebKit) qui détient le plus haut score au classement HTML5Test.
RESSOURCE HTML5Test
HTML5Test est un outil en ligne qui évalue la capacité d’un navigateur (le vôtre) à
supporter les fonctionnalités HTML 5 et CSS 3. Il dispose également d’un classement
des meilleurs navigateurs en termes de support.
http://bit.ly/html5-test
Les navigateurs mobiles ont également bénéficié d’un large essor durant ces dernières
années. Certains, à l’instar de Safari et Chrome (encore lui !), se sont taillé la part du lion,
tandis que de nouveaux challengers, comme UC Browser, Silk d’Amazon, Dolphin ou
encore Coast d’Opera, débarquent fraîchement dans l’arène.
Figure 1–1
UC Browser
L’état actuel des standards
Le W3C ( World Wide Web Consortium) est l’organisation mondiale qui fait tourner les
standards en coulisses depuis sa création en 1994. C’est à travers son impulsion que se
sont construits HTML, XHTML, SVG, PNG et bien d’autres règles d’accessibilité
numérique.
Cependant, ce qui fait à la fois la force et la faiblesse du W3C est que ses spécifications
sont soumises à un accord entre l’ensemble de ses membres influents. Cela lui confère sa
neutralité indispensable, mais c’est également une source de lenteurs pesantes.
Les enjeux économiques sont parfois tels que se mettre au diapason entre des acteurs
comme Microsoft, Apple, Adobe ou Google nécessite des processus et des négociations
très rigoureux, chronophages, voire carrément figés.
Critiquant ouvertement l’inertie du W3C ainsi que certains de ses choix stratégiques (celui
de laisser mourir HTML au profit de XHTML, notamment), certains membres d’Apple,
Mozilla et Opera ont décidé en 2004 de collaborer officieusement sur des spécifications
qui leur paraissaient « implémentables » rapidement dans les navigateurs web.
Cette nouvelle organisation, le WHATWG ( Web Hypertext Application Technology
Working Group), est composée, entre autres, de membres actifs du W3C. Elle œuvre
parallèlement à celui-ci sur des standards HTML 5, Web Workers, Microdata, Web Forms
et applications web.
Les résultats du groupe de travail HTML 5 du WHATWG furent si prometteurs qu’ils ont
été adoptés officiellement en 2007 par le W3C comme structure de base pour la future
version d’HTML. Les deux organismes collaborent depuis sur ce standard, apportant
chacun leur pierre à l’édifice.
Figure 1–2 Site du WHATWG (http://whatwg.org)
L’aventure des préfixes constructeurs
Les désormais célèbres préfixes CSS (-moz-, -webkit-, -ms-, -o-, etc.) ont été élaborés par le
W3C à l’époque des spécifications CSS 2.1, il y a donc plus de dix ans.
L’objectif était d’offrir aux constructeurs de navigateurs un moyen de tester des propriétés
ou des valeurs CSS encore non finalisées et de les proposer à leurs utilisateurs, ce qui
semblait être une bénédiction au vu de la lenteur du processus interne de standardisation
du W3C.
Chaque navigateur dispose par conséquent de sa propre variante de propriété et peut
l’implémenter à sa guise à condition qu’elle soit préfixée comme il se doit. Ainsi -moz-
animation pourrait en théorie différer complètement de la propriété animation officiellement
encore en brouillon. Il s’agit de l’interprétation de la propriété animation par le moteur de
rendu de Mozilla (Gecko).
Avec le recul, il est indubitable que l’existence des préfixes CSS a rétrospectivement fait
plus de mal que de bien, et ce, pour de multiples raisons.
Tout d’abord, les développeurs se sont rués sur cet eldorado sans forcément en
comprendre les implications, usant et abusant de propriétés non finalisées sans même le
savoir.
Par ailleurs, une tendance globale vise à ne préfixer que pour les navigateurs les plus en
vogue et les plus représentatifs. Par exemple, une multitude d’articles et tutoriels exposent
des exemples de code où les propriétés ne ciblent que -webkit- (notamment sur mobile où
ce moteur de rendu est roi), laissant en plan tous les autres navigateurs, soit par oubli, soit
par pure fainéantise. À un tel point qu’Opera s’est vu contraint de devoir reconnaître lui
aussi le préfixe -webkit- pour ne pas être considéré comme un « mauvais navigateur » (le
navigateur a par la suite abandonné le moteur de rendu Presto pour joindre Chrome et son
moteur Blink) !
Pour éviter de telles dérives mais aussi une maintenabilité complexe, un ensemble de
constructeurs a décidé d’abandonner petit à petit les préfixes CSS au profit d’une autre
solution plus robuste selon eux : la possibilité d’activer – ou non – certaines
fonctionnalités directement au sein du navigateur (via ce que l’on appelle les « flags »).
Cela signifie que si vous souhaitez tester un module tel que les Régions CSS (que nous
étudierons plus en détail dans ce livre), vous devez l’indiquer à votre navigateur et non
plus dans une surcharge de feuille de styles destinée à d’autres usagers.
L’activation de flags est une démarche pour le moins pertinente. En effet, dans l’idéal, ce
n’est pas au développeur de tester les propriétés en brouillon, mais bien au navigateur.
Cette opération devient alors totalement transparente pour l’utilisateur, mais réduit la
portée des toutes nouvelles fonctionnalités. En effet, il n’est dorénavant plus envisageable
de « forcer » vos visiteurs à les reconnaître (car vous n’allez pas demander à tous vos
clients d’aller activer un flag dans la configuration de leur navigateur).
Nous ne savons pas encore ce que l’avenir nous réserve dans ce vaste et complexe monde
de la standardisation : aujourd’hui, les préfixes n’ont pas complètement disparu et les flags
navigateurs ne sont encore exploités qu’avec parcimonie.
Une tierce solution vient d’ailleurs de voir le jour récemment. Elle est basée sur la règle
conditionnelle @supports (que l’on peut comparer au fameux Modernizr), permettant de
savoir si une propriété ou une valeur est implémentée par un navigateur. Nous reviendrons
bien évidemment sur cette fonctionnalité dans le chapitre 8 dédié aux styles conditionnels.

Figure 1–3 Page dédiée aux flags dans Google Chrome (chrome://flags)
La standardisation des CSS
Je vais vous avouer quelque chose : le titre de ce livre est quelque peu mensonger. En
réalité, CSS 3 n’existe pas, et CSS 4 non plus. Il s’agit là d’un abus de langage qui
caractérise « tout ce qui vient après CSS 2.1 ».
Voyez-vous, CSS 2.1 est la dernière version monolithique du langage. À compter de celle-
ci, les organismes chargés des spécifications CSS se sont accordés sur le fait qu’il devenait
très difficile et très long de produire et de maintenir des versions de cette ampleur.
Aussi a-t-on décrété que CSS serait désormais découpé en modules indépendants et
susceptibles d’avancer à leur propre vitesse. Beaucoup de modules ont démarré au niveau
3 parce qu’ils étaient dans la continuité des fonctionnalités présentes dans CSS 2.1. Mais
les modules émergents tels que Flexbox débutent bien évidemment au niveau 1 alors que
d’autres modules sont déjà au niveau 4 (notamment celui des sélecteurs).
En somme, il est temps d’abandonner la notion de version de CSS. CSS 2.1 est dépassé, et
CSS 3 n’existe pas. Je suggère simplement d’utiliser CSS, en toute simplicité.
À LIRE There is no such thing as CSS 4
L’article « There is no such thing as CSS 4 » par Tab Atkins Jr., éditeur principal des
spécifications CSS, explique très bien les raisons pour lesquelles le CSSWG (CSS
Working Group) a décidé de livrer CSS en modules indépendants.
http://bit.ly/css-4
Mais comment un module change-t-il de niveau ? Pour répondre à cette question, il faut
lorgner du côté de son processus de standardisation, qui est long et parsemé d’embûches.
Et pour cause, celui-ci passe par cinq étapes majeures.

Étape 1. Trouver une idée


La toute première étape, qui finalement n’en est pas vraiment une, est de trouver une idée
qui tient la route. Celle-ci peut émaner de n’importe qui : un fabricant de navigateurs, une
compagnie, vous, moi, etc. Elle est ensuite soumise au CSSWG qui la débat pour arriver à
un des aboutissements suivants.
• L’idée est approuvée.
• L’idée est rejetée.
• L’idée est oubliée. Oui, ça arrive…
Si elle est acceptée, elle entre dans la première véritable phase.

Étape 2. Editor’s Draft (ED)


Une fois l’idée jugée intéressante, le groupe de travail planche dessus jusqu’à avoir un
premier brouillon de spécification : le « brouillon de l’éditeur ». À ce niveau, tout peut
arriver, y compris l’abandon. Dans le meilleur des cas, si le groupe de travail décide que
l’idée tient la route, un First Public Working Draft (FPWD) est publié.

Étape 3. Working Draft (WD)


Tout n’est pas encore joué pour le module, car il peut toujours être abandonné. En effet, si
après les différentes itérations sur le brouillon, les acteurs impliqués (internes comme
externes) le jugent techniquement trop compliqué ou incomplet, le travail s’arrête ici.
Si, à l’inverse, tout se passe bien, le module finit par atteindre un stade de Last Call
Working Draft (LCWD), autrement dit un appel aux derniers changements avant que la
spécification ne passe en Candidate Recommendation (CR).

Étape 4. Candidate Recommendation (CR)


À partir du moment où une spécification passe en Candidate Recommendation, vous
pouvez commencer à vous pencher dessus sérieusement, car les navigateurs sont
susceptibles d’entamer les premières implémentations. Et pour cause, ceux-ci – entre
autres – sont chargés de tester la fonctionnalité de fond en comble. Une fois que le groupe
de travail est en mesure de justifier de la robustesse de la spécification grâce à deux
implémentations fonctionnelles solides (de la part de deux moteurs de rendu différents), le
module est en passe de franchir la dernière étape.

Étape 5. Recommendation (REC)


Curieusement, cette étape n’est pas aussi rose que l’on pourrait bien le croire. En théorie,
un module en REC signifie qu’il est validé, approuvé et très certainement implémenté.
Cependant, en pratique, une spécification REC est « morte », car elle n’est plus maintenue
(dans la mesure où il est extrêmement difficile de mettre à jour un module en REC) et
aussi parce qu’une nouvelle version du module est très probablement en préparation.
POUR EN SAVOIR PLUS
Pour plus d’informations sur le processus de standardisation des spécifications, je ne
peux que vous suggérer l’article de Sabastian Ekström, « The CSS Standards
Process », publié sur le blog CSS-Tricks, relu et approuvé par Tab Atkins Jr., principal
auteur des spécifications CSS.
http://bit.ly/css-standards-process
2
Les sélecteurs : l’accès au DOM

Nous allons commencer doucement en revoyant quelques bases fondamentales de CSS :


les sélecteurs. Mais êtes-vous certain d’être au fait des nouvelles façons d’accéder au
DOM ?
Nous nous sommes longtemps contentés de sélecteurs simples en CSS, ciblant des
collections d’éléments en fonction de leurs parents. Cela fonctionne bien et s’avère
amplement suffisant dans la majorité des cas. Mais, ces dernières années, nous avons
assisté à une remise au premier plan des performances. De fait, nous remplaçons de plus
en plus les images et le JavaScript superflus par des CSS et, fort heureusement, les
spécifications vont dans ce sens.
Un des pans de CSS 3 les plus supportés dans le panel des navigateurs existants est
sûrement le module dédié aux sélecteurs, qui permet de cibler les éléments non seulement
en fonction de leur parent, mais également selon un certain nombre de critères comme leur
position au sein de leur parent, leurs voisins ou même la langue du document.
Les opérateurs
Les opérateurs sont des symboles placés entre deux composants d’un sélecteur apportant
davantage d’informations sur la nature de la relation entre les deux parties. À l’heure
actuelle, il en existe relativement peu.

Opérateur d’adjacence directe (CSS 2)


L’opérateur d’adjacence directe, noté +, fait en réalité partie des spécifications CSS 2.1, et
non CSS 3. Cependant, il va de pair avec l’opérateur d’adjacence générale (dont nous
parlerons un peu plus loin), lequel a été ajouté lors du passage au niveau 3. Aussi est-il
intéressant de rappeler son fonctionnement.
Cet opérateur s’assure que l’élément ciblé est directement adjacent à l’élément précédant
l’opérateur, c’est-à-dire qu’il n’y a aucun nœud du DOM les séparant. Par exemple :
label + input {
display: block;

Ici, seuls les éléments input placés directement à la suite d’un élément label se verront
ciblés par le sélecteur. Si un autre élément vient s’installer entre les deux, par exemple un
span, alors le sélecteur ne ciblera pas l’input. De même, si c’est un label à la suite d’un input,
le sélecteur ne fonctionnera pas.
Il est possible d’utiliser plusieurs fois le sélecteur d’adjacence directe dans un même
sélecteur afin d’augmenter la précision de celui-ci. Par exemple, admettons qu’on veuille
cibler les div enfants d’un conteneur à partir de la troisième, on peut envisager un sélecteur
tel que :
/* Les div successives à partir de la troisième, dans le conteneur */
.container div + div + div {

background: hotpink;
}

En réalité, on cible les div enfants de .conteneur dont les deux éléments voisins précédents
sont également des div. Naturellement, les div 1 et 2 ne peuvent donc pas être ciblées par
ce sélecteur, et il ne fonctionnera de toute façon que dans le cas où les div se suivent sans
élément entre elles.

Compatibilité des navigateurs pour l’opérateur d’adjacence


directe
Tableau 2–1 Navigateurs desktop

Tableau 2–2 Navigateurs mobiles


SAFARI 5.1 Problème avec nav
La version 5.1 du navigateur d’Apple ne supporte pas l’opérateur d’adjacence directe
si l’élément adjacent est une nav.
INTERNET EXPLORER 7 Attention aux commentaires HTML
Internet Explorer 7 ne parviendra pas à appliquer correctement les styles utilisant
l’opérateur d’adjacence directe si un commentaire HTML sépare les deux éléments.

Opérateur d’adjacence générale


L’opérateur d’adjacence générale, indiqué au moyen du caractère ~, est très similaire à
l’opérateur d’adjacence directe. La différence est qu’il va cibler tous les éléments frères
suivant l’élément, et non pas uniquement celui directement adjacent.
ATTENTION Voisins suivants uniquement
L’opérateur ~ cible uniquement les éléments frères survenant après celui indiqué avant
l’opérateur, pas ceux venant avant dans le DOM.
Par exemple, si on souhaite que le bouton de soumission d’un formulaire ne soit pas
cliquable si un champ est invalide, on peut s’y prendre de la façon suivante :
/**
* Si un champ est invalide,

* le bouton de soumission est non cliquable,


* à condition que le champ invalide et le bouton soient frères
* et que le bouton vienne après le champ.

*/

input:invalid ~ [type="submit"] {
opacity: .3;

pointer-events: none;

Figure 2–1
L’e-mail est invalide : le bouton de soumission est désactivé.

RESSOURCE Un module de notation par étoiles


Lea Verou a développé l’interface d’un widget de notation basé sur des étoiles rien
qu’en CSS grâce à un certain nombre d’astuces, notamment l’opérateur d’adjacence
générale. Attention cependant, c’est assez musclé !
http://bit.ly/css-star-rating
Figure 2–2
Le module de notation CSS de Lea Verou

Compatibilité des navigateurs pour l’opérateur d’adjacence


générale
Tableau 2–3 Navigateurs desktop

Tableau 2–4 Navigateurs mobiles


Les sélecteurs d’attribut
La possibilité de sélectionner les éléments HTML en fonction de leurs attributs n’est pas
nouvelle puisqu’elle a été mise en place dans les spécifications de niveau 2. Jugée
insuffisamment flexible, elle a été amplement améliorée dans les spécifications sur les
sélecteurs de niveau 3, notamment par l’ajout de nouveaux modulateurs de sélection.
Commençons par une petite piqûre de rappel sur la sélection par attribut.

Sélecteur d’attribut simple (CSS 2)


Il existe plusieurs façons d’utiliser les sélecteurs d’attribut. Par exemple, il est possible de
cibler un élément parce qu’il est affublé d’un attribut particulier, ou parce qu’il en possède
un ayant une valeur spécifique. On croit souvent que cela présente peu d’intérêt mais, en
réalité, on l’emploie très régulièrement, et ce, depuis très longtemps, lorsque l’on écrit le
code suivant :
input[type="text"] {

padding: 2px;
}

Je suis certain que vous avez au moins une fois déjà utilisé une règle similaire. Celle-ci
définit un ciblage des éléments input qui sont affublés d’un attribut type ayant pour valeur
text. On aurait pu tout aussi bien retirer input pour ne garder que la seconde partie
[type="text"]. Notez toutefois qu’il n’est nécessaire de mettre la valeur de l’attribut entre
guillemets que si celle-ci contient des caractères spéciaux tels que ., /… Cependant, on le
fait aussi dans un souci qualitatif.
Comme je l’expliquais, on peut aussi cibler les éléments simplement parce qu’ils ont un
certain attribut, peu importe la valeur assignée. Par exemple :
[disabled] {

cursor: not-allowed;
}

Cette règle a pour effet de redéfinir l’apparence du curseur sur tous les éléments ayant
l’attribut disabled. En effet, vu que l’attribut peut être déterminé en écrivant soit disabled,
soit disabled="disabled", il est préférable de simplement tester la présence de celui-ci plutôt
que de vérifier la valeur.
PERFORMANCE Quid de la performance des sélecteurs d’attribut
Il est de coutume de dire que les sélecteurs d’attribut sont coûteux en termes de
performance. On peut le comprendre dans la mesure où le DOM parser doit vérifier
chaque attribut pour le comparer à celui demandé par le sélecteur.
Néanmoins, les navigateurs progressent vite et bien. Si bien que ces remarques
autrefois pertinentes tendent aujourd’hui à être peu à peu obsolètes. Des tests de
performance ont été menés, y compris sur de vieux navigateurs tels qu’Internet
Explorer 8, et la différence se ressent assez peu (de l’ordre de la milliseconde). Donc,
à moins que vous n’utilisiez que des sélecteurs d’attribut, ne vous restreignez pas.
De plus, lorsqu’il s’agit d’optimisation CSS, il est préférable de se pencher sur les
sélecteurs morts (c’est-à-dire non utilisés) et les propriétés gourmandes plutôt que sur
le type des sélecteurs utilisés. C’est là de la suroptimisation, éventuellement
envisageable lorsque tout le reste a été traité.

Compatibilité des navigateurs pour les sélecteurs d’attribut


Tableau 2–5 Navigateurs desktop

Tableau 2–6 Navigateurs mobiles

Sélecteur d’attribut modulé


On poursuit le rappel avec les sélecteurs d’attribut modulés. Notez que les deux premiers
qui seront vus faisaient déjà partie des spécifications 2.1, mais ils introduisent également
de nouvelles possibilités en CSS 3.

Attribut dont la valeur est comprise dans une liste séparée


par des espaces avec [attr~=“value”] (CSS 2)
Il est possible de sélectionner des éléments selon qu’ils ont un attribut en particulier et que
la valeur de celui-ci est contenue dans une liste séparée par des espaces.
Par exemple, les éléments de lien (<a>), de zone (<area>) et d’import (<link>) acceptent
l’attribut rel qui vise à donner du sens au type de relation que peut entretenir l’élément
avec la ressource à laquelle il est lié. Un élément peut très bien avoir plusieurs relations et,
par conséquent, la valeur de son attribut rel doit être une liste séparée par des espaces.
<a href="http://hugogiraudel.com" rel="author external nofollow">En savoir plus sur Hugo
Giraudel</a>

Dans cet exemple, on apprend de l’attribut rel que le lien :


• concerne l’auteur ;
• est externe au document actuel ;
• n’est pas cautionné par le document actuel.
Maintenant, si l’on souhaite sélectionner tous les liens qui concernent les auteurs par
exemple, on ne peut se contenter de a[rel="author"] dans la mesure où ce sélecteur ne cible
que les éléments dont l’attribut rel est strictement égal à author. On passe donc par le
sélecteur suivant :
a[rel~="author"] {
color: hotpink;

Celui-ci cible tous les éléments de type a ayant un attribut rel contenant le mot author. C’est
gagné !
INTERNET EXPLORER 7 ET 8 Pas de support
Les versions d’Internet Explorer 7 et 8 ne supportent pas ce modulateur d’attribut.

Attribut dont la valeur est exacte ou démarre par une chaîne


précédée du caractère - avec [attr|=“value”] (CSS 2)
Le modulateur |= a été introduit en CSS 2 pour simplifier les systèmes de langue. En effet,
il permet de cibler un élément dont l’attribut souhaité est exactement égal à la valeur
demandée ou démarrant par celle-ci et directement suivie du caractère - (U+002D).
Les liens pointant vers des ressources dont la langue n’est pas celle du document actuel
devraient être affublés d’un attribut hreflang qui détermine la langue du document. Par
exemple, si l’on souhaite sélectionner les liens dont la source est en anglais, on peut s’y
prendre ainsi :
a[hreflang|="en"] {
style: british;

Ce sélecteur ne va pas seulement cibler les liens avec un attribut hreflang égal à en ; les
valeurs en-US, en-EN ou encore en-licorne seront également prises en compte.
INTERNET EXPLORER 7 ET 8 Pas de support
Les versions d’Internet Explorer 7 et 8 ne supportent pas ce modulateur d’attribut.

Attribut dont la valeur débute par une chaîne avec


[attr^=“value”]
Voilà qui clôture notre piqûre de rappel sur l’état des sélecteurs d’attribut dans les
spécifications CSS 2.1. On va donc pouvoir entrer dans le vif du sujet en parlant des
nouveaux modulateurs ayant été ajoutés en CSS 3, à commencer par [attr^="value"].
On se rapproche du terrain des expressions régulières avec un modulateur signifiant
simplement « dont l’attribut commence par la valeur ». Si la valeur est une chaîne vide, le
sélecteur est considéré comme nul.
Comme cas d’utilisation, on peut imaginer vérifier si un lien pointe vers un site externe en
testant le début de la chaîne http sur l’attribut href. On se limite à http et non pas http:// afin
de traiter également des sites en https.
a[href^="http"] {
/* Liens externes */

Prenons un autre exemple : il est fort probable que vous ayez déjà mis en place une feuille
de styles dédiée à l’impression dans un de vos projets. Afin de donner du sens aux liens
une fois imprimés, on utilise souvent la règle suivante :
a[href]::after {

content: " (" attr(href) ")";


}

Celle-ci a pour effet d’afficher la source des liens entre parenthèses juste après eux. C’est
très pratique, néanmoins on se retrouve parfois avec des aberrations de type (#) ou pire
encore (javascript:return false). Certes, on ne devrait pas, mais pourtant ça arrive ! Du
coup, on va annuler la précédente règle pour ces cas de figure uniquement, grâce à notre
nouveau modulateur :
a[href^="#"]::after,
a[href^="javascript:"]::after {

content: "";
}

Attribut dont la valeur termine par une chaîne avec


[attr$=“value”]
De la même façon qu’on peut cibler les éléments dont l’attribut démarre par une chaîne
bien précise, c’est possible avec les éléments dont l’attribut s’achève par une suite de
caractères spécifiques. Là encore, une chaîne de caractères vide rend le sélecteur nul.
Un cas d’usage intéressant serait d’appliquer un style particulier aux liens pointant vers
des fichiers d’une certaine extension, par exemple PDF. Considérez l’exemple suivant qui
spécifie explicitement que le lien pointe vers un PDF uniquement grâce à CSS :
a[href$=".pdf"]::after {

content: " (PDF)";

On se sert d’un sélecteur d’attribut pour détecter les liens vers des fichiers PDF, et du
pseudo-élément after pour afficher le terme (PDF) après le lien. Simple et efficace !

Attribut dont la valeur contient une chaîne avec


[attr*=“value”]
Et enfin, le dernier modulateur mais non des moindres ! On peut tester le début et la fin de
la valeur afin de savoir si elle existe au sein de la chaîne complète.
Ce sélecteur est souvent utilisé avec la méthodologie BEM (Block Element Modifier) qui
attribue des versions modifiées d’une classe de base, par exemple .button-disabled pour
expliciter qu’il s’agit à la fois d’un bouton mais également qu’il est désactivé. On peut
donc sélectionner tous les éléments .button y compris ceux qui ont été modifiés, grâce au
code suivant :
[class*="button"] {
/* Des styles de boutons */

}
Les pseudo-classes de position
Les pseudo-classes ont toujours existé en CSS. D’abord sévèrement réduites avec
seulement :hover, :active et :focus, elles font désormais partie du quotidien de l’intégrateur
tant elles sont nombreuses. Et les spécifications de niveau 3 des sélecteurs n’y sont pas
pour rien puisqu’elles apportent des dizaines de nouvelles pseudo-classes pour rendre les
sélecteurs plus performants que jamais.
Une des brillantes nouveautés de ce module est de pouvoir sélectionner un élément en
fonction de sa position (comprendre, de son index) au sein de son parent. Est-il le premier
élément du parent ? le dernier ? le énième de son type ? le seul enfant ? Toutes ces
questions, dont les réponses ont longtemps été détenues par JavaScript, sont désormais
simples à traiter via les CSS uniquement.
Il est donc possible de cibler un élément selon s’il est le premier enfant du parent, ou le
dernier, ou encore s’il se trouve à une position bien particulière. Mais commençons par le
commencement, voulez-vous ?

Premier et dernier enfant avec :first-child & :last-child


La pseudo-classe :first-child remonte elle aussi à CSS 2. Elle permet de cibler le premier
enfant d’un parent ou, plutôt, un élément s’il est le premier enfant de son parent. Il en va
de même avec :last-child (CSS 3 pour le coup) mais pour le dernier élément. Par exemple,
pour cibler le premier élément d’une liste, voici comment procéder :
/* Premier élément d’une liste */
ul > li:first-child {
color: red;

La façon dont fonctionne cette pseudo-classe peut être un peu déroutante de prime abord,
car il ne faut pas l’appliquer au parent mais bel et bien à un enfant. En effet, l’exemple
suivant ne produit pas le même effet :
/* Une liste étant le premier enfant de son parent, quel qu’il soit */
ul:first-child {

color: red;

Et si vous désirez cibler le premier enfant d’un élément, quel que soit son type, vous
pouvez le faire tout aussi facilement en insérant un espace avant la pseudo-classe, qui fera
office de sélecteur universel (*).
/* Le premier enfant de .container, quel qu’il soit */
.container :first-child {

margin-top: 0;

En l’occurrence, :first-child sera interprété comme *:first-child, qui signifie « le premier


enfant, quel que soit le type d’élément utilisé ».
Compatibilité des navigateurs pour :first-child
Tableau 2–7 Navigateurs desktop

Tableau 2–8 Navigateurs mobiles

INTERNET EXPLORER 7 :first-child et commentaires HTML


Internet Explorer 7 ne parviendra pas à identifier le :first-child si le premier enfant du
conteneur est précédé d’un commentaire HTML.

Compatibilité des navigateurs pour :last-child


Tableau 2–9 Navigateurs desktop

Tableau 2–10 Navigateurs mobiles

INTERNET EXPLORER À propos de :last-child


Bien souvent, les développeurs ne comprennent pas pourquoi Internet Explorer a été
capable de supporter :first-child depuis sa version 7 mais a dû attendre la version 9
pour supporter correctement :last-child. Pour répondre à cette question, considérons
les règles CSS suivantes :
div p:first-child { color: red; }

div p:last-child { color: blue; }

Cela vient du fait que le navigateur va dérouler le DOM de manière linéaire. Lorsqu’il
rencontre une div, il l’ouvre et lit le premier enfant. S’il s’agit d’un paragraphe, il lui
applique alors la première règle, c’est-à-dire la couleur rouge. Seulement, à cet instant
précis, il est aussi le dernier enfant paragraphe de la div ; il lui applique donc
également la couleur bleue.
Maintenant, si le navigateur rencontre un nouveau paragraphe après le premier, alors il
doit appliquer la couleur bleue à celui-ci, car il est le dernier paragraphe du parent. Il
faut aussi qu’il recalcule la couleur du premier paragraphe qui n’est plus le dernier et
donc qui ne doit plus être bleu mais rouge. Et ainsi de suite jusqu’à fermer l’élément
pour de bon.
Sans parler de difficulté d’implémentation, on comprend néanmoins pourquoi Internet
Explorer a été capable de rapidement implémenter :first-child mais pas :last-child, qui
demande beaucoup plus de ressources.

Énièmes enfants avec :nth-child et :nth-last-child


Cibler le premier ou le dernier élément est bien souvent suffisant, mais il peut arriver que
vous souhaitiez cibler le deuxième, l’avant-dernier, ou encore tous les éléments à une
position étant un multiple de 3 (le 3e, le 6e, le 9e…) ! Tout cela devient possible grâce aux
pseudo-classes :nth-child et :nth-last-child, sensiblement identiques si ce n’est que la
première itère depuis le premier élément jusqu’au dernier, alors que la seconde fait
l’inverse (du dernier au premier).
Commençons par les choses les plus simples, soit cibler un élément à l’index n. Ces
pseudo-classes sont en réalité des fonctions qui acceptent donc un argument saisi entre
parenthèses à la fin de la pseudo-classe. Par exemple, pour cibler le second enfant d’un
conteneur s’il s’agit d’une div, on écrit donc :
/* Le second enfant de .container s’il s’agit d’une div */
.container div:nth-child(2) {

background: deepskyblue;
}

Vous pouvez bien évidemment passer le nombre que vous désirez à ces fonctions. En fait,
elles acceptent même davantage d’arguments que simplement des nombres, comme nous
allons le voir immédiatement. Admettons que vous souhaitiez cibler tous les multiples de
3.
/**
* Tous les enfants d’un ul

* dont la position est un multiple de 3,

* à condition qu’ils soient des li


* (ce qui devrait être le cas).

*/

ul > li:nth-child(3n) {
background: deepskyblue;

Le paramètre 3n passé à la fonction indique que seuls les li dont l’index (démarrant à 0,
comme dans beaucoup de langages) est un multiple de trois seront ciblés par le sélecteur
(3, 6, 9, 12…).
Figure 2-3
li:nth-child(3n)

Si, en revanche, vous voulez cibler un élément sur trois tout en partant du premier, voici
comment procéder :
/* Éléments 1, 4, 7, 10, etc. */
li:nth-child(3n + 1) {
background: deepskyblue;

Ce sélecteur signifie concrètement « cibler un li sur trois en commençant par le premier ».

Figure 2-4
li:nth-child(3n + 1)

On peut tout à fait utiliser une soustraction pour déclarer un décalage vers l’arrière. Par
exemple, cibler un li sur trois avec un décalage en arrière de un :
/* Éléments -1 (inexistant), 2, 5, 8, 11, etc. */

li:nth-child(3n - 1) {

background: deepskyblue;
}
Figure 2-5
li:nth-child(3n - 1)

Et enfin, il est possible de passer un mot-clé à la fonction afin de cibler tous les éléments
pairs :
/* Éléments pairs */

li:nth-child(even) {
background: deepskyblue;
}

Figure 2-6
li:nth-child(even)

… ou tous les éléments impairs :


/* Éléments impairs */

li:nth-child(odd) {

background: deepskyblue;
}
Figure 2-7
li:nth-child(odd)

Ces deux mots-clés sont extrêmement pratiques lors de la réalisation d’une table dite
« zebra », c’est-à-dire dont une ligne sur deux se démarque de la précédente dans le but de
faciliter la lecture. Plus besoin de recourir à JavaScript !
table tr {
background: white;

}
table tr:nth-child(even) {
background: #EFEFEF;

Figure 2-8
table tr:nth-child(even)

À part cela, tout autre type de valeur passé à la fonction rend le sélecteur nul car non
compris par le parser CSS du navigateur. En résumé, les fonctions :nth-child et :nth-last-
child acceptent :

• un index au format numérique ;


• une formule au format Xn + Y ou Xn – Y ;
• even ou odd.
Inutile de préciser que les possibilités de sélection sont pratiquement infinies grâce à ces
sélecteurs, surtout si l’on se met à les combiner ! Il ne reste plus que deux choses à
considérer lors de leur utilisation. Tout d’abord, est-ce qu’une classe ne serait pas plus
pertinente ? Ensuite, comment faire fonctionner les choses sur Internet Explorer 8 ?
RESSOURCE Polyfill JavaScript pour Internet Explorer
Selectivizr est une solution de repli en JavaScript pour émuler le support des
sélecteurs avancés sur Internet Explorer 7 et 8. Bien qu’elle soit très efficace et
pertinente, je ne la recommande pas dans la mesure où elle surcharge davantage ces
navigateurs qui sont déjà plutôt lents.
http://bit.ly/css-selectivizr

Styler en fonction du nombre d’éléments dans le parent


Une question que j’ai souvent vu posée et que je me suis moi-même posée par le passé est
de savoir comment appliquer des styles à une collection d’éléments en fonction de leur
nombre. Par exemple, considérons une liste de navigation au nombre d’entrées
dynamique. Admettons qu’on souhaite diviser la largeur du menu de manière égalitaire
entre tous les éléments selon leur nombre.
Malheureusement, il n’existe à ce jour aucun moyen simple pour les CSS de connaître le
nombre d’enfants pour un certain nœud du DOM. Du coup, on passe alors souvent par
JavaScript. Néanmoins, il existe un moyen en CSS d’y arriver bien qu’il soit relativement
alambiqué.
L’idée a été mise au point par Lea Verou et consiste à combiner deux sélecteurs avancés
afin de cibler le premier élément et tous les suivants quand il y a x éléments dans le parent.
Pour couvrir tous les cas de figure, il ne reste plus qu’à dupliquer cette règle en faisant
varier x (par exemple de 1 à 10, qui semble être approprié pour un menu).
Concrètement, le sélecteur li:first-child:nth-last-child(x), li:first-child:nth-last-child(x) ~
li cible le premier élément quand il est le énième élément de son parent en partant de la
fin, puis tous les éléments li suivant le premier. En d’autres termes, il cible le premier
enfant ainsi que tous les suivants (donc tous les enfants) quand il y a x enfants dans le
parent.
/**

* Littéralement : les éléments de liste qui sont à la fois

* le premier enfant de leur parent mais également le dernier.


* Autrement dit : tous les éléments d’une liste

* qui ne contient qu’un seul élément.

* Peut être remplacé par :only-child qu’on verra plus loin.


*/

li:first-child:nth-last-child(1) {

width: 100%;
}

/**

* Littéralement : les éléments de liste qui sont à la fois


* le premier enfant de leur parent mais aussi l’avant-dernier,

* ainsi que tous les éléments de liste qui les suivent.

* Autrement dit : tous les éléments d’une liste


* qui ne contient que deux éléments.

*/
li:first-child:nth-last-child(2),

li:first-child:nth-last-child(2) ~ li {
width: 50%;

}
/* Trois éléments */
li:first-child:nth-last-child(3),

li:first-child:nth-last-child(3) ~ li {

width: 33.3333%;
}

/* Quatre éléments */

li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {

width: 25%;
}

/* Et ainsi de suite… */

Figure 2–9
Une façon astucieuse de dimensionner les éléments selon leur nombre au sein du parent

On peut donc appliquer des styles spécifiques aux éléments en fonction du nombre
d’enfants dans le parent sans recourir à JavaScript. Néanmoins, le code est relativement
dense et surtout fastidieux. On peut le simplifier avec un préprocesseur CSS, tel que Sass :
$max: 10;

@for $i from 1 through $max {


li:first-child:nth-last-child(#{$i}) {

width: (100% / $i);


}

Ce simple code Sass génère le code CSS que nous avons écrit précédemment sans avoir à
calculer manuellement la largeur (width), ni à recopier les mêmes lignes à plusieurs
reprises.
Ceci étant dit, parce qu’Internet Explorer 8 ne supporte pas le sélecteur :nth-last-child et
que le code CSS pour un tel effet est assez dense, il peut être préférable d’utiliser d’autres
méthodes de positionnement, tel que le modèle tabulaire.
PLUS D’INFOS
Pour plus d’informations sur cette technique de ciblage, veuillez vous référer à
l’article original en anglais de Lea Verou :
http://bit.ly/lea-verou-nth
Compatibilité des navigateurs pour :nth-child et :nth-last-
child
Tableau 2–11 Navigateurs desktop

Tableau 2–12 Navigateurs mobiles

Enfant unique avec :only-child


Enfin, la dernière, mais non des moindres, dans la série des pseudo-classes de ce type
permet de cibler un élément selon s’il est l’unique enfant de son parent ou non. Il s’agit de
:only-child.

/* Un li s’il est l’unique enfant de son parent */


li:only-child {

width: 100%;
}

Cette pseudo-classe est intéressante dans certains cas où on désire appliquer une règle de
gestion particulière à un élément s’il n’a aucun voisin. Par exemple, un tel sélecteur peut
être pratique si vous manipulez un nombre d’éléments dynamiques et désirez mettre en
place une règle de gestion spéciale dans le cas où il n’y a qu’un seul élément.
CLARIFICATION
Cette pseudo-classe est strictement équivalente à la combinaison :first-child:last-child.

Compatibilité des navigateurs pour :only-child


Tableau 2–13 Navigateurs desktop

Tableau 2–14 Navigateurs mobiles


Propriétés *-of-type
Tous les sélecteurs que nous venons d’étudier ont un équivalent visant à cibler non pas le
énième enfant mais le énième enfant de ce type. Par exemple, le premier titre de niveau 2
(h2), le dernier élément d’une liste ou encore toutes les div paires.
/* Premier h2 de son élément parent */

h2:first-of-type {
margin-top: 0;

/* Dernier li */
ul > li:last-of-type {

border: none;

/* Toutes les div paires de .container */


.container > div:nth-of-type(even) {
margin-bottom: 1em;

}
/* Unique li */
ol > li:only-of-type {

width: 100%;
}

CLARIFICATION :nth-of-type et classes


Lorsque la pseudo-classe :nth-of-type(x) est utilisée sur un sélecteur de classe (par
exemple, .element:nth-of-type(2)), cela revient à demander un élément ayant la classe
.element qui se trouve également être le deuxième élément de son type (p, li, a, div…)
au sein de son parent.
Sont donc supportées les pseudo-classes suivantes :
• nth-of-type ;
• last-of-type ;
• first-of-type ;
• nth-last-of-type ;
• only-of-type.
CONSEIL *-of-type plutôt que *-child
D’expérience, j’ai réalisé qu’il était plus souvent préférable d’utiliser les pseudo-
classes *-of-type plutôt que les *-child afin d’éviter des effets de bord problématiques.
En effet, on souhaite généralement donner un comportement particulier au énième
élément d’un type précis, et non pas au énième enfant d’un parent.

Compatibilité des navigateurs pour les pseudo-classes :*-of-


type
Tableau 2–15 Navigateurs desktop

Tableau 2–16 Navigateurs mobiles


Les pseudo-classes de contexte

Ciblage par ancre avec :target


La pseudo-classe :target rentre dans la même catégorie que :active, :focus et :hover. Un
élément est considéré éligible aux règles déclarées sous :target lorsque l’URL présente une
ancre, aussi appelée fragment identifier (par exemple, http://url.com/#hash), qui correspond
à l’attribut id dudit élément. Considérons un élément de titre avec un attribut id servant
d’ancre.
<h2 id="kiwis">Kiwis</h2>

Si l’URL présente le hash #kiwis, toutes les règles CSS déclarées pour la pseudo-classe
:target sur ce titre seront alors interprétées.

h2:target {
color: hotpink;

La pseudo-classe :target est particulièrement utilisée dans les documentations, où elle met
en valeur une section lorsque celle-ci est « ciblée » par l’URL via un hash (déroulant ainsi
la page jusqu’à atteindre la partie désignée). De manière générale, elle est efficace
lorsqu’il s’agit de styler une partie de la page quand celle-ci est dans un état particulier.

Compatibilité des navigateurs pour :target


Tableau 2–17 Navigateurs desktop

Tableau 2–18 Navigateurs mobiles

INTERNET EXPLORER Boutons Précédent et Suivant du navigateur


Sur Internet Explorer, lors de l’utilisation des boutons Précédent et Suivant du
navigateur, les styles ne sont pas modifiés par rapport à l’état précédent.

Éléments vides avec :empty et :blank


La pseudo-classe :empty permet d’appliquer des règles à un élément si celui-ci ne contient
ni enfant ni contenu textuel. En effet, du moment que l’élément comporte ne serait-ce
qu’un caractère (même un retour chariot), il n’est plus considéré comme vide et n’est donc
pas ciblé par une règle utilisant :empty.
CLARIFICATION Commentaires HTML comme seul contenu
Si un élément n’a pour contenu qu’un commentaire HTML, il est alors considéré vide
et par conséquent éligible pour la pseudo-classe :empty.
Quoi qu’il en soit, cette pseudo-classe est très pratique lorsque vous voulez cacher un
élément vide afin d’éviter que ses éventuelles marges interagissent avec son
environnement. Par exemple :
p:empty {
display: none;

ATTENTION Éléments dont la balise fermante est optionnelle


Les éléments dont la balise fermante est optionnelle en HTML 5 (par exemple, <li> ou
<p>), et dont ladite balise aurait été omise, ne sont pas considérés comme vides, même
s’ils n’ont pas de contenu textuel. Par exemple :
<p>

… ne sera pas ciblé par le sélecteur p:empty.


Cette règle évite les espaces parfois étranges lorsqu’un paragraphe est généré sans
contenu. Néanmoins, rappelez-vous que les éléments vides ne sont jamais véritablement
une bonne idée, aussi est-il recommandé de les éviter autant que possible.
CURIOSITÉ Éléments autofermants
Bien que ce soit logique, il est bon de noter que les éléments autofermants, tels que <br
/>, <hr /> ou <img />, sont considérés comme vides et sont donc susceptibles d’être
stylés par la pseudo-classe :empty.
La pseudo-classe :blank fonctionne exactement comme :empty, si ce n’est qu’elle cible
également les éléments qui contiendraient uniquement des retours chariot ou des
caractères s’assimilant à des espaces (U+0020, U+0009).

Compatibilité des navigateurs pour :empty


Tableau 2–19 Navigateurs desktop

Tableau 2–20 Navigateurs mobiles

Gestion de la langue avec :lang (CSS 2)


La pseudo-classe :lang est un sélecteur très méconnu des développeurs, probablement
parce qu’il n’intervient que dans de rares circonstances, notamment lors de la réalisation
de sites multilingues. En effet, cette pseudo-classe permet d’appliquer des styles à un
élément en fonction de la langue employée.
Celle-ci est notamment définie par l’attribut HTML lang mais aussi par la balise meta dédiée
au langage, ainsi que le header HTTP Accept-Language. De fait, la pseudo-classe :lang(x) n’est
effective que dans le cas où l’élément est dans le langage x. Ceci étant dit, il n’est pas
obligatoire que x soit une langue valide. Vous pouvez tout à fait l’utiliser en spécifiant la
chaîne de caractères de votre choix.
Parce qu’on spécifie souvent la langue du document sur l’élément racine, on peut utiliser
cette pseudo-classe de deux manières :
• en elle-même, sans la qualifier : :lang(fr) ;
• sur un élément directement : blockquote:lang(en).
Un cas d’usage intéressant pour le sélecteur de langue consiste à redéclarer les guillemets
en fonction de la langue du document grâce à la propriété quotes. En effet, toutes les
langues n’utilisent pas les mêmes guillemets, ni de la même façon.
q::before { content: open-quote; }

q::after { content: close-quote; }


:lang(en) q { quotes: '“' '”'; }
:lang(fr) q { quotes: '«' '»'; }

:lang(de) q { quotes: '»' '«'; }

Ces quelques lignes de CSS sont suffisantes pour appliquer les bons guillemets en
fonction du langage du document.

Compatibilité des navigateurs pour :lang


Tableau 2–21 Navigateurs desktop

Tableau 2–22 Navigateurs mobiles

Élément racine avec :root


La pseudo-classe :root est définie par les spécifications CSS comme une pseudo-classe
« structurante » dans le sens où elle réfère au plus haut élément dans l’arborescence du
DOM. Dans le cas d’un document HTML, il s’agit de l’élément html mais, avec un
document SVG ou XML, elle peut référer à un tout autre élément.
Dans un contexte HTML traditionnel, :root n’a que peu d’intérêt dans la mesure où elle
cible quoi qu’il en soit l’élément html. Cependant, CSS 3 apporte les variables natives à
CSS qui doivent être déclarées dans la pseudo-classe :root.
:root {
--main-color: hotpink;

--margin: 1em;

PAS DE PANIQUE Variables CSS


Si vous ne comprenez pas l’exemple de code précédent, c’est tout à fait normal. Les
variables sont en effet au programme du chapitre 7 de cet ouvrage !
On notera également une différence importante avec le sélecteur html : :root étant une
pseudo-classe, elle a le même poids qu’une classe en termes de spécificités alors que html a
celui d’un élément standard.

Compatibilité des navigateurs pour :root


Tableau 2–23 Navigateurs desktop

Tableau 2–24 Navigateurs mobiles

Négation avec :not


La pseudo-classe :not permet d’impliquer une négation dans un sélecteur, c’est-à-dire de
sélectionner une collection d’éléments à condition qu’ils ne conviennent pas au sélecteur
spécifié entre les parenthèses de la pseudo-classe :not. Ainsi, .a:not(.b) sélectionne tous les
éléments ayant la classe .a mais n’ayant pas la classe .b.
Elle est aussi intéressante lorsque vous désirez restreindre le champ d’action de vos
sélecteurs. Par exemple, si vous souhaitez sélectionner tous les liens qui n’ont pas un
attribut target qui serait égal à _blank, vous écrirez :
a:not([target='_blank']) {

/* Liens s’ouvrant dans la même fenêtre */

Le support pour :not est excellent si on omet Internet Explorer 8. Malheureusement, je sais
bien que ce n’est pas toujours envisageable. Heureusement, il est souvent facile de faire la
même chose avec un peu plus de CSS (et de spécificité). Dans notre cas, nous pourrions,
par exemple, cibler tout d’abord les liens ayant un attribut target, puis les autres.
a[target='_blank'] {

/* Comportement pour les liens avec un attribut target */


}

a {
/* Comportement pour les autres liens */
}

Compatibilité des navigateurs pour :not


Tableau 2–25 Navigateurs desktop

Tableau 2–26 Navigateurs mobiles

Simplification des sélecteurs avec :matches


fait partie de cette future génération de sélecteurs encore peu, voire pas,
:matches
implémentés. Pour l’essentiel, cette pseudo-classe simplifie les sélecteurs parfois longs et
volumineux en les regroupant.
La documentation de Mozilla (MDN, Mozilla Developer Network) propose un exemple
pertinent pour :matches. L’idée serait de pouvoir styler les titres de sections différemment
en fonction du niveau d’imbrication de ces dernières. En effet, les éléments section, article,
aside et nav pouvant être imbriqués, il s’avère fastidieux de styler toutes les combinaisons.

/* Au niveau 0 */

h1 {
font-size: 30px;

/* Au niveau 1 */
section h1, article h1, aside h1, nav h1 {

font-size: 25px;

}
/* Au niveau 2 */

section section h1, section article h1, section aside h1, section nav h1,

article section h1, article article h1, article aside h1, article nav h1,
aside section h1, aside article h1, aside aside h1, aside nav h1,

nav section h1, nav article h1, nav aside h1, nav nav h1, {

font-size: 20px;
}

/* Au niveau 3 */
/* … finalement peut-être pas. */

Avec :matches(), ça devient immédiatement plus simple :


/* Au niveau 0 */

h1 {
font-size: 30px;

}
/* Au niveau 1 */

:matches(section, article, aside, nav) h1 {

font-size: 25px;
}

/* Au niveau 2 */

:matches(section, article, aside, nav)


:matches(section, article, aside, nav) h1 {

font-size: 20px;
}
/* Au niveau 3 */

:matches(section, article, aside, nav)


:matches(section, article, aside, nav)
:matches(section, article, aside, nav) h1 {

font-size: 15px;
}

Comme vous pouvez le constater, le code est propre et lisible, mais ce n’est pas là une
révolution. En effet, cela ne permet pas d’effectuer quelque chose qui n’était auparavant
pas faisable avec CSS. On peut ranger ce sélecteur dans la catégorie des fonctionnalités
faisant office de sucre syntaxique.

Compatibilité des navigateurs pour :matches


Tableau 2–27 Navigateurs desktop

Tableau 2–28 Navigateurs mobiles

FIREFOX Version propriétaire


Firefox a déjà implémenté une vieille version de cette pseudo-classe sous le nom de
:any, toujours préfixée à l’heure actuelle en :-moz-any().
Les pseudo-classes de formulaire
Nous avons vu dans les deux sections précédentes un certain nombre de pseudo-classes
visant à faciliter la sélection d’éléments en fonction du contexte dans lequel ils se
trouvent : position, éléments voisins, langue… Néanmoins, il y a quelque chose qui a
toujours manqué à CSS ; il s’agit de la possibilité de cibler les éléments de formulaire en
fonction de leur état. Par exemple, comment cibler le bouton radio sélectionné par défaut
ou encore un champ de texte s’il est obligatoire ? Peut-on savoir si un champ est
actuellement invalide par rapport aux attributs de validation HTML 5 qu’il arbore ?
Comment sélectionne-t-on un champ ciblé par l’utilisateur ?
Pour que les interactions avec les formulaires HTML soient plus agréables pour les
utilisateurs, les spécifications CSS ont introduit un grand nombre de pseudo-classes.

Focus avec :focus (en toute simplicité)


Commençons avec une pseudo-classe que nous connaissons tous : :focus. Elle permet de
styler un élément lorsqu’il est ciblé par l’utilisateur, que ce soit à l’aide de la souris, du
clavier ou encore d’une pression tactile.
De manière pratique, on retrouve cette pseudo-classe dans les champs de formulaire afin
de mettre en avant le champ que l’utilisateur est en train de remplir. Ainsi, son attention
est maintenue, même quand les formulaires comportent de nombreux champs.
input:focus {
border: 1px solid hotpink;

ACCESSIBILITÉ :focus et accessibilité


Au-delà des formulaires, cette pseudo-classe est souvent utilisée pour styler les liens
lorsqu’ils sont ciblés. Par défaut, les navigateurs appliquent un outline aux liens lors de
leur focus afin de permettre aux utilisateurs qui surfent au clavier de ne pas se perdre
et de se repérer dans la page.
On a eu pendant longtemps l’habitude de retirer ces styles par défaut via la règle
a:focus { outline: none } afin de garder une pureté visuelle sur les liens, mais il s’agit là
d’une mauvaise pratique en termes d’accessibilité. En effet, indiquer à l’utilisateur le
focus est primordial, essentiellement pour ceux naviguant au clavier.
Si vous jugez l’outline par défaut décalé par rapport à vos styles, redéclarez-le mais ne
le supprimez pas.

Compatibilité des navigateurs pour :focus


Tableau 2–29 Navigateurs desktop
Tableau 2–30 Navigateurs mobiles

État des champs de formulaire avec :enabled et :disabled


Commençons avec le plus simple : déterminer si un champ est activé ou non. Il est
désactivé quand il porte l’attribut HTML disabled (qu’il soit noté sous sa forme
disabled='disabled' ou simplement disabled). Bien évidemment, si un champ n’est pas
désactivé, il est considéré comme activé.
CSS 3 introduit donc deux pseudo-classes : :enabled et :disabled. Pour que :disabled soit
prise en compte, il faut donc qu’on assigne au champ l’attribut HTML disabled.
input:disabled,
textarea:disabled,

select:disabled {
background: silver;

cursor: not-allowed;
}

De son côté, la pseudo-classe :enabled permet de sélectionner tous les champs de


formulaire pour lesquels l’attribut HTML disabled n’est pas assigné, autrement dit ceux que
l’utilisateur peut/doit remplir dans le formulaire.
input:enabled,
textarea:enabled,
select:enabled {

background: white;
}

Autre fait intéressant, un élément a, link ou area avec un attribut href non nul est considéré
comme :enabled. Ainsi, pour éviter de sélectionner des éléments de manière involontaire, je
suggère de toujours bien qualifier la pseudo-classe, par exemple avec input, select et
textarea.

CLARIFICATION À propos de input[disabled]


Dans la mesure où il est possible de cibler un élément en fonction de la présence d’un
attribut, on est en droit de se demander quelle est la différence entre input[disabled] et
input:disabled. D’après les spécifications HTML, ce dernier cible également les champs
dans un contexte comme celui-ci :
<fieldset disabled>

<input type="text" />

</fieldset>

Un champ de formulaire enfant d’un élément fieldset qui possède l’attribut disabled
(oui, c’est tout à fait possible !) sera ciblé par le sélecteur input:disabled, mais pas par
, dans la mesure où ce n’est pas l’élément qui est affublé de l’attribut
input[disabled]
disabled. Quoi qu’il en soit, dans la majorité des cas, le résultat est le même.

Compatibilité des navigateurs pour :enabled et :disabled


Tableau 2–31 Navigateurs desktop

Tableau 2–32 Navigateurs mobiles

Modes d’écriture avec :read-write et :read-only


Nous venons de voir que la pseudo-classe :disabled cible les éléments avec un attribut
disabled ou enfants d’un fieldset avec l’attribut disabled. Mais saviez-vous qu’il existe

également l’attribut HTML readonly ? Il y a deux différences majeures entre un élément


désactivé et un élément en lecture seule :
• un champ désactivé n’est pas soumis avec le formulaire alors qu’un champ en lecture
seule l’est ;
• un champ désactivé ne peut être la cible du focus (souris/clavier) alors qu’un champ en
lecture seule le peut (d’où l’idée de lecture).
De fait, il existe des cas où il est plus intéressant de mettre un champ en readonly qu’en
disabled, par exemple quand le code postal est défini à partir de la commune. Celui-ci peut
alors être conservé dans un champ en lecture seule, qui serait toutefois soumis et envoyé
au serveur lors de la validation du formulaire.
Revenons-en à nos moutons… Il existe désormais deux pseudo-classes : :read-only et :read-
write. Contrairement à ce qu’on pourrait penser, : read-only ne cible pas uniquement les
éléments ayant l’attribut readonly mais également les éléments disabled. Si on s’en tient aux
spécifications, un élément non éditable lambda est également susceptible de satisfaire la
pseudo-classe :read-only. En somme, elle regroupe tous les éléments dont il n’est pas
possible de changer manuellement la valeur.
Tout champ textuel de formulaire qui n’est ni désactivé ni en lecture seule est éligible pour
:read-write. Une chose importante toutefois : tout élément avec l’attribut contenteditable est
également considéré comme valide pour :read-write.
Pour résumer, ces deux pseudo-classes permettent de distinguer clairement les éléments
pour lesquels on peut saisir le contenu de ceux pour lesquels ce n’est pas possible.
/**
* Tout élément sans attribut contenteditable

* et champs de formulaire
* avec attribut readonly

* ou avec attribut disabled


* ou enfants d’un fieldset disabled
*/

:read-only {

background: #EEE;
opacity: 0.8;

/**
* Tout élément avec attribut contenteditable

* et champs de formulaire
* sans attribut readonly

* sans attribut disabled


* non descendant d’un fieldset disabled
*/

:read-write {
background: white;
}

Compatibilité des navigateurs pour :read-write et :read-only


Tableau 2–33 Navigateurs desktop

Tableau 2–34 Navigateurs mobiles

Bien que certains navigateurs clament supporter ces pseudo-classes, sachez qu’il y a de
sérieuses différences entre les spécifications et ce qui est réellement implémenté. Si on
s’en tient aux spécifications, un élément pouvant être altéré par l’utilisateur mais désactivé
(disabled ou readonly), ou tout autre élément non altérable par lui, doit être considéré comme
:read-only.

Tableau 2–35 Différences d’interprétation des spécifications des pseudo-classes :read-write et :read-only
Tout d’abord, aucun navigateur ne considère un champ désactivé (soit parce qu’il a
l’attribut disabled, soit parce qu’il est enfant d’un fieldset qui l’a) comme :read-only. De
plus, Chrome et Safari ne traitent ni du cas des éléments affublés de contenteditable, ni de
celui d’un élément non éditable classique. De son côté, Opera considère un élément
éditable via contenteditable comme :read-only.
Bref, les navigateurs s’emmêlent, et c’est finalement Firefox qui s’en sort le mieux, avec
une seule erreur.

Validité des champs de formulaire avec :valid et :invalid


Je pense qu’on peut s’accorder sur le fait que la validation d’un formulaire en temps réel
est toujours plus agréable que celle exécutée par le serveur après soumission (bien que
cette dernière soit indispensable). Malheureusement, cette validation exigeait jusqu’à
présent certaines opérations côté JavaScript, ce qui n’est pas non plus sans répercussion
sur le temps de chargement de la page, entre autres.
Aujourd’hui, nous avons accès aux pseudo-classes :valid et :invalid qui permettent de
déterminer si un champ est considéré comme valide vis-à-vis des éventuels attributs de
validation HTML 5 qui lui sont affublés, tels que required, type, pattern, etc. Par exemple, il
est possible d’attribuer des styles spécifiques à un champ de formulaire selon s’il est
valide ou non afin d’indiquer à l’utilisateur que quelque chose ne va pas avant même qu’il
tente de soumettre le formulaire.
/* On bascule les champs invalides en rouge pour montrer à l’utilisateur qu’il y a un souci */

input:invalid {

background: red;
}

On peut effectuer la même chose avec les champs valides, même si cela présente bien
évidemment moins d’intérêt.
/* On bascule les champs valides en vert pour montrer à l’utilisateur que tout va bien */

input:valid {

background: green;
}

ACCESSIBILITÉ Les couleurs ne suffisent pas


Dans l’exemple précédent, nous utilisons des couleurs. Attention toutefois à ne pas
véhiculer des informations à l’aide de couleurs pour des raisons d’accessibilité. En
effet, certains personnes ont des difficultés pour distinguer les couleurs.

Compatibilité des navigateurs pour :valid et :invalid


Tableau 2–36 Navigateurs desktop

Tableau 2–37 Navigateurs mobiles

OPERA MINI Tout est valide


Le navigateur proxy d’Opera applique la pseudo-classe :valid à tous les champs, qu’ils
soient valides ou non.

Statut des champs de formulaire avec :optional et :required


De la même façon qu’on peut distinguer les champs de formulaire désactivés de ceux
activés, on différencie les champs de formulaire obligatoires de ceux optionnels grâce aux
pseudo-classes :optional et :required.
Comme vous vous en doutez, la pseudo-classe :required cible les éléments avec un attribut
required (sous sa forme simple ou bien required="required"). Quant à :optional, ce sont les
éléments qui ne possèdent pas cet attribut.
On serait tenté d’utiliser la pseudo-classe :required pour ajouter le très populaire caractère
*, qui sert à indiquer les champs de formulaire obligatoires, après le label d’un champ.
Cependant, comme elle nécessite d’être employée sur l’input et non sur le label, on ne peut
pas se contenter de cela (sans rentrer dans des ruses de Sioux !).

Compatibilité des navigateurs pour :optional et :required


Tableau 2–38 Navigateurs desktop

Tableau 2–39 Navigateurs mobiles


OPERA MINI Tout est optionnel
Le navigateur proxy d’Opera applique :optional à tous les champs, qu’ils soient
obligatoires ou non.

Précisions sur les checkboxes et les boutons radio


avec :checked et :indeterminate
Personnaliser l’affichage des checkboxes et boutons radio a toujours été un challenge en
CSS, si bien qu’il a souvent été question d’intégrer du JavaScript pour que ce soit
réalisable. Aujourd’hui, il est tout à fait possible de modifier l’apparence d’une checkbox
sans recourir à quoi que ce soit d’autre que CSS.
Pour cela, nous avons désormais la pseudo-classe :checked qui permet de cibler les champs
de formulaire de type radio et checkbox lorsqu’ils sont cochés ou sélectionnés. Ainsi, changer
l’apparence d’une checkbox lorsqu’elle a été cochée devient relativement simple.
input[type="checkbox"] {
background-image: url('/images/checkbox.png');
background-position: 0 0;

}
/* Une fois la checkbox cochée, on décale l’image d’arrière-plan pour afficher la partie
correspondant à une checkbox cochée */
input[type="checkbox"]:checked {
background-position: 0 16px;

Dans notre exemple, on applique une image d’arrière-plan aux checkboxes pour leur
donner une apparence personnalisée. C’est en réalité un sprite constitué de deux images de
16 × 16 pixels, l’une pour l’effet normal et l’autre pour l’effet coché. Lorsque la checkbox
est cochée, on décale l’image d’arrière-plan pour afficher la partie correspondant à une
checkbox cochée.
Il est même possible d’ajouter une troisième image dans le sprite pour l’effet de survol
(:hover) et de faire la même chose dans les CSS. Ainsi, grâce à la pseudo-classe :checked, on
peut facilement appliquer des styles en fonction de l’état d’une checkbox ou d’un bouton
radio. Notons que la pseudo-classe :checked s’applique également à l’option sélectionnée
d’un select.
De son côté, la pseudo-classe :indeterminate permet de cibler une checkbox si elle a sa
propriété indeterminate assignée à true. Néanmoins, il n’existe pas d’attribut HTML pour
cela ; le seul moyen pour qu’une checkbox ait l’état indéterminé est de passer par
JavaScript :
var checkbox = document.getElementById("checkbox");

checkbox.indeterminate = true;

Comme pour les états checked et standard, les navigateurs ont tous leur façon bien à eux de
présenter une checkbox indéterminée. Ceci étant dit, cette dernière est soit cochée, soit
décochée. L’état indéterminé n’est que visuel et, par conséquent, il remplace l’apparence
de la véritable valeur de la checkbox. En d’autres termes, une checkbox peut être
indéterminée (via JavaScript) tout en étant cochée ou décochée.
Du coup, dans le cas où le JavaScript serait en mesure d’éditer l’attribut indeterminate d’une
checkbox, CSS peut la cibler lors de son état indéterminé via la pseudo-classe éponyme :
input[type="checkbox"]:indeterminate {
/* La checkbox est indéterminée */
}

Un cas d’usage présenté par Ryan Seddon serait celui de la conception d’une arborescence
à l’aide de checkboxes.
• Si une checkbox parent a tous ses enfants cochés, alors elle est cochée.
• Si elle a tous ses enfants décochés, alors elle est décochée.
• Si elle a des enfants cochés et décochés, elle est indéterminée.
POUR EN SAVOIR PLUS
Pour plus d’informations sur la réalisation d’une arborescence à l’aide de checkboxes
par Ryan Seddon, référez-vous à son article original en anglais.
http://bit.ly/css-tree-checkboxes

Compatibilité des navigateurs pour :checked


Tableau 2–40 Navigateurs desktop

Tableau 2–41 Navigateurs mobiles

Compatibilité des navigateurs pour :indeterminate


Tableau 2–42 Navigateurs desktop

Tableau 2–43 Navigateurs mobiles


Valeur par défaut des boutons radio avec :default
La pseudo-classe :default est très certainement une des plus méconnues de toute la gamme
des pseudo-classes dédiées aux formulaires et, pour cause, son intérêt est très limité. Quoi
qu’il en soit, elle permet de cibler l’élément par défaut dans un groupe d’éléments
associés.
ATTENTION Ne pas confondre avec :checked
Attention à ne pas confondre ceci avec la pseudo-classe :checked qui applique des styles
à l’option sélectionnée.
Grâce à ce sélecteur, vous pourrez stipuler que telle option est celle par défaut uniquement
via CSS. Considérez le code ci-dessous :
input[type="radio"]:default + label:after {
content: " (défaut)";

Figure 2–10
On se sert de :default pour afficher l’option choisie par défaut.

Concrètement, cette règle utilise le pseudo-élément :after d’un label directement adjacent à
l’option par défaut d’un groupe de boutons radio pour afficher à l’écran Mon label (défaut).
Cette pseudo-classe s’emploie également sur les boutons de soumission de formulaire afin
d’indiquer quel est celui par défaut quand il y en a plusieurs.

Compatibilité des navigateurs pour :default


Tableau 2–44 Navigateurs desktop

Tableau 2–45 Navigateurs mobiles

Bien que de nombreux moteurs de rendu interprètent la pseudo-classe : default, il faut


savoir qu’ils le font tous à leur manière.
• Opera ne supporte : default que sur les checkboxes et les champs radio.
• Chrome et Safari ne supportent :default que sur les boutons de soumission.
• Firefox supporte :default sur tout (boutons, checkboxes et champs radio).
Aussi certaines distributions de Linux présentent-elles un souci avec la compréhension de
:default sur Firefox.

Gestion de l’amplitude des champs de type number avec :in-


range et :out-of-range
Les pseudo-classes :in-range et :out-of-range ne sont prises en compte que lorsqu’un champ
de type number a des attributs min et max assignés. Dans ce cas précis, elles permettent
d’appliquer des styles particuliers au champ lorsque la valeur saisie dans celui-ci est
contenue – ou non – entre la valeur de l’attribut min et celle de max.
Ces sélecteurs permettent de représenter de manière visuelle la validation d’un champ de
type number dont la valeur doit être comprise entre deux nombres.
input[type="number"]:in-range {

border-color: green;
}
input[type="number"]:out-of-range {

border-color: red;
}

ACCESSIBILITÉ Les couleurs ne suffisent pas


Pour cet exemple, nous utilisons des couleurs. Attention toutefois à ne pas véhiculer
des informations à l’aide de couleurs pour des raisons d’accessibilité. En effet, certains
personnes ont des difficultés pour distinguer les couleurs.

Compatibilité des navigateurs pour :in-range et :out-of-


range
Tableau 2–46 Navigateurs desktop

Tableau 2–47 Navigateurs mobiles


Les pseudo-éléments
Jusqu’à présent, nous avons vu quelques opérateurs et de nombreuses pseudo-classes dont
le but était de rendre nos sélecteurs CSS plus puissants que jamais. Mais CSS apporte
aussi des pseudo-éléments pour avoir un contrôle plus fin sur les éléments, notamment
dans le domaine de la typographie avec :first-letter et :first-line.

Évolution de la syntaxe
Bien que les spécifications CSS 3 n’apportent pas de nouveau pseudo-élément, elles
appliquent un changement important à la syntaxe en demandant à ce que les pseudo-
éléments soient préfixés par un double caractère deux-points (::), afin de les différencier
des pseudo-classes qui s’écrivent avec un seul caractère :.
Tableau 2–48 Évolution de la syntaxe des pseudo-éléments

Ancienne syntaxe Nouvelle syntaxe


:before ::before
:after ::after
:first-line ::first-line
:first-letter ::first-letter

Rappelons la différence majeure entre une pseudo-classe et un pseudo-élément selon les


spécifications officielles du W3C : le pseudo-élément crée un élément virtuel alors que la
pseudo-classe sert de filtre pour la recherche d’éléments.
Notons aussi que les pseudo-éléments n’existent pas réellement dans le DOM, bien que
certains inspecteurs de DOM, tels que le DevTools de Chromium, les affiche dans un
souci de simplicité.
INTERNET EXPLORER 8 Pas de support de la nouvelle syntaxe
Bien sûr, Internet Explorer 8 ne supporte pas la nouvelle syntaxe des pseudo-éléments.
Vous devrez donc vous contenter de l’ancienne syntaxe si vous devez assurer le
support de ce navigateur dans vos projets. Je recommande d’employer la nouvelle
syntaxe si votre projet le permet.

Contrôle de la sélection du texte avec ::selection


Le pseudo-élément ::selection était initialement prévu dans les brouillons de la
spécification CSS Selectors Level 3, mais il a été abandonné durant le passage en
Candidate Recommandation faute d’explications à son sujet, notamment dans des cas
complexes comme l’imbrication des éléments. En revanche, il a tout récemment été
réintroduit dans le module Pseudo-elements Level 4, d’où sa présence dans ce livre. De
plus, il est déjà implémenté dans différents navigateurs.
Quoi qu’il en soit, le pseudo-élément ::selection peut être employé pour personnaliser
l’apparence du texte lorsqu’il est surligné (à l’aide d’une souris ou d’un autre mécanisme)
par l’utilisateur. Comme pour ::first-letter et ::first-line, seul un petit nombre de
propriétés CSS lui sont applicables.
::selection {

background: hotpink;
color: #333;

Compatibilité des navigateurs pour ::selection


Tableau 2–49 Navigateurs desktop

Tableau 2–50 Navigateurs mobiles

Firefox est le seul navigateur nécessitant un préfixe constructeur pour ce pseudo-élément.


Il faut donc écrire la règle de la façon suivante ::-moz-selection. Attention cependant, vu que
CSS abandonne intégralement les règles dont une partie du sélecteur n’est pas comprise,
deux règles différentes doivent être écrites. En effet, ::-moz-selection, ::selection serait
ignorée sur les navigateurs n’employant pas le moteur de rendu Gecko puisque ::-moz-
selection n’est pas valide pour eux.

CHROME Problème sur les éléments input


Chrome ne traite pas le pseudo-élément ::selection sur les éléments de type input.
3
Positionnement et layout : les nouvelles
techniques de mise en page

Les plus grandes avancées qu’a connu CSS tournent toutes plus ou moins autour du sujet
de la mise en page des documents. Finis les float et inline-block, bonjour Flexbox, Grid et
compagnie !
Initialement, CSS a été inventé pour habiller des documents textuels, tels que des rapports
ou des documentations. L’idée principale était de pouvoir mettre des choses en évidence
via les feuilles de styles, comme des parties en exergue, des termes en couleurs, en gras ou
en italique, mais c’est à peu près tout.
Et nous voilà, presque vingt ans plus tard, à utiliser toujours le même langage pour mettre
en page des sites entiers cette fois, des applications, tout en tenant compte de critères aussi
divers que les tailles d’écrans, les capacités des navigateurs, et encore bien d’autres
facteurs.
Aussi la survie de CSS n’est-elle pas seulement due à son manque de concurrents
(rappelons toutefois la tentative échouée de Netscape pour faire adopter les JSSS, les
JavaScript Style Sheets) mais aussi à ses capacités d’adaptation. Les spécifications se sont
précisées, les fabricants de navigateurs rapprochés, afin de proposer de nouvelles
techniques de mise en page.
Jadis, les tableaux HTML servaient à dessiner la structure d’une page, technique qui a été
stigmatisée lorsque séparer la forme du contenu est devenu la bonne pratique, il y a bien
des années. C’est pourquoi les développeurs ont trouvé une autre technique pour mettre en
page des structures complexes : la propriété float.
Malheureusement, cette propriété n’a jamais été conçue pour cela et l’utiliser à des fins
structurelles ressemble davantage à un hack qu’à une véritable bonne idée. Plus tard, on a
vu arriver display: inline-block, qui a permis de nouvelles choses mais, dans le fond, le
problème n’était toujours pas résolu. En effet, une propriété ne suffit pas ; il faut de
nouveaux modules entiers pour accompagner les designers et développeurs dans la
construction de leurs applications.
Ces nouveaux modules, ils existent déjà. Certes, ils ne sont pas tous utilisables partout ou
dans leur intégralité, mais on peut dès aujourd’hui s’attacher à comprendre leur
fonctionnement afin d’être en mesure de les employer quand c’est possible. Parmi eux, il y
a le très populaire module Flexbox, alias « module de boîtes flexibles », mais aussi Grid,
le système de grilles natif, qui traîne un peu plus à se faire adopter par les navigateurs. On
parlera également de la distribution de contenus dans différentes zones avec les modules
de multicolonne et de régions CSS, sans oublier quelques autres outils annexes, mais non
moins efficaces, comme la propriété box-sizing ou la règle position: sticky.
Tout un programme donc pour ce chapitre qui est, selon moi, le pan de CSS le plus
important à l’heure actuelle. Mettre en page le contenu est un challenge chaque jour plus
difficile dans la mesure où les différents appareils mobiles, résolutions et tailles d’écrans
se font toujours plus nombreux.
Le modèle de boîte : retour aux sources avec box-
sizing
Ceux d’entre vous qui développaient des sites il y a de nombreuses années déjà se
souviendront sûrement que le modèle de boîte n’a pas toujours été ce qu’il est aujourd’hui.
Mais, pour ne léser personne, je vous propose une petite piqûre de rappel sur ce qu’est le
modèle de boîte – souvent appelé box model – et comment il fonctionne.
Le modèle de boîte, c’est ce qui définit la taille d’un élément. Parce que, finalement, quel
que soit l’élément que vous manipulez ou son apparence, cela reste, dans le fond, une
boîte. Un rectangle. Tous les éléments d’une page sont des rectangles. Pour agencer des
éléments, les navigateurs s’appuient sur ce fameux modèle de boîte. C’est grâce à lui
qu’ils peuvent assigner des dimensions aux éléments, travailler leurs marges, leurs
bordures et ainsi composer une page entière.
Une boîte en CSS se structure de la façon suivante : la zone de contenu est entourée par
les marges intérieures (padding), puis les bordures (border), puis les marges extérieures
(margin).
Les dimensions d’une boîte sont calculées comme ceci :
• largeur : width + padding-left + padding-right + border-left + border-right ;
• hauteur : height + padding-top + padding-bottom + border-top + border-bottom.

Figure 3–1
Modèle de boîte selon les spécifications officielles du W3C

Comme vous pouvez le constater, les marges extérieures (margin) sont exclues lorsqu’il
s’agit de calculer les dimensions d’un élément. En effet, elles serviront à séparer les
éléments les uns des autres, mais pas à déterminer leurs dimensions.
Si la largeur d’une boîte de type block n’est pas déterminée via la propriété width, elle va
s’étendre sur toute la largeur disponible. Dans le cas d’un élément de type inline, celui-ci
sera dimensionné en fonction de son contenu.
Si vous appliquez width: 100% à une boîte, ainsi qu’un padding à gauche et/ou à droite, et/ou
une bordure à gauche et/ou à droite, la boîte va « exploser » hors de son conteneur. En
d’autres termes, elle va déborder. Par exemple :
/**

* Avec le modèle de boîte classique,


* cet élément mesure 230 × 230 pixels

*/
.element {

width: 200px;

height: 200px;

padding: 10px;
border: 5px solid;

Cet élément ne mesure pas 200 × 200 pixels comme on pourrait le croire en lisant les
propriétés width et height, mais 230 × 230. En effet, on ajoute 10 pixels de padding et 5 pixels
de border de chaque côté, soit 2 × 15 pixels. Et c’est là un problème important, notamment
dans le cas de réalisations fluides (Responsive Web Design) : on ne peut visiblement pas
assigner une largeur à un élément ainsi que des marges internes (padding) et/ou bordures
sans que celui-ci ne déborde de son conteneur.
C’est pour cela que la propriété box-sizing fait son entrée dans les spécifications CSS 3.
Elle permet de redéfinir l’algorithme utilisé pour calculer les dimensions des boîtes
(comme vu précédemment). Cette propriété accepte trois valeurs :
• la première est content-box, à savoir sa valeur par défaut et donc le comportement que l’on
vient de voir ;
• la deuxième, très peu utilisée, est padding-box. Elle retire les bordures du calcul des
dimensions des éléments. Autrement dit, si vous assignez une largeur à un élément,
ainsi qu’une marge intérieure et une bordure, cette dernière seulement ne sera pas prise
en compte dans la largeur de l’élément. Cette valeur n’a finalement que peu d’intérêt
puisqu’elle ne résout pas nos soucis. De plus, son support est assez faible et les
spécifications CSS remarquent ceci : « Note: The ‘padding-box’ value is at risk. »
Autrement dit, ce n’est pas une valeur recommandée, et je pense qu’à terme elle sera
dépréciée complètement.
• Ce qui nous amène à la troisième et la plus connue des trois valeurs de la propriété box-
sizing : border-box (qui restaure le modèle de boîte tel qu’il l’était sur Internet Explorer 6,
sous Quirks Mode). Celle-ci retire les marges internes (padding) et les bordures (border)
des calculs de dimensions afin que la largeur ne soit plus déterminée que par width et la
hauteur que par height.
Aussi les padding et les border sont-ils compris dans les dimensions de l’élément. Si on
reprend l’exemple précédent, notre élément mesure donc bien 200 × 200 pixels malgré ses
marges internes et ses bordures, puisque celles-ci sont déduites de la taille totale. On se
rend vite compte que c’est un système de calcul bien plus facile à appréhender.

Cas pratique : simplifier les calculs de dimensions


C’est Paul Irish (ingénieur chez Google) qui, au début de l’année 2012 alors que
l’utilisation d’Internet Explorer 6 et 7 se faisait de plus en plus rare, a suggéré l’emploi de
ce « pas si nouveau » modèle de boîte pour tous les éléments de la page avec la règle
suivante :
*,

*::after,

*::before {
box-sizing: border-box;

Cette règle peut sembler un peu agressive, mais l’appliquer à tous les éléments assure une
cohérence générale et beaucoup moins de maux de tête pour calculer les dimensions des
boîtes.
Vous remarquerez cependant qu’il est nécessaire d’ajouter manuellement les pseudo-
éléments dans la règle afin qu’ils bénéficient eux aussi du modèle de boîte approprié. En
effet, le sélecteur universel * ne comprend pas les pseudo-éléments.
JOURNÉE INTERNATIONALE DU BOX-SIZING 1er février
Suite à cet article de Paul Irish, qui demeure un des plus célèbres articles sur CSS à ce
jour, Chris Coyier a déclaré que le 1er février (jour de parution de l’article) de chaque
année serait la journée internationale du box-sizing (International box-sizing
Awareness Day).
http://bit.ly/paul-irish-border-box
http://bit.ly/css-tricks-border-box
Courant 2014, Chris Coyier a proposé une alternative un peu moins nucléaire qui consiste
à s’appuyer sur la cascade naturelle plutôt que sur le sélecteur universel :
html {

box-sizing: border-box;

}
*,

*::after,

*::before {
box-sizing: inherit;

Cette technique, bien que très semblable à celle de Paul Irish, a le mérite de permettre à
des composants d’employer un modèle de boîte différent de border-box sans devoir
réappliquer cette propriété à tous leurs éléments. Par exemple :
.component {
box-sizing: content-box;

Parce que tous les éléments héritent de la propriété box-sizing de leur parent, redéfinir celle
du composant suffit pour que tous ses enfants emploient ce modèle de boîte plutôt que
celui fixé pour l’élément html.
Compatibilité des navigateurs pour box-sizing
Tableau 3–1 Navigateurs desktop

Tableau 3–2 Navigateurs mobiles

Le support est excellent ! En revanche, Firefox est le seul navigateur à supporter


correctement la valeur padding-box. Heureusement, celle-ci n’est quasiment pas employée,
donc box-sizing: border-box est tout à fait viable dans vos productions. De nombreux
frameworks, tels que Bootstrap, Foundation ou KNACSS, l’utilisent depuis longtemps
déjà, et il existe des alternatives en JavaScript pour Internet Explorer 7.
ANDROID Problème avec l’élément select
Le navigateur d’Android ne parvient pas à calculer correctement la hauteur (height) et
la largeur (width) de l’élément select.
SAFARI 6 Problème avec les éléments en display: table
Quand bien même vous spécifiez une valeur de box-sizing aux éléments affublés de
display: table, Safari 6 n’en tiendra pas compte.

INTERNET EXPLORER 9 Différence d’interprétation


Dans le cas d’un élément positionné en absolu (position: absolute) avec overflow à auto ou
overflow-y à scroll, Internet Explorer 9 soustrait la largeur de la « scrollbar » de la
largeur de l’élément.
Le multicolonne
Les magazines papier et les journaux ont toujours employé un système de colonage pour
leurs contenus. Dans la mesure où de plus en plus de lignes éditoriales importantes se
tournent vers un format numérique, le besoin d’un système de colonage simple et flexible
s’est fait sentir.
C’est pourquoi, aujourd’hui, nous avons le module appelé « Multi-Column Layout
Module », autrement dit un système de colonage natif et intégralement géré avec CSS. Et
c’est une très bonne chose car, finalement, réaliser des colonnes fluides a toujours été très
compliqué, même en JavaScript !
C’est aussi la raison pour laquelle je trouve personnellement surprenant qu’il ait fallu
attendre si longtemps pour en arriver là quand on sait que CSS a été initialement inventé
pour mettre en page des documents textuels, donc potentiellement des contenus ayant
besoin d’être distribués dans plusieurs colonnes.

Comment ça marche ?
Traditionnellement, le contenu d’un élément tient dans ce qu’on appelle sa content box.
Lorsqu’un élément utilise le module de colonage, on introduit un nouveau type de
conteneur entre la content box et le contenu : la column box. À savoir que ce conteneur est
l’affaire du navigateur et ne devrait donc pas du tout impacter vos développements,
d’autant qu’il est impossible de le manipuler, ni avec CSS, ni avec JavaScript. De fait, le
contenu d’un élément multicolonne s’écoule dans des colonnes, dont le nombre peut varier
en fonction des propriétés column-count et column-width.
Ces colonnes forment une rangée : comme des cellules de tableau, par exemple, les
colonnes sont arrangées dans le sens de lecture de l’élément « colonné ». Toutes les
colonnes ont la même largeur (potentiellement définie par column-width) : il n’est pas
possible d’avoir des colonnes de différentes largeurs au sein d’un élément colonné. De
même, toutes les colonnes sont de même hauteur et, vu la difficulté qu’on peut avoir à
maintenir des colonnes de même hauteur en CSS, c’est là une bonne nouvelle !
Les colonnes sont séparées par une gouttière déterminée par la propriété column-gap, qui
peut elle-même contenir une bordure définie par column-rule. Là encore, toutes les
gouttières et toutes les bordures sont identiques ; on ne peut pas différencier une gouttière
ou une bordure d’une autre. De plus, une règle ne sera visible que si les deux colonnes
qu’elle sépare ont du contenu.
Enfin, il n’est pas possible d’appliquer des styles à une colonne ; il s’agit d’un élément
invisible qu’on ne peut cibler. Autrement dit, on ne peut pas appliquer une couleur de fond
à une colonne en particulier, et une colonne n’est pas liée aux concepts du modèle de boîte
(padding, margin, border).

Syntaxe
Comme vous l’avez compris des explications précédentes, le module de colonage intègre
un certain nombre de propriétés pour ajuster le rendu selon les besoins : nombre de
colonnes, taille de la gouttière, lignes de séparation, etc., autant de caractéristiques sur
lesquelles vous avez la main afin de mettre en page vos contenus textuels.
Le nombre de colonnes dans une rangée est déterminé par la propriété column-count et/ou
column-width, ou encore par la propriété columns qui est un raccourci des deux précédentes.

Largeur des colonnes avec column-width


La propriété column-width accepte une longueur ou la valeur auto. Cette dernière signifie que
le nombre de colonnes sera fixé par la valeur de la propriété column-count. En revanche, si
vous spécifiez une longueur, celle-ci déterminera la largeur optimale pour une colonne.
Ceci étant dit, la largeur des colonnes peut varier selon la place allouée : plus grande s’il y
a davantage de place ou plus petite s’il en manque.
Par exemple :
.columns {
width: 100px;
column-width: 45px;

Dans cet exemple, l’élément fait 100 pixels de large, ce qui ne laisse la place que pour
deux colonnes. Comme celles-ci occupent nécessairement toute la largeur de l’élément
colonné, chacune des deux colonnes sera légèrement plus grande que prévu : 50 pixels,
pour compléter l’espace restant.
Dans le cas contraire :
.columns {
width: 100px;
column-width: 105px;
}

La largeur des colonnes est plus grande que celle de l’élément, ce qui signifie qu’une seule
et unique colonne sera créée, et la largeur de celle-ci sera rabaissée à 100 pixels pour être
contenue dans l’élément.
Si la propriété column-width est si flexible, c’est bien pour permettre l’utilisation du
multicolonne dans le cas où différentes tailles d’écrans seraient impliquées.
Figure 3–2
Trois exemples de column-width

Nombre de colonnes avec column-count


La propriété column-count attend un entier ou bien la valeur auto, qui implique que le nombre
de colonnes sera fixé par la valeur de la propriété column-width que nous venons de voir. Si
la valeur de column-count est un entier, elle définit tout simplement le nombre de colonnes.
Si les deux propriétés sont déterminées, la valeur de la propriété column-count fixe le nombre
maximal de colonnes.
Figure 3–3
Trois exemples de column-count
/**
* 3 colonnes, en toutes circonstances

*/
.columns {
column-count: 3;

Déclaration raccourcie avec columns


Comme nous l’avons vu, la propriété columns (attention au pluriel !) est un raccourci pour
les deux propriétés column-width et column-count. Parce que celles-ci n’attendent pas le même
type de valeur, vous pouvez les spécifier dans l’ordre que vous le souhaitez et le parser
CSS parviendra à distinguer quelle valeur revient à quelle propriété. Dans le cas où vous
spécifiez une seule valeur, la seconde sera définie à sa valeur initiale, à savoir auto.
/**

* column-width: 12em;
* column-count: auto

*/

columns: 12em;
columns: auto 12em;

/**

* column-width: auto;
* column-count: 2;

*/
columns: 2;

columns: 2 auto;
/**

* column-width: auto;
* column-count: auto;
*/

columns: auto;

columns: auto auto;

Gestion de la gouttière avec column-gap


Faire des colonnes c’est très bien, mais il faut pouvoir les séparer les unes des autres sans
quoi il est impossible de lire le contenu ! Pour ce faire, il faut utiliser une gouttière grâce à
la propriété column-gap (gap signifie « espace »).
Celle-ci attend une longueur positive, sans quoi elle retombe sur sa valeur par défaut qui
est normal. La longueur définie par la valeur normal n’est pas déterminée dans les
spécifications, aussi revient-elle au bon vouloir du navigateur. Les spécifications
recommandent une valeur de 1em, mais ce n’est pas obligatoire.
.columns {

column-gap: 1.5em;
}

Figure 3–4
Trois exemples de column-gap
Séparation des colonnes avec column-rule
Comme il est possible de séparer les colonnes les unes des autres, il est également
envisageable de définir une bordure verticale entre les colonnes. Celle-ci apparaîtra au
beau milieu de la gouttière déterminée par la propriété column-gap.
La propriété column-rule fonctionne exactement de la même façon que border, dans le sens
où elle est un raccourci pour les propriétés :
• column-rule-color : la couleur de la bordure (par défaut, currentcolor) ;
• column-rule-width : la largeur de la bordure (par défaut, medium) ;
• column-rule-style : le style de la bordure (par défaut, none).
.columns {

column-rule: 1px solid black;


}

Parce que la valeur par défaut de column-rule-style est none, c’est la seule des trois propriétés
obligatoires pour afficher une ligne de séparation entre les colonnes. Du coup, la
déclaration suivante suffit :
.columns {
column-rule: solid;
}

Figure 3–5
Trois exemples de column-rule

Interruption des colonnes avec break-*


À partir de là, vous devriez être capable d’utiliser sans mal le module de multicolonne
dans vos réalisations. C’est finalement assez simple : on spécifie le nombre de colonnes
et/ou la largeur optimale d’une colonne grâce à columns, la largeur des gouttières via column-
gap, et une éventuelle bordure de séparation via column-rule.

Mais quand l’utilisation de ce module va au-delà de la simple décoration, on a besoin de


pouvoir déterminer ce que devient le contenu lorsque celui-ci est interrompu pour une
raison ou pour une autre. On va retrouver là le fonctionnement mis en place pour le
module de pagination (@page, non abordé dans ce livre).
Nous avons ainsi affaire à trois propriétés :
• break-before : détermine le comportement d’un changement de colonne avant l’élément ;
• break-after : détermine le comportement d’un changement de colonne après l’élément ;
• break-inside : détermine le comportement d’un changement de colonne au sein de
l’élément.
Elles acceptent les valeurs suivantes (où break-after traite de la notion d’après, break-before
de la notion d’avant et break-inside de la notion d’à l'intérieur) :
• auto : ne force pas et n’évite pas le changement de colonne avant/après/à l’intérieur de
l’élément ;
• column : force le changement de colonne avant/après l’élément ;
• avoid-column : évite le changement de colonne avant/après/à l’intérieur de l’élément.
On ne traite ici que des valeurs s’appliquant aux colonnes. Les trois propriétés précédentes
acceptent également de nombreuses valeurs relatives au système de pagination, mais c’est
hors sujet. On peut donc considérer l’exemple suivant :
/**
* On définit un contexte de colonnes
*/

.columns {

column-width: 8em;
}

/**

* On force le changement de colonne


* avant un titre de niveau 2

*/

.columns h2 {
break-before: column;

/**
* On force le changement de colonne

* après une image

*/
.columns img {

break-after: column;
}

/**
* On évite le changement de colonne

* au sein d’une citation


*/
.columns blockquote {

break-inside: avoid-column;

Envahissement des colonnes avec column-span


Mettre du contenu sous plusieurs colonnes c’est bien, mais pouvoir faire en sorte que
certains éléments puissent envahir toute la largeur de l’élément sans tenir compte du
colonage, c’est mieux ! Prenez l’exemple d’un titre principal : celui-ci ne devrait pas être
contraint dans une colonne ; il doit pouvoir s’étendre sur toutes les colonnes afin de
marquer une véritable rupture.
C’est là qu’intervient la propriété column-span dans toute sa simplicité puisqu’elle n’accepte
que deux valeurs :
• none : la valeur par défaut ;
• all : qui définit ce qu’on vient de voir, à savoir un envahissement de toutes les colonnes.
Dans notre cas, par exemple :
.columns h2 {

column-span: all;
}

Figure 3–6
Envahissement des colonnes par les titres grâce à column-span
Équilibrage des colonnes avec column-fill
Par défaut, le contenu est équilibré sur toutes les colonnes, c’est-à-dire que le navigateur
essaie autant que possible de distribuer le contenu dans chaque colonne de manière
équitable en minimisant la différence de taille de contenu entre les colonnes.
De manière générale, c’est le comportement désiré, mais quand on fait face à du contenu
dynamique dont la longueur peut drastiquement varier, il est possible de rencontrer le cas
de figure où une seule courte phrase tente vainement de remplir toutes les colonnes, pour
un rendu aussi original qu’indésirable.
C’est pour cela que la propriété column-fill existe, qui elle aussi n’accepte que deux
valeurs :
• balance : valeur par défaut ;
• auto : cette valeur a pour effet de remplir les colonnes une à une. Notez qu’il est possible
que certaines colonnes soient partiellement remplies ou complètement vides.

Figure 3–7
Équilibrage des colonnes grâce à column-fill

Cas pratique : alléger une liste chargée


Je vais être honnête, je ne suis pas designer, encore moins typographe. J’aurais peine à
savoir quand il est judicieux d’utiliser des colonnes plutôt qu’un conteneur unique. Quoi
qu’il en soit, je trouve que les colonnes sont pertinentes pour alléger une liste qui contient
beaucoup d’éléments, par exemple celle des éléments chimiques existants. Dans le cas où
celle-ci ne contiendrait que le nom des propriétés, une liste – bien qu’adaptée – prendrait
beaucoup de place à l’écran.
On pourrait donc employer les colonnes CSS sur le conteneur pour répartir les éléments de
la liste en colonnes, afin de prendre moins de place. Tout simplement :
.list-container {

columns: 4;

padding: 1em;
}

.list-container ul {

margin: 0;
}
Figure 3–8
Utilisation des colonnes pour alléger l’affichage d’une longue liste d’éléments

Cas pratique : utiliser les colonnes comme grille


Au cours des derniers mois, nous avons vu apparaître un cas d’utilisation des colonnes
CSS assez astucieux qui consiste à les employer pour en faire un système de grille. Peut-
être connaissez-vous la bibliothèque JavaScript Masonry (désormais Isotope), populaire
pour ses mises en page en colonnes, à la manière de Pinterest par exemple ?
Figure 3–9
La bibliothèque JavaScript Masonry, offrant de puissants layouts en colonnes

L’idée est donc de réaliser une mise en page identique sans utiliser de JavaScript,
uniquement avec des colonnes en CSS. Et effectivement, ça fonctionne plutôt bien même
si ce n’est pas extrêmement flexible.
Imaginons que nous souhaitions afficher une collection de médias (tels que des images
avec une légende, un auteur, une date, des catégories, et tout autre type d’information)
dans une grille composée de trois colonnes.
<div class="container">
<div class="item">…</div>
<div class="item">…</div>

<div class="item">…</div>

<!-- … et encore bien d’autres enfants. -->


</div>

C’est tout ce dont nous avons besoin en termes d’HTML. Passons au CSS, qui est tout
aussi succinct.
/**

* Conteneur
* 1. Optionnel : valeur par défaut

*/

.container {
columns: 3;

column-gap: 1em; /* 1 */

padding: 1em;

}
/**

* Éléments
* 1. Optionnel : valeur par défaut

*/
.item {

width: 100%; /* 1 */
break-inside: avoid-column;
margin-bottom: 1em;

C’est tout ! Et encore, vous avez remarqué que deux déclarations ont été ajoutées dans le
but de rendre l’exemple plus explicite, mais elles sont en fait optionnelles puisqu’elles
reprennent littéralement les valeurs par défaut.
Notez également que nous empêchons un élément de démarrer en bas de colonne pour se
poursuivre sur la colonne suivante grâce à la déclaration break-inside: avoid-column.
Désormais, pour s’accommoder aux différentes tailles d’écran, il suffit de changer la
valeur de columns au sein d’une Media Query (détaillée au chapitre 8).

Figure 3–10
Un layout Masonry-like via CSS Columns

RESSOURCE Un layout horizontal


Le développeur Tommi Kaikkonen a créé un layout horizontal à l’aide de CSS
Columns et d’un peu de JavaScript pour arrondir les angles (au sens figuré bien
évidemment).
Je vous recommande fortement de jeter un œil à sa démo, qui change de ce qu’on a
l’habitude de voir et vaut le détour.
http://bit.ly/kaikkonen-columns
Figure 3–11 Un layout horizontal via le multicolonne

Compatibilité des navigateurs pour le multicolonne


Tableau 3–3 Navigateurs desktop

Tableau 3–4 Navigateurs mobiles

Notons quelques soucis malgré ce support tout à fait honorable.


• Firefox n’interprète pas column-span.
• Aucun navigateur, sauf Opera Mini et Internet Explorer, ne supporte break-before, break-
after et break-inside.

• WebKit possède une version non standard -webkit-column-break-before, -webkit-column-break-


after et -webkit-column-break-inside.
Les modèles de boîtes flexibles avec Flexbox
S’il y a bien un challenge que CSS n’a jamais véritablement résolu, c’est le
positionnement des éléments. Malgré quatre systèmes de positionnement différents
officialisés par les spécifications CSS 2.1 (block, inline, tabulaire, positionné), positionner
correctement des éléments en CSS a toujours été un sacré casse-tête. Que ce soit à l’aide
des tableaux, des floats, de l’inline-block ou même du système tabulaire, les designers et
développeurs n’ont jamais vraiment eu entre les mains un outil approprié pour la mise en
page de sites et d’applications.
Il aura donc fallu une bonne quinzaine d’années à CSS pour finalement proposer un
module entier dédié au positionnement des éléments : CSS Flexible Box Layout Module
Level 1, ou encore Flexbox pour les intimes. Ce module introduit donc un tout nouveau
système de positionnement (via la propriété display comme de coutume) permettant, entre
autres, de :
• distribuer les éléments aussi bien en lignes qu’en blocs ;
• contrôler la gestion des espaces disponibles ;
• contrôler les alignements verticaux et horizontaux ;
• agencer les éléments sans tenir compte du DOM.
Rien que ce dernier point démontre à quel point ce module est avancé par rapport à tout ce
qu’on a connu. Il n’a jamais été vraiment possible de positionner des éléments sans tenir
compte de l’ordre du flux (DOM). On pouvait toujours utiliser quelques hacks douteux ou
du positionnement absolu, mais ce ne sont pas de véritables solutions à un problème
pourtant commun.

Comment ça marche ?
Le principe de base est simple : en appliquant la déclaration display: flex (ou inline-flex
bien que ce soit beaucoup moins usité) à un élément, celui-ci devient un flex container, ou
conteneur flexible. Cet événement a pour conséquence directe de créer un contexte de
boîte flexible, faisant de tous ses enfants directs des flex items, ou éléments flexibles.
Ceux-ci n’ont pas besoin d’avoir la propriété display définie.
Dans le cas où un conteneur flexible contiendrait du texte qui ne serait pas encapsulé dans
un enfant, celui-ci serait automatiquement encapsulé dans un élément flexible anonyme et
se comporterait donc comme les autres.
Toute mise en page flexible suit deux axes :
• l’axe principal (main axis), sur lequel les éléments se suivent ;
• l’axe secondaire (cross axis), perpendiculaire à l’axe principal.
Figure 3–12
Le modèle Flexbox et la notion d’axes, par le W3C

ATTENTION La notion d’axes


On ne parlera pas d’axe vertical ou horizontal, puisque le module Flexbox est
direction-agnostic, c’est-à-dire qu’il n’est pas contraint à une direction (contrairement
au block layout qui défile verticalement, ou à l’inline layout qui défile
horizontalement). C’est la propriété flex-direction appliquée au conteneur qui définit la
direction et l’axe de défilement.
Par la suite, ce sont les propriétés justify-content et align-items qui déterminent comment les
éléments flexibles se comporteront respectivement sur l’axe principal et l’axe secondaire.
À noter également que les margin sont respectés, notamment la valeur auto qui permet un
centrage horizontal et vertical simple (comme on le verra dans les exemples).
Avant de rentrer dans le vif du sujet, dressons un petit lexique des termes (en plus des
deux que l’on vient de voir) qu’on est susceptible de rencontrer lors de l’utilisation du
module Flexbox.
• main-start et main-end : les éléments flexibles sont placés sur le main axis de main-start
à main-end (pas nécessairement de gauche à droite ni de haut en bas).
• cross-start et cross-end : les éléments flexibles sont placés sur le cross axis de cross-start
à cross-end (pas nécessairement de haut en bas ni de gauche à droite).
• main-size : la largeur ou la hauteur d’un élément (selon la dimension principale définie
par le main axis).
• cross-size : la largeur ou la hauteur d’un élément (selon la dimension secondaire définie
par le cross axis).
Si on résume, un flex container définit un main axis et un cross axis, le deuxième étant
perpendiculaire au premier. Ses enfants directs sont des flex items, ayant une main-size et
une cross-size et étant alignés le long du main axis et du cross axis. Maintenant que nous
avons fait le tour de la terminologie, nous allons pouvoir démarrer !

Syntaxe
Parce que le module Flexbox permet de faire beaucoup de choses, il est relativement
complexe à appréhender. Et pour cause, il n’introduit pas moins d’une douzaine de
nouvelles propriétés, chacune avec son set de valeurs autorisées. Autant de déclarations
qu’il est important de connaître dans le but de maîtriser le positionnement via le module
Flexbox.
Je vous propose de dédier l’intégralité de cette partie à l’explication des propriétés et à
leur syntaxe avant d’attaquer des exemples pratiques et de vrais cas d’usage pour voir
comment utiliser Flexbox dans un projet dès aujourd’hui.

Initialisation avec display: flex | inline-flex


Cette propriété s’applique au conteneur flexible (parent).
display: … | flex | inline-flex

La valeur flex de la propriété display crée un contexte pour le positionnement Flexbox


comme nous l’avons vu dans l’introduction. Selon s’il s’agit de flex ou inline-flex, le
conteneur sera de type inline ou block. Quoi qu’il en soit, tous ses enfants directs sont
considérés comme des flex items, et sont donc susceptibles d’accueillir des propriétés en
conséquence.
REMARQUE Propriétés sans effet
Le module multicolonne que nous avons vu précédemment n’a aucun effet sur un
conteneur flexible, et les déclarations float, clear et vertical-align n’ont aucun effet sur
les éléments flexibles.

Direction générale avec flex-direction


Cette propriété s’applique au conteneur flexible (parent).
flex-direction: row | row-reverse | column | column-reverse

C’est la propriété flex-direction qui va déterminer le sens de défilement pour les éléments
flexibles en définissant le main axis et sa direction.

Figure 3–13
Démonstration des différentes valeurs de la propriété flex-direction

Les quatre valeurs autorisées ont la définition suivante :


• row (par défaut) : de gauche à droite en mode ltr, de droite à gauche en mode rtl ;
• row-reverse : de droite à gauche en mode ltr, de gauche à droite en mode rtl ;
• column : de haut en bas ;
• column-reverse : de bas en haut.

Gestion des retours à la ligne avec flex-wrap


Cette propriété s’applique au conteneur flexible (parent).
flex-wrap: nowrap | wrap | wrap-reverse

Bien que la propriété flex-direction définisse indirectement le cross axis en déterminant


quel est le main axis, il faut donner une direction au cross axis. C’est le but de la propriété
flex-wrap. De plus, cette dernière permet également de déterminer si oui ou non les
éléments sont autorisés à se répartir sur plusieurs lignes.
Par défaut, les éléments flexibles ne provoquent pas de retours à la ligne, c’est-à-dire
qu’ils sont tous sur la même ligne, quitte à déborder du conteneur et à afficher une barre
de défilement. Si on souhaite que les éléments flexibles soient distribués sur plusieurs
lignes, il faut spécifier la propriété flex-wrap (comme son nom l’indique) au conteneur à
une autre valeur que nowrap.
Les trois valeurs autorisées ont le sens suivant :
• nowrap (par défaut) : une seule ligne, de gauche à droite en mode ltr, de droite à gauche
en mode rtl ;
• wrap : multiligne, de gauche à droite en mode ltr, de droite à gauche en mode rtl ;
• wrap-reverse : multiligne, de droite à gauche en mode ltr, de gauche à droite en mode rtl.

Figure 3–14
Démonstration des différentes valeurs de la propriété flex-wrap
Déclaration raccourcie avec flex-flow
Cette propriété s’applique au conteneur flexible (parent).
flex-flow: <'flex-direction'> || <'flex-wrap'>

La propriété flex-flow est un raccourci pour les deux propriétés qu’on vient de voir : flex-
direction et flex-wrap. Sa valeur par défaut est donc row nowrap.

C’est généralement de cette façon qu’on introduit le sens de défilement d’un contexte
flexible puisque les deux propriétés sont complémentaires. Ne serait-ce qu’avec celles-ci
(et donc le raccourci flex-flow), il est possible de disposer une collection d’éléments dans le
sens de lecture souhaité en combinant certaines valeurs. Considérons un sens de lecture
classique en ltr :
• de gauche à droite et de haut en bas : flex-flow: row wrap ;
• de gauche à droite et de bas en haut : flex-flow: row wrap-reverse ;
• de droite à gauche et de haut en bas : flex-flow: row-reverse wrap ;
• de droite à gauche et de bas en haut : flex-flow: row-reverse wrap-reverse.

Justification avec justify-content


Cette propriété s’applique au conteneur flexible (parent).
justify-content: flex-start | flex-end | center | space-between | space-around

Nous avons vu comment définir les directions au sein d’un contexte Flexbox grâce aux
propriétés flex-direction et flex-wrap (ou flex-flow). C’est un excellent début mais ce n’est
pas suffisant. Le module de boîtes flexibles permet également de contrôler la distribution
de l’espace disponible sur chaque ligne. C’est le rôle de la propriété justify-content, qui
ressemble beaucoup à la propriété text-align dans son fonctionnement. Elle détermine
l’alignement des éléments sur l’axe principal à l’aide de ces cinq valeurs :
• flex-start (par défaut) : les éléments sont alignés vers le début de l’axe ;
• flex-end : les éléments sont alignés vers la fin de l’axe ;
• center : les éléments sont centrés sur l’axe ;
• space-between : les éléments sont distribués de manière équitable sur l’axe, de sorte que le
premier élément touche le début de l’axe et que le dernier touche la fin de l’axe
(justification) ;
• space-around : les éléments sont distribués de manière équitable sur l’axe de sorte que tous
les espaces soient identiques à l’exception des bords où un demi-espace est laissé avant
le premier élément et après le dernier.
Figure 3–15
Démonstration des différentes valeurs de la propriété justify-content

Alignement du contenu avec align-items


Cette propriété s’applique au conteneur flexible (parent).
align-items: flex-start | flex-end | center | baseline | stretch

De la même façon que la propriété justify-content définit l’alignement des éléments


flexibles sur le main axis, la propriété align-items fait de même sur le cross axis via ces cinq
valeurs :
• flex-start : les éléments sont alignés vers le début de l’axe ;
• flex-end : les éléments sont alignés vers la fin de l’axe ;
• center : les éléments sont centrés sur l’axe ;
• baseline : les éléments sont alignés de sorte que leurs baselines soient alignées ;
• stretch (par défaut) : les éléments s’étirent pour remplir l’espace restant.
Figure 3–16
Démonstration des différentes valeurs de la propriété align-items

Répartition des lignes avec align-content


Cette propriété s’applique au conteneur flexible (parent).
align-content: flex-start | flex-end | center | space-between | space-around | stretch

Pour terminer sur l’alignement des éléments dans un contexte flexible, il reste la propriété
align-content (à ne pas confondre avec align-items) qui détermine la répartition des lignes sur
l’axe principal dans le cas où il y a de l’espace disponible pouvant être réparti de diverses
façons.
• flex-start : les lignes sont groupées au début du conteneur.
• flex-end : les lignes sont groupées à la fin du conteneur.
• center : les lignes sont centrées dans le conteneur.
• space-between : les lignes sont distribuées de manière équitable de sorte que la première
ligne touche le début du conteneur et que la dernière touche la fin du conteneur
(justification).
• space-around : les lignes sont distribuées de manière équitable de sorte que tous les
espaces soient identiques à l’exception des bords où un demi-espace est laissé avant la
première ligne et après la dernière.
• stretch (par défaut) : les lignes s’étirent pour remplir l’espace restant.
Figure 3–17
Démonstration des différentes valeurs de la propriété align-content

Gestion de l’ordre d’apparition avec order


Nous avons fait le tour des propriétés relatives au module Flexbox applicables au
conteneur, notamment pour déterminer la direction de défilement, la présence ou non de
multiligne, et enfin les alignements et la gestion des espaces disponibles. Jusque-là tout va
bien ? Si tout n’est pas encore limpide, ne paniquez pas ; ça ira mieux avec des exemples.
Nous pouvons maintenant passer aux propriétés réservées aux éléments flexibles, à
commencer par celle permettant de défier l’ordre de la source, j’ai nommé order. Cette
propriété s’applique aux éléments flexibles.
order: <integer>

Elle attend un unique entier, comme pour la propriété z-index, qui détermine l’ordre dans
lequel les éléments flexibles vont être alignés ; les éléments dont l’index est le plus proche
de 0 (la valeur par défaut) seront alignés les premiers. Dans le cas où plusieurs éléments
partageraient le même index, c’est bien l’ordre de la source qui déterminerait lequel vient
le premier.

Accroissement avec flex-grow


Cette propriété s’applique aux éléments flexibles.
flex-grow: <number>

La propriété flex-grow détermine la propension pour un élément à s’étirer dans l’espace


restant. Cette propriété attend donc un nombre (entier ou non) positif comme valeur.
Celle-ci déterminera le facteur d’étirement de l’élément par rapport aux autres éléments
flexibles du conteneur.
La valeur par défaut est de 0, ce qui signifie que dans le cas où il y aurait de l’espace
disponible sur une ligne, les éléments ne s’étirent pas pour le combler. Ils conservent leur
dimension initiale. En revanche, si tous les éléments ont une valeur flex-grow de 1 (la
valeur par défaut étant 0) et qu’un élément a une valeur de 2, alors celui-ci prendra deux
fois plus d’espace (si tant est qu’il y en a) que les autres.

Figure 3–18
La valeur flex-grow permet à un élément de prendre davantage de place que les autres.

Rétrécissement avec flex-shrink


Cette propriété s’applique aux éléments flexibles.
flex-shrink: <number>

De la même façon que flex-grow détermine la capacité pour un élément à s’étirer, la


propriété flex-shrink, au contraire, renseigne la propension pour l’élément à se contracter
vis-à-vis des autres éléments flexibles du conteneur.
La valeur par défaut est de 1, c’est-à-dire que dans la mesure du possible les éléments vont
se rétracter s’ils le doivent pour rentrer dans le conteneur, et tous de la même façon. En
revanche, si on spécifie une valeur plus importante pour un élément, alors celui-ci peut
être amené à rétrécir davantage que les autres.

Dimensions par défaut avec flex-basis


Cette propriété s’applique aux éléments flexibles.
flex-basis: <length> | auto

Pour finir, la propriété flex-basis détermine la dimension par défaut de l’élément avant que
l’espace restant ne soit distribué. La valeur par défaut est auto, comme pour la propriété
width. En effet, lorsque le parser CSS du navigateur doit calculer la dimension des éléments
flexibles, il regarde d’abord la valeur des propriétés flex-*, notamment flex-basis, avant de
regarder width et height.
Dans le cas où la valeur de flex-basis serait à auto, alors c’est la valeur de la main size (width
ou height selon la direction du main axis) qui détermine la dimension de l’élément. Cette
valeur peut d’ailleurs tout à fait être auto également, ce qui impliquerait que l’élément soit
dimensionné par rapport à son contenu.
À l’exception de cette valeur, flex-basis se comporte comme width ou height dans le sens où
une valeur en pourcentage est calculée en fonction de la largeur du conteneur, et si celle-ci
n’est pas définie, alors elle est considérée comme auto.

Déclaration raccourcie avec flex


Cette propriété s’applique aux éléments flexibles.
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

Comme vous pouvez le constater, la propriété flex est un raccourci pour les trois propriétés
qu’on vient d’étudier : flex-grow, flex-shrink et flex-basis. Elle accepte :
• soit la valeur flex-grow uniquement ;
• soit la valeur flex-grow et la valeur flex-shrink ;
• soit la valeur flex-basis uniquement ;
• soit les trois valeurs simultanément.
On peut noter quelques valeurs courantes pour la propriété flex :
• flex: 0 auto : dimensionne les éléments en fonction des valeurs de width et height, les
empêche de s’étirer mais les autorise à se contracter si besoin ;
• flex: initial : identique à flex: 0 auto ;
• flex: auto : dimensionne les éléments en fonction des valeurs de width et height, et les rend
complètement flexibles ;
• flex: none : dimensionne les éléments en fonction des valeurs de width et height, et les rend
absolument inflexibles ;
• flex: <positive-number> : dimensionne les éléments en fonction de l’espace restant dans le
conteneur.

Alignement particulier avec align-self


Cette propriété s’applique aux éléments flexibles.
align-self: auto | flex-start | flex-end | center | baseline | stretch

La propriété align-self, comme son nom l’indique, permet à un élément unique de défier
l’alignement déterminé par la propriété align-items et de suivre son propre alignement. Les
valeurs autorisées sont donc strictement les mêmes que celles de la propriété align-items,
aussi je vous invite à lire la partie concernant cette propriété pour comprendre le sens des
valeurs autorisées.
Bien qu’elle puisse sembler obsolète, il est important d’avoir une propriété permettant
d’aligner un élément dans un état particulier. Il peut arriver que vous vouliez qu’un
élément ressorte et se comporte différemment des autres. C’est grâce à align-self que vous
allez y parvenir.

Cas pratique : un menu responsive


Nous avons enfin fait le tour des propriétés introduites par le module de boîtes flexibles !
Nous sommes fins prêts pour une petite mise en application. Je vous propose de voir
comment réaliser une barre de navigation en trois états avec Flexbox :
• liens alignés à droite sur les plus grands écrans ;
• liens centrés sur les écrans moyens ;
• liens les uns en dessous des autres sur les petits écrans.
Tout d’abord, considérons le markup HTML suivant :
<ul class="navigation" role="navigation">

<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Products</a></li>

<li><a href="#">Contact</a></li>
</ul>

Appliquons quelques styles de base pour nos éléments :


.navigation {
list-style: none;

padding: 0;
margin: 0;
background: deepskyblue;

}
.navigation a {
display: block;

padding: 1em;

text-decoration: none;
text-align: center;

Maintenant, il ne nous reste plus qu’à appliquer nos propriétés relatives à Flexbox.
Référez-vous aux commentaires pour bien comprendre ce qu’il se passe :
/**
* 1. On définit un contexte Flexbox sur le parent.

* 2. On définit le sens de gauche à droite

* == flex-direction: row et flex-wrap: wrap


* 3. On aligne les éléments à droite.

* 4. Sur écrans moyens, on centre les éléments.

* 5. Sur petits écrans, on redéfinit le sens de haut en bas

* == flex-direction: column et flex-wrap: wrap


*/

/* Grands écrans */
.navigation {

display: flex; /* 1 */
flex-flow: row wrap; /* 2 */

justify-content: flex-end; /* 3 */
}
/* Écrans moyens */

@media (max-width: 800px) {

.navigation {
justify-content: center; /* 4 */

}
/* Petits écrans */

@media (max-width: 450px) {


.navigation {

flex-flow: column wrap; /* 5 */


}
}

Et voilà ! Le reste n’est que de l’habillage pour satisfaire l’esthétique. Toutes les questions
de layout sont gérées par Flexbox uniquement. Cependant, cet exemple est relativement
simple puisqu’il ne faisait usage que des propriétés applicables au conteneur.

Figure 3–19
Notre menu sur un écran de grande taille

Figure 3–20
Notre menu sur un écran de taille moyenne

Figure 3–21
Notre menu sur un écran de petite taille

Cas pratique : un layout mobile first


Prenons un autre exemple pour s’entraîner à utiliser les propriétés applicables aux
éléments flexibles.
Considérons le markup HTML suivant pour un layout mobile first qui s’adapte pour finir
en trois colonnes sur les écrans les plus larges :
<div class="wrapper">

<header class="header">

<!-- Contenu de l’en-tête -->


</header>

<article class="main">
<!-- Contenu de l’article -->

</article>
<aside class="aside aside-1">
<!-- Contenu de la première barre latérale -->

</aside>

<aside class="aside aside-2">


<!-- Contenu de la seconde barre latérale -->

</aside>

<footer class="footer">
<!-- Contenu du pied de page -->

</footer>
</div>

Pour garder l’exemple le plus simple possible, on se passera des styles par défaut et
purement destinés à l’esthétique. Démarrons immédiatement avec la définition du contexte
flexible :
.wrapper {
display: flex;

flex-flow: row wrap;


}
.wrapper > * {

flex: 100%;
}

La règle flex: 100% définit à tous les enfants directs du conteneur une largeur de 100 % afin
qu’ils occupent toute la largeur offerte par la fenêtre. Ce comportement permet de servir
les éléments les uns en dessous des autres (dans l’ordre du flux) sur les plus petits écrans.

Figure 3–22
Notre layout sur un écran de petite taille

Nous allons l’adapter quand le viewport est suffisamment grand pour permettre un
changement de layout :
@media (min-width: 600px) {

.aside {
flex: 1 auto;

}
}

Cette règle fait en sorte que les deux éléments .aside partagent une ligne, de manière
équitable. On a donc le rendu suivant.

Figure 3–23
Notre layout sur un écran de taille moyenne

Pour finir, traitons du cas où l’écran est suffisamment large pour permettre aux barres
latérales d’encercler le conteneur principal.
@media (min-width: 800px) {

.main {
flex: 3 0px;
}

.aside-1 {
order: 1;
}

.main {
order: 2;

.aside-2 {
order: 3;

.footer {
order: 4;

On spécifie à l’élément .main qu’il doit prendre trois fois plus d’espace sur la ligne que les
barres latérales (pour lesquelles la valeur est 1). Ensuite, on utilise la propriété order pour
définir l’ordre de rendu des éléments. On affiche les éléments dans l’ordre voulu sans
modifier le DOM, respectant ainsi une structure sémantique propre.
Figure 3–24
Notre layout sur un écran de grande taille

Cas pratique : centrage absolu


Nous l’avons vu dans l’introduction de cette section, Flexbox a entre autres pour objectif
de faciliter l’alignement des éléments les uns avec les autres. Du coup, si le support de
navigateur le permet, on peut utiliser Flexbox pour centrer un élément au sein d’un autre
avec un avantage majeur sur d’autres techniques plus farfelues : inutile de connaître les
dimensions du parent ni de l’élément centré !
C’est sans compter la simplicité enfantine de la solution :
/**
* Parent de l’élément à centrer

* 1. Établissement d’un contexte flexible


* 2. Centrage sur l’axe principal
* 3. Centrage sur l’axe horizontal

*/
.parent {
display: flex; /* 1 */

justify-content: center; /* 2 */
align-items: center; /* 3 */
}

Emballé, c’est pesé !

Cas pratique : un formulaire fluide


Récemment, j’ai eu à développer un formulaire de recherche horizontal avec le libellé
(label) d’un côté, et le bouton de soumission (button) de l’autre. Le champ de recherche,
situé entre les deux, devait être fluide de sorte qu’au redimensionnement du formulaire ou
au changement de texte du libellé ou du bouton, le formulaire reste intact (comprendre
« n’explose pas sur plusieurs lignes »).
Il s’avère que c’est un excellent cas d’usage pour Flexbox. En réalité, c’est typiquement le
genre de problématique que le module essaye de résoudre. Et vous allez voir qu’il le fait à
merveille.
Mais commençons par créer notre formulaire, voulez-vous ?
<form action="…">
<label for="search">Recherche</label>

<input type="text" id="search" />


<button type="submit">Go !</button>

</form>

A priori, nous n’avons pas besoin de davantage de markup pour un simple formulaire de
recherche. Il ne nous reste plus qu’à appliquer le CSS qui ne va pas être bien plus long :
/**

* Formulaire

* 1. Établissement d’un contexte flexible

*/
form {

display: flex; /* 1 */

margin: 1em;
padding: 1em;
border: 1px solid silver;

}
/**

* Champ de recherche
* 1. Occupe l’espace restant
* 2. Gouttières de chaque côté du champ

*/
input {
flex: 1; /* 1 */

margin: 0 1em; /* 2 */
}

Terminé ! Aucune dimension fixe n’a été assignée ; on peut donc tout à fait modifier
l’intitulé du libellé ou du bouton, et la taille du champ s’adaptera automatiquement. Merci
Flexbox !

Figure 3–25
Formulaire sur une seule ligne sans dimensions fixes grâce à Flexbox

Compatibilité des navigateurs pour Flexbox


Tableau 3–5 Navigateurs desktop
Tableau 3–6 Navigateurs mobiles

Comme on peut le constater, le support est très encourageant ! En revanche, il y a un


certain nombre de choses à prendre en compte lors de l’utilisation de Flexbox dans le but
de maximiser le support des navigateurs.
• Firefox 19 et iOS 6/Safari 3.1 à 6 supportent la première version des spécifications
(2009), appelée display: box, sous les préfixes respectifs -moz- et -webkit-.
• Internet Explorer 10 supporte une version hybride (2011) de la syntaxe, appelée display:
flexbox, sous le préfixe -ms-.

• Firefox 27- ne supporte pas flex-wrap et flex-flow.


Flexbox est donc un module délicat à utiliser car il a connu trois versions différentes, qui
ont nécessairement impliqué le renommage de certaines propriétés et valeurs, à
commencer par la valeur principale box, flexbox, puis enfin flex. L’indécision est grande.
Voici un tableau récapitulatif des différents états de la syntaxe des spécifications Flexbox.
Tableau 3–7 Évolution de la syntaxe de Flexbox

Nouvelle syntaxe officielle Syntaxe hybride de 2011 Vieille syntaxe de 2009


display: flex display: box display: flexbox
flex-direction: row box-orient: horizontal box-orient: horizontal
justify-content: flex-start box-pack: start box-pack: start
align-items: flex-start box-align: start box-align: start
flex: 1 flex: 1 box-flex: 1

Mettre en place une solution pour tous les navigateurs


Parce que le module Flexbox a connu diverses syntaxes qui ont été implémentées par
certains navigateurs, mettre en place une solution pour tous les navigateurs n’est pas chose
aisée. On peut vouloir s’appuyer sur la détection par JavaScript via la bibliothèque
Modernizr qui fait un travail honorable puisqu’elle n’applique non pas une mais deux
classes pour Flexbox : flexbox ou no-flexbox (standard) et flexboxlegacy ou no-flexboxlegacy
(2009).
En revanche, Modernizr considère qu’Internet Explorer 10 supporte Flexbox alors que ce
navigateur supporte la version hybride de 2011 qui n’est ni la vieille syntaxe (legacy), ni la
nouvelle syntaxe standardisée. Fort heureusement, il est possible de mettre en place des
solutions de repli pour Internet Explorer 10 en spécifiant les anciennes propriétés avant les
nouvelles.
Pour résumer, notre code va se construire de la façon suivante.
• La version hybride pour Internet Explorer 10, suivie des versions préfixées, suivie de la
version standard pour les navigateurs supportant Flexbox.
• Du code approprié dans .flexboxlegacy.no-flexbox pour les navigateurs supportant la
syntaxe legacy.
• Du code de repli dans .no-flexboxlegacy.no-flexbox pour les navigateurs ne supportant pas
Flexbox.
RESSOURCE Autoprefixer, la meilleure solution
Une solution encore plus fiable serait de s’appuyer sur la bibliothèque Autoprefixer,
qui se charge d’analyser toutes vos feuilles de styles et d’y ajouter les éventuels
préfixes nécessaires basés sur la configuration imposée. Autoprefixer s’appuyant sur
les bases de données du site référence CanIUse.com, elle effectue un travail
remarquable, retirant ainsi toute la pression d’utiliser Flexbox dès aujourd’hui.
http://bit.ly/css-autoprefixer

Un préprocesseur pour simplifier les préfixes (Sass)


Si Autoprefixer n’est pas une option pour vous et que vous utilisez un préprocesseur, vous
pouvez mettre en place des mixins pour faciliter la gestion des préfixes constructeurs. Ci-
dessous, un exemple avec Sass :
/**

* 1. Safari 3.1-6 et iOS 6-

* 2. Firefox 21-
* 3. Internet Explorer 10

* 4. Safari et iOS 7 et Chrome 28-

* 5. Chrome 29+, Firefox 22+ et Opera 12.1+ (standard)


*/

@mixin flexbox {

display: -webkit-box; /* 1 */
display: -moz-box; /* 2 */

display: -ms-flexbox; /* 3 */

display: -webkit-flex; /* 4 */
display: flex; /* 5 */

}
@mixin flex($values) {

-webkit-box-flex: $values; /* 1 */
-moz-box-flex: $values; /* 2 */

-ms-flex: $values; /* 3 */
-webkit-flex: $values; /* 4 */
flex: $values; /* 5 */

@mixin order($val) {
-webkit-box-ordinal-group: $val; /* 1 */

-moz-box-ordinal-group: $val; /* 2 */

-ms-flex-order: $val; /* 3 */
-webkit-order: $val; /* 4 */

order: $val; /* 5 */
}

Utilisés comme suit :


/* Conteneur flexible */

.container {
@include flexbox;
}

/* Éléments flexibles */
.item {
@include flex(1 200px);

@include order(2);
}
Le Grid Layout
Dans la section précédente, nous avons eu un bel aperçu de ce que l’avenir proche nous
réserve en matière de mise en page CSS avec Flexbox mais ce n’est pas tout ! Il existe
également un module dédié à la création de grilles natives, appelé « CSS Grid Layout
Module ».
L’idée principale derrière ce nom intrigant est de définir un élément comme une grille
avec des colonnes et des rangées, un peu à la manière d’un document Excel si on veut
schématiser. Ensuite, les enfants de cet élément pourront être distribués dans les cellules
de cette grille, et pourquoi pas s’étendre sur plusieurs cellules si besoin.
Ce schéma, au-delà d’être plus simple à appréhender qu’un système de float ou inline-
block, permet surtout de dissocier complètement le contenu des styles. En effet, l’atout
majeur du Grid Layout est qu’il rend la mise en page intégralement indépendante de la
structure du document et de l’ordre de la source.
CLARIFICATION Grid ou Flexbox ?
On est tout à fait en droit de se demander quel est l’intérêt d’avoir à la fois Flexbox et
Grid. Après tout, les deux modules peuvent être utilisés dans des cas plus ou moins
similaires. En réalité, ils n’ont pas été prévus pour les mêmes tâches. Le Grid Layout a
été explicitement inventé pour permettre aux designers et développeurs de mettre en
page des documents de manière totalement indépendante de la structure HTML.
De son côté, le module Flexbox est plus adapté pour gérer le comportement de
composants plus petits. C’est l’outil idéal pour la gestion de l’espacement et de
l’alignement des éléments au sein d’un conteneur. Autrement dit, de manière optimale,
on utiliserait Grid pour la mise en page globale et Flexbox pour le comportement des
modules.
En résumé, et pour reprendre les termes de Tab Atkins Jr., auteur des spécifications
des deux modules : Flexbox est prévu pour les layouts sur une dimension, c’est-à-dire
ceux dont l’objectif est d’aligner les éléments sur une ligne (possiblement divisée en
plusieurs lignes si l’espace manque). Grid, en revanche, est prévu pour les layouts à
deux dimensions.

Comment ça marche ?
Pour faire simple, une grid est un tableau dont la structure est intégralement gérée par le
CSS.
Lors de l’utilisation du Grid Layout, le contenu d’un grid container, autrement dit un
élément auquel est appliqué la déclaration display: grid, est disposé selon une grille fictive.
Cette grille est le résultat de l’intersection de lignes (techniquement appelées grid lines)
qui divisent le conteneur en cellules (grid cells) qui pourront accueillir les enfants de
celui-ci.
Figure 3–26
Une ligne servant à la division de la grille en lignes et en colonnes

Figure 3–27
Une rangée de la grille

Pour mettre en place une grille, on affuble donc un élément de la valeur grid (ou inline-grid
en fait) pour la propriété display, puis on définit un zoning.
Celui-ci peut être déclaré de différentes façons : en nommant les zones, ce qui est
généralement le plus simple, mais on ne connaît pas toujours toutes les zones à l’avance
(grille flexible, contenu dynamique, etc.).
Du coup, on peut également spécifier les dimensions des rangées et des colonnes (là
encore, de bien des manières, comme nous le verrons plus loin). On peut aussi mixer les
deux en nommant les lignes cette fois (pas les zones) tout en renseignant les dimensions
des rangées et des colonnes.
Quoi qu’il en soit, une fois la grille déclarée, on peut placer les enfants dans les cellules
générées par l’intersection des rangées et des colonnes (elles-mêmes résultant de
l’intersection des lignes).

Figure 3–28
Une cellule de la grille, créée par l’intersection des lignes

Figure 3–29
Une zone de la grille, (éventuellement) composée de plusieurs cellules

Le Grid Layout est très complet : il implémente une pléthore de nouveaux concepts et
intègre de nombreuses propriétés, certaines s’appliquant au conteneur, d’autres aux
enfants, ainsi qu’une nouvelle unité de mesure (fr).
Pour bien comprendre les tenants et aboutissants du Grid Layout, il est important de voir
toutes ces propriétés ainsi que leur fonctionnement, mais avant même de s’y atteler,
permettez-moi de vous proposer un petit exemple.

Une introduction par l’exemple


Dans le but d’illustrer le module de grille, nous allons prendre un exemple extrêmement
réducteur : une grille de morpion. Vous vous souvenez sûrement tous de vos longues
heures de cours passées à vous battre avec votre voisin de table au morpion.
Une grille de morpion est constituée de trois colonnes et trois rangées. Initialisons ça pour
commencer.
/**

* 1. Dimensions

* 2. Contexte de grille
* 3. Rangées (partage de l’espace en 3)

* 4. Colonnes (partage de l’espace en 3)

*/

.morpion {
width: 300px; /* 1 */
height: 300px; /* 1 */

display: grid; /* 2 */
grid-template-rows: 1fr 1fr 1fr; /* 3 */
grid-template-columns: 1fr 1fr 1fr; /* 4 */

Si vous ne comprenez pas encore la valeur 1fr, c’est tout à fait normal ; nous la verrons
plus en détail un peu plus loin. Sachez simplement qu’il s’agit d’une fraction de l’espace
restant. Dans notre cas, ça signifie simplement que toutes les cellules seront de même
taille.
Pour l’instant, nous n’avons fait qu’initialiser une grille de 3 par 3 cellules mesurant
chacune 100 pixels de côté (un tiers de 300 pixels). En revanche, nous n’avons pas placé
les véritables cellules (enfants de .morpion) dans la grille. C’est justement notre prochaine
étape.
/**
* Première rangée

* 1. Première colonne

* 2. Deuxième colonne
* 3. Troisième colonne

*/

.cellule-1-1 { grid-area: 1 / 1; } /* 1 */
.cellule-1-2 { grid-area: 1 / 2; } /* 2 */

.cellule-1-3 { grid-area: 1 / 3; } /* 3 */

/**

* Deuxième rangée
* 1. Première colonne

* 2. Deuxième colonne

* 3. Troisième colonne
*/

.cellule-2-1 { grid-area: 2 / 1; } /* 1 */

.cellule-2-2 { grid-area: 2 / 2; } /* 2 */
.cellule-2-3 { grid-area: 2 / 3; } /* 3 */

/**
* Troisième rangée

* 1. Première colonne
* 2. Deuxième colonne
* 3. Troisième colonne

*/

.cellule-3-1 { grid-area: 3 / 1; } /* 1 */
.cellule-3-2 { grid-area: 3 / 2; } /* 2 */

.cellule-3-3 { grid-area: 3 / 3; } /* 3 */

À nouveau, si la syntaxe vous semble encore obscure, pas d’inquiétude, c’est tout à fait
normal. Nous avons tout le temps d’approfondir tout ça.

Figure 3–30
Notre grille de morpion construite grâce à Grid Layout

Avant de passer à la suite, voyons une façon différente d’aborder notre mise en page, en
nommant les zones cette fois :
/**
* 1. Dimensions
* 2. Contexte de grille

* 3. Rangées (partage de l’espace en 3)


* 4. Colonnes (partage de l’espace en 3)
* 5. Nommage des 9 zones

*/

.morpion {
width: 300px; /* 1 */

height: 300px; /* 1 */

display: grid; /* 2 */
grid-template-rows: 1fr 1fr 1fr; /* 3 */

grid-template-columns: 1fr 1fr 1fr; /* 4 */

grid-template-areas:
"top-left top top-right"

"left center right"

"bottom-left bottom bottom-right"; /* 5 */


}

/**

* Première rangée
* 1. Coin supérieur gauche

* 2. Milieu en haut
* 3. Coin supérieur droit

*/
.cellule-1-1 { grid-area: top-left; } /* 1 */

.cellule-1-2 { grid-area: top; } /* 2 */


.cellule-1-3 { grid-area: top-right; } /* 3 */
/**

* Deuxième rangée

* 1. Milieu à gauche
* 2. Milieu

* 3. Milieu à droite

*/
.cellule-2-1 { grid-area: left; } /* 1 */

.cellule-2-2 { grid-area: center; } /* 2 */


.cellule-2-3 { grid-area: right; } /* 3 */

/**
* Troisième rangée
* 1. Coin inférieur gauche

* 2. Milieu en bas
* 3. Coin inférieur droit
*/

.cellule-3-1 { grid-area: bottom-left; } /* 1 */


.cellule-3-2 { grid-area: bottom; } /* 2 */
.cellule-3-3 { grid-area: bottom-right; } /* 3 */

Le résultat est strictement le même que pour l’exemple précédent, mais la syntaxe est plus
élégante comme ceci.
Et voilà ! Nous venons d’utiliser Grid Layout pour la première fois ; pas si compliqué
finalement. Notre exemple était volontairement enfantin, mais il a le mérite de montrer les
rudiments du module. Maintenant que vous êtes opérationnels, nous allons pouvoir passer
aux choses sérieuses !
RESSOURCE Grid by Example
Rachel Andrew, réputée pour ses prouesses avec Grid Layout depuis son apparition,
est l’auteur du projet Grid by Example dont le but est de sensibiliser les développeurs
à ce nouveau système de mise en page, et de l’expliquer à l’aide d’exemples concrets.
Évidemment, je ne peux que vous recommander ce projet.
http://bit.ly/grid-by-example
Figure 3–31 Le projet Grid by Example de Rachel Andrew

Initialiser une grille


display: … | grid | inline-grid

Cette propriété s’applique bien évidemment au conteneur. Elle est la base du module
puisqu’elle initialise un contexte de grille. Attention toutefois, les conteneurs de grille sont
légèrement différents de conteneurs blocs classiques, en cela que :
• les propriétés du module multicolonne n’ont aucun effet sur le conteneur ;
• les propriétés clear, float et vertical-align n’ont aucun effet sur les enfants ;
• les pseudo-éléments ::first-letter et ::first-line ne s’appliquent pas au conteneur.
Vous l’aurez compris, il est tout à fait possible d’instancier une grille en ligne via la valeur
inline-grid. Cependant, si l’élément est affublé de la propriété float, ou qu’il est positionné
de manière absolue, la valeur traitée pour la propriété display ne serait pas inline-grid mais
bien grid. Ne nous perdons pas inutilement dans des détails et tâchons d’avancer.

Définir la grille
Avant d’initialiser la grille, il faut la définir. Nous l’avons vu en introduction, il y a bien
des manières de procéder. Nous n’allons toutefois pas commencer par la plus simple.
Pour définir une grille, il est possible de spécifier ses dimensions en termes de colonnes et
de rangées. Pour cela, il y a deux propriétés principales :
• grid-template-rows ;
• grid-template-columns.
Ensemble, ces propriétés définissent la grille dite « explicite » du conteneur. À noter qu’il
existe également la propriété grid-template qui n’est qu’un raccourci pour définir ces
propriétés (ainsi que grid-template-areas qu’on verra plus loin) simultanément.
Commençons par les choses les plus simples : définir le nombre de colonnes et le nombre
de rangées dans notre grille. Je vous passe la syntaxe officielle qui est assez imbuvable, et
je résume.
Les propriétés grid-template-rows et grid-template-columns acceptent une liste de valeurs
séparées par des espaces correspondant à d’éventuels identifiants de colonnes/rangées
entourés de parenthèses (voir la section « Nommage », page 95) et à des valeurs de
dimensionnement exprimées :
• en longueurs fixes (42px, 13.37pt…) ;
• en longueurs relatives (42vw, 13.37em…) ;
• en pourcentage ;
• via la fonction calc() (voir chapitre 5) ;
• via la fonction repeat() (voir section suivante) ;
• via la fonction minmax() ;
• en valeurs prédéfinies (min-content, max-content ou auto, qui n’est autre qu’un alias pour
minmax(min-content, max-content)) ;

• via la nouvelle unité fr.


Pour illustrer tout cela, prenons un exemple. Considérons la déclaration suivante :
grid-template-columns: 100px max-content 50%;

Parce que la valeur est constituée d’une liste de trois valeurs séparées par des espaces, cela
signifie que la grille sera composée de trois colonnes :
• une première colonne de 100 pixels de large ;
• une deuxième colonne qui occupera suffisamment d’espace pour que son contenu le plus
large apparaisse sur une même ligne ;
• une dernière colonne qui occupera la moitié de la largeur de l’élément.

Figure 3–32
grid-template-columns: 100px max-content 50%;

Comme vous pouvez le voir, on peut mixer les unités fixes avec les unités relatives et
laisser soin au navigateur de faire fonctionner tout cela. Plutôt pratique n’est-ce pas ?

Simplifier les définitions avec la fonction repeat()


La fonction repeat() n’a rien de magique et permet simplement de s’épargner des
répétitions de code fastidieuses dans le cas de grilles un peu larges (aussi bien
verticalement qu’horizontalement). En effet, son premier paramètre spécifie combien de
fois doit être répété son second paramètre.
Un autre exemple impliquant la fonction repeat() pourrait être :
grid-template-columns: repeat(3, 1em 100px) 1em;

Figure 3–33
Les zones de 1 em sont réservées aux marges, et les zones de 100 pixels au contenu.

Dans notre exemple, la fonction répète trois fois 1em 100px, suivi d’une dernière colonne à
1em, aussi la déclaration aurait pu s’écrire ainsi :

grid-template-columns: 1em 100px 1em 100px 1em 100px 1em;

Autrement dit, la fonction repeat() est tout à fait optionnelle et n’est là que pour alléger un
peu l’écriture, mais aussi la lecture du code. N’hésitez pas à l’utiliser quand vous en avez
l’occasion !

Fractions de l’espace restant avec l’unité fr


Cette nouvelle unité (référée comme <flex> dans les syntaxes officielles), a été introduite
spécialement pour le Grid Layout. Elle correspond à une fraction de l’espace restant dans
la ligne ou la colonne après le calcul des longueurs non flexibles.
En d’autres termes, le navigateur commence par calculer l’espace occupé par les
dimensions non flexibles. Ensuite, il distribue l’espace restant selon les différentes valeurs
utilisant l’unité fr. Le calcul de dimensionnement d’une ligne ou colonne dont la
largeur/hauteur est spécifiée en fr est donc : <flex> * <espace restant> / <somme des flex>.

Nommage

Nommer les lignes


Comme nous l’avons vu dans la section précédente, il est possible de nommer les lignes
qui définissent les rangées et les colonnes au sein même de la syntaxe de grid-template-rows
et grid-template-columns. Cela donne davantage de sens à la grille et facilite la maintenance.
Pour nommer une ligne, rien de plus simple : il suffit de la dénoter par un identifiant (pas
nécessairement unique) entouré de parenthèses. Il est même possible de donner plusieurs
noms à une même ligne, en les séparant par des espaces.
.grid {

display: grid;
grid-template-columns: (first nav) 200px (main) 1fr (last);

grid-template-rows: (first header) 50px (main) 1fr (footer) 50px (last);


}

Figure 3–34
La déclaration de grille précédente génère ce layout.

Dans cette grille de deux colonnes sur trois rangées (deux longueurs dans la valeur de grid-
template-columns et trois longueurs dans la valeur de grid-template-rows) :

• la première ligne verticale peut être référée comme first, nav ou encore 1 ;
• la deuxième ligne verticale par main ou 2 ;
• la dernière ligne verticale par last ou 3 ;
• la première ligne horizontale par first, header ou 1 ;
• la deuxième ligne horizontale par main ou 2 ;
• la troisième ligne horizontale par footer ou 3 ;
• la dernière ligne horizontale par last ou 4.
ATTENTION Lignes nommées et Sass
J’ai réalisé que Sass retire les parenthèses autour des noms de ligne au moment de la
compilation (dans la mesure où les parenthèses délimitent une liste de valeurs et sont
optionnelles en Sass).
Il y a bien des manières de contourner le souci, entre autres celle consistant à
interpoler les valeurs (ou utiliser la fonction unquote) :
.grid {
display: grid;
grid-template-columns:
#{"(first nav)"} 150px

#{"(main)"} 1fr

#{"(last)"};

grid-template-rows:
#{"(first header)"} 50px

#{"(main)"} 1fr

#{"(footer)"} 50px
#{"(last)"};

}
Assez indigeste, je vous l’accorde. Le mieux reste de passer par un mixin pour éviter
de devoir gérer ça :
http://bit.ly/grid-sass-fix
Nommer les zones
Il est également possible de nommer directement les zones de la grille pour lui donner
davantage de sens. Attention, ce ne sont pas les éléments que l’on nomme, mais bien les
zones de la grille. Par la suite, ces éléments sont insérés dans les zones via leur nom.
C’est la propriété grid-template-areas qui permet de nommer les différentes sections d’une
grille, à la différence des propriétés grid-template-rows et grid-template-columns qui nomment
les lignes qui séparent les rangées et colonnes.
grid-template-areas: none | <string>+

Chaque chaîne de caractères (encapsulée dans des guillemets simples ou doubles) présente
dans la valeur de grid-template-areas correspond à une rangée. Ces chaînes sont ensuite
parsées pour déterminer les colonnes selon les règles suivantes :
• une séquence de caractères représente une cellule nommée selon ladite séquence ;
• une séquence d’espaces/tabulations ne produit rien ;
• un point (.) représente une cellule anonyme.
REMARQUE Longueurs identiques
Toutes les chaînes doivent avoir le même nombre de tokens. Aussi, dans le cas où on
souhaite faire en sorte qu’une zone nommée s’étende sur plusieurs colonnes
consécutives, il faut répéter le nom de cette zone pour chaque colonne.
Reprenons notre exemple précédent (deux colonnes, trois rangées) en nommant les zones :
grid-template-areas: "header header"
"nav main"

"footer footer";

Parce que les zones header et footer sont répétées dans leur chaîne respective, elles
s’étendent toutes deux sur toute la longueur de la grille.
Si on souhaite ajouter à ces zones des dimensions, on utilise les propriétés grid-templaterows
et grid-template-columns :
grid-template-columns: 150px 1fr;

grid-template-rows: 50px 1fr 50px;

En somme, on obtient un layout tel que :


• la zone header : 100% × 50px ;
• la zone sidebar : 150px × 1fr ;
• la zone main : 1fr × 1fr ;
• la zone footer : 100% × 50px.
Cette façon de faire est définitivement la plus intuitive, en tout cas pour ce qui est de la
mise en page d’un document dans son ensemble, aussi je vous recommande de l’employer.
Ceci étant dit, pour certaines utilisations, on ne sera pas en mesure de nommer les lignes
(parce qu’il y en a trop, parce qu’on ne connaît pas leur nombre, etc.), auquel cas on devra
employer la première méthode qu’on a vue : les valeurs directement.
Placer les éléments
Une fois la grille initialisée et définie en termes de rangées et de colonnes (et
éventuellement de zones), il faut placer les enfants du conteneur dans les cellules fictives
générées.
Le placement d’un élément dans sa grille consiste en deux notions :
• la position de l’élément dans la grille (grid position), renseignée via la ligne de début ou
la ligne de fin (ou les deux) ;
• le nombre d’emplacements occupés par cet élément (grid span), renseigné par le mot-clé
span (le comportement par défaut étant bien évidemment qu’un élément n’occupe qu’un
emplacement).
Les propriétés de positionnement (grid-row-start, grid-row-end, grid-column-start, grid-column-
end, et leurs raccourcis grid-row, grid-column et grid-area) permettent de spécifier le placement
d’un élément dans la grille en définissant une rangée et une colonne.
Tableau 3–8 Informations nécessaires au placement des éléments dans la grille

Rangée Colonne
Début Ligne horizontale de début Ligne verticale de début
Fin Ligne horizontale de fin Ligne verticale de fin
Envahissement Envahissement des rangées Envahissement des colonnes

Si au moins deux valeurs parmi début, fin et envahissement sont renseignées (ou
calculées), alors la troisième est considérée comme définie également (calculée). En effet,
si on connaît :
• la ligne de début et la ligne de fin, on peut calculer le nombre d’emplacements occupés
par l’élément ;
• la ligne de début et le nombre d’emplacements occupés par l’élément, on peut calculer
la ligne de fin ;
• la ligne de fin et le nombre d’emplacements occupés par l’élément, on peut calculer la
ligne de début.
Le tableau suivant résume les conditions selon lesquelles le placement d’un élément est
considéré comme défini ou automatique.
Tableau 3–9 Conditions selon lesquelles un emplacement/une étendue est défini automatiquement ou non

Position Envahissement
Envahissement explicite, implicite ou par
Défini Au moins une ligne spécifiée défaut

Subgrid sans envahissement explicite ou


Automatique Aucune ligne explicitement spécifiée
implicite

Le placement peut être renseigné de diverses façons : soit en termes d’index, soit avec des
identifiants. Quoi qu’il en soit, ce sont les propriétés grid-row-start, grid-row-end, grid-column-
start et grid-column-end (ou les raccourcis grid-column, grid-row et grid-area) qui le permettent.
Vous êtes perdu ? Ne vous en faites pas, tout va bientôt devenir limpide !

Le placement avec grid-row-start, grid-row-end, grid-


column-start et grid-column-end
Ces quatre propriétés déterminent la position et la dimension d’un élément dans la grille
en assignant une rangée/colonne, et éventuellement un envahissement (le nombre
d’emplacements occupés, span).
grid-row-start: <grid-line>

grid-row-end: <grid-line>
grid-column-start: <grid-line>

grid-column-end: <grid-line>

où:
<grid-line> =
auto |
<custom-ident> |

[ <integer> && <custom-ident>? ] |


[ span && [ <integer> || <custom-ident> ] ]

Comme vous pouvez le constater, il y a plusieurs manières de renseigner ces propriétés.


La notion de <custom-ident> réfère à un identifiant personnalisé comme nous l’avons vu dans
la section « Nommer les lignes », page 95. C’est par ce moyen qu’il est possible de
positionner les éléments dans la grille via les identifiants donnés aux lignes.
Il est également possible de renseigner un entier (différent de 0), qui a pour sens le numéro
de la ligne dans la grille. Par exemple, une valeur de 2 pour grid-row-start ciblera la 2e ligne
de la grille (et donc la deuxième rangée). Si l’entier est négatif, en revanche, le décompte
se fait à l’envers, depuis la dernière ligne de la grille.
Si un identifiant est joint à ce nombre, seules les lignes appelées comme ceci seront
comptées. S’il n’y a pas suffisamment de lignes appelées ainsi, cela ciblera la dernière
ligne de ce nom dans le cas d’un entier positif, ou la première dans le cas d’un entier
négatif.
La valeur span permet de faire en sorte qu’un élément s’étende sur plusieurs cellules à la
fois. Par exemple, une valeur de span 2 pour grid-row-end fera en sorte que l’élément
s’étende sur deux rangées successives, à partir de la rangée définie par grid-row-start.
Et enfin, si la valeur est auto, la propriété ne contribue en rien au placement de l’élément
dans la grille.
Pour parfaire les explications, voici quelques exemples des cas que nous venons de voir.
Considérons une grille d’une seule ligne, composée de neuf colonnes. Pour plus de
simplicité, donnons-leur des noms :
1 2 3 4 5 6 7 8 9

+--+--+--+--+--+--+--+--+
| | | | | | | | |
A B C A B C A B C

| | | | | | | | |
+--+--+--+--+--+--+--+--+

Et maintenant, quelques exemples pour s’assurer que vous avez tout compris :
/* Démarre à la ligne 4

* N’occupe qu’un seul emplacement (défaut)


*

* Résultat : entre les lignes 4 et 5

*/
grid-column-start: 4;

grid-column-end: auto;

/* Termine à la ligne 6
* N’occupe qu’un seul emplacement (défaut)

*
* Résultat : entre les lignes 5 et 6
*/

grid-column-start: auto;
grid-column-end: 6;
/* Démarre à la première ligne C

* Termine à la dernière ligne C


*
* Résultat : entre les lignes 3 et 9

*/
grid-column-start: C;
grid-column-end: C -1;

/* Démarre à la première ligne C


* S’étend jusqu’à la prochaine ligne C
*

* Résultat : entre les lignes 3 et 6

*/
grid-column-start: C;

grid-column-end: span C;

/* Termine à la dernière ligne C


* S’étend depuis la ligne C précédente

* Résultat : entre les lignes 6 et 9


*/

grid-column-start: span C;

grid-column-end: C -1;
/* Démarre à la ligne 5

* Termine à la dernière ligne C

*
* Résultat : entre les lignes 5 et 9

*/
grid-column-start: 5;

grid-column-end: C -1;
/* Démarre à la ligne 5

* S’étend jusqu’à la prochaine ligne C


*
* Résultat : entre les lignes 5 et 6

*/

grid-column-start: 5;
grid-column-end: span C;

/* Démarre à la deuxième ligne B

* S’étend sur un emplacement


*

* Résultat : entre les lignes 5 et 6


*/

grid-column-start: B 2;
grid-column-end: span 1;
/* Démarre à la ligne 8

* Termine à la ligne 8
* -> end doit être supérieur à start, donc auto
*

* Résultat : entre les lignes 8 et 9


*/
grid-column-start: 8;

grid-column-end: 8;

Le positionnement simplifié avec grid-row et grid-column


Pour ne pas avoir à spécifier les quatre propriétés vues précédemment pour chaque
élément, il existe les deux propriétés raccourcies : grid-row et grid-column.
grid-row: <grid-line> [ / <grid-line> ]?

grid-column: <grid-line> [ / <grid-line> ]?

Leur syntaxe est simple : elle accepte une ou deux valeurs. Dans le cas où une seule valeur
est renseignée, celle-ci est appliquée pour start et end simultanément (si ce sont des
identifiants, sinon auto). Si deux valeurs séparées par un slash (/) sont renseignées, alors
elles seront respectivement appliquées à start et à end.
On peut donc réécrire la déclaration suivante :
grid-column-start: 4;
grid-column-end: 8;

/* … ou plus simplement */

grid-column: 4 / 8;

Le positionnement encore plus simplifié avec grid-area


La propriété grid-area – au-delà de pouvoir placer un élément dans une zone nommée de la
grille en renseignant son identifiant – permet aussi de simplifier davantage le placement
des éléments dans la grille.
On peut l’utiliser pour renseigner en une seule règle les propriétés vues précédemment
(grid-row et grid-column, ou le niveau au-dessus : grid-row-start, grid-row-end, grid-columnstart et
grid-column-end).

grid-area: <grid-line> [ / <grid-line> ]{0,3}

Si quatre valeurs sont renseignées, elles valent respectivement : grid-row-start , grid-


columnstart, grid-row-end, et grid-column-end (attention au croisé).

Si trois valeurs sont renseignées, elles valent respectivement : grid-row-start, grid-columnstart


et grid-row-end. La valeur de grid-column-end vaut celle de grid-column-start si c’est un
identifiant, sinon auto.
Si deux valeurs sont renseignées, elles valent respectivement : grid-row-start et grid-
columnstart. Les valeurs de grid-row-end et grid-column-end valent respectivement celles de grid-
row-start et grid-column-start si ce sont des identifiants, sinon auto.

Si une seule valeur est renseignée et qu’il s’agit d’un identifiant, alors les quatre valeurs
valent cet identifiant, sinon auto.
On peut donc réécrire la déclaration suivante :
grid-row-start: 3;

grid-row-end: 3;
grid-column-start: 2;
grid-column-end: auto;

/* … ou plus simplement */
grid-area: 3 / 2 / 3;

Si on résume, grid-area peut être un raccourci pour grid-row et grid-column, sachant que grid-
row est un raccourci pour grid-row-start et grid-row-end et que grid-column est un raccourci pour
grid-column-start et grid-column-end. En somme, on peut dresser le tableau récapitulatif
suivant.
Tableau 3–10 Propriétés raccourcies du Grid Layout

grid-area (sert aussi au placement via identifiant de zone)


grid-row grid-column
grid-row-start grid-row-end grid-column-start grid-column-end

Chose intéressante à noter, utiliser la valeur auto fera en sorte que l’élément soit placé de
manière automatique dans la grille au premier emplacement disponible.
C’est un comportement qui peut s’avérer pratique, toutefois si vous souhaitez laisser la
main au navigateur pour placer tous les éléments, rendez-vous dans la section suivante.

Placement automatique des éléments avec grid-auto-flow


Nous venons de voir comment placer les éléments au sein de la grille, et cela s’avérera très
pratique lorsque nous souhaiterons placer certains éléments dans des cellules bien
spécifiques, notamment lorsqu’il s’agira d’une mise en page basée sur le Grid Layout.
Toutefois, il arrive que nous utilisions des grilles de manière plus simple, où la répartition
des éléments est automatique. Typiquement, lorsqu’on se base sur display: inline-block, on
remet au navigateur le placement des éléments les uns à côté des autres, et heureusement
sinon ce serait bien pénible !
De la même manière, le module de grille permet de déléguer le placement des éléments au
sein de la grille au navigateur, grâce à la propriété grid-auto-flow. Celle-ci s’occupe de
disposer les éléments qui ne l’ont pas été de manière explicite. Elle accepte deux valeurs :
• row (la valeur par défaut) : distribue les éléments non positionnés dans les rangées, quitte
à en rajouter si besoin.
• column : distribue les éléments non positionnés dans les colonnes, quitte à en rajouter si
besoin.
Il est possible d’y ajouter le mot-clé dense, qui fait en sorte de remplir les emplacements
laissés vides avec des éléments vides. Attention toutefois, cela peut avoir pour
conséquence d’afficher les éléments dans le désordre !
Il est également possible d’utiliser le mot-clé stack qui fait en sorte que tous les éléments
non positionnés soient placés les uns sur les autres dans le premier emplacement vide de la
grille.

Gestion des erreurs de placement


La syntaxe étant quelque peu complexe, il peut arriver qu’il y ait des erreurs de placement,
auquel cas le navigateur effectue les actions suivantes.
• Si grid-row-end ou grid-column-end spécifient une ligne inférieure ou égale à celles spécifiées
par grid-row-start et grid-column-start, elles sont tout simplement ignorées (et valent donc
auto).

• Si à la fois grid-row-start et grid-row-end, ou grid-column-start et grid-column-end spécifient un


étalement sur plusieurs cellules (span), alors celui de la propriété de fin (grid-*-end) est
ignoré.
• Si l’élément est automatiquement positionné et qu’il a un étalement sur plusieurs
cellules (span) dans une dimension, celui-ci est recalculé à 1.

Le terme subgrid
Faisons un petit aparté avant de s’attaquer aux exemples pour signaler qu’un élément de
grille peut lui-même être un conteneur de grille s’il est affublé de la déclaration display:
grid. Dans ce cas, le contexte de son contenu sera totalement indépendant de celui auquel il
participe.
Cependant, dans certains cas, il peut être nécessaire d’aligner le contenu de plusieurs
éléments de grille. Aussi, un conteneur de grille imbriqué dans une autre grille peut
déléguer la définition de ses colonnes et rangées à son parent, faisant de lui une subgrid.
Dans ce cas, les éléments de la grille imbriquée participent au dimensionnement et
contexte de la grille supérieure, rendant possible l’alignement du contenu des deux grilles.
Pour ce faire, il suffit de donner à l’élément la déclaration grid: subgrid. Par exemple :
/* Grille supérieure */

ul {
display: grid;
}

/* Éléments de grille */

li {
/* Grilles imbriquées… */

display: grid;

/* … mais liées */
grid: subgrid;

Une grille imbriquée fonctionne de la même façon qu’un conteneur de grille habituel si ce
n’est que :
• le nombre de lignes est donné par le grid span plutôt que par grid-template-rows et grid-
template-columns ;

• le placement des éléments de la subgrid via les propriétés de placement est contenu dans
la subgrid uniquement ; par exemple, les index de position démarrent à la première ligne
de la subgrid, et non à la première de la grille parent ;
• les éléments de la subgrid participent au dimensionnement de la grille parent, et y sont
alignés. De fait, la somme des marges intérieures et extérieures et des bordures des
éléments de la subgrid sont comptabilisées dans le dimensionnement de la rangée et de la
colonne dans lesquelles se trouve la subgrid ;
• la subgrid est systématiquement étirée, en cela que les déclarations de taille (width, height)
et d’alignement individuel (align-self) n’ont aucun effet.

Cas pratique : réaliser une galerie d’images


Il n’est pas rare d’utiliser une grille (au sens large du terme) pour afficher une collection
d’images. On utilise généralement float ou inline-block mais pour la beauté du geste, je
vous propose de réaliser une galerie d’images avec le Grid Layout. Bien évidemment, en
mobile first.
Dans un souci de simplicité, nous allons considérer que toutes nos images font 200 × 200
pixels. En revanche, pour que notre exemple ne soit pas trop simple non plus, nous allons
faire en sorte que la première image de la grille fasse 400 × 400 pixels.
Commençons par définir notre structure HTML :
<ul class="grid">

<li class="grid-item">
<img src="…" alt="…" />

</li>
<!-- … avec autant de li qu’on le souhaite. -->

</ul>

Nous pouvons dès à présent passer à la partie intéressante, le CSS :


/**
* Conteneur de grille

* 1. Activation d’un contexte de grille


* 2. Permet la distribution automatique

* 3. Comportement hors Media Queries définissant

* une seule et unique colonne (mobile first)


*/

.grid {

list-style: none;
padding: .5em;

display: grid; /* 1 */
grid-auto-flow: row; /* 2 */
grid-template-columns: 1fr; /* 3 */

}
/**
* Éléments de grille

*/
.grid-item {
margin: .5em;

}
/**
* Premier élément de grille

* 1. Déclare que l’élément prend la place de 2 éléments


* dans chaque direction (puisqu’il est 2 fois plus grand)
* Strictement équivalent à :

* grid-columns: 1 / span 2;

* grid-rows: 1 / span 2;
* Ou encore :

* grid-column-start: 1;

* grid-column-end: span 2;
* grid-row-start: 1;

* grid-row-end: span 2;

*/
.grid-item:first-of-type {

grid-area: 1 / 1 / span 2 / span 2; /* 1 */

}
/**

* Images

*/
.grid-item img {

display: block;
height: auto;

max-width: 100%;
margin: 0 auto;

Nous avons là les bases de notre grille, en revanche nous n’avons pas encore appliqué le
colonage en fonction de la largeur de l’écran.
@media (min-width: 400px) {

.grid { grid-template-columns: 1fr 1fr;

@media (min-width: 600px) {


.grid { grid-template-columns: 1fr 1fr 1fr;

@media (min-width: 800px) {


.grid { grid-template-columns: 1fr 1fr 1fr 1fr;
}

@media (min-width: 1000px) {


.grid { grid-template-columns: 1fr 1fr 1fr 1fr 1fr;

}
@media (min-width: 1200px) {
.grid { grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;

}
@media (min-width: 1400px) {
.grid { grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

CLARIFICATION Pas de panique !


Si vous n’êtes pas au point avec les Media Queries, je vous invite à revenir à cet
exemple après avoir lu le chapitre 8, dédié aux styles conditionnels dans lequel tout
vous sera expliqué.
À chaque fois que la taille de la fenêtre nous permet de glisser une image de plus dans la
largeur, on redéclare la propriété grid-template-columns afin d’autoriser une colonne
supplémentaire.

Figure 3–35
Notre galerie d’images sur un écran de petite taille
Figure 3–36
Notre galerie d’images sur un écran de taille moyenne

Figure 3–37
Notre galerie d’images sur un écran de large taille

Simplifier les choses avec un préprocesseur (Sass)


Notre dernier exemple de code est relativement verbeux. Il pourrait l’être d’autant plus si
nous manipulions des images plus petites encore ! Heureusement, il nous est possible de le
rendre plus simple grâce à la fonction repeat(), et à Sass ou n’importe quel autre
préprocesseur CSS capable de faire des boucles.
$thumbnail-width: 200px;
// On boucle de 2 à 7 pour avoir les valeurs

// de 400 px à 1400 px (de 200 en 200)

@for $i from 2 through 7 {


// On ouvre la Media Query appropriée

@media (min-width: $i * $thumbnail-width) {

// On utilise la fonction repeat() en lui


// passant l’index de la boucle ($i)

.grid { grid-template-columns: repeat($i, 1fr);

}
}

Cas pratique : créer un layout simple


Grâce à notre exemple précédent, nous venons de voir comment utiliser le Grid Layout
dans un contexte assez confiné. Et si nous mettions en place un layout simple ?
Par exemple, un document composé d’un en-tête, puis d’un conteneur principal divisé en
quatre parties : un titre qui s’étend sur toute la largeur, puis en dessous une navigation à
gauche, le contenu au milieu, et une barre latérale (sidebar) à droite.
Comme toujours, commençons par mettre en place la structure HTML.
<header class="header">
<!-- Contenu de l’en-tête : titre, logo… -->
</header>

<article class="main">

<nav class="nav">
<!-- Liste de navigation -->

</nav>

<h2 class="title">
<!-- Intitulé du document -->

</h2>
<main class="content">

<!-- Contenu principal -->


</main>
<aside class="sidebar">

<!-- Contenu alternatif -->


</aside>
</article>

Maintenant que nous avons notre DOM, nous pouvons y appliquer les CSS. Afin de
garder notre exemple aussi succinct et parlant que possible, nous n’appliquerons aucun
style dédié à l’embellissement du document (typographie, thème…) pour se concentrer
uniquement sur le layout.
/**
* Conteneurs

* 1. Dimensionnement

* 2. Centrage
*/

.header,

.main {
width: 1170px; /* 1 */

margin: 0 auto 1em; /* 2 */

padding: 1em 0;
}

/**

* Conteneur principal
* 1. Déclaration de la grille

* 2. Définition des colonnes

* 3. Définition des rangées


*/

.main {

display: grid; /* 1 */
grid-template-columns: 250px 25px auto 25px 250px; /* 2 */
grid-template-rows: auto 1fr; /* 3 */

Nous venons de créer une grille de deux rangées sur cinq colonnes. La première rangée est
dédiée au titre du contenu, la seconde est dédiée au contenu et aux barres latérales (nav et
sidebar).

Du point de vue des rangées, nous avons un contenu à largeur fluide entouré de deux
gouttières de 25 pixels de large, puis de deux barres latérales de 250 pixels de large.
Vous remarquerez que l’en-tête ne fait pas partie de la grille.
/**

* Titre du contenu
* Première rangée

* Troisième colonne
* Équivalent : grid-area: 1 / 3

*/
.title {
grid-row: 1;

grid-column: 3;
}
/**

* Contenu (sous le titre)


* Deuxième rangée
* Troisième colonne

* Équivalent : grid-area: 2 / 3
*/
.content {

grid-row: 2;
grid-column: 3;
}

/**

* Navigation
* Deuxième rangée

* Première colonne

* Équivalent : grid-area: 2 / 1
*/

.nav {

grid-row: 2;
grid-column: 1;

/**
* Barre latérale

* Deuxième rangée

* Cinquième colonne
* Équivalent : grid-area: 2 / 5

*/
.sidebar {

grid-row: 2;
grid-column: 5;

Rappelons que grid-area est un raccourci pour :


grid-area: grid-row-start / grid-column-start / grid-row-end / grid-column-end

Dans notre exemple, nous ne spécifions que grid-row-start et grid-column-start car nos
éléments ne s’étalent jamais sur plusieurs cellules, aussi les valeurs pour grid-row-end et
grid-column-end sont implicites.

Figure 3–38
Notre layout à trois colonnes basé sur Grid Layout

Maintenant que nous avons mis tout cela en place, il est assez simple de basculer sur des
zones nommées dans le but de faciliter la maintenance, par exemple. Il suffit de rajouter
une déclaration grid-template-areas à notre conteneur et de remplacer les déclarations grid-
row et grid-column de chaque partie avec grid-area.

.main {
display: grid;
grid-template-columns: 250px 25px auto 25px 250px;

grid-template-rows: auto 1fr;

grid-template-areas: " . . title . . "


"navigation . content . sidebar";

Ici, on utilise le symbole . pour renseigner les zones anonymes, notamment les gouttières.
En effet, inutile d’encombrer notre zoning en nommant les parties dans lesquelles aucun
élément ne sera placé.
.title {
grid-area: title;

.content {
grid-area: content;

.nav {
grid-area: navigation;

.sidebar {
grid-area: sidebar;

Compatibilité des navigateurs pour Grid Layout


Tableau 3–11 Navigateurs desktop

Tableau 3–12 Navigateurs mobiles


La position « sticky »
Nous avons longtemps fait avec seulement quatre types de positions : static (la valeur par
défaut), relative, absolute et enfin fixed. Néanmoins, CSS 3 nous apporte une cinquième
valeur de positionnement : sticky. Le positionnement dit « sticky » est un croisement entre
le positionnement statique et le positionnement fixe.
En effet, un élément affublé de position: sticky se comportera comme un élément normal
jusqu’à ce qu’il soit susceptible d’être partiellement hors du champ d’affichage du
viewport, auquel cas il deviendra fixed.
Cet effet est très pratique lorsqu’on veut qu’un élément se comporte naturellement tant
qu’on se trouve en haut de page, mais qu’il reste dans l’écran à la manière d’un élément
fixed dès lors qu’on a commencé à scroller.

Cas pratique : un header fixe


Considérez la règle CSS suivante :
.header {
position: sticky;
top: 1em;

Dans notre cas, l’élément .header se comporte de manière tout à fait normale jusqu’à ce
qu’on scrolle suffisamment pour qu’il soit partiellement hors de l’écran. À ce moment
précis, il devient sticky et se positionne selon les offsets (top, right, bottom, left) spécifiés.
Dans ce cas-ci, à 1em du haut de la fenêtre.

Compatibilité des navigateurs pour position: sticky


Malheureusement, le support de position: sticky n’est pas merveilleux à l’heure
d’aujourd’hui. Safari le supporte depuis la version 6.1 avec le préfixe -webkit- et Chrome le
supporte depuis la version 23 via un flag à activer dans les paramètres du navigateur.
Tableau 3–13 Navigateurs desktop

Tableau 3–14 Navigateurs mobiles


Chrome a récemment annoncé que position: sticky allait être purement et simplement
retirée du navigateur (y compris avec le flag activé) pour des raisons de performance. Il a
néanmoins été spécifié que la fonctionnalité ferait son retour dès que ces soucis de
performance sont corrigés.

Une solution de repli en JavaScript


Pour les navigateurs ne supportant pas position: sticky, il est tout à fait possible de
reproduire un comportement similaire à l’aide de JavaScript :
var header = document.querySelector('.header'),

originOffsetY = header.offsetTop,

onScroll;

onScroll = function(e) {
window.scrollY >= originOffsetY ? header.classList.add('sticky') :
header.classList.remove('sticky');

}
document.addEventListener('scroll', onScroll);

Comme vous pouvez le constater, le code nécessaire pour achever cet effet en JavaScript
est très court. En revanche, cette solution présente des défauts, notamment celui des
performances. En effet, il est toujours délicat de lier des événements au scroll dans la
mesure où cela a tendance à être relativement gourmand en termes d’exécution.
SUPPORT Polyfill JavaScript
L’agence web FilamentGroup a publié un polyfill sur GitHub.
http://bit.ly/filament-group-sticky
Les régions
Il y a des choses qu’on a toujours su faire dans le domaine du print, mais qui, encore
aujourd’hui, demeurent un challenge dans le Web. Distribuer du texte dans différentes
colonnes ou zones fait partie de ces choses-là.
Nous l’avons vu au début de ce chapitre sur les nouvelles méthodes de positionnement et
de mise en page, le module dédié aux colonnes CSS vient pallier ce manque. Pour aller
encore plus loin, un module appelé CSS Regions a fait son entrée.
L’idée principale derrière ce module est de pouvoir afficher le contenu de telle zone ou
telle zone à tel endroit. C’est bien grâce à cette définition qu’on voit la différence avec les
autres modèles que nous avons étudiés dans ce chapitre (multicolonne, Flexbox, Grid
Layout). Effectivement, les régions en CSS n’ont pas pour vocation de mettre en page un
document. Elles servent simplement à distribuer un contenu dans une ou plusieurs zones.

Terminologie
Le module dédié aux régions n’est en soi pas très compliqué. En revanche, il appelle à
comprendre un certain nombre de termes afin d’être à l’aise pour l’explication des
nouvelles propriétés.
• Tout d’abord, une région CSS est un élément auquel est associé un flux.
• Une chaîne de régions est comme son nom l’indique une séquence de régions associées
à un flux. Les régions dans cette chaîne reçoivent le contenu du flux selon leur ordre au
sein de celle-ci.
• Un flux est une séquence ordonnée de contenus, identifiée par un nom. Les contenus au
sein d’un flux sont triés en fonction de leur ordre dans la source du document.
• Un contenu est placé dans un flux grâce à la propriété flow-into. Ensuite, le contenu de ce
flux est distribué dans sa chaîne de régions associée grâce à la propriété flow-from.
• Un flux est donc créé quand il reçoit du contenu via flow-into ou quand au moins une
région lui réclame du contenu via flow-from.

Comment ça marche ?
Un élément injecte son contenu dans un flux en utilisant la propriété flow-into. Ensuite, un
ou plusieurs éléments réclament le contenu de ce flux via la propriété flow-frow.
Le contenu du flux est donc distribué dans sa chaîne de régions.
Les changements de région sont similaires aux changements de page (comme dans le
module dédié à la pagination en CSS, non abordé dans ce livre) ou de colonne (vu au
début de ce chapitre) en cela qu’ils suivront les règles standards et pourront être régis par
les propriétés break-*.
En somme, chaque région consomme du contenu du flux. Celui-ci est donc positionné
dans la région en cours jusqu’à ce qu’un point de rupture naturel ou forcé intervienne. À
ce moment précis, la région suivante de la chaîne devient la région en cours pour accueillir
le contenu.
S’il n’y a pas suffisamment de régions dans la chaîne pour accueillir tout le contenu du
flux, la distribution du contenu restant dans la dernière région de la chaîne est contrôlée
par la propriété region-fragment.
Dans le cas inverse où il n’y aurait pas suffisamment de contenu pour remplir toutes les
régions, certaines resteront vides.

Injecter du contenu dans un flux


Le module se compose principalement des propriétés flow-into et flow-from, et dans une
moindre mesure de la propriété region-fragment. Commençons par étudier la propriété
permettant d’injecter du contenu dans un flux.
flow-into: none | <ident> [ element | content ]?

Outre la valeur none qui signifie simplement que le contenu de l’élément ne fait partie
d’aucun flux, cette propriété accepte un identifiant (le nom d’un flux), ainsi qu’un
éventuel mot-clé parmi element et content.
Si le mot-clé element est présent, ou qu’aucun des mots-clés n’est spécifié, alors l’élément
est retiré de son parent et placé dans le flux nommé après <ident>. Si le mot-clé content est
spécifié, alors seul le contenu de l’élément est placé dans le flux, laissant l’élément vide et
utilisable pour accueillir le contenu d’un autre flux, par exemple.
REMARQUE Identifiants invalides
Les valeurs none, inherit, default, auto et initial ne sont pas des identifiants valides et par
conséquent ne devraient pas être utilisés pour décrire un flux.
Important : la propriété flow-into affecte uniquement le rendu visuel des éléments placés
dans le flux et distribués dans la chaîne de régions. De fait, elle n’a pas d’effet sur la
cascade et l’héritage des éléments auxquels elle est spécifiée, ni leur position dans le
DOM. De plus, elle n’altère pas l’ordre dans les médias non visuels comme speech, ou la
façon dont se déroule une navigation (nav-index, tabindex…). En somme, on pourrait presque
dire que c’est de la poudre aux yeux !

Réclamer le contenu d’un flux


Passons à la propriété permettant de récupérer le contenu d’un flux pour l’afficher :
flow-from: <ident> | none | inherit

La syntaxe de flow-from est légèrement plus simple que la syntaxe de flow-into. En effet, elle
n’accepte que trois valeurs différentes : none, inherit et un identifiant. Si l’identifiant ne
correspond à aucun flux, alors aucun rendu n’est effectué.
REMARQUE Régions et contenu non affiché
Si la propriété display de l’élément, ou celle de l’un de ses parents, s’avère être none,
l’élément ne devient pas une région non plus.
REMARQUE Régions et propriété content
Si la propriété content a une valeur autre que normal (ou none pour les pseudo-éléments),
l’élément ne devient pas une région. Par extension, si un pseudo-élément (par
exemple, ::after ou ::before) réclame le contenu d’un flux et qu’il a sa propriété content
à none, il est alors considéré comme une région valide.

Gérer la fin d’un flux


Et enfin, la dernière propriété à étudier, mais non des moindres cependant :
region-fragment: auto | break

Cette propriété permet de contrôler le comportement de la dernière région de la chaîne


associée à un flux.
Si sa valeur est définie à auto, le contenu se comporte comme il le ferait dans une boîte
normale. Si le contenu excède les dimensions abordables de l’élément, le surplus est
conditionné
par la propriété overflow. Les éventuels points de rupture de cette région sont alors
simplement ignorés.
Si la valeur est break, deux cas de figure se distinguent. Si le contenu tient dans la région
sans qu’il n’y ait de surplus, cette propriété n’a strictement aucun effet. En revanche, s’il y
a trop de contenu, celui-ci est divisé comme s’il allait se répandre dans une nouvelle
région.
Pour finir, tout contenu qui suit le dernier point de rupture de la dernière région de la
chaîne n’est pas rendu.

Quelques informations complémentaires


Vous devriez désormais être pleinement capable d’utiliser les régions CSS, toutefois je me
sens obligé d’apporter quelques informations complémentaires afin de parfaire votre
apprentissage.

Boucles infinies
Il est possible de générer des boucles infinies avec les régions. Par exemple, si un même
élément déclare les propriétés flow-into et flow-from à la même valeur, cela aura pour effet de
déclarer une récursion mal contrôlée. En effet, l’élément injecte son contenu dans un flux,
puis appelle le contenu de ce même flux, qui est aussitôt réinjecté dans le flux et ainsi de
suite…
Fort heureusement, les spécifications et les implémentations sont bien faites, aussi dans le
cas où un élément déclarerait les deux propriétés à la même valeur, celles-ci n’auraient pas
d’effet.
Arborescence HTML spécifique
Comme nous l’avons vu, la propriété flow-into déplace l’élément auquel elle est déclarée
dans un flux. De par ce fonctionnement, le choix des sélecteurs doit être effectué avec
précaution.
Considérons l’exemple suivant :
table {

flow-into: table-content;

Cette règle déplace toutes les tables dans un flux appelé table-content. Maintenant :
tbody > * {

flow-into: table-content;

Cette règle va déplacer tous les enfants directs d’un <tbody> dans le flux table-content. C’est
un comportement qui peut être intéressant si on souhaite fusionner plusieurs tables. Mais :
tbody * {
flow-into: table-content;
}

Cette règle va déplacer tous les descendants d’un <tbody> dans le flux table-content, sans
préserver l’arborescence initiale. En effet, les <tr>, <th> et <td> vont tous se retrouver au
même niveau hiérarchique, détruisant ainsi complètement la notion de tableau HTML.
Cet exemple illustre bien pourquoi il faut être prudent lorsqu’on choisit d’appliquer la
propriété flow-into à un élément. La hiérarchie de ses enfants ne sera pas préservée !

Cas pratique : une gestion des encarts publicitaires


différente selon la taille de l’écran
Maintenant qu’on sait utiliser les régions CSS, il est temps de les mettre en application.
Un cas judicieux serait de déplacer les espaces publicitaires selon la taille de l’écran.
Prenons le cas d’un site éditorial financé grâce à la publicité. Ces publicités se trouvent
dans la barre latérale du site, les unes en dessous des autres.
Figure 3–39
La sidebar est réservée aux publicités.

Mais quand on passe sur mobile, ladite sidebar se trouve en dessous du conteneur
principal, bien loin du regard de l’internaute. Pas de visualisation, pas de clic ; pas de clic,
pas de revenu.
Et si on utilisait les régions pour faire remonter les espaces publicitaires dans la zone de
contenu ? Pourquoi pas à plusieurs endroits ? Par exemple, un encart publicitaire après
l’introduction de l’article, puis deux plus bas au cœur du contenu, et enfin un après la
signature, juste avant les commentaires.
Pour cela, on pourrait placer des éléments vides avec une classe particulière aux endroits
désirés dans le conteneur principal. Considérons également que les quatre emplacements
publicitaires sont de taille 250 × 250 pour des raisons de simplicité.
<article class="article">

<h1 class="article-title">Titre de l’article</h1>


<p class="article-data">Date et auteur</p>

<p>Introduction …</p>

<div class="ad-slot"></div>
<p>… du contenu …</p>

<div class="ad-slot"></div>

<p>… encore du contenu …</p>


<div class="ad-slot"></div>

<p>… fin de l’article.</p>

<div class="ad-slot"></div>
<div class="article-comments"></div>
</article>

<aside class="sidebar">
<p>Du contenu dans la sidebar … </p>

<!-- Les pubs -->


<div class="ads">
<div class="ad"><img src="…" alt="…" /></div>

<div class="ad"><img src="…" alt="…" /></div>

<div class="ad"><img src="…" alt="…" /></div>


<div class="ad"><img src="…" alt="…" /></div>

</div>

<p>… encore du contenu …</p>


</aside>

Voila à quoi ressemble notre markup. Les emplacements publicitaires pour mobile sont
identifiés par la classe ad-slot. Passons justement au CSS.
/**

* Établissement d’un box-sizing correct


*/

html {
box-sizing: border-box;
}

*, *::after, *::before {
box-sizing: inherit;
}

.ad {
margin-bottom: 1em;
}

.ad img {
display: block;

max-width: 100%;

height: auto;
}

.ad-slot {

display: none;

}
/**

* Écrans dont la largeur est inférieure à 768 px

*/
@media (max-width: 767px) {

.ad {

flow-into: ads;
}

/**

* Emplacements publicitaires
* 1. Hauteur fixe (image + padding vertical + borders)
* 2. Espacement entre l’encart et le titre

* 3. Récupération du contenu du flux ads


*/

.ad-slot {
height: calc(250px + 2 * 1em + 2 * .5em); /* 1 */
padding: 1em 0;

border: .5em solid rgba(0, 0, 0, .1);

border-left: none;
border-right: none;

background: #EFEFEF;

margin-bottom: 1em; /* 2 */
flow-from: ads; /* 3 */

}
}

C’est tout ce dont on a besoin pour atteindre l’effet escompté ! Les emplacements sont
masqués en temps normal mais quand on passe sous une certaine taille d’écran, ils sont bel
et bien visibles, avec une hauteur fixe, et réclament le contenu du flux ads. Celui-ci est
rempli par le contenu des éléments .ad, initialement présents dans la sidebar du site.

Figure 3–40
Sur un écran de petite taille, les encarts publicitaires remontent au cœur du contenu.

Tout cela fonctionne plutôt bien, mais nous avons été contraints de renseigner des
éléments vides dans notre markup. Non seulement cela n’est pas très élégant, mais c’est
surtout très irréaliste dans la mesure où le contenu de l’article provient très probablement
d’un système de gestion de contenu (CMS), comme WordPress, Drupal ou que sais-je
encore.
Contournons le souci en utilisant des pseudo-éléments. En ciblant le pseudo-élément
::before des titres de niveau 2 par exemple (qui pullulent probablement dans le contenu de
l’article), on pourrait non seulement injecter des publicités plus ou moins tout au long de
l’article, mais surtout s’assurer qu’une publicité n’intervient pas au beau milieu d’une
section, de manière abrupte.
@media (max-width: 767px) {

.ad {
flow-into: ads;

.article h2::before {
display: block;

/* + mêmes déclarations que précédemment */

.ads {
flow-from: ads;
}

Deux différences par rapport à la version précédente.


• On utilise les pseudo-éléments plutôt que des éléments vides comme emplacements
publicitaires.
• On ajoute l’emplacement initial (.ads) à la chaîne de régions, afin que les publicités
soient toutes affichées dans le cas où un article n’aurait pas suffisamment de titres de
niveau 2 pour couvrir tous les emplacements.

Compatibilité des navigateurs pour les régions CSS


Tableau 3–15 Navigateurs desktop

Tableau 3–16 Navigateurs mobiles

Malheureusement, le support du module n’est pas aussi superbe que ce dernier. À l’heure
d’aujourd’hui, seul Safari le supporte correctement.
Notons qu’Internet Explorer supporte les régions CSS mais uniquement lorsque la source
d’un flux provient d’un élément iframe, ce qui rend le module somme toute assez limité.
L’ingénieur David Storey (Microsoft) m’informe que l’équipe d’Internet Explorer attend
la stabilisation des spécifications avant de basculer sur une implémentation définitive.
Google a supporté les régions CSS entre les versions 15 et 18 sous le préfixe -webkit-,
après quoi la fonctionnalité a basculé derrière un flag jusqu’à la version 34. À compter de
la version 35, Chrome a volontairement abandonné le module pour des raisons d’ordre
politique.
Les objectifs de Google pour l’année 2014 sont d’optimiser la performance, spécialement
sur mobile. En raison de leur nature, les régions CSS peuvent être coûteuses à
implémenter, aussi Google a préféré remettre leur support à plus tard.
Les masques de forme en CSS
Depuis toujours, nous avons constitué nos pages de rectangles. Après tout, tout élément
HTML est naturellement une boîte, autrement dit un rectangle. Aussi emboîtions-nous ces
éléments les uns dans les autres, tentant vainement d’en changer la forme en ayant recours
à des subtilités graphiques. Mais dans le fond, le résultat a toujours été le même : ce ne
sont que des rectangles.
C’est cette restriction fondamentale que le module dédié aux formes (shapes), ainsi qu’un
certain nombre d’autres modules d’ailleurs, essaye de corriger. Introduite en 2012 par
Adobe, la spécification dédiée aux formes vise à prodiguer aux designers et développeurs
web un moyen de définir comment le contenu s’écoule au sein et/ou autour d’une forme
plus ou moins complexe.
POUR ALLER PLUS LOIN
Nous n’allons voir qu’une brève introduction au concept des masques de forme. Si le
sujet vous plaît, je ne peux que vous recommander les écrits de Sara Soueidan,
véritable experte en la matière.
http://bit.ly/shapes-101
http://bit.ly/sara-soueidan-shapes

Figure 3–41 Sara utilise les masques de forme pour rendre ses mises en page plus agréables à l’œil.

Comment ça marche ?
Pour donner une forme à un élément de type bloc, les propriétés shape-inside et shape-outside
sont utilisées. Celles-ci acceptent des fonctions comme valeurs, telles que circle(), polygon()
ou encore url().
La différence entre les deux propriétés est une question de contexte. Pour shape-inside, nous
allons déclarer une forme dans laquelle le contenu peut s’écouler. Pour shape-outside, nous
allons déclarer une forme autour de laquelle le contenu peut s’écouler.
Par la suite, davantage de contrôles sont possibles grâce aux propriétés annexes telles que
shape-image-threshold et shape-margin.
Avant de rentrer dans le vif du sujet, il y a certains termes qu’il est important
d’appréhender dès maintenant afin de mieux comprendre le fonctionnement du module.

Zone de flottement
La zone de flottement correspond à la zone utilisée pour l’écoulement du contenu autour
d’un élément flottant. Ce sont les bords de cette zone qui détermineront comment le
contenu va s’écouler le long de l’élément flottant. Par défaut, la zone de flottement
correspond à la margin box de l’élément. La propriété shape-outside qu’on verra plus loin
permettra de définir une zone de flottement non rectangulaire.

Formes de base
Les formes de base sont utilisées par le biais des fonctions de forme (voir plus loin). Le
système de coordonnées pour la déclaration de la forme est celui de l’élément à qui est
appliquée la propriété, l’origine se trouvant dans le coin supérieur gauche de celui-ci. De
cette façon, les longueurs exprimées en pourcentage réfèrent aux dimensions de l’élément.
Seules quatre fonctions de forme sont actuellement supportées :
• circle() ;
• ellipse() ;
• inset() ;
• polygon().
Ce sont ces fonctions qui vont permettre de définir la forme d’un élément, conditionnant
ainsi l’écoulement du contenu autour et à l’intérieur de lui.

circle
circle() = circle( [<shape-radius>]? [at <position>]? )

Le premier argument <shape-radius> représente le rayon du cercle défini. Il peut être


renseigné de plusieurs façons :
• avec une longueur (par exemple, 42px) ;
• avec un pourcentage (par exemple, 42%) ;
• avec les mots-clés closest-side ou farthest-side.
Que la valeur soit une longueur (fixe ou relative) ou un pourcentage, sachez qu’elle doit
impérativement être positive sans quoi la fonction sera considérée comme invalide. Dans
le cas où une valeur en pourcentage serait appliquée, le rayon est calculé selon la formule
suivante :
Comme vous avez pu le constater, il est tout à fait possible de définir le rayon du cercle
via les mots-clés closest-side et furthest-side. closest-side est d’ailleurs la valeur par défaut
lorsque cet argument est omis, c’est-à-dire que lorsque vous appliquez circle() à un
élément rectangulaire, le cercle va par défaut être conscrit dans le rectangle.
Toutefois, si vous appliquez la valeur further-side au premier argument, le rayon va prendre
le côté du rectangle le plus éloigné, changeant potentiellement la façon dont le contenu
s’écoulera.
Le second argument définit la position du cercle. Par défaut, celui-ci est centré au sein du
système de coordonnées de l’élément.

ellipse
ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )

La fonction ellipse() fonctionne exactement comme la fonction circle() si ce n’est qu’elle


nécessite deux rayons et non pas un seul : le premier est pour l’axe X, le second pour l’axe
Y. Bien entendu, si les deux rayons sont identiques, on retombe sur un cercle, comme la
fonction circle() le ferait.

inset
inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )

On peut se demander l’intérêt d’avoir une fonction de forme permettant de tracer un


rectangle : en réalité, cette fonction permet de faire en sorte que le contenu s’écoule
correctement autour d’un élément ayant des bords arrondis.
En effet, la fonction inset() définit un rectangle interne. Quand les quatre arguments sont
fournis, ils représentent les offsets top, right, bottom et left vers l’intérieur, définissant ainsi
la position et la dimension du rectangle interne. Les arguments suivent la même syntaxe
que les propriétés margin et padding, c’est-à-dire qu’il est possible d’en fournir de 1 à 4.
La notion de <shape-arg> signifie qu’il s’agit soit d’une longueur (relative ou fixe), soit d’un
pourcentage.
L’argument optionnel <border-radius> arrivant après les autres, il définit les éventuels bords
arrondis pour le rectangle interne, exactement de la même façon que la propriété border-
radius. Notez qu’il est nécessairement précédé du mot-clé round, afin de le différencier des
valeurs d’offsets (par exemple, inset(42px round 13px)).
Dans le cas où les offsets opposés seraient trop importants pour les dimensions de
l’élément (par exemple, top et bottom à 51%), la fonction définirait une forme qui n’aurait
finalement pas de zone de flottement à proprement parler. C’est ce qu’on appelle une
« zone de flottement vide ».

polygon
polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
La fonction polygon() est la plus complexe de toutes puisqu’elle permet de définir des
formes arbitraires plus avancées, utilisant un nombre indéfini de points.
Cette fonction accepte un nombre variable d’arguments, chacun étant une paire de
coordonnées X et Y (en valeurs fixes, relatives ou pourcentages) séparées par un espace.
Chaque paire de coordonnées représente bien sûr un point de la forme, celle-ci étant
finalement définie par le navigateur en reliant les points les uns aux autres, notamment le
premier de la liste au dernier.
Parce qu’une forme ne peut être constituée de moins de trois points, au moins trois paires
de coordonnées sont donc nécessaires pour que la fonction aboutisse.
RESSOURCE Shape Tools
Bear Travis, ingénieur chez Adobe très impliqué dans les spécifications autour de
Shapes, a réalisé une collection d’outils pour faciliter l’utilisation des masques de
forme. Citons notamment Poly Draw, outil permettant de définir un polygone grâce à
des curseurs pour en retirer la syntaxe pour la fonction polygon().
http://bit.ly/shape-tools

Syntaxe
À l’heure actuelle, la spécification n’intègre que trois propriétés différentes pour
manipuler l’écoulement du contenu autour et à l’intérieur de zones arbitraires :
• shape-outside ;
• shape-image-threshold ;
• shape-margin.
La propriété shape-inside n’existe pas encore ou plutôt n’existe plus, puisqu’un brouillon de
spécification avait été rédigé mais fut retiré par la suite pour être implémenté dans une
future version du module.
De la même façon que shape-outside profite de shape-margin, shape-inside sera accompagnée
d’une propriété shape-padding. Autrement dit, il n’est pour l’instant pas possible de
manipuler la façon dont le contenu s’écoule au sein d’une forme. Il est uniquement
possible de le faire autour d’une forme.

Déclaration d’une forme avec shape-outside


Comme nous l’avons vu dans l’introduction de ce chapitre, la propriété shape-outside
permet de définir une forme. Il y a bien des façons de déclarer shape-outside :
• avec une valeur de boîte : margin-box (valeur par défaut), border-box, padding-box et content-
box ;

• avec une fonction de forme : circle(), ellipse(), polygon() et inset() ;


• avec une fonction de forme et une valeur de boîte ;
• avec une image passée via la fonction url() : la forme sera basée sur le canal alpha de
ladite image (plus de détails dans la section dédiée à la propriété shape-image-threshold).

Seuil d’opacité avec shape-image-threshold


La propriété shape-image-threshold, derrière son intitulé relativement complexe, détermine le
seuil de transparence selon lequel la forme d’une image sera évaluée, dans le cas où shape-
outside serait définie via une image (url()).

Cette propriété attend donc un nombre entre 0 et 1 comme valeur, comme pour la
propriété opacity. Ce nombre représente le seuil de transparence au-delà duquel un pixel est
impliqué dans la détermination de la forme d’une image.
shape-image-threshold: <number>

Par exemple, si la valeur est 1, seuls les pixels complètement opaques seront évalués.
Inversement, si la valeur est de 0, les pixels intégralement transparents seront
comptabilisés. La valeur par défaut est de 0.5, et c’est à vous de l’ajuster en fonction de
l’image si le besoin se fait sentir.
Notons qu’une future version des spécifications prévoit de permettre la définition du seuil
à partir de la luminosité de l’image plutôt que de la transparence. Ceci permettra de gérer
des formes sur des images intégralement opaques, comme des fichiers JPEG.

Marge avec shape-margin


La propriété shape-margin n’a de sens que lorsque la propriété shape-outside est définie
également puisqu’elle permet d’ajouter une marge autour de la forme, comme le ferait la
propriété margin autour de l’élément.
shape-outside: <length> | <percentage>

Une valeur en pourcentage est calculée par rapport à la largeur de l’élément.

Cas pratique : une bio avec un avatar circulaire


En guise d’exemple simple, créons un petit encart à propos d’un utilisateur composé d’un
avatar circulaire, et d’un contenu textuel épousant le contour de l’image. Commençons
avec la structure de notre module :
<section class="bio">

<img class="bio-avatar" src="http://hugogiraudel.com/images/hugo-giraudel.jpg"

alt="Avatar Hugo Giraudel" />


<h2 class="bio-name">Hugo Giraudel, Front-end Developer</h2>

<div class="bio-content">

<!-- Contenu textuel à propos de l’utilisateur -->


</div>

</section>

Dans un souci de lisibilité, j’ai omis le contenu textuel au sein de l’élément .bio-content,
mais c’est à cet endroit que se trouve la description de l’utilisateur.
.bio {
padding: 1em;

border: 1px solid silver;


margin: 1em;

}
/**

* Avatar

* 1. Flottant obligatoire
* 2. Forme circulaire « fonctionnelle » sur l’image

* 3. Forme circulaire « visuelle » sur l’image

*/
.bio-avatar {

float: left; /* 1 */
shape-outside: circle(); /* 2 */
border-radius: 50%; /* 3 */

margin: 0 1em 1em 0;


}
.bio-content {

text-align: justify;
}

Dans notre cas, les choses se dégradent de manière très gracieuse si les masques de forme
ne sont pas supportés : le contenu textuel n’épouse pas les bords de l’image. Rien de
dramatique en somme !
Figure 3–42
Exemple d’amélioration progressive avec les masques de forme

Compatibilité des navigateurs pour les masques de forme


Tableau 3–17 Navigateurs desktop

Tableau 3–18 Navigateurs mobiles


4
Interfaces graphiques et amélioration
visuelle

Moins d’images, davantage de vectoriel ! Moins de JavaScript, davantage de CSS ! Les


nouvelles bonnes pratiques vont bon train. Tout ce chapitre est dédié aux avancées de CSS
en matière d’amélioration visuelle.
Le poids des pages web ne cesse d’augmenter depuis les débuts d’Internet. Et pour cause,
des images de haute qualité pour des écrans à haute densité (appelée Retina par Apple),
des bibliothèques JavaScript toujours plus performantes, et bien d’autres ressources
parfois très volumineuses… autant de facteurs responsables de l’obésité croissante de la
page web moyenne.
Pourtant, les spécifications CSS vont de l’avant afin de mettre à disposition des
développeurs des outils permettant de déléguer au navigateur ce qu’ils devaient faire à
l’aide d’images, tels que les bords arrondis, des dégradés ou encore de simples couleurs
transparentes. C’est ainsi que les spécifications CSS de niveau 3 introduisent une
collection de nouvelles propriétés et valeurs de décoration afin de faciliter le design in the
browser.
Des couleurs plus colorées
Les propriétés color et background-color font probablement partie des premières propriétés
CSS standardisées et supportées dans les navigateurs. Et c’est bien normal dans la mesure
où la gestion des couleurs fait partie de tout travail graphique. Les spécifications HTML
4.01 ont standardisé 16 termes pour faciliter l’utilisation des couleurs les plus
fréquemment employées : aqua, black, blue, fuschia, gray, green, lime, maroon, navy, olive, purple,
red, silver, teal, white et yellow, puis orange fut ajouté durant les spécifications CSS 2.1.

De nouvelles couleurs
Finalement, le module de couleurs de niveau 3 a standardisé pas moins de 130 nouveaux
noms pour un total de 147 noms différents (dont 13 nuances de gris), tous utilisables
aujourd’hui y compris sur de vieux navigateurs comme Internet Explorer 6. On ne les
listera pas ici car ce serait trop long, mais vous pouvez facilement trouver la liste sur
Internet. Quoi qu’il en soit, cette première étape démontre la volonté des fabricants de
navigateurs à faciliter l’utilisation des couleurs parfois complexe avec la notation
hexadécimale.
CULTURE GÉNÉRALE À propos de rebeccapurple
Le 19 juin 2014, le W3C s’est accordé pour ajouter la couleur nommée rebeccapurple
(#663399) aux 147 couleurs existantes, en hommage à Rebecca, la fille d’Eric A. Meyer,
décédée d’un cancer le 7 juin, jour de son 6e anniversaire.
Eric Meyer a dédié près de deux décennies de sa vie au Web et ses travaux ont eu
impact indéniable sur l’évolution de celui-ci, notamment en ce qui concerne les CSS,
domaine dans lequel il a toujours excellé. C’est pour saluer le travail d’Eric que le
W3C a souhaité rendre hommage à sa fille, avec son accord bien sûr.
Parallèlement, il a rapidement été question d’ajouter aux couleurs une notion de
transparence, ce que l’on connaît aussi sous le nom de canal alpha (alpha channel). C’est
pour cela qu’ont été standardisées deux notations : rgba et hsla.

La notation rgba
La notation rgba est directement corrélée avec la notation déjà existante de rgb, si ce n’est
que la fonction accepte un quatrième argument pour l’opacité, définie entre 0 et 1, 0 étant
complètement transparent et 1 complètement opaque.
/* Noir à moitié transparent */

.black {
color: rgba(0, 0, 0, 0.5);

color: rgba(0%, 0%, 0%, 0.5);

/* Blanc 2/3 transparent */


.white {

color: rgba(255, 255, 255, 0.33);


color: rgba(100%, 100%, 100%, 0.33);

}
/* Rouge transparent, donc invisible */

.red {
color: rgba(255, 0, 0, 0);
color: rgba(100%, 0%, 0%, 0);

Comme pour la fonction rgb, il est possible de définir des valeurs pour les canaux vert,
bleu et rouge, en pourcentage ou sous la forme d’entiers compris entre 0 et 255. J’ai beau
trouver la notation en pourcentage plus facile, je dois dire que j’ai tendance à utiliser la
version entier, principalement par habitude.

Compatibilité des navigateurs pour rgba


Tableau 4–1 Support des navigateurs desktop

Tableau 4–2 Support des navigateurs mobiles

La notation hsla
hsla et hsl (dans une moindre mesure) sont deux formats qui ont vu le jour durant le
passage du module des couleurs au niveau 3. Ils permettent de définir des couleurs à l’aide
d’une représentation HSL (Hue Saturation Lightness) et non pas RGB (Red Green Blue).
L’objectif derrière la fonction hsl est d’offrir aux développeurs une solution plus intuitive
pour la création de couleurs. En effet, le mode de représentation HSL est plus facile à
appréhender et sûrement moins mathématique que le mode de représentation RGB utilisé
par rgb, rgba et hexadécimal.
Le premier argument est une valeur sans unité (définie en degrés) entre 0 et 360 où se
placent les couleurs : rouge à 0 (et 360), vert à 120, bleu à 240. C’est cette valeur qui va
définir la couleur à proprement parler.
Figure 4–1
Le cercle chromatique des couleurs utilisé par la notation HSL

Le deuxième argument définit la saturation de la couleur en pourcentage ; concrètement


est-ce qu’elle va être très intense ou au contraire plutôt grise. Et enfin, le troisième
argument détermine la luminosité, en pourcentage également.
/* Rouge */

.element {
color: hsl(0, 100%, 50%);

/* Noir */
.element {

color: hsl(0, 0%, 0%);

/* Blanc */
.element {
color: hsl(0, 0%, 100%);

Et comme nous l’avons vu, il est donc possible d’utiliser la fonction hsla afin d’ajouter un
quatrième argument, à savoir le degré d’opacité exprimé entre 0 et 1, exactement comme
pour la fonction rgba.
CURIOSITÉ Des teintes nommées
Le niveau 4 du module des couleurs intègre 24 noms visant à simplifier l’utilisation
des teintes (valeur en degrés de la notation HSL), répartis de manière plus ou moins
équitable autour du cercle chromatique. Dans l’ordre : red (0deg), orangish red (7.5deg),
red orange/orange red (15deg), reddish orange (22.5deg), orange (30deg), yellowish orange
(37.5deg), orange yellow/yellow orange (45deg), orangish yellow (52.5deg), yellow (60deg),
greenish yellow (75deg), yellow green/green yellow (90deg), yellowish green (105deg),
green (120deg), bluish green (150deg), green blue/blue green (180deg), greenish blue
(210deg), blue (240deg), purplish blue (255deg), blue purple/purple blue (270deg), bluish
purple (285deg), purple (300deg), reddish purple (315deg), red purple/purple red (330deg),
purplish red (345deg).
En revanche, cette spécification n’est pas encore validée et ceci n’est implémenté dans
aucun navigateur. Ceci étant dit, vous conviendrez que CSS nous réserve encore de
belles choses !

Compatibilité des navigateurs pour hsla


Tableau 4–3 Support des navigateurs desktop

Tableau 4–4 Support des navigateurs mobiles


L’opacité
La propriété opacity a beau parler d’elle-même, elle réserve néanmoins quelques petites
surprises qui pourraient en étonner plus d’un. En effet, son fonctionnement interne fait
qu’elle a un impact parfois surprenant sur son environnement, et c’est ce que je vais vous
faire découvrir (ou redécouvrir pour certains) dans cette section.
Tout d’abord un petit rappel : la propriété opacity qui définit l’opacité d’un élément est
exprimée par un flottant (nombre décimal) entre 0 et 1 ; 0 rend l’élément intégralement
transparent et 1 le rend complètement opaque (valeur par défaut).
/* Élément à moitié transparent */

.element {
opacity: 0.5;

La modification de l’opacité d’un élément est souvent utilisée pour le rendre légèrement
moins impactant visuellement, jusqu’à ce qu’il soit survolé, auquel cas il reprend alors son
opacité complète, à savoir 1.
.element {
opacity: 0.5;
}

.element:hover {
opacity: 1;
}

Figure 4–2
Niveaux d’opacité de 1 à 0.1 (de 0.1 en 0.1)

opacity et l’héritage
Une chose très importante à savoir sur la propriété opacity est qu’elle n’est pas héritée,
autrement dit il n’est pas possible pour l’enfant d’un élément ayant une opacité réduite de
la rehausser. Par exemple, si vous appliquez une opacité de 0 à l’élément body, il est
absolument impossible de rendre visible un quelconque élément de la page.
C’est pourquoi on ne peut pas utiliser opacity pour rendre le fond d’un élément semi-
transparent, tout en conservant le contenu de l’élément (texte, par exemple) complètement
visible. À la place, il faut se rabattre sur les modes de couleur hsla et rgba qui permettent de
définir la transparence d’une couleur via le canal alpha.
Dans le cas où le fond serait une image et non pas une couleur unie, on ne peut pas se
contenter d’une couleur d’arrière-plan en semi-transparence. Il y a donc deux solutions : la
première, et probablement la plus simple, consiste à utiliser un PNG semi-transparent.
Fonctionnel, mais nécessite de regénérer une image si l’opacité change.
L’autre solution consiste à utiliser un pseudo-élément pour afficher l’image de fond, placé
derrière l’élément, avec une transparence amoindrie. Par exemple :
/**
* Élément

* 1. Contexte de positionnement pour le pseudo-élément

*/
.element {

position: relative; /* 1 */
}
/**

* Pseudo-élément, destiné à afficher l’arrière-plan


* 1. Dimensions identiques au parent
* 2. Positionnement sous le parent

* 3. Image d’arrière-plan
* 4. Réduction de l’opacité
*/

.element::after {
content: '';
position: absolute; /* 1 */

top: 0; /* 1 */
right: 0; /* 1 */

bottom: 0; /* 1 */
left: 0; /* 1 */

z-index: -2; /* 2 */

background-image: url(../images/background.jpg); /* 3 */
opacity: .5; /* 4 */

C’est donc le pseudo-élément (considéré comme un enfant de l’élément) qui est utilisé
pour afficher l’image en semi-transparence ; l’élément n’est pas impacté par la propriété
opacity et son contenu est affiché normalement. Pratique, mais attention aux versions
d’Internet Explorer inférieures à 8 qui ne supportent pas les pseudo-éléments !
Figure 4–3
À gauche, l’opacité est appliquée sur le parent rendant le contenu illisible ; à droite, on utilise l’astuce du pseudo-
élément.

opacity et les contextes d’empilement


De plus, appliquer la propriété opacity à un élément crée un nouveau contexte
d’empilement, comme c’est le cas pour un élément positionné avec un z-index différent de
auto. Un contexte d’empilement définit que les éléments au sein de ce contexte suivent les
mêmes règles d’empilement que d’habitude, mais sont traités séparément des éléments en
dehors de leur contexte d’empilement.
Autrement dit, si vous vous demandez pourquoi votre valeur z-index ne fonctionne pas,
c’est probablement parce que vous essayez de superposer deux éléments provenant de
deux contextes d’empilement différents, et ça peut tout à fait venir de la règle opacity.

Compatibilité des navigateurs pour opacity


Tableau 4–5 Support des navigateurs desktop

Tableau 4–6 Support des navigateurs mobiles

Émuler le support sur Internet Explorer grâce aux filtres


propriétaires de Microsoft
Internet Explorer 8 et les versions inférieures ne supportent pas la propriété opacity,
cependant ils supportent un filtre d’opacité via la propriété filter spécifique à Microsoft, et
ce depuis très longtemps.
Pour émuler l’opacité (ici 50 %, soit l’équivalent de 0.5) sur Internet Explorer 5 à 8, il
suffit d’employer la syntaxe suivante :
.element {

/* Internet Explorer 5, 6 et 7 */
filter: alpha(opacity=50);

/* Internet Explorer 8 */
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
}

Il est possible de faciliter la gestion de l’opacité avec un préprocesseur en créant un mixin


acceptant un degré d’opacité comme seul argument. Le préprocesseur (ci-après, Sass) fait
le calcul et génère les lignes pour Internet Explorer !
@mixin opacity($value) {
$opacity-ie: $value * 100;

filter: alpha(opacity=#{$opacity-ie});

-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=#{$opacity-ie})";

opacity: $value;
}
.element {

@include opacity(.5);
}

Pour le rendu suivant :


.element {
filter: alpha(opacity=50);

-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
opacity: 0.5;
}

Plus pratique, n’est-ce pas ?


Des bords arrondis en CSS
Les plus aguerris d’entre vous se souviendront peut-être de l’époque où il fallait imbriquer
des éléments, chacun avec leur propre image d’arrière-plan calée dans un coin pour
réaliser des bords arrondis sur un élément fluide ; époque que je n’ai moi-même pas
connue. Quoi qu’il en soit, cette époque est bel et bien révolue puisqu’on a aujourd’hui
une solution native aux bords arrondis. Enfin.
Ironiquement, on a attendu près de 12 ans pour avoir une solution native aux bords
arrondis qui fonctionne (presque) partout et maintenant que l’on en a une, la mode est
revenue aux angles droits. La vie est pleine d’humour, n’est-ce pas ?
Quoi qu’il en soit, la propriété border-radius (qui aurait dû s’appeler border-corner ou à la
limite corner-radius) est une des propriétés CSS 3 les plus répandues aujourd’hui, et par
conséquent les mieux supportées. Comme son nom l’indique, elle permet d’arrondir les
angles (au sens propre), de manière individuelle si besoin.

La version simple
La propriété border-radius est un raccourci pour les quatre propriétés individuelles :
• border-top-left-radius ;
• border-top-right-radius ;
• border-bottom-right-radius ;
• border-bottom-left-radius.
De fait, elle accepte plusieurs syntaxes différentes. Tout d’abord, la plus évidente et la plus
répandue consiste à donner une longueur qui agira pour les quatre angles de l’élément.
Ladite longueur détermine le rayon du cercle qui définit la forme arrondie du coin.
/**
* Supérieur gauche : 2em

* Supérieur droit : 2em

* Inférieur droit : 2em


* Inférieur gauche : 2em

*/

.element {
border-radius: 2em;

}
Figure 4–4
border-radius: 2em;

Il est possible de donner deux valeurs séparées par un espace. Dans ce cas, la première
valeur agira pour les angles supérieur gauche et inférieur droit, et la seconde valeur agira
pour les angles supérieur droit et inférieur gauche.
/**

* Supérieur gauche : 2em


* Supérieur droit : 1em

* Inférieur droit : 2em

* Inférieur gauche : 1em


*/

.element {

border-radius: 2em 1em;

Figure 4–5
border-radius: 2em 1em;

Vous pouvez aussi spécifier trois valeurs séparées par des espaces. La première correspond
au coin supérieur gauche, la deuxième aux coins supérieur droit et inférieur gauche et la
troisième au coin inférieur droit.
/**

* Supérieur gauche : 2em


* Supérieur droit : 1em

* Inférieur droit : 3em

* Inférieur gauche : 1em


*/

.element {

border-radius: 2em 1em 3em;


}
Figure 4–6
border-radius: 2em 1em 3em;

Et enfin, bien évidemment, vous pouvez déclarer quatre valeurs pour spécifier les quatre
angles, dans le sens des aiguilles d’une montre à commencer par l’angle supérieur gauche.
/**

* Supérieur gauche : 2em

* Supérieur droit : 1em


* Inférieur droit : 3em

* Inférieur gauche : 4em

*/
.element {

border-radius: 2em 1em 3em 4em;

Figure 4–7
border-radius: 2em 1em 3em 4em;

CLARIFICATION Valeurs négatives


Il n’est malheureusement pas possible d’utiliser des valeurs négatives afin de créer des
arrondis internes. Toute valeur négative sera considérée comme nulle (donc 0).

Dissocier les axes X et Y


À partir de là, vous êtes en mesure de gérer 99 % des cas d’utilisation de la propriété
border-radius mais il est bon de savoir qu’il est possible de dissocier l’arrondi X de l’arrondi
Y de chaque angle. En d’autres termes, il est possible d’utiliser des ellipses pour générer
les angles arrondis au lieu de simples cercles.
Pour séparer les valeurs verticales des valeurs horizontales, on utilise un slash (/). Les
valeurs définies avant le slash correspondent à la longueur des rayons horizontaux alors
que les valeurs définies après le slash sont attribuées à la longueur des rayons verticaux. Si
on omet le caractère / comme nous l’avons vu dans les exemples précédents, cela signifie
simplement que les rayons sont égaux.
/**

* Supérieur gauche : 2em / 4em


* Supérieur droit : 2em / 4em

* Inférieur droit : 2em / 4em

* Inférieur gauche : 2em / 4em


*/

.element {
border-radius: 2em / 3em;

Figure 4–8
border-radius: 2em / 4em;
/**
* Supérieur gauche : 2em / 4em

* Supérieur droit : 1em / 2em


* Inférieur droit : 2em / 4em
* Inférieur gauche : 1em / 2em

*/
.element {
border-radius: 2em 1em / 4em 2em;

Figure 4–9
border-radius: 2em 1em / 4em 2em;
/**
* Supérieur gauche : 2em / 4em

* Supérieur droit : 1em / 2em

* Inférieur droit : 3em / 1em


* Inférieur gauche : 1em / 2em

*/

.element {
border-radius: 2em 1em 3em / 4em 2em 1em;

}
Figure 4-10
border-radius: 2em 1em 3em / 4em 2em 1em;
/**

* Supérieur gauche : 2em / 4em

* Supérieur droit : 1em / 2em


* Inférieur droit : 3em / 1em
* Inférieur gauche : 4em / 3em

*/
.element {
border-radius: 2em 1em 3em 4em / 4em 2em 1em 3em;

Figure 4-11
border-radius: 2em 1em 3em 4em / 4em 2em 1em 3em;

ASTUCE Élément rond


Bien qu’il soit possible d’appliquer des valeurs en pourcentage, il faut savoir que
celles-ci sont relatives aux dimensions de l’élément. Un pourcentage pour un rayon
vertical se base sur la hauteur de l’élément quand un pourcentage pour un rayon
horizontal se base sur la largeur de l’élément.
Du coup, rendre un élément initialement carré, rond (ou elliptique dans le cas d’un
rectangle) est on ne peut plus simple. Il suffit d’appliquer un border-radius: 50%.
.circle {

border-radius: 50%;

}
Figure 4-12 border-radius: 50%;

RESSOURCE The Humble border-radius


Pour une exploitation plus aboutie de la propriété border-radius, je vous recommande
chaudement cette présentation par Lea Verou qui explore de manière approfondie les
subtilités de cette propriété de décoration.
http://bit.ly/lea-verou-border-radius

Bordures et bords arrondis


Dans le cas où l’élément aurait une bordure et des bords arrondis, la règle de gestion des
bords arrondis à l’intérieur de la bordure n’est pas intuitive. Pour faire simple, le bord
arrondi de l’angle à l’intérieur de la bordure est égal à la valeur du bord arrondi extérieur
moins l’épaisseur de la bordure. Si le résultat est négatif, le coin intérieur n’est
simplement pas arrondi.
Par exemple, si un élément a une bordure d’une épaisseur de 10px, et des bords arrondis de
10px ou moins, les angles à l’intérieur de la bordure seront droits. En revanche, si les
valeurs de border-radius sont supérieures à 10px – par exemple 15px – les angles à l’intérieur
de la bordure seront arrondis de 15px - 10px, soit 5px.
Vous pouvez appliquer ce même principe si vous avez deux éléments imbriqués l’un dans
l’autre et que le parent a une marge interne (padding) ainsi que des bords arrondis. Pour que
les bords arrondis de l’enfant soient cohérents avec ceux du parent, il faut que leur valeur
soit égale à celle des bords arrondis du parent moins la taille de la marge interne.
.parent {
border-radius: 2em;

padding: 1em;

}
/**

* radius enfant = radius parent – marge interne parent

* soit 2em – 1em


* soit 1em

*/

.enfant {
border-radius: 1em;

}
Figure 4–13
Radius interne égal à celui du parent (2em) et visuellement incorrect

Figure 4–14
Radius interne correctement calculé (1em) et visuellement plaisant

Compatibilité des navigateurs pour border-radius


Tableau 4–7 Support des navigateurs desktop

Tableau 4–8 Support des navigateurs mobiles

À l’heure actuelle, il n’est plus utile d’utiliser les préfixes constructeurs pour la propriété
border-radius. Tout d’abord, ni Internet Explorer ni Opera n’ont besoin de préfixe pour cette
propriété, contrairement à ce que l’on peut lire dans certaines sources : ces deux
navigateurs sont passés de la case « non supporté » à la case « supporté sans préfixe ».
Concernant Mozilla et Safari, il n’est pas nécessaire d’écrire les préfixes sauf si la majorité
de votre trafic provient des navigateurs Firefox 3.6-, Safari 4-, iOS 3.2- ou Android 2.3-, à
savoir des navigateurs très anciens (ce qui serait malheureux). Dans le cas contraire, je
vous recommanderais d’écrire uniquement la règle non préfixée, rendant le code plus
lisible, et aussi plus léger.
WEBKIT Champs de recherche
Par défaut, les éléments input[type="search"] ont des bords arrondis sur le moteur de
rendu WebKit (entre autres). Si vous désirez restaurer les angles droits communs à
tous les champs de saisie, il vous faut malheureusement passer par la propriété non
standardisée appearance.
En effet, ce n’est pas la propriété border-radius qui donne ce rendu sur WebKit mais la
déclaration -webkit-appearance: searchfield. Pour corriger le tir, on peut donc écrire :
input[type="search"] {
-webkit-appearance: textfield;

INTERNET EXPLORER 9 Problème avec les éléments fieldset


Internet Explorer 9 est incapable d’appliquer la propriété border-radius aux éléments
fieldset. Sans surprise, cependant, quand on sait combien ces éléments sont pénibles à
styler.
ANDROID 2.3 Problème avec les valeurs en pourcentage
Les versions d’Android inférieures et égales à 2.3 n’interprètent pas les valeurs de
border-radius définies en pourcentage. Ce problème est aisément contourné en
renseignant une valeur fixe.
SAFARI 5 Problème quand appliqué sur une image avec bordure
Il subsiste un problème sur certaines versions de Safari lorsque la propriété est
appliquée à une image qui se trouve avoir une bordure.
Le fait est que Safari applique d’abord la bordure à l’image (rectangulaire), puis le
bord arrondi à l’élément. Malheureusement, la bordure ne suit pas les nouveaux
contours et se trouve elle aussi « coupée ». Pour pallier ce problème, il faut pouvoir
appliquer les bords arrondis à l’image et à la bordure. Pour se faire, la solution est
d’encapsuler l’image dans un élément.
.img-container {

border-radius: 1em;
border: .5em solid black ;

}
.img-container img {

border-radius: 1em;

Figure 4-15 Problème de border-radius sur une image avec bordure sur Safari 5
Des ombres avec box-shadow
Une autre propriété bien trop connue du module Background and Borders Level 3 est la
propriété box-shadow qui permet d’appliquer des ombres portées aux éléments sans avoir à
recourir à des images. Cette propriété décorative est aujourd’hui correctement supportée et
joue un rôle important dans la conception de sites et d’applications puisqu’elle permet
d’ajouter de manière très simple des effets d’ombres plus ou moins subtils.
La propriété box-shadow accepte une liste de <shadow> séparées par des virgules, allant du
premier plan à l’arrière-plan. Cela signifie que si vous spécifiez plusieurs ombres dans la
propriété box-shadow, en cas de chevauchement, la première définie se trouvera devant la
seconde, qui se trouvera devant la troisième et ainsi de suite jusqu’à la dernière.
Une <shadow> se compose de la façon suivante :
<shadow> = inset? && <length>{2,4} && <color>?

Cela peut se traduire littéralement par :


• le mot-clé optionnel inset pour appliquer une ombre à l’intérieur de l’élément ;
• une valeur d’offset horizontal (positif vers la droite, négatif vers la gauche) ;
• une valeur d’offset vertical (positif vers le bas, négatif vers le haut) ;
• une valeur optionnelle de blur, c’est-à-dire d’estompe à la manière d’un flou gaussien
(positif uniquement) ;
• une valeur optionnelle de spread, c’est-à-dire d’étalage (positif pour étendre, négatif pour
rétracter) ;
• une valeur optionnelle de couleur (par défaut currentcolor, à savoir la valeur de la
propriété color de l’élément).
Voici un exemple très simple de box-shadow :
.element {

box-shadow: .5em .5em .25em #aaa;


}

Figure 4–16
box-shadow: . 5em .5em .25em #aaa;

On peut imaginer des choses un petit peu plus subtiles, comme un fin liseré blanc à
l’intérieur de l’élément, juste en dessous de sa bordure supérieure, comme pour simuler un
léger effet de lumière.
/**

* Offset horizontal : 0
* Offset vertical : 1px

* Blur : 0
* Spread : 0

* Couleur : rgba(255, 255, 255, .3)


* Inset : oui
*/

.element {

background: tomato;
border: 1px solid rgba(0, 0, 0, .1);

box-shadow: inset 0 1px rgba(255, 255, 255, .3);

De même, on peut appliquer une ombre légère sous l’élément pour lui donner un petit peu
de profondeur.
/**
* Offset horizontal : 0

* Offset vertical : 0.5em


* Blur : 0.25em

* Spread : -0.25em
* Couleur : rgba(0, 0, 0, .2)
* Inset : non

*/
.element {
box-shadow: 0 .5em .25em -.25em rgba(0, 0, 0, .2);

Figure 4–17
box-shadow: 0 .5em .25em -.25em rgba(0, 0, 0, .2);

Il est même possible de simuler une bordure avec la propriété box-shadow en ne spécifiant ni
offset ni flou, juste une valeur de spread de la valeur désirée comme épaisseur de bordure.
.element {

box-shadow: 0 0 0 .5em black;

Il y a toutefois une différence notable entre box-shadow et border : les ombres sont appliquées
sur un calque visuel, c’est-à-dire qu’elles n’interagissent pas avec l’environnement
(modèle de boîte). Une ombre ne peut modifier le layout ou déclencher l’apparition de
barres de défilement.

À propos des performances


Il est de coutume de se méfier des box-shadow pour des raisons de performance. En effet,
quand elles sont appliquées sur de larges zones, les box-shadow peuvent effectivement être
coûteuses à dessiner pour le navigateur, particulièrement lors du scroll où elles doivent
être intégralement « repeintes ».
De plus, les spécifications ne déterminent pas l’algorithme pour appliquer le flou gaussien
via le paramètre blur, ce qui implique que chaque fabricant de navigateur le fasse à sa
manière, conduisant ainsi à des divergences d’un navigateur à l’autre.
Quoi qu’il en soit, ceci ne devrait pas pour autant vous empêcher d’utiliser box-shadow dans
vos productions. Veillez juste à ne pas appliquer des ombres trop larges, particulièrement
dans les zones à forte tendance de scroll.

Cas pratique : donner du sens aux interfaces


À première vue, et comme pour beaucoup de propriétés graphiques, on peut douter de
l’utilité réelle de box-shadow ; après tout, il semble assez facile de sombrer dans le kitsch.
Sachez que les ombres sont pourtant très importantes. Dans la vie de tous les jours, c’est
principalement grâce aux ombres que l’on comprend les perspectives. Sans les ombres,
tout semblerait bien plat. Il en va de même pour les interfaces web.
Google a récemment publié Google Design, le styleguide utilisé pour toutes les interfaces
Android. Celui-ci comprend un chapitre très intéressant au sujet du découpage des
interfaces en couches successives, pareilles à des feuilles de papier.
Selon le document, un système de calques se glissant les uns au-dessus des autres
prodigue du sens aux interfaces, et aide l’utilisateur à s’y retrouver. Pour rendre ceci à
l’écran, on a besoin d’ombres.
Google dénote six niveaux différents (le premier étant le niveau standard, sans ombre), et
après quelques essais, j’ai réussi à les reproduire avec plus ou moins de fidélité via la
propriété box-shadow. Notez que pour réaliser l’effet adéquat, nous avons besoin de deux
ombres à chaque fois.
.z-layer-1 {
border: 1px solid rgba(0, 0, 0, .1);

box-shadow:

0 1.5px 4px rgba(0, 0, 0, .24),


0 1.5px 6px rgba(0, 0, 0, .12);

.z-layer-2 {

border: 1px solid rgba(0, 0, 0, .1);


box-shadow:

0 3px 12px rgba(0, 0, 0, .23),

0 3px 12px rgba(0, 0, 0, .16);


}

.z-layer-3 {

border: 1px solid rgba(0, 0, 0, .1);


box-shadow:

0 6px 12px rgba(0, 0, 0, .23),


0 10px 40px rgba(0, 0, 0, .19);

}
.z-layer-4 {
border: 1px solid rgba(0, 0, 0, .1);

box-shadow:

0 10px 20px rgba(0, 0, 0, .22),


0 14px 56px rgba(0, 0, 0, .25);

.z-layer-5 {
border: 1px solid rgba(0, 0, 0, .1);

box-shadow:
0 15px 24px rgba(0, 0, 0, .22),

0 19px 76px rgba(0, 0, 0, .3);


}

Figure 4–18
Les six niveaux de layering des interfaces Android

Par la suite, ces classes peuvent être appliquées à des éléments d’interface pour les
ordonner sur l’axe Z. Quand elles sont utilisées de manière transversale, ces ombres
donnent du sens au layout et facilitent les interactions.
Figure 4–19
Un zoning d’une interface Android issu des guidelines Google Design utilisant les ombres pour introduire la notion de
plans superposés

Compatibilité des navigateurs pour box-shadow


Tableau 4–9 Support des navigateurs desktop

Tableau 4–10 Support des navigateurs mobiles

Comme pour la propriété border-radius, il n’est plus nécessaire d’écrire les préfixes
constructeurs pour la propriété box-shadow. Internet Explorer et Opera n’en ont jamais eu
besoin, et les préfixes -moz- et -webkit- ne seront utiles que pour Firefox 3.6-, Safari 4-, iOS
3.2- et Android 2.3-, à savoir de très vieux navigateurs.
Je vous recommande donc de n’écrire aucun préfixe et de vous contenter de la règle
originale.
SAFARI 6, IOS 6 ET ANDROID 2.3 Problème avec un blur à 0px
Les navigateurs Safari 6, iOS 6 et Android 2.3 rencontrent un problème lorsque la
valeur du paramètre blur est de 0px. Définir une valeur de 1px au minimum suffit à
contourner ce souci.
Des ombres de texte avec text-shadow
La propriété text-shadow est très similaire à la propriété box-shadow que l’on vient d’étudier si
ce n’est qu’elle permet d’appliquer des effets d’ombrage aux textes et non pas aux
éléments eux-mêmes. Là encore, cette propriété au support plus que correct joue un grand
rôle dans la conception d’interfaces graphiques engageantes.
Sa syntaxe est très proche de celle de box-shadow si ce n’est que le mot-clé inset n’est pas
autorisé, et que la valeur de spread n’existe pas.
<shadow> = <length>{2,3} && <color>?

Cela peut se traduire littéralement par :


• une valeur d’offset horizontal (positif vers la droite, négatif vers la gauche) ;
• une valeur d’offset vertical (positif vers le bas, négatif vers le haut) ;
• une valeur optionnelle de blur, c’est-à-dire d’estompe à la manière d’un flou gaussien
(positif uniquement) ;
• une valeur optionnelle de couleur (par défaut la valeur de la propriété color de
l’élément). Par exemple :
.element {

text-shadow: .075em .075em 0 tomato;


}

Figure 4–20
Utilisation d’une ombre grasse et non floutée sur un titre

C’est là un effet relativement primaire, et pour un rendu plus qualitatif vous allez sûrement
avoir besoin de plusieurs ombres en même temps. Vous remarquerez aussi que bien
souvent, on se sert des text-shadow non pas comme ombres mais comme reflets lumineux en
utilisant un ton blanc semi-transparent.
DÉMO
CodePen recèle de démos typographiques utilisant text-shadow. Par exemple, dans cette
démo, Diego Pardo utilise pas moins de cinq ombres différentes pour un effet
letterpress.
text-shadow:

/* Couleur du texte avec effet de flou */


0 1px 5px rgb(88, 92, 42),

/* Ombres (utilisant un noir transparent) */

0 -1px 0 rgba(0, 0, 0, 1),


0 -3px 0 rgba(0, 0, 0, .45),

/* Reflets (utilisant un blanc transparent) */

0 1px 0 rgba(255, 255, 255, .5),


0 2px 2px rgba(255, 255, 255, .2);
http://bit.ly/codepen-inset-effect

Figure 4–21 Une démonstration impressionnante des capacités de text-shadow par Diego Pardo

Compatibilité des navigateurs pour text-shadow


Tableau 4–11 Support des navigateurs desktop

Tableau 4–12 Support des navigateurs mobiles

OPERA MINI Problème avec le paramètre blur


Notons un léger problème avec Opera Mini qui ne supporte pas l’argument blur.
Autrement dit, que l’ombre soit floutée ou non, Opera Mini la rendra nette.
INTERNET EXPLORER 10 Paramètre spread
Curieusement, les versions supérieures ou égales à 10 d’Internet Explorer supportent
une quatrième longueur avant la couleur qui agit comme valeur d’étalement (spread).
En somme, Internet Explorer synchronise les propriétés text-shadow et box-shadow, plutôt
intelligent !
Des dégradés
La propriété background-image a longtemps été utilisée pour appliquer une image de fond aux
éléments à partir d’une ressource externe comme un fichier .jpg ou .png. Aujourd’hui, elle
accepte d’autres fonctions que url() à savoir linear-gradient() et radial-gradient(). Ces deux
nouvelles fonctionnalités permettent de définir des dégradés comme couleur de fond à
partir de couleurs et de points d’arrêt.
Les spécifications en charge des dégradés ont beaucoup évolué, si bien que les syntaxes
proposées entre la première version du brouillon et la version actuelle (qui n’est toujours
pas en recommandation) sont très différentes l’une de l’autre. De fait, vous pouvez
aujourd’hui rencontrer la vieille syntaxe dans des exemples publiés il y a quelques années.

Syntaxe
La syntaxe des dégradés CSS n’est pas facile, non pas parce qu’elle a été mal conçue mais
parce qu’elle doit être suffisamment flexible pour permettre divers cas d’usage. Pour la
résumer très simplement, un dégradé se compose d’une direction (qui est verticale par
défaut) ainsi que d’une succession de ce que l’on appelle des color-stops, c’est-à-dire une
couleur éventuellement accompagnée d’un longueur ou d’un pourcentage indiquant le
point de départ de la couleur au sein du dégradé.

Dégradés linéaires
Pour les amateurs de syntaxe bien écrite :
<linear-gradient> = linear-gradient(
[ [ <angle> | to <side-or-corner> ] ,]?

<color-stop>[, <color-stop>]+
)

<side-or-corner> = [left | right] || [top | bottom]

Commençons par la partie la plus simple de la syntaxe : spécifier la direction du dégradé


par rapport à l’élément. Pour cela, il y a deux façons de procéder comme vous pouvez le
constater : soit en spécifiant un angle en degrés (deg) où 0deg pointe vers le haut, soit en
spécifiant un mot-clé qui n’est jamais qu’un alias pour un angle. Les mots-clés suivants
sont autorisés :
• to top (0deg) ;
• to top right (45deg) ;
• to right (90deg) ;
• to bottom right (135deg) ;
• to bottom (180deg), la valeur par défaut ;
• to bottom left (225deg) ;
• to left (270deg) ;
• to top left (315deg).
SUPPORT Ancienne syntaxe
En parcourant des articles et feuilles de styles ayant quelques années, vous pouvez
rencontrer des déclarations de dégradés linéaires utilisant les mots-clés right, left, top
et bottom en place de to left, to right, to bottom et to top.
En effet, il s’agit de l’ancienne syntaxe, qui ne doit bien évidemment plus être utilisée.
En ce qui concerne les color-stops, ils peuvent être réduits dans leur forme minimale à une
simple couleur. Par exemple, la valeur linear-gradient(tomato, deepskyblue) produira un
dégradé linéaire de haut en bas du rouge vers le bleu. Il est possible de joindre une
longueur ou un pourcentage à une couleur afin de déterminer la zone d’étendue de la
couleur. Par exemple, linear-gradient(tomato 25%, deepskyblue) produira un dégradé du rouge
au bleu de sorte que la transition entre les deux couleurs ait lieu au quart de la hauteur de
l’élément.
Les lignes suivantes présentent plusieurs façons de définir le même dégradé :
/* Plusieurs façons d’écrire le même dégradé */
.element {

background: linear-gradient(tomato, deepskyblue);


background: linear-gradient(to bottom, tomato, deepskyblue);
background: linear-gradient(180deg, tomato, deepskyblue);

background: linear-gradient(to top, deepskyblue, tomato);


background: linear-gradient(0deg, deepskyblue, tomato);
background: linear-gradient(to bottom, tomato 0%, deepskyblue 100%);

Figure 4–22
Le dégradé linéaire réalisé par l’une des déclarations précédentes

PLUS D’INFOS Pour aller plus loin


Pour approfondir vos connaissances et surtout comprendre comment est calculé un
dégradé de manière mathématique, je vous invite à lire cet article écrit par Ana Tudor
et publié sur mon propre site.
http://bit.ly/ana-tudor-css-gradients
Figure 4-23 Schéma explicatif du fonctionnement des dégradés linéaires issu de l’article d’Ana Tudor

Dégradés radiaux
Nous avons vu comment écrire des dégradés linéaires simples. Vous l’aurez compris, il est
possible de faire des choses assez poussées grâce à la syntaxe permissive des dégradés. De
plus, il est possible de réaliser de radieux dégradés radiaux ! Ce sont des dégradés qui ne
suivent pas un axe mais deux, permettant ainsi la mise en place de dégradés circulaires ou
elliptiques.
Le besoin est plus compliqué et naturellement la syntaxe l’est aussi. Cela vient
essentiellement du fait que la quasi-totalité des arguments de la fonction radial-gradient()
sont optionnels (à l’exception des couleurs et de leur point de rupture bien sûr).
Nous n’allons pas nous étendre sur celle-ci, aussi je vais résumer le plus simplement
possible comment écrire un dégradé radial simple.
Vous pouvez tout d’abord spécifier une forme et/ou une dimension suivie de at et d’une
position, et finalement une suite de color-stops comme pour les dégradés linéaires. Seule
la collection de couleurs est obligatoire.
Par exemple :
/* Ces 3 déclarations produisent le même dégradé */

background-image: radial-gradient(yellow, green);

background-image: radial-gradient(ellipse at center, yellow 0%, green 100%);


background-image: radial-gradient(farthest-corner at 50% 50%, yellow, green);

/* Quelques autres exemples */

background-image: radial-gradient(circle, yellow, green);


background-image: radial-gradient(circle, #006, #00a 90%, #0000af 100%, white 100%);

background-image: radial-gradient(5em circle at top left, yellow, blue);


Figure 4–24
Un exemple de dégradé radial elliptique

Si vous êtes très intéressé par les dégradés radiaux, je ne peux que vous recommander de
lire les spécifications bien qu’elles soient assez complexes. Attention aux articles
obsolètes ! La syntaxe des dégradés a changé de nombreuses fois, aussi certains articles à
leur propos ne sont plus à jour. Veillez à consulter la date de vos sources pour vous assurer
qu’elles sont actualisées.

Les dégradés au quotidien


Soyons honnêtes, la mode n’est plus tellement au dégradé. C’était effectivement le cas il y
a quelques années mais aujourd’hui, on a tendance à privilégier les aplats de couleur plutôt
que les dégradés criards. Néanmoins, il est tout à fait possible d’utiliser les dégradés CSS
pour donner un peu de relief aux éléments. Google emploie par exemple les dégradés sur
les boutons de ses interfaces :
.button {
background-color: #d14836;

background-image: linear-gradient(#dd4b39, #d14836);

Figure 4–25
Les célèbres boutons de Google utilisent de subtiles dégradés linéaires verticaux pour donner du relief

ATTENTION Couleur de fallback


N’oubliez jamais de spécifier une couleur unie avant vos dégradés de sorte que les
navigateurs ne les supportant pas puissent quand même accéder au contenu.

Compatibilité des navigateurs pour les dégradés


Tableau 4–13 Support des navigateurs desktop
Tableau 4–14 Support des navigateurs mobiles

La valeur linear-gradient() doit être la valeur/propriété la plus mal préfixée de toutes les
fonctionnalités nécessitant un préfixe constructeur. Je me permets donc de faire un rappel
sur l’état actuel des préfixes dans le cadre de la fonction linear-gradient().
Tout d’abord le préfixe -ms- n’a jamais été nécessaire dans la mesure où la première
version d’Internet Explorer supportant les dégradés CSS, Internet Explorer 10 a
directement supporté la version standard de la fonction, rendant le préfixe absolument
inutile.
Mozilla Firefox a supporté les dégradés CSS à partir de la version 3.6 via le préfixe -moz-,
puis sans le préfixe à compter de Firefox 16. Aujourd’hui, le trafic généré par Firefox
entre les versions 3.6 et 16 est proche de nul. Je pense ne pas prendre de risque en vous
recommandant de vous passer du préfixe -moz-, en tout cas pour les dégradés les plus
subtiles qui ne visent qu’à améliorer légèrement le rendu de tel ou tel aplat de couleur.
Opera s’est mis à supporter la version standardisée des dégradés à partir d’Opera 12.1, et
Opera Mini n’a jamais mis en place le support pour cette propriété donc à moins de
vouloir impérativement tenir compte des 0,25 % que représentent les utilisateurs d’Opera
12.1-, je pense qu’il est tout à fait possible de se passer du préfixe -o-, à condition d’avoir
une couleur unie de repli.
Et enfin, le dernier mais non des moindres, -webkit- qui est malheureusement encore
nécessaire puisque Safari ne supporte toujours pas la version standard et que Chrome ne
l’a fait que très tardivement. Aussi, il est important de conserver ce préfixe, non seulement
pour Safari et les anciennes versions de Chrome, mais aussi pour le navigateur d’Android
qui est basé sur le moteur de rendu WebKit.
En somme et à moins que les statistiques de votre site n’affirment le contraire, je pense
que le seul préfixe qu’il est important de conserver à l’heure actuelle est -webkit-. La
version standardisée et un aplat de couleur en tant que solution de repli feront le reste.
.element {

background: tomato;
background: -webkit-linear-gradient(tomato, deepskyblue);

background: linear-gradient(tomato, deepskyblue);


}

INTERNET EXPLORER filter gradient pour émuler des dégradés simples


Il est possible d’émuler des dégradés linéaires simples grâce au filter propriétaire
gradient de Microsoft. En revanche, j’aurais tendance à ne pas le recommander pour les
raisons suivantes :
• les dégradés sont typiquement utilisés pour améliorer les visuels, et ne sont par
conséquent pas obligatoires ;
• le rendu est loin d’être du même niveau que celui obtenu avec un dégradé standard ;
• le filtre a un impact non négligeable sur le temps d’affichage ;
• la syntaxe est très complexe, si bien qu’il faut souvent passer par un générateur.
Un meilleur contrôle des arrière-plans
Les arrière-plans ont toujours eu un grand rôle à jouer dans le design web. Ce n’est pas
pour rien que la propriété background-color fut une des premières propriétés CSS à être
implémentée. S’en suivit rapidement la propriété background-image permettant d’appliquer de
véritables images comme arrière-plans d’éléments.
Aujourd’hui, nous pouvons même déclarer des dégradés linéaires ou radiaux comme nous
l’avons vu dans la section précédente, et il est bien normal qu’avec tout cela, les
spécifications nous offrent davantage de contrôle sur ces arrière-plans.
En effet, le module dédié aux bordures et fonds de niveau 3 apporte de nouvelles
fonctionnalités :
• la possibilité de définir de multiples arrière-plans simultanément ;
• de nouvelles valeurs pour les propriétés background-repeat, background-size, background-
attachment et background-position ;

• les propriétés background-clip et background-origin.

Plusieurs arrière-plans sur un même élément


Commençons avec ce qui est probablement la plus grande amélioration du module : la
possibilité de définir plusieurs arrière-plans pour un même élément, sachant qu’un arrière-
plan peut tout aussi bien être une image qu’un dégradé.
Ainsi, il est possible d’appliquer plusieurs calques de fond plus ou moins transparents à un
élément unique, chose qui n’était pas possible par le passé. Il fallait alors cumuler les
éléments, chacun ayant un fond unique.
Pour définir plusieurs arrière-plans à un élément, il suffit de renseigner à la propriété
background-image (ou background) une liste de valeurs séparées par des virgules.

Il est ensuite possible de dimensionner, positionner et manipuler chacun des fonds grâce
aux autres propriétés background-* en renseignant là encore une liste de valeurs séparées par
des virgules.
.ciel {
background-image:

url('oiseaux.png'),

url('nuages.png'),
url('soleil.png');

Notons que l’ordre a une importance ici : les arrière-plans sont classés du plus proche de
l’utilisateur au plus éloigné. Dans notre cas, les oiseaux seront au premier plan, puis les
nuages et enfin le soleil.
Imaginons désormais que nous souhaitions positionner ces arrière-plans :
.ciel {

background-image:
url('oiseaux.png'),

url('nuages.png'),
url('soleil.png');

background-position:
100% bottom,
center top,

top right;

Les index sont respectés, c’est-à-dire que le premier fond de la liste background-image se
verra attribuer la première position de la liste background-position, et ainsi de suite.
Dans le cas où une liste aurait trop de valeurs, celles en surplus sont simplement omises.
Par exemple, si nous avions quatre positions pour trois arrière-plans, la quatrième position
ne serait pas traitée.
Dans le cas contraire où une liste manquerait de valeurs, cette liste serait répétée jusqu’à
ce qu’il y ait suffisamment de valeurs pour satisfaire tous les arrière-plans. Si nous
n’avions renseigné que deux positions pour trois fonds, alors la liste aurait été copiée
depuis la première position, renseignant ainsi la troisième position manquante.
Notons qu’il est possible d’écrire notre exemple précédent de la façon suivante grâce à la
propriété raccourcie (dite shorthand) background :
.ciel {
background:

url('oiseaux.png') 100% bottom,


url('nuages.png') center top,
url('soleil.png') top right;

Compatibilité des navigateurs pour les arrière-plans


multiples
Tableau 4–15 Support des navigateurs desktop

Tableau 4–16 Support des navigateurs mobiles

De nouvelles valeurs pour background-repeat


Nous avons tous vécu ce moment où lors du rafraîchissement d’une page pendant le
développement, une image de fond se voit répétée sur toute la surface de l’élément ! C’est
bien évidemment la propriété background-repeat et sa valeur par défaut repeat qui en sont
responsables.
Le dernier état des spécifications apporte deux nouvelles valeurs à cette propriété bien
connue : space et round.
La première valeur, space, ressemble beaucoup à repeat dans le sens où elle fait en sorte que
le fond soit répété sur toute la surface de l’élément, à l’exception près qu’elle s’assure que
le fond ne soit pas coupé. De fait, la première et dernière occurrence de chaque
ligne/colonne touchent les bords de la zone, et l’espace restant sur chaque ligne/colonne
est divisé entre les occurrences.
La valeur round, de son côté, est très similaire à space sauf qu’elle s’assure qu’il n’y ait pas
d’espace entre les répétitions. Si besoin, le fond est légèrement redimensionné afin de
pouvoir y insérer une occurrence de plus ou de moins, de sorte que celles-ci soient collées
les unes aux autres.
Ces deux valeurs offrent donc un contrôle plus fin sur les répétitions d’arrière-plans.

Cas pratique : placeholders dans une galerie d’images


Un très bon cas d’usage pour space est de simuler de fausses zones d’image (dites
placeholders) lors d’un manque d’images dans une galerie. Considérons une galerie de
810 px de large ayant cinq images de 150 × 150 pixels par ligne (laissant donc un peu
d’espace pour séparer les images les unes des autres), malheureusement il n’y a pas
suffisamment d’images et la ligne est incomplète.

Figure 4–26
Les emplacements inoccupés sont indiqués par des placeholders, grâce aux dégradés linéaires et à background-repeat

Il y a plusieurs façons d’attaquer le problème, mais une idée pourrait être de laisser des
zones grises aux emplacements non occupés. Pour faire cela, la méthode la plus simple
consisterait à utiliser des éléments vides (<div />, par exemple), mais c’est somme toute
assez inapproprié de générer du markup vide, surtout dans un cas comme celui-ci.
On peut donc aborder le problème avec davantage de sagacité et employer la méthode
subtile des dégradés répétés. En effet, il n’est pas possible de répéter une couleur de fond
sur des zones bien définies, aussi on utilise un dégradé plat, c’est-à-dire un dégradé allant
d’une couleur à… la même couleur. Voyez plutôt :
/**

* 1. Application du dégradé plat


* 2. Cadrage du fond dans la zone interne au padding

* 3. Répétition intelligente du fond

* 4. Dimension d’une vignette


*/
.gallery {

width: 810px;
padding: 15px;

background-image: linear-gradient(silver, silver); /* 1 */


background-origin: content-box; /* 2 */
background-repeat: space; /* 3 */

background-size: 150px 150px; /* 4 */

}
.gallery img {

float: left;

}
.gallery img + img {

margin-left: 15px;
}

Grâce à la répétition intelligente du dégradé de 150 × 150 pixels sur toute la largeur de la
galerie et à la distribution équitable de l’espace restant entre les occurrences, les
emplacements inoccupés sont représentés par des carrés gris (la couleur utilisée dans le
dégradé).
PLUS D’INFOS
Si vous désirez approfondir l’idée, je vous invite à lire l’article original de Nico
Hagenburger, en anglais.
http://bit.ly/background-repeat

Compatibilité des navigateurs pour background-repeat:


space et background-repeat: round
Tableau 4–17 Support des navigateurs desktop

Tableau 4–18 Support des navigateurs mobiles

De nouvelles valeurs pour background-size


La propriété background-size permettant de définir la taille d’un arrière-plan n’est pas
nouvelle, toutefois elle accepte désormais deux nouvelles valeurs très pratiques,
spécialement en ce qui concerne le Responsive Web Design : contain et cover.
La valeur contain redimensionne l’image (tout en conservant son ratio hauteur/largeur) de
sorte qu’elle apparaisse la plus grande possible tout en s’assurant qu’à la fois sa hauteur et
sa largeur tiennent dans la zone d’affichage, c’est-à-dire qu’elle soit intégralement visible.
À l’inverse, la valeur cover redimensionne l’image de sorte qu’elle recouvre l’intégralité de
la zone d’affichage, quitte à ce qu’une partie ne soit pas visible.
De nos jours, il est très courant d’utiliser de larges images de fond pour habiller une page.
Cependant, les supports mobiles et tailles d’écran se multipliant, il est difficile d’assurer
un fond adapté en toute situation. Ces deux nouvelles valeurs viennent faciliter cette
intégration en déléguant au navigateur le travail de redimensionnement selon la taille de la
fenêtre.
Par exemple, appliquer une image d’arrière-plan de sorte qu’elle recouvre toute la fenêtre
devient un jeu d’enfant :
.ciel {

background-image: url('ciel.png');
background-size: cover;
}

RAPPEL Omission de la seconde valeur


Quand on ne renseigne qu’une seule valeur à background-size, la seconde valeur a pour
valeur par défaut auto et non la valeur renseignée. C’est une erreur dans l’écriture des
spécifications du module, désormais implémentée dans tous les navigateurs.

Figure 4–27
Le site de l’agence Edenspiekermann utilise la valeur cover pour leur grande image d’arrière-plan.

Compatibilité des navigateurs pour background-size: cover


et background-size: contain
Tableau 4–19 Support des navigateurs desktop
Tableau 4–20 Support des navigateurs mobiles

IOS SAFARI Ne pas utiliser sur l’élément body

Il est recommandé de ne pas utiliser background-size: cover sur l’élément body car Safari
sur iOS rencontre quelques soucis avec cette configuration.

Émuler le support sur Internet Explorer grâce aux filtres


propriétaires de Microsoft
Malheureusement, Internet Explorer 8 et les versions précédentes ne supportent pas les
valeurs cover et contain. Il est toutefois possible d’émuler la valeur cover via un filtre
propriétaire de Microsoft.
.ciel {

background-image: url('ciel.png');
background-position: cover;
filter:

progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.ciel.png',
sizingMethod='scale');
-ms-filter:

"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='ciel.png',

sizingMethod='scale')";
}

ATTENTION Ne pas utiliser sur body et html


Attention toutefois à l’utilisation de ces filtres propriétaires Microsoft sur les éléments
body et html (ou :root). Ils sont problématiques et peuvent causer des problèmes de
scrollbars et de liens morts.
Il est donc recommandé de les utiliser sur un élément positionné de manière fixe
recouvrant toute la page.

Un nouveau système de valeurs pour background-position


On ne présente plus la propriété background-position, toutefois saviez-vous qu’elle a
récemment été revisitée dans le but d’offrir davantage de possibilités ?
Initialement, les deux valeurs de la propriété background-position spécifient respectivement le
décalage en X et en Y entre le coin supérieur gauche de l’image et le coin supérieur
gauche de l’élément.
Ainsi, l’exemple suivant aura pour effet de positionner le fond soleil.png à 50 pixels du
bord gauche et 20 pixels du bord haut de l’élément .ciel.
.ciel {
background-image: url('soleil.png');
background-position: 50px 20px;

Mais que faire si l’on souhaite spécifier les décalages non pas par rapport au coin
supérieur gauche de l’élément mais plutôt par rapport au coin inférieur droit ? C’est là que
les nouvelles spécifications interviennent en offrant la possibilité de déclarer les décalages
depuis n’importe quel bord en le spécifiant avant sa valeur.
Si l’on souhaite placer notre soleil à 50 pixels du bord droit, et 20 pixels du bord bas, on
peut donc réécrire notre règle comme suit :
.ciel {
background-image: url('soleil.png');

background-position: right 50px bottom 20px;


}

Notons que les valeurs peuvent tout aussi bien être renseignées en pourcentage et même
être omises, auquel cas elles seront considérées comme valant 0. Si l’on désire coller notre
soleil dans le coin inférieur droit, on peut tout à fait écrire right bottom.
La propriété background-position a donc trois syntaxes différentes désormais (et ce pour
chaque valeur dans le cas où plusieurs positions pour plusieurs fonds seraient spécifiées) :
• une valeur unique (mot-clé, pourcentage ou longueur) ayant effet sur les deux axes
depuis le coin supérieur gauche ;
• deux valeurs (mots-clés, pourcentages ou longueurs) ayant effet depuis le coin supérieur
gauche ;
• deux valeurs (mots-clés + éventuels longueurs ou pourcentages) ayant effet depuis les
bords renseignés.
Ou si vous préférez…
[ left | center | right | top | bottom | <percentage> | <length> ]

|
[ left | center | right | <percentage> | <length> ]

[ top | center | bottom | <percentage> | <length> ]

|
[ center | [ left | right ] [ <percentage> | <length> ]? ] &&

[ center | [ top | bottom ] [ <percentage> | <length> ]? ]


Figure 4–28
Positionner un arrière-plan par rapport au coin inférieur droit n’a jamais été si facile !

Compatibilité des navigateurs pour le nouveau système de


background-position
Tableau 4–21 Support des navigateurs desktop

Tableau 4–22 Support des navigateurs mobiles

Malheureusement, Safari ne supporte pas cette nouvelle syntaxe (voir page 187).
Toutefois, il est possible de dresser une alternative spécialement pour Safari en utilisant le
potentiel de la fonction calc() que vous pouvez découvrir au chapitre 5 de ce livre.

Origine d’arrière-plan avec background-origin


La propriété background-origin est intimement liée à la propriété background-position
puisqu’elle permet de définir l’origine du repère orthonormé utilisé par la propriété
background-position. En d’autres termes, elle définit à quel niveau démarre la zone
d’affichage du fond (sachant que le fond est toujours peint derrière l’éventuelle bordure) :
• border-box ;
• padding-box (valeur par défaut) ;
• content-box.
Dans le cas où la valeur serait border-box et que l’élément posséderait une bordure, une
partie du fond serait masqué par ladite bordure. C’est un comportement qui n’a pas lieu
avec la valeur initiale padding-box.
De son côté, la valeur content-box a pour effet de positionner l’origine du fond dans le coin
supérieur gauche de la zone de contenu. Si la valeur de background-repeat n’est pas no-repeat
et que l’élément a un padding, cela implique que des répétitions du fond soient partiellement
visibles dans la zone de padding de l’élément (à condition bien sûr que la valeur de
background-clip ne soit pas content-box).
Figure 4–29
Illustration des différentes valeurs de background-origin

Compatibilité des navigateurs pour background-origin


Tableau 4–23 Support des navigateurs desktop

Tableau 4–24 Support des navigateurs mobiles

Fixation de l’arrière-plan avec background-attachment:


local
La propriété background-attachment n’est pas nouvelle ; elle existait déjà dans les
spécifications 2.1 et permettait de définir comment se comporte un arrière-plan vis-à-vis
du viewport (c’est-à-dire de la fenêtre). Grâce à la valeur fixed, il est possible de faire en
sorte que le fond d’un élément soit attaché à la fenêtre comme le serait un élément avec
position: fixed plutôt qu’à l’élément lui-même. Par opposition, la valeur par défaut scroll
positionne le fond par rapport à l’élément.
Le fait est que lorsque ledit élément est scrollable (dans le cas de overflow: auto, par
exemple), le fond reste statique lorsque l’élément est scrollé. C’est le problème que tente
de résoudre la spécification de niveau 3 en ajoutant une troisième valeur à cette propriété :
local. Celle-ci positionne le fond par rapport au contenu de l’élément plutôt que son repère
orthonormé.
Cas pratique : effet d’ombre sur un élément scrollable
Un exemple concret serait d’appliquer des ombres en haut et en bas d’une zone scrollable.
Lorsque l’on se trouve tout en haut de celle-ci, seule une ombre en bas est visible. Lorsque
l’on scrolle, une ombre en haut apparaît. Lorsque l’on atteint le bas de la zone, l’ombre du
bas disparaît.
Cette expérience réalisée par Lea Verou utilise bon nombre de fonctionnalités que nous
avons vues dans ce chapitre : couleurs semi-transparentes, multiples fonds, dégradés
linéaires et radiaux et background-attachment: local. Le code suivant est une version
condensée de son code :
.scrollbox {

overflow: auto;

max-height: 200px;
background-repeat: no-repeat;
background-color: white;

background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;


background-attachment: local, local, scroll, scroll;

background-image:
linear-gradient(white 30%, rgba(255, 255, 255, 0)),
linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%,

radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)),


radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) 0 100%;
}

Figure 4–30
Affichage d’ombres en haut et en bas d’une zone scrollable pour indiquer qu’il est possible de faire défiler le contenu au
sein de celle-ci.

PLUS D’INFOS
Pour plus d’informations sur cette expérience, notamment sur la déclaration backgroud-
attachment: local, veuillez vous référer à l’article original de Lea Verou.

Pour une solution alternative à cet effet visuel ne nécessitant pas background-attachement:
local, sachez que Roman Komarov y est parvenu grâce à une astucieuse utilisation des
pseudo-éléments.
http://bit.ly/lea-verou-background-attachment
http://bit.ly/kizmarh-background-attachment

Compatibilité des navigateurs pour background-attachment:


local
Tableau 4–25 Support des navigateurs desktop

Tableau 4–26 Support des navigateurs mobiles

CURIOSITÉ Opera et la valeur local


Opera ne supporte pas que la valeur local soit définie dans une valeur raccourcie. Il
faut donc spécifier une liste complète de valeurs, et ne pas compter sur la répétition de
celles-ci.

Zone d’arrière-plan avec background-clip


La propriété background-clip détermine la zone d’affichage du fond (dite background
painting area). Elle n’accepte que trois valeurs, désormais bien connues :
• border-box (valeur par défaut) ;
• padding-box ;
• content-box.
La valeur initiale fait en sorte que le fond soit visible sous une bordure dans le cas où
celle-ci ne serait pas intégralement solide ou opaque (couleur semi-transparente ou border-
style à dashed ou dotted).

Si on applique la valeur padding-box, alors le fond ne sera pas peint par le navigateur dans la
zone de bordure. De la même manière, la valeur content-box a pour effet d’appliquer le fond
uniquement à l’intérieur de la zone de contenu, ne tenant ainsi pas compte de la zone de
padding, ce qui est normalement le cas.
Figure 4–31
Illustration des différentes valeurs de background-clip (avec background-origin: border-box pour la démo)

ATTENTION Ne pas utiliser sur l’élément html


Attention à l’application de background-clip à l’élément racine du document (:root ou
html dans le cas d’un document HTML) : pour des raisons de fonctionnement interne
au navigateur, la propriété n’a pas d’effet.
SUPPORT WebKit supporte une valeur non officielle
WebKit supporte la valeur non officielle text, permettant d’appliquer l’arrière-plan
uniquement au contenu textuel de l’élément. En combinant ceci avec la déclaration
tout aussi propriétaire -webkit-text-fill-color: transparent, il est possible d’appliquer une
image à du texte, chose théoriquement impossible en CSS.L
Bien que ce soit du meilleur effet et très simple à mettre en place, attention malgré
tout dans la mesure où il s’agit là d’un duo de déclarations bien spécifiques au moteur
de rendu WebKit. Il peut être intéressant de s’en servir pour offrir aux navigateurs
utilisant ce moteur une amélioration du design, mais il ne faut pas compter sur un
support transversal.

Figure 4-32
Masque de texte avec background-clip: text et -webkit-text-fill-color: transparent, fonctionnel uniquement sur le
moteur de rendu WebKit, bien évidemment

Cas pratique : pop-up et bordure semi-transparente


On peut imaginer un cas où un élément au premier plan de la page (une pop-up, par
exemple) aurait un fond blanc et une bordure épaisse et semi-transparente noire. Par
défaut, le fond étant appliqué sous la bordure, celle-ci apparaît grise et ne laisse pas voir le
contenu se trouvant autour de l’élément.
Figure 4–33
Utilisation de background-clip pour une bordure semi-transparente en plus d’une couleur d’arrière-plan

En appliquant la règle suivante, la bordure devient alors correctement semi-transparente,


laissant voir le contenu dessous.
.modal {

background-color: white;
background-clip: padding-box;
border: 1em solid rgba(0, 0, 0, .5);

Pour les navigateurs ne supportant pas background-clip, il est possible d’obtenir un résultat
sensiblement identique grâce à box-shadow.
.modal {
background-color: white;
box-shadow: 0 0 0 1em rgba(0, 0, 0, .5);
}

Ceci étant dit, le support de box-shadow n’est pas tellement mieux que background-clip.
Pour les navigateurs ne supportant aucune des deux propriétés, il faut recourir à deux
éléments imbriqués, le parent ayant un fond transparent noir via un PNG semi-transparent,
et l’enfant un fond blanc. M’est avis qu’il vaut mieux se passer de la bordure semi-
transparente à ce niveau-là.

Compatibilité des navigateurs pour background-clip


Tableau 4–27 Support des navigateurs desktop

Tableau 4–28 Support des navigateurs mobiles


Les filtres CSS
Initialement prévus pour les documents SVG (Scalable Vector Graphics), les filtres sont
désormais disponibles en CSS. Ceux-ci sont des opérations graphiques appliquées à des
éléments rendus dans un document dans le but d’en modifier leur apparence : flou, ombre
portée, balance des couleurs, opacité…

Comment fonctionnent-ils ?
De manière très générale, un filtre est une opération graphique. Il faut lui passer une image
d’entrée (input) qui sera traitée pour finalement retourner une nouvelle image (output).
Parce que les filtres sont avant tout des opérations d’images (un peu à la manière de
Photoshop), il est important et intéressant de comprendre comment le navigateur s’y prend
pour les appliquer.
Le fonctionnement basique est le suivant : le navigateur calcule le positionnement de tous
les éléments, applique les styles, puis les dessine mais ne les affiche pas encore. À cet
instant, il copie le rendu de la page comme image bitmap, effectue des calculs graphiques
au niveau des pixels, puis applique le résultat sur l’image originale.
Fonctionnellement, c’est proche d’un filtre placé sur la lentille d’un appareil photo.
Lorsque l’on regarde à travers cette lentille, le rendu parait modifié (sans qu’il le soit pour
autant réellement).
Vous l’aurez compris, appliquer des filtres aux éléments du document demande aux
navigateurs des manipulations supplémentaires, ce qui peut potentiellement impacter les
performances. Cependant vous connaissez le dicton : « Ne jamais abuser des bonnes
choses ». Appliqués intelligemment, les filtres n’auront aucun impact négatif sur votre
document.
RAPPEL De la notion d’image
Pour toute la partie dédiée aux filtres, le terme « image » ne réfère pas strictement à
une image au sens HTML du terme, mais bien à l’image d’entrée du filtre, générée par
le navigateur, autrement dit au rendu graphique de l’élément auquel est appliqué le
filtre.

Syntaxe
filter: none | <filter-function-list>

où <filter-function-list> signifie :
[ <filter-function> | <url> ]+

et <filter-function> :
[ <blur()> | <brightness()> | <contrast()> | <custom()> | <drop-shadow()> |
<grayscale()> | <hue-rotate()> | <invert()> | <opacity()> | <sepia()> |

<saturate()> ]
La propriété filter accepte donc une liste de filtres séparés par des espaces, eux-mêmes
déclarés via la fonction url(), une fonction prédéfinie ou une fonction personnalisée
(<custom()>).
Pour bien comprendre leur fonctionnement mais surtout la façon de les utiliser, je vous
propose de les voir un à un, notamment leur effet quand utilisé sur une image HTML (car
d’expérience, je dois dire qu’ils sont souvent appliqués sur des images).

Figure 4-34
Image initiale, non filtrée

blur
filter: blur( <length> )

La fonction blur() applique un flou gaussien à l’image. L’unique argument accepté


correspond à la valeur de déviation standard de la fonction gaussienne. De fait, cet
argument doit être une longueur positive. Une fois n’est pas coutume, un pourcentage
n’est pas autorisé.

Figure 4-35
filter: blur(5px)

brightness
filter: brightness( [ <number> | <percentage> ] )

La fonction brightness() permet de gérer la luminosité de l’image. La valeur par défaut de


100% (ou 1) laisse l’image inchangée, et plus la valeur s’approche de 0, plus l’image
s’assombrit pour finir complètement noire une fois à 0.
Par conséquent, il est tout à fait possible de spécifier des valeurs supérieures à 100% (ou 1).
Celles-ci ajustent la luminosité de l’image vers le haut en conséquence.

Figure 4-36
filter: brightness(.33)

contrast
filter: contrast( [ <number> | <percentage> ] )

Comme la fonction brightness(), contrast() permet de régler le contraste de l’image. Là


encore, une valeur de 1 (ou 100%) ne change pas l’image. À mesure que l’on descend vers 0,
l’image est de moins en moins contrastée pour finir entièrement grise.
Les valeurs supérieures à 1 (ou 100%) sont là encore autorisées et rendent l’image de plus en
plus contrastée.

Figure 4-37
filter: contrast(.33)

drop-shadow
filter: drop-shadow( <length>{2,3} <color>? )

Le filtre drop-shadow() est très semblable à la propriété box-shadow que nous avons vu plus en
amont dans ce chapitre, excepté qu’il n’accepte pas de paramètre spread et qu’il n’est pas
possible de définir de multiples ombres.
Ces restrictions viennent de la façon dont drop-shadow() est implémenté : il s’agit d’une
copie du masque alpha de l’image, floutée, déplacée et rendue dans une couleur en
particulier sous l’image.
Figure 4–38
filter: drop-shadow(0 0 1em red)

Grâce à ce fonctionnement, le filtre drop-shadow() a un avantage majeur sur son cousin


CSS : il respecte à la lettre les contours (PNG transparents, pseudo-éléments…). Parce
qu’il s’agit d’une opération graphique, l’ombre va véritablement épouser les contours de
l’élément contrairement à la propriété box-shadow qui va dessiner une boîte (plus ou moins
floue, large et colorée) autour de l’élément.

Figure 4–39
À gauche le pseudo-élément est intégré dans le tracé de l’ombre via drop-shadow alors qu’à droite, il est omis avec box-
shadow.

CURIOSITÉ Divergence de rendu entre box-shadow et drop-shadow


J’ai récemment remarqué qu’une même ombre est rendue différemment selon si elle
est appliquée avec box-shadow ou le filtre drop-shadow. Visiblement, l’algorithme de
définition du flou gaussien n’est pas le même car la version filtre semble bien plus
douce.
Après discussion avec Paul Lewis, ingénieur chez Google, il semblerait effectivement
que box-shadow utilise un algorithme plus léger afin d’améliorer les performances. De
son côté, le filtre va plus en détail, spécialement dans la mesure où il peut être utilisé
sur des images.
CHROME Anti-aliasing
Sur Chrome, le filtre détériore légèrement la qualité du contenu de l’élément à cause
de l’anti-aliasing du texte. C’est très léger, mais suffisamment visible pour que l’on
puisse le remarquer à l’œil nu.
On peut corriger le souci en appliquant la règle suivante à l’élément filtré :
transform: translateZ(0);
Figure 4-40
À gauche le problème de rendu, à droite le correctif

grayscale
filter: grayscale( [ <number> | <percentage> ] )

Le filtre grayscale() permet de transformer une image en noir et blanc. Le paramètre passé à
la fonction définit la proportion de conversion. 0% (ou 0) laisse l’image dans son état initial
alors qu’une valeur de 100% (ou 1) rend l’image intégralement noire et blanche.
Les valeurs en dessous de 0 sont interdites et donc considérées comme invalides. En
revanche, les valeurs supérieures à 100 % sont autorisées mais n’ont pas plus d’effet que
la valeur 100%. C’est le rôle du navigateur de restreindre la valeur au maximum.

Figure 4-41
filter: grayscale(100%)

ATTENTION Dégradation de la qualité d’un SVG sur écrans à haute densité


Le développeur Dennis Gaebel a découvert que le filtre grayscale appliqué à une image
vectorielle SVG rendait cette dernière légèrement floue sur un écran à haute densité
(par exemple, Retina). Pour plus d’informations, je vous renvoie vers son article :
http://bit.ly/filter-svg-bug

Figure 4-42
À gauche le SVG original, à droite le même avec le filtre grayscale appliqué
hue-rotate
filter: hue-rotate( <angle> )

La fonction hue-rotate() permet de manipuler la teinte de l’image. La représentation


chromatique des couleurs selon le modèle HSL (Hue Saturation Lightness, littéralement
Teinte Saturation Luminosité) repose sur une roue de couleurs (dite « cercle
chromatique ») pour la gestion de la teinte (hue).
Sur cet anneau, le rouge est situé en haut à 0°, le vert en bas à droite à 120° et le bleu en
bas à gauche à 240°. Pivoter ce disque change la teinte de l’image ; c’est ce que fait ce
filtre.
Cette fonction attend donc un angle comme unique argument, pouvant être spécifié avec
n’importe quelle unité de mesure d’angles (degrés, grades, radians et tours).

Figure 4-43
filter: hue-rotate(100deg)

invert
filter: invert( [ <number> | <percentage> ] )

Le filtre invert() applique un négatif à l’image, selon le taux de conversion passé à la


fonction. La valeur 0 (ou 0%) ne modifie pas l’image et la valeur 1 (ou 100%) transforme
l’image en négatif. Toute valeur comprise entre ces deux valeurs applique une conversion
proportionnelle.

Figure 4-44
filter: invert(1)
opacity
filter: opacity( [ <number> | <percentage> ] )

Le filtre d’opacité est très semblable à la propriété opacity que nous avons vue un peu plus
tôt dans ce chapitre à deux différences près.
• Il accepte aussi bien une valeur en pourcentage qu’en flottant (comme la majorité des
autres filtres).
• Il peut faire partie d’une chaîne de filtres, ayant donc un impact sur le rendu des filtres
suivants, contrairement à la propriété opacity qui agit après le rendu des filtres.

Figure 4-45
filter: opacity(.33)

saturate
filter: saturate( [ <number> | <percentage> ] )

La fonction saturate() permet de saturer ou désaturer l’image. La valeur passée correspond


encore au niveau de conversion. 100% (ou 1) laisse l’image originale. En revanche, 0% (ou 0)
désature complètement l’image.
Il est également possible de passer des valeurs supérieures à 100% (ou 1) pour un résultat
sur-saturé.

Figure 4-46
filter: saturate(.33)

sepia
filter: sepia( [ <number> | <percentage> ] )

Le filtre sepia() permet de donner un cachet sépia à l’image. Le paramètre passé à la


fonction définit la proportion de conversion. 0% (ou 0) laisse l’image dans son état initial
alors qu’une valeur de 100% (ou 1) rend l’image complètement sépia.
Comme pour la fonction grayscale(), les valeurs en dessous de 0 sont invalides. Là encore,
les valeurs supérieures à 100 % sont considérées comme valant 100%.

Figure 4–47
filter: sepia(100%)

Cas pratique : différents coloris d’image


On est en droit de se demander quel est l’intérêt des filtres CSS quand on a des outils tels
que les préprocesseurs CSS, prodiguant déjà des fonctions de manipulation de couleurs,
notamment saturate(), hue-rotate() ou grayscale().
Il est vrai que la version préprocesseur a l’avantage d’agir au niveau de la compilation au
format .css et non pas au sein du navigateur, balayant ainsi les problèmes de support de
navigateur.
Toutefois, il existe des cas où les filtres CSS natifs vont là où les préprocesseurs ne vont
pas : la modification d’image. Autant une fonction de couleur d’un préprocesseur convient
très bien quand il s’agit de faire varier la couleur d’un texte, d’une bordure ou d’un fond,
autant lorsqu’il s’agit de manipuler une image, le préprocesseur est impuissant mais le
navigateur excelle.
Considérons une boutique en ligne de vêtements, proposant un t-shirt en différentes
teintes. Chaque teinte est prévisualisable en cliquant sur la puce de la couleur respective.
Jusqu’alors, une seule solution : JavaScript modifie la source de l’image pour appeler
l’image correspondant à la teinte sélectionnée. Ceci a pour effet de télécharger une image
supplémentaire, et ce à chaque prévisualisation de teinte.
Et si au lieu de changer d’image, on lui appliquait plutôt un filtre ? On pourrait modifier la
teinte de l’image grâce à hue-rotate(). Il suffirait simplement de connaître le degré de
rotation du cercle chromatique pour obtenir chacune des teintes existantes.
Ensuite, le reste est simple :
<img id="product-image" src="/images/product/shirt.png" alt="T-Shirt Homme" />

<ul>
<li data-hue="0" class="is-selected">Rouge</li>

<li data-hue="120">Vert</li>
<li data-hue="240">Bleu</li>

</ul>

Du côté du JavaScript (jQuery, dans un souci de simplicité ; bouuuuh, je sais) :


(function () {
var image = $('#product-image');

var item, tint;

$('[data-hue]').on('click', function () {
item = $(this);

tint = item.data('hue');

// Ajout d’une classe à l’élément choisi pour


// déclarer des styles spécifiques

item
.addClass('is-selected')
.siblings()

.removeClass('is-selected');
image.css({
'-webkit-filter': 'hue-rotate(' + tint + 'deg)',

'filter': 'hue-rotate(' + tint + deg)'


});
});

}());

Au clic sur un des éléments de liste, le script JavaScript récupère la valeur de l’attribut
data-hue, et le passe au filtre hue-rotate() appliqué à l’image du produit. Ainsi, une seule et
unique requête HTTP est effectuée : celle de l’image. Les différentes teintes du produit
sont gérées via CSS exclusivement.
Cette technique demande un processus pour déterminer les bonnes valeurs de hue-rotate
pour chaque déclinaison du produit, mais le nombre d’images requises chute
drastiquement : une par produit.

Figure 4–48
Une seule image utilisée, altérée par le filtre hue-rotate

Compatibilité des navigateurs pour les filtres CSS


Tableau 4–29 Support des navigateurs desktop
Tableau 4–30 Support des navigateurs mobiles

SUPPORT À propos de la propriété filter de Microsoft


Les versions antérieures à Internet Explorer 8 (comprise) intègrent déjà la propriété
filter, toutefois celle-ci n’a rien à voir avec la propriété filter que nous venons
d’étudier. En effet, Microsoft avait implémenté un moyen d’appliquer un certain
nombre d’effets graphiques aux éléments via cette propriété, comme les
transformations, l’opacité…
FIREFOX Support de url() depuis 3.6
En réalité, Firefox supporte la propriété filter depuis la version 3.6. En revanche,
seule la fonction url() est interprétée.
Pointer-events
Les capacités de CSS s’étendent toujours plus loin, quitte à venir parfois empiéter sur le
domaine du JavaScript. C’est notamment le cas avec la propriété pointer-events qui permet
d’empêcher les clics d’avoir un effet sur un élément en particulier.
.element {
pointer-events: auto | none;

CLARIFICATION Davantage de valeurs pour le SVG


Il existe en réalité beaucoup plus de valeurs que ça pour la propriété pointer-events
(visiblePainted, visibleFill, visibleStroke, visible, painted, fill, stroke et all) seulement
celles-ci sont exclusivement réservées au SVG. On ne s’étendra donc pas sur ces
valeurs dans ce livre.
La valeur par défaut est auto, c’est-à-dire que les actions de souris se comportent
normalement. Lorsque la valeur est assignée à none en revanche, l’élément n’est plus
sensible au dispositif de pointage. c’est-à-dire que :
• Les actions de survol et de focus ne sont plus prises en compte.
• L’écoute des événements en JavaScript n’est plus prise en compte.
En revanche, si un élément descendant d’un élément avec pointer-events: none a lui-même la
propriété pointer-events explicitement définie de manière à autoriser les événements souris
(par exemple, auto), alors n’importe quel événement ciblant cet enfant passera à travers le
parent et sera donc susceptible de déclencher un événement souris sur l’élément parent.

Cas pratique : améliorer les performances durant le scroll


Lorsque vous faites défiler une page, le navigateur doit effectuer des calculs pour
repositionner les éléments dans le cadre visible de la fenêtre. En temps normal, il fait ça
suffisamment bien pour que ce soit complètement transparent. Toutefois, lorsque vous
abusez de propriétés CSS graphiquement coûteuses comme box-shadow, le scroll peut être
lent et saccadé.
Si ces propriétés sont déclenchées au survol d’éléments, notamment dans un but
d’emphase, on peut utiliser la propriété pointer-events pour limiter les pertes de
performance. L’idée est de désactiver les événements souris lors du scroll pour ne les
réactiver qu’une fois le scroll achevé. Cela permet d’empêcher les événements de clics
mais surtout de survol durant un survol, qui pourraient avoir un effet néfaste sur les
performances.
Pour cela, il suffit d’une simple fonction JavaScript pour ajouter pointer-events: none au body
durant le scroll :
(function () {

var body = document.body;


var defaultPE = body.style.pointerEvents;

var timer;
window.addEventListener('scroll', function() {

clearTimeout(timer);
if (body.style.pointerEvents !== 'none') {

body.style.pointerEvents = 'none';
}
timer = setTimeout(function() {

body.style.pointerEvents = defaultPE;

}, 500);
}, false);

}());

Compatibilité des navigateurs pour les pointer-events


Tableau 4–31 Support des navigateurs desktop

Tableau 4–32 Support des navigateurs mobiles

Le problème avec la propriété pointer-events est qu’elle n’est supportée qu’à partir
d’Internet Explorer 11, mais comme on dit, mieux vaut tard que jamais. En revanche, il
existe des solutions implémentées en JavaScript si vous avez impérativement besoin de
cette propriété dans un projet devant supporter Internet Explorer 10 et les versions
antérieures à celles-ci.
Des images comme bordures
Il a longtemps été possible d’utiliser une image comme bordure grâce à la propriété border-
image, mais le support a toujours été relativement mauvais. Au-delà de ça, certaines des
implémentations étaient parfois approximatives, à la hauteur de la spécification.
En somme, border-image a toujours été le vilain petit canard des propriétés dédiées à
l’embellissement des documents. Même aujourd’hui, alors que les spécifications sont
finalisées, il est toujours très difficile d’utiliser correctement cette fonctionnalité.
La syntaxe est complexe, les prérequis sont nombreux et exigeants, le tout pour un rendu
souvent discutable. C’est pour ces raisons que nous n’allons voir ici qu’un aperçu léger de
cette propriété. Je vous encourage donc à l’expérimenter par vous-même si le sujet vous
passionne.
Avant d’aller plus loin, il faut savoir que la propriété border-image n’est en réalité qu’un
raccourci pour la collection de propriétés relatives aux images comme bordures, toutes
préfixées par border-image-*. Aussi, il est possible de n’utiliser que la propriété border-image,
ou les autres, ou un mélange des deux, comme pour les autres propriétés raccourcies.

Comment ça marche ?
L’application d’une image comme bordure fonctionne selon une découpe en neuf parties :
quatre zones d’angles, quatre zones de côtés et une zone centrale. L’image désignée via
border-image-source est divisée en neuf parties formant ainsi une grille de trois par trois selon
les dimensions renseignées par la propriété border-image-slice.
Les quatre zones d’angle se placeront dans les angles de la zone de bordure de l’élément.
Le comportement adopté par les quatre zones de côté est paramétrable via la propriété
border-image-repeat. La zone centrale est omise, considérée comme transparente, sauf si la
propriété border-image-slice l’indique autrement.

Syntaxe

border-image-source
border-image-source: none | <image>

La propriété border-image-source spécifie une image à utiliser à la place du style donné par
border-style. Dans le cas où la valeur est à none, que l’image ne peut pas être utilisée
(chemin incorrect, par exemple), ou que la propriété n’a simplement pas d’effet, la valeur
définie par border-style prendra effet.
En l’occurrence, la valeur <image> détermine le chemin vers un fichier image valide défini
par la fonction url(), comme dans le cas d’un arrière-plan.

border-image-slice
border-image-slice: [<number> | <percentage>]{1,4} && fill?

La syntaxe de cette propriété ressemble énormément à celle des propriétés margin et padding
si ce n’est que la ou les valeurs renseignées le sont en pourcentage ou nombres sans unité,
contrairement aux deux propriétés susnommées pour lesquelles des pourcentages ou des
longueurs sont attendus.
Dans le cas où des valeurs seraient renseignées sans unité, elles correspondent à des pixels
si l’image est non vectorielle, ou à des coordonnées de vecteur si elle l’est.
Ces valeurs, qu’elles soient renseignées ou automatiquement calculées, servent au
découpage de l’image en neuf parties. Elles définissent donc les offsets vis-à-vis des bords
haut, droit, bas et gauche, respectivement.

Figure 4–49
Le découpage de la zone d’image en neuf parties selon le W3C

Si le mot-clé fill est renseigné, la zone centrale est alors appliquée par-dessus l’arrière-
plan au lieu d’être laissée transparente.

border-image-width
border-image-width: [ <length> | <percentage> | <number> | auto ]{1,4}

La propriété border-image-width est assez trompeuse dans le sens où elle ne définit pas la
largeur de la bordure comme on pourrait le croire ; ceci est le rôle de border-width. En effet,
border-image-width détermine les dimensions de la zone d’application de l’image.

À l’instar des propriétés padding et margin, border-image-width accepte de une à quatre valeurs
renseignées en longueurs, pourcentages ou, cette fois à l’inverse des propriétés
susnommées, en nombres sans unité auquel cas il s’agit d’un ratio vis-à-vis de la propriété
border-width.

Par défaut, sa valeur est de 1 ; en somme cela signifie « une fois la dimension de border-
width ». C’est généralement la valeur que vous allez choisir, mais cela peut varier.
Si la valeur (peu importe la façon dont elle est renseignée : longueur, pourcentage,
nombre…) est plus grande que l’épaisseur de la bordure, la zone d’image empiétera sur
l’arrière-plan. À l’inverse, si elle est plus petite, seule une partie de la zone de bordure sera
imagée.

border-image-outset
border-image-outset: [ <length> | <number> ]{1,4}

Cette propriété détermine à quel point hors du modèle de boîte l’image va être rendue. Par
défaut, elle est à 0, à savoir à l’intérieur de l’élément. Si la valeur est supérieure à 0, alors
l’image est rendue hors de l’élément, sans pour autant perturber ce qu’il y a autour.
Là encore, cette propriété fonctionne sur le même modèle que padding et margin.

border-image-repeat
border-image-repeat: [ stretch | repeat | round | space ]{1,2}

C’est grâce à cette propriété que la magie va véritablement pouvoir opérer. Celle-ci
indique le comportement adopté par les quatre zones de côté, grâce aux quatre valeurs
possibles :
• stretch : l’image est étirée dans les zones de côté ;
• repeat : l’image est répétée dans les zones de côté ;
• round : l’image est répétée dans les zones de côté, quitte à être légèrement
redimensionnée pour qu’un nombre rond d’occurrences apparaisse ;
• space : l’image est répétée dans les zones de côté, et l’espace restant est distribué de
manière égale entre les occurrences.
Il est possible de spécifier deux valeurs différentes (ou identiques, auquel cas la seconde
est optionnelle) afin de déterminer deux comportements différents pour les axes X et Y.

Compatibilité des navigateurs pour border-image


Tableau 4–33 Support des navigateurs desktop

Tableau 4–34 Support des navigateurs mobiles


Pour les navigateurs qui ne supportent pas border-image, notamment toutes les versions
d’Internet Explorer inférieures à 11, il est toujours possible de déclarer une bordure
classique en amont. Par exemple :
.element {
border: 1px solid silver;

border-image: url('my/awesome/image.svg') 20 repeat;


}

SUPPORT Un meilleur support pour la propriété raccourcie


De vieilles implémentations ne supportent pas les propriétés individuelles mais
uniquement la propriété raccourcie. Pour cette raison, et parce qu’il est toujours
préférable d’utiliser les raccourcis, je vous invite à n’utiliser que border-image.
5
De nouvelles unités et valeurs

Entre les nouveaux modules de mise en page et les améliorations graphiques, on a


tendance à oublier des atouts très importants : les unités. Nous étudierons également la
fonction calc() dont vous avez sûrement déjà entendu parler, ainsi que les nouvelles
valeurs de dimensionnement.
Manipuler les unités fait partie du quotidien de l’intégrateur. Et pour cause, CSS en
propose beaucoup ! Du très fameux px jusqu’à cm en passant par em, in et pt, il y en a pour
tous les goûts et surtout pour toutes les occasions.
Mais il faut croire que ça ne suffisait pas car les spécifications des valeurs CSS et du
modèle d’unité de niveau 3 (CSS Values and Units Module Level 3) en proposent quelques
nouvelles afin de compléter notre boîte à outils déjà chargée.
Bien que ce ne soit pas une surprise, il est bon de préciser que les nouvelles unités sont
exclusivement relatives, et qu’aucune unité fixe n’a été ajoutée. Après tout, c’est dans l’air
du temps que de manipuler des longueurs relatives ; à la taille de la police initiale, à la
taille du parent, à la taille de la fenêtre…
On notera également l’apparition de la tant attendue fonction calc permettant de déléguer
des calculs au navigateur, et surtout de mixer différentes unités, ce que même les
préprocesseurs CSS ne permettent pas. Commençons d’ailleurs par cela.
Le Saint-Graal des calculs : calc
N’avez-vous jamais souhaité pouvoir faire des calculs au sein même de vos feuilles de
styles ? Bien sûr, et ce n’est pas un rêve nouveau. Voila des années déjà que les
intégrateurs et développeurs web ont demandé cette fonctionnalité aux fabricants de
navigateurs mais bien sûr, il faut que cela passe par l’étape « spécifications ». C’est ainsi
que le module Values & Units Level 3 introduit la fonction calc, permettant d’écrire des
calculs dans CSS.
Cette fonction accepte les quatre opérateurs mathématiques principaux : addition (+),
soustraction (-), division (/) et multiplication (*). De plus, il est possible d’imbriquer des
fonctions calc dans d’autres fonctions calc (encore que je ne voie pas l’intérêt), et même
d’utiliser la fonction attr pour récupérer la valeur d’un attribut HTML et s’en servir dans
des calculs (bien que ce ne soit pas encore bien supporté par les navigateurs).
La fonction calc peut être utilisée dans à peu près tous les contextes impliquant un nombre,
notamment : les longueurs, les fréquences, les angles, les durées, les nombres et les
entiers. En d’autres termes, vous pouvez l’utiliser partout sauf dans les chaînes de
caractères.

À propos de la syntaxe
Il y a quelques éléments à connaître sur la syntaxe de calc pour s’en servir sans problème.
Tout d’abord, comme nous l’avons vu précédemment, seuls les opérateurs +, -, / et * sont
supportés. On note donc que modulo (%) n’est pas supporté.
Point important à propos de la syntaxe : les opérateurs + et - doivent être entourés
d’espaces pour être interprétés. En effet, la valeur calc(1em -5px) sera interprétée comme
une liste de deux valeurs séparées par un espace et non pas un calcul mathématique. De
fait, je recommande de mettre également des espaces autour des signes / et * même s’ils ne
sont pas nécessaires, principalement dans un souci de cohérence.
Vous pouvez bien évidemment utiliser des parenthèses internes à votre calcul afin de
faciliter la compréhension ou pour forcer la précédence des opérations + et - sur * et /. Et
enfin, les divisions par 0 sont bien sûr proscrites.

Pourquoi pas un préprocesseur ?


Le principal atout de calc par rapport à un préprocesseur CSS (qui permet de faire des
calculs depuis la feuille de styles), c’est de pouvoir mixer les unités, notamment les unités
relatives avec les unités absolues, ou bien les unités relatives entre elles.
Lorsque vous écrivez un calcul à l’aide de Sass, LESS, Stylus ou le préprocesseur de votre
choix, vous déléguez le calcul au compilateur qui transforme le fichier écrit en fichier
CSS. Seulement, ce compilateur n’a aucune idée du contexte ; 1em n’a aucun sens pour lui,
et il serait bien incapable de rajouter 10px à ce em.
En revanche, le navigateur sait, lui. Il connaît le contexte puisque c’est lui-même qui rend
la page et affiche les éléments. C’est pourquoi lui confier les calculs permet de mixer les
unités.
Comme le navigateur connaît le contexte, il est capable d’additionner des px avec des em,
de multiplier des em avec des vh et de diviser des vh par des in. C’est tout à fait faisable pour
lui, et c’est ce qui rend calc si pratique.
Autre différence importante entre la fonction de calcul et un préprocesseur : le
préprocesseur compile une fois le fichier écrit en CSS. À ce moment là, il effectue les
calculs pour que le CSS qui en résulte soit valide. Il transformera donc un (100% / 4) en 25%
afin que le parser CSS du navigateur sache que l’élément fait 25% de large. Et ça s’arrête là.
De son côté, calc fait office d’écouteur, c’est-à-dire qu’il est capable de recalculer les
styles en temps réel si le résultat vient à changer, par exemple dans le cas d’un
redimensionnement de la fenêtre ou d’un changement de taille de police. Là encore, un
gros avantage de calc.

Cas pratique : utiliser calc pour le layout


Il existe un problème en CSS qui aurait dû être simple à résoudre dans la mesure où c’est
finalement quelque chose de basique : créer un layout à deux colonnes dont l’une à une
largeur fixe. L’autre occupe donc le reste de l’espace disponible. Malheureusement, une
opération aussi banale que celle-ci a toujours demandé des techniques pour le moins
douteuses à l’aide de hacks CSS pas forcément bien vus.
Grâce à calc, on peut désormais résoudre ce problème de façon très simple :
.sidebar, .content {
float: left;

}
.sidebar {
width: 250px;

.content {
width: calc(100% - 250px);

Et voila, c’est aussi simple que ça. Attendez que l’on ait accès aux variables natives (voir
chapitre 7) pour insérer ce 250px dans une variable afin d’éviter la répétition et ce sera le
plus simple et le plus beau code que vous n’aurez jamais vu !

Cas pratique : position de l’arrière-plan


Il arrive que vous souhaitiez positionner une image d’arrière-plan à partir de la droite de
l’élément, ou du bas de celui-ci, ou même des deux. Néanmoins, la syntaxe originale de
background-position permet de déclarer les offsets partant du coin supérieur gauche de
l’élément, ne permettant donc pas de faire ce que vous désirez.
Heureusement, nous avons vu dans le chapitre précédent que le niveau 3 du module dédié
aux arrière-plans permet de déclarer une syntaxe comme celle-ci :
background-position: right 42px bottom 24px;

Impeccable ! Toutefois, certains navigateurs utilisant le moteur de rendu WebKit,


notamment Safari 6, ne supportent pas (encore) cette syntaxe. On peut donc avoir recours
à calc pour corriger le souci ! En effet, il suffit d’écrire :
background-position: calc(100% - 42px) calc(100% - 24px);

Figure 5–1
Utilisation de calc pour contourner l’absence de support du nouveau modèle de background-position sur Safari 6

Cas pratique : centrage absolu


Vous connaissez sûrement la technique de centrage absolu consistant à positionner
l’élément de manière absolue au centre du document avec left et top à 50 %, puis à utiliser
un margin négatif de la moitié des dimensions de l’élément pour le centrer.
.element {
width: 20em;

height: 8em;

position: absolute;
left: 50%;

top: 50%;

margin: -4em 0 0 -10em;


}

Fonctionnel, mais il y a plus élégant avec la fonction calc ! Au lieu de décaler l’élément de
50 % vers la droite et vers le bas pour ensuite le redécaler vers le haut et vers la gauche
avec une marge négative, pourquoi ne pas le décaler de la valeur exacte dès le départ ?
.element {

width: 20em;

height: 8em;
position: absolute;

left: calc(50% - 20em / 2);

top: calc(50% - 8em / 2);


}

Compatibilité des navigateurs pour calc


Tableau 5–1 Navigateurs desktop

Tableau 5–2 Navigateurs mobiles

INTERNET EXPLORER 9 calc et background-position


Attention à l’utilisation de calc sur Internet Explorer 9. En effet, ce dernier plante si la
propriété background-position a pour valeur calc.
Root em : l’évolution de l’unité em
Commençons par l’unité CSS 3 la plus connue : rem. rem signifie root em et est donc
intrinsèquement liée à l’unité em qu’on ne présente plus. Comme cette dernière, le rem
s’appuie sur la valeur de la propriété font-size pour calculer les dimensions. En revanche,
et c’est là la différence majeure avec l’unité em, rem ne s’appuie pas sur la taille de la police
de l’élément mais de la racine du document, à savoir l’élément html (dans le cas d’un
document HTML).
Ainsi, une valeur de 1rem mesurera 100 % de la taille de la police de l’élément html qui est
définie dans les options du navigateur si celle-ci n’est pas redéclarée en CSS
(généralement 16px).

Pourquoi rem et pas simplement em ?


Je pense que l’on comprend tous très bien pourquoi on n’utilise pas d’unités fixes pour
dimensionner les éléments. En revanche, on a toujours eu recours à l’unité relative em aussi
on est en droit de se demander pourquoi aurait-on besoin d’une nouvelle unité pour ce
genre de choses, surtout si similaire à son aïeul.
En fait, em présente un inconvénient majeur lors de l’application de styles à des éléments
imbriqués, justement parce que son fonctionnement fait que la valeur est appliquée en
cascade sur les enfants, puis les petits-enfants…
Prenons un exemple tout à fait anodin et considérons que nous n’avons pas modifié les
options de notre navigateur, laissant la taille initiale de la police à 16 px :
li {

font-size: 1.2em;
}

Pour une raison quelconque, nous souhaitons que nos éléments de liste soient légèrement
plus gros que le corps de texte, aussi nous leur appliquons une taille de police de 1.2em, soit
1,2 fois la taille du parent (qui est très probablement un ul) qui lui a une police par défaut à
1em, soit 1 fois la taille du parent, et ainsi de suite jusqu’à atteindre l’élément html. Quoi
qu’il en soit, cette règle applique une taille de 19.2px à nos éléments de liste (1.2 × 1 × 1 …
× 16 px).

Figure 5–2
Les éléments de liste ont une taille de police de 1.2 em, soit 120 % la taille normale.

Jusque-là, aucun souci. Maintenant, imaginons que nous ayons une liste imbriquée dans
une liste, comme ceci :
<ul>

<li>1</li>
<li>

<ul>
<li>1.1</li>
<li>1.2</li>

</ul>

</li>
</ul>

Quand bien même les éléments de liste de premier niveau se comporteront toujours de la
même façon, on va faire face à un souci avec les éléments de liste de second niveau : leur
taille est définie à 1,2 fois celle de leur parent, un ul, qui est enfant d’un li dont la taille est
déjà à 1,2 fois celle du parent. En somme, la taille de la police des li imbriqués n’est pas à
19.2px mais 23px (1.2 × 1 × 1.2 × 1 … × 16 px).

Figure 5–3
Les éléments de la liste imbriquée ont une taille de 1.2 em, soit 120 % du parent qui lui-même a une taille de 120 % celle
de son parent, soit 144 %.

C’est précisément ce genre de problèmes que l’unité rem tente de corriger. En rendant les
dimensions non pas relatives à la fonte du parent mais à celle de l’élément racine, on évite
les soucis de cascade tout en gardant une unité relative (permettant, par exemple, le zoom
texte).

Cas pratique : une grille typographique


En plus d’éviter les problèmes de cascade de l’unité em, l’unité rem est très pratique pour
réaliser une échelle typographique simple, notamment pour les titres afin de conserver une
cohérence et une certaine hiérarchie entre tous les niveaux d’en-têtes.
/**

* Pour une taille de base à 16 pixels…


*/

h1, h2, h3, h4, h5, h6 {

margin-top: 0;
margin-bottom: 1em;

}
h1 {

font-size: 3.2rem; /* == 51.2 pixels */


}

h2 {
font-size: 2.8rem; /* == 44.8 pixels */
}

h3 {

font-size: 2.4rem; /* == 38.4 pixels */


}

h4 {

font-size: 2.0rem; /* == 32.0 pixels */


}

h5 {
font-size: 1.8rem; /* == 28.8 pixels */

}
h6 {
font-size: 1.6rem; /* == 25.6 pixels */

Figure 5–4
Mise en place d’un rythme vertical simple en rem

RESSOURCE Créer un rythme vertical


L’outil Vertical Rhythm Tool permet de générer une feuille de styles pour définir un
rythme vertical, c’est-à-dire un ensemble de tailles de police, hauteurs de ligne et
espacements verticaux harmonieux.
http://bit.ly/soqr-vertical-rhythm
Figure 5-5 Outil Vertical Rhythm Tool

Compatibilité des navigateurs pour l’unité rem


Tableau 5–3 Navigateurs desktop

Tableau 5–4 Navigateurs mobiles

INTERNET EXPLORER 9 ET 10 Problème avec la propriété font


Internet Explorer 9 et 10 rencontrent de sérieux soucis lorsque l’unité rem est utilisée
au sein de la propriété font (raccourci pour toutes les propriétés relatives au texte). La
déclaration est purement et simplement ignorée.
INTERNET EXPLORER 9, 10 ET 11 Problème avec la propriété line-height et les
pseudo-éléments
Internet Explorer versions 9, 10 et 11, n’interprètent pas la déclaration line-height sur
un pseudo-élément (::after ou ::before) si la valeur est en rem.
CHROME Disparition des bordures en rem lors du zoom sur la page
Les bordures déclarées avec l’unité rem ont la fâcheuse tendance à disparaître
complètement lorsque la page est zoomée sur Chrome.

Une solution de repli avec un préprocesseur (Sass)


Malheureusement, Internet Explorer 8 ne supporte pas l’unité rem ce qui reste un frein
considérable dans l’adoption de cette fonctionnalité. Néanmoins, cela fait partie des
propriétés simples à remplacer dans le cas où elles ne sont pas supportées. En effet, il
suffit simplement de fournir une valeur en pixels avant la valeur en rem afin que celle-ci
soit lue dans le cas où rem n’est pas supporté.
Toutefois, effectuer cette manipulation à la main peut s’avérer assez fastidieux mais
heureusement, on peut rendre la chose très facile à l’aide d’un préprocesseur. Tout ce dont
on a besoin est un mixin (une fonction qui applique des règles CSS) acceptant une valeur
en pixels comme argument :
@mixin rem($value, $base: 16px) {
font-size: $value;
font-size: ($value / $base) * 1rem;

L’utilisation est très simple. Au lieu de saisir à la main la taille de police en rem, il suffit
d’inclure le mixin en spécifiant la taille désirée. Celui-ci se chargera de définir la règle en
pixels et de calculer la valeur en rem. Par exemple :
.element {
@include rem(14px);
}

Ceci sera compilé en :


.element {

font-size: 14px;
font-size: 0.875rem;

Non seulement on automatise le fallback pour Internet Explorer 8 (et les autres
navigateurs obsolètes) mais en plus on s’épargne les calculs en les remettant entre les
mains du préprocesseur.
Toutefois, notez que l’on sert du coup des valeurs en pixels pour les quelques navigateurs
ne supportant pas rem, ce qui peut empêcher le zoom texte. À vous de voir si vous pouvez
vous permettre l’utilisation de rem avec un repli en px, ou s’il vaut mieux rester sur em.
CONSEIL Favoriser un postprocesseur
Utiliser un préprocesseur pour la mise en place de fallbacks pour rem n'est pas
nécessairement une bonne idée. Cela peut rendre la maintenance délicate et augmente
considérablement le nombre de lignes de code. Je recommanderais plutôt d'utiliser un
postprocesseur, à savoir un outil qui balaie les feuilles de styles une fois compilées
pour les modifier. Dans notre cas, un outil tel que px_to_rem est tout à fait adapté.
http://bit.ly/px-to-rem
Démystification : à propos de 62.5 %
Dans le but de simplifier les calculs de tailles de police, il est coutume de définir la taille
de la police de la racine du document (l’élément html) à 62.5 % via la déclaration suivante :
/**
* Taille par défaut du navigateur : 16 px

* 16 × 62.5% = 10 px
*/

html {

font-size: 62.5%;
}

En effet, tous les navigateurs ont par défaut une taille de police de 16 pixels. Or 16 × 62.5
vaut 10, ce qui rend tous les calculs autour de l’unité rem extrêmement évidents. Besoin de
l’équivalent de 16 pixels ? Facile, c’est 1.6rem.
Malheureusement, je me dois de faire l’avocat du diable et de vous intimer de cesser cette
mauvaise pratique. Effectivement, cette astuce transforme des calculs pénibles en simples
opérations mentales, mais elle présente aussi des inconvénients.
En effet, elle va nécessiter de redéfinir la taille de la police de tous les conteneurs textuels,
tels que p, li, les titres, et bien d’autres encore. Les calculs sont donc plus simples, mais en
même temps plus nombreux !
En définissant la taille de police de la racine du document à la taille de texte désirée,
généralement entre 12 et 20 pixels, on s’abstient de devoir redéclarer la font-size sur tous
les éléments !
Quant à la complexité des calculs, c’est un souci généralement pris en charge par le post
/préprocesseur. Dans le cas où vous n’en utiliseriez pas, la calculette reste votre meilleur
allié !
Si comme moi, vous êtes un utilisateur de Sass (ou de tout autre préprocesseur), on peut
utiliser le même principe que pour le mixin vu précédemment :
// On déclare une taille de base,

// généralement entre 12 et 20 pixels.


$base-font-size: 16px !default;

// On l’applique à l’élément html

// sous la forme d’un pourcentage.


html {

font-size: $base-font-size / 16px * 100%;

}
// La fonction rem se charge de

// la conversion px en rem.

@function rem($value) {
@return $value / $base-font-size * 1rem;

// Un petit exemple d’utilisation.


h1 {
font-size: rem(28px); // 1.75 rem

POUR ALLER PLUS LOIN


Si vous désirez approfondir les raisons qui font que définir la taille de la police de la
racine du document à 10 pixels est une fausse bonne idée, je vous invite à lire ces
deux articles, écrits par Harry Roberts et l’agence Filament Group respectivement.
http://bit.ly/css-wizardry-rem
http://bit.ly/filament-group-rem
Une unité pour la largeur d’un caractère : ch
L’unit é ch est probablement la moins connue de toutes les unités que propose CSS.
Ajoutée durant le passage au niveau 3 du module, ch est une unité relative à la police de
caractères, à la manière de l’unité ex qui correspond à la hauteur de la lettre x d’une fonte.
En effet, l’unité ch est relative au caractère 0 (zéro, U+0030) de la police de caractères
utilisée.
Ainsi, 1ch correspond à la largeur du caractère 0. Dans le cas d’une police dite
« monotype », 1ch vaut donc la largeur de n’importe quel caractère puisque tous les
caractères occupent le même espace.
Avoir à disposition une unité relative à la largeur d’un caractère peut être intéressant pour
limiter la largeur d’un bloc en fonction d’un certain nombre de caractères par ligne. On
peut, par exemple, imaginer avoir un conteneur de texte qui ferait environ 80ch de large,
permettant d’avoir des lignes de texte agréables à lire.
.container {
width: 90ch;

padding: 5ch;
}

RESSOURCE Animation typographique


Lea Verou avait trouvé un autre cas d’usage pour l’unité ch : animer l’apparition d’un
texte à la manière d’un traitement de texte, une lettre à la fois. Pour cela, elle utilise
une police monotype et anime la largeur du conteneur, en l’augmentant de 1ch à chaque
intervalle de l’animation, affichant chaque fois une lettre supplémentaire. Gadget,
mais très bien réalisé !
http://bit.ly/lea-verou-typing-animation
J’ai un jour eu une autre idée pour l’unité ch : dimensionner un champ de texte en fonction
du nombre de caractères qu’il contient. Ceci est malheureusement impossible à faire
exclusivement avec CSS, mais à l’aide de quelques lignes de JavaScript et de l’unité ch, on
obtient un résultat concluant :
<input type="text" id="autosized" />

<script>

document.getElementById('autosized').onkeypress = function () {
el.style.width = el.value.length + 'ch';

</script>

Compatibilité des navigateurs pour l’unité ch


Tableau 5–5 Navigateurs desktop
Tableau 5–6 Navigateurs mobiles

INTERNET EXPLORER ET IOS 7 Inclusion de l’interlettrage


Internet Explorer et Safari sur iOS 7 incluent l’interlettrage de la police dans le calcul
de la largeur de l’unité ch. C’est un facteur à prendre en compte dans le cas où vous
souhaiteriez une implémentation similaire entre les navigateurs.
Les unités relatives au viewport
Pour faire très simple, le viewport constitue la partie visible d’une page affichée dans
votre navigateur. On peut le considérer comme le parent fictif de la racine du document, à
savoir l’élément html. C’est le viewport qui définit la largeur de la page, donc du html et de
tous ses descendants.
Les nouvelles spécifications CSS introduisent une collection d’unités visant à appliquer
des dimensions relatives à celles du viewport. En effet, bien qu’il soit possible d’utiliser
les pourcentages, ceux-ci ne sont jamais que relatifs à la largeur du parent direct ce qui
devient vite limité lorsque l’on souhaite appliquer des styles à un élément en fonction de la
dimension du document lui-même.

Pourcentage de la largeur/hauteur avec vw et vh


Sous leur nom étrange, ces deux unités sont très semblables à l’unité %. En réalité, c’est
comme ceci qu’elles sont introduites dans les spécifications : 1vw correspond à 1 % de la
largeur du viewport (d’où les initiales vw, pour Viewport Width). Vous l’aurez compris, il en
va de même pour 1vh qui correspond à 1 % de la hauteur du viewport (Viewport Height).
Attention, ça ne correspond pas nécessairement à 1 % de la hauteur ou largeur du
document html. Il s’agit bel et bien d’unités relatives aux dimensions du viewport, c’est-à-
dire de la fenêtre du navigateur. S’il y a suffisamment de contenu pour qu’il y ait des
barres de défilement, la valeur de vw et vh ne varie pas pour autant.
Un des cas les plus intéressants pour ces nouvelles unités est la possibilité de
dimensionner la police du corps de texte en fonction de la taille de la fenêtre, afin d’avoir
une longueur de ligne et une dimension toujours au top. De plus, il n’était pas possible de
faire quelque chose de similaire avec des pourcentages dans la mesure où une font-size
définie en % est relative à la taille de police du parent, et non à sa largeur.
Par exemple :
body {

font-size: 2vw;

Cas pratique : limiter la hauteur des images à la hauteur du


viewport
Selon moi, un cas d’utilisation utile pour l’unité vh serait d’empêcher une image d’être
plus haute que la hauteur de la fenêtre afin de pouvoir la visualiser dans son intégralité. On
pourrait donc penser une règle comme ceci :
article img {
max-height: 90vh;

}
Cas pratique : typographie responsive
Il arrive que vous vouliez dimensionner un contenu textuel – un titre, par exemple – en
fonction de la largeur de la fenêtre ; c’est un procédé graphique assez courant de nos jours
(notamment dans les slides, entre autres).
Cependant, vous ne voulez pas d’une taille fixe qui implique des retours à la ligne
différents en fonction de la largeur de la fenêtre : vous voulez que la taille de la police
s’adapte à la largeur de l’écran, de sorte que les retours à la ligne soient toujours les
mêmes.
/**

* Titre principal

* 1. Taille de la police arbitraire mais


* proportionnelle à la largeur du viewport

* 2. Marges proportionnelles à la taille de la police,


* donc indirectement à la taille du viewport
*/

.masthead {
font-size: 9vw;/* 1 */
padding: 0 1em; /* 2 */

/* Autres styles graphiques */


}

Comme vous pouvez le constater, à partir du moment où on déclare une font-size


proportionnelle à la largeur de l’écran grâce à l’unité vw, l’unité em devient également
relative à la taille du viewport. Pratique pour ne déclarer vw qu’une fois !

Figure 5–6
Typographie responsive grâce à vw sur un écran de grande taille
Figure 5–7
Typographie responsive grâce à vw sur un écran de taille moyenne

Figure 5–8
Typographie responsive grâce à vw sur un écran de petite taille

Cas pratique : typographie responsive et rem


On pourrait même envisager de coupler l’utilisation de vw avec rem pour rendre l’utilisation
de rem relative à la largeur de l’écran. Pensez-y : si l’on applique une taille de police
proportionnelle à la largeur du viewport à l’élément racine (html) et qu’on utilise l’unité rem
pour le dimensionnement par la suite, indirectement on dimensionne en fonction de la
largeur de la fenêtre.
html {
font-size: 2.5vw;

/**
* La règle suivante correspond à

* 1,2 fois 2,5 % de la largeur du viewport


* soit 3vw

*/
.element {
font-size: 1.2rem;

Cette technique aurait l’avantage de ne déclarer qu’à un seul endroit une taille relative au
viewport (la racine du document), les tailles de police seraient alors définies de manière
proportionnelle à l’élément html via rem.
Si demain on veut basculer sur une typographie non responsive, il suffit de changer la
valeur de font-size de html, sans avoir à repasser sur toutes les feuilles de styles.

Cas pratique : conserver un ratio relatif au viewport


Imaginez un élément rectangulaire qui aurait un ratio de 4:3, mais qui doit toujours rentrer
entièrement dans l’écran ; pas question de déborder ou d’avoir une taille ridicule !
Ajoutez-y la contrainte selon laquelle l’écran peut être celui-ci d’un mobile, dont
l’orientation peut changer.
Commençons par le cas le plus courant : le mode paysage. La hauteur de l’élément fait
donc la hauteur de l’écran tout en conservant le ratio 4:3. Ajoutons également une
contrainte plausible : l’élément doit être centré sur l’axe courant.
/**
* 1. L’élément occupe toute la hauteur du viewport
* 2. Sa largeur conserve le ratio 4:3 (~133.333333vh)

* 3. Le positionnement absolu permet de centrer l’élément


* 4. L’élément à un offset depuis le bord gauche
* égal à la moitié de la largeur du viewport (50vw)

* moins la moitié de sa largeur (~66.666666vh)

*/
.box {

height: 100vh; /* 1 */

width: calc(4 / 3 * 100vh); /* 2 */


position: absolute; /* 3 */

left: calc(50vw - 4 / 3 * 100vh / 2); /* 4 */

}
Figure 5–9
Un ratio 4:3 maintenu aux dimensions maximales pour tenir dans l’écran en mode paysage

Maintenant, grâce à une Media Query (voir chapitre 8) nous détectons le mode portrait et
appliquons des styles spécifiques :
@media (max-aspect-ratio: 4/3) {
/**

* 1. L’élément occupe toute la largeur du viewport


* 2. Sa hauteur conserve le ratio 4:3, soit 75vw
* 3. L’élément a un offset depuis le bord haut

* égal à la moitié de la hauteur du viewport (50vh)


* moins la moitié de sa hauteur (37.5vw)
* 4. Annulation de l’offset depuis le bord gauche

*/
.box {
width: 100vw; /* 1 */

height: 75vw; /* 2 */
top: calc(50vh - 3 / 4 * 100vw / 2); /* 3 */
left: auto; /* 4 */

}
Figure 5–10
Un ratio 4:3 maintenu aux dimensions maximales pour tenir dans l’écran en mode portrait

Plutôt efficace, n’est-ce pas ?


CLARIFICATION Image d’arrière-plan
Dans le cas d’une image d’arrière-plan, il est plus simple d’utiliser background-size:
contain, qui s’assure que l’image rentre intégralement dans la zone d’arrière-plan de
l’élément, tout en préservant son ratio initial.
Toutefois, quand il s’agit d’une vidéo, de tout autre média, ou même d’un simple
conteneur de texte, il faut changer de méthode. Or vous connaissez le dicton : aux
grands maux, les grands remèdes.

Compatibilité des navigateurs pour les unités vw et vh


Tableau 5–7 Navigateurs desktop

Tableau 5–8 Navigateurs mobiles

IOS Bugs et problèmes d’interprétation

Les versions de Safari sur iOS 6 et iOS 7 sont connues pour avoir des soucis de rendu
avec l’unité vh (corrigés dans iOS 8). L’unité en elle-même est tout à fait supportée, en
revanche les calculs sont erronés. Lorsqu’un élément a une hauteur de 100vh par
exemple, et que le layout est recalculé par le navigateur (scroll, changement
d’orientation, entre autres), alors l’élément ne fait plus 100 % de la hauteur du
viewport mais davantage.
Si vous avez vraiment besoin d’utiliser l’unité vh de cette façon sur Safari iOS, sachez
qu’il existe un script JavaScript visant à corriger ce problème.
http://bit.ly/viewport-units-buggyfill
De plus, Safari sur iOS 7 définit les valeurs utilisant les unités de viewport à 0 si la
page est quittée puis réouverte dans les 60 secondes.
Pour finir, Safari sur iOS 7 recalcule les largeurs (width) définies en vh en vw, et les
hauteurs définies en vw en vh quand l’orientation de l’appareil change.
CHROME 33 ET IOS 6 ET 7 Problème d’interprétation des unités dans certains cas
En plus des problèmes mentionnés précédemment, Safari sur iOS 6 et 7, ainsi que
Chrome 33 et versions inférieures ne supportent pas les unités relatives au viewport
pour les propriétés border-width, column-gap, transform, box-shadow et toutes les propriétés
raccourcies auxquelles celles-ci sont rattachées. Similairement, les valeurs en rem
rendent la fonction calc() invalide.
WEBKIT Problème de rafraîchissement
Bien que Chrome et Safari supportent sans mal ces nouvelles unités, et que les
spécifications stipulent explicitement que lors du redimensionnement de la fenêtre le
calcul de ces unités est supposé être réeffectué, l’implémentation de ces deux
navigateurs est partiellement défectueuse puisque le calcul n’est réalisé qu’au
chargement de la page.
Si vous désirez forcer le rafraîchissement du calcul de ces unités lors du
redimensionnement du viewport, vous pouvez déclencher un repaint sur les éléments
dont une propriété utilise une valeur en vh ou vw. Pour cela, un simple écouteur
JavaScript suffit :
window.onResize = function () {

document.body.style.zIndex = 'auto';
}

Modifier la valeur de la propriété z-index, même pour réappliquer la valeur déjà


existante, suffit à déclencher un repaint sur l’élément concerné, et donc le calcul des
unités relatives au viewport. Notez néanmoins que ça marche avec d’autres propriétés
CSS. Ce n’est pas très propre et peu s’avérer potentiellement coûteux mais c’est pour
l’instant une des seules solutions possibles pour pallier ce problème.
SUPPORT Un polyfill en JavaScript
Notons également qu’il existe un polyfill JavaScript correct permettant d’émuler le
support pour les anciens navigateurs et par la même occasion de corriger ce fâcheux
bug sur Chrome et Safari.
http://bit.ly/vminpoly

Pourcentage de la largeur/hauteur avec vmin et vmax


Les deux unités vmin et vmax sont directement rattachées aux deux unités que nous venons
de voir dans la mesure où elles empruntent la valeur de l’une d’elles en fonction des
circonstances. Je m’explique. Une valeur spécifiée en vmin correspondra à un pourcentage
de la plus petite des deux dimensions du viewport. De la même façon, vmax correspondra à
un pourcentage de la plus grande des deux dimensions du viewport.
Par exemple, une valeur de 50vmax correspond à :
• 50 % de la largeur du viewport si la largeur est supérieure à la hauteur ;
• 50 % de la hauteur du viewport si la hauteur est supérieure à la largeur.

Cas pratique : ratio 16:9 occupant toute la largeur du


viewport
Un exemple très pertinent de l’utilisation des unités vmin et vmax serait de conserver l’aspect
d’une vidéo (ou d’un média quel qu’il soit) grâce aux unités de viewport. Imaginons que
l’on ait une vidéo de taille 16:9 dont on souhaite que sa largeur occupe toute celle du
viewport tout en étant toujours intégralement dans l’écran ; scroller pour voir une vidéo
n’est guère pratique.
/**
* 1. Largeur du viewport
* 2. 100vmin / 16 × 9

*/
iframe {
width: 100vmin; /* 1 */

height: 56.25vmin; /* 2 */
}

Voila tout ce dont nous avons besoin. Quelle que soit l’orientation de l’écran ou sa taille,
la vidéo occupera toujours un maximum de place sans jamais déborder de l’écran.

Compatibilité des navigateurs pour les unités vmin et vmax


Tableau 5–9 Navigateurs desktop

Tableau 5–10 Navigateurs mobiles

ATTENTION Support bancal pour vmax


Les navigateurs Internet Explorer, Chrome 20 à 25, Safari 6 et Safari iOS ne
supportent pas l’unité vmax.
Les dimensions intrinsèques
Derrière ce curieux nom se cache tout un module des spécifications de niveau 3 visant à
allouer davantage de flexibilité aux développeurs en ce qui concerne le dimensionnement
des éléments. En effet, ce module étend le système de dimensions de CSS avec de
nouvelles valeurs permettant de décrire de manière plus naturelle le comportement des
boîtes vis-à-vis de leur contenu et de leur contexte.

Largeur minimale avec min-content


La valeur min-content correspond à la plus petite mesure que peut prendre un élément pour
épouser son contenu si toutes les opportunités de césure dite « douce » sont saisies.

Cas pratique : figure dimensionnée selon la largeur de


l’image
L’exemple le plus probant pour illustrer cette valeur est probablement celui d’un élément
figure. Imaginons la structure HTML suivante d’une image avec sa légende :

<figure>
<img src="/images/vespa.jpg" alt="" />

<figcaption>Ceci est une très longue légende à propos de la photographie ci-dessus. Elle tire
inutilement en longueur et n’apporte rien de plus que ce que vous voyez déjà.</figcaption>
</figure>

ACCESSIBILITÉ L’élément figcaption et l’attribut alt


Il est de coutume de laisser l’attribut alt d’une image vide quand celle-ci est
contextualisée via l’élément figcaption.
Maintenant, imaginons que nous souhaitions que cet élément épouse les contours de
l’image (avec une éventuelle marge interne) et que la légende, trop longue, soit distribuée
sur plusieurs lignes. Sans min-content, c’est impossible à moins d’assigner une largeur à
l’élément, chose qui n’est pas forcément une bonne idée pour des raisons de fluidité.
Grâce à la valeur min-content, on peut tout à fait réaliser cela ! Il suffit d’attribuer width: min-
content à l’élément figure afin que celui-ci soit dimensionné selon son contenu le moins
large : en l’occurrence, la photo. Le texte va donc saisir toutes les opportunités de césure
(par exemple, les espaces entre les mots) pour rentrer dans la largeur du conteneur.
figure {

width: min-content;
margin: 0 auto 1em;

padding: 1em;

background: #EFEFEF;
border: 1px solid rgba(0, 0, 0, .1);

figure img {
display: block;
margin-bottom: 1em;

}
figcaption {

line-height: 1.3;
}

Figure 5–11
L’élément figure est dimensionné selon son contenu le moins large, en l’occurrence l’image.

Largeur maximale avec max-content


Comme vous pouvez le deviner, la valeur max-content fonctionne comme celle que l’on
vient de voir si ce n’est qu’elle se base sur la largeur maximale, c’est-à-dire la largeur la
plus grande possible en épousant le contenu si aucune opportunité de césure n’est saisie.
Si l’on reprend notre exemple précédent, cela signifie que le texte ne va en aucun cas être
distribué sur plusieurs lignes et donc que l’élément aura pour dimension la plus grande
largeur entre la largeur de l’image, et la largeur de la ligne de texte. Ici, ce sera celle de la
ligne de texte.

Figure 5–12
Avec max-content, la largeur de l’élément figure est déterminée par le contenu le plus large, ici la légende. Résultat : un
débordement horizontal.

Comportement de type block avec fill


Cette valeur, aussi connue sous l’ancien nom de fill-available, correspond à l’identique au
comportement par défaut des éléments de type block. Quand il est utilisé comme valeur
pour la propriété width, l’élément occupe toute la place disponible en prenant en compte les
marges internes et externes ainsi que les bordures.
Prenons par exemple un élément qui aurait une marge interne (padding) et une bordure. Si
on lui appliquait une largeur de 100% pour qu’il occupe toute la largeur disponible, il
déborderait de son conteneur.
En effet, rappelons que dans le modèle de boîte par défaut, les marges internes et les
bordures ne sont pas prises en compte dans le calcul des dimensions de width et height (voir
page 54).
Du coup, c’est l’occasion rêvée d’utiliser fill.
.element {

padding: 1em;
border: 1px solid;

width: fill;
}

Quand elle est appliquée comme valeur pour la propriété height, cela fonctionne de la
même manière, seulement ce n’est pas un comportement qui existe hors de cette
déclaration à l’heure actuelle.

Largeur optimale avec fit-content


La valeur fit-content est en quelque sorte un condensé des valeurs max-content et fill. Très
simplement, il s’agit du comportement des éléments flottants : la largeur est définie selon
le contenu via max-content, sauf si celle-ci s’avère plus grande que la largeur fill, auquel cas
le rendu est le même que pour fill. En somme :
fit-content = min(max-content, max(min-content, fill))

Cas pratique : liste centrée mais dimensionnée selon son


contenu
Un exemple pertinent pour l’utilisation de la valeur fit-content serait le centrage d’une liste
horizontale. Attention, nous parlons bien ici de centrer l’élément liste lui-même, pas
uniquement ses enfants. En effet, ce dernier cas peut être exécuté de manière assez simple
à l’aide de text-align: center sur la liste, et de display: inline-block sur les éléments de liste.
En l’occurrence, nous parlons bien de faire en sorte que le conteneur (ul) épouse son
contenu et soit centré dans la page. Bien que ça ait l’air impossible, fit-content rend ça
extrêmement facile. Voyez plutôt :
<ul>

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

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

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

Et le CSS :
/**

* 1. Dimensionnement
* 2. Centrage

*/
ul {

width: fit-content; /* 1 */

margin: auto; /* 2 */
}

li {

display: inline-block;
}

Et voilà !

Figure 5–13
Une liste centrée et dimensionnée selon son contenu grâce à fit-content

Compatibilité des navigateurs pour les dimensions


intrinsèques
Tableau 5–11 Navigateurs desktop

Tableau 5–12 Navigateurs mobiles

Bien que Firefox supporte le module de dimensions intrinsèques, il ne connaît pas


directement la valeur fill, mais available qui se trouve être une valeur tirée d’une très
ancienne version des spécifications.
De la même manière, le moteur de rendu WebKit (en tout cas, dans certaines versions) ne
connaît que la valeur fill-avalaible, valeur désormais obsolète au profit de fill.
Au vu du nombre de préfixes et des différentes variantes des propriétés, je ne peux que
vous recommander d’utiliser Autoprefixer afin de mettre un terme à tous vos soucis ! Si
vous ne pouvez pas vous permettre cette bibliothèque mais que vous utilisez un
préprocesseur, vous pouvez également mettre en place des mixins pour faciliter les choses.

Faciliter l’utilisation des valeurs intrinsèques avec un


préprocesseur (Sass)
L’utilisation de ces valeurs peut parfois être fastidieuse entre les navigateurs qui exigent
un préfixe constructeur et ceux qui ne nomment pas certaines valeurs comme les autres.
J’ai donc mis en place un petit mixin Sass qui permet de gérer cela plus facilement.
@mixin intrinsic($property, $value) {

// Si la valeur est fit-content, min-content ou max-content,


// on génère les versions préfixées et la version standard.

@if index(fit-content min-content max-content, $value) {

#{$property}: -webkit-#{$value};
#{$property}: -moz-#{$value};

#{$property}: $value;
}
// Si la valeur est fill, available ou fill-available,

// on génère la valeur appropriée pour chaque navigateur.


@else if index(fill-available available fill, $value) {
#{$property}: -webkit-fill-available;

#{$property}: -moz-available;
#{$property}: fill;
}

// Sinon, on génère la déclaration telle quelle.


@else {
#{$property}: $value;

}
}

On peut ensuite l’utiliser comme ceci :


.element {

@include intrinsic(width, fill-available);


}

Pour obtenir le CSS suivant :


.element {

width: -webkit-fill-available; /* Chrome, Safari & Opera */

width: -moz-available; /* Firefox */


width: fill; /* Standard */

}
6
Contrôle du texte

Bien que nous ne soyons toujours pas en mesure de répliquer à l’identique les mises en
page des magazines papier, CSS nous offre toujours plus de contrôle sur nos contenus
textuels.
Le contenu le plus important sur Internet reste le texte, dans la majorité des cas. Peuvent
éventuellement être exclues les galeries d’images, mais elles sont suffisamment rares pour
que l’on puisse statuer sur le fait que la plupart des sites sont constitués essentiellement de
contenus textuels. Parfois en grande quantité, notamment dans le cas des magazines qui
tendent de plus en plus à se tourner vers Internet pour leurs publications.
Aussi est-il normal que CSS apporte autant de contrôle sur le texte que le feraient d’autres
outils tels que Publisher ou InDesign. Nous avons vu dans les chapitres précédents qu’il
est possible de distribuer le texte dans plusieurs colonnes grâce au module CSS Columns
mais ça ne fait pas tout ! Qu’en est-il des césures ? De l’interlettrage ? De l’alignement ?
Tant de problématiques typographiques auxquelles CSS n’était simplement pas préparé
jusqu’à encore peu. Mais aujourd’hui, les nouveautés du langage, notamment celles
s’attelant à la typographie sur le Web, introduisent une pléthore d’outils afin de conférer
aux designers et développeurs un contrôle plus fin sur leurs contenus textuels.
Je vous propose donc de dédier ce chapitre à la découverte (ou redécouverte pour certains)
de ces propriétés.
La gestion des débordements avec overflow-wrap
La propriété overflow-wrap vise à déterminer si le navigateur est à même d’effectuer une
coupure à l’intérieur des mots si ceux-ci sont trop longs pour tenir sur une seule ligne au
sein de leur conteneur.
En temps normal, celui-ci ne s’autorise un retour à la ligne que sous deux conditions :
présence d’un espace sécable ou d’un trait d’union. Cette propriété permet donc au
navigateur de couper où il le souhaite. Elle ne propose que deux valeurs.
• normal, qui est la valeur par défaut et indique que le navigateur ne peut pas s’autoriser à
couper un mot en deux dans le cas où il serait trop long.
• break-word, qui indique que le navigateur peut arbitrairement couper un mot s’il est trop
long pour tenir dans son conteneur.
À noter que cette propriété n’aura d’effet que dans le cas où un mot serait trop long pour
rentrer sur une seule ligne dans son conteneur. On ne parle pas là de césure classique, qui
est, quant à elle, gérée par hyphens (voir plus loin). C’est pourquoi aucun caractère (tel que -
) ne sera inséré à l’emplacement de la césure.
Il est relativement rare que des termes soient trop longs pour déborder de leur conteneur.
En revanche, c’est quelque chose qui peut tout à fait se produire dans le cas des URL,
chaînes de caractères parfois très longues. Pour éviter qu’elles débordent hors du
conteneur et forcent un retour à la ligne, on peut tout à fait utiliser la propriété overflow-
wrap.

/**

* Autorise le navigateur à effectuer une césure agressive


* en cas de chaîne de caractères trop longue pour rentrer
* dans son conteneur (par exemple, une URL).

*/
.container {

overflow-wrap: break-word;
}

Compatibilité des navigateurs pour overflow-wrap


Tableau 6–1 Navigateurs desktop

Tableau 6–2 Navigateurs mobiles

SUPPORT À propos de word-wrap


La propriété overflow-wrap a été introduite par Microsoft sous le nom word-wrap, il y a fort
longtemps. Il n’est donc pas rare de voir word-wrap au lieu de la propriété officielle,
d’autant que de nombreux navigateurs supportent ce nom d’appoint.
De plus, les spécifications stipulent clairement que les navigateurs doivent supporter
word-wrap comme alias de la propriété overflow-wrap, afin de préserver une certaine
rétrocompatibilité. Notre exemple précédent s’écrirait plutôt de la façon suivante :
.container {

word-wrap: break-word;

overflow-wrap: break-word;
}
La gestion des espaces avec white-space
Par défaut, en HTML, les espaces successifs sont fusionnés en un espace unique (ou
même aucun espace dans certains cas). C’est pour cette raison que vous pouvez indenter
votre code HTML sans que cela ait d’impact sur le rendu de votre page. Ceci étant dit, il y
a des cas de figure où l’on souhaite conserver ces espaces et indentations, notamment dans
le cas où l’on souhaite présenter du code.
La propriété white-space permet de spécifier si et comment les espaces au sein d’un élément
sont fusionnés, et si les lignes peuvent être générées aux opportunités de création de ligne
habituelles. C’est grâce à cette propriété que l’on peut faire en sorte que du code indenté à
l’écriture reste indenté à l’affichage.
.element {
white-space: normal | pre | pre-wrap | nowrap | pre-line;
}

Cette propriété accepte donc cinq valeurs différentes :


• normal (comportement par défaut) :
– les espaces et tabulations sont fusionnés ;
– les retours à la ligne sont fusionnés ;
– les lignes sont générées naturellement ;
• pre :
– les espaces sont préservés ;
– les retours à la ligne sont préservés ;
– les lignes ne sont pas générées naturellement ;
• nowrap :
– les espaces sont fusionnés ;
– les retours à la ligne sont fusionnés ;
– les lignes ne sont pas générées naturellement ;
• pre-wrap :
– les espaces sont préservés ;
– les retours à la ligne sont préservés ;
– les lignes sont générées naturellement ;
• pre-line :
– les espaces sont fusionnés ;
– les retours à la ligne sont préservés ;
– les lignes sont générées naturellement.
CURIOSITÉ À propos de nowrap
La valeur nowrap aurait théoriquement dû s’intituler no-wrap. C’est une erreur de design
irrattrapable du CSS Working Group.
http://bit.ly/css-mistakes
En somme, on peut dresser le tableau récapitulatif suivant.
Tableau 6–3 Fonctionnement des valeurs de white-space

Comme vous pouvez le constater, les possibilités sont multiples et il n’est pas toujours
évident de choisir la bonne valeur d’autant qu’elles se ressemblent toutes plus ou moins.

Cas pratique : affichage de code


Illustrons tout ceci avec un exemple concret. Imaginons que vous désiriez afficher à
l’écran (dans le cadre d’un article, d’un styleguide, d’une documentation ou de tout autre
projet) un petit exemple de code CSS. Arbitrairement, utilisons le micro-clearfix de
Nicolas Gallagher, bout de code célèbre s’il en est, dont le but est de réinitialiser les
flottants à l’intérieur d’un élément.
/**
* Classe helper "clearfix" : à utiliser pour réinitialiser les flottants internes

*/
.clearfix::after {
content: '';

display: table;

clear: both;
}

Le code précédent correspond à celui que vous souhaitez afficher : indentation et retours à
la ligne compris. On va utiliser le combo pre > code pour créer un bloc d’affichage de code.
<pre><code>/**

* Classe helper "clearfix" : à utiliser pour réinitialiser les flottants internes

*/
.clearfix::after {

content: '';

display: table;
clear: both;

}</code></pre>

Maintenant, attardons-nous sur l’impact de la propriété white-space sur le rendu de ce bloc


de code à l’écran.
Figure 6–1
Avec normal, les espaces et les retours à la ligne ne sont pas préservés mais il n’y a pas de débordement.

Figure 6–2
Avec pre, les espaces et les retours à la ligne sont préservés, mais les lignes trop longues déclenchent un débordement
(ce qui peut être souhaité dans ce cas).

Figure 6–3
Avec nowrap, les espaces et les retours à la ligne ne sont pas préservés et il y a un débordement, produisant ainsi un
résultat sur une ligne unique.

Figure 6–4
Avec pre-wrap, les espaces et les retours à la lignes sont préservés, mais il n’y a pas de débordement.
Figure 6–5
Avec pre-line, les retours à la ligne sont préservés, mais les espaces sont fusionnés ce qui pose un souci par rapport à
l’indentation du code.

Autrement dit, si vous désirez afficher du code à l’écran, il est fort probable que vous vous
rabattiez sur la valeur pre (ou pre-wrap si vous souhaitez éviter les débordements) afin de
conserver l’indentation du code.
pre code {

white-space: pre;
}

Compatibilité des navigateurs pour white-space


Tableau 6–4 Navigateurs desktop

Tableau 6–5 Navigateurs mobiles

Les versions des navigateurs supportant l’intégralité des valeurs de white-space sont
présentées dans les tableaux précédents.
Ceci étant, la majorité des navigateurs supportaient déjà certaines valeurs dans des
versions antérieures, notamment :
• Internet Explorer 5.5 supportait normal et nowrap ;
• Internet Explorer 6 supportait normal, pre et nowrap ;
• Firefox 1 supportait normal, pre, nowrap et -moz-pre-wrap ;
• Firefox 3 supportait normal, pre, nowrap et pre-wrap ;
• Opera 4 supportait normal, pre et nowrap ;
• Opera 8 supportait normal, pre, nowrap et pre-wrap ;
• Safari 1 supportait normal, pre et nowrap.
Les débordements de texte et text-overflow
À nouveau la gestion des débordements ? Et oui ! En fait, nous avions besoin d’étudier la
propriété white-space entre temps pour que vous puissiez bien comprendre nos futurs
exemples.
La propriété text-overflow a pour but de gérer le rendu du texte quand il déborde de son
conteneur ; soit parce qu’on l’empêche d’être étalé sur plusieurs lignes (via white-space:
nowrap), soit parce qu’une chaîne de caractères est simplement trop longue pour rentrer sur
une seule ligne, par exemple.
.element {

text-overflow: clip | ellipsis | <string>;


}

La syntaxe pour cette propriété est très permissive, puisque non seulement elle autorise les
valeurs clip et ellipsis mais aussi n’importe quelle chaîne de caractères. Commençons par
expliquer les valeurs :
• clip : cache la partie débordante (un caractère peut donc être à moitié rendu) ;
• ellipsis : cache la partie débordante et affiche des points de suspension (U+2026) ;
• <string> : cache la partie débordante et affiche la chaîne de caractères renseignée.
ATTENTION Syntaxe à deux valeurs et valeur <string>
Les spécifications précisent qu’il est possible de spécifier deux valeurs, une pour le
bord end (droit en mode d’écriture de gauche à droite) et une pour le bord start (gauche
en mode d’écriture de gauche à droite). Toutefois, celles-ci mentionnent que la syntaxe
à deux valeurs et la valeur <string> sont considérées « à risque », c’est-à-dire qu’elles
sont susceptibles d’être retirées du module. Je vous recommande donc de ne pas les
utiliser.
Il devient donc possible de faire en sorte qu’un contenu ne tienne que sur une ligne, et que
le débordement soit caché via CSS par des points de suspension (ou tout autre chaîne de
caractères). S’agissant d’une solution purement native à CSS, le contenu en lui-même
reste tout à fait accessible et indexable.
En revanche, pour que cette propriété ait un effet, il faut que la valeur d’overflow du
conteneur soit définie à autre chose que visible, généralement hidden.
/**

* 1. Force l’affichage du contenu sur une seule ligne

* 2. Masque les débordements de contenu hors du conteneur


* 3. Ajoute des points de suspension en fin de ligne

*/

.ellipsis {
white-space: nowrap; /* 1 */

overflow: hidden; /* 2 */

text-overflow: ellipsis; /* 3 */
}
CURIOSITÉ À propos de text-overflow et overflow-wrap
Dans un cas où white-space: nowrap ne serait pas impliqué (c’est-à-dire du contenu sur
plusieurs lignes), on remarque une certaine incompatibilité entre les propriétés text-
overflow et overflow-wrap (word-wrap) si cette dernière a pour valeur break-word.

En effet, cette déclaration spécifie explicitement d’effectuer une césure en fin de ligne
si les mots sont trop longs, alors que text-overflow déclare de son côté qu’il faut cacher
les débordements.
En l’occurrence, il n’y a pas de débordement car c’est word-wrap: break-word qui prend le
pas et les mots sont coupés par un retour à la ligne.

Cas pratique : des lignes de tableau de même hauteur


Lorsque l’on utilise un tableau au sein d’une interface, il arrive que l’on souhaite avoir des
lignes de même hauteur. Mais comment faire lorsque le contenu est dynamique ? Et
lorsque la largeur du tableau dépend de la taille de la fenêtre ?
C’est typiquement le cas de figure où nous pouvons tirer parti de white-space: nowrap et text-
overflow: ellipsis pour faire en sorte que les lignes du tableau n’aient jamais plus d’une
ligne de texte et qu’elles soient proprement terminées par ….
En guise d’exemple, reprenons grossièrement le tableau utilisé sur les dépôts GitHub
constitué de trois colonnes : le nom du fichier, la description du dernier commit ayant
modifié ledit fichier et la date dudit commit.
L’idée est de rendre la colonne centrale – celle dédiée à la description du dernier commit –
fluide. Pour ce faire, on désire que le contenu soit nécessairement sur une seule ligne,
quitte à le tronquer en y ajoutant la chaîne de caractères ….
<table>
<thead>

<tr>

<th>Filename</th>
<th>Last commit description</th>

<th>Last commit date</th>

</tr>

</thead>
<tbody>

<tr>

<td>src</td>
<td><p>fix: add implicit type for required placeholders</p></td>

<td>a day ago</td>

</tr>
<tr>

<td>.gitignore</td>

<td><p>Add `grunt update-image` task.</p></td>


<td>17 days ago</td>
</tr>

<tr>
<td>README.md</td>

<td><p>Fixed a broken repo link.</p></td>


<td>2 days ago</td>
</tr>

<tr>

<td>package.json</td>
<td><p>Bump patch 1.5.2</p></td>

<td>a day ago</td>

</tr>
<!-- Autant de lignes qu’on le souhaite -->

</tbody>
</table>

Dans un souci de simplicité, on ne discutera pas ici des styles destinés à embellir notre
tableau ; allons à l’essentiel et contentons nous de rendre la description du dernier commit
(la seconde colonne de chaque rangée) fluide.
Considérez l’exemple suivant :
/**
* 1. Donne une largeur implicite aux colonnes
*/

table {
table-layout: fixed; /* 1 */
width: 100%;

}
/**
* 1. Force l’affichage du contenu sur une seule ligne
* 2. Masque les débordements de la cellule

* 3. Ajoute des points de suspension en fin de ligne

*/
td p {

white-space: nowrap; /* 1 */

overflow: hidden; /* 2 */
text-overflow: ellipsis; /* 3 */

}
Figure 6–6
Notre tableau de commits sur un écran de grande taille
Figure 6–7
Notre tableau de commits, sur un écran de petite taille, non déformé

Compatibilité des navigateurs pour text-overflow


Tableau 6–6 Navigateurs desktop

Tableau 6–7 Navigateurs mobiles

Malgré le support excellent de text-overflow, Firefox est le seul navigateur capable de


masquer le débordement avec une chaîne de caractères arbitraire. De plus, c’est également
le seul navigateur supportant la syntaxe à deux arguments. Ceci étant dit, et comme nous
l’avons vu, ce sont là deux fonctionnalités à éviter.
Les césures avec hyphens
Nous avons débuté ce chapitre en apprenant comment nous pouvons autoriser le
navigateur à effectuer une césure dans le cas où une chaîne de caractères s’apprêterait à
sortir de son conteneur à cause de sa taille trop grande, mais c’est là somme toute un cas
assez rare. En revanche, un cas plus fréquent serait la césure dite « classique », par
exemple dans le cas d’un texte justifié.
Lorsque vous justifiez un texte, le navigateur calcule ligne par ligne combien de mots sont
susceptibles de tenir. Or il arrive que plusieurs mots un peu longs se suivent, occupant
presque la totalité d’une ligne. Du coup, un nouveau mot ne pouvant pas rentrer et le texte
devant être justifié, l’interlettrage entre ces quelques mots devient important, ce qui n’est
visuellement pas terrible : les mots se retrouvent séparés par de larges espaces.
L’idéal est que les espaces entre les mots soient plus ou moins équivalents, autant que faire
se peut en tout cas, quitte à effectuer des césures douces (avec un signe -) en fin de ligne si
un mot ne rentre pas intégralement. C’est là un procédé tout à fait courant en imprimerie,
et tous les logiciels de traitement de texte sont capables d’effectuer ceci.
Pourtant, il a fallu attendre longtemps pour que CSS présente une propriété hyphens (qui
aurait définitivement dû s’appeler hyphenate) permettant de contrôler les césures douces en
cas de justification du texte. En revanche, l’impact de cette propriété dépend de la langue,
et toutes ne sont pas supportées par tous les navigateurs.
CURIOSITÉ À propos du nom hyphens
La propriété hyphens aurait théoriquement dû s’intituler hyphenate. Pour la petite histoire,
elle a le nom qu’elle a car le groupe de travail responsable des normes XSL-FO (XSL
Formatting Object) a refusé hyphenate pour des raisons de cohérence entre les deux
langages.

Syntaxe
La propriété hyphens accepte trois valeurs uniquement :
hyphens: none | manual | auto

Parce que chaque valeur nécessite un certain nombre de détails afin de bien comprendre
comment elle fonctionne, je vous propose de les décortiquer une par une.

none
De manière tout à fait évidente, la valeur none empêche les césures. Les mots ne sont
absolument jamais divisés en fin de ligne, même si un caractère autoriserait parfaitement
le navigateur à effectuer une césure à cet endroit. Autrement dit, les lignes ne sont coupées
qu’aux espaces blancs et aux traits d’union.
Figure 6–8
Les césures sont proscrites et résultat, les espaces entre les mots sont parfois disgracieux.

manual (valeur initiale)


Les mots ne peuvent être coupés qu’aux endroits où un caractère indique une opportunité
de césure. Il n’y a que deux caractères susceptibles de faire cela :
• U+2010 (HYPHEN) : le trait d’union dur (-) ; même si la ligne n’est pas coupée à cet
endroit, le trait d’union est affiché ;
• U+00AD (SHY) : le trait d’union conditionnel, invisible (aussi appelé soft hyphen) dont le
but est de suggérer un endroit où le navigateur peut effectuer une césure si besoin est.
En HTML, c’est l’entité &shy;.
Autrement dit, si le navigateur rencontre un hyphen ou un soft hyphen au sein d’un mot et
que la propriété hyphens a pour valeur manual, il est tout à fait autorisé à effectuer une césure
si le besoin se fait sentir.
/**
* Autorise les césures au sein des mots si :

* - un trait d’union est rencontré (par exemple, mot-clé)

* - l’entité &shy; est rencontrée (par exemple spé&shy;cifications)


*/

p {

hyphens: manual;
}

auto
Finalement, la valeur auto autorise le navigateur à effectuer des césures non seulement
dans les cas de la valeur manual (hyphen et soft hyphen) mais également aux endroits
déterminés par un dictionnaire de césure spécifique à la langue utilisée (supporté par le
navigateur de manière native, ou renseignée par la directive @hyphenation-resource, non
traitée ici). En français, par exemple, on aura tendance à couper une ligne entre deux
lettres identiques successives quand c’est possible, comme le veut la règle typographique.
Figure 6–9
Les césures sont autorisées, du coup il y a moins d’espaces trop larges.

À noter que les opportunités de césure universelles (manual) prennent le pas sur les cas de
césure relatifs à la langue dans le cas où hyphens aurait pour valeur auto.

Compatibilité des navigateurs pour hyphens


Tableau 6–8 Navigateurs desktop

Tableau 6–9 Navigateurs mobiles

SUPPORT Une solution de repli en JavaScript : Hyphenator.js


Le support de la propriété hyphens n’est pas merveilleux, sans être catastrophique non
plus. À ce niveau, le plus gros problème est Chrome (pour une fois).
Toutefois, selon l’importance que vous donnez à votre contenu textuel, il se peut que
cette propriété ne soit pas suffisante, surtout si vous tenez à ce que vos textes
respectent les conventions typographiques d’imprimerie.
Il faut alors se tourner vers une solution en JavaScript. Heureusement, vous n’aurez
pas à la développer à la main ! Une bibliothèque existe déjà pour cela : Hyphenator.js.
Elle repose sur des dictionnaires de langues afin d’injecter des traits d’union
conditionnels (&shy;) dans le contenu. Mieux encore, dans le cas où la propriété CSS
hyphens serait supportée, le script JS se contentera de l’utiliser sans avoir recours à de la
manipulation de texte.
Le plus gros problème de cette bibliothèque – au-delà du fait qu’elle requiert
JavaScript pour un but strictement visuel – est qu’elle peut poser souci dans le cas de
recherches (via Ctrl+F) sur un mot dans lequel aurait été injecté une entité &shy;. En
effet, pour le navigateur, le terme n’existe pas à cause des hyphens invisibles.
http://bit.ly/hyphenator
Si vous jugez cette fonctionnalité trop importante pour risquer de la perdre avec
Hyphenator.js, David Newton propose une solution aboutie en JavaScript dans cet
article :
http://bit.ly/hyphenation
Les césures agressives avec word-break
En quelque sorte, la propriété word-break est complémentaire avec les propriétés hyphens et
word-wrap dans le sens où elle permet de définir si oui ou non, les césures peuvent être
effectuées n’importe où dans les mots. Elle n’accepte que trois valeurs :
• normal : règles de césure par défaut, valeur définie dans la section hyphens ;
• break-all : des césures peuvent être effectuées entre n’importe quel caractère pour les
textes non CJC (chinois/japonais/coréen) ;
• keep-all : comme pour normal, sauf dans le cas de textes CJC où la césure est proscrite.
Le problème de cette règle est qu’elle est extrêmement agressive vis-à-vis des césures : les
mots démarrent à l’endroit où ils sont supposés démarrer, mais s’ils sont trop longs pour
rentrer dans la ligne, alors ils sont purement et simplement tranchés au beau milieu sans
même qu’un caractère de césure ne soit inséré ni qu’un calcul de redéfinition de l’espace
soit effectué.
On peut se demander la plus-value d’une telle propriété sachant qu’overflow-wrap (word-wrap)
s’occupe déjà de rompre les chaînes causant un débordement et hyphens se charge de la
césure des mots. D’après les spécifications (qui ne sont pas toujours très claires, faute de
contexte), il semblerait que cette propriété soit essentiellement utilisée dans le but de
rompre des mots non CJC dans des contenus CJC.
Par conséquent, et au vu de la nature très agressive de cette propriété, je ne recommande
pas son utilisation dans des zones à contenu textuel dense, type article. En coupant les
mots à des endroits impromptus sans même ajouter de traits d’union, elle rend la lecture
très difficile.

Compatibilité des navigateurs pour word-break


Tableau 6–10 Navigateurs desktop

Tableau 6–11 Navigateurs mobiles

En revanche, seuls Internet Explorer et Firefox supportent la valeur keep-all ; tous les
autres navigateurs ne supportent que normal et break-all. Fort heureusement, cette dernière
est la plus utilisée.
La gestion des tabulations avec tab-size
Poursuivons sur une fonctionnalité directement liée à la propriété white-space. Lorsque vous
définissez la valeur de la propriété white-space à pre ou pre-wrap (qui conservent les espaces
et les tabulations) et que le contenu de l’élément a été indenté avec des tabulations (U+0009),
vous ne pouvez pas savoir à l’avance le rendu que cela va donner puisque la notion de
tabulation n’est pas une mesure fixe mais un caractère dont la taille peut varier.
Aussi étonnant que cela puisse paraître, la taille par défaut d’une tabulation est de 8
espaces (le caractère U+0020). Dans la majorité des cas, c’est bien trop, aussi vous allez
vouloir réduire cette distance à une valeur plus convenable, comme deux ou quatre
espaces (selon les choix de chacun sur l’indentation).
Pour cela, la propriété tab-size existe. Elle accepte un nombre ou une longueur comme
valeur. S’il s’agit d’un entier, la valeur indique l’équivalence vis-à-vis du nombre
d’espaces (U+0020), par défaut 8 (oui, oui… 8). Si c’est une longueur, elle indique tout
simplement la longueur d’une tabulation.
pre code {
tab-size: 2;

Je trouve plus représentatif de raisonner en termes de nombre d’espaces plutôt que de


dimensions (relatives ou absolues) parce que la majorité des éditeurs de texte utilisent une
police monospace, c’est-à-dire une police où tous les caractères sont de même largeur.
Aussi, est-il plus facile de déterminer qu’une tabulation équivaut à deux ou quatre
caractères, plutôt qu’à 20 px, qui n’a finalement de sens qu’en CSS.

Compatibilité des navigateurs pour tab-size


Tableau 6–12 Navigateurs desktop

Tableau 6–13 Navigateurs mobiles


Une ponctuation plus élégante avec hanging-
punctuation
Le concept de « ponctuation hors justification » (hanging punctuation) est une règle
typographique optionnelle qui veut que l’on sorte certains signes de ponctuation hors des
marges de justification afin de conserver justement l’alignement engendré par ces marges.
Ce phénomène intervient lorsqu’une ligne de texte débute par un signe de ponctuation
comme un guillemet (simple ou double), une apostrophe, un tiret (normal, cadratin ou
demicadratin), ou s’achève en fin de ligne par un signe de ponctuation comme un point,
une virgule, un point-virgule. La règle voudrait que l’on décale le symbole de ponctuation
dans la marge afin de conserver l’alignement produit par le texte.

Figure 6–10
Un exemple de ponctuation hors justification (ou « suspendue »)

Cette notion typographique a été portée en CSS par la propriété hanging-punctuation. Pour
faire simple, celle-ci accepte trois valeurs bien différentes :
• none : pas de ponctuation hors justification (valeur par défaut) ;
• first : un guillemet ou une parenthèse ouvrante en début de ligne, est déplacé dans la
marge ;
• last : un guillemet ou une parenthèse fermante en fin de ligne, est déplacé dans la marge.
CLARIFICATION Caractères impactés
Je mentionne « un guillemet ou une parenthèse », mais il faut savoir que c’est un
comportement qui s’applique en réalité à tous les caractères Unicode des catégories Ps
(ouverture de ponctuation), Pe (fermeture de ponctuation), Pi (guillemet ouvrant) et Pf
(guillemet fermant).
Notons en réalité que les spécifications mentionnent également :
• force-end : un point ou une virgule en fin de ligne est nécessairement déplacé dans la
marge ;
• allow-end : un point ou une virgule en fin de ligne est déplacé dans la marge si elle ne
tient pas sur la ligne avant la justification.
Néanmoins, ces deux dernières valeurs sont selon moi trop spécifiques pour avoir un réel
intérêt. Non seulement elles sont si similaires que la distinction aurait pu être épargnée,
mais de plus elles n’affectent qu’une collection restreinte de caractères (voir tableau
suivant).

Figure 6–11
La valeur allow-end

Figure 6–12
La valeur force-end

La notion de « un point ou une virgule » des propriétés force-end et allow-end réfère à la liste
de caractères suivante.
Tableau 6–14 Caractères englobés dans la notion de « un point ou une virgule »

Code Caractère Nom officiel


U+002C , COMMA
U+002E . FULL STOP
U+060C ، ARABIC COMMA
U+06D4 ‫۔‬ ARABIC FULL STOP
U+3001 、 IDEOGRAPHIC COMMA
U+3002 。 IDEOGRAPHIC FULL STOP
U+FF0C , FULLWIDTH COMMA
U+FF0E . FULLWIDTH FULL STOP
U+FE50 ﹐ SMALL COMMA
U+FE51 ﹑ SMALL IDEOGRAPHIC COMMA
U+FE52 ﹒ SMALL FULL STOP
HALFWIDTH IDEOGRAPHIC FULL
U+FF61 。
STOP
HALFWIDTH IDEOGRAPHIC
U+FF64 、
COMMA

Cette liste n’est pas exhaustive, aussi les navigateurs sont-ils tout à fait autorisés à y
ajouter des caractères s’ils le jugent approprié.
PLUS D’INFOS
Si les jeux de typographie vous intéressent, et pour aller plus loin dans l’utilisation de
hanging-punctuation, je vous suggère cet article de Steve Hickey dans lequel il décrit
comment les spécifications auraient selon lui dû être écrites.
http://bit.ly/hickey-hanging-punctuation

Compatibilité des navigateurs pour hanging-punctuation


Tableau 6–15 Navigateurs desktop
Tableau 6–16 Navigateurs mobiles

Malheureusement, cette propriété n’est à l’heure actuelle implémentée par aucun


navigateur. Toutefois, il est possible d’utiliser un text-indent négatif comme alternative
même si ce n’est pas idéal puisque ça ne couvre que le début de la première ligne et ce
quel que soit le caractère d’ouverture. De plus, la valeur d’indentation négative (dans la
marge) est relative à la police utilisée, ce qui nécessite d’ajuster la valeur pour chaque
police.
De meilleurs alignements avec text-align-last
Vous connaissez bien évidemment la propriété text-align dont nous avons parlé en début de
chapitre afin d’introduire la notion de césure. Généralement, lorsqu’un texte est justifié,
les lignes précédant un retour à la ligne forcé (fin de bloc de texte, <br>…) et la dernière
ligne du texte ne le sont pas ; elle sont alignées dans le sens de lecture (généralement à
gauche).
Autrement dit, on n’a pas de contrôle direct sur ces lignes trop courtes pour générer une
ligne complète. C’est précisément ce que la propriété text-align-last tend à corriger. Elle
accepte les mêmes valeurs que la propriété text-align et permet donc d’aligner
intégralement du contenu textuel, par exemple, ou de centrer ces dernières lignes.
.element {
text-align: justify;
text-align-last: center;

Figure 6–13
Centrage de la dernière ligne de texte grâce à text-align-last

ATTENTION text-align-last: justify et contenu dynamique


Attention, en revanche, lors de l’utilisation de text-align-last: justify avec du contenu
dynamique : si la dernière ligne d’un bloc ne contient que quelques mots, l’espace
entre ces mots va être extrêmement grand afin de justifier la ligne. Le rendu ne sera
bien évidemment pas optimal.

Compatibilité des navigateurs pour text-align-last


Tableau 6–17 Navigateurs desktop

Tableau 6–18 Navigateurs mobiles


La restriction de caractères avec unicode-range
Au risque de paraître un peu rabat-joie, je dirais que les « webfonts » (les polices de
caractères destinées au Web) sont autant une bénédiction qu’un fléau. Effectivement, nos
documents sont toujours plus beaux grâce aux jeux de police qu’on leur applique, mais ils
sont aussi de plus en plus lourds à cause de toutes ces polices que l’on charge.
Nous ne parlerons pas là des moyens de chargement asynchrone des polices de caractères
car c’est tout à fait hors du cadre de notre ouvrage. En revanche, sachez qu’il existe un
moyen de prévenir le téléchargement d’une police quand les caractères du texte stylé ne
sont pas supportés par la police en question.
Tout cela grâce à la propriété unicode-range. Celle-ci ne s’applique pas à un sélecteur mais à
une déclaration @font-face (que l’on connaît déjà trop bien). Elle permet de spécifier les
caractères supportés par la police appelée afin que le navigateur évite de la télécharger si
le texte sur lequel elle est appliquée ne contient que des caractères non supportés par celle-
ci.
En somme, la propriété unicode-range ne permet pas de ne télécharger que certains glyphes
d’une police, mais bien d’utiliser des polices légères ne contenant qu’un ensemble réduit
de caractères pour un téléchargement rapide tout en pouvant définir des polices de repli
(fall-backs) contenant beaucoup plus de glyphes.
Nous n’allons pas rentrer trop profondément dans la syntaxe parfois complexe. Pour faire
simple, il est possible de spécifier des « points de code Unicode » (débutant par U+) séparés
par des virgules, ou bien des intervalles de points en spécifiant un point de début et un
point de fin séparés par un trait d’union. Enfin, il est possible d’utiliser un intervalle
« joker » (dit « wildcard »).
PLUS D’INFOS À propos des points de code Unicode
Pour plus d’informations au sujet des points de code Unicode, notamment sur les
intervalles, je vous invite à lire la page Wikipédia dédiée.
http://bit.ly/wikipedia-unicode
Voici quelques exemples de déclarations valides :
/* Le symbole du yen */

unicode-range: U+A5;
/* Les caractères ASCII basiques */

unicode-range: U+0-7F;

/* Les caractères hébreux */


unicode-range: U+590-5ff;

/* Le symbole du yen et les alphabets hiragana, katakana

* ainsi que les kanjis */


unicode-range: U+A5, U+4E00-9FFF, U+30??, U+FF00-FF9F;

Il n’est pas toujours évident de comprendre le fonctionnement de cette propriété, je vous


propose donc un exemple défini dans les spécifications du module auquel cette propriété
appartient.
Considérons une police de caractères construite de sorte à optimiser la bande passante en
divisant les ensembles de caractères Latin, Japonais et autres dans différents fichiers.
La même police (ici DroidSans) est définie trois fois, avec trois fichiers différents, et trois
valeurs d’unicode-range différentes.
/**
* Police intégrale (4.5Mo),

* contenant tous les caractères supportés

* Pas de déclaration `unicode-range`


*/

@font-face {

font-family: DroidSans;
src: url(DroidSansFallback.woff);

}
/**
* Glyphes japonais (1.2Mo)

*/
@font-face {
font-family: DroidSans;

src: url(DroidSansJapanese.woff);
unicode-range: U+3000-9FFF, U+ff??;
}

/**
* Glyphes latins, grecs, cyrilliques
* et certains caractères de ponctuation (190Ko)

*/
@font-face {
font-family: DroidSans;
src: url(DroidSans.woff);

unicode-range: U+000-5FF, U+1e00-1fff, U+2000-2300;

Maintenant, considérons un texte tout à fait standard comme cette phrase auquel on
appliquerait la police “ DroidSans ”.
<!-- HTML -->
<p>CSS, c’est fun !</p>

/* CSS */

p {
font-family: DroidSans;

Le navigateur effectue une vérification sur la dernière déclaration @font-face (set latin) :
tous les caractères de notre texte se trouvent dans l’intervalle Unicode U+000-5FF. Le
navigateur télécharge le fichier DroidSans.woff et applique la police au texte.
Maintenant, modifions notre texte initial pour y inclure un caractère spécial : une flèche
(⇨) dont le point de code Unicode est U+21E8, écrite via l’entité HTML &#x21e8.
<!-- Introduction du caractère dans le texte -->

<p>CSS &#x21e8; fun !</p>

Le navigateur effectue le même test que précédemment sur le set latin : l’intervalle U+2000-
2300 contient U+21E8 alors la police est téléchargée. Toutefois, elle ne contient pas de glyphe
pour ce caractère (c’est-à-dire que l’unicode-range déclaré n’est pas l’unicode-range effectif,
dans un souci de simplicité).
Le navigateur va alors évaluer la police contenant les glyphes japonais. La valeur de
unicode-range déclarée n’inclue pas U+21E8 puisqu’elle démarre à U+3000, alors cette police
n’est pas téléchargée.
Enfin, le navigateur évalue la dernière police qui n’a pas d’unicode-range spécifié, ce qui
revient à considérer tous les points de code Unicode. Elle est alors téléchargée et
appliquée.
Cet exemple montre comment il est possible d’optimiser la bande passante en s’épargnant
le téléchargement de polices non utilisables grâce à la propriété unicode-range. C’est bien
évidemment un cas d’usage que l’on va essentiellement rencontrer dans un contexte multi-
alphabet.
REMARQUE Google Fonts
Le service très populaire de Google, Google Fonts, utilise la propriété unicode-range
dans la feuille de styles desservie afin de limiter les téléchargements superflus.
Par exemple, pour la police Lobster, appelée avec le navigateur Chrome (précision qui
a son importance quand on sait que Google effectue de la détection de navigateur pour
ne servir que les formats supportés, en l’occurrence woff2) :
/* cyrillic */
@font-face {
font-family: ’Lobster’;

font-style: normal;
font-weight: 400;

src: local(’Lobster’),
url(http://fonts.gstatic.com/s/lobster/v9/MeFZ5NpSE1j8mC06Jh1miALUuEpTyoUstqEm5AMlJo4.woff2)
format(’woff2’);
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;

/* latin-ext */
@font-face {

font-family: ’Lobster’;

font-style: normal;
font-weight: 400;

src: local(’Lobster’),
url(http://fonts.gstatic.com/s/lobster/v9/MCZn_h27nLxWmTqnbmnb3gLUuEpTyoUstqEm5AMlJo4.woff2)
format(’woff2’);
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-
A7FF;
}

/* latin */

@font-face {
font-family: ’Lobster’;

font-style: normal;
font-weight: 400;

src: local(’Lobster’), url(http://fonts.gstatic.com/s/lobster/v9/G6-


OYdAAwU5fSlE7MlBvhQLUuEpTyoUstqEm5AMlJo4.woff2) format(’woff2’);
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F,
U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;

http://bit.ly/fonts-googleapis

Cas pratique : embellir les esperluettes


L’esperluette – communément appelée « ampersand » ou encore de manière très
disgracieuse « le et commercial » – est un caractère bien particulier. Déjà de par sa forme,
mais aussi par son sens : la conjonction. Peu de caractères représentent un mot à part
entière (exception faite des idéogrammes).
C’est pourquoi il est très utilisé en typographie, généralement dans une police alternative
pour lui donner une certaine emphase. Figurez-vous qu’il est tout à fait possible d’en faire
de même en CSS.

Figure 6–14
Une esperluette personnalisée grâce à unicode-range

Pour commencer, nous allons déclarer une police appelée « Ampersand », qui utilise la ou
les polices de votre choix dont vous considérez l’esperluette comme élégante. Cette police
va utiliser la propriété unicode-range pour ne déclarer que ce point de code Unicode.
@font-face {
font-family: 'Ampersand';

src: url('MyAwesomeFont.ttf') format('woff')


url('MyAwesomeFont.svg') format('svg')

url('MyAwesomeFont.ttf') format('truetype');

unicode-range: U+26;
}

h1 {

font-family: 'Ampersand', 'Helvetica', 'Arial', sans-serif;


}

Théoriquement, ce code devrait suffire. Je dis bien « théoriquement » car dans la pratique,
ce n’est jamais si facile. Pour les navigateurs qui supportent unicode-range, tout se passe
bien : s’il y a une esperluette dans le texte stylé, la police est téléchargée et appliquée sur
l’esperluette uniquement.
En revanche, les navigateurs qui n’interprètent pas la propriété unicode-range procèdent
comme avec n’importe quelle propriété non supportée : ils la passent. Or une déclaration
@font-face sans unicode-range utilise l’intervalle par défaut, c’est-à-dire l’intégralité
d’Unicode. Ces navigateurs vont donc appliquer votre police à tout le texte.
Il faut donc ruser pour s’assurer que seul le caractère esperluette soit concerné. Pour cela,
nous pouvons ajouter une seconde déclaration @font-face qui vient se cumuler à la première
ou la remplacer totalement selon le support d’unicode-range. Par exemple :
@font-face {
font-family: 'Ampersand';

src: url('MyAwesomeFont.ttf') format('woff'),

url('MyAwesomeFont.svg') format('svg'),
url('MyAwesomeFont.ttf') format('truetype');

unicode-range: U+26;

}
@font-face {

font-family: 'Ampersand';
src: local('Helvetica'), local('Arial');
unicode-range: U+270C;

}
h1 {
font-family: 'Ampersand', 'Helvetica', 'Arial', sans-serif;

Vous avez remarqué que la seconde @font-face contient une déclaration d’unicode-range, sur
le point de code U+270C ; nous allons voir pourquoi dans un instant. De plus, les fichiers de
polices utilisées sont des fichiers locaux, c’est-à-dire des polices dites « système ».
Les navigateurs ne supportant pas unicode-range vont donc appliquer la deuxième
déclaration au texte (car définie plus en aval), à savoir la police Helvetica (ou Arial si
Helvetica n’existe pas).
En revanche, les navigateurs interprétant unicode-range font une vérification sur le texte afin
de voir si les caractères demandés sont présents dans l’intervalle Unicode. En
l’occurrence, l’intervalle Unicode de notre deuxième déclaration ne contient qu’un
caractère, très rarement utilisé : la « main de la victoire » (✌).
Le caractère n’étant pas utilisé dans notre cas, le navigateur remonte sur la première
déclaration. Il s’assure de la présence des caractères spécifiés dans l’intervalle Unicode –
l’esperluette – puis télécharge et applique la police spécifique à l’esperluette, et la police
suivante dans la « font stack » (Helvetica) pour le reste du texte. Tout le monde est
gagnant !
CLARIFICATION La main de la victoire
Nous avons utilisé le caractère de la main de la victoire (U+270) afin de nous assurer que
l’intervalle Unicode ne serait jamais effectif, mais vous pouvez tout à fait choisir un
autre caractère rare.
Cette astuce provient d’un article de Drew McLellan sur le site 24ways.org, que je
vous invite à lire si le sujet vous intéresse.
http://bit.ly/unicode-range
Compatibilité des navigateurs pour unicode-range
Tableau 6–19 Navigateurs desktop

Tableau 6–20 Navigateurs mobiles

Bien que les navigateurs clament supporter la propriété unicode-range, il faut savoir qu’ils ne
se comportent pas tous bien pour autant. Ci-dessous un récapitulatif de l’état du support de
cette fonctionnalité.
Tableau 6–21 À gauche le test ainsi que le résultat attendu ; dans chaque colonne, les polices téléchargées par le
navigateur. En gras les résultats corrects.
Cas pratique : des blocs de code qui donnent envie
Vous êtes probablement des lecteurs assidus de blogs techniques et par conséquent vous
avez sûrement déjà rencontré des blocs de code qui ne donnent pas du tout envie d’être
lus… Une indentation chaotique, des retours à la ligne apocalyptiques, pas de coloration
syntaxique, et bien d’autres rimes en -ique…
Pourtant, mettre en place des blocs de code corrects n’est pas si sorcier, surtout avec tout
ce que nous venons d’étudier ! Aussi je vous propose de voir comment faire avec un court
exemple de code.
pre > code {

/* Comportement */
display: block;

overflow-x: auto;
/* Espacements */

padding: 1em;
tab-size: 2;
margin-bottom: 1em;

/* Typographie */
font: 1em/1.5 "Consolas", "Monaco", "Lucida Console", "Liberation Mono", "DejaVu Sans Mono",
"Courier New", monotype;
white-space: pre;
/* Thème */

background: #EFEFEF;
border-left: .5em solid #777;
}

Dans un souci de lisibilité, j’ai séparé les déclarations dans diverses sections en les
précédant d’un commentaire. Notre première section traite du comportement du bloc de
code : notamment le fait que ce soit un élément de type block justement, et qu’il soit
possible de scroller de manière horizontale si les lignes sont trop longues.
Ensuite vient la gestion des espacements : un marge interne (padding) vient décoller le
contenu des bords du conteneur alors que la marge en bas (margin-bottom) permet juste de
s’assurer que tout contenu venant juste après le bloc de code soit séparé de celui-ci par une
gouttière. Concernant les tabulations, j’ai une préférence pour 2 espaces mais c’est un
débat dans lequel je ne rentrerai pas.
S’en suit la partie qui nous intéresse le plus, la typographie. Tout d’abord, il est de
coutume d’utiliser une police « monospace » pour afficher du code à l’écran (que ce soit
dans un navigateur ou un éditeur), c’est-à-dire une police dans laquelle tous les caractères
ont la même largeur.
Nous préservons aussi la mise en forme du code avec la déclaration white-space: pre. En
revanche, nous empêchons également les retours à la ligne (faute de place) en choisissant
pre plutôt que pre-wrap. Je vous accorde que c’est là une décision assez discutable car c’est
essentiellement subjectif. Certaines personnes n’aiment pas du tout avoir à scroller mais
personnellement, je trouve le code extrêmement pénible à lire quand il y a des retours à la
ligne.
Dans le cas où vous souhaiteriez remplacer white-space: pre par white-space: pre-wrap pour
autoriser les retours à la ligne afin d’éviter le scroll horizontal, je vous suggère d’ajouter la
déclaration hyphens: none pour éviter toute césure qui n’a pas lieu d’être dans du code.
Et enfin, nous appliquons une légère couleur de fond à notre bloc de code. Je n’ai pas
extrapolé davantage sur cette partie dans la mesure où c’est souvent un « colorateur
syntaxique » tiers qui va s’occuper de tout ce qui a attrait au thème.

Figure 6–15
Le rendu de notre bloc de code, et encore, sans colorateur syntaxique

RESSOURCE Prism.js
Prism.js est colorateur syntaxique léger et extensible écrit en JavaScript par Lea
Verou. Il s’adapte particulièrement bien aux blogs et aux slides.
http://bit.ly/prism-js
7
Variables natives

Vous les attendiez ? Les voilà enfin ! CSS propose finalement un système de variables
natives qui ne nécessite pas un programme tiers (post/préprocesseur).
Une des principales raisons qui a fait que les préprocesseurs CSS ont connu un tel succès,
est le manque crucial de variables dans le langage CSS. Bien que celui-ci soit relativement
simple sur le papier, dans les faits, il est parfois compliqué de maintenir une architecture
viable. C’est d’autant plus vrai que l’on supporte de plus en plus de choses avec CSS : les
écrans de diverses tailles, des médias de différents types, des thèmes, et bien plus encore.
Alors, autant il était envisageable de concevoir des sites avec des outils simples il y a 10
ans de cela, autant aujourd’hui la boîte à outils des CSS semble parfois manquer de
quelques ustensiles incontournables. C’est d’ailleurs précisément ce que les
préprocesseurs tentent de corriger, en attendant que les spécifications le fassent et que les
navigateurs adoptent ces dernières.
Une application, qu’elle soit de petite ou de grande ampleur, peut contenir énormément de
styles, et de par la nature simpliste de CSS, cela produit nécessairement beaucoup de
répétitions. Par exemple, un site aura sûrement une palette de couleurs, réutilisant les
mêmes trois ou quatre couleurs un peu partout. Modifier ces couleurs peut s’avérer
compliqué dans la mesure où elles sont répétées à de nombreux endroits, potentiellement
dans des fichiers différents, rendant le « chercher-remplacer » difficile (au-delà du fait que
ce soit potentiellement dangereux).
C’est pourquoi nous avons là une bonne nouvelle puisque les spécifications CSS
introduisent désormais un module destiné intégralement à l’implémentation de variables
natives à CSS
(connu sous le doux nom de CSS Custom Properties for Cascading Variables Module
Level 1), c’est-à-dire utilisables sans l’aide d’un outil ou d’un langage annexe.
Comment ça marche ?
Les variables CSS sont effectivement des variables au sens où on l’entend, c’est-à-dire des
espaces de stockage permettant de conserver une donnée afin de la réutiliser à divers
endroits. Mais d’un point de vue strictement technique, elles se définiraient plutôt comme
des « propriétés personnalisées » (d’où le terme officiel Custom Properties), dont la valeur
peut être retrouvée à partir du nom de la propriété.
Parce qu’il s’agit techniquement de propriétés CSS, elles sont dotées d’un certain nombre
de caractéristiques avantageuses qu’il faut bien comprendre, surtout lorsque l’on est
habitué aux variables d’un préprocesseur.
Tout d’abord, elles « cascadent », c’est-à-dire qu’une variable déclarée à la racine du
document cascadera sur tous les enfants, et sera donc accessible partout. C’est d’ailleurs
pour ça qu’il est coutume de déclarer les variables CSS dans la pseudo-classe :root, qui se
trouve systématiquement être la racine du document (soit <html> dans le cas du HTML),
afin qu’elles soient accessibles par tous les éléments du DOM.
Ensuite, et ceci découle directement de ce l’on vient de voir, elles peuvent être redéfinies.
Ainsi, si un élément définit une variable à une valeur A, mais que son enfant la redéfinit à
une valeur B, les enfants de l’enfant auront cette variable définie à B. Souvenez-vous, les
variables sont des propriétés qui cascadent. De même, il est tout à fait possible de redéfinir
une variable dans une Media Query (voir chapitre 8).
Enfin, parce qu’il s’agit bel et bien de propriétés CSS comme les autres, elles ne sont pas
restreintes à une feuille de styles comme peuvent l’être les variables d’un préprocesseur.
Mieux encore, elles sont toujours accessibles une fois la page rendue, c’est-à-dire qu’il est
tout à fait possible de les lire et de les altérer avec JavaScript.
Il est d’ailleurs bon de préciser que le contenu d’une variable CSS ne peut être utilisé que
comme valeur d’une propriété (y compris d’une variable). Elle n’est pas utilisable dans un
sélecteur, un nom de propriété ou quoi que ce soit d’autre.
La syntaxe
Définir une variable en CSS est très simple : il suffit de déclarer une propriété et de lui
assigner une valeur, comme vous le feriez normalement avec une propriété CSS tout à fait
standard.
La seule contrainte est que la propriété doit débuter par deux traits d’union (--) suivis de la
chaîne de caractères de votre choix faisant office de nom de variable. Il est recommandé
de choisir un nom qui peut être renseigné sans avoir à échapper des caractères. Par
exemple, --0 doit être échappé pour être référencé, ce qui risque de ne pas être très
pratique.
Concernant la valeur, elle peut accueillir absolument n’importe quoi du moment qu’elle ne
provoque pas d’erreur de syntaxe (), ], }, etc.). La seule valeur non autorisée est une
chaîne de caractères vide non encapsulée dans des guillemets, autrement dit le point-
virgule de fin d’expression directement juxtaposé aux deux points d’assignation (par
exemple, --prop:;).
Mais vous n’êtes pas tenu de définir des valeurs CSS valides, vous pouvez tout à fait
stocker ce que vous désirez, comme du JavaScript que vous pourrez par la suite récupérer
et exécuter.
Prenons un exemple simple pour démarrer et déclarons une variable appelée --my-custom-
property, ayant pour valeur la longueur 20px.

:root {
--my-custom-property: 20px;
}

Pour utiliser cette variable maintenant, il faut recourir à la fonction var(). Cette dernière ne
prend généralement qu’un seul argument : le nom de la propriété personnalisée (variable),
y compris les deux traits d’union.
.element {
margin-bottom: var(--my-custom-property);

ATTENTION Noms sensibles à la casse


Les propriétés personnalisées sont sensibles à la casse. Aussi var(--MY-CUSTOM-PROPERTY)
n’aura pas l’effet désiré puisque --MY-CUSTOM-PROPERTY n’existe pas. Dans ce cas, la
variable est considérée comme invalide.
Les variables invalides
Justement, parlons des variables invalides ! Il peut arriver qu’une variable soit invalide
pour la propriété dans laquelle elle est utilisée : soit parce qu’elle n’existe tout simplement
pas, soit parce que le type de la valeur n’est pas approprié à l’utilisation comme dans le
cas d’une longueur pour color, ou d’une couleur pour width pour ne donner que des
exemples très simples.
Quand cela arrive, la propriété utilisant la variable invalide est alors définie à inherit dans
le cas d’une propriété pouvant être héritée, ou à initial dans le cas contraire. Ce
comportement n’est pas anodin comme nous pouvons le voir dans l’exemple suivant :
:root {
--length: 20px;

}
.element {
background-color: hotpink;

background-color: var(--length);
}

Ici, la variable est considérée comme invalide car la propriété CSS background-color attend
une couleur, alors que la variable référencée est une longueur (20px). La propriété
background-color n’étant pas héritée, elle sera alors définie à sa valeur initiale, c’est-à-dire
transparent. De fait, la première définition background-color: hotpink sera bel et bien écrasée.

De la même manière, si nous reprenons exactement l’exemple précédent en remplaçant la


propriété background-color par color, la valeur de celle-ci sera alors inherit, puisque color est
une propriété dont la valeur cascade.
:root {
--length: 20px;
}

.element {

color: hotpink;
color: var(--length);

Quoi qu’il en soit, ces deux exemples montrent que l’on ne peut pas dresser de solutions
de repli aux variables invalides comme ceci.
Les valeurs de recours en cas d’invalidité
C’est pourquoi la fonction var() accepte en réalité deux arguments. Le premier, comme
nous l’avons vu, est le nom de la propriété personnalisée à référencer. Le second est une
valeur de repli, dans le cas où la valeur récupérée de la variable serait invalide ou
inexistante. Ainsi, vous pouvez définir un fallback en renseignant le second argument :
/**

* Applique la valeur de --my-custom-property si :

* - la variable existe
* - la valeur est valide pour la propriété margin-bottom

* sinon, applique 1em

*/

.element {
margin-bottom: var(--my-custom-property, 1em);
}

La valeur de repli correspond à tout ce qui se trouve après la première virgule, c’est-à-dire
que vous pouvez tout à fait utiliser des virgules dans le second argument, comme dans
l’exemple suivant :
/**
* Applique la valeur de --my-custom-font-family si :

* - la variable existe
* - la valeur est valide pour la propriété font-family
* sinon, applique "Arial", "Helvetica", sans-serif

*/
.element {

font-family: var(--my-custom-font-family, "Arial", "Helvetica", sans-serif);


}
Cas particuliers et clarifications

Variables qui se référencent mutuellement


CSS est un langage qui échoue de manière silencieuse, on peut donc être tenté de faire des
essais un peu farfelus pour voir ce que cela donne, comme définir deux variables qui se
référencent mutuellement. Par exemple :
:root {

--alpha: calc(var(--beta) + 10px);


--beta: calc(var(--alpha) - 10px);

Ou encore une seule variable qui se référence elle-même :


:root {
--alpha: var(--alpha);
}

Dans des cas comme ceux-ci, les variables sont automatiquement définies à leur valeur
initiale, qui se trouve être une valeur vide, donc invalide.

Propriété all et propriétés personnalisées


La propriété all permet de réinitialiser toutes les propriétés CSS existantes de l’élément
(ou des éléments) auquel elle est appliquée :
• à leur valeur initiale avec all: initial ;
– à leur valeur héritée avec all: inherit ;
• à leur valeur héritée si elles sont héritées ou initiale dans le cas contraire avec all: unset.
En revanche, cette propriété n’a aucun effet sur les variables (propriétés personnalisées).
On peut donc utiliser la propriété all sans crainte d’effacer les éventuelles variables
existantes.

Animations
Les propriétés personnalisées peuvent théoriquement être animées, mais dans la mesure où
le navigateur ne peut pas interpréter leur contenu, elles basculent d’un état à l’autre passée
la moitié (50 %) de l’animation/transition.
En revanche, une propriété personnalisée utilisée au sein d’une déclaration d’animation
(@keyframes, voir le chapitre 10 dédié aux animations et transitions) est normalement animée
quand utilisée via la fonction var().
La compatibilité des navigateurs pour les variables
natives
Tableau 7–1 Navigateurs desktop

Tableau 7–2 Navigateurs mobiles

Firefox supporte en réalité les variables natives depuis la version 29 via un flag. En
revanche, il s’agit là de l’ancienne version des spécifications. Seules les versions 31 et
plus récentes supportent la dernière version.
Le support est encore très léger, mais compte tenu du besoin crucial auquel répondent les
variables natives, il y a fort à parier que le support se décantera bientôt pour que l’on
puisse les utiliser sur des projets en production.
8
Styles conditionnels

On a parfois tendance à oublier qu’en CSS, les styles sont bien souvent conditionnels.
Écran ? Impression ? Dimensions ? Fonctionnalités supportées ? C’est d’autant plus vrai
que l’on doit prendre en compte un éventail toujours plus large de navigateurs et
d’appareils. Heureusement, @supports et @media permettent d’écrire des styles conditionnels.
Feature Queries
On a beau critiquer les problèmes que l’on peut rencontrer avec CSS, il s’agit globalement
d’un langage bien pensé. Par exemple, le fait que CSS ne plante pas (ou plutôt ne fasse pas
planter le parser) lorsqu’une propriété ou une valeur non reconnue est rencontrée est
vraiment une très bonne chose pour un langage interprété par le navigateur comme CSS.
C’est parce que CSS est prévu pour simplement omettre les erreurs plutôt que de les
relever que l’on peut mettre en place de l’amélioration progressive. Dupliquer une
propriété ne déclenche pas d’erreur (ce qui en soit pourrait être le cas), et assigner une
valeur erronée ne déclenche pas d’erreur non plus. À la place, les navigateurs omettent
simplement la ligne, n’empêchant pas le reste du fichier d’être utilisé.
Seulement parfois, dupliquer les règles dans le but de demander à chaque navigateur de
faire ce qu’il peut faire de mieux n’est pas suffisant. Autant cela fonctionne très bien pour
déclarer une couleur en rgba, avec un fallback en hexadécimal juste au-dessus, autant
quand il s’agit d’utiliser une technique de mise en page plutôt qu’une autre en fonction du
navigateur, ça devient plus compliqué.
Pour cela, il existe l’excellente bibliothèque JavaScript Modernizr, qui effectue des tests
de support de fonctionnalités au chargement de la page afin d’assigner des classes à
l’élément racine déterminant si le navigateur supporte telle ou telle propriété. Modernizr
vient également avec une API JavaScript simple permettant de tester les fonctionnalités
supportées par le navigateur directement depuis JavaScript, et pourquoi pas de
conditionner l’inclusion de scripts, par exemple.
Seulement Modernizr, c’est du JavaScript, pas du natif ! Et ça a beau être open source et
extrêmement bien développé, ce serait quand même mieux que ce soit le navigateur qui
indique lui même ce qu’il est capable de supporter ou non. Heureusement, les
spécifications CSS évoluent et le module de styles conditionnels intègre une nouvelle
section appelée Feature Queries.
TRADUCTION À propos de Media Queries et Feature Queries
Suite à un petit sondage effectué auprès de la communauté web francophone – merci à
tous ceux d’entre vous qui y ont participé –, il s’est avéré que majoritairement les
développeurs ne souhaitent pas traduire le terme Media Queries. Dans un souci de
cohérence, on ne traduira donc pas non plus Feature Queries.
Ce module se base sur la directive @supports, qui permet de tester la compréhension d’une
déclaration (paire propriété/valeur) auprès du navigateur. Comme la majorité des
directives CSS, elle est évaluée à true ou false, définissant ainsi si son contenu sera évalué
ou non. Le module implémente aussi une API JavaScript que l’on analysera plus loin.

De la notion de « support »
Avant même de rentrer dans la syntaxe et les cas d’utilisation de @supports, il faut définir ce
qui est entendu par « supporter une propriété ». Selon les spécifications, un parser CSS
(par exemple, celui d’un navigateur) peut affirmer supporter une déclaration (constituée
d’une propriété et d’une valeur) si celle-ci ne déclenche pas une erreur de parsing, c’est-à-
dire si elle est interprétée et a un effet sur l’élément auquel elle est appliquée.
Toute déclaration qui serait normalement omise à cause d’une erreur de parsing parce
qu’elle n’est pas implémentée ou que son implémentation n’est pas utilisable doit être
considérée comme « non supportée ».
Ceci étant dit, une fonctionnalité qui serait manuellement désactivée par l’utilisateur sera
toujours considérée comme supportée. Par exemple, si l’utilisateur active le mode de
contraste élevé qui a pour effet de redéfinir les couleurs, le processeur considérera toujours
color comme supportée même si les déclarations de la propriété color n’ont pas d’effet.

En revanche, une fonctionnalité expérimentale dont le support serait effectif via une
option du navigateur (flag) comme c’est de plus en plus le cas dans les navigateurs récents
aujourd’hui (voir page 9), est considérée supportée ou non supportée en fonction de l’état
actuel de l’option. Il est donc tout à fait possible – et c’est d’ailleurs souvent le premier
cas d’usage – de tester le support des nouvelles fonctionnalités CSS pour les implémenter
uniquement quand elles sont disponibles.

Syntaxe
La syntaxe en elle-même ressemble beaucoup à celle des Media Queries si ce n’est que
celle-ci est un peu plus permissive puisqu’elle intègre également la négation via le mot-clé
not, la conjonction via le mot-clé and et la disjonction via le mot-clé or. Ainsi, il est possible
d’implémenter des styles selon si un set de propriétés est supporté, ou bien une propriété
plutôt qu’une autre, ou encore si une propriété n’est pas supportée du tout.
Une directive de support se construit de la façon suivante :
/* Simple */
@supports (propriété: valeur) {

/**
* Déclarations dans le cas où

* propriété: valeur est supporté

*/
}

/* Négation */

@supports not (propriété: valeur) {


/**

* Déclarations dans le cas où

* propriété: valeur n’est PAS supporté


*/

/* Conjonction */
@supports (propriété: valeur) and (autre-propriété: autre-valeur) {

/**

* Déclarations dans le cas où


* propriété: valeur est supporté ET
* autre-propriété: autre-valeur est supporté

*/
}

/* Disjonction */
@supports (propriété: valeur) or (autre-propriété: autre-valeur) {
/**

* Déclarations dans le cas où

* propriété: valeur est supporté OU


* autre-propriété: autre-valeur est supporté

*/

Comme vous pouvez le constater, la syntaxe est somme toute assez stricte puisqu’il faut
impérativement que les déclarations testées soient encapsulées par des parenthèses
(@supports property: value est donc invalide). De plus, les mots-clés doivent être séparés par
des espaces.
Par ailleurs, et pour des raisons d’interprétation des expressions impliquant une
combinaison de mots-clés and, or et not, il faut impérativement spécifier les différentes
composantes de l’expression entre parenthèses pour que celle-ci soit considérée valide.
/* Cette règle est invalide */

@supports (propriété-1: valeur-1)


or (propriété-2: valeur-2)
and (propriété-3: valeur-3) {

/* Déclarations */
}
/* Il faut l’écrire ainsi */

@supports ((propriété-1: valeur-1) or (propriété-2: valeur-2)) and (propriété-3: valeur-3) {


/* Déclarations */
}

/* Ou comme ceci */

@supports (propriété-1: valeur-1) or ((propriété-2: valeur-2) and (propriété-3: valeur-3)) {


/* Déclarations */

CURIOSITÉ @supports et !important


Il est tout à fait possible de joindre le flag !important à la valeur de la déclaration testée,
toutefois ça n’aura aucun impact. Quoi qu’il en soit, c’est valide.

API JavaScript
Comme expliqué au début de ce chapitre, le module de support conditionnel intègre aussi
une API JavaScript permettant de tester le support de propriétés directement depuis
JavaScript mais sans passer par une bibliothèque tierce telle que Modernizr ; dans ce cas,
c’est bel et bien le navigateur directement que l’on interroge via JavaScript.
La syntaxe est on ne peut plus simple puisqu’il ne s’agit que d’une unique fonction
supports() .
interface CSS {
static boolean supports(DOMString property, DOMString value);

static boolean supports(DOMString conditionText);


}

Comme vous pouvez le constater, celle-ci a deux signatures différentes. La première


consiste à lui passer deux arguments : une propriété et une valeur à la manière de la
syntaxe de @supports. La seconde accepte une chaîne de caractères valide pour la syntaxe
CSS @supports. Les deux lignes suivantes sont donc valides :
CSS.supports('property', 'value')

CSS.supports('(property: value)')

Par conséquent, lorsque vous désirez tester une expression faite de différentes
composantes, il vous faudra soit utiliser la seconde syntaxe qui le permet très facilement
en évaluant la chaîne comme le ferait le parser CSS pour une règle @supports standard, soit
faire les conditions directement en JavaScript :
/**
* En réalisant les opérations en JavaScript

*/
if (CSS.supports('propriété-1', 'valeur-1')
&& !CSS.supports('propriété-2', 'valeur-2')) {

/* … */
}
/**

* En utilisant une chaîne valide pour @supports


*/
if (CSS.supports('(propriété-1: valeur-1) and not (propriété-2: valeur-2)') {

/* … */
}

JAVASCRIPT Tester le support de CSS.supports


Pour tester le support de CSS.supports en JavaScript, vous pouvez utiliser l’expression
suivante :
var supportsCSS = !!((window.CSS && window.CSS.supports) ||

window.supportsCSS || false);

Le test effectué sur la propriété supportsCSS est destiné à Opera 12.10.

@supports et préfixes constructeurs


Tout d’abord, la directive @supports n’est bien évidemment pas préfixée selon
l’implémentation, ce qui défierait complètement le but initial du module. Si l’on doit
écrire @-webkit-supports, @-moz-supports et le reste, tout ceci n’a évidemment plus de sens.
Donc la propriété @supports n’existe que sous sa forme standard contrairement à d’autres
directives comme @keyframes.
Ensuite, et c’est là un des problèmes de @supports bien que ce ne soit pas la directive en
elle-même qui soit à l’origine du souci : pour qu’un test soit correct, il faut tester la forme
standard mais aussi toutes les formes préfixées… C’est très pénible et cela génère des
règles de test de support à rallonge, mais c’est nécessaire si l’on souhaite cibler tous les
navigateurs connaissant la déclaration.
Prenons un exemple tout à fait plausible : vous désirez tester le support du module
Flexbox. Effectuer un test @supports (display: flex) sera suffisant pour cibler tous les
navigateurs supportant la dernière version des spécifications du modèle de boîtes flexibles.
Toutefois, les autres navigateurs supportent aussi Flexbox, mais pas sous cette syntaxe !
Du coup, il faut également tester -webkit-flex, -ms-flexbox, -moz-box et -webkit-box si l’on veut
cibler tous les navigateurs capables d’utiliser Flexbox.
Ainsi, la règle serait la suivante :
@supports (display: flex) /* Opera 12.1, Firefox 20+ */
or (display: -webkit-box) /* iOS 6-, Safari 3.1-6 */
or (display: -moz-box) /* Firefox 19- */

or (display: -ms-flexbox) /* Internet Explorer 10+ */


or (display: -webkit-flex) /* Chrome, Opera 14+ */ {
/* Flexbox est supporté */

Cas pratique : header fixe à compter d’une certaine position


avec position: sticky
Les cas d’usage sont nombreux. Je pense que tous ceux qui ont déjà utilisé Modernizr ne
serait-ce qu’une fois peuvent envisager de remplacer cette bibliothèque par le module de
support natif à CSS quand celui-ci sera largement implémenté au sein des navigateurs les
plus populaires.
Prenons le cas d’un en-tête qui serait fixé à partir d’une certaine position grâce à la
nouvelle valeur position: sticky, et mettons en place de l’amélioration progressive grâce à
@supports.

.header {

position: fixed;
top: 0;

left: 0;

right: 0;
}

@supports (position: sticky) {

.header {
position: sticky;

}
Cas pratique : carrousel de Bootstrap 3
Si vous êtes un utilisateur du framework CSS/JavaScript Bootstrap, vous avez sûrement
déjà mis les mains sur son carrousel. Celui-ci possède deux versions : une qui utilise le
positionnement absolu et la propriété left, et une qui utilise les transformations CSS que
l’on verra dans l’avant-dernier chapitre de cet ouvrage.
Pour appliquer les transformations CSS (et annuler le positionnement avec l’offset left)
uniquement aux navigateurs qui les supportent, Bootstrap utilise actuellement une Media
Query propriétaire : @media (transform-3d). Cela va sans dire qu’il s’agit là d’une très
mauvaise idée.
CLARIFICATION À propos de @media (transform-3d)
C’est en lisant la source du framework Bootstrap un beau jour que j’ai découvert
l’existence de cette Media Query. Intrigué, j’ai mené ma petite enquête jusqu’à avoir
une confirmation de Paul Irish, ingénieur chez Google : il s’agit d’une Media Query
propriétaire inventée par Google pour tester le support des transformations 3D.
Au-delà du fait qu’il est toujours déconseillé d’utiliser des fonctionnalités non
standardisées, Paul a ajouté que celle-ci est potentiellement buggée, retournant parfois
des résultats « faux-positifs ». Autrement dit, vous ne devriez pas l’utiliser. Et
Bootstrap non plus.
Vous connaissez la solution. La directive @supports bien entendu ! Elle est faite pour ça et
convient parfaitement à un cas d’usage comme celui-ci.
@supports (transform: translate3d(0, 0, 0)) {
.carousel-inner > .item {

transition: transform .6s ease-in-out;


backface-visibility: hidden;
perspective: 1000;

.carousel-inner > .item {


left: 0 !important;

.carousel-inner > .item.next,


.carousel-inner > .item.active.right

transform: translate3d(100%, 0, 0);

}
.carousel-inner > .item.prev,

.carousel-inner > .item.active.left {

transform: translate3d(-100%, 0, 0);


}

.carousel-inner > .item.next.left,

.carousel-inner > .item.prev.right,


.carousel-inner > .item.active {

transform: translate3d(0, 0, 0);

}
}

Si vous ne comprenez pas toutes les déclarations, pas de panique ! Nous verrons tout ça
plus en détail dans le chapitre 9 dédié aux transformations.

Compatibilité des navigateurs pour les Feature Queries


Tableau 8–1 Navigateurs desktop

Tableau 8–2 Navigateurs mobiles

CHROME ET OPERA Problème avec :not


L’implémentation de @supports du moteur de rendu Blink (Chrome et Opera) semble
poser des soucis lorsqu’un sélecteur utilisant :not vient après. Pour éviter tout
problème, je recommande de mettre les directives de support à la fin.
Media Queries
Les feuilles de styles ont toujours été sensibles au média auquel elles étaient appliquées.
Est-ce un écran ? Est-ce une impression papier ? C’est d’autant plus vrai aujourd’hui alors
que nous avons chaque jour davantage de supports pour accéder à Internet : des
téléphones, des tablettes, des TV, des montres et même des lunettes !
Par exemple, certaines propriétés sont spécifiques à un média bien particulier, comme page-
break-before qui n’existe que dans le contexte du média page, ou la propriété speak qui n’a de
sens que dans le cas d’un lecteur d’écran. C’est pourquoi les spécifications CSS 2 ont
introduit la directive @media, acceptant un certain nombre de mots-clés pour déterminer le
contexte dans lequel les styles encapsulés doivent agir.
On pouvait donc définir des styles spécifiques pour :
• les écrans (@media screen) ;
• les impressions papier (@media print) ;
• les projections avec vidéoprojecteur (@media projection) ;
• les télévisions (@media tv) ;
• les lecteurs d’écran (@media speech) ;
• les lecteurs en braille pour non-voyants (@media braille) ;
• les impressions pour braille (@media embossed) ;
• les terminaux (@media tty) ;
• tous les contextes (@media all).
Bien que ces mots-clés furent suffisants à une époque, à l’heure actuelle les développeurs
ont besoin de davantage d’informations sur le contexte de lecture afin de personnaliser
l’affichage. On pense notamment au Responsive Web Design, qui s’adapte aux
dimensions de l’écran (entre autres bien sûr), phénomène qui n’est aujourd’hui plus un
luxe mais une nécessité tant la navigation sur les téléphones (smartphones) et autres
appareils mobiles (tablettes, montres, lunettes…) prend de l’importance.
De fait, les spécifications CSS 3 ont repris le sujet des Media Queries très à cœur afin d’en
faire un outil primordial de l’intégration d’aujourd’hui. En effet, on peut désormais
détecter les dimensions de l’écran, le ratio hauteur/largeur, le système de couleurs, la
résolution, le système de pointage et même la luminosité. Autant de facteurs que l’on peut
dorénavant prendre en compte pour offrir à l’utilisateur un site Internet qui s’adapte à
toutes les situations.

Syntaxe
La syntaxe proposée par les spécifications de niveau 3 reprend en partie la syntaxe de
l’ancienne version dans le sens où il s’agit toujours de la directive @media suivie d’un des
mots-clés mentionnés précédemment, auxquels peuvent se greffer des expressions qui
testent des conditions particulières du media analysé.
Dans tous les cas, une Media Query est une expression qui est évaluée à true ou false. Si
elle est considérée comme valide, tous les styles définis dans celle-ci sont appliqués.
@media all and (min-width: 500px) {

/* Déclarations */
}

Il est possible d’omettre le mot-clé all puisqu’il constitue la valeur par défaut du média
analysé. Ainsi, l’exemple précédent peut être écrit de la façon suivante :
@media (min-width: 500px) {
/* Déclarations */

C’est d’ailleurs dans cette syntaxe qu’on le rencontre le plus, notamment dans le cadre de
Responsive Web Design.

Conjonction
Il est bien évidemment possible de cumuler diverses expressions à l’aide du mot-clé and.
@media all and (orientation: portrait) and (min-width: 500px) {
/* Déclarations */

Condition
Le mot-clé or n’existe pas. C’est une séparation par virgule qui fait office d’aiguilleur. Par
exemple, si vous désirez appliquer des styles uniquement quand le support a un mode
portrait ou que la largeur de l’écran fait au moins 500 pixels, vous pouvez l’écrire ainsi :
@media (orientation: portrait), (min-width: 500px) {
/* Déclarations */

Négation
En revanche, il existe le mot-clé not qui permet d’introduire une négation. Très
simplement, ce mot-clé permet d’appliquer les styles quand la condition n’est pas remplie.
@media not print {

/* Déclarations */
}

Restriction
Vous pouvez rencontrer le mot-clé only qui a pour objectif d’empêcher un navigateur ne
supportant que les spécifications de niveau 2 du module d’interpréter le bloc. C’est une
sorte de filtre pour distinguer les navigateurs capables de supporter le module de niveau 3
de ceux qui n’y parviennent pas.
@media only screen {

/* Déclarations */
}

Que peut-on détecter ?


Tout ceci semble bien alléchant et on imagine déjà le contrôle immense que l’on peut
avoir sur un rendu en fonction du contexte. Mais quand vient la question de ce que l’on
peut effectivement tester via la direction @media, la réponse est parfois un peu vague. En
réalité, il y a bien souvent qu’une seule chose que l’on teste : la largeur de l’écran. Mais il
existe bien des paramètres que l’on peut analyser au-delà des dimensions de la fenêtre.
C’est ce que je vous propose de voir dans cette section.

Les dimensions : width et height


Les expressions traitant des dimensions de l’écran s’appliquent aux médias visuels et
tactiles, c’est-à-dire qu’elles n’auront par exemple aucun effet pour un lecteur d’écran ou
une imprimante. Ces deux expressions peuvent être préfixées par min- et max-. Et
heureusement, parce que compte tenu du nombre impressionnant de supports différents
existants, les probabilités de toucher un périphérique en renseignant une Media Query de
type @media (width: <nombre>px) sont extrêmement minces.
De fait, on a tendance à tester les dimensions à partir ou jusqu’à un certain point. Par
exemple, il est coutume de cibler tous les médias dont la largeur de la zone d’affichage est
inférieure à 800 pixels. Ceci permet de modifier l’apparence de la page afin de réagencer
les éléments pour que les petits écrans ne soient pas lésés.
@media (max-width: 800px) {
/* Déclarations */

CONSEIL À propos de device-width et device-height


Vous pourriez être amené à rencontrer des expressions testant device-width ou device-
height (y compris avec les préfixes min et max). Celles-ci traitent de la résolution du
support. Par exemple, un ordinateur de bureau avec une résolution de 1 440&times900
aura une device-width de 1440px, peu importe la taille de la fenêtre du navigateur.
En revanche, l’iPhone 4, par exemple, dispose d’une largeur en pixels de 640 mais
d’une device-width fixe de 320px. C’est parce qu’il a ce que l’on appelle un « pixel-ratio »
de 2. Quoi qu’il en soit, la largeur désirée est bien souvent 640px (qui est concrètement
la largeur disponible) et non pas 320px. De fait, je vous recommanderais d’utiliser
plutôt width que device-width lorsque vous désirez conditionner l’affichage d’un site en
fonction de la largeur disponible, d’autant que les Media Queries débutant par device-
sont dépréciées.

L’orientation de l’appareil
Comme vous avez pu le constater dans les premiers exemples de ce chapitre, il est
possible de tester l’orientation d’un écran : portrait ou paysage. La définition est très
simple à déterminer : si la largeur du média est plus grande que sa hauteur, il s’agit d’une
orientation paysage. Sinon, il s’agit d’une orientation portrait.
Parce qu’il y a vraiment très peu d’écrans d’ordinateur pouvant être utilisés en mode
portrait, on peut se servir de cette expression pour déterminer si l’on est sur un media
mobile orienté en mode portrait. Ainsi, il est par exemple possible d’ajuster la navigation
pour qu’elle apparaisse sous la forme d’un menu déroulant, dans l’en-tête.
@media (orientation: portrait) {
/* Déclarations */

Le ratio hauteur/largeur de l’écran


De la même façon qu’il est possible de déterminer s’il s’agit d’un écran en mode portrait
ou en mode paysage, il existe une expression pour tester « l’aspect ratio », c’est-à-dire le
ratio entre la hauteur et la largeur du média. C’est un concept bien connu, notamment en
ce qui concerne les écrans de télévision que l’on rangeait jadis dans deux grandes
catégories : 4:3 ou 16:9.
Par ailleurs, il est tout à fait possible de tester cette valeur avec les mots-clés min et max pour
établir des règles de gestion à partir ou jusqu’à une certaine valeur.
@media (aspect-ratio: 16/9) {
/* Déclarations */

CONSEIL À propos de device-aspect-ratio


Comme pour width et height, vous pouvez rencontrer l’expression device-aspect-ratio, elle
aussi pouvant être préfixée par min- et max-. Quoi qu’il en soit, celle-ci est dépréciée et
ne devrait pas être utilisée.

La résolution de l’écran
La résolution d’un écran, en tout cas dans les spécifications CSS, se mesure en dpi (dots
per inch), en dpcm (dots per centimeter) ou en dppx (dots per pixel, aussi connu sous le nom
de Device Pixel Ratio, alias DPR). Concrètement, elle détermine la taille d’un dot (point)
dans une représentation graphique en indiquant combien de ces points sont susceptibles de
rentrer dans 1in, 1cm ou 1px. Parce que CSS fixe un ratio de 1:96 entre 1in et 1px (ce qui
signifie que 1in == 96px), 1dppx est strictement égal à 96dpi (c’est d’ailleurs la résolution par
défaut des images affichées en CSS).
En somme, la résolution détermine le ratio entre les pixels réels et les pixels que l’on
qualifie de device-independant (DIPS). En testant cette propriété, il est donc possible de
savoir s’il on a affaire à un écran à haute densité, c’est-à-dire un écran dont les dimensions
en pixels physiques sont différentes (plus grandes) que les device-independant pixels.
C’est le cas par exemple d’un iPhone qui a une largeur physique de 640px mais une largeur
effective de 320px, d’où la résolution de 2dppx : 2 dots par pixel.
Le moteur de rendu WebKit a longuement supporté – et supporte toujours d’ailleurs – une
propriété équivalente appelée device-pixel-ratio, également sensible aux modulateurs minet
max- (préfixés en -webkit-min-device-pixel-ratio et -webkit-max-device-pixel-ratio). Cette propriété
attend un nombre sans unité contrairement à la propriété resolution standardisée. Chrome et
Safari ne supportent pas la version standardisée mais uniquement celle-ci, aussi vous
devrez utiliser les deux formules.
En l’occurrence, pour cibler les écrans à la densité supérieure ou égale à 1.5dppx, il vous
faudra utiliser une combinaison de ces trois Media Queries :
/**
* (-webkit-min-device-pixel-ratio: 1.5) pour Chrome et Safari

* (min-resolution: 144dpi) pour Opera, Firefox 15- et Internet Explorer 10

* (1.5 × 96)
* (min-resolution: 1.5dppx) pour Firefox 16+

*/
@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) {
/* Écrans à haute densité */

Il existe des tas de résolutions différentes à l’heure actuelle ; ça dépend entièrement de


l’appareil. Certains, comme la majorité des ordinateurs sont à 1. Des téléphones sont à 1.3,
d’autres à 1.5, des appareils de tout type sont à 2, certains sont à 3. C’est pour cette raison
qu’il est préférable de tester une résolution supérieure à 1.3 ou 1.5, et de traiter tous ces
écrans de la même façon.
CURIOSITÉ Pixels non carrés
Dans les rares cas où les pixels ne seraient pas carrés, les spécifications précisent que
les Media Queries de résolution utiliseraient la résolution verticale de l’écran.

Futur : davantage de contrôle


Les possibilités sont nombreuses mais je pense que nous n’en sommes qu’au début. Nos
appareils nous permettent de faire de plus en plus de choses, aussi il n’y a rien d’anormal
si le nombre de Media Queries disponibles ne cesse de croître.
Parce que le futur est radieux, je vous propose de faire un tour d’horizon des Media
Queries à venir, dites de « niveau 4 ».

Présence de JavaScript
Une des premières Media Queries à avoir été ajoutée à ce niveau 4 du module a été celle
permettant de détecter si JavaScript est supporté et activé dans le navigateur : scripting.
Elle accepte trois valeurs :
• none : si JavaScript n’est pas activé ou pas supporté ;
• initial-only : si JavaScript n’est activé qu’au chargement de la page (impression,
navigateurs proxy…) ;
• enabled : si JavaScript est activé.
Dans le cas où vous auriez une fonctionnalité dépendante de JavaScript, il est désormais
possible de mettre en place une solution de repli en CSS uniquement si JavaScript n’est
pas supporté ou est simplement désactivé.
CLARIFICATION Aucune distinction
Les spécifications stipulent clairement qu’il ne devrait y avoir aucune différence entre
le JavaScript désactivé et le JavaScript non supporté.
Cependant, l’absence de JavaScript n’est plus tellement un problème dans la mesure où
l’immense majorité des navigateurs actuels l’activent par défaut. Les problèmes rencontrés
sont plutôt :
• des scripts qui ne sont pas chargés faute de connectivité ;
• des scripts qui sont exécutés trop tard ou trop lentement ;
• des scripts qui plantent à cause d’un manque de support de certaines fonctionnalités
(Internet Explorer 8, par exemple).
Malheureusement, cette Media Query ne corrige aucun de ces soucis. Aussi, je dois
avouer que je suis quelque peu sceptique à son sujet. De plus, il faudrait attendre que tous
les navigateurs l’implémentent avant de s’en servir puisqu’il faudrait mettre en place une
solution de repli dans tous les cas.
C’est sans compter qu’en théorie, le JavaScript est supposé venir se greffer à une solution
de base fonctionnelle dans le but d’améliorer l’expérience de l’utilisateur. Dans le cas où
ce ne serait pas fait ainsi, j’ai peine à penser que quelques propriétés CSS déclarées dans
un bloc @media (scripting: none) puissent venir corriger le tir.

Luminosité ambiante
Une Media Query qui a beaucoup fait parler d’elle depuis que les nouvelles spécifications
ont vu le jour est celle permettant de détecter le niveau du luminosité ambiante : light-
level.

La requête light-level accepte donc trois valeurs prédéfinies :


• dim : dans un environnement sombre où les forts contrastes pourraient s’avérer
distrayants ou inconfortables ;
• normal : dans un environnement à la luminosité normale ;
• : dans un environnement exceptionnellement lumineux qui causerait des
washed
difficultés de lecture.
CLARIFICATION À propos des valeurs en lux
Les spécifications ont délibérément pris le parti de ne pas traiter les possibles valeurs
de cette requête en lux (l’unité de mesure de l’éclairement lumineux) pour les raisons
suivantes :
• les appareils équipés d’un capteur de luminosité font généralement un calibrage
automatique ce qui fausse la valeur ;
• les différentes technologies utilisées dans les écrans (cristaux liquides, encre digitale…)
ne réagissent pas toutes de la même façon à la luminosité ambiante ;
• de nombreux capteurs lumineux sont mal calibrés.
Malheureusement, les raisons qui font que light-level n’accepte pas de valeurs en lux sont
les mêmes que celles qui empêchent de véritablement adapter le design à la luminosité
ambiante.
Il est très difficile d’évaluer le degré de contraste nécessaire en fonction de la luminosité
quand le capteur est potentiellement mal calibré et que le système d’exploitation est
susceptible de faire des adaptations en arrière-plan.
Ceci étant dit, cette Media Query pourrait tout à fait être utilisée pour déclencher ce que
l’on appelle communément un night mode, autrement dit un mode adapté à une luminosité
sombre où les couleurs pâles, notamment le blanc, gênent la lecture et/ou peuvent heurter
la sensibilité des yeux.
Schématisons notre idée avec un exemple extrêmement vulgarisé :
body {
background: white;
color: black;

}
@media (light-level: dim) {
body {

background: black;
color: white;

}
}

L’idéal serait même de proposer ce mode à l’utilisateur plutôt que de le lui imposer. Pour
cela, on a besoin de JavaScript mais dans la mesure où nous ne faisons qu’améliorer
l’expérience de l’utilisateur, c’est tout à fait envisageable.
if (window.matchMedia('(light-level: dim)')) {

// On propose à l’utilisateur d’activer le mode nuit


// via une petite pop-up, par exemple

CLARIFICATION matchMedia
La fonction matchMedia n’est pas issue d’une bibliothèque JavaScript, il s’agit bien d’une
fonction native à JavaScript présente chez tous les navigateurs supportant les Media
Queries. Celle-ci permet de tester une Media Query directement depuis JavaScript.
Cette fonction accepte un unique argument : une requête sous la forme d’une chaîne
de caractères, exactement comme elle était écrite dans une feuille de styles classique.
Ensuite, cette requête est évaluée à true ou false, selon les conditions.
INTERNET EXPLORER 10 SUR WINDOWS 8 Équivalence propriétaire
Il est intéressant de remarquer que les versions d’Internet Explorer supérieures ou
égales à 10 sur Windows 8 supportent une Media Query propriétaire appelée high-
contrast (préfixée par -ms- bien entendu).

Celle-ci accepte les valeurs active, black-on-white, white-on-black ou none. Les trois
premières valeurs sont susceptibles d’être déclenchées lorsque l’application est lancée
en mode « contraste élevé ».

Système de pointage
Parmi les nouvelles Media Queries disponibles, il en est une qui permet de déterminer le
niveau de précision du système de pointage. Rappelons que le système de pointage est
l’appareil utilisé pour déplacer le curseur à l’écran, très communément une souris ou un
trackpad.
Cette requête appelée pointer en tout simplicité, offre là encore trois valeurs possibles :
• none : l’appareil ne dispose d’aucun système de pointage (TV, imprimante…) ;
• coarse : l’appareil dispose d’un système de pointage à la précision limitée (écran tactile,
Nintendo Wii, Kinect, Google Glass…) ;
• fine : l’appareil dispose d’un système de pointage précis (souris, stylet, trackpad…).
Ainsi, il serait possible de revoir certains éléments d’interface selon si l’utilisateur est
capable d’effectuer des actions précises ou non. Par exemple, on peut imaginer agrandir la
taille des boutons quand l’utilisateur dispose d’un système de pointage imprécis (coarse),
par exemple son smartphone ou sa tablette (auquel cas on peut considérer le doigt comme
le système de pointage).
Cependant, on se heurte rapidement à un problème : nous disposons déjà d’un moyen de
faciliter nos actions lorsque nous ne sommes pas équipés d’un curseur précis, et il s’agit
du zoom.
Pensez-y, lorsque vous naviguez sur votre téléphone et que vous désirez cliquer sur un lien
ridiculement petit, vous zoomez pour pouvoir appuyer dessus. Lorsque vous avez zoomé,
considérez-vous toujours que vous êtes en possession d’un système imprécis ?
Techniquement, oui. Votre système n’a pas évolué.
En revanche, les éventuelles adaptations d’interface qui ont été effectuées par rapport à
votre système de pointage n’ont plus lieu d’être dans ce cas.
Si l’on ne fait que modifier la taille des boutons, on ne risque pas grand-chose, je vous
l’accorde. En revanche, si l’on se met à modifier l’interface dans son entièreté, les choses
peuvent devenir complexes. Prenons un exemple pas à pas où un site/une application
adapterait l’interface en fonction du système de pointage pour que :
• l’interface soit complète et précise lorsque pointer vaut fine ;
• l’interface soit plus rudimentaire et que le scroll soit horizontal quand pointer vaut coarse.
Lorsque je visite le site sur ma tablette tactile, je bénéficie d’une interface adaptée à ma
venue avec des boutons d’action larges et un scroll horizontal qui n’est pas sans rappeler
Windows 8. Tout va bien.
Je me rends ensuite sur mon ordinateur de bureau, où l’interface est adaptée là encore :
davantage de boutons visibles à l’écran, parfois plus petits certes, mais ma souris me
permet des clics précis. Pas de problème non plus.
J’achète un nouvel écran qui s’avère être tactile comme ça se fait beaucoup de nos jours
pour mon ordinateur. Me voilà avec l’interface grossière et le scroll horizontal sur mon
ordinateur !
Et cela parce que dans le cas où plusieurs systèmes de pointage seraient détectés, selon les
spécifications, le navigateur se doit de retourner la valeur la « moins capable ». Par
exemple, coarse, si coarse et fine sont tous deux détectés.

Capacité de survol
Dans la même lignée que la Media Query que l’on vient de voir, il sera bientôt possible de
détecter si l’utilisateur est susceptible de survoler des éléments du document. Là encore,
trois valeurs sont proposées :
• none : il est impossible pour l’utilisateur de survoler des éléments ;
• on-demand : il est complexe pour l’utilisateur de survoler des éléments (on pensera aux
smartphones équipés de Air Gesture) ;
• hover : l’utilisateur est en mesure de survoler les éléments sans effort.
Fait curieux, les spécifications demandent explicitement aux auteurs de ne pas supposer
qu’un contexte retournant none ne supporte jamais :hover.
En effet, dans le cas où un appareil supporterait deux systèmes de pointage, par exemple
un appareil tactile pouvant éventuellement être contrôlé par souris (Windows Surface,
Chromebook Pixel, entre autres), la valeur retournée par cette Media Query serait none car
l’appareil initial ne permet pas le survol (et les navigateurs se doivent de retourner la
valeur la moins capable s’il y a plusieurs valeurs). Cependant, la souris lui permettrait de
déclencher des actions de survol.
Cet exemple, pourtant simple, présenté dans les spécifications montre un fait tout aussi
simple : l’intérêt de cette Media Query est somme toute très limité. D’autant qu’il est
d’ores et déjà tout à fait possible d’utiliser la pseudo-classe :hover comme amélioration
progressive.

Fréquence de mise à jour


Laissons de côté les effets de pointeur et de survol pour s’atteler à quelque chose de
totalement différent : la capacité pour un appareil à mettre à jour le document avec la
requête update-frequency. Bien que ce soit souvent le cas, il y a des cas où il n’est pas
possible de le mettre à jour, ou alors de façon très limitée.
Trois valeurs sont disponibles :
• none : il est impossible de mettre à jour le document (lors d’une impression, par
exemple) ;
• slow : des changements CSS peuvent être effectués mais l’appareil n’est pas en mesure
de les rendre suffisamment rapidement à l’écran pour percevoir une animation fluide
(encre électronique ou appareils en manque de batterie) ;
• normal : il est possible de mettre à jour le document et à une vitesse suffisante pour que
des animations puissent être utilisées.
Je trouve personnellement que @media (update-frequency: none) fait doublon avec @media print.
À ma connaissance, le papier est le seul support qui ne peut pas être mis à jour. Quant à la
valeur slow, j’ai peine à lui trouver une véritable utilité.
Les spécifications prennent comme exemple le souhait de rendre les liens soulignés en
permanence plutôt qu’au survol grâce à cette Media Query afin qu’ils soient distinguables
du texte normal quand le document est imprimé.
a {
text-decoration: none;

}
a:hover, a:focus {
text-decoration: underline;

}
@media (update-frequency: none) {
a {

text-decoration: none;
}
}

aurait été suffisante, avec l’avantage d’être supportée partout. Quoi qu’il en
@media print
soit, je suis peut-être médisant. Après tout, qui sait ? Peut-être que l’avenir nous réserve
du papier qui peut être mis à jour, avec une vitesse réduite dans un premier temps, ce qui
nous offrirait un cas d’usage pour @media (update-frequency: slow) !

Gestion des débordements


Deux Media Queries traitant de la gestion du débordement font leur apparition : overflow-
inline et overflow-block. Commençons par la première qui est plus légère avec seulement
deux valeurs :
• none : tout contenu débordant sur l’axe X est tout simplement masqué (non rendu), par
exemple pour des affiches ;
• scroll : tout contenu débordant sur l’axe X peut être atteint en scrollant.
En plus de celles-ci, la requête overflow-block accepte deux valeurs supplémentaires :
• optional-paged : le contenu peut être atteint en scrollant mais il est possible de déclencher
des changements de page (via les propriétés break-after, break-before, break-inside), par
exemple dans le cadre d’un diaporama ;
• paged : le contenu est paginé (impressions, e-books…).

Futur : Media Queries et variables


Dans le chapitre précédent, nous avons vu comment définir des propriétés personnalisées
en guise de variables. De la même manière, il sera bientôt possible de stocker des Media
Queries dans des variables. En revanche, il s’agit là d’un autre module, et par conséquent
d’une autre syntaxe.
Les spécifications décrivent ces variables comme des alias pour les Media Queries. La
syntaxe est très simple : on utilise la directive @custom-media, suivie du nom de l’alias
précédé par deux traits d’union (--) comme pour les variables, suivis d’une ou plusieurs
Media Queries.
Par exemple :
/* Définition d’un alias pour une Media Query */
@custom-media --small (min-width: 400px);

Par la suite, cet alias peut être utilisé comme s’il s’agissait d’une requête classique :
@media (--small) {
/* Styles soumis à (min-width: 400px) */

Fait intéressant, parce que cet alias est considéré comme une Media Query à part entière, il
peut être utilisé au sein d’une liste de requêtes. Par exemple :
@media (--small) and (max-width: 800px) {

/* Styles soumis à (min-width: 400px)


* et (max-width: 800px) */
}

Cas pratique : le design Mobile First


La très populaire méthodologie de design Mobile First, qui consiste à prévoir un design
pour les petits écrans avant tout, puis d’élargir le cadre pour les plus grands écrans, est
généralement basée sur une succession de Media Queries testant la largeur minimale. Par
exemple :
/**
* Styles hors d’une Media Query

* pour les petits écrans (smartphones)

*/

@media (min-width: 600px) {


/**

* Styles pour écrans à partir de 600 px de large,

* vraisemblablement les premières tablettes


*/

@media (min-width: 900px) {


/**

* Styles pour écrans à partir de 900 px de large,


* les ordinateurs portables et grandes tablettes

*/
}
@media (min-width: 1200px) {

/**

* Styles pour écrans à partir de 1200px de large,


* les écrans de bureau et grands portables

*/

Bien que l’on teste souvent la largeur de l’écran, il peut parfois être utile de conditionner
la mise en page en fonction de la hauteur de l’écran. Un cas d’usage intéressant serait de
n’implémenter un en-tête fixe que lorsque l’écran fait une certaine hauteur, sous peine de
rendre le contenu difficilement accessible.
.header {
height: 200px;

}
/**
* Uniquement les écrans ayant une hauteur

* d’au moins 800 pixels


*/
@media (min-height: 800px) {

.header {
position: fixed;
top: 0;

}
}

Compatibilité des navigateurs pour les Media Queries


Ces tableaux de compatibilité ne concernent que les Media Queries que nous avons
abordées dans la première partie de ce chapitre, et aucunement les nouvelles Media
Queries et les alias.
Tableau 8–3 Navigateurs desktop

Tableau 8–4 Navigateurs mobiles


OPERA 12.1 ET INTERNET EXPLORER 9 Inclusion de la largeur de la scrollbar
Les navigateurs Opera 12.1 et Internet Explorer 9 intègrent la largeur de la scrollbar
dans les calculs pour les Media Queries basées sur la largeur de la fenêtre.
9
Transformations : un nouveau monde en
2D et en 3D

Les transformations CSS 2D et 3D permettent de donner à vos interfaces un aspect plus


réaliste et proche de véritables matériaux, ce qui permettra aux utilisateurs de mieux se
repérer.
Les transformations CSS ont pour objectif d’offrir aux intégrateurs la possibilité de
manipuler les éléments du DOM dans un environnement à 2 ou 3 dimensions.
Ce module va permettre la réalisation de mises en page toujours plus créatives en offrant
un moyen aux développeurs de pivoter, mettre à l’échelle, incliner ou transposer les
éléments uniquement via les feuilles de styles, sans recourir ni à JavaScript ni à des
images (ou encore à Flash).
Toutes les manipulations de transformation se font à l’aide de la propriété transform, qui
accepte comme valeur les fonctions de transformation telles que rotate().
/* Syntaxe d’une transformation CSS */

transform: function(value);

Plusieurs fonctions peuvent être enchaînées, séparées par des espaces. Le résultat d’une
transformation est passé à la suivante, et ainsi de suite jusqu’à ce que toutes les
transformations soient appliquées.
/* Chaînage des transformations CSS */

transform: first-function(value) second-function(value);

Il est important de comprendre que les transformations ne sont que visuelles, c’est-à-dire
qu’elles sont effectuées sur un « calque » à part. De fait, une transformation n’a
absolument aucun impact sur le reste de l’environnement, encore moins sur le modèle de
boîte.
Par exemple, un élément rectangulaire transposé occupera toujours son emplacement
initial, même si visuellement il aura l’air d’avoir été déplacé, un peu à la manière du
positionnement relatif. De même, un élément mis à l’échelle de sorte qu’il soit deux fois
plus grand qu’initialement ne poussera pas les éléments autour de lui. Là encore, ce n’est
que visuel.
REMARQUE Transformations et éléments inline
Une restriction technique vient s’imposer à l’utilisation des transformations : il ne faut
pas que l’élément transformé soit inline. Tout autre type de display, y compris inline-
block, est nécessaire.
À quoi servent les transformations CSS ?
Ne vous êtes-vous jamais posé cette question ? Elle est tout à fait légitime, rassurez-vous.
Nous réalisons des interfaces pour le Web depuis des années maintenant, et nous n’avons
jamais ressenti le besoin d’avoir recours à de la 3D.
Pourtant, les solutions sont là depuis longtemps déjà : Flash, three.js, WebGL… Et depuis
quelques temps, CSS. Et pour cause, les transformations, et plus généralement la
manipulation des éléments dans un environnement en 2 et 3 dimensions n’a pas qu’une
dimension (sans jeu de mot) esthétique. C’est aussi très pratique pour inculquer des
repères.
Un exemple assez classique d’utilisation des transformations CSS, notamment les
transformations 3D, est le card flip, littéralement « retourné de carte ». Pensez-y : vous
êtes face à un plan qui affiche un certain nombre d’informations, par exemple une liste de
tâches (la fameuse to do list). Quand vous sélectionnez un élément de cette liste, le plan
pivote sur son axe central à la manière d’une carte de jeu pour se retourner et afficher une
nouvelle interface : les informations spécifiques à l’action effectuée.
Dans ce cas de figure, l’utilisation d’une rotation est non seulement agréable parce qu’elle
évite le côté abrupte d’un changement d’état, mais surtout elle donne du sens à l’action
effectuée : l’utilisateur n’est pas « téléporté » dans un autre endroit de l’application.
En somme, les transformations CSS, notamment lorsqu’elles sont couplées avec des
animations (que l’on étudiera dans le prochain chapitre), permettent de donner du sens aux
interfaces en se rapprochant de véritables textures et matériaux.
Les transformations 2D
On peut discerner deux grandes familles de transformations : celles qui s’appliquent sur un
plan unique, et celles qui ont lieu dans un environnement en trois dimensions. Bien
évidemment, ces dernières sont plus complexes à appréhender, et c’est pourquoi nous les
étudierons dans un second temps.
Je vous propose de commencer tout de suite avec la découverte des fonctions de
transformation de base (ou la redécouverte pour ceux d’entre vous qui sont déjà à l’aise
avec le sujet).

Rotation
La fonction de rotation, rotate() permet de pivoter un élément sur lui-même. Elle accepte
un unique argument, un angle, qui déclenchera une rotation dans le sens des aiguilles
d’une montre de l’élément.
Au risque de vous surprendre, sachez que l’angle (et tous les angles en CSS, y compris
ceux pour les dégradés) peut être spécifié dans quatre unités différentes :
• en degrés (deg), entre 0 et 360 ;
• en grades (grad), entre 0 et 400 ;
• en radians (rad), un cercle étant égal à 2π radians ;
• en tours (turn), 1 tour valant 360° bien évidemment.
La règle de calcul est la suivante : 1turn == 360deg == 400grad ~= 6.283rad. Notons que la
rotation se fait dans le sens horaire et non trigonométrique. Toutefois, il est possible
d’utiliser des valeurs négatives pour tourner dans le sens inverse des aiguilles d’une
montre.
Quoi qu’il en soit, les angles sont souvent définis en degrés ou en tours, principalement
parce que ce sont là les deux unités les plus intuitives pour mesurer la valeur d’un angle.
Pivoter un élément peut sembler être un gadget mais en réalité c’est là quelque chose que
l’on trouve dans de très nombreux projets. Par exemple, pour réaliser une petite pastille à
la manière d’un sticker, ou pour donner un effet photo ou Post-it à une note. Jusqu’alors, il
fallait utiliser une image pour cela mais aujourd’hui c’est on ne peut plus simple :
.element {

transform: rotate(15deg);
}
Figure 9–1
Application d’une légère rotation à un élément (3 degrés)

CURIOSITÉ « Do a barrel roll »


Le moteur de recherche Google a intégré un petit clin d’œil à l’aide de transform:
rotate() lors d’une recherche sur « do a barrel roll », qui peut se traduire par « faire un
looping ». En effet, la page pivote d’un tour dans le sens des aiguilles d’une montre de
manière animée grâce à une transition CSS.

Figure 9–2 Recherche Google « do a barrel roll »

De même pour les recherches « tilt » et « askew », où la page est très légèrement
inclinée via transform: rotate(1deg).
Figure 9–3 Recherche Google « akew »

Cas pratique : réaliser des flèches


J’aime beaucoup jouer avec des pseudo-éléments pivotés dans le but de réaliser des
flèches. Considérez un step wizard, c’est-à-dire un composant d’interface dont le but est
d’indiquer l’étape à laquelle l’internaute se trouve dans le cadre d’une action longue,
comme l’achat en ligne, par exemple.

Figure 9–4
Un step wizard sans image, pour lequel les flèches sont réalisées avec des pseudo-éléments pivotés.

Pour indiquer que les étapes sont successives, on fait en sorte de les lier par des flèches, un
peu à la manière des panneaux. Ces flèches indiquent la direction et le cheminement, au-
delà de donner des indications sur le parcours effectué et le parcours restant avant
aboutissement de l’action.
Le HTML dont nous avons besoin est très sémantique : une liste ordonnée, ni plus ni
moins.
<ol class="step-wizard">

<li>Panier</li>

<li>Authentification</li>
<li class="is-selected">Livraison</li>

<li>Paiement</li>

</ol>

Commençons par appliquer quelques lignes de CSS pour habiller tout cela :
/**
* Step wizard

* 1. Retrait des espaces entre les enfants générés par inline-block

*/
.step-wizard {

list-style: none;

font-size: 0; /* 1 */
padding: 0;

color: #555;
}

/**
* Élément de liste
* 1. Rétablissement de la taille de police par défaut

* 2. Contexte de positionnement pour le pseudo-élément

* 3. Rabattement de la hauteur de ligne


*/

li {

display: inline-block;
font-size: initial; /* 1 */

position: relative; /* 2 */
line-height: 1; /* 3 */

background: #EFEFEF;
padding: .5em 2em;
border: 1px solid rgba(0, 0, 0, .25);

}
li + li {
border-left: none;

}
/**
* Étape en cours

*/
li.is-selected {
background: deepskyblue;

font-weight: bold;
color: white;
}

Il ne nous reste plus qu’à mettre en place nos pseudo-éléments qui font office de flèches.
/**

* Flèches

* 1. Dimensionnement (diagonale d’un carré)


* 2. Pivotement du pseudo-élément pour faire une flèche

* 3. Positionnement sur la droite de chaque élément

*/
li::after {

content: '';

width: 1.41421356em; /* 1 */
height: 1.41421356em; /* 1 */

transform: rotate(45deg); /* 2 */

position: absolute; /* 3 */
left: 100%; /* 3 */

top: .25em; /* 3 */
margin-left: -.7em; /* 3 */

border: inherit;
border-left: none;

border-bottom: none;
z-index: 2;
background: inherit;

RESSOURCE Un step wizard avancé


J’ai personnellement développé un step wizard assez avancé en CSS (à l’aide de Sass)
faisant bon usage des pseudo-éléments, transformations, compteurs et bien d’autres
propriétés CSS, le tout pour un support desktop et mobile, avec une dégradation
gracieuse pour les vieilles versions d’Internet Explorer.

Figure 9–5 Un step wizard avancé, sans image, sans JavaScript

http://bit.ly/step-wizard-css

Émulation de rotation avec les filtres propriétaires


Bien qu’Internet Explorer 8 ne supporte pas la propriété transform, et par conséquent pas
non plus toutes les fonctions de transformation, Microsoft permet la rotation d’éléments
du DOM depuis longtemps déjà grâce à un filtre propriétaire à la syntaxe pour le moins
confuse.
Malheureusement, ce filtre ne permet pas une rotation minutieuse de l’élément mais
uniquement par quarts de tour, c’est-à-dire que vous pouvez pivoter un élément de 90,
180, 270 ou 360 degrés (soit 0 degré).
La valeur à définir pour rotation au sein de la valeur du filtre doit être 0, 1, 2 ou 3 où :
• 0 : 0 degré ;
• 1: 90 degrés ;
• 2 : 180 degrés ;
• 3 : 270 degrés.
/* Rotation de 270 degrés pour Internet Explorer 7 et 8 */
.element {

/* Internet Explorer 8 */

-ms-filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
/* Internet Explorer 7 */

filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);

Conversion d’angle avec un préprocesseur (Sass)


Selon le contexte et le fonctionnement employé dans votre framework, vous pouvez avoir
à convertir un angle d’une unité à l’autre. Effectuer les calculs à la main peut être un peu
fastidieux, aussi il est possible d’effectuer une fonction de calcul si vous utilisez un
préprocesseur CSS (ici, Sass).
Cette fonction a été développée par Chris Eppstein, le développeur du framework pour
Sass appelé Compass. Elle permet de convertir un angle défini selon n’importe laquelle
des quatre unités en l’unité de votre choix.
@function convert-angle($value, $unit) {

$units: deg grad turn rad;


$factors: 1 10grad/9deg 1turn/360deg 3.1415926rad/180deg;

@if index($units, unit($value)) and index($units, $unit) {

@return $value
/ nth($factors, index($units, unit($value)))

* nth($factors, index($units, $unit));


}
@error "Cannot convert #{unit($value)} to #{$unit}";

Par exemple :
.element {

transform: rotate(convert-angle(90deg, rad)); // 1.5708rad


}

Translation
La propriété transform accepte comme valeur la fonction translate(), qui peut prendre un ou
deux arguments. Cette fonction permet de transposer un élément sur son calque visuel. De
fait, elle ne déplace pas réellement l’élément mais simplement sa zone de rendu. L’espace
initial occupé par l’élément reste donc vide et ne peut pas être comblé.
La fonction translate() attend des longueurs (positives ou négatives) comme arguments. Le
premier argument (et possiblement le seul) agit pour l’axe X, et le second pour l’axe Y.
/**

* Déplace l’élément de

* 100 px vers la droite


*/

.element {

transform: translate(100px);
}

/**

* Déplace l’élément de
* 50 px vers la gauche,

* 75 px vers le haut

*/
.element {
transform: translate(-50px, -75px);

Il est également possible de définir les translations X et Y de manière individuelle grâce


aux fonctions translateX() et translateY(). Attention toutefois, si vous déclarez deux fois la
propriété transform, comme pour toutes les propriétés CSS, seule la seconde sera prise en
compte. Aussi, pensez à enchaîner les transformations au sein de la même valeur.
/**
* Déplace l’élément de

* 42 px vers la droite

*/
.element {

transform: translateX(42px);

À propos des offsets


On est en droit de se demander quel est l’intérêt de cette fonction par rapport à la position
relative et la translation via les offsets (top, right, bottom et left). Après tout, le résultat est
strictement le même. C’est une question que j’ai personnellement posé à Paul Irish,
ingénieur chez Google afin de connaître les éventuelles différences entre les deux
techniques. Paul a écrit par la suite un article très complet sur le sujet.
Pour résumer, la principale différence entre les deux méthodes est que le positionnement
via les offsets est contraint de respecter la grille de pixels alors que la translation CSS 3 est
capable de gérer le sub-pixel rendering.
Quand bien même cela fait peu de différence dans le cas d’un positionnement que je
qualifierais de « statique », ça rentre en ligne de compte lorsqu’il s’agit d’animer la
position d’un élément. Utiliser une translation plutôt que des offsets donnera un effet
beaucoup plus fluide.
L’autre différence majeure intervient du côté des performances. En effet, déplacer un
élément avec les offsets demande au navigateur de « repeindre » l’élément, c’est-à-dire de
recalculer son apparence. De l’autre côté, transposer un élément avec translate() est
effectué sur un calque séparé (appelé RenderLayer), ce qui est beaucoup moins coûteux en
termes de performance.
REMARQUE Pas de panique
Nous retrouverons ces notions plus en détail dans la section sur l’accélération
matérielle du chapitre 10 dédié aux animations CSS.
Autrement dit, préférez l’utilisation de translate() quand il s’agit de déplacer le calque
visuel d’un élément, et mettez en place une solution avec position: relative et les offsets
pour Internet Explorer 8 uniquement. Le résultat sera plus fluide et plus rapide.
PLUS D’INFOS
Retrouvez l’article de Paul Irish à propos de la différence de performance entre les
deux méthodes de translation sur son propre blog.
http://bit.ly/paul-irish-offset-performance

Cas pratique : centrage absolu


Nous avons parlé de centrage absolu dans le chapitre traitant de la fonction calc et nous
avons vu que ce n’est pas chose aisée en CSS. Les développeurs ont redoublé d’efforts
pour trouver des techniques plus ou moins farfelues, et l’une d’elles implique l’utilisation
d’une translation.
Un des plus gros problèmes des solutions de centrage absolu est qu’il faut bien souvent
connaître les dimensions de l’élément pour qu’elles fonctionnent. Bien que ce soit souvent
le cas pour les pop-ups, par exemple, il peut y avoir des cas où on ne connaît pas les
dimensions de l’élément à centrer.
On serait tenté de décaler l’élément via des margin négatifs déclarés en pourcentage.
Malheureusement, dans le cas des marges, une valeur en pourcentage s’appuie sur les
dimensions du parent, et pas sur les dimensions de l’élément lui-même. En revanche, ce
n’est pas le cas pour une translation via la propriété transform. Lorsque vous indiquez une
translation de 50 %, cela déplace l’élément de la moitié de sa largeur.
Grâce à cette particularité, on peut mettre au point une technique qui ne nécessite pas de
connaître les dimensions de l’élément :
.element {
position: absolute;
top: 50%;

left: 50%;
transform: translate(-50%, -50%);
}

Vous avez là une excellente technique de centrage absolu si le support de navigateur le


permet.

Figure 9–6
Centrage absolu dans des dimensions inconnues grâce à transform: translate()

RESSOURCE Pour aller plus loin


Si vous désirez plus d’informations sur le centrage en CSS, je vous recommande ce
guide complet de Chris Coyier sur CSS-Tricks. Il traite des deux types de centrages
(horizontal et vertical) et de toutes les manières d’y parvenir. Une ressource à garder
sous la main !
http://bit.ly/css-centering

Mise à l’échelle
De la même façon qu’il est possible de pivoter et de transposer un élément, il est
également possible de le mettre à l’échelle, c’est-à-dire de le grossir ou de le rapetisser. Là
encore, ce n’est jamais que l’apparence visuelle qui est modifiée, et non pas les
dimensions de l’élément.
En d’autres termes, modifier l’échelle d’un bloc via la fonction scale() n’aura aucune
incidence sur son environnement.
La fonction scale accepte un ou deux arguments, sans unité. Les arguments passés à la
fonction sont des multiplicateurs, c’est-à-dire que c’est un coefficient de mise à l’échelle.
Par exemple, scale(2) signifie concrètement « deux fois plus gros ». Si un seul argument
est spécifié, la mise à l’échelle sera effectuée sur les deux axes. En revanche, si deux
arguments sont spécifiés, le premier effectuera une mise à l’échelle sur l’axe X, et le
second sur l’axe Y.

Cas pratique : agrandissement au survol


J’aime particulièrement cette propriété pour rendre un élément légèrement plus grand lors
de son survol sans pour autant que ça ait un impact sur ses voisins. Par exemple :
.element:hover {
transform: scale(1.10);

Cette règle aura pour effet de grossir un élément de 10 % sur les deux axes lorsqu’il est
survolé.

Figure 9–7
Au survol de l’élément, il grossit légèrement.

Comme pour la fonction de translation, vous avez également accès aux fonctions scaleX()
et scaleY() pour ne mettre à l’échelle un élément que sur un axe bien précis.

Émulation de mise à l’échelle avec zoom sur Internet


Explorer 8
Bien qu’Internet Explorer 8 ne supporte pas les transformations en CSS, Microsoft a mis
en place la méthode propriétaire zoom qui, au-delà de déclencher ce que l’on appelle le
hasLayout d’Internet Explorer et corriger un certain nombre de bugs, permet de zoomer ou
dézoomer sur un élément.
En revanche, la propriété zoom ne semble faire varier que la taille du texte d’un élément et
pas directement ces dimensions. Selon ce que vous désirez comme effet, utiliser le zoom
peut être une alternative pour Internet Explorer 8 et moins.
.element {
-ms-zoom: 0.5;
transform: scale(0.5);

Bug d’Android 2.3


Bien que le navigateur par défaut d’Android (Android Stock Browser) supporte les
transformations CSS depuis la version 2.1, la version 2.3 intègre un bug qui ne
redimensionne pas les images de fonds appliquées via la propriété background-image. C’est un
bug mineur mais il est bon de le connaître dans le cas où vous auriez à supporter Android
2.3 et que vous désiriez appliquer une mise à l’échelle sur un élément avec une image
d’arrière-plan.

Inclinaison
Bien que l’on puisse trouver sans mal un intérêt à des fonctions de rotation, de
transposition et de mise à l’échelle, je dois avouer que j’ai davantage de mal à trouver un
cas d’usage pratique pour une fonction d’inclinaison d’un élément. C’est pourtant ce que
font les fonctions skewX() et skewY() ; elles permettent de faire varier l’angle de croisement
des axes X et Y.
Contrairement aux autres fonctions de transformation, il faut impérativement dissocier les
deux axes ; la fonction skew() n’existe plus. Elle a existé dans les premières ébauches de
spécifications, mais elle a été par la suite remplacée par skewX() et skewY().
Les deux fonctions n’acceptent qu’un seul argument : un angle, généralement exprimé en
degrés, mais il est également possible de l’exprimer en radians (rad), grades (grad) ou
même tours (turn) comme nous l’avons vu dans la section traitant de la fonction rotate().
Lorsque vous appliquez une inclinaison via skewX(), l’axe horizontal ne bouge pas mais
l’axe vertical pivote (déformant ainsi l’élément). À l’inverse, quand c’est une
transformation en Y, l’axe vertical reste statique mais l’axe horizontal pivote.
RESSOURCE Un outil pour skew()
Ana Tudor, spécialiste en géométrie et amatrice de CSS à ses heures perdues, a
développé une petite démo interactive pour comprendre le fonctionnement de la
transformation skew(). Évidemment, je vous la recommande fortement.

Figure 9–8 La démo interactive d’Ana Tudor pour apprendre à se servir de skew()

http://bit.ly/skew-tool

Cas pratique : un menu incliné


À titre d’exemple, prenons un menu de navigation au look un peu rétro. Les liens ont une
couleur de fond et se trouvent légèrement inclinés sur un côté. C’est le moment d’utiliser
skewX !

Figure 9–9
Un menu incliné grâce à transform: skew()

Mais d’abord, commençons par le HTML.


<ul role="navigation" class="nav">

<li><a href="#">Robot</a></li>
<li><a href="#">Rainbow</a></li>

<li><a href="#">Unicorn</a></li>

</ul>

Nous pouvons dès à présent passer au CSS.


.nav {
padding: 0;

list-style:none;

}
/**

* Éléments de liste

* 1. Inclinaison des éléments de liste


*/

.nav li {
display: inline-block;

transform: skewX(20deg); /* 1 */
background: #ddd;
}

.nav li:hover {

background: deepskyblue;
}

/**

* Liens
* 1. Inclinaison inverse des liens

*/
.nav a {

display: block;
text-decoration: none;
padding: .5em;

color: black;
transform: skewX(-20deg);/* 1 */
}

li:hover a {
color: white;
}

Vous l’avez remarqué, nous appliquons une transformation aux éléments de liste (li), mais
également une transformation aux liens. En effet, cette dernière a pour but d’annuler celle
du parent afin que le texte soit toujours lisible ! Sans celle-ci, le texte du lien serait lui
aussi incliné sur le côté, chose que nous ne désirons pas.

matrix
Nous avons vu les quatre transformations disponibles en CSS : rotation, translation, mise à
l’échelle et inclinaison.
Toutefois, une cinquième fonction a été élaborée en secret visant à remplacer les quatre
autres. Une fonction pour se moquer. Une fonction pour les amener toutes, et dans les
mathématiques les lier — en référence au Seigneur des anneaux de J. R. R. Tolkien. Il
s’agit de la fonction matrix().
Celle-ci a pour but de combiner toutes les transformations en une seule. On peut voir cette
fonction comme un raccourci pour une chaîne de transformations, mais cela impliquerait
qu’elle soit destinée à être renseignée manuellement, ce qui n’est fort heureusement pas le
cas quand on voit une valeur comme celle-ci :
transform: matrix(0.7071067811865475, 0.7071067811865476, - 0.7071067811865476, 0.7071067811865475,
-0.7071067811865497, 34.648232278140824)

Cet exemple est l’équivalent exact de la chaîne de transformations suivante :


transform: rotate(45deg) translate(24px, 25px)
On est en droit de s’interroger sur l’intérêt d’une telle fonction, surtout dans la mesure où
elle est essentiellement plus longue et plus complexe à écrire qu’une simple chaîne de
transformations.
En réalité, c’est une question de performance. Lorsque l’on en vient à enchaîner de
nombreuses transformations, la version matrix() devient alors plus rapide à exécuter par le
navigateur.
Ceci étant dit, pour que matrix() devienne plus intéressante que des fonctions de
transformations enchaînées, il faut que votre page dépende de manière importante des
transformations CSS.

Compatibilité des navigateurs pour les transformations 2D


Tableau 9–1 Navigateurs desktop

Tableau 9–2 Navigateurs mobiles

INTERNET EXPLORER ET FIREFOX Problème avec les éléments SVG


Internet Explorer et Firefox (toutes versions confondues) ne semblent pas être en
mesure d’appliquer des transformations CSS sur des éléments SVG. Cependant, les
attributs SVG dédiés aux transformations fonctionnent normalement.
CONSEIL Ne pas utiliser sur des éléments avec position: fixed
La majorité des navigateurs rencontrent de lourds problèmes lorsqu’une
transformation CSS est appliquée à un élément affublé de position: fixed. Je vous
recommande donc d’éviter ce cas de figure.
L’origine de transformation
Appliquer des transformations ne fait pas tout. Parfois, il est nécessaire de modifier le
point d’origine de la transformation, notamment dans le cas d’une rotation, par exemple.
Par défaut, l’origine d’un élément est en son centre (50% 50%) mais elle peut être modifiée
grâce à la propriété transform-origin.
Cette propriété peut être définie de plusieurs façons : soit en lui passant une ou deux
longueurs, soit en lui passant un ou deux pourcentages, soit en lui passant un ou deux
mots-clés. Dans le cas où un seul argument lui serait passé, l’origine ne serait modifiée
que sur l’axe X et toujours considérée à center (c’est-à-dire 50%) sur l’axe Y.
Les mots-clés autorisés sont :
• top, soit 0% sur l’axe vertical ;
• right, soit 100% sur l’axe horizontal ;
• bottom, soit 100% sur l’axe vertical ;
• left, soit 0% sur l’axe horizontal ;
• center, soit 50% sur l’axe horizontal si celui-ci n’est pas spécifié, sinon 50% pour l’axe
vertical.
Vous pouvez donc utiliser les combinaisons de mots-clés suivantes (où l’ordre n’a pas
d’importance) :
• top left (0% 0%) ;
• top center (0% 50%) ;
• top right (0% 100%) ;
• right center (50% 100%) ;
• bottom right (100% 100%) ;
• bottom center (100% 50%) ;
• bottom left (100% 0%) ;
• left center (50% 0%).
.element {

transform: rotate(25deg);

transform-origin: left top;


}

COMPATIBILITÉ DES NAVIGATEURS transform-origin


Le support de transform-origin est strictement le même que le support des
transformations 2D énoncé précédemment.

Tester et débugger transform-origin


Il n’est pas toujours évident de tester et débugger la propriété transform-origin, surtout
lorsqu’on est à la recherche d’un effet plus ou moins complexe. Pour cela, il existe une
petite technique consistant à s’appuyer sur un pseudo-élément pour visualiser l’origine de
transformation de l’élément.
Le positionnement de ce pseudo-élément doit être absolu et ses valeurs d’offset depuis les
bords gauche et haut doivent respectivement correspondre aux deux valeurs de transform-
origin. Par exemple :

.element {

transform-origin: 25% 66%;


}

/**

* Pseudo-élément destiné à la visualisation de l’origine


* 1. Dimensionnement (petit point)

* 2. Décalage négatif de la moitié des dimensions


* 3. Équivaut à la première valeur de transform-origin
* 4. Équivaut à la seconde valeur de transform-origin

*/
.element::after {
content: '';

width: 6px; /* 1 */
height: 6px; /* 1 */
margin: -3px; /* 2 */

position: absolute;
left: 25%; /* 3 */
top: 66%; /* 4 */

border-radius: 50%;
background: black;
}

Figure 9–10
Le petit cercle noir est le pseudo-élément, représentant l’origine de la transformation.
L’ordre des transformations
Je l’ai mentionné au début de ce chapitre, l’ordre d’application des transformations n’est
pas anodin. Parce que le résultat d’une transformation est passé à la transformation
suivante, permuter l’ordre des transformations peut produire un résultat tout à fait
différent.
Par exemple, les deux déclarations suivantes n’auront pas le même effet :
/* Cette transformation… */
.element {

transform: scale(1.5) translate(0, 50%);

}
/* … et cette transformation… */

.element {
transform: translate(0, 50%) scale(1.25);

}
/* … produisent des résultats différents ! */

Figure 9–11
Mêmes transformations et ordre différent = résultat différent

Pour que les deux transformations soient identiques, on pourrait modifier notre code
comme suit :
.element {

transform: scale(1.25) translate(0, 50%);


}

/**

* 50% × 1.25 = 62.5%


*/

.element {

transform: translate(0, 62.5%) scale(1.25);


}

Cette fois, les deux transformations sont équivalentes. Pensez toujours à l’ordre dans
lequel vous appliquez vos fonctions !
Figure 9–12
Une fois les transformations adaptées, l’effet est corrigé !
Les transformations 3D
Jusqu’à présent, nous avons essentiellement parlé des transformations 2D, c’est-à-dire les
manipulations d’un élément sur un plan unique. Cependant, vous l’aurez compris, le
module destiné aux transformations CSS propose également des transformations 3D, à
savoir la manipulation des éléments non seulement sur les axes X et Y, mais également Z.
Il est impossible de parler de profondeur de champ sans parler de perspective, aussi les
transformations 3D font entrer deux nouvelles propriétés en jeu : perspective et perspective-
origin, ainsi qu’une nouvelle fonction : perspective(). Nous allons voir la différence entre la
fonction et la propriété dans un instant.
Il est important de comprendre que les transformations 3D n’auront pas l’effet escompté si
l’élément auquel elles sont appliquées n’évolue pas dans un environnement en trois
dimensions. Cet environnement est généré en assignant de la perspective, aussi je vous
propose de démarrer par cela.
RESSOURCE Un FPS en CSS
À ce jour, la démonstration la plus impressionnante des effets 3D réalisés en CSS est
le FPS (First Person Shooter) de Keith Clark. À voir absolument.
http://bit.ly/css-fps

Figure 9-13 Le FPS en HTML 5 et CSS 3 de Keith Clark

RESSOURCE Géométrie en trois dimensions


Ana Tudor est réputée pour ses expériences exceptionnelles mélangeant de la
géométrie spatiale et des transformations 3D, le tout animé avec CSS également.
Machine puissante oblige.
http://bit.ly/ana-tudor-surfaces
http://bit.ly/ana-tudor-polyhedra
Figure 9-14
Une superbe démo parmi tant d’autres d’Ana Tudor

Compatibilité des navigateurs pour les transformations 3D


Tableau 9–3 Navigateurs desktop

Tableau 9–4 Navigateurs mobiles

Environnement 3D avec perspective


Il y a donc deux façons de conférer de la perspective, et par la même occasion un
environnement en trois dimensions à un élément : la propriété perspective ou la fonction
perspective().

Les deux formats activent un espace 3D avec une différence cependant. La notation sous
forme de fonction est très pratique lorsque vous souhaitez appliquer des transformations
3D à
un élément unique. Mais quand elle est utilisée sur plusieurs éléments, on remarque assez
rapidement le problème : chaque élément va avoir son propre point de fuite.
Figure 9–15
À gauche, chaque élément a son propre point de fuite et à droite, l’environnement est partagé entre tous les éléments.

Pour éviter cela, on peut alors utiliser la propriété perspective sur le parent de ces éléments
pour décrire un environnement en 3D unique, que tous les enfants partagent.
La valeur de perspective détermine le degré de perspective appliqué à l’élément. Plus la
longueur est courte (proche de 0), plus l’effet est prononcé. Au contraire, plus la longueur
se fait grande, moins la perspective est visible.
/* Effet léger */

.element {
perspective: 1000px;
}

/* Effet prononcé */
.element {
perspective: 250px;

Figure 9–16
Plus la valeur de perspective est proche de 0, plus l’effet est prononcé.

COMPATIBILITÉ DES NAVIGATEURS perspective


Le support de perspective est strictement le même que le support des transformations
3D énoncé précédemment.

Origine de perspective
La propriété perspective-origin détermine le point de fuite de l’élément. Elle doit être
renseignée en conjonction de la propriété perspective sur l’élément parent ou de la fonction
perspective() sur l’élément transformé.

Syntaxiquement, la propriété perspective-origin fonctionne exactement comme la propriété


. Vous pouvez lui passer une ou deux valeurs absolues, relatives, en
transform-origin
pourcentage ou des mots-clés.
.scene {

perspective: 1000px;
perspective-origin: top right;

COMPATIBILITÉ DES NAVIGATEURS perspective-origin


Le support de perspective-origin est strictement le même que le support des
transformations 3D énoncé précédemment.

En résumé
Nous avons à notre disposition les fonctions suivantes pour les transformations sur les
trois axes :
• rotateX(), rotateY(), rotateZ() et rotate3d() ;
• translateX(), translateY(), translateZ() et translate3d() ;
• scaleX(), scaleY(), scaleZ() et scale3d() ;
• skewX() et skewY() ;
• la fonction perspective() ;
• la propriété perspective ;
• perspective-origin ;
• matrix() et matrix3d().
Voilà beaucoup de fonctions ! Fort heureusement, la majorité d’entre elles fonctionne de la
même manière que les fonctions 2D que nous avons vu tout au long de ce chapitre, si ce
n’est qu’elles acceptent davantage d’arguments.
Il reste deux propriétés intimement liées aux transformations 3D que nous n’avons pas
encore abordées et dont nous allons avoir besoin pour notre exemple à venir, aussi je vous
propose de les étudier dès à présent.

Contexte et transform-style
La propriété transform-style détermine si les enfants d’un élément sont positionnés dans un
espace en trois dimensions (preserve-3d) ou s’ils sont aplatis dans le plan de l’élément (flat,
la valeur par défaut). Dans ce dernier cas, les enfants n’existeront pas en tant que tels dans
l’environnement 3D de l’élément.
Je dois admettre qu’il s’agit là d’une notion un peu compliquée à appréhender, toutefois
elle est capitale. Pensez à transform-style: preserve-3d comme un moyen de passer le
contexte de perspective d’un élément à ses enfants. Heureusement, notre exemple devrait
faciliter la compréhension de tout ceci.
COMPATIBILITÉ DES NAVIGATEURS transform-style
Le support de transform-style est strictement le même que le support des
transformations 3D énoncé précédemment si ce n’est qu’Internet Explorer ne
reconnaît pas cette propriété.

Notion de faces avec backface-visibility


Là encore, nous avons affaire à une propriété qui n’a de sens que dans un contexte en trois
dimensions. Grâce aux transformations 3D (par exemple, transform: rotateY(180deg)), il est
possible de retourner un élément à la manière d’une crêpe. Ainsi, ce que l’on considère
comme le recto de l’élément se retrouve au fond, et son verso se trouve face à l’utilisateur.
Lorsque l’on effectue cette manipulation, on peut voir que l’élément est tel qu’il le serait
dans le reflet d’un miroir. Ce comportement est déterminé par la propriété backface-
visibility. Sa valeur par défaut est visible, mais si vous la définissez à hidden, alors un
élément retourné sera tout bonnement invisible.
Si tout cela vous paraît abstrait, ne vous inquiétez pas ; notre prochain exemple va faire
bon usage de cette propriété ainsi que de transform-style.
COMPATIBILITÉ DES NAVIGATEURS backface-visibility
Le support de backface-visibility est strictement le même que le support des
transformations 3D énoncé précédemment.
Cas pratique : effet card flip
Afin d’illustrer toute cette théorie, je vous propose un exemple pratique : l’effet card flip,
c’est-à-dire le retourné de carte.
Pour cela, nous avons besoin de la structure HTML suivante :
<section class="container">

<div class="card">
<div class="side recto">Recto</div>

<div class="side verso">Verso</div>

</div>
</section>

L’élément .container sert d’environnement 3D, pour l’objet .card, qui contient quant à lui
deux éléments représentant les deux faces de la carte : recto et verso. Lors d’une
utilisation avancée des transformations 3D comme nous le faisons ici, il est généralement
recommandé de procéder comme suit :
• un conteneur qui initialise un environnement en trois dimensions grâce à perspective ;
• l’objet composé qui va essentiellement servir de conteneur pour les sous-composants qui
seront quant à eux transformés.
C’est une bonne pratique que de dissocier l’environnement de l’élément en lui-même.
Maintenant que notre structure est fin prête, nous pouvons commencer à appliquer des
styles. Commençons tout naturellement avec le conteneur d’environnement, auquel nous
définissons une dimension, et bien sûr une perspective.
/**
* Définition d’un environnement en trois dimensions
* 1. Dimensions
* 2. Contexte de positionnement pour les enfants

* 3. Établissement de l’espace 3D

*/
.container {

height: 420px; /* 1 */

width: 300px; /* 1 */
position: relative; /* 2 */

perspective: 750px; /* 3 */

Notre conteneur constitue les limites de notre environnement, aussi tous les enfants, aussi
bien .card que les éléments .side, emprunteront les dimensions de celui-ci. De plus, pour
pouvoir placer les éléments les uns au-dessus des autres, nous allons réinitialiser leur
système de positionnement grâce à position : absolute.
/* Réinitialisation du système de positionnement des enfants */
.card, .side {

position: absolute;

top: 0;
right: 0;

bottom: 0;
left: 0;

Nous allons dès maintenant faire bon usage de notre propriété transform-style. En effet, la
perspective du conteneur (.container) ne descend que sur ses enfants directs (c’est-à-dire
.card). Pour que les enfants de .card fassent également partie de cet espace 3D, il faut le
leur intimer grâce à transform-style: preserve-3d, sur leur parent (.card).
N’oublions pas non plus une transition (que nous étudierons plus en détail dans le dernier
chapitre de ce livre) afin que l’effet flip se remarque.
.card {

transform-style: preserve-3d;

transition: 1s;
}

Nous avons quasiment terminé ! Il ne nous reste qu’à cacher les faces lorsqu’elles sont
retournées grâce à la propriété backface-visibility. Nous pouvons également appliquer
d’autres styles à nos faces, si nous le souhaitons bien évidemment.
/* Afin que les faces retournées ne soient pas visibles */
.side {
backface-visibility: hidden;

Enfin, il faut pouvoir déclencher l’animation d’une manière ou d’une autre. Une solution
simple consisterait à appliquer une classe à l’élément .card pour le faire pivoter d’un
demitour sur son axe. Cette classe peut être ajoutée et retirée via JavaScript.
.card.flipped {

transform: rotateY(0.5turn);

C’est terminé ! L’effet est complètement fonctionnel et peut être utilisé assez simplement.
Parmi les usages intéressants que j’ai pu noter pour le card flip, on trouve :
• un changement de vue dans une application, typiquement passer d’un mode liste à une
vue de détails ;
• la confirmation de prise en compte d’un envoi de formulaire ;
• les différents états d’un bouton si le contexte s’y prête et que l’effet est suffisamment
rapide pour ne pas être trop tape-à-l’œil.
Figure 9-17
L’effet card flip en action

Si nous désirons avoir un effet un peu plus subtil justement, c’est tout à fait possible en ne
changeant que quelques lignes. Par exemple, à l’heure actuelle la carte tourne sur son axe
central sans le moindre déplacement. C’est un mouvement pur, à tel point qu’il attire
l’attention parce qu’il est difficilement reproductible à la main.
On pourrait faire en sorte que la carte pivote sur elle-même depuis un côté, mouvement
qui serait assez semblable à celui que l’on ferait pour retourner une carte sur une table.
Nous le verrons plus en détail dans le chapitre suivant, mais les animations ont pour but de
donner des repères, au-delà d’être esthétiques.
Revenons à notre exemple. Nous pouvons commencer par modifier la propriété transform-
origin de notre élément .card, qui je le rappelle, a pour valeur par défaut 50% 50%, soit le
centre de l’élément.
.card {
transform-origin: center right;

/* Autres styles … */
}

Toutefois, cette déclaration fait que la carte ne se trouve plus à la même position une fois
retournée. En effet, parce qu’elle n’a pas pivoté sur son axe central, elle est désormais
placée directement à droite de sa position initiale. Il faut donc annuler ce mouvement via
une transformation additionnelle : une translation.
.flipped {

transform: translate(-100%) rotateY(0.5turn);

}
Figure 9-18
Un effet un peu plus subtil pour le card flip

POUR ALLER PLUS LOIN


Pour une introduction et des exemples plus aboutis de transformations 3D, je vous
recommande chaudement ce document très complet de Dave DeSandro.
http://bit.ly/desandro-transforms
10
Animations et transitions : pour des
interfaces moins statiques

Les animations font partie intégrante d’une interface de qualité. Leur but n’est pas
simplement esthétique. En effet, elles apportent énormément d’informations sur
l’environnement et aident l’utilisateur à s’y retrouver.
Il y a toujours eu des animations dans les créations web. Plus ou moins kitch, certes, mais
il y en a toujours eu. On utilisait d’abord Flash pour cela, jusqu’à ce que l’on se rende
compte des limites de l’outil et des problèmes qu’il pouvait engendrer. Ce fut alors au tour
de JavaScript de briller, notamment grâce à l’API .animate() de jQuery qui a rendu
l’animation très accessible, y compris aux intégrateurs n’ayant que de maigres
connaissances en JavaScript.
Malheureusement, les animations en JavaScript ne sont pas sans défaut non plus. Parce
qu’elles sont gérées par le moteur JavaScript du navigateur et non par le navigateur lui-
même (pour rester simple), elles peuvent s’avérer relativement lentes et coûteuses. C’est
d’autant plus vrai sur de vieux navigateurs et/ou des processeurs peu puissants.
C’est pourquoi un système d’animation intégralement géré par CSS a vu le jour. En
réalité, il y a deux spécifications différentes qui traitent de deux fonctionnalités différentes
mais intimement liées : les transitions et les animations. C’est ce que je vous propose de
voir dans ce chapitre, mais pas sans une petite introduction.
À quoi servent les animations ?
Si comme moi, vous avez longtemps cru que les animations ne sont que de la poudre aux
yeux, détrompez-vous ! J’ai eu la chance de pouvoir discuter avec Rachel Nabors,
illustratrice et web designer de renom qui a su me faire comprendre l’importance que
peuvent avoir les animations dans les interfaces web.
Elle a su me prouver que les animations ont un véritable rôle à jouer dans les interfaces
avec un exemple très simple : un menu déroulant. Considérez un menu déroulant tout ce
qu’il y a de classique : lorsque vous cliquez dessus pour choisir une option, il s’ouvre.

Figure 10–1
À gauche le menu est fermé, au milieu son opacité change pour apparaître, à droite il est ouvert.

S’il n’est pas animé, c’est-à-dire s’il passe d’un état fermé à un état ouvert de manière
instantanée, le cerveau va devoir procéder à l’animation lui-même. Il s’agit là d’un
comportement mécanique du cerveau humain : il visualise les étapes qui permettent de
passer d’un état A à un état B parce que dans le monde réel (comprendre hors écran), rien
ne passe d’un état initial à un état final sans passer par des transitions plus ou moins
courtes (un ballon qui roule, un coucher de soleil, un éclair qui s’abat…).
Maintenant, si le menu est animé de sorte qu’il se déroule au moment du clic ou qu’il
apparaisse de manière progressive, la tâche n’est plus réalisée par le cerveau, qui peut
alors se concentrer sur l’essentiel : le choix de l’élément et la poursuite de son action.
Ce simple exemple démontre bien que les animations ne sont pas uniquement esthétiques.
Elles jouent un rôle primordial dans l’expérience de l’utilisateur. Des animations subtiles
permettent de garder l’utilisateur focalisé sur son action principale.
C’est d’autant plus vrai lorsque l’on parle d’interfaces mobiles. Vous avez sûrement déjà
rencontré une application ou un site mobile dans lequel le menu est initialement masqué,
pour n’être révélé que lorsque vous touchez l’icône « hamburger » (les trois segments
horizontaux placés les uns au-dessus des autres, design pattern qui s’instaure petit à petit
comme le symbole universel du menu). À cet instant, vous avez peut-être remarqué que le
menu n’apparaît pas d’un coup d’un seul à l’écran : il glisse rapidement depuis le bord
gauche pour venir chevaucher la zone de contenu.
Pour terminer, on mentionnera la façon dont Google perçoit les animations au cœur des
interfaces (notamment celles d’Android L) :
« Percevoir la forme d’un objet nous permet de comprendre comment le manipuler.
Observer le mouvement d’un objet nous informe s’il est léger ou lourd, flexible ou rigide,
petit ou grand. Le mouvement, dans le monde du design matériel n’est pas seulement
agréable, il permet surtout de définir l’espace, le fonctionnement et l’objectif du
système. »
RESSOURCE Google Design
L’approche de Google au sujet de l’animation est très intéressante, notamment
lorsqu’elle est mise en œuvre sur les applications mobiles (dans leur cas, l’interface
d’Android Lollipop). Je ne peux que vous suggérer de lire les études réalisées par
Google à ce sujet.
http://bit.ly/google-design-animation

Figure 10–2 Google Design, le styleguide des interfaces Android


Animation ou transition ?
Animation ou transition, telle est la question. Et il s’avère que c’est une question délicate
dans la mesure où les transitions ne sont jamais que des animations, et vice versa. Mais si
vous vous posez cette question, rassurez-vous, vous n’êtes pas le(la) seul(e). Il m’a fallu
du temps avant de finalement comprendre dans quels cas utiliser une transition et dans
quels cas utiliser une animation.
Si je devais résumer ma réponse au plus simple, je dirais que si vous vous trouvez dans le
besoin de plusieurs itérations ou d’une répétition, alors il vous faut une animation. En
effet, les transitions ne font que passer d’un état A à un état B ; aussi, il n’est pas possible
de planifier une boucle.
De la même façon, si vous n’avez besoin que d’une animation douce entre deux états,
comme pour un survol, par exemple, une transition suffit généralement amplement, surtout
si vous n’animez qu’une ou deux propriétés. Gardez toujours à l’esprit que les animations
CSS nécessitent plus de code et de complexité que les transitions, donc optez plutôt pour
ces dernières quand c’est envisageable.
À propos de l’accélération matérielle
Avez-vous déjà entendu parler de l’accélération matérielle ? Pour faire simple, elle
consiste à confier une tâche normalement réalisée par le processeur à un circuit intégré
dédié qui effectuera cette fonction de façon plus efficace.
Pour ramener ça au monde du Web et particulièrement à CSS, il faut savoir que la
modification d’une propriété ayant un impact direct sur le rendu d’une boîte demande au
navigateur de recalculer les styles de l’élément en question, et potentiellement ceux des
boîtes aux alentours. Bien que ce soit généralement insignifiant, cela devient vite coûteux
lorsqu’il s’agit d’une animation ou d’une transition puisque les styles doivent être
recalculés à de multiples reprises.
L’idée est donc de déléguer l’animation de ces propriétés à la carte graphique (aussi
appelée GPU pour Graphic Processor Unit), qui non seulement est plus performante que
le processeur (dit CPU pour Central Processor Unit) pour ce genre de tâche, mais laisse
également le temps au CPU de faire le reste (chargement de page, JavaScript, requêtes,
etc.).
Pour intimer au navigateur l’ordre de déplacer un élément sur son propre calque afin que
la carte graphique s’occupe de la modification, il y a deux solutions. La première, et
sûrement la plus connue, consiste à appliquer une transformation sur l’axe Z, c’est-à-dire
une transformation 3D. Cependant, pour ne pas modifier l’apparence de l’élément (nous
souhaitons juste communiquer avec le navigateur), nous utilisons souvent l’une de ces
deux déclarations :
/* Le null transform hack */
.element {
transform: translateZ(0);

}
/* … ou encore */
.element {

transform: translate3d(0, 0, 0);

N’importe laquelle de ces règles aura pour effet de basculer l’élément sur son propre
calque, pouvant ainsi être traité par la carte graphique et non par le processeur, améliorant
grandement les performances notamment en cas d’animation.
La seconde solution est plus récente mais aussi beaucoup plus propre. En effet, le W3C a
récemment proposé des spécifications pour une propriété appelée will-change. Celle-ci
permet de faire ce pourquoi on utilise habituellement une transformation nulle : prévenir le
navigateur que l’élément est susceptible de voir certaines de ses propriétés modifiées dans
un futur plus ou moins proche afin qu’il soit en mesure de procéder à d’éventuelles
optimisations.
/* La promotion de calque avec will-change */

.element {

will-change: transform, opacity;


}
/* … ou plus simplement */

.element {
will-change: all;

Attention ! Pour une solution comme pour l’autre, il peut être tentant de promouvoir tous
les éléments du document sur leur propre calque en utilisant le sélecteur universel *. Non
seulement ça ne va probablement pas améliorer les performances autant qu’on le
souhaiterait, mais surtout ça risque de faire plus de mal que de bien.
En effet, si chaque élément se retrouve promu sur son propre calque, tout ou presque est
délégué au GPU qui est susceptible de ne pas tenir la charge. À cet instant, on peut
constater des ralentissements (notez l’ironie) voire un plantage de la page. En résumé, il
faut utiliser l’accélération matérielle avec intelligence et parcimonie.
À noter que les propriétés opacity et transform sont naturellement optimisées par le
navigateur, aussi il est inutile de déclencher l’accélération matérielle manuellement (en
revanche, il est toujours judicieux d’utiliser will-change).
Bien évidemment, parce que cela s’appelle l’accélération « matérielle », cette technique
est intimement liée au matériel de votre ordinateur. Selon l’appareil utilisé, il est possible
que ce hack n’ait aucun effet. Certains navigateurs permettent de jeter un œil aux capacités
de votre machine et de voir s’il est possible de bénéficier de l’accélération matérielle. Sur
Firefox, rendez-vous à l’URL about:support, et sur Chrome à chrome://gpu.
POUR ALLER PLUS LOIN will-change
La propriété will-change étant relativement récente, les ressources sont assez rares.
Heureusement, Sara Soueidan a écrit un article extrêmement complet sur le sujet pour
le blog d’Opera.
http://bit.ly/will-change

Compatibilité des navigateurs pour will-change


Tableau 10–1 Navigateurs desktop

Tableau 10–2 Navigateurs mobiles


JavaScript ou CSS ?
Vous avez peut-être relevé que dans l’introduction de ce chapitre, j’ai déclaré que
JavaScript n’était pas en mesure de gérer des animations de manière performante. Je suis
allé un peu vite en besogne car ce n’est pas tout à fait vrai.
En réalité, les récents moteurs JavaScript sont tout à fait performants en matière
d’animations, à condition que celles-ci soient implémentées correctement.
J’ai volontairement pris l’exemple de jQuery et de son API très intuitive pour l’animation.
Le fait est que jQuery n’est pas une bibliothèque dédiée à l’animation, mais
essentiellement à la manipulation de DOM et à l’AJAX ; c’est la raison pour laquelle il
utilise setInterval au lieu de requestAnimationFrame (rAF) qui existe pourtant spécifiquement
pour l’animation en JavaScript.
Alors si JavaScript est tout aussi performant que CSS pour les animations, pourquoi ne pas
le faire en JavaScript directement ? C’est là une question délicate, parce que comme
souvent, la réponse est « ça dépend ».
Si vous devez choisir entre utiliser une bibliothèque JavaScript dédiée à l’animation
(GSAP, Velocity.js, TweenLite…) et réaliser vos animations en CSS, la réponse va
dépendre :
• des propriétés que vous souhaitez animer ;
• de l’impact de l’animation sur le reste (événements, AJAX…) ;
• du degré de contrôle que vous désirez avoir sur l’animation.
Il faut savoir que la différence entre des animations JavaScript optimisées et des
animations CSS est assez faible en termes de performance. Tout dépend des propriétés
animées. Comme nous l’avons vu dans la section dédiée à l’accélération matérielle, il est
préférable d’animer uniquement les transformations et l’opacité car ce travail est
naturellement transférable sur le GPU pour ne pas bloquer le fil d’exécution principal du
navigateur.
À partir de là, cela va essentiellement se jouer sur le contrôle nécessaire ; nous allons voir
que les animations CSS offrent un nombre de fonctionnalités non négligeables. En
revanche, si vous souhaitez synchroniser vos animations avec du JavaScript, cela devient
plus compliqué, sans être impossible pour autant.
En somme, j’aurais tendance à dire que si votre animation se résume à un état « booléen »,
par exemple un menu qui glisse dans/hors de l’écran, les CSS conviennent à merveille et il
est inutile de charger une bibliothèque JavaScript. En revanche, si vous avez besoin d’un
contrôle plus granulé sur l’animation, par exemple pour déclencher une autre animation,
ou même du JavaScript à un moment précis de l’animation, il serait préférable d’opter
pour une solution intégrale en JavaScript.
RESSOURCE CSS Triggers
Il n’est pas toujours évident de connaître l’impact d’une propriété CSS sur les
performances, et pourtant c’est très utile quand on cherche à améliorer les
performances des animations ! L’ingénieur Paul Lewis de chez Google a créé le projet
CSS Triggers qui rassemble tout ce que vous devez savoir à propos de l’impact de
chaque propriété CSS sur le navigateur.
http://bit.ly/css-triggers

Figure 10–3 CSS Triggers renseigne à propos des propriétés CSS coûteuses.
Les transitions
Quand une propriété CSS est modifiée, lors d’un changement d’état ou à l’aide de
JavaScript, par exemple, l’effet est appliqué de manière instantanée. Le module CSS
Transitions permet justement de faire en sorte que ce changement d’état ne soit pas abrupt
mais animé. Il est possible d’avoir un contrôle assez fin sur l’animation, notamment en
spécifiant la durée, le délai avant animation, le style d’animation ainsi que d’autres
paramètres.
Toutefois, toutes les propriétés ne peuvent pas être animées. Vous trouverez en annexe de
ce livre la liste des propriétés qui peuvent l’être mais pour faire simple, seules les
propriétés acceptant des couleurs et des valeurs numériques peuvent être animées. Par
exemple, vous pouvez tout à fait animer le changement de couleur de rouge à bleu ou une
longueur, mais pas la valeur de la propriété display.
La propriété transition est un raccourci des propriétés suivantes :
• transition-property, la propriété à animer ;
• transition-duration, la durée d’animation ;
• transition-timing-function, le type d’animation ;
• transition-delay, le délai avant animation.
En résumé, une transition concerne une seule propriété (« quoi ? »), pour une certaine
durée (« pendant combien de temps ? »), avec un éventuel délai (« quand ? ») et dans un
certain style (« comment ? »). C’est ce que nous allons voir immédiatement.
Notons qu’il est en revanche possible de définir plusieurs transitions pour plusieurs
propriétés, en les séparant par des virgules.
.element {

transition:
color 1s ease-in,

width 500ms linear;


}

« Quoi ? » avec transition-property


La propriété transition-property détermine quelles sont la ou les propriétés à être animées
dans le cas d’un changement sur l’une de ces propriétés. Il est donc possible de spécifier
plusieurs propriétés, séparées par des virgules.
.element {

transition-property: color, width;


}

Chaque propriété listée va correspondre à une transition, et pourra être liée à un délai, une
fonction de timing et une durée de manière indépendante.
Dans le cas où l’une des propriétés spécifiées n’est pas reconnue ou ne peut être animée,
toutes les autres propriétés sont tout de même animées. En d’autres termes, une propriété
non reconnue ne met pas en péril la transition des autres propriétés ; elle est simplement
omise.
Si vous ne désirez pas spécifier de propriété mais animer tout ce qu’il est possible
d’animer, vous pouvez utiliser le mot-clé all (valeur par défaut). Il ne semble pas y avoir
de répercussion sur les performances lors de l’utilisation de all plutôt que d’une propriété
en particulier.
.element {

transition-property: all;

« Comment ? » avec transition-timing-function


La propriété transition-timing-function est utilisée pour décrire comment les valeurs
intermédiaires des propriétés animées sont calculées. Elle permet donc de faire varier la
vitesse de la transition au cours de sa durée.
Il y a plusieurs façons de renseigner cette propriété. La solution la plus aboutie est de
définir une courbe de Bézier cubique via la fonction cubic-bezier(). Pour vulgariser, une
courbe de Bézier est une courbe pouvant être représentée sur un plan orthonormé où l’axe
Y représente chaque étape entre la valeur initiale et la valeur finale de la propriété animée,
et l’axe X représente l’avancement dans le temps de la transition.

Figure 10–4
La représentation de la valeur initiale de transition-timing-function

RESSOURCE Outil de création de courbes de Bézier


Lea Verou a développé un petit outil permettant de réaliser vos propres courbes de
Bézier de manière visuelle. Pratique quand on est à la recherche d’un effet bien
particulier !
http://bit.ly/cubic-bezier
Figure 10–5 L’outil de création de courbes de Bézier par Lea Verou

Il est très compliqué de renseigner une courbe de Bézier, principalement parce que ce n’est
pas du tout intuitif, les navigateurs supportant le module de transitions intègrent également
une collection de mots-clés servant d’alias à des courbes de Bézier prédéfinies :
• linear (cubic-bezier(0, 0, 1, 1)) ;
• ease (cubic-bezier(0.25, 0.1, 0.25, 1)), la valeur par défaut ;
• ease-in (cubic-bezier(0.42, 0, 1, 1)) ;
• ease-out (cubic-bezier(0, 0, 0.58, 1)) ;
• ease-in-out (cubic-bezier(0.42, 0, 0.58, 1)).

Figure 10–6 De gauche à droite : linear, ease, ease-in, ease-out et ease-in-out

WEBKIT Différence de retour de la fonction getComputedStyle()


En théorie, la fonction getComputedStyle() est supposée retourner la valeur de transition-
timing-function sous la forme d’une fonction cubic-bezier, même dans le cas où elle est
spécifiée via un mot-clé. En pratique, le moteur de rendu WebKit retourne le mot-clé,
par exemple ease, alors que les autres navigateurs retournent cubic-bezier(0.25, 0.1, 0.25,
1).

Sachez qu’il est également possible de renseigner cette valeur à l’aide de la fonction steps,
mais pour des raisons de simplicité et parce qu’elle est assez rarement utilisée dans le
cadre des transitions, nous traiterons cette fonction uniquement dans la partie dédiée aux
animations.
De manière générale, j’aurais tendance à vous conseiller d’utiliser ease-out. Un timing
ease-out débute rapidement pour ralentir progressivement sa vitesse jusqu’au stade final.
Figure 10–7
Courbe de Bézier ease-out

Ce comportement donne à l’utilisateur un sentiment de réactivité, ce qui est bien


évidemment positif. C’est sans compter le ralentissement sur la fin de la transition qui
laisse le temps à l’utilisateur de comprendre le mouvement (et de l’apprécier).
Bien que ease-out soit un mot-clé associé à la courbe cubic-bezier(0, 0, 0.58, 1), sachez qu’il
est possible de produire des variations de cette équation tout en restant dans un mode ease-
out. Pour un effet plus prononcé, on peut envisager ce que l’on appelle un ease-out
« Quintic » dont la courbe est cubic-bezier(0.23, 1, 0.32, 1).

Figure 10–8
Courbe de Bézier ease-out dite « Quintic »

RESSOURCE Une bibliothèque de courbes de Bézier


Si vous cherchez de l’inspiration pour vos animations mais ne parvenez pas à trouver
la timing-function qui convient, sachez qu’il existe un site référençant toute une
collection de fonctions connues.
http://bit.ly/easings
RESSOURCE Comprendre le fonctionnement des courbes de Bézier
Si le sujet vous intéresse, je ne peux vous recommander assez cette courte vidéo de
Peter Nowell expliquant avec beaucoup de simplicité le fonctionnement des courbes
de Bézier.
http://bit.ly/video-cubic-bezier
« Combien de temps ? » avec transition-duration
La propriété transition-duration indique la durée de la transition entre l’état de départ et
l’état d’arrivée. Vous l’aurez compris, la valeur par défaut est 0, c’est pour cela qu’un
changement sur une propriété d’un élément, comme la couleur, se fait de manière
instantanée.
En incrémentant cette valeur, les navigateurs supportant le module de transitions CSS
modifient les styles de l’état initial à l’état final sur la durée indiquée. C’est ce qui produit
un effet d’animation subtile et visuellement agréable à l’œil.
Cette propriété attend donc une valeur de type <time>, c’est-à-dire une durée en secondes
(s) ou millisecondes (ms), telle que 1s ou 500ms.
.element {

transition-duration: 1s;
}

Choisir la durée d’une transition n’est pas chose aisée. Trop courte, l’animation peut
paraître agressive. Trop longue, elle est susceptible d’être obstructive et de frustrer
l’utilisateur. Pour choisir la bonne durée, cela dépend avant tout de l’effet recherché, mais
également de la timing-function utilisée :
• ease-out : entre 200 et 500 millisecondes. Une durée dans cet intervalle permet de
percevoir l’animation sans pour autant qu’elle ne vienne entraver le contenu. Souvenez-
vous que les animations doivent être subtiles ;
• ease-in : à nouveau, entre 200 et 500 millisecondes est généralement une bonne
moyenne. Gardez à l’esprit que ease-in se termine nécessairement par une accélération et
peu importe la durée, l’effet sera toujours aussi « brutal » ;
• effets bounce, elastic ou autre : autour d’une seconde, à 200 millisecondes près. Les effets
plus exotiques exigent une durée sensiblement plus longue afin de percevoir l’effet
complet. Un effet complexe sur une courte durée paraîtra extrêmement agressif.
INTERNET EXPLORER ET OPERA Valeurs ridiculement petites
Quand bien même nul être humain serait capable de distinguer une transition
inférieure à 10 millisecondes, il est intéressant de noter qu’Internet Explorer et Opera
ne supportent pas des valeurs aussi infimes.
WEBKIT Fonction getComputedStyle() imprécise (ou trop précise)
La fonction JavaScript getComputedStyle() permet de récupérer la valeur réelle (pas
nécessairement la même que la valeur assignée) d’une propriété CSS. Tout d’abord, il
est important de relever que la valeur de transition-duration est toujours exprimée en
secondes (s), même si elle a été spécifiée en millisecondes (ms).
Il s’avère que cette fonction a un léger problème d’implémentation sur le moteur de
rendu WebKit, retournant des valeurs extrêmement précises telles que
0.009999999776482582s au lieu de 0.01s.
« Quand ? » avec transition-delay
La propriété transition-delay, comme la propriété transition-duration, attend une valeur de
type <time> afin de définir le délai avec lequel l’animation débutera. En spécifiant une
durée positive, l’animation ne sera pas déclenchée dès que la ou les propriétés sont
modifiées mais une fois le délai écoulé.
En revanche, spécifier un délai négatif aura un effet particulier : la transition débutera
immédiatement mais on aura toutefois l’impression qu’elle a déjà effectué une partie de sa
course. Par exemple, une transition d’une seconde avec un délai de -0.5s débutera
instantanément au changement d’état. Elle ne démarrera pas à l’état initial mais
directement dans un état à moitié entamé.
.element {

transition-delay: 0.5s;

Il peut être intéressant d’appliquer un léger délai aux transitions lorsqu’elles sont
appliquées à des éléments majeurs dans le layout dont les propriétés sont modifiées en cas
de survol.
Ceci permet d’éviter des effets de flash désagréables lors d’un survol hâtif de la page.
Délayer simplement les transitions d’un dixième de seconde (ou moins) suffit à éviter de
les déclencher par inadvertance.
.element {
transition-delay: 100ms;
}

INTERNET EXPLORER ET OPÉRA Valeurs ridiculement petites


À nouveau, les navigateurs Internet Explorer et Opéra ne supportent pas les valeurs
comprises entre -10 ms et 10 ms.

Utilisation
Les transitions sont parfaites pour éviter les modifications de styles abruptes lors d’un
changement d’état. Le cas le plus commun est de changer la couleur des liens lorsqu’ils
sont survolés ou la cible du focus. Avec une simple transition CSS, on peut donner un effet
smooth à ce changement de couleur :
a {
transition-property: color;

transition-duration: .15s;

transition-delay: 0s;
transition-timing-function: ease;

Notez que l’on spécifie la transition sur l’élément dans son état standard, et pas sous l’effet
de :hover ou :focus. En effet, on souhaite que le changement d’état soit adouci ; il faut donc
bien déclarer la transition sur l’état initial pour qu’elle ait de l’effet dès que l’état est
modifié.
On pourrait réduire la déclaration précédente en utilisant la propriété transition, raccourci
pour toutes les propriétés énoncées précédemment :
a {

transition: color .15s 0s ease;


}

C’est déjà beaucoup mieux ! On pourrait raccourcir davantage la déclaration en comptant


sur les valeurs par défaut de chacune des propriétés :
a {
transition: .15s;

Parfait ! Comme nous l’avons vu tout à l’heure, la valeur par défaut de transition-property
est all, c’est-à-dire que toutes les propriétés pouvant être animées bénéficient de la
transition, dont color.
Par défaut, les transitions n’ont pas de délai, rendant la valeur 0s obsolète. Et pour finir, il
se trouve que ease est la fonction d’animation par défaut. Ce qui ne nous laisse plus que la
durée, définie à 150 millisecondes.

Quelques informations complémentaires


Tout d’abord, les transitions ne sont pas soumises à l’événement DOMContentLoaded, c’est-à-
dire qu’elles peuvent tout à fait débuter avant que le document soit intégralement chargé.
Toutefois, et pour des raisons de performance, les transitions ne sont pas appliquées à des
éléments invisibles (hors écran, masqués…). Cependant, Opera est le seul navigateur qui
exécute tout de même les transitions sur ces éléments.
Il n’est pas possible d’effectuer une transition entre deux dégradés. De la même manière,
la transition d’un dégradé vers une couleur solide est possible mais ne fonctionne pas
correctement : la transition est effectuée du blanc à la couleur finale, infligeant ainsi un
« flash » au début de la transition lorsque le dégradé est remplacé par du blanc.
Dernière information, et non des moindres, les transitions unidirectionnelles (quand le
passage d’un état A vers un état B est animé, mais pas le passage de B à A) se comportent
un peu bizarrement. La logique voudrait que l’on ait quelque chose comme ceci :
/**

* .initial > .final = instantané


* .final > .initial = animé

*/

.element.initial {
background-color: tomato;

transition: background-color 400ms ease-out;

}
.element.final {

background-color: deepskyblue;

transition: none;
}

Eh bien non. Ce code aura pour effet de rendre abrupt le passage de l’état initial à l’état
final, et d’animer le retour de l’état final vers l’état initial. Pour que seul l’aller soit animé,
il faut écrire :
/**

* .initial > .final = animé

* .final > .initial = instantané


*/

.element.initial {

background-color: tomato;
transition: none; /* optionnel */

.element.final {

background-color: deepskyblue;
transition: background-color 400ms ease-out;
}

Aussi étonnant – et si vous me demandez mon avis, illogique – que cela puisse paraître,
c’est bien dans ce sens que cela fonctionne.
En fait, au moment où la classe .final est appliquée à l’élément, c’est la nouvelle valeur de
transition qui prend effet pour animer le passage de la classe .initial à la classe .final.

Cas pratique : une sidebar plus légère


Il y a un effet que j’aime personnellement beaucoup sur les sites : la sidebar semi-
transparente quand inactive (c’est-à-dire non survolée/focusée).

Figure 10–9
Sans interaction, la sidebar est semi-transparente.

Lorsque l’on interagit avec celle-ci, son contenu redevient opaque à l’aide d’une brève
transition, puis lorsque l’on cesse de l’utiliser, une transition beaucoup plus longue et donc
plus subtile vient diminuer son opacité pour qu’elle ne vienne pas visuellement empiéter
sur le contenu principal.

Figure 10–10
Au survol, la sidebar devient opaque.

Voyons comment réaliser cet effet à l’aide des transitions, notamment le fait d’avoir deux
transitions différentes (lorsque l’on rentre et lorsque l’on sort de la sidebar).
.sidebar {
opacity: .5;
transition: opacity 5s;

}
.sidebar:hover,
.sidebar:focus {

opacity: 1;

transition: opacity 300ms;


}

Cas pratique : changement de vue sur mobile


Si vous avez le loisir de développer des interfaces dédiées aux supports mobiles,
notamment aux smartphones, vous avez sûrement rencontré le cas typique du changement
de vue. Par exemple, le passage d’une liste d’éléments à un écran dédié aux informations
de l’élément choisi, ou encore l’ouverture d’un menu au clic sur une icône.
Figure 10-11
Une liste d’éléments comme on en voit beaucoup.
Figure 10-12 Au clic sur un élément de la liste, la vue change pour donner des informations.

Bien évidemment, vous avez appris votre leçon et savez désormais qu’animer le passage
d’une vue à une autre est important pour donner de l’information à l’utilisateur sur l’action
exécutée.
Figure 10–13
Au choix d’un élément de la liste, cette dernière glisse pour laisser place à la vue dédiée aux détails de l’élément choisi.

Commençons par installer notre markup :


<div class="container">

<div class="view view-list">


<!-- Liste d’éléments -->

</div>

<div class="view view-details">

<!-- Info concernant l’élément choisi,


inséré en JavaScript, par exemple -->

</div>

</div>

Ensuite, quelques lignes de CSS pour habiller tout cela :


/**
* Conteneur pour les vues

* 1. Pleine largeur/hauteur
* 2. Contexte de positionnement pour les vues

* 3. Masque la vue hors champ


*/
.container {

width: 100%; /* 1 */

height: 100%; /* 1 */
position: relative; /* 2 */

overflow: hidden; /* 3 */

}
/**

* Vues
* 1. Pleine largeur/hauteur

* 2. Transition ease-out (personnalisée)


* 3. Optimisation des performances via will-change
*/

.view {
position: absolute; /* 1 */
top: 0; /* 1 */

left: 0; /* 1 */
right: 0; /* 1 */
bottom: 0; /* 1 */

transition: 300ms cubic-bezier(.23, 1, .32, 1); /* 2 */


will-change: transform; /* 3 */
}

/**
* Place la vue de détails hors champ, sur la droite
*/

.view-details {

transform: translateX(100%);
}

Il ne nous reste plus qu’une chose à faire : décrire quelques styles afin de déplacer les vues
lors de l’ajout d’une classe au conteneur (ici, et par simplicité, .switch). La vue de liste se
déplace hors champ vers la gauche, et la vue de détails rentre dans le champ par la droite.
En somme, c’est comme si cette dernière poussait la première vue hors de l’écran.
.switch .view-list {

transform: translateX(-100%);
}

.switch .view-details {

transform: translateX(0);
}

Pour revenir à l’état précédent, il suffit de retirer la classe .switch du conteneur. La


transition sera alors jouée en sens inverse : la vue par défaut poussera la vue de détails
hors champ par la gauche.
Vous pouvez bien évidemment agrémenter cet exemple simple de davantage de fioritures,
comme une animation de l’opacité, un effet de transition plus prononcé ou que sais-je
encore.
L’idée reste la même, on anime le changement de vue pour donner une indication à
l’utilisateur que :
• son action a bien été prise en compte ;
• son action a déclenché un changement de vue.

Cas pratique : animation d’une pop-up


Nous parlons bien là de cet élément d’interface qui vient se superposer à la vue principale
jusqu’à action de l’utilisateur.
Vous en avez déjà tous vu et vous en avez déjà probablement tous codé. Que ce soit pour
alerter l’utilisateur à propos de quelque chose, pour lui demander de saisir une
information, ou juste à des fins publicitaires (comme le veut la triste tendance), la pop-up
est devenue un pattern incontournable des interfaces web, souvent bien mal utilisée
malheureusement.
Parce que c’est un composant d’interface délicat de par sa nature même, il est important
d’arrondir les angles et de faire en sorte que l’apparition d’une pop-up fasse sens, au-delà
d’être visuellement plaisante.
Dressons un postulat initial : la pop-up entre en scène de manière fluide, mais en sort
instantanément. Quand l’utilisateur a effectué l’action menant à la fermeture de la pop-up,
ne le faites pas attendre inutilement : détruisez-la aussi vite que possible.
Voyons comment faire cela. Dans un souci de simplicité, nous ne parlerons pas du markup
ici. Sachez simplement que l’on a besoin d’un élément (div, par exemple) avec la classe
.modal (parce que j’ai personnellement horreur du terme pop-up) au sein du conteneur
principal (body, .container, peu importe). Passons tout de suite aux styles.
/**
* Popup

* 1. Recouvrement total de la fenêtre

* 2. Centrage vertical, indépendant de la hauteur


* 3. Centrage horizontal avec marges latérales

* 4. Effet d’ombre sur le reste de la fenêtre

* 5. Invisibilité de la pop-up en temps normal


* 6. Retrait des effets de pointeur sur la pop-up

* 7. Optimisation des performances

*/
.modal {

position: fixed; /* 1 */

top: 50%; /* 2 */
transform: translateY(-50%); /* 2 */
right: 1em; /* 3 */

left: 1em; /* 3 */
box-shadow: 0 0 0 100em rgba(0, 0, 0, .5); /* 4 */

opacity: 0; /* 5 */
pointer-events: none; /* 6 */
will-change: opacity; /* 7 */

CLARIFICATION À propos de box-shadow


Nous utilisons box-shadow pour simuler un filtre transparent noir derrière la pop-up, mais
nous aurions tout aussi bien pu utiliser outline ou une simple bordure.
La raison pour laquelle j’ai opté pour box-shadow est que outline ne suit pas les bords
arrondis, souvent utilisés pour les pop-ups. De son côté, border interagit avec le modèle
de boîte, ce qui n’est pas idéal. Notons également que l’application d’une couleur,
semi-transparente qui plus est, à une zone aussi grande (~200 em × ~200 em) est
susceptible de causer des pertes de FPS (Fraps Per Second, autrement dit les images
par seconde) sur les processeurs les moins puissants.
La solution la plus fiable consiste à ajouter un conteneur autour de la pop-up auquel
on applique une couleur de fond semi-transparente, mais celle-ci exige un élément
supplémentaire, raison pour laquelle nous avons opté pour la solution de facilité.
Appliquons maintenant des styles lorsque la pop-up est visible. On considère qu’une
classe .is-visible a été ajoutée en JavaScript.
/**
* 1. Transition de l’état initial à l’état final fluide
* Transition de l’état final à l’état initial instantané

* 2. Affichage de la pop-up
* 3. Rétablissement des effets de pointeur
*/

.modal.is-visible {

transition: 250ms ease-out; /* 1 */


opacity: 1; /* 2 */

pointer-events: auto; /* 3 */

Jusque-là tout fonctionne à merveille, mais l’effet est un peu simple. Que diriez-vous
d’ajouter à l’animation de l’opacité un effet de zoom, ou plutôt de « dézoom ». Nous
allons faire en sorte que la pop-up « atterrisse » sur l’écran en diminuant son échelle
jusqu’à 1. Cela implique donc que la pop-up ait par défaut une échelle supérieure à 1 ;
nous devons donc modifier les styles initiaux.
.modal {

transform: translateY(-50%) scale(1.15);

will-change: opacity, transform;


}

Notez que nous devons enchaîner les transformations comme nous l’avons vu dans le
chapitre précédent. La première est destinée au centrage vertical, la seconde à la mise à
l’échelle. Attention, l’ordre est important !
Ajoutons une simple ligne à notre état final :
.modal.is-visible {
transform: translateY(-50%);
}

Prudence ! Il ne s’agit pas de retirer les transformations d’un seul bloc ; la translation
verticale n’a rien à voir avec notre effet d’apparition et doit perdurer sans quoi la pop-up
va se trouver déplacée ce qui n’est pas ce que l’on souhaite.

Compatibilité des navigateurs pour les transitions


Tableau 10–3 Navigateurs desktop

Tableau 10–4 Navigateurs mobiles

WEBKIT ET GECKO Transitions non synchronisées


Les transitions simultanées de plusieurs propriétés ne sont pas synchronisées dans les
moteurs de rendu WebKit et Gecko.
Par exemple, si vous réduisez la taille d’une bordure (border) et augmentez la taille de
la marge interne (padding) du même montant, le contenu va « trembler », alors qu’il est
théoriquement supposé rester complètement statique puisque les deux transitions
« s’annulent ».
FIREFOX Problème lorsque le parent est positionné
Firefox n’animera pas les propriétés d’un élément si l’un de ses parents voit son type
de position changée dans le même temps.

Transitions et pseudo-éléments
Malgré le support honorable des transitions, il faut savoir qu’il y a longtemps eu des
restrictions quant à l’application de celles-ci aux pseudo-éléments (notamment ::after et
::before). Parce que vous pourriez en avoir besoin, le tableau suivant présente la
compatibilité des pseudo-éléments uniquement.
Tableau 10–5 Tableau de compatibilité des transitions sur pseudo-éléments

Soulevons également un bug étrange sur Internet Explorer 10 (ne changeons pas les
bonnes habitudes !) : la transition d’un pseudo-élément au survol fonctionne à condition
que l’état survolé soit lui même défini, même à vide.
/* Ne fonctionne pas */
.element:hover:before { /* … */ }

/* Fonctionne */
.element:hover {}
.element:hover:before { /* … */ }
Les animations
En un sens, les animations CSS sont très voisines des transitions. La principale différence
entre les deux est le contrôle que vous avez sur vos animations (au sens large). En effet,
les transitions sont principalement prévues pour éviter les changements d’état abrupts,
mais pas pour gérer une véritable animation avec des étapes clés, des changements de
rythme…
Par exemple, une animation CSS va pouvoir boucler un certain nombre de fois, ou même
indéfiniment alors qu’une transition ne fait jamais qu’animer un élément d’un état initial à
un état final sur une certaine durée. De plus, il va être possible d’avoir un contrôle très fin
sur la qualité de l’animation, en spécifiant les étapes par lesquelles passe l’élément.
Parce qu’il est plus complet, le module d’animations est légèrement plus complexe que
celui des transitions. Le principe de base est simple : il faut définir une animation qui est
globalement une succession d’étapes clés au cours desquelles les propriétés de l’élément
animé varient, puis appliquer cette animation à l’élément grâce à la propriété animation.
Justement, la propriété animation est un raccourci de toutes les propriétés relatives à
l’animation, à savoir :
• animation-name, le nom de l’animation ;
• animation-duration, la durée de l’animation ;
• animation-timing-function, le type d’animation ;
• animation-iteration-count, le nombre d’itérations ;
• animation-direction, la direction de l’animation ;
• animation-play-state, l’état de l’animation ;
• animation-delay, le délai avant le début de l’animation ;
• animation-fill-mode, l’impact de l’animation hors de son exécution.
Comme vous pouvez le constater, une animation accepte de nombreux paramètres aussi je
vous propose de les voir un par un. Comme pour les transitions, il est possible d’appliquer
plusieurs animations à un même élément en les séparant par des virgules.
.element {
animation: <paramètres de l'animation 1>, <paramètres de l'animation 2>; }

« Quoi ? » avec animation-name


La propriété animation-name, comme son nom l’indique, comprend le nom de la ou des
animations appliquées à l’élément. Pour qu’une animation soit considérée comme valide,
il faut que le nom corresponde à une animation définie avec la directive @keyframes (voir
plus loin).
Si la valeur est none, aucune animation n’est jouée.
/**

* Applique l’animation mon-animation à l’élément


*/

.element {
animation-name: mon-animation;

}
/**
* Applique les animations mon-animation et mon-autre-animation à l’élément

*/

.element {
animation-name: mon-animation, mon-autre-animation;

« Comment ? » avec animation-timing-function


La propriété animation-timing-function fonctionne exactement comme la propriété transition-
timing-function du module de transitions : elle définit la façon dont l’animation va être
jouée. C’est par cette fonction que vous pouvez faire varier la vitesse d’exécution, par
exemple rapide au début, puis plus lente à la fin.
Cette propriété peut être renseignée de diverses façons :
• en renseignant une courbe de Bézier, à l’aide de la fonction cubic-bezier() ;
• par les mots-clés ease, ease-in, ease-out, ease-in-out et linear qui ne sont jamais que des alias
pour des courbes de Bézier prédéfinies ;
• en renseignant la fonction steps() (ou step-start ou encore step-end, qui sont des alias
respectifs pour steps(1, start) et steps(1, end)).
La fonction steps() est une fonction réservée aux modules de transitions et d’animations
qui accepte deux arguments. Le premier est le nombre d’intervalles d’animation. Le
second, qui est soit start, soit end (valeur par défaut) spécifie à quel moment de l’intervalle
le changement de valeurs a lieu.
Vous l’aurez compris, assigner cette fonction à la propriété animation-timing-function permet
d’animer le changement de valeurs d’un élément de manière hachée, c’est-à-dire que les
différents changements d’étapes seront effectués de façon abrupte (à la manière du stop-
motion).
Figure 10–14
À gauche steps(2, start), à droite steps(4, end)

Par exemple, si votre animation définie via @keyframes est constituée de cinq étapes, et que
vous désirez animer les changements de valeurs de l’élément sans qu’il y ait de véritable
transition d’une étape à l’autre, vous pouvez vous y prendre ainsi :
.element {
animation-timing-function: steps(5);
}

Cela dit, on utilise le plus souvent les mots-clés ease (valeur par défaut de cette propriété)
ou linear afin d’avoir une animation plate, c’est-à-dire dont la vitesse d’exécution ne varie
pas au cours de l’animation.
Quoi qu’il en soit, et là je vous renvoie à ce que l’on a vu au sujet de transition-timing-
function (voir page 296), il est souvent préférable d’opter pour ease-out, qui correspond à un
comportement naturellement appréciable.

« Combien de temps ? » avec animation-duration


La propriété animation-duration détermine la durée que prend l’animation pour être jouée une
fois. Cette propriété attend donc une valeur spécifiée en secondes (s) ou en millisecondes
(ms).
/**

* Définit une durée d’animation de 1 seconde

*/
.element {

animation-duration: 1s;

}
/**

* Définit une durée d’animation de 500 millisecondes,


* soit une demi-seconde

*/
.element {

animation-duration: 500ms;
}

Comme pour les transitions, choisir la durée d’une animation n’est pas toujours évident, et
pourtant c’est un des facteurs principaux dans la réussite d’une animation. Je vous invite à
vous référer à la section dédiée à la propriété transition-duration de la partie précédente
(voir page 299).

« Combien de fois ? » avec animation-iteration-count


La propriété animation-iteration-count permet de définir le nombre de fois où l’animation
sera jouée. La valeur par défaut est 1 et c’est pourquoi l’animation n’est jouée qu’une fois,
du début à la fin. Incrémenter cette valeur implique qu’une fois l’animation terminée, elle
redémarrera du début et ce jusqu’à satisfaire la valeur définie.
Notez qu’il est également possible de spécifier un nombre décimal comme 3.5, ce qui aura
pour effet de la faire jouer trois fois intégralement, puis de s’arrêter après avoir joué la
moitié de la quatrième fois. Ceci étant dit, je n’ai personnellement jamais rencontré de cas
d’usage pour cela, mais c’est toujours bon à savoir.
Si comme dans beaucoup de cas vous souhaitez que l’animation soit jouée indéfiniment, il
est possible de spécifier la valeur infinite afin de la faire boucler sans fin.
/**
* Répète l’animation ad vitam aeternam

*/
.element {
animation-iteration-count: infinite;

« Dans quel sens ? » avec animation-direction


La propriété animation-direction détermine si l’animation doit être jouée dans le sens
original ou le sens inverse sur certains cycles (ou itérations, déclarées via animation-
iterationcount). Grâce à cette propriété, il est donc possible de jouer une animation depuis
sa dernière étape clé jusqu’à sa première ou vice versa.
Il n’y a que quatre valeurs possibles pour cette propriété :
• normal : l’animation joue du début à la fin ;
• reverse : l’animation joue de la fin au début ;
• alternate : normal sur les cycles impairs, reverse sur les cycles pairs ;
• alternate-reverse : reverse sur les cycles impairs, normal sur les cycles pairs.
/**

* L’animation commence à la dernière étape clé


* pour finir à la première

*/
.element {

animation-direction: reverse;
}

Figure 10–15
Représentation visuelle des différentes valeurs d’animation-direction (sur une animation à trois itérations)

D’expérience, je dois dire qu’on se sert quasi exclusivement de normal et parfois d’alternate
dans le cas d’une animation infinie, mais j’ai toutefois trouvé un cas d’usage où reverse a
du sens.
Imaginez que vous ayez une animation qui transite la valeur d’opacité (opacity) de 0 à 1, et
qu’à un certain moment vous désiriez l’animer de 1 à 0. Plutôt que de créer une deuxième
animation avec @keyframes, il suffit d’appliquer la direction reverse à l’appel de l’animation.
CURIOSITÉ reverse et animation-timing-function
Lorsqu’une animation est jouée en reverse, son animation-timing-function est également
inversée, c’est-à-dire qu’une animation ease-in inversée aura l’effet d’un ease-out.

« Quel état ? » avec animation-play-state


La propriété animation-play-state permet tout simplement de mettre une animation en pause.
En effet, cette propriété ne connaît que deux valeurs : paused ou running. Quand une
animation est mise en pause, elle affiche l’état actuel de l’élément, et quand elle est remise
en route, elle reprend là où elle s’est arrêtée.
/**

* Met l’animation en pause


*/

.element {

animation-play-state: paused;
}

Les cas d’utilisation ne sont pas les plus fréquents, mais on peut imaginer un carrousel
fonctionnant avec des animations CSS qui serait mis en pause lorsqu’il est survolé. Il est
courant de survoler la zone que l’on observe à l’écran, aussi interrompre les animations du
carrousel lorsque le curseur est dessus permet de laisser le temps à l’internaute de
visualiser le contenu.
.carousel {
animation: carousel 15s infinite;

}
.carousel:hover {

animation-play-state: paused;
}

« Quand ? » avec animation-delay


Comme pour transition-delay, la propriété animation-delay permet de faire démarrer une
animation avec un certain délai. Là encore, il est possible de définir un délai négatif qui
aura pour effet de déclencher l’animation immédiatement, mais pas à son début. Par
exemple, une animation de 2 s avec un délai de -1 s, débutera immédiatement, mais au
beau milieu de l’animation.
Comme pour toutes les durées, cette propriété attend une valeur en secondes (s) ou en
millisecondes (ms).
/**

* Définit un délai de 150 millisecondes


* avant le début de l’animation
*/

.element {
animation-delay: 150ms;
}

« Quel impact ? » avec animation-fill-mode


La propriété animation-fill-mode définit l’impact de l’animation au-delà de son champ
d’exécution. Par exemple, à l’aide de cette propriété, vous pouvez faire en sorte que l’état
de l’élément à la fin de son animation persiste une fois celle-ci achevée, ou bien que l’état
décrit à la première étape de l’animation soit déjà effectif avant qu’elle ne démarre.
C’est pour cela que cette propriété n’accepte que quatre valeurs :
• forwards : les propriétés définies dans la dernière étape clé de l’animation sont maintenues
une fois l’animation terminée ;
• backwards : les propriétés définies dans la première étape clé de l’animation sont
appliquées durant le délai défini par animation-delay ;
• both : suit le comportement de forwards et backwards à la fois ;
• none : ne suit aucun comportement.
On se sert régulièrement de forwards ou de both pour conserver les styles modifiés par une
animation une fois celle-ci achevée.
/**

* Conserve les styles décrits à la dernière étape

* une fois l’animation intégralement achevée


*/
.element {

animation-fill-mode: forwards;
}

ANDROID 2.3 Pas de support


Attention lors de l’utilisation de cette propriété : Android 2.3 et les versions
inférieures ne la supportent pas.

La définition d’une animation : @keyframes


Nous venons de voir toutes les propriétés relatives à la mise en place d’une animation
mais nous n’avons toujours pas appris comment en créer une ! Nous l’avons compris
cependant, cela se fait via la directive @keyframes. C’est cette dernière qui va définir le
comportement de l’élément sur la durée de l’animation.
Une règle @keyframes se compose de la directive @keyframes immédiatement suivie d’un nom
qui servira de référence pour la propriété animation-name.
@keyframes mon-animation {

/* Le contenu de mon-animation */
}

Elle contient ensuite une ou plusieurs étapes clés (le terme officiel étant « sélecteurs
keyframes », selon les spécifications) qui se composent d’une ou plusieurs valeurs définies
en pourcentage et/ou par les mots-clés from (équivalent à 0%) et to (équivalent à 100%). Ces
sélecteurs définissent les différentes étapes auxquelles l’élément animé verra ses
propriétés modifiées.
/**

* L’élément prend la couleur deepskyblue


* au début de l’animation,
* pour arriver à tomato au premier quart de l’animation,

* puis lightgreen à la moitié

* et enfin tomato à nouveau au 3e quart

*
*/

@keyframes mon-animation {

from {
color: deepskyblue;

25%, 75% {
color: tomato;

50% {
color: lightgreen;

Vous n’êtes pas obligé de préciser les clés 0% (ou from) et 100% (to). Dans tous les cas, le
navigateur construira lui-même une clé à ces pourcentages. De même, vous n’êtes pas tenu
de spécifier les clés dans l’ordre croissant. Elles seront de toute façon triées par le
navigateur, qui en profitera pour exclure les duplicatas et les clés invalides (inférieures à 0%
ou supérieures à 100%).
Chaque étape clé accepte donc un nombre illimité de propriétés/valeurs classiques.
Cependant, seules les propriétés pouvant être animées le seront ; vous trouverez la liste de
ces propriétés dans les annexes de ce livre.
Illustrons tout ceci avec un exemple, voulez-vous ? L’animation suivante, appelée
animation-opacity, est composée d’une unique étape clé to (soit 100%) qui indique que
l’élément doit avoir une opacité de 0 lorsque l’animation s’achève.
@keyframes animation-opacity {
to {
opacity: 0;

}
}

Elle est appliquée au survol d’un élément :


/**
* Équivaut à :

* animation-name: animation-opacity;
* animation-duration: 750ms;
* animation-timing-function: ease-in-out;

* animation-iteration-count: 1;
* animation-delay: 0.1s;
* animation-fill-mode: none;

* animation-play-state: running;
*/
.element:hover {

animation: animation-opacity 750ms ease-in-out 0.1s;

Animations et performance
Une animation maintenue en CSS sera généralement plus performante qu’une animation
réalisée en JavaScript, mais ce n’est pas pour cela que les animations CSS ne coûtent rien.
En réalité, il y a deux propriétés que le navigateur est capable d’animer de manière
extrêmement fluide : opacity, et transform. Cela n’est pas sans rappeler la notion
d’accélération matérielle que nous avons abordé au début de ce chapitre.
Bien évidemment, il est possible d’animer beaucoup plus que cela, mais ce n’est pas sans
coût du point de vue des performances. Animer une autre propriété que celles-ci peut faire
tomber les images par seconde (FPS, Fraps Per Second) en dessous de 60, palier que l’on
considère généralement optimal.
Fort heureusement, ce sont souvent ces propriétés que l’on souhaite animer. Par
conséquent, souvenez-vous qu’il vaut mieux utiliser transform: translate() que les propriétés
offset top, right, bottom et left lorsqu’il s’agit de déplacer un élément à l’écran.
POUR ALLER PLUS LOIN
Si vous désirez approfondir la question de l’animation de la position d’un élément à
l’écran, je vous recommande vivement ces articles par Chris Coyier et Paul Irish.
http://bit.ly/css-tricks-offset-performance
http://bit.ly/paul-irish-offset-performance

Cas pratique : animation d’un loader


Design pattern courant dans les applications (qu’elles soient mobiles ou web), mais aussi
sur certains sites, le loader est un élément d’interface destiné à indiquer un état de
chargement.
Évidemment, il existe autant de loaders différents que de bugs dans Internet Explorer,
aussi je vous propose d’en créer un simple ne serait-ce que pour bien comprendre le
principe.
Un bon loader est constitué d’un élément unique, qui contient du contenu textuel indiquant
que quelque chose est en train d’être chargé. Ce contenu, bien que masqué à l’écran,
servira à l’accessibilité, notamment aux lecteurs d’écran.
<div class="loader">Chargement…</div>

Côté HTML, c’est tout ce dont nous avons besoin et vous allez voir que les styles
nécessaires à la réalisation d’un loader en CSS sont tout aussi succincts.
/**

* Loader
* 1. Masquage du texte
* 2. Dimensionnement du loader

* 3. Centrage horizontal
* 4. Rendu sphérique

* 5. Bordure invisible …

* 6. … sauf à droite
* 7. Animation spin infinie

*/

.loader {
text-indent: 101%; /* 1 */

overflow: hidden; /* 1 */

white-space: nowrap; /* 1 */

width: 5em; /* 2 */
height: 5em; /* 2 */

margin: 1em auto; /* 3 */

border-radius: 50%; /* 4 */
border: .5em solid transparent; /* 5 */

border-right-color: deepskyblue; /* 6 */

animation: spin 1s linear infinite; /* 7 */


}

/**
* Définition de l’animation avec une étape clé unique

* à laquelle l’élément aura effectué un tour complet


*/
@keyframes spin {

to {

transform: rotate(1turn);
}

Figure 10–16
Un loader accessible et animé rien qu’en CSS

Pour un rendu un petit peu moins monotone, on peut opter pour une timing-function
différente de linear, comme ease-out. En somme, la rotation sera d’abord rapide, puis un
peu plus lente sur la fin, avant de reprendre la rotation suivante rapidement à nouveau.
RESSOURCE Un loader avancé par Lea Verou
Si vous aimez l’idée d’un loader exclusivement fait en CSS (et vous devriez), sachez
que Lea Verou a écrit un très bon article à ce sujet dans lequel elle combine visuel
intéressant, accessibilité et animation.
http://bit.ly/lea-verou-loader

Cas pratique : animation d’un carrousel


Dans le cas où vous seriez sur un projet qui ne vous contraint pas à supporter des
navigateurs obsolètes tels qu’Internet Explorer 8 ou même 9, vous pouvez utiliser les
animations CSS pour des besoins fonctionnels, au-delà même de l’idée d’amélioration
progressive.
Figure 10–17
Un carrousel sans JavaScript

Par exemple, vous pourriez mettre en place un carrousel d’images animé intégralement en
CSS plutôt qu’en JavaScript. C’est ce que je vous propose de faire en commençant par
déclarer le HTML nécessaire à notre carrousel :
<div class="slider-wrapper">
<ul class="slider">
<li class="slide"><img src="…" alt="…" /></li>

<li class="slide"><img src="…" alt="…" /></li>


<li class="slide"><img src="…" alt="…" /></li>
<li class="slide"><img src="…" alt="…" /></li>

<li class="slide"><img src="…" alt="…" /></li>


</ul>
</div>

Le conteneur global, dont les dimensions sont les mêmes que celles d’une slide, fait office
de masque. Son débordement est caché.
/**

* Conteneur aux dimensions fixes calées sur un slide


*/

.slider-wrapper {

width: 620px;
height: 240px;

margin: 1em auto;

overflow: hidden;

}
/**

* Conteneur des slides qui va être animé sur l’axe X

* 1. 5 slides de 5 secondes → animation de 25 s


* 2. 5 slides → largeur de 500 %

*/

.slider {
list-style: none;

padding: 0;
animation: slide 25s infinite linear; /* 1 */

margin: 0;
width: 500%; /* 2 */
overflow: hidden;

/**
* Alignement des slides à l’horizontal

*/

.slide {
float: left;

}
/**

* Tous les 20 %, on décale le conteneur de 20 % sur la gauche


* pour faire place au slide suivant.
* Afin que le slider reste fixe durant le temps d’un slide,

* on limite les déplacements sur des intervalles de 1 %. */


@keyframes slide {
0%, 19% { transform: translateX(0); }

20%, 39% { transform: translateX(-20%); }


40%, 59% { transform: translateX(-40%); }
60%, 79% { transform: translateX(-60%); }

80%, 99% { transform: translateX(-80%); }


100% { transform: translateX(-100%); }
}

Cet exemple est somme toute assez sommaire, et peut-être n’êtes vous pas friand de l’effet
« slide ». Concluons donc cette démo avec une autre feuille de styles pour notre slider,
cette fois pour un effet « fade-in ».
/**

* Conteneur des slides


* 1. Contexte relatif pour les slides

*/

.slider {
width: 620px;

margin: 1em auto;

height: 240px;
overflow: hidden;

position: relative; /* 1 */

}
/**

* Slides

* 1. 5 slides de 5 secondes → animation de 25 s


*/
.slide {

max-width: 100%;
position: absolute;

left: 0;
right: 0;
animation: slide 25s linear infinite; /* 1 */

/**
* Décalage de l’animation de 5 s pour chaque slide

*/

.slide:nth-child(2) { animation-delay: 5s; }


.slide:nth-child(3) { animation-delay: 10s; }

.slide:nth-child(4) { animation-delay: 15s; }


.slide:nth-child(5) { animation-delay: 20s; }

/**
* De 0 à 1.25 s (25 × 5 / 100), le slide apparaît
* De 1.25 s à 3.75 s (25 × 15 / 100), le slide subsiste

* De 3.75 s à 5 s, le slide disparaît


* De 5 s à 25 s, le slide est invisible
*/

@keyframes slide {
0%, 20%, 100% { opacity: 0 }
5%, 15% { opacity: 1; }

RESSOURCE
Bien que l’effet n’ait rien de nouveau, cette implémentation à la fois légère et flexible
a pour auteur Brad Knutson.
http://bit.ly/knutson-carousel
POUR ALLER PLUS LOIN Un slider en CSS
Notre exemple est relativement simple mais sachez qu’il est possible de pousser les
choses beaucoup plus loin. Dans cet article publié sur le site Smashing Magazine,
Alessio Atzeni utilise des animations assez complexes pour réaliser un carrousel du
plus bel effet.
http://bit.ly/css-slideshow
Figure 10-18
Un slider CSS impressionnant par Alessio Atzeni, avec barre de défilement, légendes au survol et pour couronner
le tout, des animations soignées

Cas pratique : animation de l’affichage d’une galerie


d’images
Il y a quelques mois, j’ai développé le site de la photographe qui illustre cet ouvrage. Une
page unique comme on en voit souvent, principalement constituée d’une grille de photos.
Ces photos, je ne voulais pas qu’elles soient visibles d’entrée de jeu ; je désirais animer
leur arrivée de sorte qu’elles n’apparaissent pas toutes en même temps mais avec un
certain décalage.

Figure 10–19
Les images sont affichées avec un léger décalage, rendant le chargement initial bien moins abrupt.

Commençons par donner quelques styles de base (volontairement simplifiés) à nos


éléments :
.item {
display: inline-block;

width: 10em;
height: 10em;

margin: 1em;
}

Maintenant, nous avons besoin de notre animation. Elle est extrêmement simple puisque
nous ne faisons varier que l’opacité (opacity) :
@keyframes fade-in {

to {

opacity: 1;
}

Et enfin, il ne reste plus qu’à appliquer l’animation à nos éléments, avec un délai
proportionnel à leur position dans la grille.
.item {

animation: fade-in 350ms 0s backwards;


}
/**

* Application d’un délai


* en fonction de la position de l’élément dans la grille
*/

.item:nth-of-type(1) { animation-delay: 0.1s; }


.item:nth-of-type(2) { animation-delay: 0.2s; }
.item:nth-of-type(3) { animation-delay: 0.3s; }

.item:nth-of-type(4) { animation-delay: 0.4s; }


.item:nth-of-type(5) { animation-delay: 0.5s; }
.item:nth-of-type(6) { animation-delay: 0.6s; }
.item:nth-of-type(7) { animation-delay: 0.7s; }

.item:nth-of-type(8) { animation-delay: 0.8s; }

.item:nth-of-type(9) { animation-delay: 0.9s; }


.item:nth-of-type(10) { animation-delay: 1s; }

Le quatrième paramètre de l’animation (backwards) correspond à animation-fill-mode. Dans


notre cas, il fait en sorte que les éléments soient stylés selon la première étape clé de notre
animation (from) durant leur délai.
CLARIFICATION Pourquoi mettre les styles de base dans l’animation ?
On est en droit de se demander pourquoi mettre les styles initiaux tels que opacity: 0
dans l’animation (@keyframes) et non pas dans le bloc .item {} avec les autres propriétés.
La raison est très simple : si un navigateur ne supporte pas les animations CSS, on ne
veut pas que ces styles soient appliqués puisqu’ils sont intimement liés à l’animation
elle-même justement.
Pour éviter cela, on intègre donc ces styles dans la première étape clé de l’animation
(from / 0%), et on utilise la valeur backwards pour animation-fill-mode. Ainsi, aucun souci
de compatibilité ni besoin de Modernizr !

Faciliter la génération des styles avec un préprocesseur


(Sass)
Vous avez sûrement remarqué combien le code est répétitif. C’est précisément dans ces
moments-là que l’on est content d’utiliser un préprocesseur CSS, afin d’utiliser une boucle
pour nous simplifier la vie.
/* Sass */

@for $i from 1 through 10 {


.item:nth-of-type(#{$i}) {

animation-delay: $i * 0.1s;

}
}

Cas pratique : animations comme feedbacks


Nous venons d’étudier un cas essentiellement basé sur l’esthétique. En effet, que les
éléments apparaissent de manière abrupte au chargement de la page, ou qu’ils soient
brièvement animés lors de leur mise en page, cela ne change pas le scénario et n’aide pas
particulièrement à se repérer. C’est principalement esthétique.
Mais comme nous l’avons vu en début de chapitre, les animations ne sont pas
exclusivement réservées à embellir les interfaces, mais bien à aider l’utilisateur. Aussi, je
vous propose de voir un cas de figure où l’animation est utilisée comme feedback, c’est-à-
dire « retour », pour indiquer à l’utilisateur que l’action qu’il vient d’effectuer a bel et bien
eu un effet.
Pour cela, nous allons prendre un cas de figure assez classique : une liste de tâches à
laquelle vous pouvez ajouter et retirer des éléments. Pour donner davantage de sens à
notre démonstration, nous allons considérer le cas où nous ajoutons un élément entre deux
tâches existantes.
Sans animation, celui-ci apparaît de manière instantanée, poussant par conséquent tous les
éléments en dessous de lui d’une place. C’est tout à fait fonctionnel, mais ça n’aide pas à
comprendre ce qu’il se passe.
Et si l’on faisait en sorte que l’élément grossisse rapidement entre la tâche précédente et la
tâche qui le suit pour pousser tous les éléments vers le bas en se faisant une place ?
@keyframes grow {
from {

max-height: 0;

opacity: 0;
transform: scale(0);

to {
max-height: 100px;

}
}

.new-item {
animation: grow .2s ease;
}

Quelques explications s’imposent. Tout d’abord, rappelons que l’élément est ajouté par le
biais de JavaScript, injecté dans le DOM à l’index désiré. Il est ajouté avec la classe new-
item, afin que l’on puisse appliquer des styles particuliers à cet élément justement.

Vous remarquerez que l’on n’indique pas les propriétés opacity et transform dans l’étape clé
finale (to) dans la mesure où l’on transite depuis les valeurs de l’étape clé initiale (from)
vers les valeurs par défaut (opacity: 1 et transform: scale(1)) ; par conséquent, elles sont
optionnelles.
L’animation de la propriété max-height est nécessaire afin de pousser progressivement les
éléments vers le bas pour dégager de la place. En effet, lorsque l’élément est ajouté au
DOM, il occupe déjà physiquement un emplacement même s’il est invisible à cause
d’opacity et scale(0) (qui je le rappelle n’est que visuel et ne modifie pas la taille physique
de l’élément).
Afin que les tâches suivantes soient poussées vers le bas de manière fluide, il faut que la
hauteur du nouvel élément soit animée également ; pour cela, rien de tel que max-height. De
plus, dans la mesure où la propriété animation-fill-mode n’a pas de valeur déclarée, la valeur
de max-height n’aura d’effet que durant l’animation, après quoi elle sera purement et
simplement supprimée de l’objet.

Figure 10–20
Les éléments se séparent pour faire place, puis le nouvel élément entre depuis le fond.

On peut pousser cette idée plus loin en imaginant une autre animation : d’abord, tous les
éléments venant après l’élément sur le point d’être ajouté sont décalés vers le bas pour
dégager un emplacement vide, puis l’élément vient occuper celui-ci en rentrant par la
gauche.
@keyframes make-space {

from {
max-height: 0;

}
to {

max-height: 50px;
}

}
@keyframes slide-in {
from {

opacity: 0;

transform: translateX(-300px);
}

.new-item {
animation:

open-space .2s,
slide-in .3s .2s backwards;

Cet effet étant un peu plus complexe, nous avons besoin de réaliser deux animations
différentes :
• une pour dégager un emplacement pour le nouvel élément en jouant avec max-height
comme dans notre exemple précédent ;
• une pour insérer notre élément dans le nouvel emplacement par la gauche.
Là encore, nous ne spécifions pas les valeurs finales dans notre animation slide-in
puisqu’il s’agit des valeurs par défaut (pas d’opacité et pas de transformation). En outre,
seules les valeurs pour la première étape clé sont nécessaires.
L’utilisation de nos animations est un peu plus subtile également puisqu’il faut jouer avec
animation-delay et animation-fill-mode. En effet, l’animation slide-in ne doit débuter que
lorsque la première animation est achevée ; pour cela, on applique un délai égal à la durée
de l’animation make-space.
Concernant animation-fill-mode, son utilisation est toute indiquée. Pour que l’élément soit
invisible et hors champ durant make-space, il faut que les valeurs spécifiées dans la première
étape clé de slide-in soient appliquées durant le délai de cette animation, d’où la valeur
backwards.

Figure 10–21
Les éléments se séparent pour faire place, puis le nouvel élément entre depuis la gauche avec une opacité réduite.

PLUS D’INFOS
Les exemples que nous venons d’étudier proviennent d’un article bien connu intitulé
« Transitional Interfaces » par Pasquale D’Silva. Je ne peux que vous recommander
cet article de qualité décrivant bien comment les animations peuvent aider à la
compréhension d’une interface et faciliter les actions de l’utilisateur.
http://bit.ly/transitional-interfaces
Le code, quant à lui, est de Chris Coyier qui a mis en pratique Transitional Interfaces
en CSS. J’ai pris des libertés sur son code dans le but de faciliter les explications (et
de l’améliorer un peu), mais vous devriez probablement lire son article.
http://bit.ly/transitional-interfaces-css

Cas pratique : mise en place d’un système anti-spoiler


Vous connaissez sûrement le terme « spoiler » : il s’agit d’un contenu dont la lecture
pourrait ruiner la curiosité d’autrui (comme la fin d’un film, par exemple). En anglais, to
spoil signifie « gâcher ».
Que diriez-vous de mettre en place un système servant à masquer un certain contenu tant
que l’utilisateur ne l’a pas explicitement demandé. C’est quelque chose de très facilement
réalisable en JavaScript, mais quand on veut s’en tenir au CSS, sans même ajouter de
HTML dédié, ça devient plus compliqué !
C’est pourtant un défi qu’a relevé le développeur Will Boyd, en créant un système de flou
du contenu jusqu’au survol de l’élément. Un compte à rebours visuel de trois secondes
apparaît alors et le contenu s’affiche une fois qu’il prend fin.

Figure 10–22
L’état normal du système, flouté avec la note « SPOILER ALERT »
Figure 10–23
Au survol, le compte à rebours est déclenché.

Figure 10–24
Après trois secondes, le flou disparaît laissant le contenu lisible.

RESSOURCE La démo originale de Will Boyd


J’ai pris la liberté de revisiter le code de Will Boyd afin de faciliter les explications,
aussi je vous invite à jeter un œil à sa démo initiale, ainsi qu’à lire l’article qu’il a écrit
à ce sujet.
http://bit.ly/css-spoiler
Je vous propose d’étudier cette démonstration car elle utilise un mélange d’animations et
de transitions, ainsi que la fonction steps() qui n’est pas toujours évidente à cerner.
Comme à notre habitude, nous démarrons avec notre HTML qui est tout à fait sémantique.
Tout ce dont nous avons besoin, c’est une classe sur notre contenu à flouter. Soit
directement sur un paragraphe, soit sur un conteneur plus large, peu importe.
<p class="spoiler">

<!-- Contenu spoiler à masquer -->


</p>

Plutôt élégant, n’est-ce pas ? Passons aux choses sérieuses.


/**

* 1. Contexte de positionnement pour les pseudo-éléments


* 2. Effacement du texte en le rendant transparent

* 3. Floutage du texte avec une ombre à fort blur


* 4. Application d’un curseur plus adapté

* 5. Transition du floutage
*/
.spoiler {

position: relative; /* 1 */

color: transparent; /* 2 */
text-shadow: 0 0 1em black; /* 3 */

cursor: help; /* 4 */

transition: all 1s; /* 5 */


}

/**
* 1. Retour à la couleur initiale (ou celle de votre choix)

* 2. Retrait du flou
* 3. Délai uniquement lors du retrait du flou et non
* lors de la réapplication lorsque le curseur sort du survol

*/
.spoiler:hover {
color: initial; /* 1 */

text-shadow: 0 0 0 transparent; /* 2 */
transition-delay: 3s; /* 3 */
}

CLARIFICATION Pourquoi pas filter: blur() ?


On est en droit de se demander pourquoi ne pas utiliser le filtre blur() pour flouter le
contenu plutôt que de rendre le texte transparent et d’ajouter une ombre floutée.
Le fait est qu’un filtre appliqué à un élément ne peut pas être annulé par un de ses
enfants : c’est strictement impossible.
En l’occurrence, nous utilisons des pseudo-éléments (techniquement des enfants,
donc) pour le compte à rebours et l’intitulé, nous empêchant ainsi d’utiliser le filtre de
flou.
Pour l’instant, nous avons fait en sorte que notre texte soit illisible en temps normal, mais
lisible au survol. C’est un bon début, mais il est temps d’utiliser les pseudo-éléments pour
améliorer les choses, d’autant que nous n’avons pas encore mis d’animation en place !
/**
* Pseudo-éléments

* before → message « SPOILER ALERT »

* after → compte à rebours


*/

.spoiler::before,

.spoiler::after {
position: absolute;

line-height: 1;
font-family: 'Fjalla One', sans-serif;

color: #872e27;
text-shadow: none;

}
/**
* Message « SPOILER ALERT »

* 1. Centrage horizontal

*/
.spoiler::before {

content: "SPOILER ALERT";

top: .5em;
left: 0; /* 1 */

right: 0; /* 1 */
text-align: center; /* 1 */

font-size: 3em;
}
/**

* Au survol de l’élément, on lance l’animation


* countdown-before qui dure 4 secondes
* et gardera son état final une fois achevée.

*/
.spoiler:hover::before {
animation: countdown-before 4s forwards;

}
/**
* Compte à rebours

* 1. Trois étapes dans la chaîne de contenus


* 2. Centrage horizontal
* 3. Masque pour afficher uniquement le « 3 »

* 4. Caché par défaut

*/
.spoiler::after {

content: "3 2 1"; /* 1 */

left: 50%; /* 2 */
margin-left: -.5em; /* 2 */

clip: rect(0, 1em, 1em, 0); /* 3 */

opacity: 0; /* 4 */
top: 1.25em;

width: 1em;

font-size: 3em;
line-height: 1em;

text-align: center;

}
/**

* Au survol de l’élément, on lance l’animation


* countdown-after qui dure 4 secondes

* et gardera son état final une fois achevée.


*/

.spoiler:hover::after {
animation: countdown-after 4s forwards;
}

Nous en avons fini avec la partie styles. Il ne nous reste plus qu’à créer les animations
countdown-before et countdown-after. La première, appliquée sur le pseudo-élément dont le
contenu est « SPOILER ALERT » va se contenter de remonter légèrement le texte, puis de
le faire disparaître.
@keyframes countdown-before {

/**

* Après une seconde (4 × 25 / 100),


* on réhausse légèrement le texte pour laisser place

* au compte à rebours.
*/
25% {

transform: translateY(-.33em);
}
/**

* À partir de 3 secondes (4 × 75 / 100),


* l’opacité diminue progressivement.
*/

75% {
opacity: 1;
}

/**
* À la fin de l’animation, le texte

* reste à sa place mais disparaît.


*/

100% {

opacity: 0;
transform: translateY(-.33em);

La seconde animation, dédiée au compte à rebours, est plus complexe. Elle doit animer la
propriété clip pour ne laisser apparaître que le bon chiffre à chaque étape (c’est là qu’entre
en jeu la fonction steps tant attendue). Comme pour l’animation précédente, elle doit
également s’occuper de faire disparaître le pseudo-élément pour qu’il ne gêne pas la
lecture du texte après coup.
@keyframes countdown-after {

/**

* À la première seconde (4 × 25 / 100)


* 1. Positionné sur le chiffre « 3 »
* 2. Intégralement opaque

* 3. Évite une transition fluide vers la prochaine étape clé


*/

25% {
clip: rect(0, 1em, 1em, 0); /* 1 */
transform: translateY(0); /* 1 */

opacity: 1; /* 2 */

animation-timing-function: steps(1, start); /* 3 */


}

/**

* À la 2e seconde (4 × 50 / 100)
* 1. Positionné sur le chiffre « 2 »

* 2. Évite une transition fluide vers la prochaine étape clé


*/

50% {
clip: rect(1em, 1em, 2em, 0); /* 1 */
transform: translateY(-1em); /* 1 */

animation-timing-function: steps(1, start); /* 2 */


}
/**

* À la 3e seconde (4 × 75 / 100)
* 1. Positionné sur le chiffre « 1 »
* 2. À partir de cet instant, l’opacité va progressivement

* diminuer jusqu’à ce que l’élément soit invisible.


*/
75% {

clip: rect(2em, 1em, 3em, 0); /* 1 */

transform: translateY(-2em); /* 1 */
opacity: 1; /* 2 */

/**
* À la fin de l’animation,

* le compte à rebours est intégralement transparent.

*/
100% {

opacity: 0;

clip: rect(2em, 1em, 3em, 0);


transform: translateY(-2em);

Vous vous demandez peut-être pourquoi on applique la propriété animation-timing-function au


cœur de l’animation, et pas dans l’appel de celle-ci directement sur le sélecteur
.spoiler::after.

Je me suis posé la même question, la raison en est très simple : le passage de l’état initial à
la première étape clé doit se faire de manière fluide, afin que le compte à rebours
apparaisse progressivement.
Ensuite, les étapes 2 et 3 (passant respectivement le compte à rebours à « 2 », puis « 1 »)
sont forcées en steps() de sorte que l’on ne voit pas l’animation de clip et translate(), qui
gâcherait l’effet.
Enfin, le passage à la dernière étape clé se fait de manière fluide également, afin que
l’opacité diminue progressivement jusqu’à ce que l’élément disparaisse intégralement.
C’est là une utilisation audacieuse de la propriété animation-timing-function, mise à jour
dynamiquement au cours de l’animation !
CLARIFICATION À propos de la propriété clip
Si vous connaissez peu ou pas la propriété clip, sachez qu’elle a été dépréciée au profit
de la propriété clip-path afin de maintenir une certaine cohérence avec le monde du
SVG.
Cependant, le support de cette dernière n’est pas au mieux, surtout comparé à celui de
clip qui remonte jusqu’à Internet Explorer 4 ! De fait, on se contente de clip en
attendant que le support de la propriété recommandée s’améliore.
Si vous désirez comprendre son fonctionnement, je vous recommande ce tutoriel écrit
par votre serviteur et publié sur le site Codrops.
http://bit.ly/codrops-clip

Compatibilité des navigateurs pour les animations


Tableau 10–6 Navigateurs desktop

Tableau 10–7 Navigateurs mobiles

La propriété animation-fill-mode n’est pas supportée sur Android 2.3 et toutes les versions
inférieures.
IOS 6.1 Animations des pseudo-éléments
Safari sur iOS 6.1 et ses versions inférieures ne permettent pas d’animer des pseudo-
éléments.
A
Liste des propriétés CSS et de leur valeur
par défaut

Vous trouverez ci-dessous la liste de toutes les propriétés CSS avec leur valeur par défaut.
Ces valeurs correspondent aux valeurs appliquées par le navigateur lorsque la valeur
renseignée est initial.
Tableau A-1 Liste des propriétés CSS officielles et de leur valeur par défaut

Propriété Valeur initiale


animation none
animation-delay 0
animation-direction normal
animation-duration 0
animation-fill-mode none
animation-iteration-count 1
animation-name none
animation-play-state running
animation-timing-function ease
backface-visibility visible
background 0
background-attachment scroll
background-clip border-box
background-color transparent
background-image none
background-origin padding-box
background-position 0 0
background-position-x 0
background-position-y 0
background-repeat repeat
background-size auto auto
border 0
border-style none
border-width medium
border-color inherit
border-bottom 0
border-bottom-color inherit
border-bottom-left-radius 0
border-bottom-right-radius 0
border-bottom-style none
border-bottom-width medium
border-collapse separate
border-image none
border-left 0
border-left-color inherit
border-left-style none
border-left-width medium
border-radius 0
border-right 0
border-right-color inherit
border-right-style none
border-right-width medium
border-spacing 0
border-top 0
border-top-color inherit
border-top-left-radius 0
border-top-right-radius 0
border-top-style none
border-top-width medium
bottom auto
box-shadow none
box-sizing content-box
caption-side top
clear none
clip auto
color inherit
columns auto
column-count auto
column-fill balance
column-gap normal
column-rule medium none currentcolor
column-rule-color currentcolor
column-rule-style none
column-rule-width none
column-span 1
column-width auto
content normal
counter-increment none
counter-reset none
cursor auto
direction ltr
display inline
empty-cells show
float none
font normal
font-family inherit
font-size medium
font-style normal
font-variant normal
font-weight normal
height auto
hyphens none
left auto
letter-spacing normal
line-height normal
list-style none
list-style-image none
list-style-position outside
list-style-type disc
margin 0
margin-bottom 0
margin-left 0
margin-right 0
margin-top 0
max-height none
max-width none
min-height 0
min-width 0
opacity 1
orphans 0
outline 0
outline-color invert
outline-style none
outline-width medium
overflow visible
overflow-x visible
overflow-y visible
padding 0
padding-bottom 0
padding-left 0
padding-right 0
padding-top 0
page-break-after auto
page-break-before auto
page-break-inside auto
perspective none
perspective-origin 50% 50%
position static
quotes '\201C '\201D' '\2018' '\2019'
right auto
tab-size 8
table-layout auto
text-align inherit
text-align-last auto
text-decoration none
text-decoration-color inherit
text-decoration-line none
text-decoration-style solic
text-indent 0
text-shadow none
text-transform none
top auto
transform none
transform-style flat
transition none
transition-delay 0s
transition-duration 0s
transition-property none
transition-timing-function ease
unicode-bidi normal
vertical-align baseline
visibility visible
white-space normal
widows 0
width auto
word-spacing normal
z-index auto
B
Liste des propriétés CSS qui peuvent être
animées

Voici la liste des propriétés CSS qui peuvent être animées, ainsi que le type de valeur
qu’elles attendent au cours d’une animation.
Vous trouverez ci-dessous la liste de toutes les propriétés qui peuvent être animées (ou
transitées, pardonnez-moi le terme). Notons toutefois qu’il s’agit là de la théorie, et que
certains navigateurs ne supportent peut-être pas les animations et transitions sur certaines
de ces propriétés.
Tableau B-1 Index des propriétés CSS qui peuvent être animées

Catégorie Propriété Type


Catch-all all
Toutes les propriétés
« animables »
color Couleur
font Voir les propriétés individuelles
font-size Longueur ou pourcentage
font-size-adjust Nombre
font-stretch Mot-clé
font-weight Nombre ou mot-clé
letter-spacing Longueur
line-height Nombre ou longueur
Propriétés de texte max-lines Entier
text-decoration-color Couleur
text-emphasis-color Couleur
text-indent Longueur, pourcentage ou
calc()
text-shadow Ombre
text-size-adjust Pourcentage
vertical-align
Longueur, pourcentage ou mot-
clé
word-spacing Longueur ou pourcentage
background Voir les propriétés individuelles
background-color Couleur
background-image Images ou dégradés
background-position
Liste de longueurs, pourcentage
ou calc()
background-size
Liste de longueurs, pourcentage
ou calc()
border Voir les propriétés individuelles
border-color Voir les propriétés individuelles
border-*-color Couleur
border-radius Voir les propriétés individuelles
border-*-*-radius Longueur (une ou deux valeurs)
border-spacing Liste de longueurs
border-* Voir les propriétés individuelles
border-width Voir les propriétés individuelles
Propriétés de boîte border-*-width Longueur
box-shadow Liste d’ombres
clip Rectangle
crop Rectangle
Height, min-height, max-height Longueur, pourcentage ou
calc()
margin Voir les propriétés individuelles
margin-* Longueur
opacity Nombre
outline-color Couleur
outline-offset Longueur
outline-width Longueur
padding Voir les propriétés individuelles
padding-* Longueur
Width, min-width, max-width Longueur, pourcentage ou
calc()

Top, right, bottom, left Longueur, pourcentage ou


calc()
Offset-before, offset-end, offset-
after, offset-start Longueur ou pourcentage
Propriétés de position
visibility Mot-clé
z-index Entier
zoom Nombre
columns Voir les propriétés individuelles
column-count Entier
column-gap Longueur
Propriétés du module de column-rule Voir les propriétés individuelles
colonnes
column-rule-color Couleur
column-rule-width Longueur
column-width Longueur
flex Voir les propriétés individuelles
flex-grow Nombre (sauf 0)
Propriétés du module Flexbox
flex-shrink Nombre (sauf 0)
flex-basis Longueur, pourcentage ou
calc()
order Entier
perspective Longueur
perspective-origin
Liste de longueurs, pourcentage
Propriétés du module de ou calc()
transformation transform Transformation
transform-origin
Liste de longueurs, pourcentage
ou calc()
fill « Paint server »
fill-opacity Flottant
flood-color Couleur ou mot-clé
lighting-color Couleur ou mot-clé
marker-offset Longueur
stop-color Couleur
stop-opacity Flottant
Propriétés SVG stroke « Paint server »
stroke-dasharray Liste de nombres
stroke-dashoffset Nombre
stroke-miterlimit Nombre
stroke-opacity Flottant
stroke-width Flottant
viewport-fill Couleur
viewport-fill-opacity Couleur
Notes
Les propriétés raccourcies (par exemple, background) peuvent être utilisées pour spécifier les
valeurs pour leurs propriétés individuelles qui peuvent être animées.
Bien que le module dédié aux arrière-plans spécifie que ceux-ci ne peuvent pas être
animés, il s’avère que Chrome supporte la transition progressive (dite « fade ») entre deux
images. La solution cross-browser consiste à jouer avec la propriété opacity, ou bien des
sprites d’images.
Il n’est pas possible d’animer la propriété background-size si elle est renseignée avec des
mots-clés (par exemple, cover).
La propriété visibility peut être animée, bien que les spécifications mentionnent
explicitement qu’elle ne devrait pas l’être. Il est donc recommandé de ne pas l’animer, afin
d’éviter toute mauvaise surprise dans le futur.
À l’heure actuelle, ne sont supposées pouvoir être animées que les propriétés -top-, -right-,
-bottom- et -left- pour les propriétés border-color, border-radius, border-width, padding et margin.
Cependant la majorité des navigateurs (à l’exception d’Internet Explorer) supportent
l’animation des propriétés raccourcies.
PLUS D’INFOS
Pour plus d’informations sur les propriétés qui peuvent être animées ou non, je vous
invite à lire cette étude réalisée par Oli Studholme.
http://oli.jp/2010/css-animatable-properties/
Ce que l’on aimerait pouvoir animer
Parmi les choses que l’on souhaiterait pouvoir animer mais que l’on ne peut pas, on
trouve :
• background-image (même si Chrome le permet) ;
• float ;
• height et width depuis/vers auto ;
• display depuis/vers none ;
• position.
C
Ressources et liens

Voici quelques références, livres et sites communautaires dignes d’intérêt.


Sites francophones
• Le forum d’Alsacréations (http://forum.alsacreations.com). Communauté discutant des
standards : CSS, HTML, JavaScript et des nouveaux langages…
• Openweb (http://openweb.eu.org). Référence en français dans le domaine des standards.
Openweb est un collectif d’experts proposant de nombreuses ressources et divers
tutoriels portant sur HTML, CSS, JavaScript…
• Opquast (http://www.opquast.com). Projet ambitieux de référentiel qualité pour les sites
web, prenant en compte l’ergonomie, les fonctionnalités et l’accessibilité.
• Mozilla Developer Network (https://developer.mozilla.org/fr/). Projet de documentation
communautaire sur les technologies et langages côté client, lancé par la fondation
Mozilla et évoluant grâce aux contributions des utilisateurs.
• Pompage (http://www.pompage.net/). Projet de traduction des articles internationaux
dans la langue de Molière.
Sites anglophones
• CSS-Tricks (http://css-tricks.com). Blog personnel de Chris Coyier, l’homme à qui l’on
doit la préface de l’ouvrage que vous tenez dans les mains. Mais ne vous méprenez pas,
CSS-Tricks est beaucoup plus qu’un blog avec désormais ses propres forums, un index
des propriétés/sélecteurs CSS, des screencasts et bien d’autres ressources.
• SitePoint (http://sitepoint.com). Importante ligne éditoriale délivrant sans cesse du
contenu pour les professionnels du Web.
• Codrops (http://tympanus.com/codrops). Ligne éditoriale dédiée au design web,
essentiellement basée sur les technologies côté client (HTML, CSS, JavaScript,
SVG…).
• Webdesign Tuts+ (http://webdesign.tutsplus.com). Importante ligne éditoriale délivrant
très régulièrement des articles sur les technologies web, côté client comme serveur.
• Web Platform (http://www.webplatform.org). Projet initié par le W3C et ses acteurs dans
le but de documenter toutes les technologies côté client (HTML, CSS, JavaScript, SVG,
WebGL, DOM, accessibilité…) de manière moins formelle que les spécifications.
• CSS Wizardry (http://csswizardry.com). Blog personnel de Harry Roberts, consultant
CSS à l’international.
• QuirksMode (http://www.quirksmode.org). Blog personnel de Peter-Paul Koch.
• HTML5 Doctor (http://html5doctor.com). Collaboration de plusieurs développeurs ayant
pour objectif de documenter de manière moins formelle que les spécifications les
fonctionnalités HTML 5, notamment les « nouveaux » éléments (article, main, figure…).
• Smashing Magazine (http://www.smashingmagazine.com). Magazine web majeur
indépendant, réputé pour sa qualité et son savoir-faire.
• A List Apart (http://alistapart.com). Ligne éditoriale lancée par Jeffrey Zeldman,
désormais alimentée par des acteurs divers et variés, délaissant généralement le côté très
technique pour des réflexions plus générales sur nos professions.
• Treehouse (http://teamtreehouse.com). Plate-forme anglophone d’apprentissage des
métiers du Web, conférant articles et vidéos de qualité, ainsi qu’un podcast
hebdomadaire.
• Sidebar (http://sidebar.io). Cinq liens en rapport avec le web design par jour, par Sacha
Greif.
• This is Responsive (http://bradfrost.github.io/this-is-responsive/). Collection de patterns,
ressources et news pour créer des modules, sites et applications responsives, initiée par
Brad Frost.
• Show Talk Show (http://shoptalkshow.com). Podcast animé par Chris Coyier et Dave
Rupert, invitant un acteur de la scène web internationale par semaine pour discuter de
son expertise dans le domaine.
Newsletters
• Web Design Weekly (http://web-design-weekly.com)
• CSS Weekly (http://css-weekly.com)
• HTML5 Weekly (http://html5weekly.com)
• Responsive Design Newsletter (http://responsivedesignweekly.com)
• Open Web Platform Daily Digest (http://webplatformdaily.org)
• SassNews (http://sassnews.com)
Comptes Twitter
• Rachel Andrew (@rachelandrew)
• Tab Atkins (@tabatkins)
• Mathias Bynens (@mathias)
• Chris Coyier (@chriscoyier)
• CSS-Tricks (@real_css_tricks)
• Kaelig Deloumeau-Pringent (@kaelig)
• Brad Frost (@brad_frost)
• Nicolas Gallagher (@necolas)
• Raphael Goetter (@goetter)
• Sacha Greif (@sachagreif)
• Christian Heilmann (@codepo8)
• Paul Irish (@paul_irish)
• Laura Kalbag (@laurakalbag)
• Peter-Paul Koch (@ppk)
• Bruce Lawson (@brucel)
• Louis Lazaris (@impressivewebs)
• Paul Lewis (@aerotwist)
• Ethan Marcotte (@beep)
• Eric A. Meyer (@meyerweb)
• Rachel Nabors (@rachelnabors)
• Mark Otto (@mdo)
• Rémi Parmentier (@hteumeuleu)
• Harry Roberts (@csswizardry)
• Remy Sharp (@rem)
• Simurai (@simurai)
• SitePoint (@sitepointdotcom)
• Smashing Magazine (@smashingmag)
• Jonathan Snook (@snookca)
• Sara Soueidan (@sarasoueidan)
• David Storey (@dstorey)
• Nicole Sullivan (@stubbornella)
• Ana Tudor (@thebabydino)
• Lea Verou (@leaverou)
• David Walsh (@davidwalshblog)
• Stéphanie Walter (@walterstephanie)
• Webdesign Tuts+ (@wdtuts)
• Fabrice Weinberg (@fweinb)
• Estelle Weyl (@standardista)
• Luke Wroblewski (@lukew)
• Jeffrey Zeldman (@zeldman)
Bibliographie
• CSS Avancées par Raphaël Goetter (éditions Eyrolles)
• CSS Maintenables par Kaelig Deloumeau-Prigent (éditions Eyrolles)
• Sass et Compass Avancé par Mehdi Kabab (éditions Eyrolles)
• Intégration Web – Les bonnes pratiques par Corinne Schillinger (éditions Eyrolles)
• Mobile First par Luke Wroblewski (collection A Book Apart)
• Responsive Web Design par Ethan Marcotte (collection A Book Apart)
• CSS 3 for Web Designers par Dan Cederholm (collection A Book Apart)
• Sass for Web Designers par Dan Cederholm (collection A Book Apart)
• SMACSS par Jonathan Snook (e-book)
• CSS 3 Pushing the Limits par Stephen Greig (éditions Wiley)
• CSS Secrets par Lea Verou (éditions O’Reilly)
• Responsive Web Design with HTML5 and CSS 3 par Ben Frain (éditions PACKT)
• Sass and Compass for Designers par Ben Frain (éditions PACKT)
• The Elements of Typographic Style par Robert Bringhurst (éditions Hartley and Marks)
• Typography: A Manual of Design par Emil Ruder (éditions Niggli)
• HTML & CSS : design et création web par Jon Duckett (éditions Pearson)
• Jump Start Responsive Web Design par Craig Sharkie et Andrew Fisher (éditions
SitePoint)
• Adaptative Web Design par Aaron Gustafson (éditions Easyreaders pour la version
anglophone, Pearson pour la version francophone)
Index

!important 246
:any 39
:blank 34
:checked 45
:default 47
:disabled 40
:empty 33
:enabled 40
:first-child 23
:first-of-type 31
:focus 39, 301, 303
:hover 273, 301, 303, 310
:indeterminate 46
:in-range 49
:invalid 44
:lang 35
:last-child 23
:last-of-type 31
:matches 37
:not 36, 250
:nth-child 24
:nth-last-child 24
:nth-of-type 31
:only-child 30
:only-of-type 31
:optional 45
:out-of-range 49
:read-only 42
:read-write 42
:required 45
:root 36, 162, 168
:target 32
:valid 44
@custom-media 260
@font-face 229
@hyphenation-resource 223
@keyframes 242, 247, 311, 316, 324
@page 63, 250
@supports 244

A
accélération matérielle 271, 292, 318
accessibilité 40, 44, 49
align-content 76
align-items 75, 79
align-self 79, 104
all 241
all: inherit 241
all: initial 241
all: unset 241
Android 58, 144, 149, 274, 316, 334
animation 242, 289, 310, 341
animation-delay 315, 327
animation-direction 313
animation-duration 312
animation-fill-mode 315, 324, 326
animation-iteration-count 313
animation-name 311, 316
animation-play-state 314
animation-timing-function 311, 333
anti-aliasing 174
Autoprefixer 86, 208

B
backface-visibility 285
backface-visibility: hidden 287
background 344
background-attachment 165
background-clip 167
background-image 151, 157, 274, 344
background-origin 160, 164
background-position 158, 163, 187, 189
background-repeat 159
round 159
space 159
background-size 161, 344
background-size: contain 202
contain 161
cover 161
BEM 22
Bézier (courbe de) 296, 311
Blink 250
blur() 171, 330
boîte (modèle de) 54, 146
Bootstrap 57, 248
border 54
border-color 344
border-radius 137, 344
border-bottom-left-radius 137
border-bottom-right-radius 137
border-top-left-radius 137
border-top-right-radius 137
border-width 344
border-bottom 54
border-box 56, 125, 164, 167
border-image 181
border-image-outset 183
border-image-repeat 183
border-image-slice 182
border-image-source 182
border-image-width 183
border-left 54
border-radius 124
border-right 54
border-top 54
box model 54
box-shadow 144, 151, 173, 308
box-sizing 55
box-sizing: border-box 56
break-after 63
break-before 63
break-inside 68
break-inside 63
brightness() 172

C
calc() 93, 186
centrage 271
ch (unité) 196
Chrome 5, 7, 43, 48, 51, 112, 121, 174, 193, 202, 204, 232, 250, 293, 344
circle() 122
clip 333
clip-path 333
column-count 60
column-fill 65
column-gap 61
column-rule 62
column-rule-color 62
column-rule-style 62
column-rule-width 62
columns 61
column-span 64
column-width 59
content-box 56, 125, 164, 167
contenteditable 42
contrast() 172
courbe de Bézier 296, 311
CPU 292
CSSWG 12, 214

D
display
flex 72, 247
grid 92
DOM 2, 16, 28, 36, 50, 70, 83, 108, 238, 263, 294, 325
DOM parser 19
DOMContentLoaded 301
drop-shadow() 173

E
ease 311
ease-in 311
ease-in-out 311
ease-out 311, 319
ellipse() 123
em 189

F
fallback 155, 194, 230, 240, 243
Feature Queries 244
fill 206
filter 136, 171
Firefox 7, 43, 48, 51, 57, 70, 85, 179, 208, 216, 221, 225, 242, 277, 293, 309
fit-content 207
flag 2, 10, 112, 121, 242, 244
Flash 264, 289
flex 78
flex-basis 78
Flexbox 3, 11, 70, 88, 247
flex-direction 72
flex-flow 74
flex-grow 77, 78
flex-shrink 77, 78
flex-wrap 73
float 72, 87, 92, 104
float 344
flow-from 115
flow-into 114
focus (:focus) 301, 303
font-size 189, 198, 200
font-size
62.5% 194
Foundation 57
fr 90, 93, 95
fragment identifier 32

G
Gecko 51, 309
Google 6
GPU 292, 294
grayscale() 174
grid
subgrid 103
grid-area 101
grid-auto-flow 103
grid-column 101
grid-column-end 97, 101
grid-column-start 97, 101
grid-row 101
grid-row-end 97, 101
grid-row-start 97, 101
grid-template 93
grid-template-areas 96, 110
grid-template-columns 93
grid-template-rows 93

H
hack 54, 70, 187
hanging-punctuation 226
hash 33
hasLayout 273
height 54, 78, 206, 252, 344
high-contrast 257
hover (
hover) 303
hover (:hover) 273, 301, 303, 310
hsl 131
hsla 131
hue-rotate() 175
hyphens 221
auto 223
manual 222
none 222, 236

I
identifier (fragment) 32
important (!important) 246
inline-block 70, 87, 102, 104, 267
inset() 123
Internet Explorer 5, 7, 17, 20, 21, 24, 28, 33, 50, 56, 58, 70, 85, 121, 136, 144, 156, 162, 179, 181, 184, 189, 193, 197,
204, 225, 257, 262, 269, 277, 285, 300
invert() 175
Isotope 66

J
justify-content 74

K
KNACSS 57

L
linear 311, 319
linear-gradient() 152, 160

M
margin 54, 71, 188, 271, 344
margin-bottom 236
margin-box 125
Masonry 66
matchMedia 257
matérielle (accélération) 271, 292, 318
matrix() 276
max-content 93, 206
Media Queries 68, 106, 201, 238, 248, 251
min-content 93, 204
mixin 194
mobile 6
Mobile First 81, 104, 261
modèle de boîte (box model) 54, 146
Modernizr 10, 86, 244, 246

N
not (:not) 250

O
opacity 133, 314, 317, 318, 323, 325, 344
opacity() 176
Opera 8, 43, 48, 167, 216, 250, 262, 300
Opera Mini 44, 45, 70, 151
Opéra 300
opérateur d’adjacence 16
directe 16
générale 17
order 76, 83
overflow 116
overflow-wrap 212, 218

P
padding 54, 142, 206, 236, 344
padding-bottom 54
padding-box 56, 125, 164, 167
padding-left 54
padding-right 54
padding-top 54
page (@page) 63
parser CSS 28, 61, 187, 243, 244, 247
performance 19, 146, 180, 271, 317
perspective 282
perspective() 282
perspective-origin 284
PNG 134, 169
pointer-events 179
none 180, 307
Polyfill 28
polyfill 113, 203
polygon() 124
position 344
fixed 277
sticky 111, 248
préfixe constructeur 2, 9, 86, 143, 149, 156, 208, 247
préprocesseur 6, 29, 86, 107, 136, 177, 186, 193, 195, 208, 237, 269, 324
pseudo-classe 22
pseudo-élément 49, 115, 120, 134, 267, 278, 309, 330

Q
Quirks Mode 56
quotes 35

R
radial-gradient() 154
rebeccapurple 130
region-fragment 115
rem 189, 200
RenderLayer 271
repeat() 93, 94, 107
requestAnimationFrame 294
responsive 251
Responsive Web Design 6, 55, 79, 198, 200
Retina 129
rgba 130, 243
root (:root) 162, 168
rotate() 265
rtl 73
rythme vertical 192

S
Safari 7, 17, 43, 48, 58, 85, 112, 144, 149, 162, 164, 188, 197, 202, 204, 216, 334
Sass 6, 29, 30, 86, 96, 107, 136, 186, 193, 195, 208, 269, 324
saturate() 176
scale() 273
scaleX() 273
scaleY() 273
sélecteur d’attribut 18
modulé 19
simple 18
sepia() 177
shape-image-threshold 125
shape-inside 124
shape-margin 125
shape-outside 125
shape-padding 125
skewX() 274
skewY() 274
spécifications 11, 18, 186
spécificité 37
sprite 46, 344
step-end() 311
steps() 311, 329
step-start() 311
sticky (position) 248
subgrid 103
SVG 6, 36, 170, 174, 179, 277

T
tab-size 225
text-align-last 228
justify 229
text-overflow 217
ellipsis 218
text-shadow 149
transform 3, 174, 318, 325
translate3d(0, 0, 0) 292
translateZ(0) 292
transformation 248, 263
transform-origin 277, 284, 288
transform-style 284
flat 285
preserve-3d 284, 286
transition 242, 291, 295, 341
cubic-bezier() 296
transition-delay 300
transition-duration 299
transition-property 296
transition-timing-function 296, 299
translate() 270

U
Unicode 230
unicode-range 229
URL 32, 293
url() 125, 151, 171, 179

V
var() 239
Version 39
vertical (rythme) 192
vh 197
viewport 82, 111, 165, 197
visibility 344
vmax 203
vmin 203
vw 197

W
W3C 7, 8, 50, 71, 292
WebGL 6, 264
WebKit 70, 143, 168, 188, 203, 208, 254, 298, 300, 309
WHATWG 7, 9
white-space 213
normal 213
nowrap 213, 217, 218
white-space
pre 213, 235
pre-line 214
pre-wrap 214
width 54, 78, 206, 252, 344
will-change 292
word-break 224
word-wrap 213

X
XML 36

Z
zoom 273, 308
Ce livre est imprimé sur du papier couché mi-mat 115g, issu de forêts gérées durablement.
Dépôt légal : février 2015
N° d’éditeur : 9458
IMPRIMÉ EN SLOVÉNIE PAR GPS GROUP
Pour suivre toutes les nouveautés numériques du Groupe Eyrolles, retrouvez-nous sur Twitter et Facebook

@ebookEyrolles

EbooksEyrolles
Et retrouvez toutes les nouveautés papier sur

@Eyrolles

Eyrolles