Crear o hacer referencia a variables dinámicamente en Sass

94

Estoy tratando de usar la interpolación de cadenas en mi variable para hacer referencia a otra variable:

// Set up variable and mixin
$foo-baz: 20px;

@mixin do-this($bar) {
    width: $foo-#{$bar};
}

// Use mixin by passing 'baz' string as a param for use $foo-baz variable in the mixin
@include do-this('baz');

Pero cuando hago esto, aparece el siguiente error:

Variable indefinida: "$ foo-".

¿Sass admite variables variables de estilo PHP?

Krzysztof Romanowski
fuente

Respuestas:

50

Sass no permite que se creen variables ni se acceda a ellas de forma dinámica. Sin embargo, puede utilizar listas para comportamientos similares.

scss:

$list: 20px 30px 40px;    
@mixin get-from-list($index) {
  width: nth($list, $index);
}

$item-number: 2;
#smth {
  @include get-from-list($item-number);
}

css generado:

#smth {
  width: 30px; 
}
lisowski.r
fuente
9
@castus, ¿cómo resolvió esto tu problema? Me encuentro con un problema muy similar en el que necesito tomar un valor de cadena de una lista y agregarle un $ y usarlo como una variable.
cmegown
88

En realidad, esto es posible mediante mapas SASS en lugar de variables. Aquí hay un ejemplo rápido:

Hacer referencia de forma dinámica:

$colors: (
  blue: #007dc6,
  blue-hover: #3da1e0
);

@mixin colorSet($colorName) {
    color: map-get($colors, $colorName);
    &:hover {
        color: map-get($colors, $colorName#{-hover});
    }
}
a {
    @include colorSet(blue);
}

Salidas como:

a { color:#007dc6 }
a:hover { color:#3da1e0 }

Creando dinámicamente:

@function addColorSet($colorName, $colorValue, $colorHoverValue: null) {
  $colorHoverValue: if($colorHoverValue == null, darken( $colorValue, 10% ), $colorHoverValue);

  $colors: map-merge($colors, (
    $colorName: $colorValue,
    $colorName#{-hover}: $colorHoverValue
  ));

  @return $colors;
}

@each $color in blue, red {
  @if not map-has-key($colors, $color) {
    $colors: addColorSet($color, $color);
  }
  a {
    &.#{$color} { @include colorSet($color); }
  }
}

Salidas como:

a.blue { color: #007dc6; }
a.blue:hover { color: #3da1e0; }
a.red { color: red; }
a.red:hover { color: #cc0000; }
dibbledeedoo
fuente
10
Tenga en cuenta que esto todavía no es "variables dinámicas". Esta es solo una variación del uso de una lista de listas que hemos estado usando desde siempre.
cimmanon
13
En realidad, esto amplía las listas, que solo aceptan un número de índice como variable de especificación. Permite que las variables sean llamadas con un nombre generado dinámicamente, creado por concatenación de una cadena pasada, que era la funcionalidad solicitada.
dibbledeedoo
3
Esta debería ser la respuesta aceptada. Aunque no es completamente dinámico, imita mejor la funcionalidad solicitada.
thomaux
1
Si bien es útil, este ejemplo no crea ninguna variable en absoluto
hasta el
Cierto. A pesar del título de la pregunta de la operación, su texto no describía la creación de variables, así que no abordé eso. Sin embargo, acabo de agregar una sección relativa a hacer eso (aunque todavía con mapas, no variables singulares).
dibbledeedoo
5

Cada vez que necesito usar un valor condicional, me apoyo en funciones. He aquí un ejemplo sencillo.

$foo: 2em;
$bar: 1.5em;

@function foo-or-bar($value) {
  @if $value == "foo" {
    @return $foo;
  }
  @else {
    @return $bar;
  }
}

@mixin do-this($thing) {
  width: foo-or-bar($thing);
}
Adam Stacoviak
fuente
Creo que esto es exactamente lo que estoy buscando. Esto es esencialmente tomar el valor pasado al mixin y ejecutarlo a través de la función. Luego, dependiendo de una serie de declaraciones if, devuelve el mismo valor de cadena que una variable. ¿Sería posible hacer esto con un número potencialmente infinito de valores? Digamos que tenemos una lista de muchas cadenas, no solo foo o bar.
cmegown
3
Esto es pura mala práctica, no querrás terminar con una cadena interminable de condiciones if. Utilice mapas SASS con pares clave-valor y extraiga los valores de ellos.
mystrdat
Es principalmente redundante en este escaparate, ¿por qué no llamarías simplemente @include do-this($foo);? ... sin embargo, esto tendría sentido, si la función realmente hiciera algo, pero solo pasa ...
jave.web
2

Aquí hay otra opción si está trabajando con rieles y posiblemente en otras circunstancias.

Si agrega .erb al final de la extensión del archivo, Rails procesará erb en el archivo antes de enviarlo al intérprete de SASS. Esto le da la oportunidad de hacer lo que quiera en Ruby.

Por ejemplo: (Archivo: foo.css.scss.erb)

// Set up variable and mixin
$foo-baz: 20px; // variable

<%
def do_this(bar)
  "width: $foo-#{bar};"
end
%>

#target {
  <%= do_this('baz') %>
}

Resultados en el siguiente scss:

// Set up variable and mixin
$foo-baz: 20px; // variable

#target {
  width: $foo-baz;
}

Lo cual, de ordinario, da como resultado el siguiente CSS:

#target {
  width: 20px;
}
Blake Taylor
fuente
0

Me encontré con la necesidad de hacer referencia a un color de forma dinámica recientemente.

Tengo un archivo _colours.scss para cada proyecto, donde defino todos mis colores una vez y los hago referencia como variables en todo momento.

En mi archivo _forms.scss, quería configurar estilos de botón para cada color disponible. Suele ser una tarea tediosa. Esto me ayudó a evitar tener que escribir el mismo código para cada color diferente.

El único inconveniente es que debe enumerar cada nombre y valor de color antes de escribir el CSS real.

// $red, $blue - variables defined in _colours.scss
$colours: 
  'red' $red,
  'blue' $blue;

@each $name, $colour in $colours {
  .button.has-#{$name}-background-color:hover {
    background-color: lighten($colour, 15%);
  }
}
lukeseager
fuente
-2

Hacer una variable dinámica no es posible en SASS a partir de ahora, ya que agregará / conectará otra var que necesita ser analizada una vez cuando ejecute el comando sass.

Tan pronto como se ejecute el comando, arrojará un error de CSS no válido, ya que todas las variables declaradas seguirán el levantamiento.

Una vez ejecutado, no puede volver a declarar variables sobre la marcha

Para saber que he entendido esto, por favor indique si lo siguiente es correcto:

desea declarar variables donde la siguiente parte (palabra) es dinámica

algo como

$list: 100 200 300;

@each $n in $list {
    $font-$n: normal $n 12px/1 Arial;
}

// should result in something like

$font-100: normal 100 12px/1 Arial;
$font-200: normal 200 12px/1 Arial;
$font-300: normal 300 12px/1 Arial;

// So that we can use it as follows when needed

.span {
    font: $font-200;
    p {
       font: $font-100
    }
}

Si esto es lo que quieres, me temo que a partir de ahora esto no está permitido

Om Shankar
fuente
3
Desafortunadamente, esto no funciona: error scss / components.scss (Línea 71: CSS no válido después de "$ font-": esperado ":", era "$ n: normal $ n 1 ...")
Krzysztof Romanowski
4
@castus, ¡Ups! perdón por no ser claro - no estoy dando una solución, sino explicando la pregunta una vez más
Om Shankar
16
¡probablemente no debería publicar una solución que no esté permitida!
Rhyso
¿De ancho esto funcionaría pero parece que no podemos crear variables a través de sass?
v3nt