====== 13. Sistema de diseño: Tokens ======
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:
* **Tokens de sistema**: Variables CSS globales que usan todos los componentes. En nuestro sistema empiezan por ''--mlt-sys-'' (como ''--mat-sys-'' en Angular Material).
* **Tokens de diseño**: Variables CSS públicas de cada componente, por ejemplo ''--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.
===== Números mágicos =====
Veamos el estilo del botón que hicimos en [[clase:daw:diw:angular-creacion-componentes|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 [[clase:daw:diw:css-basico#variables_css|CSS: Básico, «Variables CSS»]]): los tokens.
===== Tokens de sistema =====
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.
A las variables del sistema les ponemos el prefijo ''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 Material
Los 9 tonos de cada color se han generado con el [[https://logongas.github.io/color-schemes-generator/|Color Scheme Generator]]:
* [[https://logongas.github.io/color-schemes-generator/?p0=202-95-5&p1=205-88-16&p2=207-84-28&p3=210-81-39&p4=212-80-50&p5=215-81-61&p6=217-84-73&p7=220-88-84&p8=222-95-95&minLightness=5&maxSaturation=95&rotate-hue=10&ecuacion=parabola&tasa-crecimiento=8|principal]]
* [[https://logongas.github.io/color-schemes-generator/?p0=17-95-5&p1=20-88-16&p2=22-84-28&p3=25-81-39&p4=27-80-50&p5=30-81-61&p6=32-84-73&p7=35-88-84&p8=37-95-95&minLightness=5&maxSaturation=95&rotate-hue=10&ecuacion=parabola&tasa-crecimiento=8|alternativo]]
* [[https://logongas.github.io/color-schemes-generator/?p0=370-95-5&p1=367-88-16&p2=365-84-28&p3=362-81-39&p4=0-80-50&p5=3-81-61&p6=5-84-73&p7=8-88-84&p8=10-95-95&minLightness=5&maxSaturation=95&rotate-hue=10&ecuacion=parabola&tasa-crecimiento=8|rojo]]
* [[https://logongas.github.io/color-schemes-generator/?p0=205-25-15&p1=208-19-24&p2=210-15-33&p3=213-12-41&p4=215-11-50&p5=218-12-59&p6=220-15-68&p7=223-19-76&p8=225-25-85&minLightness=15&maxSaturation=25&rotate-hue=10&ecuacion=parabola&tasa-crecimiento=8|gris]]
* [[https://logongas.github.io/color-schemes-generator/?p0=180-0-92&p1=180-0-93&p2=180-0-94&p3=180-0-95&p4=180-0-96&p5=180-0-97&p6=180-0-98&p7=180-0-99&p8=180-0-100&minLightness=10&maxSaturation=95&rotate-hue=0&ecuacion=constante&tasa-crecimiento=8|blanco]]
* [[https://logongas.github.io/color-schemes-generator/?p0=45-95-22&p1=46-89-32&p2=46-85-42&p3=47-82-51&p4=47-81-61&p5=48-82-71&p6=48-84-81&p7=49-89-90&p8=49-95-100&minLightness=15&maxSaturation=95&rotate-hue=2&ecuacion=parabola&tasa-crecimiento=8|amarillo]]
: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?:
{{:clase:daw:diw:colores.png|}}
===== Tokens de diseño =====
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);
}
Dentro del botón hay dos tipos de variables:
* **Tokens de diseño** (''--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.
* **Estado privado** (''--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**:
* **Función**: Elige qué token de color se usa. ''c-boton--funcion-peligrosa'' pone en ''--boton-privateState-color'' el token ''--boton-color-peligrosa''.
* **Importancia**: Decide dónde va ese color: en la primaria el fondo y el borde, en la secundaria el borde y el texto, y en la terciaria solo el texto. La secundaria y la terciaria tienen el fondo ''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'' |
Para que los botones peligrosos sean de otro rojo se cambia el token ''--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.
Los nombres de las variables CSS distinguen mayúsculas de minúsculas: ''--boton-privatestate-text-color'' es otra variable y el botón no funcionaría.
===== Añadiendo el tamaño =====
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);
}
Lo que ha cambiado:
* **Tokens de diseño**: ''--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.
* **Estado privado**: ''--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.
* **Propiedades CSS**: ''font-size'' y ''padding'' ya no usan el token directamente sino el estado.
* **Modificador ''--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.
Si en ''.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.
===== Padding vertical y horizontal =====
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:
* **Tokens de diseño**: Cada ''--boton-padding-*'' se parte en dos, ''--boton-padding-vertical-*'' y ''--boton-padding-horizontal-*''. Con tres tamaños son seis tokens.
* **Estado privado**: ''--boton-privateState-padding'' se parte también en ''--boton-privateState-padding-vertical'' y ''--boton-privateState-padding-horizontal''.
* **Propiedades CSS**: ''padding'' recibe los dos valores: el primero es el vertical y el segundo el horizontal.
* **Modificadores ''--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.
===== Comparación con Angular Material =====
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'' |
===== Mejorando los tokens con SASS =====
==== Tokens de sistema ====
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 [[clase:daw:diw:sass#ejercicio_1|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.
==== Tokens de diseño ====
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);
}
}
===== Ejercicios =====
==== Ejercicio 1 ====
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;
}
==== Ejercicio 2 ====
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);
}
==== Ejercicio 3 ====
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 [[https://logongas.github.io/color-schemes-generator/|Color Scheme Generator]]) y comprueba que el botón cambia de color sin tocar su CSS.
==== Ejercicio 4 ====
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:
{{:clase:daw:diw:ejercicio_paneles.png?400|}}
* Los tokens de diseño se llaman ''--panel-*'' y su valor por defecto es un token de sistema.
* El color lo elige la función, con los modificadores ''c-panel--funcion-normal'', ''c-panel--funcion-alternativa'' y ''c-panel--funcion-peligrosa''.
* Los elementos ''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.