Less.js: Ajout de la prise en charge des variables "default" (similaire à !default dans SASS)

Créé le 3 déc. 2013  ·  35Commentaires  ·  Source: less/less.js

Nous envisageons de passer de SASS à LESS, mais le principal obstacle pour nous est l'absence d'une fonctionnalité "variable par défaut" (voir http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_defaults_)

Cette fonctionnalité est extrêmement utile lors de la distribution de votre code SASS sous forme de bibliothèque où les variables servent en quelque sorte d'"API" pour vos utilisateurs. Dans un tel cas d'utilisation, tout ce que l'utilisateur a à faire est de s'assurer que ses variables sont incluses à l'avance pour remplacer les valeurs par défaut qu'il souhaite modifier, et le tour est joué ! pas de manipulation avec l'ordre des (éventuellement) nombreux fichiers qui peuvent contenir les variables qui doivent être remplacées.

J'ai commencé à travailler sur une implémentation, je n'ai pas encore écrit de tests mais j'espère que nous pourrons utiliser cette pull request comme point de départ pour la discussion : https://github.com/less/less.js/pull/1705

J'ai choisi la syntaxe suivante :

?foo: value;

par opposition à la méthode SASS :

<strong i="13">@foo</strong>: value !default;

pour 2 raisons - c'est plus concis, et la syntaxe !default peut présenter des problèmes potentiels avec l'analyse d'expression sur le côté droit à l'avenir (mais je peux me tromper à ce sujet).

L'implémentation que j'ai proposée était étonnamment simple - j'espère que je n'ai rien manqué d'important. J'apprécierais vraiment tout commentaire que vous pourriez avoir.

Acclamations,
Phil

consider closing feature request low priority

Commentaire le plus utile

J'allais suggérer l'article que j'ai écrit et j'ai ensuite vu que @seven-phases-max avait un lien vers celui-ci. 😄 Faites-nous confiance, ce que vous demandez existe déjà ! Mais vous devez comprendre l'évaluation variable de Less pour comprendre comment/pourquoi elle existe.

Tous les 35 commentaires

Voir La documentation .


Mise à jour : Suppression de mon commentaire précédent de ce message pour ne pas confondre les futurs visiteurs (ici, j'essayais de répondre à la question "comment définir une variable si elle n'est pas déjà définie" et j'ai raté le point c'est un "problème XY" et la bonne réponse pour un Sass-like !default est : dans Less, vous n'avez besoin de rien de ce genre car la fonctionnalité nécessaire ("(re)définition après utilisation") est déjà fournie par le fonctionnement des variables Less).

Si vous définissez deux fois la même variable, la dernière déclaration l'emporte et est valide dans toute la portée. Ainsi, une autre option serait de déclarer les variables normalement et de demander à l'utilisateur d'inclure ses variables après votre bibliothèque.

bibliothèque.sans

<strong i="7">@width</strong>: 0;
.mixin {
  width: @width;
}

sans utilisateur :

<strong i="11">@import</strong> "library.less"; //first declaration of <strong i="12">@width</strong>
<strong i="13">@width</strong>: 1; //this will override <strong i="14">@width</strong> defined previously
.class {
  .mixin();
}

se compile en :

.mixin {
  width: 1;
}
.class {
  width: 1;
}

Merci pour les commentaires sur notre PR. Ces solutions seraient réalisables si nous avions une seule chose ou quelques petites choses à consommer. Alors laissez-moi expliquer pourquoi ces solutions ne sont pas réalisables dans notre cas.

Nous créons un vaste cadre de composants implémentés sous la forme d'une hiérarchie de classes. Nous déclarons les variables de chaque composant dans son propre fichier... dans notre thème de base. Nous "dérivons" alors des thèmes d'ici qui veulent modifier ces variables. Les utilisateurs peuvent en outre le faire pour adapter un thème à leurs besoins.

De plus, les variables « dérivent » souvent d'autres variables. Le plus évident est la couleur de base. Nous avons passé beaucoup de temps à discuter d'alternatives aux problèmes de grandes hiérarchies de classes de composants utilisant une propagation agressive de valeurs variables combinée à des thèmes et à leur propre comportement d'héritage.

De loin, la meilleure solution à ces problèmes est de _toujours_ définir les variables comme "!default" (en termes Sass). Cela permet aux utilisateurs d'entrer simplement en premier et de définir leurs valeurs avant que ces valeurs ne soient utilisées pour calculer d'autres valeurs. Le timing est alors assez simple à gérer pour nos utilisateurs. Comme c'est toujours le cas pour chaque variable dans tous nos thèmes, les difficultés de syntaxe comme celles suggérées ci-dessus seraient assez lourdes et également sujettes aux erreurs.

Nous aimerions ajouter d'autres fonctionnalités à Less au fur et à mesure que nous continuons. Je pense que notre cas d'utilisation (exceptionnellement ?) À grande échelle pourrait être une validation utile de ce qui est nécessaire pour faire évoluer le langage / l'ensemble de fonctionnalités.

J'espère que vous envisagerez de fusionner ceci ou que vous proposerez des alternatives de nature plus déclarative. Exploiter les astuces de portée ne fonctionnera vraiment pas pour nous.

Merci encore pour votre temps et votre considération !

Il semble que vous feriez mieux de définir des mixins au lieu de classes droites. Ensuite, les variables peuvent être remplacées tardivement dans votre feuille de style principale ou un autre thème dérivé. C'est ainsi que je peux remplacer des choses comme mes largeurs de gouttière dans mes grilles après les avoir importées.

Juste pour clarifier, nous ne parlons ici que de variables. Et nous n'avons pas de feuille de style principale - nous en avons une par classe par thème. :)

Je ne suis peut-être pas en train de suivre votre suggestion, mais à titre d'exemple, l'un de nos fichiers de variables de thème ressemble à ceci :

$panel-base-color: $neutral-light-color !default;
$panel-header-color: $base-color !default;
$panel-frame-border-width: 1px !default;
$panel-header-font-size: round($font-size * 1.15) !default;
$panel-body-border-color: $neutral-dark-color !default;

$panel-light-header-color: #000 !default;
$panel-light-header-background-color: #fff !default;
$panel-light-tool-background-image: 'tools/tool-sprites-dark' !default;
$panel-light-body-border-color: $neutral-dark-color !default;
$panel-ignore-frame-padding: true !default;

Ce fichier configure les différentes valeurs de panneau pour un thème qui a une chaîne de thèmes de base de 4 profondeurs. Ces thèmes de base ont leurs propres valeurs de variables pour Panel, mais celles-ci sont chargées en premier, donc remplacez-les. Sauf si un thème utilisateur dérive de ce thème et fournit des valeurs qui lui sont propres.

Ce motif se répète _beaucoup_ :)

D'accord, pourquoi ne pas simplement remplacer @panel-base-color dans votre feuille de moins principale ? Les variables LESS sont globales, donc celle qui arrive en dernier est la gagnante.

<strong i="7">@import</strong> 'theme.less';

@panel-base-color: red;

Désormais, tout ce qui est utilisé dans le thème sera remplacé. Si personne ne remplace cela dans votre chaîne d'importation, alors ce qu'il a été initialement défini sera la valeur par défaut.

Nous n'avons pas de "feuille principale moins" :) J'apprécie votre aide et vos suggestions, mais nous avons eu de nombreuses discussions à ce sujet en interne et elles ont tendance à durer un peu. Qu'il suffise de dire que nous pensons avoir besoin d'un paramètre de variable par défaut, comme nous l'avons proposé ici. Cette fonctionnalité existe dans Sass et nous l'avons trouvée très utile pour réduire la complexité et offrir aux utilisateurs une flexibilité dans la configuration de nos thèmes.

Je suis curieux de savoir si tout cela signifie que cette pull request ou un dérivé similaire serait jamais accepté ? Nous serions heureux de modifier la syntaxe si cela est répréhensible.

Nous n'avons pas de "feuille principale moins" :)

Remplacez simplement "votre feuille de moins principale" dans les réponses ci-dessus par "l'une des feuilles de moins de l'utilisateur". Jusqu'à présent, il semble que SASS !default (quelle que soit sa syntaxe) ait été inventé pour résoudre un problème qui n'existe pas dans LESS.
@dongryphon , je peux me tromper bien sûr, mais vous n'avez pas encore expliqué pourquoi vous ne pouvez pas utiliser la solution suggérée par @SomMeri et @Soviut.

Pour le dire autrement, imaginez si vous n'importiez pas et aviez à la place toutes vos variables dans la même feuille. C'est effectivement ce que fait l'importation. Donc, dans cette situation, si vous aviez deux déclarations de variables portant le même nom, la dernière de la feuille gagnerait.

@base-color: green;

div {
    background: @base-color;
}

@base-color: red;

Parce que la couleur de base est déclarée à nouveau à la fin, la couleur de base utilisée dans la div sera rouge. Il compilera pour :

div {
    background: red;
}

Cela s'est produit avant, en fait, je crois qu'il a même été implémenté et nous avons eu une demande d'extraction de gars qui le voulaient dans bootstrap et ils ont convenu après quelques discussions que c'était une fonctionnalité inutile en moins.

La seule chose qu'il vous permet de faire est que les utilisateurs définissent des variables avant l'importation. Si les utilisateurs définissent des remplacements après, cela fonctionne comme s'il avait une valeur par défaut. C'est parce que même dans le fichier importé, il prendra la dernière définition de la même manière que css, même s'il est défini après utilisation.

Ce n'est pas un fardeau pour les utilisateurs d'une bibliothèque de dire qu'ils remplacent les variables après les importations (ou importent un fichier de variables après les importations) par rapport à l'ajout de plus de syntaxe à moins... nous pensons que c'est l'un des avantages de moins, que vous pouvez faites la même chose que sass mais la plupart du temps avec une syntaxe plus simple.

C'est contre ce qu'un programmeur JavaScript pourrait penser, mais l'idée sous-jacente est que c'est plus proche de css.

Pouvez-vous expliquer pourquoi il n'est pas possible ou souhaitable de demander aux consommateurs d'envisager de commander ? Nous accepterons les fonctionnalités avec des cas d'utilisation forts mais nous devons être rigoureux pour éviter les surcharges de langage et de complexité.

Voir #1109 #1104 #313

Et juste pour clarifier, nous ne disons pas "non" par tous les moyens. Nous essayons de lui montrer que cette fonctionnalité existe peut-être déjà.

J'ai ajouté quelques informations à less-docs

Fermeture comme fonctionnalité déjà disponible (dans "Less way") (http://lesscss.org/features/#variables-feature-default-variables).

Je pense que cette confusion revient souvent parce que, bien qu'il y ait un chevauchement de _syntaxe_, SASS "s'exécute" de haut en bas, mais LESS ne le fait pas, et fonctionne plutôt comme CSS. LESS est comme CSS+ (CSS avec des fonctionnalités supplémentaires), et SASS est comme "PHP avec la syntaxe CSS". Je me demande s'il y a un moyen (si nécessaire) de faire cette distinction.

Pouvez-vous expliquer pourquoi il n'est pas possible ou souhaitable de demander aux consommateurs d'envisager de commander ? Nous accepterons les fonctionnalités avec des cas d'utilisation forts mais nous devons être rigoureux pour éviter les surcharges de langage et de complexité.

Parce que nous apprenons aux utilisateurs (et aux consommateurs) de la bibliothèque à ne jamais modifier eux-mêmes les codes de la bibliothèque. La fonctionnalité !default manquante signifie que nous devons faire l'une de ces deux choses - toutes deux tout aussi mauvaises imo :

  • Les utilisateurs doivent modifier eux-mêmes la feuille de style de la bibliothèque afin de modifier les variables où elles sont déclarées (ce qui est un peu interdit, ce qui rend les mises à jour de la bibliothèque plus difficiles) ou,
  • La bibliothèque doit fournir deux feuilles de style : une pour les variables par défaut et une autre pour les règles réelles. Ensuite, l'utilisateur doit @importer la première, puis déclarer ses propres variables, puis @importer la seconde. C'est plus complexe que ça ne devrait l'être, surtout pour garder ces deux fichiers à la même version.

L'approche sass signifie qu'une bibliothèque n'a qu'à fournir un seul fichier, que l'utilisateur pourra ensuite personnaliser.

my-color: red;
<strong i="15">@import</strong> "./my-library.less";

Au lieu de:

<strong i="19">@import</strong> "./my-library-variables.less";
my-color: red;
<strong i="20">@import</strong> "./my-library-rules.less";

Je pense que vous devriez reconsidérer cette question.

@arcanis En fait, je ne vois pas pourquoi vous pensez que:

La bibliothèque doit fournir deux feuilles de style : une pour les variables par défaut et une autre pour les règles réelles.
L'approche sass signifie qu'une bibliothèque n'a qu'à fournir un seul fichier

Dans Less, c'est absolument le même design de bibliothèque :

<strong i="11">@import</strong> "./my-library.less";
@my-color: red;

Je suppose que vous manquez simplement la chose "Lazy-Loading" (et exactement le même exemple est utilisé dans les docs pour dire no, thanks! à !default ).

Je suppose que vous manquez simplement la chose "Lazy-Loading"

@seven-phases-max WTF, vous avez raison. Merde, je devrais probablement supprimer toute ma réponse (et je viens de le faire). Vous me dites que vous pouvez importer bootstrap et simplement remplacer des vars spécifiques, et cela fonctionnera simplement?

Je ne vous ai pas cru, et j'ai fait mes propres tests pour le vérifier. Comment ai-je manqué ça?? Je pense que c'est parce que chaque article que j'ai vu sur Bootstrap, qui incluait la personnalisation, recommandait de copier le fichier variables.less et de définir vos propres valeurs, ce qui bien sûr pose des problèmes lorsque davantage de variables sont ajoutées à la bibliothèque. Et je pense que j'avais l'impression que les sélecteurs étaient sortis au point d'importation immédiat. Les variables ont-elles toujours "lazy load" de cette manière ?

Ce doit être la fonctionnalité la plus importante souvent manquée dans Less. Je n'ai jamais vu un article de blog sur Less ou une bibliothèque Less qui mentionne cette fonctionnalité. Même avec tout ce qui se trouve dans le fil ici et la documentation, il n'était pas immédiatement évident de savoir ce que cela signifiait avec les bibliothèques du monde réel. Je pensais que tout le monde disait simplement que vous pouviez remplacer les variables déclarées plus tôt en les définissant plus tard, et je n'ai jamais compris que cela affecterait l'évaluation des variables des documents importés plus tôt.

Je ne peux pas croire que je n'ai jamais eu ça jusqu'à maintenant. Cela change pratiquement tout sur la façon dont je structure mes moins de documents, et il y a une petite mention dans la documentation qui ne montre pas la sortie.

Peut-être que la raison pour laquelle nous recevons autant de demandes pour une fonctionnalité !default dans Less est que peu de gens ont l'intuition de cette fonctionnalité ou la comprennent à partir du bref exemple de la documentation (y compris moi, évidemment).

À tout le moins, merci de l'avoir expliqué à nouveau @seven-phases-max !

Oups. Eh bien, vous avez raison. J'ai mal compris les docs aussi, mon mauvais!

@Soviut tu es un sacré génie ! Je n'avais aucune idée que les déclarations de variables fonctionnent comme ça dans SASS/LESS. Merci!

Je me tape toujours sur le front que ce n'était pas un comportement évident avec tout ce que j'ai écrit dans Less. Je pense que mon problème était que j'avais vu de mauvais exemples d'articles écrits par des gens qui ne comprenaient pas comment le faire.

Notez également : @spikesagal en ce qui concerne votre déclaration : "Je n'avais aucune idée que les déclarations de variables fonctionnent comme ça dans SASS/LESS" -- À ma connaissance, le comportement des variables n'est pas le même entre les deux langages, puisque SASS et LESS évaluent les choses très différemment.

Et ils tuent cette fonctionnalité dans BS4 à cause du passage à Sass... :-1: Toujours assez triste à propos de ce déménagement.

Alors ça commence, la lutte pour plus de variables !default : [twbs/bootstrap#17418]

Eh bien, ils ne peuvent pas tuer la fonctionnalité Less en modifiant les sources SCSS de quelque manière que ce soit. (Techniquement, ce n'est même pas une "fonctionnalité" (comme une sorte de "comportement synthétisé") mais une propriété fondamentale/corollaire de l'évaluation paresseuse). Tant qu'il y a une version Less de BS (c'est juste une question de nombre de personnes prêtes à y contribuer) et qu'il y a au moins une variable globale - vous pourrez toujours remplacer cette variable.

Bien. La v4-alpha officielle de bootstrap a été déplacée vers Sass. Cela, du moins à ma connaissance, tue moins dans leur documentation officielle - et cela signifie également tuer le support de cette fonctionnalité car Sass n'a rien de tel que des variables de chargement paresseux. Ils ne supportent que !default, ce qui, d'une certaine manière, signifie : "Oh oui, nous vous permettons de remplacer nos variables une seule fois de l'extérieur, et seulement si nous le permettons. Donc, si nous oublions de vous donner accès à une variable en omettant simplement le !default, vous êtes à peu près foutu et devez remplacer tout le sélecteur dans vos fichiers.

et cela signifie également supprimer la prise en charge de cette fonctionnalité car Sass n'a pas de variables de chargement paresseux.

Eh bien, cela signifie seulement "ils le tuent dans Bootstrap" (ce qui est évidemment quelque chose hors de la portée de ce fil - tant qu'il n'y a pas de version Less de BS, nous ne devrions pas nous soucier des fonctionnalités de Bootstrap dans _ce_ référentiel, n'est-ce pas ?).

Étant donné que cette discussion portait à peu près sur le remplacement des variables dans bootstrap ...

On s'en tient à moins :sourire:

J'ai un cas d'utilisation pour le dilemme initialement énoncé. Considérez l'exemple simplifié suivant :

  1. J'ai un fichier less qui définit la taille de la police de h1, quelque chose comme ça
    <strong i="8">@font_size__h1</strong> = 30px;
  2. J'ai (faute d'une meilleure expression) un plugin qui contient un fichier LESS séparé qui utilise la déclaration @font_size__h1 . Alors je l'ai <strong i="12">@import</strong> (reference) "path/to/file.less"; . Pas de problème, il est maintenant disponible.
  3. Maintenant, je prends ce "plugin" dans un nouveau site qui n'a pas de @font_size__h1 défini nulle part. À ce stade, il serait bon de dire "Si @font_size__h1 est défini, utilisez cette valeur. Si elle n'est pas définie, utilisez la valeur que je définis ici."

Le point 3 n'est actuellement pas possible, pour autant que je sache.

@theMikeD

Le point 3 n'est actuellement pas possible, pour autant que je sache.

Il semble que vous n'ayez pas lu le fil :

<strong i="10">@import</strong> "path/to/file.less";
<strong i="11">@import</strong> "here.less"; // if it's not defined <elsewhere>, then use the value I define <here>
<strong i="12">@import</strong> "elsewhere.less";

qui bien sûr se réduit à :

<strong i="16">@import</strong> "path/to/file.less"; // <- define default value there
@font-size-h1: foo;          // if it's not defined here then use the value defined above

qui est fondamentalement le même exemple que http://lesscss.org/features/#variables -feature-default-variables

Oui j'ai lu le fil. On dirait que vous ne comprenez pas ce que je demande, car votre exemple a ma question à l'envers.

qui bien sûr se réduit à :
@import "chemin/vers/fichier.less" ; // <- y définir la valeur par défaut
@font-size-h1 : foo ; // si ce n'est pas défini ici, utilisez la valeur définie ci-dessus

C'est le contraire de ce dont j'ai besoin. Cela écrasera la valeur de @font-size-h1 dans path/to/file.less avec la valeur locale quoi qu'il arrive. Ce dont j'ai besoin, c'est que la valeur dans le fichier local n'est utilisée que si:
a) path/to/file.less n'est pas chargé, ou
b) la valeur de '@ font_size__h1 is not set in path/to/file.less`

IOW la valeur locale de @font-size-h1: foo; sera toujours présente dans le fichier local, mais devrait être remplacée si elle est définie dans path/to/file.less

Dans tous les cas, j'ai trouvé la solution hier soir, qui consiste à attribuer d'abord la valeur locale, puis à placer l'instruction @import à la fin du fichier, pas au début. S'il est trouvé, il écrasera la valeur locale.

Merci quand même.

On dirait que tu ne comprends pas ce que je demande

Je vous suggère plutôt de commencer par quelques bases moins variables pour rafraîchir votre vue :

(parce qu'il semble que vous essayez simplement de penser à tout cela d'une manière impérative semblable à C/PHP, alors que dans Less/CSS, c'est totalement "déclaratif à l'envers").
On en a parlé jusqu'à la mort tout au long de ces années, !default peut ajouter _rien_ de nouveau si on le compare à l'écrasement de la variable native Less. Point final. (Nous y sommes allés plusieurs fois auparavant : si l'on pense qu'il a trouvé un cas d'utilisation pour !default dans Less, cela ne signifie rien d'autre que son incompréhension de la sémantique des variables Less).

Ce dont j'ai besoin, c'est que la valeur dans le fichier local n'est utilisée que si:
a) path/to/file.less n'est pas chargé, ou
b) la valeur de @ font_size__h1 n'est pas définie dans path/to/file.less

Alors c'est tout simplement l'inverse :

@font-size-h1: foo;
<strong i="27">@import</strong> "path/to/file.less"; 

tada !

... c'est là que j'ai atterri, comme je l'ai dit.

J'allais suggérer l'article que j'ai écrit et j'ai ensuite vu que @seven-phases-max avait un lien vers celui-ci. 😄 Faites-nous confiance, ce que vous demandez existe déjà ! Mais vous devez comprendre l'évaluation variable de Less pour comprendre comment/pourquoi elle existe.

J'ai un composant - qui est une grille de données. Ce composant doit avoir un style par défaut - défini par le package du composant. Mais si une certaine variable de l'extérieur est déjà définie, celle-ci devrait avoir la priorité.

sans application
/grid/sans grille

Comme la grille est un composant, oubliez d'ajouter quoi que ce soit ici - ou d'ajouter du code après le fichier grid.less.
Je ne vois pas comment moins couvre ce problème. Scss offre cette fonctionnalité pour une bonne raison.

@geri777

Mais si une certaine variable de l'extérieur est déjà définie, celle-ci devrait avoir la priorité.

Moins évalue comme CSS.

.css {  
  --color: blue;
  color: var(--color);  // --color will be red
  --color: red;
  border-color: var(--color);  // --color will still be red, red is the scope's final value
}

.less {  
  <strong i="10">@color</strong>: blue;
  color: @color;  // <strong i="11">@color</strong> will be red
  <strong i="12">@color</strong>: red;
  border-color: @color;  // <strong i="13">@color</strong> will still be red, red is the scope's final value
}
````

Scss, instead, doesn't mimic CSS evaluation and instead evaluates more like, say, PHP.

```scss
.scss {  
  $color: blue;
  color: $color;  // $color will be blue
  $color: red;
  border-color: $color;  // $color will be red
}

Ainsi, dans Sass/SCSS, pour remplacer la valeur d'une variable racine, vous êtes obligé de faire deux choses :

  1. Vous devez baliser toutes vos déclarations de variables avec !default
  2. Vous devez insérer vos variables globales avant ces déclarations par défaut.

Un péché:

// main.scss
<strong i="22">@import</strong> "overrides.scss";
<strong i="23">@import</strong> "library.scss";

// overrides.scss
$color: red;

// library.scss
$color: blue !default;

.scss {
  color: $color;
}

Dans Less, la thématisation est beaucoup plus facile. Vous n'avez (généralement) pas besoin de changer quoi que ce soit à propos de votre bibliothèque, il vous suffit de mettre vos remplacements après. Vous n'avez qu'une seule chose à faire.

// main.less
<strong i="27">@import</strong> "library.less";
<strong i="28">@import</strong> "overrides.less";

// overrides.less
<strong i="29">@color</strong>: red;

// library.less
<strong i="30">@color</strong>: blue;

.less {
  color: @color;
}

Par conséquent, vous n'avez pas besoin !default car Less acceptera toujours votre valeur finale.

Pensez à Moins d'évaluation comme la cascade du CSS. Cela fonctionne. La déclaration finale l'emporte.

Cette page vous a été utile?
0 / 5 - 0 notes