SASS es un preprocesador de CSS. Es como añadir funcionalidad a CSS que no tiene pero no de cosas del navegador sino para ayudar a escribir menos CSS. El código se escribe en SASS y se transforma en CSS. Permite bucles, funciones, variables ,etc.
Enlaces con mas información de SASS:
Curso de SASS:
Las variables en SASS empiezan por el símbolo $. Sirven para no repetir el mismo color o la misma fuente en todo el CSS.
$color-principal: #1979E6;
$color-texto: #323E49;
$fuente-titulos: Georgia, serif;
body {
color: $color-texto;
}
h1 {
font-family: $fuente-titulos;
color: $color-principal;
}
se transforma en
body {
color: #323E49;
}
h1 {
font-family: Georgia, serif;
color: #1979E6;
}
Una lista guarda varios valores en una variable. Por ejemplo los tamaños de fuente del sistema de tamaños que vimos en el tema de diseño.
list.nth. El índice empieza por 1 y no por cero.list.length.
@use "sass:list";
$tamanyos-fuente: (12px, 14px, 16px, 18px, 20px, 24px, 30px, 36px, 48px);
body {
font-size: list.nth($tamanyos-fuente, 3);
}
h1 {
font-size: list.nth($tamanyos-fuente, list.length($tamanyos-fuente));
}
se transforma en
body {
font-size: 16px;
}
h1 {
font-size: 48px;
}
Un mapa guarda pares clave-valor. Por ejemplo los breakpoints de las media queries con su nombre.
map.get.map.keys. Se obtiene una lista.
@use "sass:map";
$breakpoints: (
'tablet': 576px,
'desktop': 768px,
'fulldesktop': 992px
);
h1 {
font-size: 30px;
}
@media (min-width: map.get($breakpoints, 'desktop')) {
h1 {
font-size: 48px;
}
}
se transforma en
h1 {
font-size: 30px;
}
@media (min-width: 768px) {
h1 {
font-size: 48px;
}
}
map.keys($breakpoints) devolvería la lista ('tablet', 'desktop', 'fulldesktop').
Repite un trozo de CSS cambiando un número. Por ejemplo para crear las clases de columnas de grid.
@for $i from 1 to 5: Desde 1 hasta 4. El último número no se incluye.
@for $i from 1 to 5 {
.columnas-#{$i} {
display: grid;
grid-template-columns: repeat($i, 1fr);
}
}
@for $i from 1 through 4: Desde 1 hasta 4. El último número sí se incluye.
@for $i from 1 through 4 {
.columnas-#{$i} {
display: grid;
grid-template-columns: repeat($i, 1fr);
}
}
Los dos se transforman en
.columnas-1 {
display: grid;
grid-template-columns: repeat(1, 1fr);
}
.columnas-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.columnas-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.columnas-4 {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
#{$variable}
through: se lee igual que se piensa, «de 1 a 4».
Recorre los elementos de una lista. Por ejemplo para crear los tamaños de los iconos.
$tamanyos-icono: (16px, 24px, 32px);
@each $tamanyo in $tamanyos-icono {
.icono-#{$tamanyo} {
width: $tamanyo;
height: $tamanyo;
}
}
se transforma en
.icono-16px {
width: 16px;
height: 16px;
}
.icono-24px {
width: 24px;
height: 24px;
}
.icono-32px {
width: 32px;
height: 32px;
}
Recorre un map obteniendo la clave y el valor. Por ejemplo para los colores que se usan para cosas concretas, como vimos en el tema de diseño: verde si ha ido bien, amarillo para avisar y rojo si hay un error.
$colores-estado: (
'exito': #1BAF7A,
'aviso': #ECC94B,
'error': #E61919
);
@each $nombre, $color in $colores-estado {
.mensaje-#{$nombre} {
border-left: 4px solid $color;
}
}
se transforma en
.mensaje-exito {
border-left: 4px solid #1BAF7A;
}
.mensaje-aviso {
border-left: 4px solid #ECC94B;
}
.mensaje-error {
border-left: 4px solid #E61919;
}
En SASS se puede escribir un selector dentro de otro. El "&" se convierte en el selector "padre", así que todo lo de un elemento queda junto.
.boton {
background-color: #1979E6;
transition: background-color 0.3s;
&:hover {
background-color: #1363B4;
}
&:focus {
outline: 2px solid #80ADF4;
}
&-grande {
font-size: 20px;
}
.icono {
margin-right: 8px;
}
}
se transforma en:
.boton {
background-color: #1979E6;
transition: background-color 0.3s;
}
.boton:hover {
background-color: #1363B4;
}
.boton:focus {
outline: 2px solid #80ADF4;
}
.boton-grande {
font-size: 20px;
}
.boton .icono {
margin-right: 8px;
}
&:hover: El "&" pega el :hover al selector padre: .boton:hover.&-grande: El "&" pega el texto al nombre de la clase: .boton-grande..icono sin "&": Es un elemento que está dentro: .boton .icono.Mas información en The Sass Ampersand
En SASS también existen funciones "normales" que calculan un valor. Por ejemplo, si todos los espacios del sistema de tamaños son múltiplos de 4px:
@function espacio($n) {
@return $n * 4px;
}
.tarjeta {
padding: espacio(4);
margin-bottom: espacio(6);
}
se transforma en
.tarjeta {
padding: 16px;
margin-bottom: 24px;
}
Son como trozos de código CSS que puedes añadir en otra clase CSS. Es como llamar a una "función" pero con CSS. Son muy útiles para evitar repetir código CSS. Por ejemplo, todos los botones son iguales salvo el color:
@mixin boton($color) {
padding: 8px 16px;
border: none;
border-radius: 6px;
background-color: $color;
color: #FFFFFF;
}
.boton-aceptar {
@include boton(#1979E6);
}
.boton-borrar {
@include boton(#E61919);
}
se transforma en
.boton-aceptar {
padding: 8px 16px;
border: none;
border-radius: 6px;
background-color: #1979E6;
color: #FFFFFF;
}
.boton-borrar {
padding: 8px 16px;
border: none;
border-radius: 6px;
background-color: #E61919;
color: #FFFFFF;
}
SASS tiene funciones para calcular colores a partir de otro: sass:color
Las más útiles son:
color.scale($color, $lightness: -20%): Un 20% más oscuro. Con un valor positivo, más claro. También vale con $saturation.color.mix($color1, $color2, $peso): Mezcla dos colores. $peso es cuánto hay del primero.
Mejoramos el mixin del botón para que el borde y el :hover sean un poco más oscuros que el color del botón, sin tener que calcularlos a mano:
@use "sass:color";
@mixin boton($color) {
padding: 8px 16px;
border: 1px solid color.scale($color, $lightness: -20%);
border-radius: 6px;
background-color: $color;
color: #FFFFFF;
&:hover {
background-color: color.scale($color, $lightness: -10%);
}
}
.boton-aceptar {
@include boton(#1979E6);
}
se transforma en
.boton-aceptar {
padding: 8px 16px;
border: 1px solid rgb(20, 96.8, 184);
border-radius: 6px;
background-color: #1979E6;
color: #FFFFFF;
}
.boton-aceptar:hover {
background-color: rgb(22.5, 108.9, 207);
}
SASS escribe los colores calculados con rgb() y decimales. El navegador los acepta tal cual.
Permite generar un CSS u otro según una condición.
En el botón, si el color de fondo es muy claro, el texto blanco no se lee: hay que cambiarlo a negro para que haya contraste. color.channel devuelve la luminosidad del color en HSL.
@use "sass:color";
@mixin boton($color) {
padding: 8px 16px;
border: 1px solid color.scale($color, $lightness: -20%);
border-radius: 6px;
background-color: $color;
@if color.channel($color, "lightness", $space: hsl) > 60% {
color: #000000;
} @else {
color: #FFFFFF;
}
&:hover {
background-color: color.scale($color, $lightness: -10%);
}
}
.boton-aceptar {
@include boton(#1979E6);
}
.boton-avisar {
@include boton(#ECC94B);
}
se transforma en
.boton-aceptar {
padding: 8px 16px;
border: 1px solid rgb(20, 96.8, 184);
border-radius: 6px;
background-color: #1979E6;
color: #FFFFFF;
}
.boton-aceptar:hover {
background-color: rgb(22.5, 108.9, 207);
}
.boton-avisar {
padding: 8px 16px;
border: 1px solid rgb(225.0452261307, 181.2864321608, 23.7547738693);
border-radius: 6px;
background-color: #ECC94B;
color: #000000;
}
.boton-avisar:hover {
background-color: rgb(233.0306532663, 192.5608040201, 46.8693467337);
}
El .boton-aceptar tendrá el texto blanco y el .boton-avisar (amarillo, que es claro) lo tendrá negro.
Con @use se usa en un fichero lo que hay en otro: variables, funciones y mixins. Por ejemplo, los colores en un fichero y el botón en otro:
$color-principal: #1979E6; $color-error: #E61919;
@use 'colores';
.boton-aceptar {
background-color: colores.$color-principal;
}
.boton-borrar {
background-color: colores.$color-error;
}
Con @use se cargan los estilos de otro fichero y, para evitar conflictos, las variables se acceden con el prefijo del nombre del fichero nombreFichero.$nombreVariable.
Si quieres usar las variables directamente sin el prefijo, puedes añadir la opción as *:
@use 'colores' as *;
.boton-aceptar {
background-color: $color-principal;
}
.boton-borrar {
background-color: $color-error;
}
_ ni .scss. Por qué empieza por _ está en Ficheros parciales.
Cuando tenemos varios ficheros con variables, mixins o funciones, puede ser pesado tener que importarlos uno por uno en cada archivo SASS.
Para simplificar, usamos @forward, que actúa como un "reexportador". Permite centralizar varios ficheros en uno solo y luego usarlos todos de golpe.
Estructura de ejemplo:
proyecto ├─ estilos │ ├─ _colores.scss │ ├─ _funciones.scss │ ├─ _mixins.scss │ └─ _index.scss └─ main.scss
Contenido de los ficheros:
$color-principal: #1979E6; $color-error: #E61919;
@function espacio($n) {
@return $n * 4px;
}
@mixin boton($color) {
padding: 8px 16px;
border: none;
border-radius: 6px;
background-color: $color;
color: #FFFFFF;
}
@forward 'colores'; @forward 'funciones'; @forward 'mixins';
@use 'estilos';
.boton-aceptar {
@include estilos.boton(estilos.$color-principal);
margin: estilos.espacio(2);
}
_index.scss lo carga automáticamente al hacer @use de la carpeta. Véase loading-a-directory
Todos los ficheros SASS empiezan por _ (_colores.scss, _index.scss, _c-panel.scss) excepto el que lo une todo, main.scss.
Los ficheros que empiezan por _ son parciales: SASS no genera un CSS para ellos, solo se cargan desde otro con @use o @forward. Así, al compilar la carpeta con npx sass --watch scss:css sale un único css/main.css con todo, que es el que se enlaza en el HTML (ver Sistema de diseño: Estructura de carpetas).
_, SASS genera también su propio CSS: un colores.css vacío o un c-panel.css con el mismo CSS que ya está dentro de main.css. La carpeta css se llena de ficheros que no se usan y no se sabe cuál hay que enlazar.
Dado el siguiente mapa con los 9 tonos de cada color de la paleta, generados con el Color Scheme Generator:
$colores: ( 'principal': (#011019, #052F4D, #0B4D83, #1363B4, #1979E6, #4B8EEC, #80ADF4, #B2CAFA, #E6EDFE), 'alternativo': (#190801, #4D1D05, #83370B, #B45613, #E67519, #EC9C4B, #F4BE80, #FADCB2, #FEF5E6), 'rojo': (#190501, #4D0D05, #83150B, #B41813, #E61919, #EC534B, #F48A80, #FABCB2, #FEEAE6), 'gris': (#1D2830, #323E49, #485461, #5C6775, #717D8E, #8A93A3, #A1A9BA, #B6BDCD, #CFD4E2) );
Genera las siguientes variables CSS:
:root {
--color-principal-1: #011019;
--color-principal-2: #052F4D;
...
--color-gris-8: #B6BDCD;
--color-gris-9: #CFD4E2;
}
--color-#{$nombre}-#{$i}: #{list.nth($tonos, $i)};
Crea la función tono($color, $n) que devuelva el tono $n (de 1 a 9) de un color:
color.mix) al 80%, 60%, 40% y 20%.
Úsala para generar los 9 tonos de #1979E6 como variables CSS --color-principal-1 … --color-principal-9 y compáralos con los del ejercicio anterior, que se hicieron con el Color Scheme Generator. ¿Por qué no salen iguales?
Dada la lista:
$escala-titulos: (48px, 36px, 30px, 24px, 20px, 18px);
Genera con un bucle el tamaño de fuente de los títulos h1 a h6:
h1 {
font-size: 48px;
}
h2 {
font-size: 36px;
}
...
h6 {
font-size: 18px;
}
Crea el mixin elevacion($nivel) que ponga una de las sombras del tema de diseño (la luz viene del cielo):
$sombras: ( 0 1px 3px rgb(0 0 0 / 20%), 0 4px 6px rgb(0 0 0 / 20%), 0 5px 15px rgb(0 0 0 / 20%), 0 10px 24px rgb(0 0 0 / 20%), 0 15px 35px rgb(0 0 0 / 20%) );
0 no tendrá sombra (box-shadow: none).1 a 5 tendrá la sombra correspondiente.Úsalo en una página con una tarjeta (nivel 2), un menú desplegable (nivel 3) y una ventana modal (nivel 5).
Crea el mixin desde($nombre) que use el mapa $breakpoints para generar la media query y meta dentro el CSS que se le pase:
h1 {
font-size: 30px;
@include desde('desktop') {
font-size: 48px;
}
}
@content es el CSS que se ha escrito entre las llaves del @include.
Rehaz con él las media queries del menú hamburguesa del tema de CSS responsivo.
Crea la función fluido($min, $max, $ancho-min, $ancho-max) que devuelva el clamp() calculando la pendiente y el coeficiente con las fórmulas del tema de CSS responsivo.
Comprueba que fluido(12px, 20px, 230px, 600px) devuelve clamp(12px, 2.1621621622vw + 7.027027027px, 20px).
math.div($a, $b) de @use "sass:math".vw y px, así que el resultado hay que escribirlo con interpolación: clamp(#{$min}, #{$pendiente}vw + #{$coeficiente}, #{$max}).
Haz una lista de 5 elementos que aparezcan uno detrás de otro: todos usan la misma animación (de opacity: 0 a opacity: 1) pero cada uno empieza 0.2s después que el anterior.
Genera con un bucle el animation-delay de cada elemento usando :nth-child().
Organiza en ficheros todo lo que has hecho en los ejercicios anteriores:
_colores.scss: El mapa de colores._funciones.scss: Las funciones tono y fluido._mixins.scss: Los mixins elevacion y desde._index.scss: Con los @forward.main.scss: Que lo use todo con un único @use.