====== 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.