En el tema de BEM los componentes tenían los valores puestos directamente. En este tema vamos a hacer bien un componente usando tokens, que es lo mismo que hace Angular Material pero hecho por nosotros:
--mlt-sys- (como --mat-sys- en Angular Material).--boton-color-peligrosa (como --mat-button-filled-container-color en Angular Material). Su valor por defecto es un token de sistema.Además, cada componente tiene un estado privado: variables que no son tokens sino CSS que necesita el componente para funcionar y que no le interesan al diseñador.
Veamos el estilo del botón que hicimos en Angular: Creación de componentes (boton.scss):
.boton {
font-family: sans-serif;
font-size: 16px;
padding: 6px;
border-radius: 6px;
border-width: 1px;
border-style: solid;
display: inline-block;
cursor: pointer;
text-decoration: none;
border-color: #0056b8;
background-color: #0056b8;
color: #ffffff;
}
Desgraciadamente tiene muchos números mágicos, es decir números puestos directamente. Para evitarlo se usan variables CSS (cómo se crean y se usan está en CSS: Básico, «Variables CSS»): los tokens.
Los tokens de sistema son variables CSS globales con todos los valores que se pueden usar: colores, fuentes, tamaños, márgenes, etc. Así limitamos las opciones a una serie de valores, como vimos en el tema de diseño.
mlt (de Mislata) de la misma forma que mat es de Angular Material.
Y añadimos sys al igual que hace Angular Material para decir que son variable que se usan en todo el sistema ya que afectan a todos los componentes.
--mlt-sys-color-alternativo-5: En nuestro framework de clase en Mislata--mat-sys-secondary: En Angular MaterialLos 9 tonos de cada color se han generado con el Color Scheme Generator:
:root {
--mlt-sys-color-principal-1: #011019;
--mlt-sys-color-principal-2: #052F4D;
--mlt-sys-color-principal-3: #0B4D83;
--mlt-sys-color-principal-4: #1363B4;
--mlt-sys-color-principal-5: #1979E6;
--mlt-sys-color-principal-6: #4B8EEC;
--mlt-sys-color-principal-7: #80ADF4;
--mlt-sys-color-principal-8: #B2CAFA;
--mlt-sys-color-principal-9: #E6EDFE;
--mlt-sys-color-alternativo-1: #190801;
--mlt-sys-color-alternativo-2: #4D1D05;
--mlt-sys-color-alternativo-3: #83370B;
--mlt-sys-color-alternativo-4: #B45613;
--mlt-sys-color-alternativo-5: #E67519;
--mlt-sys-color-alternativo-6: #EC9C4B;
--mlt-sys-color-alternativo-7: #F4BE80;
--mlt-sys-color-alternativo-8: #FADCB2;
--mlt-sys-color-alternativo-9: #FEF5E6;
--mlt-sys-color-rojo-1: #190501;
--mlt-sys-color-rojo-2: #4D0D05;
--mlt-sys-color-rojo-3: #83150B;
--mlt-sys-color-rojo-4: #B41813;
--mlt-sys-color-rojo-5: #E61919;
--mlt-sys-color-rojo-6: #EC534B;
--mlt-sys-color-rojo-7: #F48A80;
--mlt-sys-color-rojo-8: #FABCB2;
--mlt-sys-color-rojo-9: #FEEAE6;
--mlt-sys-color-gris-1: #1D2830;
--mlt-sys-color-gris-2: #323E49;
--mlt-sys-color-gris-3: #485461;
--mlt-sys-color-gris-4: #5C6775;
--mlt-sys-color-gris-5: #717D8E;
--mlt-sys-color-gris-6: #8A93A3;
--mlt-sys-color-gris-7: #A1A9BA;
--mlt-sys-color-gris-8: #B6BDCD;
--mlt-sys-color-gris-9: #CFD4E2;
--mlt-sys-color-blanco-1: #EBEBEB;
--mlt-sys-color-blanco-2: #EDEDED;
--mlt-sys-color-blanco-3: #F0F0F0;
--mlt-sys-color-blanco-4: #F2F2F2;
--mlt-sys-color-blanco-5: #F5F5F5;
--mlt-sys-color-blanco-6: #F7F7F7;
--mlt-sys-color-blanco-7: #FAFAFA;
--mlt-sys-color-blanco-8: #FCFCFC;
--mlt-sys-color-blanco-9: #FFFFFF;
--mlt-sys-color-amarillo-1: #6D5303;
--mlt-sys-color-amarillo-2: #9A7809;
--mlt-sys-color-amarillo-3: #C69C10;
--mlt-sys-color-amarillo-4: #E9BC1C;
--mlt-sys-color-amarillo-5: #ECC94B;
--mlt-sys-color-amarillo-6: #F2D978;
--mlt-sys-color-amarillo-7: #F7E7A6;
--mlt-sys-color-amarillo-8: #FCF4CF;
--mlt-sys-color-amarillo-9: #FFFFFF;
--mlt-sys-font-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mlt-sys-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--mlt-sys-font-serif: Palatino, "Palatino Linotype", "Book Antiqua", Georgia, "Times New Roman", Times, serif;
--mlt-sys-font-default: var(--mlt-sys-font-sans-serif);
--mlt-sys-font-size-1: 10px;
--mlt-sys-font-size-2: 12px;
--mlt-sys-font-size-3: 14px;
--mlt-sys-font-size-4: 16px;
--mlt-sys-font-size-5: 18px;
--mlt-sys-font-size-6: 20px;
--mlt-sys-font-size-7: 22px;
--mlt-sys-font-size-8: 24px;
--mlt-sys-font-size-9: 26px;
--mlt-sys-font-size-10: 28px;
--mlt-sys-font-size-11: 30px;
--mlt-sys-margin-0: 0px;
--mlt-sys-margin-1: 4px;
--mlt-sys-margin-2: 8px;
--mlt-sys-margin-3: 12px;
--mlt-sys-margin-4: 16px;
--mlt-sys-margin-5: 20px;
--mlt-sys-margin-6: 24px;
--mlt-sys-margin-7: 28px;
--mlt-sys-margin-8: 32px;
--mlt-sys-margin-9: 36px;
--mlt-sys-margin-10: 40px;
--mlt-sys-padding-0: 0px;
--mlt-sys-padding-1: 4px;
--mlt-sys-padding-2: 8px;
--mlt-sys-padding-3: 12px;
--mlt-sys-padding-4: 16px;
--mlt-sys-padding-5: 20px;
--mlt-sys-padding-6: 24px;
--mlt-sys-padding-7: 28px;
--mlt-sys-padding-8: 32px;
--mlt-sys-padding-9: 36px;
--mlt-sys-padding-10: 40px;
--mlt-sys-border-radius-1: 1px;
--mlt-sys-border-radius-2: 2px;
--mlt-sys-border-radius-3: 4px;
--mlt-sys-border-radius-4: 6px;
--mlt-sys-width-0: 0px;
--mlt-sys-width-1: 1px;
--mlt-sys-width-2: 4px;
--mlt-sys-width-3: 8px;
--mlt-sys-width-4: 12px;
--mlt-sys-width-5: 16px;
--mlt-sys-width-6: 20px;
--mlt-sys-width-7: 24px;
--mlt-sys-width-8: 28px;
--mlt-sys-width-9: 32px;
--mlt-sys-width-10: 36px;
--mlt-sys-width-11: 40px;
}
Esas variables se usarán siempre en vez de poner directamente números. El botón, ya con el nombre BEM del bloque (c-boton), queda así:
.c-boton {
font-family: var(--mlt-sys-font-default);
font-size: var(--mlt-sys-font-size-4);
padding: var(--mlt-sys-padding-3);
border-radius: var(--mlt-sys-border-radius-4);
border-width: var(--mlt-sys-width-1);
border-style: solid;
display: inline-block;
cursor: pointer;
text-decoration: none;
border-color: var(--mlt-sys-color-principal-5);
background-color: var(--mlt-sys-color-principal-5);
color: var(--mlt-sys-color-blanco-9);
}
Algunas de las variables de colores en la página principal de google, ¿te suena de algo?:
Pero aun se tiene que mejorar la legibilidad del código. En vez de usar directamente los tokens de sistema, el botón crea sus propias variables:
.c-boton {
/* Tokens de diseño: decisiones del diseñador, un valor cada uno */
--boton-color-normal: var(--mlt-sys-color-principal-5);
--boton-color-alternativa: var(--mlt-sys-color-alternativo-5);
--boton-color-peligrosa: var(--mlt-sys-color-rojo-5);
--boton-color-texto-primaria: var(--mlt-sys-color-blanco-9);
--boton-font-family: var(--mlt-sys-font-default);
--boton-font-size: var(--mlt-sys-font-size-4);
--boton-padding: var(--mlt-sys-padding-3);
--boton-border-width: var(--mlt-sys-width-1);
--boton-border-radius: var(--mlt-sys-border-radius-4);
/* Estado privado: por defecto, normal y primaria */
--boton-privateState-color: var(--boton-color-normal);
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
/* Propiedades CSS: solo usan el estado y los tokens */
font-family: var(--boton-font-family);
font-size: var(--boton-font-size);
padding: var(--boton-padding);
border-radius: var(--boton-border-radius);
border-width: var(--boton-border-width);
border-style: solid;
display: inline-block;
cursor: pointer;
text-decoration: none;
border-color: var(--boton-privateState-border-color);
background-color: var(--boton-privateState-background-color);
color: var(--boton-privateState-text-color);
}
/* La función elige qué token de color se usa */
.c-boton--funcion-normal {
--boton-privateState-color: var(--boton-color-normal);
}
.c-boton--funcion-alternativa {
--boton-privateState-color: var(--boton-color-alternativa);
}
.c-boton--funcion-peligrosa {
--boton-privateState-color: var(--boton-color-peligrosa);
}
/* La importancia decide dónde va el color: fondo, borde o texto */
.c-boton--importancia-primaria {
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
}
.c-boton--importancia-secundaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-privateState-color);
}
.c-boton--importancia-terciaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: transparent;
--boton-privateState-text-color: var(--boton-privateState-color);
}
<button class="c-boton c-boton--funcion-peligrosa c-boton--importancia-secundaria">Borrar</button>
Dentro del botón hay dos tipos de variables:
--boton-*): Son las decisiones del diseñador: de qué color es el botón normal, el alternativo y el peligroso, el color del texto en la primaria, la fuente, el tamaño, el redondeo… Cada token tiene un único valor, que por defecto es un token de sistema.--boton-privateState-*): Guarda cómo está el botón ahora mismo: qué color le toca y qué color va en el fondo, el borde y el texto. No son tokens sino algo informático: el CSS que hace falta para implementar el botón y que al diseñador no le interesa.
Para saber si una variable es un token o es estado: si su valor depende del modificador que lleve el botón, es estado. --boton-color-peligrosa vale siempre rojo; --boton-privateState-color vale el color que le toque según su función.
Por eso los modificadores solo cambian el estado, nunca los tokens:
c-boton--funcion-peligrosa pone en --boton-privateState-color el token --boton-color-peligrosa.transparent, así que quedan bien sobre cualquier fondo. En la terciaria el borde también es transparent: está para que el botón no cambie de tamaño, pero no se ve. Eso es un truco de CSS, no una decisión de diseño.
transparent no es un token: es una palabra clave de CSS y no hay nada que el diseñador pueda elegir.
| Token de diseño | Estado privado | |
|---|---|---|
| Qué guarda | Una decisión del diseñador | Cómo está el botón ahora |
| Nombre | --boton-* | --boton-privateState-* |
| Su valor | Uno solo, por defecto un token de sistema | Depende de los modificadores |
| Quién lo cambia | El diseñador o quien personaliza el botón | Solo los modificadores del botón |
| Ejemplo | --boton-color-peligrosa | --boton-privateState-background-color |
--boton-color-peligrosa, nunca el estado. Si en un botón secundario redefines --boton-privateState-background-color, pintas de rojo el fondo que debía ser transparente y el borde y el texto se quedan como estaban.
--boton-privatestate-text-color es otra variable y el botón no funcionaría.
Ahora necesitamos botones de tres tamaños, con los modificadores c-boton--size-s, c-boton--size-m y c-boton--size-l. El tamaño cambia el font-size y el padding, así que el botón crece: más tokens de diseño, más estado privado y un modificador nuevo. Lo nuevo está resaltado:
.c-boton {
/* Tokens de diseño: decisiones del diseñador, un valor cada uno */
--boton-color-normal: var(--mlt-sys-color-principal-5);
--boton-color-alternativa: var(--mlt-sys-color-alternativo-5);
--boton-color-peligrosa: var(--mlt-sys-color-rojo-5);
--boton-color-texto-primaria: var(--mlt-sys-color-blanco-9);
--boton-font-family: var(--mlt-sys-font-default);
--boton-font-size-s: var(--mlt-sys-font-size-3);
--boton-font-size-m: var(--mlt-sys-font-size-4);
--boton-font-size-l: var(--mlt-sys-font-size-6);
--boton-padding-s: var(--mlt-sys-padding-2);
--boton-padding-m: var(--mlt-sys-padding-3);
--boton-padding-l: var(--mlt-sys-padding-4);
--boton-border-width: var(--mlt-sys-width-1);
--boton-border-radius: var(--mlt-sys-border-radius-4);
/* Estado privado: por defecto, normal, primaria y tamaño m */
--boton-privateState-color: var(--boton-color-normal);
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
--boton-privateState-font-size: var(--boton-font-size-m);
--boton-privateState-padding: var(--boton-padding-m);
/* Propiedades CSS: solo usan el estado y los tokens */
font-family: var(--boton-font-family);
font-size: var(--boton-privateState-font-size);
padding: var(--boton-privateState-padding);
border-radius: var(--boton-border-radius);
border-width: var(--boton-border-width);
border-style: solid;
display: inline-block;
cursor: pointer;
text-decoration: none;
border-color: var(--boton-privateState-border-color);
background-color: var(--boton-privateState-background-color);
color: var(--boton-privateState-text-color);
}
/* La función elige qué token de color se usa */
.c-boton--funcion-normal {
--boton-privateState-color: var(--boton-color-normal);
}
.c-boton--funcion-alternativa {
--boton-privateState-color: var(--boton-color-alternativa);
}
.c-boton--funcion-peligrosa {
--boton-privateState-color: var(--boton-color-peligrosa);
}
/* La importancia decide dónde va el color: fondo, borde o texto */
.c-boton--importancia-primaria {
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
}
.c-boton--importancia-secundaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-privateState-color);
}
.c-boton--importancia-terciaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: transparent;
--boton-privateState-text-color: var(--boton-privateState-color);
}
/* El tamaño elige qué tokens de font-size y padding se usan */
.c-boton--size-s {
--boton-privateState-font-size: var(--boton-font-size-s);
--boton-privateState-padding: var(--boton-padding-s);
}
.c-boton--size-m {
--boton-privateState-font-size: var(--boton-font-size-m);
--boton-privateState-padding: var(--boton-padding-m);
}
.c-boton--size-l {
--boton-privateState-font-size: var(--boton-font-size-l);
--boton-privateState-padding: var(--boton-padding-l);
}
<button class="c-boton c-boton--funcion-peligrosa c-boton--importancia-secundaria c-boton--size-s">Borrar</button> <button class="c-boton c-boton--size-m">Guardar</button> <button class="c-boton c-boton--funcion-alternativa c-boton--size-l">Comprar</button>
Lo que ha cambiado:
--boton-font-size y --boton-padding pasan a ser un token por tamaño (--boton-font-size-s, -m, -l y lo mismo con el padding). Un token tiene un único valor, así que si hay tres tamaños hay tres tokens.--boton-privateState-font-size y --boton-privateState-padding guardan el tamaño que le toca al botón ahora. Por defecto es el m, así que un botón sin modificador de tamaño sigue igual que antes.font-size y padding ya no usan el token directamente sino el estado.--size-*: Como los de función e importancia, solo cambia el estado: elige qué tokens de font-size y padding se usan.El tamaño no toca las variables de la función ni de la importancia, por eso se combina con ellas sin problemas: un botón puede ser peligroso, secundario y pequeño a la vez. Cada nuevo modificador es un grupo más de estado privado.
.c-boton--size-l pones directamente font-size: var(--boton-font-size-l), funciona, pero el botón ya no sigue la regla de que las propiedades CSS solo usan el estado y los tokens. Los modificadores siempre cambian el estado, nunca las propiedades CSS.
El último extra: el padding vertical y el horizontal no tienen por qué ser iguales. Un botón queda mejor con más padding a los lados que arriba y abajo, así que cada tamaño necesita dos paddings. Otra vez el botón crece: más tokens de diseño y más estado privado, pero ningún modificador nuevo. Lo nuevo está resaltado:
.c-boton {
/* Tokens de diseño: decisiones del diseñador, un valor cada uno */
--boton-color-normal: var(--mlt-sys-color-principal-5);
--boton-color-alternativa: var(--mlt-sys-color-alternativo-5);
--boton-color-peligrosa: var(--mlt-sys-color-rojo-5);
--boton-color-texto-primaria: var(--mlt-sys-color-blanco-9);
--boton-font-family: var(--mlt-sys-font-default);
--boton-font-size-s: var(--mlt-sys-font-size-3);
--boton-font-size-m: var(--mlt-sys-font-size-4);
--boton-font-size-l: var(--mlt-sys-font-size-6);
--boton-padding-vertical-s: var(--mlt-sys-padding-1);
--boton-padding-vertical-m: var(--mlt-sys-padding-2);
--boton-padding-vertical-l: var(--mlt-sys-padding-3);
--boton-padding-horizontal-s: var(--mlt-sys-padding-3);
--boton-padding-horizontal-m: var(--mlt-sys-padding-4);
--boton-padding-horizontal-l: var(--mlt-sys-padding-6);
--boton-border-width: var(--mlt-sys-width-1);
--boton-border-radius: var(--mlt-sys-border-radius-4);
/* Estado privado: por defecto, normal, primaria y tamaño m */
--boton-privateState-color: var(--boton-color-normal);
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
--boton-privateState-font-size: var(--boton-font-size-m);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-m);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-m);
/* Propiedades CSS: solo usan el estado y los tokens */
font-family: var(--boton-font-family);
font-size: var(--boton-privateState-font-size);
padding: var(--boton-privateState-padding-vertical) var(--boton-privateState-padding-horizontal);
border-radius: var(--boton-border-radius);
border-width: var(--boton-border-width);
border-style: solid;
display: inline-block;
cursor: pointer;
text-decoration: none;
border-color: var(--boton-privateState-border-color);
background-color: var(--boton-privateState-background-color);
color: var(--boton-privateState-text-color);
}
/* La función elige qué token de color se usa */
.c-boton--funcion-normal {
--boton-privateState-color: var(--boton-color-normal);
}
.c-boton--funcion-alternativa {
--boton-privateState-color: var(--boton-color-alternativa);
}
.c-boton--funcion-peligrosa {
--boton-privateState-color: var(--boton-color-peligrosa);
}
/* La importancia decide dónde va el color: fondo, borde o texto */
.c-boton--importancia-primaria {
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
}
.c-boton--importancia-secundaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-privateState-color);
}
.c-boton--importancia-terciaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: transparent;
--boton-privateState-text-color: var(--boton-privateState-color);
}
/* El tamaño elige qué tokens de font-size y padding se usan */
.c-boton--size-s {
--boton-privateState-font-size: var(--boton-font-size-s);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-s);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-s);
}
.c-boton--size-m {
--boton-privateState-font-size: var(--boton-font-size-m);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-m);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-m);
}
.c-boton--size-l {
--boton-privateState-font-size: var(--boton-font-size-l);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-l);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-l);
}
Lo que ha cambiado:
--boton-padding-* se parte en dos, --boton-padding-vertical-* y --boton-padding-horizontal-*. Con tres tamaños son seis tokens.--boton-privateState-padding se parte también en --boton-privateState-padding-vertical y --boton-privateState-padding-horizontal.padding recibe los dos valores: el primero es el vertical y el segundo el horizontal.--size-*: Siguen siendo los mismos, solo que ahora ponen dos variables de padding en vez de una.El HTML no cambia: los botones se siguen usando igual que antes. Eso es lo bueno de separar tokens, estado y propiedades: el componente crece por dentro sin que se entere quien lo usa.
Nuestro sistema es una versión sencilla de lo que hace Angular Material:
| Angular Material | Nuestro sistema | |
|---|---|---|
| Token de sistema | --mat-sys-primary | --mlt-sys-color-principal-5 |
| Token de diseño | --mat-button-filled-container-color | --boton-color-normal |
| El token de diseño toma el valor de un token de sistema | var(--mat-button-filled-container-color, var(--mat-sys-primary)) | --boton-color-normal: var(--mlt-sys-color-principal-5) |
| Cambiar un componente | mixin mat.button-overrides(…) | redefinir sus tokens de diseño --boton-* (nunca el privateState) |
| Cambiar todo el sistema | el tema (mat.theme) | cambiar las variables de SASS que generan las --mlt-sys-* |
| Variantes del componente | matButton="filled", "outlined", "text" | modificadores --importancia-primaria, -secundaria, -terciaria |
Escribir a mano todos los tokens de sistema es largo y es fácil equivocarse. Con SASS se guardan los valores en listas y mapas y los tokens se generan con bucles. Así, para cambiar el sistema solo hay que cambiar las variables de SASS. Los colores se generan igual que en el ejercicio 1 de SASS, ahora con el prefijo --mlt-sys-color-.
@use "sass:list";
$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),
'blanco': (#EBEBEB, #EDEDED, #F0F0F0, #F2F2F2, #F5F5F5, #F7F7F7, #FAFAFA, #FCFCFC, #FFFFFF),
'amarillo': (#6D5303, #9A7809, #C69C10, #E9BC1C, #ECC94B, #F2D978, #F7E7A6, #FCF4CF, #FFFFFF)
);
$font-sizes: (10px, 12px, 14px, 16px, 18px, 20px, 22px, 24px, 26px, 28px, 30px);
:root {
@each $nombre, $tonos in $colores {
@for $i from 1 through list.length($tonos) {
--mlt-sys-color-#{$nombre}-#{$i}: #{list.nth($tonos, $i)};
}
}
@for $i from 1 through list.length($font-sizes) {
--mlt-sys-font-size-#{$i}: #{list.nth($font-sizes, $i)};
}
}
se transforma en
:root {
--mlt-sys-color-principal-1: #011019;
--mlt-sys-color-principal-2: #052F4D;
...
--mlt-sys-color-amarillo-9: #FFFFFF;
--mlt-sys-font-size-1: 10px;
...
--mlt-sys-font-size-11: 30px;
}
El resto de tokens de sistema (márgenes, paddings, fuentes…) se generan igual. Es lo que se pide en los ejercicios.
El botón con sus modificadores, con el selector padre &:
.c-boton {
// Tokens de diseño: decisiones del diseñador, un valor cada uno
--boton-color-normal: var(--mlt-sys-color-principal-5);
--boton-color-alternativa: var(--mlt-sys-color-alternativo-5);
--boton-color-peligrosa: var(--mlt-sys-color-rojo-5);
--boton-color-texto-primaria: var(--mlt-sys-color-blanco-9);
--boton-font-family: var(--mlt-sys-font-default);
--boton-font-size-s: var(--mlt-sys-font-size-3);
--boton-font-size-m: var(--mlt-sys-font-size-4);
--boton-font-size-l: var(--mlt-sys-font-size-6);
--boton-padding-vertical-s: var(--mlt-sys-padding-1);
--boton-padding-vertical-m: var(--mlt-sys-padding-2);
--boton-padding-vertical-l: var(--mlt-sys-padding-3);
--boton-padding-horizontal-s: var(--mlt-sys-padding-3);
--boton-padding-horizontal-m: var(--mlt-sys-padding-4);
--boton-padding-horizontal-l: var(--mlt-sys-padding-6);
--boton-border-width: var(--mlt-sys-width-1);
--boton-border-radius: var(--mlt-sys-border-radius-4);
// Estado privado: por defecto, normal, primaria y tamaño m
--boton-privateState-color: var(--boton-color-normal);
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
--boton-privateState-font-size: var(--boton-font-size-m);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-m);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-m);
// Propiedades CSS: solo usan el estado y los tokens
font-family: var(--boton-font-family);
font-size: var(--boton-privateState-font-size);
padding: var(--boton-privateState-padding-vertical) var(--boton-privateState-padding-horizontal);
border-radius: var(--boton-border-radius);
border-width: var(--boton-border-width);
border-style: solid;
display: inline-block;
cursor: pointer;
text-decoration: none;
border-color: var(--boton-privateState-border-color);
background-color: var(--boton-privateState-background-color);
color: var(--boton-privateState-text-color);
// La función elige qué token de color se usa
&--funcion-normal {
--boton-privateState-color: var(--boton-color-normal);
}
&--funcion-alternativa {
--boton-privateState-color: var(--boton-color-alternativa);
}
&--funcion-peligrosa {
--boton-privateState-color: var(--boton-color-peligrosa);
}
// La importancia decide dónde va el color: fondo, borde o texto
&--importancia-primaria {
--boton-privateState-background-color: var(--boton-privateState-color);
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-color-texto-primaria);
}
&--importancia-secundaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: var(--boton-privateState-color);
--boton-privateState-text-color: var(--boton-privateState-color);
}
&--importancia-terciaria {
--boton-privateState-background-color: transparent;
--boton-privateState-border-color: transparent;
--boton-privateState-text-color: var(--boton-privateState-color);
}
// El tamaño elige qué tokens de font-size y padding se usan
&--size-s {
--boton-privateState-font-size: var(--boton-font-size-s);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-s);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-s);
}
&--size-m {
--boton-privateState-font-size: var(--boton-font-size-m);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-m);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-m);
}
&--size-l {
--boton-privateState-font-size: var(--boton-font-size-l);
--boton-privateState-padding-vertical: var(--boton-padding-vertical-l);
--boton-privateState-padding-horizontal: var(--boton-padding-horizontal-l);
}
}
Dadas las listas:
$paddings: (0px, 4px, 8px, 12px, 16px, 20px, 24px, 28px, 32px, 36px, 40px); $margins: (0px, 4px, 8px, 12px, 16px, 20px, 24px, 28px, 32px, 36px, 40px);
Genera los tokens de sistema de paddings y márgenes. Empiezan en 0:
:root {
--mlt-sys-padding-0: 0px;
--mlt-sys-padding-1: 4px;
...
--mlt-sys-padding-10: 40px;
--mlt-sys-margin-0: 0px;
...
--mlt-sys-margin-10: 40px;
}
Dado el mapa:
$fuentes: ( 'sans-serif': (-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif), 'serif': (Palatino, "Palatino Linotype", "Book Antiqua", Georgia, "Times New Roman", Times, serif), 'monospace': (SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace) );
Genera los tokens de sistema de las fuentes y el de la fuente por defecto, que es la sans-serif:
:root {
--mlt-sys-font-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mlt-sys-font-serif: Palatino, "Palatino Linotype", "Book Antiqua", Georgia, "Times New Roman", Times, serif;
--mlt-sys-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--mlt-sys-font-default: var(--mlt-sys-font-sans-serif);
}
Junta en un único fichero SASS todas las variables de SASS del sistema (colores, tamaños de fuente, márgenes, paddings, fuentes, border-radius y anchos) y genera con él todo el :root de los tokens de sistema del tema.
Cambia ahora el color principal por un verde (genera sus 9 tonos con el Color Scheme Generator) y comprueba que el botón cambia de color sin tocar su CSS.
Haz en SASS el componente c-panel con tokens de diseño y estado privado, igual que el botón, para que se puedan hacer estos 3 paneles:
--panel-* y su valor por defecto es un token de sistema.c-panel--funcion-normal, c-panel--funcion-alternativa y c-panel--funcion-peligrosa.c-panel__titulo y c-panel__contenido no llevan modificadores: el color les llega por el estado privado del bloque.Crea una página HTML con los 3 paneles y comprueba que, al cambiar el color principal por el verde del ejercicio 3, el panel normal también cambia sin tocar su CSS.