Il y a moins d'un mois, j'écrivais un article sur les post-processeurs CSS et je m'interrogeais, publiquement, sur l'intérêt de tels outils, comparés aux pré-processeurs. Je trouve l'idée très prometteuse. Le concept de conserver son CSS «propre», «future-proof» pour pouvoir y appliquer toutes sortes de traitements automatiques offrant un support maximal m'enchante.
Sauf que, tout n'est pas réalisable. Je suis allé un peu trop loin dans mon cheminement, et j'ai franchi la limite à ne jamais dépasser: la cascade CSS et le DOM.
Alors oui, cela dépend des actions effectuées, le fameux «ça dépend». Par exemple, l'utilisation d'Autoprefixer pour l'ajout des préfixes CSS en post-traitement est à préconiser sur l'utilisation des pré-processeurs (attention, je ne parle pas d'outil comme prefixmycss qui ajoute les préfixes au sein du navigateur). L'utilisation d'un polyfill pour les filtres pourrait l'être également.
Par contre, lorsque je parle de polyfill pour les variables CSS ou calc(), il faut faire attention.
Myth.io
Hier soir a été annoncé Myth. C'est une sorte de super-polyfill-post-processeur pour votre CSS, exactement ce dont je parlais. Et avec, les limites apparaissent. Reprenons les différents exemples de code proposés sur le site.
Les variables
/* CSS en entrée */ :root { var-purple: #847AD1; } a { color: var(purple); } /* CSS en sortie */ :root { var-purple: #847AD1; } a { color: #847AD1; }
Les variables CSS sont dans l'actualité cette semaine, depuis l'annonce de la prise en charge par Firefox dans la version 29. Mais, il faut faire attention, car les «variables CSS» n'en sont pas réellement. Ce sont en fait des propriétés personnalisées. Et ces propriétés fonctionnent en suivant le principe même de CSS, la cascade! Les propriétés personnalisées sont uniquement connues dans le scope où elles sont définies, ce qui n'est pas le cas avec cet outil.
Les variables, telles que présentées par Myth ne sont en fait «que» de simples variables, et non réellement des propriétés personnalisées, comme le prévoit CSS. Il y a donc tromperie sur la marchandise.
Certes, cela peut s'avérer utile si le développeur reste rigoureux (ou si le plugin l'est pour vous ) et ne crée que des variables globales, mais le détournement d'une syntaxe normalisée pour en faire autre chose peut s’avérer dangereux. De plus, le code n'est plus «future-proof», comme espéré au départ.
Mathématiques et couleurs
Là encore, les exemples présentés sont trompeurs. Regardez mieux:
/* CSS en entrée */ pre { margin: calc(var(large) * 2); } /* CSS en sortie */ pre { margin: 20px; }
Cet exemple fonctionne ici avec l'utilisation d'une variable, mais qu'en est-il pour une autre utilisation de calc(), plus classique? Par exemple combinant unités relatives et absolues comme calc( 50% - 25px ) (ce qui équivaut à 50% de la largeur de son containing block moins 25px) Bien entendu, ni un pré-processeur, ni un post-processeur ne pourront résoudre ces problèmes contextuels inhérents au document sans en connaitre sa structure.
Dans ce cas, votre CSS est bien préparé pour l'avenir, mais aucun outil ne pourra vous venir en aide pour offrir un meilleur support navigateur.
Il en est de même pour l'exemple des fonctions de couleurs. Dans ce cas, c'est même encore plus problématique puisque la spécification n'en est qu'à ses balbutiements, et donc non stable. Il serait dangereux d'utiliser du code non normalisé, ou non en passe de l'être. De plus, la logique précédente s'applique, il n'est pas possible de profiter de la cascade CSS.
/* CSS en entrée */ a { color: color(var(purple) tint(20%)); } /* CSS en sortie */ a { color: rgb(157, 149, 218); }
Conclusion
Même si le principe est intéressant sur la forme, l'objectif principal ne peut (et ne pourra) jamais être atteint. Après l'annonce d'hier, j'ai immédiatement montré ma satisfaction, mais sans vraiment prendre la mesure de ce que Myth nous propose. En réalité, et après étude, il s'avère que ce post-processeur propose moins de choses que ce qu'a tenté récemment Nicolas Gallagher avec rework-suit, son post-processeur (ou pré-processeur transparent). Rework-suit propose le support des variables (identiques à Myth, puisque basé sur le même plugin), les préfixes automatiques avec Autoprefixer, ainsi que le support de l'opacité pour IE8.
C’est la tendance qui se dégage depuis quelques mois. Notamment depuis la démocratisation des outils d’automatisation du workflow front-end, comme Grunt.
Les postprocesseurs CSS, dont le nom évoque directement les préprocesseurs, sont en fait toutes les étapes qui devraient être effectuées en post-traitement, sans que le dev ne se soucis de cela. L’intérêt étant d’écrire du code CSS propre, sans préfixes, maintenable, voire à la pointe, afin d’obtenir en retour un code compilé pour un maximum de compatibilité.
Actuellement, on peut évoquer Rework et PostCSS qui vont plus ou moins dans cette direction.
Automatiser les préfixes
La première tâche à postprocesser, c’est l’automatisation des préfixes CSS. En effet, avant l’arrivée des préprocesseurs, et avant que les navigateurs ne soient aussi à jour, il fallait répéter les préfixes, souvent plusieurs fois. Par exemple, il y a quelques années, une transition CSS s’écrivait plus ou moins de cette façon, et c’était pénible:
qui génère le CSS adéquat. Mais, même si l’on pourrait penser que c’est mieux, ça ne l’est pas (en tout cas pour les préfixes). Certes l’outil travaille pour nous, mais le code manipulé s’éloigne du CSS, et certaines fois énormément. Mais surtout, le CSS est dépendant du préprocesseur choisi et difficilement évolutif.
Ce qui est mieux, c’est justement ce que nous offrent les outils de post-traitement. Aujourd’hui, on écrit du «plain CSS» et c’est tout:
.element { transition: background 1s ease; }
Notre CSS sera alors généré de la «bonne» manière qu’il soit, en fonction de plusieurs critères définis en amont. L’outil qui fait actuellement cela très bien s’appelle AutoPrefixer, et si vous n’en avez jamais entendu parler, courez vite sur leur GitHub et prenez-le en main. Il est basé sur Rework mais devrait bientôt se baser sur PostCSS. Le code généré se base sur différents paramètres:
sur le pourcentage d’utilisateurs par navigateur basé sur les données caniuse.com
sur le nombre de versions supportées d’un navigateur par rapport à la version actuelle
sur un navigateur particulier etc.
Il permet également de nettoyer le CSS en enlevant les préfixes antidatés. Autoprefixer est disponible sous un nombre incroyable de forme, que ce soit comme plugin à votre éditeur de texte (type Sublime Text), comme option d’outils d’aide à la gestion des assets comme Prepros ou Mixture, ou tout simplement comme plugin à Grunt. Personnellement, j’utilise Brunch que je trouve plus simple que Grunt, et autoprefixer-brunch avec support des source maps.
Incorporer les polyfills
La deuxième fonctionnalité envisagée, c’est l’automatisation de Polyfill. Prenons l’exemple des filtres CSS. La spécification définit le filtre flou de cette manière:
.element { filter: blur(10px); }
Sauf qu’actuellement seuls Chrome et Safari supportent cette fonctionnalité, et de manière préfixée. Pourtant Firefox permet également d’appliquer un flou sur un élément, mais en référençant un filtre SVG, de cette manière:
.element { filter: url(‘#filtre’); }
Et IE 6-9 également, via les vieux filtres DirectX:
Pour générer les filtres CSS de chaque navigateur, un polyfill existe. L’énorme avantage d’un postprocesseur serait ici d’automatiser cette tâche afin de ne conserver dans le CSS de travail que le code sans préfixes, et toujours future-proof.
Une autre idée pourrait être d’utiliser rework-vars, qui ajoute le support des variables CSS telles que définies dans la spécification officielle. Ainsi, notre CSS pourrait ressembler à cela:
Idem pour calc(), nous n'aurions plus besoin de préprocesseurs pour çà.
Quels autres traitements?
Bien entendu, autre la minification, concaténation de fichiers, linters, tests, il existe d’autres petites actions que l’on pourrait effectuer en post-traitement. Par exemple, le postprocesseur PostCSS propose de vérifier que la propriété content est bien présente dès que le sélecteur contient ::after ou ::before. Au cas où il serait manquant, de le rajouter. On pourrait aussi envisager une tâche qui vérifie que le sélecteur :focus est systématiquement présent lorsque que :hover est utilisé, ou de le rajouter également, etc.
Mouais, pas sur...
Certes, tout cela est encore très récent et le partage des tâches entre préprocesseurs et postprocesseurs peu sembler obscur. Voici une image qui tente de répartir les différentes actions:
Nouveau worflow de développement CSS?
Pour moi, c’est vraiment la transparence des actions effectuées en post-traitement qui est importante, pour conserver son code le plus propre possible. J’espère avoir réussi à vous exposer mon point de vue mais je suis curieux de connaitre vos avis à ce sujet. Alors, vous en pensez quoi?
Le 29 juin dernier s'est déroulé la Kiwi Party: un évènement web entièrement gratuit, organisé par Alsacréations. Les conférences ont été variées et toutes plus intéressantes les unes que les autres. De mon coté, j'ai pu y présenter «Ce que vous avez toujours voulu savoir sur CSS», une sorte de retour aux bases au langage CSS.
Dans cette présentation, j'ai notamment évoqué:
La cascade CSS
La spécificité des sélecteurs
Le box model CSS2.1 et CSS3
Les valeurs de display (modèle tabulaire)
Les interactions entre display, float et position
Le block formatting context
La fusion des marges
Les contextes d'empilement et z-index
Les slides utilisées pour la conf sont en ligne:
[Slides] Ce que vous avez toujours voulu savoir sur CSS
Je tenais à féliciter et à remercier encore une fois Alsacréations pour l'organisation de cet événement. Vous êtes au top.
Merci aussi pour tous les retours positifs que j'ai pu avoir sur ma présentation et tous ceux avec qui j'ai échangé, discuté et avec qui j'ai pu me désaltéré pendant l'::after! :)
Voici deux compte-rendus, en attendant l'officiel:
LiveCoding est un petit plugin qui permet d'éditer du CSS, du HTML ou du SVG en live directement dans une présentation Reveal.js. Très pratique si vous devez faire des démos de code.
Le 23 avril dernier, j’ai eu l’occasion d’animer une présentation pompeusement nommée: «Le futur des effets graphiques sur le web» lors du 9ème Meetup HTML5.
Dans cette mini-présentation, j’ai évoquer des fonctionnalités de CSS très, voire trop, avancées. Tous les aspects de cette conférence sont donc à prendre à titre d’expérimentations. J’ai pu y parler de choses diverses:
les fonctions d’images: cross-fade(), element()
les masques CSS, SVG et les méthodes de rognage
les exclusions et les formes CSS
les filtres CSS avancés, avec notamment une démo d'utilisation des shaders
Ma présentation est disponible en ligne, directement dans votre navigateur.
Version HTML de «Le futur des effets graphiques sur le web»
Attention, prévoyez différents navigateurs et basculez de l'un à l'autre pour bénéficier de chaque effet:
Google Chrome Canary (28) (avec flags actifs: «Fonctionnalités expérimentales WebKit», «Shaders» et éventuellement «WebGL»
Firefox Nightly (23)
IE10
Lors de ce même Meetup, deux autres présentations ont eu lieu:
Démonstration d'un jeu basé sur la Gamepad API avec une manette Guitar Hero.
L'ère des icon-fonts: création d'une police d'icônes libre de droit et utilisation via CSS3.
Un grand merci aux organisateurs pour ce chouette évènement, ainsi qu'à tout ceux avec qui j'ai pu discuter lors de cette soirée.
Et surtout, expérimentez!
Edit: La vidéo est en ligne!
Edit 2: Quelques tweets pour vous donner envie de regarder la vidéo:
La pres de @iamvdo sur #CSS3 est un concentré de fonctionnalités aussi géniales les une que les autres #FranceHTML5 twitter.com/_jmpp/status/3…
— J.M. Cléry (@_jmpp)
23 avril 2013
Bravo à @iamvdo pour sa présentation aumeetup #FRANCEHTML5 avec une mention spéciale pour l’effet de brillance css3create.com/Effet-de-brill…
— Philippe Nenert (@papilip)
23 avril 2013
Drapeau HTML5 en pur CSS ! Bravo @iamvdo #FranceHTML5
— Mathieu Santostefano (@welcoMattic)
23 avril 2013
Démo filtres avancés au meetup #francehtml5 tout siimplement bluff ant. Bravo @iamvdo !
Avez-vous déjà utilisé les transformations 3D avec CSS? Oui? Bon point. Si ce n'est pas le cas, je vous conseille un article complet que j'avais écrit à ce sujet.
Le problème des transformations CSS 3D, ce n'est pas le support dans les navigateurs, qui est aujourd'hui plutôt bon: Chrome 12+, Safari 4+, Firefox 10+, IE10, iOS, Android 3+ (les navigateurs Opera n'ont actuellement pas de support mais cela devrait changer avec le passage à Blink). De plus, il est assez facile de prévoir des solutions de repli pour les navigateurs trop anciens.
Le problème, c'est bien son support dans IE10, et notamment la propriété transform-style: preserve-3d. Cette propriété, qui permet de forcer les éléments imbriqués à subir les transformations 3D lorsque le parent est transformé, n'est tout simplement pas reconnue dans ce navigateur.
Cette propriété peut être facultative dans le cas de transformations simples (un seul élément), mais devient nécessaire dès lors que l'on souhaite appliquer une transformation commune à un ensemble d'éléments, comme lors de la création du fameux effet Flip-Card (pourtant simple).
Sur le site de Microsoft, il est expliqué clairement:
Pour le moment, Internet Explorer ne supporte pas le mot-clé preserve-3d sur la propriété transform-style. Vous pouvez contourner ce comportement en additionnant manuellement les transformations du parent à chaque élément à transformer.
OK. Alors allons-y...
L'effet Flip-Card
Voici un effet flip-card «classique»:
Notre démo est donc ici composée:
d'un élement .scene qui contient un élément .flip
l'élément .flip contient les 2 éléments .avant et .arriere.
ces 2 éléments sont placés l'un sur l'autre et .arriere est transformé de 180deg sur l'axe X pour l'afficher à l'envers. Les faces arrières sont masquées.
Lors du survol de .scene, l'élément .flip est transformé de 180deg sur l'axe X également pour créer le retournement. Sans oublier de préciser transform-style: preserve-3d sur .flip pour que l'élément imbriqué .arriere subisse la transformation du parent.
Ce code fonctionne dans tous les navigateurs supportant les transformations 3D. Pour IE10, il faut se passer de transform-style et répercuter la transformation du parent sur les enfants. Il nous faut donc:
lors du survol de .scene, transformer de 180deg sur l'axe X l'élément .avant (0 +180)
lors du survol de .scene, transformer de 360deg sur l'axe Y l'élément .arriere (180 + 180)
ôter (ou mettre en commentaire) transform-style-preserve-3d;
Edit 04/06/2013: En tout cas, c'est ce que l'on pourrait croire! (Merci à Johan dans les commentaires pour avoir remonté un bug)
En fait, c'est plus compliqué que cela. IE10 a un comportement étrange lors de l'utilisation de backface-visibility et de perspective. Lors de l'animation, les faces arrières ne sont tout simplement pas masquées. Pour contrer ce problème, il nous faut alors définir les perspectives sur chaques éléments transformés, en utilisant la fonction perspective() prévue pour cela.
De plus, il est préférable de définir les transformations initiales, même lorsque qu'il n'y a pas de rotation à effectuer. Le code final, compatible IE10 devient donc:
Et voici le rendu final (identique à l'effet précédent mais compatible IE10!):
Conclusion
Ce petit manque d'IE10 peut s'avérer vite pénible, notamment dans le cas d'imbrications complexes où chaque transformation doit alors se répercuter sur chaque enfant. Et le fait de devoir définir plusieurs fois la perspective rend la maintenance vraiment difficile.
Je ne sais pas personnellement pourquoi IE10 n'implémente pas cette valeur pourtant si pratique (performance? politique?). Et vous, connaissez-vous la raison?
Edit 02/07/2013: IE11 n'ajoute toujours pas le support de transform-style: preserve-3d.
box-decoration-break: son support progresse... lentement!
La propriété box-decoration-break du module CSS Backgrounds and Borders Level 3 n'est pas très connue, et pourtant s'avère bien pratique!
Comme le dit la spécification, cette propriété propose de définir la façon dont un élément scindé en plusieurs fragments est affiché. Un élément peut se retrouver coupé lors:
d'un changement de page (media print)
d'un saut de colonne (Multicolumn Layout)
d'un changement de ligne, pour les éléments inline
Lorsque qu'une boite est alors scindée, les options disponibles sont:
slice: chaque fragment est considéré comme une partie d'un seul élément. (c'est la valeur par défaut)
clone: chaque fragment est indépendant et a ses propres bordures, marges et arrière-plans.
Lorsque la valeur clone est utilisée, les propriétés suivantes sont affectées indépendamment pour chaque fragment:
border et ses déclinaisons
border-image
border-radius
padding
box-shadow
background
Sur cette démo, vous distinguez clairement la différence de rendu entre l'élément de la première colonne (avec box-decoration-break: clone) et celui de la deuxième (avec box-decoration-break: slice) si vous utilisez un navigateur compatible.
Cette propriété est actuellement supportée sur Opera (depuis la version 10.60) sans préfixes, et depuis peu dans Chrome avec préfixes. IE10 ne la supporte pas, ni Firefox et Safari d'ailleurs. Cependant Firefox supporte une propriété non standard -moz-background-inline-policy qui définit la manière dont traiter les arrière-plans au sein de plusieurs fragments. C'est une sorte de sous-propriété de box-decoration-break.
Conclusion
Après tests, il s'avère que parmi les 3 situations qui créent des fragments, seule une est implémentée dans les navigateurs: celle qui s'applique aux éléments inline sur plusieurs lignes. En conséquence, il n'est pas possible d'utiliser box-decoration-break pour des fragments sur plusieurs colonnes ou sur plusieurs pages d'impression. Et c'est bien dommage...
Le module CSS3 Color définit 4 fonctions de couleur: rgb(), hsl() et leurs pendants respectifs avec transparence rgba() et hsla().
Les fonctions rgb() ou rgba(), comme leurs noms l'indiquent, acceptent comme paramètres les valeurs Rouge (R), Vert (G), Bleu (B) et Alpha (A) définissants une couleur.
les valeurs R, V, et B allant de 0 (noir) à 255 (blanc)
la valeur Alpha allant de 0 (transparent) à 1 (visible).
Voici deux exemples d'utilisation:
/* Un rouge foncé */ rgb(80, 0, 0); /* Du noir transparent à 50% */ rgba(0, 0, 0, .5);
Cela peut être très pratique, mais dans la majorité des cas il est très difficile de décliner une couleur afin d'obtenir des couleurs plus claires, plus foncées ou la couleur complémentaire de la teinte de base. Par exemple, prenons cette couleur:
rgb(227, 93, 54);
Personne ne peut me dire instantanément les valeurs de la couleur qui est 10% plus foncée! Après un passage dans Photoshop, je trouve
rgb(201, 84, 48);
Super.
Les avantages de hsl()
Les fonctions hsl() ou hsla() reposent sur un mode de représentation des couleurs différent, basé sur une Teinte (H), la Saturation (S) et la Lumière (L):
Teinte: valeur de couleur sur un cercle chromatique de 0° (Rouge) à 360° (Rouge). A 60°: Jaune, à 120°: Vert, à 180°: Cyan, à 240°: Bleu, à 300°: Magenta.
Saturation: de 0 à 100%
Lumière ou Luminance: de 0 (entièrement noir) à 100% (entièrement blanc). 50% correspond à la teinte de base.
Voici donc du rouge foncé:
hsl(0, 100%, 16%);
Et du noir transparent (peu importe la teinte et la saturation en fait, seule la luminosité à 0% compte)
hsla(0, 100%, 0%, .5);
Cette façon de représenter la couleur nous offre pas mal d'avantages.
Représentation graphique du modèle HSL en CSS
Nuances et ombres
Le premier avantage est la possibilité évidente de définir les nuances (couleurs plus claires) et ombres (couleurs plus foncées) d'une teinte. Il suffit pour cela de modifier la luminosité.
/* Teinte de base */ hsl(14, 76%, 55%); /* Plus foncée */ hsl(14, 76%, 75%); /* Plus claire */ hsl(14, 76%, 35%);
Couleurs complémentaires
En jouant sur le paramètre Teinte, il est possible d'obtenir les couleurs complémentaires. Pour cela, il suffit d'ajouter 180° à la valeur de la teinte sur le cercle chromatique (un demi-tour).
/* Teinte de base */ hsl(14, 76%, 55%); /* Couleur complémentaire */ hsl(194, 76%, 55%);
Il est également envisageable d’obtenir les couleurs complémentaires adjacentes en ajoutant 120° puis 240°.
Toujours en jouant sur ce paramètre Teinte, il est possible d'obtenir les couleurs analogues (couleurs adjacentes). Pour cela, il suffit d'ajouter ou d'enlever à la valeur de la teinte des angles de 30°.
Enfin, en modifiant le paramètre Saturation, nous obtenons une palette de couleur dite monochrome.
/* Désaturation */ hsl(14, 46%, 55%); /* Désaturation plus forte */ hsl(14, 16%, 55%);
Conclusion
Les avantages à utiliser hsl() au lieu de rgb() sont évidents. Néanmoins, il existe une contrainte de taille: les logiciels graphiques (tel Photoshop) n'utilisent pas le même modèle HSL que celui de CSS. Il n'est donc pas possible de récupérer les valeurs fournies pour les utiliser directement dans votre CSS. Pour pouvoir utiliser les bonnes valeurs, vous devrez les convertir grâce à des outils comme:
Coloratum
ColorZilla
Recherchez «RGB to HSL» sur Google
Deuxième contrainte, ces fonctions ne sont supportées qu'à partir d'IE9. Espérons que la mise à jour vers Windows 8 accélère les choses.
Allez, et comme vous êtes sympa, voici une petite démo en ligne!
Dans la gestion de la cascade CSS, il existe deux mots-clés qui peuvent s’avérer bien pratique. Ce sont les mots-clés inherit et initial.
Le mot-clé inherit
Lorsque que la valeur spécifiée pour une propriété CSS est inherit, la valeur calculée de cet élément est identique à la valeur calculée de son parent. Cela permet de forcer l'héritage. Par exemple, prenons ce code HTML:
Dans ce cas, la couleur d’arrière-plan (qui ne s'hérite pas par défaut) du pseudo-élément sera la même que celle de l’élément <p>. Il est par exemple possible de créer une infobulle où la couleur de la petite flèche est toujours la même que la couleur de la bulle elle-même. Cela offre un gain de temps dans la mise à jour: une seule modification requise.
Mais il existe pleins d'autres cas où ce mot-clé est très pratique (width et height d'un élément en absolute par exemple). Il existe même une utilisation détournée de inherit qui permet d'animer les pseudo-éléments.
Le mot-clé inherit est très bien supporté dans les navigateurs (depuis IE8).
Le mot-clé initial en CSS3
Lorsque la valeur d'une propriété CSS est initial, la valeur utilisée sera la valeur initiale de la propriété telle qu'elle est définie dans la spécification CSS. C'est une sorte de retour à un «état normal», non hérité. Elle peut s'avérer pratique lorsque l'on souhaite annuler l'effet d'héritage tout en réinitialisant à la valeur par défaut:
lorsque l'on ne se souvient plus de cette valeur (e.g. C'est quoi déjà la valeur par défaut de white-space? auto, normal, none, 0? initial est le mot-clé passe-partout)
lorsque cette valeur est dynamique (e.g. text-align vaut left en mode ltr, et vaut right en mode rtl. text-align: initial vaut left ou right en fonction du contexte. Par ailleurs, les valeurs start et end couvrent justement ce cas là).
Cas d'utilisation: Annuler la propagation des tailles de police
Lorsque l'on utilise des unités de polices relatives (avec l'unité em par exemple), la gestion de la cascade est toujours délicate. Par exemple, sur un navigateur configuré par défaut à 16px, si l'élément <html> est défini à 0.9em (14.4px) et les p à 1em, les <p> seront affichés en fait à 0.9em (14.4px). Il faudrait utiliser 1.111111em pour que les <p> soit proche de 16px. Le mot-clé initial peut alors devenir utile dès lors que l'imbrication devient complexe.
Vous l'aurez compris, son utilisation est limitée. De plus, le support de initial est malheureusement plus faible, Opera et Internet Explorer ne la reconnaissent tout simplement pas.
Ce mot-clé deviendra certainement plus utile lorsque la propriété all arrivera dans les navigateurs. La propriété all permettant de redéfinir l'ensemble des propriétés CSS et ainsi stopper la cascade de manière globale.
Ressources supplémentaires
Pour référence, voici quelques liens intéressants:
inherit sur MDN
initial sur MDN
spécification CSS2.1 (inherit)
module CSS3 Cascading & Inheritance (inherit et initial)
Pour entrer directement dans le vif du sujet: non, je ne suis pas contre les préprocesseurs. D’ailleurs j’utilise PHP (HyperText Preprocessor) pour générer des sites web dynamiques. C’est même indispensable.
En revanche, je ne travaille pas avec les préprocesseurs CSS parce que je n’en ai pas besoin. Je pense d’ailleurs que très peu de personnes en ont réellement besoin. Et cela pour plusieurs raisons que je vais détailler dans cet article:
Ils complexifient CSS
Ils n’ajoutent pas de fonctionnalités CSS aux CSS
Ils ne font pas (toujours) gagner de temps
Ils peuvent être dangereux pour le standard CSS (et provoquer une confusion)
Pour faire court, je ne remets pas en cause le besoin, ni la logique de développement qui découle de l’utilisation des préprocesseurs CSS, mais cette automatisation est-elle efficace/utile pour tous? Et avec son utilisation intensive, n’est on pas en train de se mettre des bâtons dans les roues des standards?
Ils complexifient CSS
Je sais qu’en lisant ce titre, certains d'entre vous fulminent! Avant que vous ne m’envoyiez des menaces de mort sur Twitter, je vais tenter de m’expliquer.
J’ai bien employé ici le verbe «complexifier» et non le verbe «compliquer». Complexifier signifie juste «rendre plus complexe». Vous pouvez trouver la syntaxe des préprocesseurs simple, elle n’en reste pas moins plus complexe que le CSS de base.
Alors bien entendu, lorsque l’on ajoute les éléments imbriqués, les références, les inclusions complexes, l’héritage, etc., je pense que parler de complexification du langage n’est pas faux. (certes, la syntaxe des préprocesseurs est réutilisable et plus facilement mise à jour)
Le problème avec cela, c’est que de jeunes intégrateurs ou des personnes ne travaillant pas dans le milieu du web (mais l’utilisant beaucoup) se complexifient la tâche inutilement, avec le gain de fonctionnalités et de temps en ligne de mire.
Ils n’ajoutent pas de fonctionnalités CSS aux CSS
Comme je le disais plus haut, j’utilise PHP pour générer des pages web HTML. Ce langage me permet de réaliser ce que HTML ne permets pas seul: affichage conditionnel, traitement des formulaires, interaction avec des bases de données, suivi de l’internaute, etc. Mais c’est vrai aussi que je profite de ce langage de programmation pour créer du contenu HTML plus rapidement et plus maintenable.
Quoi qu’il en soit, les préprocesseurs CSS n’ajoutent pas de fonctionnalités CSS aux CSS. Ce qu’ils font, c’est simplement permettre de générer des CSS plus rapidement via une syntaxe propriétaire à chaque préprocesseur, il n’y a pas de miracle.
Attention, utiliser les préprocesseurs CSS ne fait pas de vous des experts CSS plus rapidement.
Je pense même qu’une démocratisation intensive des préprocesseurs peut être dangereuse pour l’évolution du langage (voir plus bas).
Ils ne font pas (toujours) gagner du temps
Il est évident que les préprocesseurs permettent de gagner en maintenance, en rigourosité, en temps de mise à jour, etc. mais seulement si ce besoin est extrêmement fort. Je pense personnellement que l’on peut perdre plus de temps à mettre en place une structure trop complexe avec un préprocesseur plutôt que de recopier 3 fois la même couleur dans CSS. Certes, cet exemple est un peu caricatural mais vous comprenez le principe. Pour faire un parallèle, vous n’utiliseriez pas PHP pour faire un simple echo d’une balise HTML sans contenu dynamique.
N’oubliez pas que vous voyez les préprocesseurs CSS avec vos yeux d’experts web.
Malheureusement, la plupart des gens qui font du web ne sont pas des experts web (loin de là) et beaucoup ne travaillent pas sur de très gros sites (où le nombre de pages est important):
site d’association (sportive, culturelle...)
site vitrine, site événementiel
site personnel / professionnel
site d’entreprise réalisé en interne / intranet
application web métier
et en règle générale tous les sites de moins de 20 pages.
Ne faites pas miroiter à tous ces «développeurs» des fonctionnalités magiques, de la maintenabilité, des gains de temps en utilisant les préprocesseurs. C’est faux.
Seule la bonne connaissance de CSS permet un gain de temps dans le développement.
De plus, replonger ou découvrir un code réalisé avec les préprocesseurs CSS fait ressortir les questions liées à la programmation:
que contient cette variable à ce moment donné?
pourquoi cette boucle?
que fait cette inclusion déjà?
etc.
Le gain de temps est-il toujours si évident?
Enfin, ce que je pense surtout, c’est que la majeure partie des fonctionnalités des préprocesseurs peuvent êtres réalisées sans, tout en offrant un gain de temps et un niveau de maintenance équivalents. Cela grâce à la mise en place de conventions de travail via les frameworks CSS ou de principes de programmation orientée objet (OOCSS, etc.). Ce qui rend les préprocesseurs encore moins utile.
Ils peuvent être dangereux pour le standard CSS
Une des questions que je me pose quand je pense aux préprocesseurs, c’est si le langage CSS va continuer à évoluer ou si les préprocesseurs vont entièrement le remplacer?
Prenons l’exemple le plus pratique des préprocesseurs: les variables. Dans Sass, leur syntaxe est proche de la déclaration de variables PHP tout en conservant des aspects de CSS:
$variable: 8px;
Il n’y a quasiment pas de contrainte.
Dans le module des variables CSS du W3C, les variables ne sont en fait pas des variables! Ce sont simplement des propriétés personnalisées qui s’héritent
div{ var-variable: 8px; }
Les mêmes contraintes du DOM s’appliquent donc (limite de la cascade, impossibilité de cibler le parent, etc.).
Je me demande alors ce que vont devenir les variables CSS lorsque celles-ci seront un standard W3C et qu’elles seront supportées par les navigateurs (si cela arrive un jour). Les développeurs qui utilisent actuellement les préprocesseurs seront-ils prêts à modifier leurs méthodes d’aujourd’hui pour mettre en place les standards de demain, alors que cela impose plus de contraintes. Je ne crois pas.
Le risque, c’est qu’à terme, les «nouveautés CSS» apparaissent dans les préprocesseurs et que le standard CSS stagne, voire régresse.
C’est d’ailleurs déjà le cas avec certaines fonctionnalités:
utilisation d’une couleur transparente avec notation raccourcie rgba(white, .5) ou fonctions de couleurs saturate(), desaturate(), lighten(), darker() : toutes ces fonctionnalités sont très pratique mais le langage CSS doit-il tenter de normaliser cela également?
min(), max(): des fonctions éponymes ont déjà été à l’étude par le W3C dans le passé. Est-il encore utile d’y réfléchir pour une réintroduction?
calcul avec les opérateurs arithmétiques: personne n'utilise calc() ?
imbrications des sélecteurs: le langage CSS doit-il permettre cela aussi?
etc.
De plus, je pense que la syntaxe des préprocesseurs peut provoquer une confusion avec le langage CSS lui-même, si elle n'est pas clairement maitrisée. Et peut-être encore plus demain si toutes les fonctionnalités décrites ci-dessus deviennent des standards de fait.
Conclusion
Je ne dis pas qu’il faut arrêter d’utiliser les préprocesseurs CSS, mais il faut être conscient de ce que cela implique. Vouloir à tout prix les mettre en place n’est pas toujours une bonne chose et peut devenir dangereux. Pas forcément pour vous, mais pour les autres, les non-experts, qui les utilisent sans savoir. Faites leur comprendre.
Si vous aimez le langage CSS mais vous le trouvez limité, impliquez-vous et faites le évoluer!
Mais surtout, ne les utilisez pas uniquement pour faire comme tout le monde. «La mode, les moutons, faire de la merde, tout ça (@7studio) »
Encore une très bonne présentation de Zoe M. Gillenwater (@zomigi) sur les modèles de positionnement avancés et futurs en CSS.
Pour ce qui est très proche de nous, on découvre notamment:
Une présentation approfondie de Flexbox avec quelques cas d'usages intéressants.
Une présentation du Grid Layout avec une démo (sous IE10).
Pour le futur de CSS, elle présente certaines parties du Basic Box Module:
Les floats du futur
La nouvelle valeur contour.
La nouvelle propriété float-displace
Les nouvelles méthodes pour contenir les floats:
nouvelle propriété clear-after
nouvelle valeur contain-floats pour min-height (dans le CSS Intrinsic & Extrinsic Sizing Module)
Retrouvez d'ailleurs la liste de toutes les propositions en cours pour contenir les flottants au sein de ce module (Editor's Draft).
Pour rappel sur comment contenir les flottants actuellement (création d'un contexte de formatage block), je vous conseille cet article de Raphaël Goetter.
Retrouvez donc la présentation directement sur Slideshare ou sur son site perso avec en plus pas mal de ressources intéressantes.
Styler les input inactifs (disabled) sur iOS (iPhone, iPad)
Si vous tentez d’appliquer un changement de couleur de texte ou d’arrière-plan à un champ de formulaire inactif, cette couleur ne sera pas correctement utilisée partout, notamment sur Safari et iOS.
En effet, voici le rendu visuel obtenu sur iOS, même en forçant la couleur:
Rendu d’un champ input disabled même en forçant les couleurs
Ce n’est pas du meilleur effet.
Si jamais l’on vous demande de respecter précisément une maquette graphique, cela peut devenir vite problématique. De plus, si la couleur utilisée pour les input est plutôt claire, celle qui sera utilisée par défaut sur les input disabled le sera encore plus et des problèmes d’accessibilité peuvent se poser (contraste trop faible).
Il existe donc une technique que voici.
1 - Forcer l’opacité
input[disabled]{ ... opacity: 1; }
Rendu d’un champ input disabled après ajout de l’opacité
Ainsi, notre élément retrouve sa couleur de fond originelle, et sa couleur de bordure. Cela peut être suffisant mais si vous souhaitez également avoir le contrôle sur la couleur du texte, il est possible de la modifier.
2 - Utiliser la propriété CSS non standard -webkit-text-fill-color
Rendu d’un champ input disabled après ajout de l’opacité et de -webkit-text-fill-color
Cette propriété spécifique au moteur de rendu webkit permet de modifier la couleur du texte, mais surclasse la propriété color. En apprendre plus sur cette propriété.
Cette propriété est également souvent utilisée avec la déclaration -webkit-background-clip: text (non standard) pour permettre à un arrière-plan d’être visible uniquement dans le texte (tel un masque).
Une petite expérience de paiement en ligne qui simule une carte bancaire. Entrez un numéro de carte, la date d'expiration, retournez la carte et remplissez votre code de sécurité et le code postal. Payez!
Cet article est une traduction de What No One Told You About Z-Index écrit par Philip Walton le 22 décembre 2012.
Ce que personne ne vous a dit sur z-index
Le problème avec z-index, c’est que très peu de personnes ont réellement compris son fonctionnement. Ce n’est pas très compliqué, mais si vous n’avez jamais pris le temps de lire la spécification, il y a certainement des aspects cruciaux qui vous ont échappé.
Vous ne me croyez pas? Ok, voyons si vous pouvez résoudre ce problème?
Le problème
Dans le HTML suivant, vous avez trois <div>, et chaque <div> contient un seul élément <span>. Chaque <span> a sa propre couleur d’arrière-plan, respectivement rouge, vert et bleu. Chaque <span> est positionné de manière absolue près du coin haut gauche du document, de manière à chevaucher légèrement un autre élément <span>, ce qui permet de bien distinguer l’empilement. Le premier <span> a une valeur de z-index de 1, alors que les deux autres non pas de z-index défini.
Voici à quoi ressemble le HTML et le CSS. J’ai également inclus une démo du rendu (via CodePen)
Le challenge: essayez de faire passer le <span> rouge derrière les éléments bleu et vert en respectant ces règles:
Ne pas modifier le code HTML
Ne pas ajouter ni modifier la propriété z-index, sur aucun élément
Ne pas ajouter ni modifier la propriété position, sur aucun élément
Pour vérifier que vous avez bien compris, cliquez sur «Edit on CodePen» et jouez un peu avec. Le résultat doit être identique à l’exemple ci-dessous:
Attention: Ne cliquez pas sur l’onglet CSS dans l’exemple ci-dessous au risque de dévoiler la solution.
La solution
La solution est d’ajouter une valeur d’opacité inférieure à 1 sur la première <div> (le parent du <span> rouge). Voici le CSS du CodePen ci-dessus:
div:first-child{ opacity: .99; }
Si vous êtes perplexe et n’aviez jamais pensé que l’opacité avait un impact sur l’ordre d’empilement des éléments, bienvenue au club. J’ai eu la même réaction quand je suis tomber pour la première fois sur ce cas.
Heureusement, la suite de l’article va clarifier les choses.
Ordre d’empilement (Stacking order)
z-index semble très simple: les éléments avec un z-index plus élévé apparaissent devant les éléments avec un z-index plus faible. Et bien en fait, non. Et c’est bien là le problème de z-index. Ça parait tellement simple, que beaucoup de développeurs ne prennent pas le temps de lire les détails.
Chaque élément HTML peut être devant ou derrière un autre élément du document. C’est ce que l’on appelle l’ordre d’empilement. Les règles qui définissent cet ordre sont plutôt bien définies dans la spécification, mais comme je l’ai déjà mentionné, elles ne sont pas bien comprises par la plupart des développeurs web.
Quand les propriétés z-index et position ne sont pas utilisées, les règles sont plutôt simples: l’ordre d’empilement est identique à l’ordre d’apparition dans le HTML. (OK, c’est en fait un peu plus compliqué que cela, mais tant que vous n’utilisez pas des marges négatives pour superposer des éléments en ligne, vous ne rencontrerez jamais ce genre de cas là)
Quand vous commencez à utiliser la propriété position, les éléments positionnés (et ses enfants) sont affichés devant les éléments non-positionnés. (Un élément est considéré «positionné» lorsque que la valeur de la propriété position est différente de static, comme par exemple relative, absolute, etc.)
Enfin, lorsque z-index est associé, les choses deviennent plus délicates. D’abord, il est naturel de penser que les éléments avec un z-index plus élevé apparaissent devant les éléments avec un z-index plus faible, et que les éléments qui ont un z-index sont affichés devant les éléments qui n’en ont pas, mais ce n’est pas si simple. Premièrement, le z-index ne fonctionne que sur les éléments positionnés. Si vous tentez d’appliquer un z-index sans position définie, il ne se passera rien. Deuxièmement, les valeurs de z-index peuvent créer un contexte d’empilement, et subitement les choses deviennent légèrement plus compliquées.
Contextes d’empilement (Stacking contexts)
Un groupe d’éléments qui ont un parent commun qui se déplace d’avant en arrière dans l’ordre d’empilement est ce que l’on appelle un contexte d’empilement. Une bonne compréhension des contextes d’empilement est la clé pour vraiment saisir la manière dont le z-index fonctionne.
Chaque contexte d’empilement a un élément HTML unique comme racine. Lorsque un nouveau contexte d’empilement est créé sur un élément, cela contraint ses enfants à apparaître à un endroit précis dans l’ordre d’empilement. Cela signifie qu’un élément au sein d’un contexte d’empilement, qui est le plus bas dans l’ordre d’empilement, ne peut pas être affiché devant un élément qui est dans un autre contexte d’empilement affiché plus haut, même avec un z-index très important.
De nouveaux contextes d’empilement peuvent êtres créés:
Lorsque un élément est la racine du document (l’élément <html>)
Lorsque un élément dont la valeur de la propriété position est différente de static et lorsque z-index est différent de auto.
Lorsque un élément à une opacité inférieure à 1
La première et la seconde façon de créer un contexte d’empilement sont plutôt claires et sont généralement connues des développeurs web (mêmes s’ils ne savent pas forcément que ça a un nom)
La troisième façon (l’opacité) n’est presque jamais évoquée hors des spécifications du W3C.
Note personnelle de traduction: Il existe d’autres règles qui crée un contexte d’empilement (en CSS3):
Lorsque un élément dont la valeur de la propriété transform est différente de none
Lorsque un élément dont la valeur de la propriété transform-style vaut preserve-3d
Lorsque un élément dont la valeur de la propriété filter est différente de none
Lorsque un élément, enfant d’un Flexbox, dont la valeur de la propriété z-index est différente de auto (même si la valeur de la propriété position est static)
Lors de l’utilisation des propriétés clip-path, mask ou mask-image
et d’autres…
Définir l’ordre d’empilement d’un élément
Réussir à déterminer l’ordre d’affichage pour chaque élément d’une page (y compris les bordures, les arrière-plans, les textes, etc.) est très difficile et très loin du cadre de cet article (je vous renvoie une nouvelle fois à la spécification)
Mais dans la plupart des cas, une compréhension de base peut permettre de prévoir plus facilement le développement CSS. Commençons donc par définir l’ordre d’affichage au sein de chaque contexte d’empilement.
Ordre d’empilement au sein du même contexte d’empilement
Voici les règles qui définissent l’ordre d’affichage pour un contexte d’empilement (du bas vers le haut):
L’élément racine du contexte d’empilement
Les éléments positionnés (et ses enfants) avec un z-index négatif (les valeurs les plus hautes sont affichées devant les plus basses; les éléments avec le même z-index sont affichés en suivant l’ordre du HTML)
Les éléments non-positionnés (en suivant l’ordre du HTML)
Les éléments positionnés (et ses enfants) avec un z-index dont la valeur est auto (en suivant l’ordre du HTML)
Les éléments positionnés (et ses enfants) avec un z-index positif (les valeurs les plus hautes sont affichées devant les plus basses; les éléments avec le même z-index sont affichés en suivant l’ordre du HTML)
Note: les éléments positionnés avec un z-index négatif sont affichés en premier dans le contexte d’empilement, ce qui signifie qu’ils apparaissent derrière tous les autres éléments. Grâce à cela, il devient possible qu’un élément soit affiché derrière son parent, ce qui est normalement impossible. Cela fonctionne uniquement si le parent est dans le même contexte d’empilement et si ce parent n’est pas l’élément racine du contexte d’empilement. Un très bon exemple de cette technique est la création d’ombres sans images par Nicolas Gallagher.
Ordre d’empilement global
Avec une solide compréhension sur la création de nouveaux contextes d’empilement ainsi qu’une maîtrise de l’ordre d’empilement au sein d’un contexte d’empilement, il devient assez facile de déterminer l’ordre d’empilement global des éléments.
La clé de la réussite est d’être capable de repérer quand de nouveaux contextes d’empilement sont créés. Si vous appliquez un z-index d’un milliard sur un élément et que l’ordre de cet élément n’est pas affecté, vérifiez si l’un de ses parents ne crée pas un contexte d’empilement. Si c’est le cas, votre z-index ne vous sera pas d’une grande aide.
Pour conclure
Revenons au problème initial, j’ai recréé la structure HTML en ajoutant des commentaires pour chaque balise qui indique son ordre d’empilement. Cet ordre se base sur le CSS d’origine.
Le span.red, initialement en position 6, se retrouve en 1.1. J’ai utilisé ici la notation avec un point pour bien symboliser la création d’un nouveau contexte d’empilement où span.red devient le premier élément au sein de ce contexte.
C’est donc maintenant un peu plus clair sur pourquoi l’élément rouge est affiché sous les autres éléments. L’exemple original contenait seulement deux contextes d’empilement, le contexte racine et celui formé par le span.red. Avec l’ajout de l’opacité sur le parent de span.red, nous avons créé un troisième contexte, et donc, le z-index du span.red est uniquement actif au sein de ce nouveau contexte. Du fait que la première <div> (celle où l’opacité est appliquée) et ses éléments frères ne sont ni positionnés ni n’ont de z-index, leur ordre d’affichage est celui du HTML, ce qui signifie que la première <div>, et tous ses enfants contenus dans le contexte d’empilement, sont affichés sous la deuxième et la troisième <div>.
Utiliser les masques, les pseudo-éléments et les dégradés CSS - Créer un effet brillant qui mettra en valeur vos logos, images, bannières, etc. Une petite touche de bling-bling qui fait toujours son petit effet.
Dans la longue liste des nouveautés CSS, il y a les filtres graphiques. Grâce à eux, il est d'ores et déjà possible de créer des flous, de modifier la luminosité ou le contraste, de convertir en niveaux de gris ou en négatif... Bref, la majeure partie des effets réalisés depuis un éditeur graphique.
Pour découvrir ce que sont les filtres, vous pouvez:
lire cette traduction par Jérémie Patonnier
découvrir une explication rapide avec démo à l'appui.
lire la longue spécification officielle
A la lecture de ces ressources, deux filtres attirent l'attention:
filter: opacity
filter: drop-shadow
Et la question suivante se pose: Quelle différence existe t'il entre ces filtres et les propriétés opacity et box-shadow?
filter: drop-shadow vs. box-shadow
Un article sur bricss.net l'a très bien expliqué et un autre article plus complet existe également. Pour faire simple, la propriété box-shadow ajoute une ombre au bloc lui-même (un rectangle) alors que filter: drop-shadow ajoute une ombre à l'élément tout en conservant les contours et la transparence. Il s'applique à la couche alpha de l'élément. Ainsi, une ombre sur une image PNG suivra le contour de cette image:
Malheureusement, drop-shadow n'accepte pas d'ombre interne ni de valeur d'étendue d'ombre, comme c'est le cas avec box-shadow.
filter: opacity vs. opacity
La différence entre ces deux options est plus floue mais peut apparaître lors de l'utilisation simultanée de plusieurs filtres. Par exemple, lors de l'utilisation conjointe de filter: blur et d'opacity, un bug se produit: le flou est rogné sur les bords! Avec filter: blur et filter: opacity, aucune découpe.
Mais pourquoi?
Je pense que tout cela est une question d'ordre dans le rendu d'une page web. Que dit la spec sur la propriété CSS opacity:
Opacity can be thought of as a postprocessing operation. Conceptually, after the element (including its descendants) is rendered into an RGBA offscreen image, the opacity setting specifies how to blend the offscreen rendering into the current composite rendering.
On comprends donc que l'application de la propriété opacity s'effectue en post-traitement. Un élément est donc entièrement rendu hors de l'écran, puis la propriété opacity fusionne l'élément et la page pour le rendu final (compositing).
Et sur les filtres CSS:
Filter effects are a way of processing an element's rendering before it is displayed in the document. Typically, rendering an element via CSS or SVG can conceptually described as if the element, including its children, are drawn into a buffer (such as a raster image) and then that buffer is composited into the elements parent. Filters apply an effect before the compositing stage. Examples of such effects are blurring, changing color intensity and warping the image.
Les filtres CSS sont donc appliqués avant l'étape de compositing, et donc avant l'application de la propriété opacity.
On peut donc en déduire que lorsque opacity s'applique, elle utilise les bornes connues de l'élément, et donc l'effet de flou est rogné.
Edit 20-03-2013: Le problème ne semble plus être présent dans Chrome 27 sur WXP. A voir...
De plus, filter: opacity offre l'avantage de pouvoir intervenir avant ou après l'application d'un second filtre. Des différences peuvent donc intervenir, comme c'est le cas avec l'utilisation conjointe des filtres drop-shadow et opacity. Le résultat n'est pas similaire si le filtre est appliqué avant ou après.
Enfin, dernière possibilité graphique intéressante: la spécification en cours propose la fonction filter(). Cette fonction, qui retourne une image, pourrait être utilisée directement via CSS, par exemple avec la propriété background-image. Il deviendrait donc possible d'appliquer une image en arrière-plan d'un élément, et de modifier l'opacité de celle-ci. C'est en quelque sorte comme s'il existait background-opacity.
Malheureusement, aucun navigateur n'implémente (encore) cette fonction!