Blunt : une collection de classes CSS pour créer un layout responsive rapidement
Découvrez Blunt : une collection de classes CSS pour créer un layout responsive rapidement.
![]()
Découvrez Blunt : une collection de classes CSS pour créer un layout responsive rapidement.
![]()
Tailwind ou pas Tailwind ? Ce framework CSS, fondé sur la méthodologie atomique, divise les rangs des intégratrices et intégrateurs. Chaque camp y trouve des arguments souvent très tranchés : on aime pas du tout ou bien on adore, il reste peu de place pour le "ça dépend".
Au sein de notre agence web Alsacréations, spécialisée dans les domaines front-end et dans l'accessibilité, nous avons expérimenté Tailwind depuis un peu plus de deux années à présent. Cet article détaille les leçons que nous avons tirées de l'usage ce framework.
Quelques précisions concernant notre contexte d'agence : nous ne sommes pas une start-up, nous intégrons des maquettes fournies par le client ou designées en interne et validées par le client. Même s'il nous arrive d'appliquer un framework pour un client (Bootstrap, Tailwind), notre expérience de plusieurs (dizaines d')années en intégration nous interdit le design de nouvelles pages "à l'arrache" comme certains pourraient être tentés de le faire avec ce genre d'outils.
Le mois de juin 2020 marque notre première intégration pour un client réalisée à l'aide de Tailwind CSS, puis une demi-douzaine de projets ont suivi. Sans oublier quelques projets personnels ou secrets.
En novembre 2020 nous publions sur Alsacréations l'article "Tailwind CSS, découverte du framework original et innovant". Quelques mois plus tard paraissent nos Guidelines Tailwind CSS publiques.
Enfin, en mai 2021, nous publions une sorte de synthèse sur la question : "Quels framework et méthodologie CSS choisir ?".
Aucun projet d'intégration ne se déroule à la perfection, surtout dans la durée, et même au sein d'une agence web qui se considère comme compétente en la matière.
Rien que du côté CSS, les écueils que peut traverser un projet sont nombreux :
Notre expérience nous a souvent permis d'atténuer ces problèmes. Et quand je parle d'expérience, j'y inclus les différentes méthodologies que nous avons pu adopter chez Alsacréations : OOCSS dans un premier temps, BEM (un peu) et enfin Tailwind CSS.
Nous avons pu constater au cours de ces deux années d'usage que les promesses faites par Tailwind sont parfaitement tenues.
Les points suivants comptent parmi les plus retenus et cités dans notre agence :
Sans surprise, TailwindCSS n'est pas exempt de désagréments. Voici une petite énumération que nous avons pu étabir en interne :
items-baseline backdrop-invert-0 md:row-start-5 sm:content-around leading-snug dark:tracking-wider 🤷♂️Pour finir sur ce sujet, un témoignage assez évocateur :
Tailwind peut avoir un impact non négligeable sur certaines performances de chargement lorsqu'il y a de nombreuses répétitions de blocs (exemple vécu sur un projet client dont le menu généré pesait 40 Ko à cause des seules classes Tailwind)
Nous ne sommes sans doute pas les seuls dans ce cas, mais au quotidien on adapte Tailwind à notre sauce interne :
Nous sommes parfaitement conscients que nous n'employons pas ce framework "comme il faudrait" mais plutôt d'une façon hybride, cumulée avec d'autres méthodologies dans lesquelles nous piochons des idées à appliquer.
Nous nous trouvons actuellement dans une phase de transition où l'on s'est remis à tester de nouvelles méthodologies et approches.
En toute transparence, Tailwind CSS ne nous convient pas à 100%. Ou plutôt, cela dépend des types de projets : sur certains projets, il nous paraît naturel de l'employer tandis que sur d'autres typologies il devient contre-productif.
Il est tout à fait probable qu'aucune méthodologie ne nous convienne parfaitement pour l'ensemble de nos prestations d'intégration et seul l'avenir nous le dira.
En attendant, un nouveau challenger est entré en jeu et nous commençons à le tester en production : Cube CSS.
CubeCSS n'est pas un framework mais une méthodologie plus générale. Pour le moment, nous la trouvons très prometteuse, mais laissez-nous le temps de l'expérimenter et… de vous partager nos conclusions dans un prochain article !
Les styles CSS doivent leur nom à la Cascade, qui est un bien joli et complexe algorithme tenant compte de nombreux paramètres tels que l'origine des styles, la spécificité des sélecteurs ainsi que leur ordre d'apparence.
La Cascade, c'est l'essence même de CSS. C'est ce qui fait son utilité, sa beauté… et c'est aussi le pire cauchemar des intégratrices et intégrateurs.
La Cascade, c'est ce qui fait que nos paragraphes arboreront une chatoyante couleur hotpink avec les déclarations suivantes :
<p class="kiwi">Coucou !</p>
p {color: tomato;}
p {color: hotpink;}
Dans un monde idéal, l'intégration HTML / CSS est prise en compte en amont du projet, en se donnant les ressources adéquates et les compétences nécessaires. Ce n'est bien évidemment pas un domaine que l'on traite à la légère, après le budget alloué à la communication, au SEO, aux développements "lourds" etc. en se disant que "après tout ce n'est que du CSS, même pas un vrai langage".
Mais ça c'est dans un monde idéal.
Dans un vrai projet, on récupère du code produit par des anciens stagiaires disparus ou par des frameworks de version obsolète, on a totalement oublié de s'appuyer sur une convention de nommage, on intègre avec un onglet de Stackoverflow toujours ouvert dans un coin, et on peste contre le monde entier à tenter d'écraser des styles avec !important (voire des !veryimportant ah non ça c'était une blague).
Qui n'a jamais utilisé !important me jette la première <br> !
Le vrai projet, c'est celui où l'on passe toujours trop de temps à comprendre pourquoi nos paragraphes sont chocolate et pas hotpink dans ce cas là :
p {color: tomato;}
.kiwi {color: pink;}
div p:first-child {color: chocolate;}
p.kiwi {color: hotpink;}
Les spécifications CSS ont introduit une règle-at @layer permettant de redéfinir l'ordre et la priorité dans la cascade CSS à l'aide de "layers" (couches).
Le principe général est simple : l'ordre de déclaration des layers (Layers Order) est prioritaire sur la spécificité des sélecteurs.
Ainsi, dans l'exemple qui suit, les paragraphes prendront la couleur hotpink car leur couche est déclarée en dernier.
@layer reset {
p {color: olive;}
.kiwi {color: pink;}
div p:first-child {color: chocolate;}
}
@layer base {
p {color: hotpink;}
}
![]()
Il existe plusieurs moyens de créer des couches de styles : la règle @layer avec styles associés, la même sans styles, ou l'import de fichiers de styles externes.
@layer avec styles associésOn déclare la couche via @layer en lui donnant un nom (optionnel) et on y applique des règles CSS.
@layer reset {
/* ici les règles CSS de la couche reset */
}
@layer base {
/* ici les règles CSS de la couche base */
}
@layer sans stylesOn déclare la couche via @layer vide.
@layer reset;
@layer base;
Pour info, cet exemple est équivalent à cette syntaxe :
@layer reset, base;
Cette formulation, sans styles associés, n'a d'autre but que de déclarer un ordre précis dans les couches.
La notion de layer peut être associée à la règle @import :
@import url("reset.css") layer(reset);
En plus de l'import via @import, le W3C travaille sur une version importée avec l'élément <link>.
Concrètement cela représente un moyen très simple de pouvoir :
!important… et si j'évoque ce framework en particulier, ce n'est pas tout à fait anodin (oui, il y a bien 1307 !important dans ce seul fichier CSS)@import url("bootstrap.css") layer(framework);
![]()
Le postulat de base est que chaque layer est prioritaire sur le layer déclaré avant lui. L'ordre est donc primordial.
Dans l'exemple qui suit, tous les paragraphes seront de couleur hotpink même s'ils disposent de la classe .kiwi car le layer base est déclaré après reset :
@layer reset {
p.kiwi {color: tomato;}
}
@layer base {
p {color: hotpink;}
}
Il est possible d'ajouter des styles à une couche existante, simplement en reprenant le nom du layer déjà créé :
@layer reset {
p.kiwi {color: tomato;}
}
@layer base {
p {color: hotpink;}
h1 {color: olive;}
}
@layer reset {
h1 {color: chocolate;}
}
Dans cet exemple, les styles sur h1 sont ajoutés à ceux déjà présents dans la couche reset.
Notez qu'étendre des layers ne modifie pas l'ordre originel (et donc l'application) des layers. En clair, ici la couleur des titres h1 sera olive car le layer base est déclaré après reset.
Une bonne pratique consiste à définir dans un premier temps l'ordre de toutes les couches en une règle raccourcie (ex. @layer reset, base;), puis d'étendre les styles de chacun des layers, ainsi il n'est pas possible de se tromper dans l'ordre d'application des couches.
Dans l'exemple suivant, je souhaite prioriser les styles de la couche base, je vais donc commencer par indiquer l'ordre à respecter.
Ici, malgré ma maladresse (j'ai étendu les styles base puis ceux de reset), ce sont bien les styles de base qui sont prioritaires et s'appliquent. Les paragraphes seront hotpink :
@layer reset, base;
@layer base {
p {color: hotpink;}
}
@layer reset {
p {color: olive;}
.kiwi {color: tomato;}
}
![]()
Cela peut paraître curieux, mais les styles sans layer sont appliqués en priorité.
Ainsi, dans l'exemple qui suit les paragraphes seront de couleur hotpink :
p {color: hotpink;}
@layer reset {
p {color: olive;}
p.kiwi {color: tomato;}
}
On peut imbriquer les Cascade Layers de cette manière :
@layer framework {
@layer reset {
}
}
Il est ensuite possible d'étendre les styles de ce layer en y faisant référence ainsi :
@layer framework.reset {
/* j'étends les styles dans le layer reset dans framework */
p { color: hotpink; }
}
![]()
Bonne nouvelle : CSS Cascade Layers est une spécification dont le support est relativement large. À l'heure où cet article est rédigé, seules les anciennes versions de Safari et Samsung Internet sont à la traîne (si on ne tient pas compte d'Internet Explorer bien sûr). Cela signifie que cette fonctionnalité peut très vite être utilisable en production.
Et vous, qu'en pensez-vous ? Êtes-vous aussi impatient que moi de pouvoir bénéficier de cette spécification afin d'assainir radicalement tous les anciens projets web que l'on maintient tant bien que mal ?
Les navigateurs web sont tenus d'appliquer des styles par défaut (User Agent Stylesheet) à chaque page HTML, sinon le document afficherait une page blanche (source : specifications CSS).
Chaque navigateur applique ses propres styles par défaut pouvant se révéler différents les uns des autres, sinon ce serait trop facile pour nous autres développeuses et développeurs !
Deux méthodes permettent de contourner ces différences d'affichages entre les navigateurs : le Reset CSS et le Normalize CSS.
Un "Reset CSS" (réinitialisation) est une technique qui consiste à repasser toutes les valeurs des propriétés CSS à leur état initial afin repartir de zéro et d'une base vierge avant d'appliquer nos propres styles.
Un "Normalize CSS" (harmonisation) consiste à appliquer des styles de base cohérents identiques sur chaque navigateur. Cette méthode corrige également les différences d'interprétation des spécifications et d'affichage sur l'ensemble des navigateurs et produit une base de travail suffisante (typographie, interlignages, marges, etc.)
Cela dépend bien évidemment de vos besoins, mais dans la plupart des projets il est suffisant d'appliquer une couche de Normalisation, car un Reset vous forcera à redéfinir toutes les propriétés une à une pour votre projet.
L'article (en anglais) Normalize CSS or CSS Reset?! de Elad Shechter résume bien cette dualité d'outils.
L'article (en anglais aussi) A tale of CSS Resets and Everything You Need to Know About Them. Revisited. de Margo Roi est extrêmement riche en informations sur l'évolution des techniques de Reset / Normalize, leur intérêt dans le détail et propose une liste très complète de l'ensemble des solutions existantes actuellement (Eric Meyer, Yahoo!, Normalize, Sanitize, Reboot, Remedy, etc.)
Une propriété héritable est une propriété qui récupère automatiquement la valeur de son parent. C'est le cas par exemple des propriétés typographiques ou des listes (ex. font-size, color, list-style, etc.).
Une propriété non héritable adoptera par défaut la valeur initial telle que définie dans les Spécifications. Par exemple une bordure, une largeur de boîte ou une couleur de fond n'est pas transmise par héritage aux descendants. La majorité des propriétés CSS ne sont pas héritables.
Voici une liste des propriétés CSS héritables courantes :
colorcursordirectionfont-familyfont-sizefont-stylefont-variantfont-weightfontletter-spacingline-heightlist-style-imagelist-style-positionlist-style-typelist-styletext-aligntext-indenttext-transformvisibilitywhite-spaceword-spacing
Il est possible de forcer l'héritage d'une propriété en lui appliquant la valeur inherit. Elle prendra alors la valeur de la propriété du parent (c'est à dire son ancêtre direct).
Il est également possible de contrôler l'héritage de toutes les propriétés grâce à la propriété raccourcie all afin d'appliquer la valeur indiquée sur toutes les propriétés (source : MDN : Héritage).
allLa propriété all est un super-raccourci de toutes les propriétés appliquables à un élément, ce qui lui permet d'hériter ou de réinitialiser toutes les valeurs à la fois.
Les valeurs possibles sont inherit, initial, unset et revert.
Exemple :
.parent {
display: grid;
margin: 2rem;
color: hotpink;
}
.enfant {
all: inherit;
}
Dans cet exemple, all cible toutes les propriétés de .enfant et inherit les rend héritables, donc cela est équivalent au code suivant (pour les propriétés concernées) :
.enfant {
display: grid;
margin: 2rem;
color: hotpink;
}
initialLa valeur initial appliquée à une propriété lui confère sa valeur par défaut telle que prévue par les Spécifications CSS.
Exemple :
.parent {
display: grid;
margin: 2rem;
color: hotpink;
}
.enfant {
all: initial;
}
Cet exemple est équivalent au code suivant (pour les propriétés concernées) :
.enfant {
display: inline;
margin: 0;
color: canvastext;
}
Ce résultat peut surprendre dans la mesure où l'on s'attend généralement à la valeur block pour la propriété display (surtout si l'enfant est un élément tel que <div> ou <p>) ou black pour la propriété color, or il n'en est rien.
Il faut comprendre que toutes les propriétés CSS ont une valeur initiale définie dans les Spécifications (exemple display vaut inline). Puis elle est parfois écrasée par le navigateur au cas par cas selon les éléments (exemple div {display: block})
Quelques exemples de valeurs initial surprenantes :
display: initial; vaut inlinemax-width: initial; vaut nonewidth: initial; vaut autoposition: initial; vaut staticcursor: initial; vaut autoappearance: initial; vaut nonecolor: initial; vaut canvastext (valeur récente, adaptée aux modes utilisateur Dark et Light).unsetLa valeur unset appliquée à une propriété lui confère la valeur du parent si la propriété peut être héritée ou la valeur initiale dans le cas contraire.
Exemple :
.parent {
display: grid;
margin: 2rem;
color: hotpink;
}
.enfant {
all: unset;
}
Cet exemple est équivalent au code suivant (pour les propriétés concernées) :
.enfant {
display: inline;
margin: 0;
color: hotpink;
}
Dans cet exemple, display et margin ne sont pas héritables donc leur valeur est calculée à initial, mais la couleur, héritable, est transmise.
Quelques exemples de valeurs unset :
max-width: unset; : non héritable donc unset vaut initial qui vaut nonewidth: unset; : non héritable donc unset vaut initial qui vaut autoposition: unset; : non héritable donc unset vaut initial qui vaut staticfont-size: unset; : héritable donc récupère la valeur du parentcolor: unset; : héritable donc récupère la valeur du parent (ici hotpink)Friendly reminder: if you don't need IE/Edge support (e.g. internal tools), you don't have to reset every CSS property individually.
— Benjamin De Cock (@bdc) April 24, 2018
button.lame {
padding: 0;
border: none;
outline: none;
font: inherit;
color: inherit;
background: none
}
button.cute {
all: unset
}
revertLa valeur revert récupère la valeur appliquée par l'Agent Utilisateur.
S'il n'y en a pas, revert devient unset (qui lui-même vaut inherit ou initial selon le cas 🤯)
Exemple :
.parent {
display: grid;
margin: 2rem;
color: hotpink;
}
.enfant {
all: revert;
}
Cet exemple est équivalent au code suivant (pour les propriétés concernées) :
.enfant {
display: block;
margin: 0;
color: hotpink;
}
Dans cet exemple, display vaudra block puisque c'est la valeur attribuée par le navigateur sur l'élément <div>. Le navigateur n'applique pas de valeurs spécifiques au propriétés margin et color donc elles deviendront respectivement initial et inherit.
Quelques exemples de valeurs revert selon l'élément appliqué :
div {display: revert} vaut blockp {display: revert} vaut blockspan {display: revert} vaut inlinetd {display: revert} vaut table-cellinput {display: revert} vaut inline-blockCes valeurs conférées par les navigateurs (User Agent Stylesheet) présentent parfois quelques différences selon les moteurs de rendu.
L'ensemble des propriétés et valeurs décrites au sein de cet article (all, inherit, initial, unset et revert) offre une très large panoplie de possibilités de réinitialisation en CSS lorsque cela vous est nécessaire.
Un Reset CSS proche de la perfection ressemblerait à ces deux simples lignes :
* {
all: unset;
display: revert;
}
Ces deux lignes résument à elles seules les consignes suivantes :
display, nous souhaitons que la navigateur continue d'appliquer ses valeurs préférées (donc block, inline, etc. selon l'élément).Si la perfection existait, nous serions en présence du Reset ultime. Mais dans la vraie vie il y a aussi des images, des vidéos, des iframes, des tableaux, des SVG, etc.
Voilà pourquoi je ne saurais que vous conseiller de vous intéresser au projet "The New CSS Reset" maintenu sur Github par l'excellent Ahmad Shadeed et qui apporte les quelques affinages nécessaires.
![]()
Et vous, quelle est votre position par rapport à ces problématiques d'héritages, de cascades et de reset CSS ?
Avez-vous des solutions à tout faire que vous nous recommanderiez ?