Sass, el manual oficial

Capítulo 6. SassScript

Además de extender la sintaxis básica de CSS, Sass incluye una serie de extensiones más avanzadas llamadas SassScript. Gracias a estas extensiones, las propiedades pueden utilizar variables, expresiones matemáticas y otras funciones. Sass permite el uso de SassScript para definir cualquier valor de cualquier propiedad.

6.1. Shell interactiva

Si quieres experimentar con SassScript antes de empezar a utilizarlo en tus hojas de estilos, puedes hacer uso de "la shell interactiva". Para ello, ejecuta el comando sass añadiendo la opción -i y escribe cualquier expresión válida de SassScript. La shell te mostrará el resultado de evaluar esa expresión o un mensaje de error si no es correcta:

$ sass -i
>> "¡Hola Mundo!"
"¡Hola Mundo!"

>> 1px + 1px + 1px
3px

>> #777 + #777
#eeeeee

>> #777 + #888
white

6.2. Variables

La funcionalidad básica de SassScript es el uso de variables para almacenar valores que utilizas una y otra vez en tus hojas de estilos. Para ello, utiliza cualquier palabra como nombre de la variable, añádele el símbolo $ por delante y establece su valor como si fuera una propiedad CSS normal. Si por ejemplo defines una variable de la siguiente manera:

$width: 5em;

Ahora ya puedes utilizar la variable llamada $width como valor de cualquier propiedad CSS:

#main {
  width: $width;
}

Una limitación importante de las variables es que sólo están disponibles dentro del contexto donde se han definido. Esto significa que si defines la variable dentro de una regla anidada, sólo estará disponible para esas reglas anidadas. Si quieres poder utilizar una variable como valor de cualquier propiedad de la hoja de estilos, defínela fuera de cualquier selector.

6.3. Tipos de datos

SassScript soporta seis tipos de datos:

  • Números (ejemplo: 1.2, 13, 10px)
  • Cadenas de texto con o sin comillas simples o dobles (ejemplo "foo", 'bar', baz)
  • Colores (ejemplo blue, #04a3f9, rgba(255, 0, 0, 0.5))
  • Valores lógicos o booleanos (ejemplo true, false)
  • Valores nulos (ejemplo null)
  • Listas de valores, separados por espacios en blanco o comas (ejemplo 1.5em 1em 0 2em, Helvetica, Arial, sans-serif)
  • Pares formados por una clave y un valor separados por : (ejemplo (key1: value1, key2: value2))

SassScript también soporta todos los otros tipos de datos soportados por CSS, como por ejemplo los caracteres Unicode o la palabra reservada !important. No obstante, Sass no trata estos valores de manera especial y se limita a considerarlos como si fuera una cadena de texto normal y corriente.

6.3.1. Cadenas de texto

CSS define dos tipos de cadenas de texto: las que tienen comillas (dobles o simples) como por ejemplo "Lucida Grande" o 'http://sass-lang.com'; y las que no tienen comillas, como por ejemplo sans-serif o bold.

SassScript soporta y reconoce estos dos tipos de cadenas. En general, el archivo CSS compilado mantendrá el mismo tipo de cadena que el que se utilizó en el archivo Sass original.

La única excepción es cuando se utiliza la interpolación #{} que se explica en los próximos capítulos. En este caso, las cadenas siempre se generan sin comillas. Ejemplo:

@mixin firefox-message($selector) {
  body.firefox #{$selector}:before {
    content: "Hi, Firefox users!";
  }
}

@include firefox-message(".header");

El código Sass anterior se compila de la siguiente manera:

body.firefox .header:before {
  content: "Hi, Firefox users!";
}

6.3.2. Listas

Las listas son el tipo de dato que utiliza Sass para representar los valores que normalmente se utilizan en las propiedades CSS como margin: 10px 15px 0 0 o font-face: Helvetica, Arial, sans-serif. Las listas son simplemente una colección de valores separados por comas o espacios en blanco. Técnicamente, cada elemento de la lista también se considera una lista simple de un solo elemento.

Por si solas las listas no sirven para mucho, pero gracias a las funciones para listas definidas por SassScript que se explican en los siguientes capítulos, puedes conseguir resultados muy avanzados. La función nth() por ejemplo permite acceder al enésimo elemento de una lista, la función join() puede concatenar todos los valores y la función append() puede fusionar varias listas en una sola. Por último, la directiva @each permite aplicar estilos a cada elemento de una lista.

Además de contener valores simples, las listas pueden contener en su interior otras listas. Así por ejemplo, la lista 1px 2px, 5px 6px es una lista de dos elementos, que a su vez son las listas 1px 2px y 5px 6px. Si las listas interiores utilizan el mismo carácter para separar sus elementos que la lista principal, puedes añadir paréntesis para indicar claramente cuáles son los elementos de las listas anidadas. Así por ejemplo, la lista (1px 2px) (5px 6px) también es una lista de dos elementos cuyos valores son a su vez dos listas con los valores 1px 2px y 5px 6px.

Cuando se genera el archivo CSS, Sass no mantiene los paréntesis de las listas porque CSS no es capaz de entenderlos. Así que los valores (1px 2px) (5px 6px) y 1px 2px 5px 6px de Sass generan el mismo código cuando se compilan a CSS. No obstante, en Sass estos dos valores son diferentes: el primero es una lista que tiene dos listas en su interior y el segundo es una lista de cuatro números.

Las listas también pueden estar vacías y no contener ningún elemento. Estas listas vacías se representan mediante () y no se pueden incluir directamente en el archivo CSS compilado. Así que si defines una regla como font-family: (), Sass mostrará un mensaje de error. Si una lista contiene valores vacíos o nulos, como por ejemplo 1px 2px () 3px o 1px 2px null 3px, estos valores se eliminan antes de convertir la lista a código CSS.

Las listas separadas por comas pueden incluir una coma después del último elemento. Esto es muy útil por ejemplo para crear listas de un solo elemento. Así por ejemplo (1,) es una lista que contiene el elemento 1, mientras que (1 2 3,) es una lista separada por comas cuyo primer elemento es a su vez una lista separada por espacios en blanco y que contiene los elementos 1, 2 y 3.

6.3.3. Mapas

Los mapas son asociaciones de claves y valores. La clave se utiliza para acceder fácilmente al valor de cualquier elemento del mapa. Se utilizan principalmente para agrupar valores y acceder a ellos dinámicamente. En CSS no existe ningún elemento equivalente a los mapas, pero su sintaxis es similar a las expresiones media query:

$map: (clave1: valor1, clave2: valor2, clave3: valor3);

A diferencia de las listas, los mapas siempre se encierran con paréntesis y los pares clave: valor deben separarse con comas. Tanto las claves como los valores de los mapas pueden utilizar cualquier función o expresión de SassScript. Las claves de un mapa deben ser únicas, por lo que si quieres asociar varios valores a una misma clave, debes utilizar una lista.

Al igual que sucede con las listas, los mapas se pueden manipular mediante funciones de SassScript. La función map-get() por ejemplo busca un valor dentro del mapa a partir de la clave indicada y la función map_merge() añade nuevos pares clave: valor a un mapa existente. Además, la directiva @each se puede emplear para aplicar estilos a cada par clave: valor de un mapa.

Los mapas también se pueden utilizar en cualquier función preparada para manipular listas. Si pasas un mapa a una función que espera una lista, el mapa se transforma primero en un lista de pares de valores. Así por ejemplo, si pasas el mapa (clave1: valor1, clave2: valor2) a una función para listas, este se transforma automáticamente en clave1 valor1, clave2 valor2. Lo contrario no es cierto, ya que no puedes utilizar listas en las funciones preparadas para mapas. La única excepción es la lista vacía (), que representa tanto a un mapa vacío como a una lista vacía.

Los mapas no se pueden convertir directamente a código CSS. Por tanto, si utilizar un mapa como valor de una variable o como argumento de una función CSS, Sass mostrará un mensaje de error.

6.4. Operadores

Todos los tipos de datos soportan el operador de igualdad (== y !=) para comprobar si dos valores son iguales o distintos. Además, cada tipo de dato define otros operadores propios.

6.4.1. Operadores para números

SassScript soporta los cinco operadores aritméticos básicos: suma +, resta -, multiplicación *, división / y módulo %. El operador módulo calcula el resto de la división sin decimales (ejemplo: 5 módulo 2 = 1, % % 2 = 1). Además, si realizas operaciones sobre números con diferentes unidades, Sass convertirá automáticamente las unidades siempre que sea posible:

p {
  width: 1in + 8pt;
}

El código Sass anterior se compila de la siguiente manera:

p {
  width: 1.111in;
}

Con los números también se pueden utilizar los operadores relacionales (<, >, <=, >=) y los de igualdad (==, !=).

6.4.1.1. El problema del carácter / con la división de números

CSS permite el uso del carácter / para separar números. Como Sass es totalmente compatible con la sintaxis de CSS, debe soportar el uso de esta característica. El problema es que el carácter / también se utiliza para la operación matemática de dividir números. Por todo esto, si utilizas el carácter / para separar dos números en SassScript, en el archivo CSS compilado aparecerán tal cual los has escrito.

No obstante, existen tres situaciones en las que el carácter / siempre se interpreta como una división matemática:

  1. Si uno de los operandos de la división es una variable o el resultado devuelto por una función.
  2. Si el valor está encerrado entre paréntesis.
  3. Si el valor se utiliza como parte de una expresión matemática.

Ejemplo:

p {
  // El carácter '/' se interpreta como código CSS normal
  font: 10px/8px;
  $width: 1000px;

  // El carácter '/'  se interpreta como una división
  width: $width/2;        // Uno de los operandos es una variable
  width: round(1.5)/2;    // Uno de los operados es el resultado de una función
  height: (500px/2);      // Los parénteis encierran la expresión
  margin-left: 5px + 8px/2px; // El '+' indica que es una expresión matemática
}

El código Sass anterior se compila de la siguiente manera:

p {
  font: 10px/8px;
  width: 500px;
  height: 250px;
  margin-left: 9px;
}

Si quieres utilizar el carácter / normal de CSS incluso cuando empleas variables, encierra las variables con #{}. Ejemplo:

p {
  $font-size: 12px;
  $line-height: 30px;
  font: #{$font-size}/#{$line-height};
}

El código Sass anterior se compila de la siguiente manera:

p {
  font: 12px/30px;
}

6.4.2. Operadores para colores

Los operadores aritméticos también se pueden aplicar a los valores que representan colores. En este caso, los cálculos siempre se realizan sobre cada componente del color. Esto significa que antes de cada operación, el color se descompone en sus tres componentes R, G y B, para después aplicar la operación a cada componente. Ejemplo:

p {
  color: #010203 + #040506;
}

Las tres operaciones realizadas son 01 + 04 = 05, 02 + 05 = 07 y 03 + 06 = 09, por lo que el código CSS compilado resultante es:

p {
  color: #050709;
}

En la mayoría de los casos, es mejor utilizar las funciones especiales de SassScript para colores que se explicarán más adelante, en vez de realizar operaciones matemáticas sobre ellos.

Las operaciones matemáticas también se pueden realizar combinando colores y números. Ejemplo:

p {
  color: #010203 * 2;
}

Las tres operaciones realizadas son 01 * 2 = 02, 02 * 2 = 04 y 03 * 2 = 06, por lo que el código CSS compilado resultante es:

p {
  color: #020406;
}

Si realizas operaciones sobre colores que incluyen un canal alpha (por ejemplo los que han sido creados con las funciones rgba() o hsla()) los dos colores deben tener el mismo valor alpha para poder realizar la operación con éxito. El motivo es que los cálculos no afectan al valor alpha. Ejemplo:

p {
  color: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75);
}

El código CSS compilado resultante es:

p {
  color: rgba(255, 255, 0, 0.75);
}

El canal alpha de un color se puede ajustar con la función opacify() o transparentize(). Ejemplo:

$translucent-red: rgba(255, 0, 0, 0.5);

p {
  color: opacify($translucent-red, 0.3);
  background-color: transparentize($translucent-red, 0.25);
}

El código Sass anterior se compila de la siguiente manera:

p {
  color: rgba(255, 0, 0, 0.8);
  background-color: rgba(255, 0, 0, 0.25);
}

Los filtros de Internet Explorer requieren que todos los colores incluyan una capa alpha, y que lo hagan siguiendo estrictamente el formato #AABBCCDD. Para convertir fácilmente un color a ese formato, utiliza la función ie_hex_str(). Ejemplo:

$translucent-red: rgba(255, 0, 0, 0.5);
$green: #00ff00;

div {
  filter: progid:DXImageTransform.Microsoft.gradient(
    enabled='false',
    startColorstr='#{ie-hex-str($green)}',
    endColorstr='#{ie-hex-str($translucent-red)}'
  );
}

El código Sass anterior se compila de la siguiente manera:

div {
  filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=#FF00FF00, endColorstr=#80FF0000);
}

6.4.3. Operadores para cadenas de texto

El operador + se puede utilizar para concatenar dos o más cadenas de texto:

p {
  cursor: e + -resize;
}

El código Sass anterior se compila de la siguiente manera:

p {
  cursor: e-resize;
}

Si la cadena que está a la izquierda del operador + está encerrada por comillas, el resultado de la operación será una cadena con comillas. Igualmente, si la cadena de la izquierda no tiene comillas, el resultado será una cadena sin comillas. Ejemplo:

p:before {
  content: "Foo " + Bar;
  font-family: sans- + "serif";
}

El código Sass anterior se compila de la siguiente manera:

p:before {
  content: "Foo Bar";
  font-family: sans-serif;
}

Por defecto, si dos valores son contiguos, se concatenan con un espacio en blanco:

p {
  margin: 3px + 4px auto;
}

El código Sass anterior se compila de la siguiente manera:

p {
  margin: 7px auto;
}

Dentro de una cadena de texto puedes utilizar la sintaxis #{ } para realizar operaciones matemáticas o para evaluar expresiones antes de incluirlas en la cadena. Esta característica se llama "interpolación de cadenas de texto":

p:before {
  content: "¡Me he comido #{5 + 10} pasteles!";
}

El código Sass anterior se compila de la siguiente manera:

p:before {
  content: "¡Me he comido 15 pasteles!";
}

Cuando interpolas una cadena de texto, los valores nulos se consideran cadenas vacías:

$value: null;

p:before {
  content: "¡Me he comido #{$valor} pasteles!";
}

El código Sass anterior se compila de la siguiente manera:

p:before {
  content: "¡Me he comido pasteles!";
}

6.4.4. Operadores para valores lógicos o booleanos

SassScript soporta el uso de los tradicionales operadores and, or y not sobre los valores lógicos o booleanos.

6.4.5. Operadores para listas

Sass no define ningún operador específico para las listas de elementos, ya que estas se manipulan mediante las funciones especiales que se explican en los siguientes capítulos.

6.5. Paréntesis

Puedes añadir paréntesis a cualquier expresión Sass para afectar al orden en el que se realizan las operaciones:

p {
  width: 1em + (2em * 3);
}

El código Sass anterior se compila de la siguiente manera:

p {
  width: 7em;
}

6.6. Funciones

SassScript define algunas funciones muy útiles para crear las hojas de estilos y que utilizan la misma sintaxis que CSS:

p {
  color: hsl(0, 100%, 50%);
}

El código Sass anterior se compila de la siguiente manera:

p {
  color: #ff0000;
}

6.6.1. Argumentos con nombre

Para que su uso sea más flexible, a las funciones de Sass les puedes pasar argumentos con nombre. De esta manera no es obligatorio respetar el orden en el que se definieron los argumentos, sólo su nombre:

p {
  color: hsl($hue: 0, $saturation: 100%, $lightness: 50%);
}

Aunque obviamente esta forma de usar las funciones no es tan concisa, hace que las hojas de estilo resultantes sean mucho más fáciles de leer. Además permite que las funciones tengan interfaces más flexibles y fáciles de usar, aún cuando incluyan muchos argumentos.

Los argumentos con nombre se pueden pasar en cualquier orden y puedes omitir los que tienen un valor por defecto. Además, como los argumentos con nombre en realidad son nombres de variables, puedes utilizar indistintamente guiones medios y bajos.

En los próximos capítulos se detalla la lista completa de funciones Sass y los nombres de todos sus argumentos.

6.7. Interpolación

Las variables definidas con SassScript se pueden utilizar incluso en los nombres de los selectores y de las propiedades:

$name: foo;
$attr: border;

p.#{$name} {
  #{$attr}-color: blue;
}

El código Sass anterior se compila de la siguiente manera:

p.foo {
  border-color: blue;
}

También es posible usar #{ } en los valores de las propiedades. Normalmente es mejor utilizar una variable, pero la ventaja de usar #{ } es que todas las operaciones que estén cerca suyo se interpretan como código CSS normal y corriente. Ejemplo:

p {
  $font-size: 12px;
  $line-height: 30px;
  font: #{$font-size}/#{$line-height};
}

El código Sass anterior se compila de la siguiente manera:

p {
  font: 12px/30px;
}

6.8. Variables con valores por defecto

La palabra reservada !default permite controlar la asignación de valores a las variables de manera mucho más precisa. Si una variable ya tenía un valor asignado, !default hace que se mantenga sin cambios. Si la variable no existía o no tenía ningún valor, se utiliza el nuevo valor asignado. Ejemplo:

$contenido: "Primer contenido";
$contenido: "¿Segundo contenido?" !default;
$nuevo_contenido: "Tercer contenido" !default;

#main {
  contenido: $contenido;
  nuevo-contenido: $nuevo_contenido;
}

El código Sass anterior se compila de la siguiente manera:

#main {
  contenido: "Primer contenido";
  nuevo-contenido: "Tercer contenido";
}

Al utilizar !default, las variables con valores nulos se considera que no han sido asignadas:

$contenido: null;
$contenido: "Contenido no nulo" !default;

#main {
  contenido: $contenido;
}

El código Sass anterior se compila de la siguiente manera:

#main {
  contenido: "Contenido no nulo";
}