Sass et interpolation
Tout ce que vous avez toujours voulu savoir sur l'interpolation dans Sass, expliqué par Kitty Giraudel. Une bonne introduction toute simple et les cas d'utilisation principaux, pour interpoler avec plaisir.
Depuis quelque temps vous jouez avec Sass, ça vous plaît bien et ça répond à la plupart de vos besoins, mais il y a ce truc que vous ne comprenez toujours pas très bien : l'interpolation - insérer des valeurs à l'intérieur d'autres valeurs. Eh bien nous allons y mettre un peu de clarté/
Interpo... quoi ?
L'interpolation, qu'on appelle aussi interpolation de variable ou substitution de variable n'est pas une originalité de Sass, en fait on la trouve dans de nombreux langages (PHP, Perl, Ruby, Tcl, Groovy, Unix shells...). On parle souvent d'interpoler une variable ou d'interpoler une expression. Pour le dire vite, interpoler consiste à évaluer une expression ou une chaîne de caractères contenant une ou plusieurs variables, et à remplacer lesdites variables par leurs valeurs correspondantes stockées en mémoire.
Hmmm...
Bon, prenons un exemple. Si vous avez des notions de PHP, ce sera facile à comprendre. Admettons que vous vouliez afficher une chaîne de caractères contenant une variable. La façon habituelle de le faire est :
$description = "awesome";
echo "Tuts+ is " . $description . "!";
Ceci n'est pas une interpolation. C'est une >concaténation de caractères. En gros, on accroche ensemble trois chaînes de caractères : "Tuts+ is "
, "awesome"
et "!"
. Mais nous pourrions tout aussi bien utiliser l'interpolation :
$description = "awesome";
echo "Tuts+ is ${description}!";
Les accolades qui entourent la variable indiquent à PHP d'afficher la valeur de la variable à l'intérieur de la chaîne de caractères. Remarquez au passage qu'il faut des guillemets pour que ça marche en PHP (comme dans la plupart des langages).
Voilà pour l'interpolation de variable ou d'expression. Vous pouvez utiliser la concaténation ou l'interpolation, comme vous voulez, mais disons que l'interpolation apporte un surcroît de douceur syntactique.
Et Sass là-dedans ?
Regardons maintenant comment fonctionne la substitution de variable dans Sass.
Les noms de variables, dans Sass comme dans PHP, portent le préfixe $
. La comparaison s'arrête là, car pour l'interpolation Sass et PHP se comportent différemment. La raison est que Sass est construit en Ruby, qui utilise #{}
pour la substitution d'expression.
En Sass vous feriez :
$description: 'awesome';
@warn "Tuts+ is #{$description}!";
Remarquez qu'avec Sass le signe $
n'est pas supprimé dans le nom de la variable, contrairement à PHP. Celle-ci est simplement encapsulée, avec $
, à l'intérieur de #{}
. À remarquer également le fait que vous pouvez interpoler n'importe quel type de variable, pas uniquement des chaînes de caractères. Par exemple :
$answer: 42;
@warn "The Answer to the Ultimate Question of Life, the Universe, and Everything is #{$answer}.";
Quand utiliser l'interpolation ?
Maintenant que nous avons compris ce qu'est l'interpolation et comment elle fonctionne dans Sass, il est temps de passer à des cas d'utilisation. Pour le premier, nous allons reprendre ce que nous venons de faire avec la directive @warn
, qui affiche un contenu dans la console.
Chaînes de caractères
Supposons que vous ayez une map de couleurs que vous avez appelée $colors
(une map est une variable qui emmagasine des paires de clés/valeurs, comme une table ou un array), mais que vous en ayez assez de taper map-get($colors, ...)
: vous construisez une petite fonction color()
qui va chercher la couleur correspondant à la clé passée en argument. C'est très simple :
// _config.scss
$colors: (
'primary': tomato,
'secondary': hotpink,
);
// _function.scss
@function color($key) {
@return map-get($colors, $key);
}
// _component.scss
.el {
background-color: color(primary);
}
Pas mal, mais il faudrait y ajouter un message d'alerte au cas où la clé n'existerait pas, ou si vous faites une faute de frappe (au passage, cette introduction au traitement des erreurs peut vous intéresser). Pour ce faire, on utilise la directive @warn
à l'intérieur de la fonction color()
.
@function color($key) {
@if not map-has-key($colors, $key) {
@warn "Key not found.";
}
@return map-get($colors, $key);
}
OK, et maintenant si vous avez envie d'identifier la clé qui n'a pas été trouvée :
@function color($key) {
@if not map-has-key($colors, $key) {
@warn "Key `#{$key}` not found.";
}
@return map-get($colors, $key);
}
Et boum, voilà notre interpolation de variable. Appeler color(awesomeness)
enverra le message suivant dans la console : Key 'awesomeness' not found
.
C'est bien, mais nous ne connaissons pas réellement le contexte. Pour nous aider à l'avenir, nous pourrions ajouter le nom de la table (map) à l'intérieur du message d'erreur.
@function color($key) {
@if not map-has-key($colors, $key) {
@warn "Key `#{$key}` not found in $colors map.";
}
@return map-get($colors, $key);
}
Dans ce cas, puisque la variable $colors
n'a pas été interpolée, son nom sera affiché dans le message d'erreur. Key 'awesomeness' not found in $colors map.
Fonctions CSS
Jusqu'à présent, nous avons vu le cas d'utilisation le plus banal de substitution de variable : afficher le contenu d'une variable à l'intérieur d'une chaîne de caractères. C'est un bon exemple, mais il y en a un encore meilleur : les variables dans les fonctions CSS, par exemple dans calc()
.
Admettons que vous vouliez dimensionner votre container principal en fonction de la largeur du sidebar. Comme vous êtes un développeur consciencieux, vous avez enregistré cette largeur dans une variable, vous pouvez donc écrire ceci :
$sidebar-width: 250px;
.main {
width: calc(100% - $sidebar-width);
}
Et là, surprise ! Ça ne marche pas. Il n'y a pas d'erreur Sass, mais votre container n'est pas dimensionné comme il faut. Si vous inspectez ses styles avec DevTools, vous verrez ceci - rayé parce que c'est invalide : t.main {width: calc(100% - $sidebar-width);}
Penchons-nous là-dessus une minute. calc()
est une fonction CSS, pas une fonction Sass. Cela signifie que Sass interprète l'expression tout entière comme une chaîne de caractères. Pour le vérifier, vous pouvez faire :
$type-of-expression: type-of(calc(100% - $sidebar-width));
// string
Puisque c'est une chaîne de caractères, Sass se comporte comme précédemment avec $colors
dans notre chaîne de caractères @warn
, donc $sidebar-width
est considéré comme une chaîne de caractères et elle est affichée comme telle. Mais ce n'est pas ce que nous voulons, n'est-ce pas ? Alors interpolons !
t.main {
width: calc(100% - #{$sidebar-width});
}
Maintenant, lorsque Sass compile la feuille de style, il remplace #{$sidebar-width}
par la valeur associée à $sidebar-width
, dans ce cas 250px
, ce qui donne l'expression CSS valide suivante : t.main {width: calc(100% - 250px);}
Mission accomplie ! Nous avons parlé de calc()
, mais nous aurions pu utiliser url()
, linear-gradient()
, radial-gradient()
, cubic-bezier()
et toute autre fonction CSS native, y compris toutes les pseudo-classes.
Par exemple :
t@for $i from 1 through $max {
.el:nth-of-type(#{$i}) {
// ...
}
}
C'est un cas que vous avez sans doute déjà rencontré : l'utilisation d'une boucle for
en conjonction avec les sélecteurs :nth-*()
. Là encore, vous devez interpoler la variable pour qu'elle apparaisse correctement dans le CSS final.
En résumé : Sass traite les fonctions CSS comme des chaînes de caractères, ce qui vous impose d'échapper toute variable utilisée dans la fonction si vous voulez que le CSS soit correct.
Directives CSS
Passons maintenant à un autre cas intéressant d'utilisation de l'interpolation, les directives CSS comme @support
, @page
et surtout @media
.
Il s'agit ici de la façon dont Sass analyse les directives CSS @
, en particulier la directive media. J'ai mis mon nez dans le code Sass et, bien que mon niveau de Ruby ne soit pas top, j'ai trouvé des choses intéressantes :
def query_expr
interp = interpolation
return interp if interp
return unless tok(/\(/)
res = ['(']
ss
res << sass_script(:parse)
if tok(/:/)
res << ': '
ss
res << sass_script(:parse)
end
res << tok!(/\)/)
ss
res
end
En gros, les premières lignes disent à Sass de retourner la media query s'il y a une expression interpolée, ou une erreur, sauf s'il trouve une ouverture de parenthèse, auquel cas il doit continuer et analyser le reste. Essayons avec un exemple :
$value: screen;
@media $value {
// ...
}
Sans surprise, c'est un échec :Invalid CSS after "@media": expected media query (e.g. print, screen, print and screen), was "$value{"
Comme l'indique le message d'erreur, Sass attendait une media query. Il faut interpoler votre variable si elle arrive juste après @media
, par exemple :
$value: screen;
@media #{$value} {
// ...
}
Comme nous l'avons déduit de notre petite escapade en Ruby, si @media
est directement suivi de parenthèses ()
, nous n'avons plus besoin d'interpoler la variable parce que Sass évaluera tout ce qui se trouve à l'intérieur des parenthèses. Par exemple :
$value: 1336px;
@media (max-width: $value) {
// ...
}
Dans ce cas, Sass évalue l'expression (max-width: $value)
, la transforme en (max-width: 1337px)
pour aboutir à un résultat CSS valide, il n'y a donc pas besoin d'échapper la variable.
Pourquoi les développeurs de Sass l'ont-ils conçu ainsi, j'ai posé la question à Nathan Weizenbaum et voici sa réponse :
D'une manière générale, nous ne voulons pas autoriser l'utilisation de variables "brutes" là où les expressions SassScript ne peuvent être utilisées. C'est le cas des media queries, car SassScript peut être ambigu dans ce contexte (en particulier les maps).
Sélecteurs
Un dernier exemple de cas d'utilisation où nous aurons besoin d'interpoler les variables Sass : Lorsque nous utilisons une variable comme sélecteur, ou comme partie d'un sélecteur. Ce n'est peut-être pas un cas très courant, mais il n'est pas non plus si rare :
$value: custom;
selector-$value {
property: value;
}
Malheureusement, ça ne marche pas :Invalid CSS after "selector-": expected "{", was "$value {"
C'est à peu près la même raison que pour les media queries. Sass a sa propre façon d'analyser les sélecteurs CSS. S'il rencontre quelque chose d'inattendu, par exemple un $
non échappé, il crashe.
Heureusement, ce problème est facile à résoudre, et vous connaissez déjà la solution : l'interpolation !
$value: custom;
selector-#{$value} {
property: value;
}
Conclusion
En définitive, l'interpolation Sass n'est pas si simple. Il existe des cas où vous devrez échapper votre variable, d'autres où ce ne sera pas nécessaire. À partir de là, il y a deux façons de faire :
- soit vous attendez un message d'erreur, et vous échappez
- soit vous échappez tout mais avec des valeurs CSS régulières (dont vous savez qu'elles fonctionnent).
J'espère en tout cas que cet article vous aura permis de mieux comprendre comment fonctionne l'interpolation de variable.