Voici une magnifique subtilité du CSS, mais parfaitement logique quand-même :

Que se passe-t-il si on met !important dans une variable ?

Voyons ça :

div {
  --color: red !important;
  color: var(--color);
  color: yellow;
}

À votre avis ça donne quoi ça en CSS ? Le point litigieux, c’est bien-sûr la présence du !important
Sauf qu’il est à savoir que le !important n’est pas sur une déclaration de propriété, mais sur une déclaration de variable CSS.

Or, une variable ne peut contenir qu’une valeur. La valeur contenue dans --color est donc red. Il faut savoir que le !important ne fait pas partie de la valeur : il s’agit d’un élément de langage qui permet de jouer sur la spécificité, comme un drapeau (un flag).

Résultat : la couleur finale du texte sera décidée par le vainqueur des deux déclarations de color juste en dessous. Comme celle-ci ont la même spécificité, c’est la dernière qui gagne.

Conclusion : le texte est en jaune.

!important n’est pas sans effet !

Maintenant, utiliser un !important dans les variables CSS n’est pas sans effet pour autant.
Regardons :

div {
  --color: red !important;
  --color: blue;
  color: var(--color);
}

Ici, aucune ambiguïté sur la déclaration de la couleur, puisqu’il n’y en a qu’une seule : color prendra la valeur contenue dans la variable --color. Oui mais laquelle ?

Réponse : celle dont la déclaration a la spécificité la plus grande ! Autrement dit, la première.
Conclusion : ça donnera du rouge.

Il convient donc de distinguer deux cas de figure :

div {
  --color: red !important;
  color: var(--color);
  color: blue;
}
div {
  --color: red;
  color: var(--color) !important;
  color: blue;
}

Dans le premier cas, les deux déclarations de couleur ont la même spécificité. C’est donc la dernière qui gagne : le texte sera bleu.
Dans le second cas, c’est la première déclaration qui a la plus grande spécificité (à cause du !important).

Peu importe qu’elle ait une variable ou non, c’est elle qui gagne : cela donnera du rouge.

Le conflit ? Quel conflit ?

Dans sa conclusion, l’article dit qu’il y a deux niveaux de « scope » (traduction ?) sur lesquelles sont appliqués le !important. Une sur les variables, et une sur les propriétés. Je ne suis pas d’accord avec ça : pour moi il n’y a en a qu’une seule. En revanche, il a deux déclarations différentes à considérer : une sur une variable, et une sur une propriété.
Maintenant, dans les deux cas, on peut ajouter un !important si l’on veut. Et comme ce sont deux choses différentes, elles n’entreront pas en conflit.

Appliquer deux !important à deux propriétés différentes n’est pas un problème :

div {
  font-weight: bold !important;
  color: red !important;
}

Ces deux choses n’entrent pas en conflit.
Par conséquent, les deux choses suivantes non plus :

div {
  --color: bleu !important;
  color: red !important;
}

Notez que ci-dessus, la variable --color est déclarée, mais jamais utilisée, donc aucun conflit à l’horizon.

Dans ce qui suit, toujours aucun conflit :

div {
  --color: bleu !important;
  color: var(--color) !important;
}

Tout est clair : la variable reçoit du bleu. La couleur reçoit la variable. Donc la couleur finale est bleue.

Et même s’il y avait plusieurs déclarations de la variable, celle qui gagne est celle de la plus grande spécificité. Ensuite, c’est la déclaration de la couleur avec la plus grande spécificité qui gagne. Ainsi l’exemple suivant donnera du rouge :

div {
  --color: red !important;
  --color: blue;
  color: var(--color) !important;
  color: green;
}

Parmi les variables, c’est le rouge qui gagne sur le bleu.
Parmi les propriétés, c’est celui avec le var() qui gagne sur celui avec le vert. Or la variable contient du rouge. Donc le texte sera rouge.

Applications à un contexte plus large

Tout ceci est évidemment à mettre dans un contexte plus global avec des déclarations sur des sélecteurs de spécificité différente.

div {
  --color: blue !important;
  color: var(--color);
}

div#foo {
  --color: orange;
  color: var(--color);
}

Ici ça sera… Bleu !

Regardons la couleur pour commencer. La couleur reçoit ce que contient la variable. Aucune ambiguïté. Et la règle qui s’applique est celle dans le second bloc. Pourquoi ? Parce que le sélecteur y est plus spécifique.

Maintenant que contient cette variable ? Cette variable est celle qui est la plus spécifique. Donc celle avec le !important. Donc le bleu.

En effet, même s’il y a un #id sur le sélecteur du second bloc, la variable --color ne contient pas de l’orange, mais du rouge : le !important sur une propriété est plus spécifique que la même propriété placée sur un ID (Spécificité de 1-0-0-0-1 au lieu de 0-1-0-0-1).

Par contre, il en est différent si le !important sous le deuxième bloc avait été sur la variable au lieu de la propriété :

div {
  --color: blue;
  color: var(--color)!important;
}

div#foo {
  --color: orange;
  color: var(--color);
}

Dans ce cas, la variable qui s’applique est l’orange dans le second bloc (car cette variable est plus spécifique à cause de l’ID dans le sélecteur). Le texte est donc orange.

Notons de façon anecdotique que la propriété qui s’applique est bien celle du premier bloc (car cette propriété a un !important).

Le vocabulaire est important : la propriété et la variable sont deux déclarations différentes. Elles ont chacune leur spécificité (au sens de « priorité CSS »).

Si je ne conserve que les éléments qui s’appliquent réellement, ça donne ceci :

div {

  color: var(--color) !important;
}

div#foo {
  --color: orange !important;

}

La couleur reçoit une variable, comme indiqué dans le premier bloc. Mais cette variable a une valeur assignée dans le second bloc seulement.

Ça vous semble casse gueule ? Attendez la suite.

Cas des sélecteurs enfants

Jusqu’à maintenant, même si on utilise deux sélecteurs différents, ils ciblent le même élément HTML.
Poursuivons les complications par quelques exemples pour montrer ce qui se passe avec avec des sélecteurs enfants.

Voyons :

div {
  --color: red !important;
  color: var(--color) !important;
}

div a {
  --color: orange;
  color: black;
}

La question : quelle est la couleur du lien ?
Réponse : noire.

Noire ? Oui, noire. La déclaration tout à la fin.

En effet, le premier bloc a beau avoir des !important sur toutes les propriétés, ils s’appliquent au div, tout comme leur spécificité.
Or, la spécificité n’est héritée que si la valeur sur laquelle elle s’applique est également héritée. En réalité, seule la propriété la plus spécifique est alors héritée (et la spécificité devient inutile).

Ainsi, pour savoir ce qui s’applique à notre a, on regarde le code destiné au a. C’est alors lui qui s’appliquera, s’il y en a. Et le a possède une déclaration de couleur : color: black;.
L’hérédité ne s’applique donc plus : on applique le color: black;, point barre.

L’hérédité ne s’applique qu’en l’absence de déclaration.

Ainsi, si on avait mis :

div {
  --color: red !important;
  color: var(--color) !important;
}
div a {
  --color: orange;
}

Alors, le texte serait rouge. La couleur du a serait héritée du div, et contiendra var(--color), qui, dans le contexte du div (le contexte hérité, donc), est rouge.

Pas orange ?

Non : l’orange est du contexte de a. Or la propriété de couleur est héritée du contexte de div, et là, dans son contexte, c’est bien du rouge que contient la variable. Ce qui est héritée, c’est la propriété et sa valeur.

On aurait même pu mettre un !important dans le second bloc, cela ne changera rien :

div {
  --color: red !important;
  color: var(--color) !important;
}
div a {
  --color: orange!important;
}

Cela rester rouge, pour la même raison : la couleur est héritée, même si elle provient d’une variable.

Si l’on voulait du orange, on doit ajouter un color: var(--color) dans le second bloc. Comme ça, la couleur du a est déclarée et non-héritée. Mais il faut bien conserver la déclaration de la variable dans le a, sinon la variable n’est plus déclarée et elle hérite.

Il faut retenir que la variable s’applique sur les propriétés du même contexte. Le parent transmet une propriété ? D’accord, mais sa valeur est également transmise, même si c’est une variable.

Par contre, dans le code suivant, la variable est bien héritée :

div {
  --color: red !important;
}
div a {
  color: var(--color);
}

Le a sera rouge. Ici, l’on ne déclare pas la variable --color dans le a. Elle est donc héritée. Or son parent lui dit que la variable contient du rouge. Donc le texte est en rouge.

Et avec des variables partagées sur plusieurs propriétés ?

La blague n’est pas terminée !
On arrive ici à quelque chose de très drôle : quid des variables appliquées à plusieurs propriétés, l’une déclarée et l’autre héritée ?

div {
  --color: white;
  color: var(--color);
}
div a {
  --color: black;
  background-color: var(--color);
}

Ça va donner quoi ?
Ici le texte dans le a sera bien en blanc sur fond noir !

Décomposons :

  • La couleur du texte : la couleur n’est pas déclarée sur le a. Elle est donc héritée. Et l’héritage contient du blanc : c’est donc du blanc.
  • Le couleur du fond : le fond est déclaré sur le a. Le fond contient var(--color). Et --color, dans ce contexte, contient du noir. Le fond est donc noir.

Si l’on n’avait pas déclaré le --color: black;, alors la variable aurait été héritée, et on aurait eu du blanc sur fond blanc. Mais comme on l’a redéfini dans le contexte du a, elle prend une nouvelle valeur… dans son contexte seulement.

Notez que dans ce dernier exemple on n’a mis aucun !important : normal, car la spécificité n’est pas héritée si la propriété sur laquelle elle est appliquée n’est pas elle-même héritée.

On aurait pu en mettre partout dans le premier bloc, on aurait tout de même eu du blanc sur du noir.

Conclusion

Pour moi tout est logique, même si c’est parfois compliqué.
Il faut retenir :

  • !important s’applique à la déclaration de la variable. Mais elle n’est pas dans la valeur assignée à la variable.
  • une variable CSS peut être déclarée plusieurs fois. Tout comme on peut déclarer plusieurs fois une couleur. Dans ces conditions, c’est la déclaration la plus spécifique qui est retenue. Si toutes ont la même spécificité, la dernière est retenue. C’est du CSS de base.
  • concernant l’hérédité, rien n’est nouveau là non plus : les valeurs, même avec un !important ne sont transmis aux éléments enfants que si ces derniers ne les redéclarent pas. Ça vaut pour les variables comme pour les propriétés CSS.

Bref, c’est compliqué, un peu curieux, parfois casse-gueule, mais rien de nouveau.

Et rien de différent d’autre langages de prog non plus : les variables ont leur contexte et peuvent être redéfinies dans des boucles imbriquées d’où elles ne sortent pas (pensez au let en JS par exemple : deux variables redéclarées avec le même nom auront leur propre contexte :

let a = "bar";

function newScope() {
    let a = "foo";
    console.log(a); // "foo"
}

console.log(a); // "bar"

Ah et : ici on ne parle que de la propriété color. Je vous laisse imaginer ce que ça donne si l’on utilise des variables dans display, flex ou position, le tout assaisonnée de sélecteurs comme a ~ a et de pseudo-classes :not() ou :placeholder-shown. Ça promet, non ?

i
Log4Shell est une nouvelle de ces failles informatique qui touchent des milliards d’appareils, et qui permettent potentiellement à n’importe qui d’en prendre le contrôle. Elle est décrite comme « la plus grosse et la plus critique des failles unique de la décennie » (« the single biggest, most critical vulnerability of the last decade »). La CISA et la BSI (respectivement les ANSSI américaines et allemandes) l’ont qualifié de « extrêmement critique ». Le gouvernement Canadien a lui décidé de fermer tous ses sites du service publics en attendant que ça soit corrigé.

Cette faille peut affecter non seulement un ordinateur ou un SI d’entreprise, mais aussi votre téléphone, imprimante connectée, ou de plus en plus votre frigo, télé, voiture, porte de garage ou four à micro-ondes connecté.

Pour comprendre la faille, je vous renvoie à ce thread sur Twitter.

Si je devais résumer : cela concerne un module utilisé par les outils de gestion des journaux système d’une installation informatique (les « logs »).
Beaucoup de programmes utilisent les logs (tous en fait, plus ou moins). Certains peuvent alors y mettre des variables, comme ${date}, qui seront alors remplacés par l’outil qui gère le journal (en l’occurrence, ${date} sera remplacé par la date courante au moment de l’accès au journal).

Or il se trouve que certaines variables sont renvoyées en cascade vers d’autres modules qui peuvent y placer d’autres informations. En particulier, ici un module (JNDI) qui vient remplacer une des variables par des données arbitraires… Par exemple une URL au bout de laquelle n’importe qui peut placer du code malicieux.

Ce problème est signalé depuis 2016.

Le module, utilisé par toutes les big-tech (Microsoft, Apple, Google, Tesla…) est maintenu par… Trois gus qui font ça sur leur temps libre :

Les dépendances en informatique.
Le XKCD qui va bien (source) : « toute l’infrastructure informatique moderne » soutenue par « le petit projet maintenu gracieusement depuis 2003 par un inconnu au fin fond de nulle part ».

Ces trois personnes n’y sont pour rien : les erreurs arrivent et ils vont sûrement corriger ça très vite. Le problème que je vois surtout, c’est que des sociétés dont la valorisation cumulée doit bien dépasser 5 trillions de dollars, n’ont pas gratifié ces dév ne serait-ce que de 1 000 $ durant du tout ce temps.

Ce manque de reconnaissance est à vomir.

D’ailleurs je n’aurais qu’une chose à dire : si votre architecture fonctionne comme ça : soutenez ceux qui vous permettent de bosser ! C’est la moindre des choses.

Il ne fait pas beaucoup de doutes que quelqu’un qu’on paye pour s’occuper de quelque chose à temps plein sera plus efficace pour corriger les bugs que quelqu’un qu’on laisse bosser sur son seul temps libre de temps en temps.

Je vois deux choses à retenir ici :

  • en profitant du boulot des autres, on hérite aussi de leurs erreurs. Ce n’est pas grave, mais faut en être conscient : on est dépendant d’eux ;
  • en abusant des lib et outils sans prodiguer ne serait-ce qu’un simplement soutien ponctuel, faut pas s’étonner que le module reste du boulot fait irrégulièrement et sur du temps libre.

Qu’on soit clair : le boulot d’un amateur sur son temps libre peut être très bon. Ce que je veux dire en vrai, c’est que si l’on construit son empire sur ce boulot, il faut peut-être s’assurer que ce code « amateur » soit solide. Et en l’occurrence, ça passe par un soutien suffisant à celui qui s’occupe de ce code ou de son audit.

Les dév mangent aussi (je vous jure !), et le temps qu’ils passent à bosser ailleurs pour pouvoir manger, ils ne le passeront pas à corriger l’outil qui maintient votre empire en place. Tout simplement parce que votre empire, il s’en fout autant que vous vous foutez de lui en ne donnant pas ne serait-ce qu’un micro-pourcent de ce que vous distribuez aux actionnaires chaque année. Et c’est bien normal.

Or, n’importe quel appareil numérique aujourd’hui fonctionne grâce à des dizaines, des centaines de ces modules développées par des passionnés et des bénévoles. Ouvrez n’importe programme ou n’importe quelle application et allez dans les « crédits » ou les « à propos ». Vous y verrez le nom des personnes qui vous rendent la vie plus simple, chaque jour, y compris, si c’est fait correctement, les outils qui permettent à ces applications de fonctionner, et leurs auteurs.

Et les exemples où l’industrie se fiche de remercier ces gens et s’en retrouve à genoux également : on se souviendra de Heartbleed, la faille de 2014 qui avait fait un grand bruit parce qu’il touchait tous les sites sécurisés (SSL) et tous les outils, y compris ceux des Gafam, et avait mis en lumière le manque de moyens de la fondation OpenSSL qui s’occupe de ça. Quelques années après, OpenSSL ne tourne toujours qu’avec environ 50 000 $ par an…

ÉDIT : ah oui… Ceci est aussi à mettre en perspective avec cet article de ce journaleux qui demande aux dév d’arrêter de vouloir être payés un salaire à la hauteur de leur compétences.