Less.js: Agregue soporte para variables "predeterminadas" (similar a !default en SASS)

Creado en 3 dic. 2013  ·  35Comentarios  ·  Fuente: less/less.js

Estamos considerando cambiar de SASS a LESS, pero el principal obstáculo para nosotros es la falta de una función de "variable predeterminada" (consulte http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_defaults_)

Esta característica es increíblemente útil cuando distribuye su código SASS como una biblioteca donde las variables sirven como una especie de "API" para sus usuarios. En tal caso de uso, todo lo que el usuario tiene que hacer es asegurarse de que sus variables se incluyan por adelantado para anular los valores predeterminados que desea cambiar, ¡y listo! sin jugar con el orden de los (posiblemente) muchos archivos que pueden contener las variables que necesitan anularse.

Empecé a trabajar en una implementación, aún no he escrito ninguna prueba, pero espero que podamos usar esta solicitud de extracción como punto de partida para la discusión: https://github.com/less/less.js/pull/1705

Elegí la siguiente sintaxis:

?foo: value;

a diferencia de la forma SASS:

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

por 2 razones: es más conciso y la sintaxis predeterminada puede presentar problemas potenciales con el análisis de expresiones en el lado derecho en el futuro (pero podría estar equivocado al respecto).

La implementación que se me ocurrió fue sorprendentemente simple; espero no haberme perdido nada importante. Realmente agradecería cualquier comentario que pueda tener.

Salud,
phil

consider closing feature request low priority

Comentario más útil

Iba a sugerir el artículo que escribí y luego vi que @seven-phases-max lo había vinculado. 😄 Confía en nosotros, ¡lo que pides ya existe! Pero debe comprender la evaluación variable de Less para comprender cómo/por qué existe.

Todos 35 comentarios

Ver La Documentación .


Actualización: eliminé mi comentario anterior de esta publicación para no confundir a los futuros visitantes (aquí estaba tratando de responder a la pregunta "cómo definir una variable si aún no está definida" y perdí el punto de que es un "Problema XY" y la respuesta correcta para un Sass-like !default es: en Less no necesita nada de eso porque la funcionalidad necesaria ("(re)definición después del uso") ya se proporciona por la forma en que funcionan las variables de Less).

Si define la misma variable dos veces, la última declaración gana y es válida en todo el ámbito. Entonces, otra opción sería declarar las variables normalmente y pedirle al usuario que incluya sus variables después de su biblioteca.

biblioteca.menos

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

sin usuario:

<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 compila en:

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

Gracias por los comentarios sobre nuestro PR. Estas soluciones serían factibles si tuviéramos una sola cosa o algunas cosas pequeñas para consumir. Permítanme explicar por qué estas soluciones no son viables en nuestro caso.

Hacemos un gran marco de componentes implementados como una jerarquía de clases. Declaramos las variables para cada componente en su propio archivo... en nuestro tema base. Luego "derivamos" temas de aquí que quieren modificar estas variables. Los usuarios también pueden hacerlo para ajustar un tema a sus necesidades.

Además, las variables a menudo "derivan" de otras variables. Este más obvio es el color base. Hemos dedicado mucho tiempo a discutir alternativas a los problemas de las grandes jerarquías de clases de componentes que utilizan una propagación agresiva de valores variables combinada con temas y su propio comportamiento de herencia.

Con mucho, la mejor solución a estos problemas es _siempre_ definir las variables como "! predeterminadas" (en términos de Sass). Esto permite a los usuarios simplemente ingresar primero y establecer sus valores antes de que estos valores se utilicen para calcular otros valores. El tiempo es bastante simple de administrar para nuestros usuarios. Dado que este es siempre el caso para cada variable en todos nuestros temas, los problemas de sintaxis como los sugeridos anteriormente serían una gran carga y también propensos a errores.

Nos encantaría contribuir con otras características a Less a medida que continuamos. Creo que nuestro (¿inusualmente?) caso de uso a gran escala podría ser una validación útil de lo que se necesita para ampliar el conjunto de funciones/lenguaje.

Espero que considere fusionar esto o proporcione algunas alternativas que sean de naturaleza más declarativa. Explotar trucos de alcance realmente no funcionará para nosotros.

¡Gracias de nuevo por su tiempo y consideración!

Parece que sería mejor definir mixins en lugar de clases directas. Luego, las variables se pueden anular más adelante en su hoja de estilo principal o en otro tema derivado. Así es como puedo anular cosas como los anchos de los canalones en mis cuadrículas después de haberlos importado.

Solo para aclarar, aquí solo estamos hablando de variables. Y no tenemos una hoja de estilo principal, tenemos una por clase por tema. :)

Tal vez no estoy siguiendo su sugerencia, pero como ejemplo, uno de nuestros archivos de variables de tema se ve así:

$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;

Este archivo configura los distintos valores del Panel para un tema que tiene una cadena de temas base de 4 de profundidad. Esos temas básicos tienen sus propios valores de variables para el Panel, pero estos se cargan primero, por lo que debe anularlos. A menos que un tema de usuario se derive de este tema y proporcione valores propios.

Este patrón se repite _mucho_ :)

Bien, bueno, ¿por qué no anular @panel-base-color en su hoja de menos principal? Las variables LESS son globales, por lo que la última que ocurra es la ganadora.

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

@panel-base-color: red;

Ahora se anulará cualquier lugar que se use en el tema. Si nadie anula esto en su cadena de importación, lo que se configuró originalmente será el valor predeterminado.

No tenemos una "hoja principal menos" :) Agradezco su ayuda y sugerencias, pero hemos tenido muchas discusiones sobre esto internamente y tienden a durar un poco. Baste decir que creemos que necesitamos una configuración de variable predeterminada como hemos propuesto aquí. Esta característica existe en Sass y la encontramos muy útil para reducir la complejidad y brindar a los usuarios flexibilidad para configurar nuestros temas.

Tengo curiosidad si todo esto significa que esta solicitud de extracción o algún derivado similar alguna vez sería aceptado. Estaremos encantados de modificar la sintaxis si eso es objetable.

No tenemos una "hoja principal menos" :)

Simplemente reemplace "su hoja principal menos" en las respuestas anteriores con "cualquiera de las hojas menos del usuario". Hasta ahora parece que SASS !default (sea cual sea la sintaxis que tenga) está inventado para solucionar un problema que no existe en LESS.
@dongryphon , puedo estar equivocado, por supuesto, pero aún no ha explicado por qué no puede usar la solución sugerida por @SomMeri y @Soviut.

Para decirlo de otra manera, imagine si no estuviera importando y en su lugar tuviera todas sus variables en la misma hoja. Eso es efectivamente lo que hace la importación. Entonces, en esa situación, si tuviera dos declaraciones de variables con el mismo nombre, la última en la hoja ganaría.

@base-color: green;

div {
    background: @base-color;
}

@base-color: red;

Debido a que el color base se declara nuevamente al final, el color base utilizado en el div será rojo. Se compilará para:

div {
    background: red;
}

Esto surgió antes, de hecho, creo que incluso se implementó y tuvimos una solicitud de extracción de los muchachos que lo querían en el arranque y acordaron después de una discusión que era una función sin sentido en menos...

Lo único que le permite hacer es que los usuarios definan variables antes de importar. Si los usuarios definen anulaciones después, funciona como si tuviera un valor predeterminado... eso se debe a que incluso en el archivo importado tomará la última definición de la misma manera que css, incluso si se define después del uso.

No es una gran carga para los usuarios de una biblioteca decir que anulan las variables después de las importaciones (o importan un archivo de variables después de las importaciones) en lugar de agregar más sintaxis a menos... creemos que es una de las ventajas de menos, que puede haga lo mismo que sass pero la mayor parte del tiempo con una sintaxis más simple.

Esto es contrario a lo que podría pensar un programador de JavaScript, pero la idea detrás de esto es que está más cerca de css.

¿Puede explicar por qué no es posible o deseable pedir a los consumidores que consideren el orden? Aceptaremos funciones con casos de uso sólidos, pero tenemos que ser rigurosos para evitar el lenguaje y la complejidad.

Ver #1109 #1104 #313

Y solo para aclarar, no estamos diciendo "no" de ninguna manera. Estamos tratando de mostrarle que es posible que esta función ya exista.

Agregué algo de información a less-docs

Cerrando como característica ya disponible (en "Menos camino") (http://lesscss.org/features/#variables-feature-default-variables).

Creo que esta confusión surge a menudo porque, si bien hay una _syntax_ superpuesta, SASS se "ejecuta" de arriba a abajo, pero LESS no, y en su lugar funciona más como CSS. LESS es como CSS+ (CSS con características adicionales) y SASS es como "PHP con sintaxis CSS". Me pregunto si hay alguna manera (si es necesario) de que podamos hacer esa distinción.

¿Puede explicar por qué no es posible o deseable pedir a los consumidores que consideren el orden? Aceptaremos funciones con casos de uso sólidos, pero tenemos que ser rigurosos para evitar el lenguaje y la complejidad.

Porque enseñamos a los usuarios (y consumidores) de la biblioteca a nunca editar los códigos de la biblioteca ellos mismos. La función !default que falta significa que tenemos que hacer una de esas dos cosas, ambas igualmente malas en mi opinión:

  • Los usuarios tienen que editar la hoja de estilo de la biblioteca ellos mismos para editar las variables donde se declaran (lo cual es algo prohibido, lo que dificulta las actualizaciones de la biblioteca) o,
  • La biblioteca debe proporcionar dos hojas de estilo: una para las variables predeterminadas y otra para las reglas reales. Luego, el usuario tiene que @importar el primero, luego declarar sus propias variables, luego @importar el segundo. Es más complejo de lo que debería ser, especialmente para mantener esos dos archivos en la misma versión.

El enfoque sass significa que una biblioteca solo tiene que proporcionar un único archivo, que el usuario podrá personalizar.

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

En lugar de:

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

Creo que deberías reconsiderar este tema.

@arcanis En realidad, no puedo ver por qué piensas eso:

La biblioteca debe proporcionar dos hojas de estilo: una para las variables predeterminadas y otra para las reglas reales.
El enfoque sass significa que una biblioteca solo tiene que proporcionar un único archivo

En Less es absolutamente el mismo diseño de biblioteca:

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

Supongo que simplemente te estás perdiendo la cosa de "Lazy-Loading" (y exactamente el mismo ejemplo se usa en los documentos para decir no, thanks! a !default ).

Supongo que simplemente te estás perdiendo la cosa de "Lazy-Loading"

@seven-phases-max WTF, tienes razón. Maldita sea, probablemente debería borrar toda mi respuesta (y lo hice). ¿Me está diciendo que puede importar bootstrap, y simplemente anular vars específicos, y simplemente funcionará?

No te creí, e hice mis propias pruebas para verificarlo. ¿Cómo me perdí esto? Creo que es porque todos los artículos que he visto en Bootstrap, que incluían la personalización, recomendaban que copiara el archivo variables.less y estableciera sus propios valores, lo que por supuesto causa problemas cuando se agregan más variables a la biblioteca. Y creo que tuve la impresión de que los selectores se emitían en el punto inmediato de importación. ¿Las variables siempre "cargaron perezosamente" de esta manera?

Esta tiene que ser la característica más importante que comúnmente se pasa por alto en Less. Nunca he visto una publicación de blog sobre Less o una biblioteca Less que mencione esta función. Incluso con todo en el hilo aquí y la documentación, no fue inmediatamente obvio lo que eso significaba con las bibliotecas del mundo real. Pensé que todo el mundo simplemente decía que se podían anular las variables declaradas anteriormente configurándolas más tarde, y nunca entendí que afectaría la evaluación de las variables de documentos importados anteriores.

No puedo creer que nunca tuve esto hasta ahora. Esto cambia básicamente todo sobre cómo estructuro mis documentos menos, y tiene una pequeña mención en los documentos que no demuestra la salida.

Tal vez la razón por la que recibimos tantas solicitudes de una función !default en Less es porque pocas personas intuyen esta función o la entienden a partir del breve ejemplo de los documentos (incluyéndome a mí, obviamente).

Por lo menos, ¡gracias por explicarlo de nuevo @seven-phases-max!

Ups. Bueno, tienes razón. También entendí mal los documentos, ¡mi error!

@Soviut , ¡eres un maldito genio! No tenía idea de que las declaraciones de variables funcionaran así en SASS/LESS. ¡Gracias!

Todavía me estoy dando palmadas en la frente porque esto no fue un comportamiento obvio con todo lo demás que he escrito en Less. Creo que mi problema era que había visto artículos de ejemplo deficientes escritos por personas que no sabían cómo hacerlo.

Además, tenga en cuenta: @spikesagal en cuanto a su declaración: "No tenía idea de que las declaraciones de variables funcionaran así en SASS/LESS" -- Hasta donde sé, el comportamiento de las variables no es el mismo entre los dos idiomas, ya que SASS y LESS evalúan las cosas de manera muy diferente.

Y están eliminando esta característica en BS4 debido a que se mudaron a Sass... :-1: Todavía estoy bastante triste por ese movimiento.

Así comienza, la lucha por más !variables por defecto: [twbs/bootstrap#17418]

Bueno, no pueden eliminar la función Less cambiando las fuentes SCSS de ninguna manera. (Técnicamente, ni siquiera es una "característica" (como una especie de "comportamiento sintetizado") sino una propiedad/corolario fundamental de la evaluación perezosa). Siempre que haya una versión Less de BS (que es solo una cuestión de cantidad de personas dispuestas a contribuir) y haya al menos una variable global, siempre podrá anular esa variable.

Bien. La v4-alfa oficial de bootstrap se trasladó a Sass. Eso, al menos según tengo entendido, está matando menos en su documentación oficial, y eso significa también eliminar el soporte para esta función porque Sass no tiene variables de carga diferida. Solo admiten !default, lo que, en cierto modo, significa: "Oh, sí, le permitimos anular nuestras variables solo una vez desde el exterior, y solo si permitimos hacerlo. Entonces, si olvidamos darle acceso a una variable simplemente omitiendo el valor predeterminado, estás bastante jodido y tienes que anular todo el maldito selector en tus archivos. Lidia con eso".

y eso significa también eliminar el soporte para esta función porque Sass no tiene variables de carga diferida.

Bueno, esto solo significa "lo matan en Bootstrap" (que obviamente es algo fuera del alcance de este hilo; siempre que no haya una versión Less de BS, no deberíamos preocuparnos por las funciones de Bootstrap en _este_ repositorio, ¿deberíamos?).

Dado que esta discusión fue más o menos sobre la anulación de variables en bootstrap...

Nos quedamos con menos :smile:

Tengo un caso de uso para el dilema planteado originalmente. Considere el siguiente ejemplo simplificado:

  1. Tengo un archivo less que define el tamaño de fuente de h1, algo como esto
    <strong i="8">@font_size__h1</strong> = 30px;
  2. Tengo (a falta de una frase mejor) un complemento que contiene un archivo LESS separado que usa la declaración @font_size__h1 . Así que lo <strong i="12">@import</strong> (reference) "path/to/file.less"; . No hay problema, ya está disponible.
  3. Ahora llevo ese "complemento" a un nuevo sitio que no tiene @font_size__h1 definido en ninguna parte. En este punto, sería genial decir "Si @font_size__h1 está definido, use ese valor. Si no está definido, use el valor que defino aquí".

El artículo 3 no es posible actualmente, por lo que puedo ver.

@theMikeD

El artículo 3 no es posible actualmente, por lo que puedo ver.

No parece que hayas leído el hilo:

<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";

que por supuesto se puede reducir a:

<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

que es básicamente el mismo ejemplo que http://lesscss.org/features/#variables -feature-default-variables

Sí, leí el hilo. Parece que no entiendes lo que estoy preguntando, porque tu ejemplo tiene mi pregunta al revés.

que por supuesto se puede reducir a:
@import "ruta/al/archivo.sin"; // <- definir el valor predeterminado allí
@font-size-h1: foo; // si no está definido aquí, utilice el valor definido anteriormente

Esto es lo contrario de lo que necesito. Esto sobrescribirá el valor de @font-size-h1 en path/to/file.less con el valor local pase lo que pase. Lo que necesito es que el valor en el archivo local solo se use si:
a) path/to/file.less no está cargado, o
b) el valor de '@ font_size__h1 is not set in path/to/file.less`

IOW, el valor local de @font-size-h1: foo; siempre estará presente en el archivo local, pero debe anularse si está configurado en path/to/file.less

En cualquier caso, encontré la solución anoche, que es asignar primero el valor local y luego colocar la instrucción @import al final del archivo, no al principio. Si lo encuentra, sobrescribirá el valor local.

Gracias de cualquier manera.

Parece que no entiendes lo que estoy preguntando.

Prefiero sugerirle que comience con algunos conceptos básicos de menos variables para actualizar su vista:

(porque parece que solo está tratando de pensar en todo de una manera imperativa similar a C/PHP, mientras que en Less/CSS es totalmente "declarativo al revés").
Se ha hablado hasta la saciedad durante todos estos años, !default puede agregar _nada_ nuevo si se compara con la anulación nativa de la variable Less. Período. (Hemos estado allí muchas veces antes: si uno piensa que encontró algún caso de uso para !default en Less, no significa nada más que su falta de comprensión de la semántica variable de Less).

Lo que necesito es que el valor en el archivo local solo se use si:
a) path/to/file.less no está cargado, o
b) el valor de @ font_size__h1 no está establecido en path/to/file.less

Entonces es simplemente lo contrario:

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

tada!

... que es donde aterricé, como dije.

Iba a sugerir el artículo que escribí y luego vi que @seven-phases-max lo había vinculado. 😄 Confía en nosotros, ¡lo que pides ya existe! Pero debe comprender la evaluación variable de Less para comprender cómo/por qué existe.

Tengo un componente, que es una cuadrícula de datos. Este componente debe tener un estilo predeterminado, definido por el paquete del componente. Pero si una determinada variable externa ya está definida, esa debería tener prioridad.

app.less
/grid/grid.less

Como la cuadrícula es un componente, olvídese de agregar nada aquí, o de agregar cualquier código después del archivo grid.less.
No veo cómo menos cubre este problema. Scss ofrece esta característica por una buena razón.

@geri777

Pero si una determinada variable externa ya está definida, esa debería tener prioridad.

Menos evalúa como 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
}

Entonces, en Sass/SCSS, para anular el valor de una variable raíz, está obligado a hacer dos cosas:

  1. Debe etiquetar todas sus declaraciones de variables con !default
  2. Debe insertar sus variables globales antes de estas declaraciones predeterminadas.

Como en:

// 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;
}

En Less, la tematización es mucho más fácil. (Normalmente) no necesita cambiar nada sobre su biblioteca, solo necesita poner sus anulaciones después. Solo necesitas hacer una cosa.

// 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;
}

Por lo tanto, no necesita !default porque Less siempre aceptará su valor final.

Piense en la evaluación Less como la cascada de CSS. Simplemente funciona. La declaración final gana.

¿Fue útil esta página
0 / 5 - 0 calificaciones