Filtrando tokens con Style Dictionary
Aprenderemos como definir filtros para poder agrupar los tokens en diferentes ficheros
Continuamos evolucionando el ejemplo que empezamos hace unas semanas de introducción a Style Dictionary.
En el mencionado artículo hicimos un ejemplo desde 0 para a partir de unos ficheros JSON con nuestros tokens, poder generar ficheros consumibles por nuestras plataformas. Concretamente, generamos un fichero variables.css con todas las variables CSS listas para usarse en un navegador.
Hoy veremos como filtrar tokens a la hora de generar ficheros y así poder tener consumibles según el tipo de token.
Objetivo
Ahora tenemos un único fichero con extensión .css donde se encuentran todos nuestros tokens, pero esto en un escenario real es poco útil. Qué pasa si solo quiero usar los tokens relativos a las fuentes? Pues con nuestro panorama actual nos veríamos obligados a importar el fichero variables.css con todo.
Es cierto que en nuestro ejemplo el fichero variables.css solo tiene una docena de tokens, pero en un ejemplo real acabaremos con un fichero con cientos (sino miles) de lineas (no solo de nuestros tokens fundacionales, sino de todos los alias que podamos generar).
El objetivo de este post será partir del ejemplo tal y como lo dejamos en este artículo, donde generamos un único fichero con todo, a generar dos ficheros:
colors.css,
fonts.css.
Para ello tendremos que hacer algunas modificaciones en el cómo configuramos Style Dictionary e introducir el concepto de filtros.
Style Dictionary API
Cuando empezamos hablando de esta herramienta, lo hicimos usando la forma más primitiva y básica de configuración. Esto es, a través de un fichero config.json que el comando “style-dictionary build” leerá en el momento de ejecutarlo, y el cuál define como interpretar los ficheros con tokens y que queremos generar.
Para este caso, vamos extender la configuración que definimos desde el API de Style Dictionary para poder generar posteriormente los distintos ficheros.
Para el que no tenga un perfil de desarrollador, un API, o Interfaz de Programación de Aplicaciones (por sus siglas en inglés), es un conjunto de reglas y definiciones que permite a diferentes aplicaciones o servicios comunicarse entre sí. Básicamente, actúa como un intermediario que permite que dos programas informáticos se comuniquen y compartan datos entre ellos de manera estandarizada.
Vamos a ir haciendo la transformación a usar el API de SD paso a paso, no obstante te dejaremos el código con todos los cambios al final del post.
Cambios en el package.json
Primero, vamos a decirle a nuestro modulo NPM que se comporte como un módulo.
Esto es, una config un poco redundante (cosas del ecosistema de NodeJS), pero que viene a decir que podamos utilizar el sistema de imports de ES6, que a su vez quiere decir que podamos usar un estilo de código mucho más intuitivo y amigable que CommonJS (ya se que si no eres desarrollador, todo esto son “palabros”).
El cambio es sencillo, solo hay que agregar una nueva propiedad llamada “type” y con valor “module” en nuestro package.json:
{
"name": "getting-started-sd",
"type": "module", <--- Esto es lo que hemos metido
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "style-dictionary build"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"style-dictionary": "^3.9.2"
}
}
Nuevo punto de entrada
Genial, ahora vamos a crear un nuevo fichero Javascript en la raíz de nuestro proyecto (a la misma altura donde tenemos el package.json) y al que vamos a llamar build.js.
En el, meteremos el siguiente código:
import StyleDictionary from "style-dictionary";
import Config from './config.json' assert { type: "json" };
const SD = StyleDictionary.extend(Config);
SD.buildAllPlatforms();
Primero importamos el módulo de Style Dictionary, después importamos nuestro fichero de configuración.
En la cuarta línea estamos creando un nuevo objeto con acceso a todo el API que extiende nuestro fichero de configuración.
Finalmente, usando el método buildAllPlatforms le decimos a Style Dictionary que debe generar los ficheros usando el fichero de configuración con el que hemos extendido.
Esto a resumidas cuentas va a generar lo mismo que ya teníamos, solo que en lugar de dejar a Style Dictionary que manualmente busque el fichero config.json, lea su contenido y construya todas las plataformas ahí definidas, lo hacemos manualmente para que el futuro ganemos control sobre el como y el cuando.
Para poder probar esto, necesitamos cambiar la forma en la que compilamos. Por tanto, en el apartado scripts de nuestro package.json tendremos que modificar el comando build a esto:
{
"name": "getting-started-sd",
"type": "module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "node build.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"style-dictionary": "^3.9.2"
}
}
Cambiamos el comando “style-dictionary build” a “node build.js” para indicar que ahora usaremos style-dictionary desde el API en lugar desde su utilidad de CLI.
Si ahora ejecutamos el comando:
npm run build
Deberíamos de obtener el mismo resultado que teníamos antes de empezar.
Filtros
Ahora ya ha llegado el momento de introducir el concepto de un filtro en relación a tokens y Style Dictionary.
Un filtro es una función que se ejecuta para cada token y que nos permite comprobar si la categoría, el tipo o el ítem del mismo coinciden con nuestros criterios, para así poder ignorar tokens, procesarlos o enviarlos a ficheros específicos.
La magia de esto viene cuando sabemos que en los ficheros de destino que definimos en nuestro config.json podemos especificar filtros para así poder generar un fichero donde se guarden solo los tokens que cumplan ese filtro.
Usando los filtros
Para usar un filtro primero necesitamos saber para que los queremos. En nuestro caso, queremos generar un fichero colors.css donde se generen solo las variables de colores y otro fichero fonts.css para las variables de las fuentes. Para ello necesitaremos dos filtros:
Uno que filtre por la categoría color.
Otro que filtre por el tipo font.
En el caso de colores la categoría es suficientemente de alto nivel para poder agrupar a todos los colores, pero en el caso de las fuentes que se agrupan bajo una misma categoría llamada “size”, necesitamos un atributo de un nivel inferior para poder apuntar solo a la parte de “fonts”. Es por eso que usamos el atributo “tipo”.
Antes de entrar en como definir estos filtros vamos a dejar el fichero config.json preparado para usar estos hipotéticos filtros:
{
"source": ["tokens/**/*.json"],
"platforms": {
"css": {
"transformGroup": "css",
"files": [
{
"format": "css/variables",
"destination": "dist/colors.css",
"filter": "isColor"
},
{
"format": "css/variables",
"destination": "dist/fonts.css",
"filter": "isFont"
}
]
},
"javascript": {}
}
}
Como puedes ver, hemos definido dos ficheros usando el mismo formato (puesto que en ambos casos queremos variables de CSS), con un destino especifico para cada uno (dist/fonts.css para las fuentes y dist/colors.css para los colores) y además hemos establecido dos filtros usando la propiedad “filter” de cada fichero.
Si intentamos ejecutar ahora el comando “npm run build” nos generará un error indicando que los filtros definidos no existen. Es un error esperado, puesto que aún no hemos definido los filtros.
Definiendo los filtros
Para que nuestros ficheros se generen correctamente con las variables de cada tipo, necesitamos definir los filtros que hemos especificado en el fichero config.json.
Para ello vamos a recurrir al API de Style Dictionary, concretamente a su método registerFilter que recibe un objeto con las siguientes propiedades:
name: el nombre del filtro. Tiene que coincidir con el nombre que le hayamos dado a la hora de usarlo en nuestro config.json.
matcher: una función que toma un token como primer parámetro y que espera que devuelva un boolean (true o false). Es la función que usaremos para establecer la lógica de nuestro filtro.
Sin más dilación, vamos a definir el filtro isColor en nuestro fichero build.js:
SD.registerFilter({
name: 'isColor',
matcher: function (token) {
return token.attributes.category === 'color';
}
});
Es un filtro que se apoya en la categoría “color” para funcionar, ya que queremos que todos los tokens bajo esta categoría pasen este filtro.
La posición del registerFilter en el fichero build.js debe de ser después de definir la variable SD, pero antes de ejecutar el buldAllPlatforms().
Ahora para el filtro de isFont la mecánica es igual pero la lógica es ligeramente diferente:
SD.registerFilter({
name: 'isFont',
matcher: function (token) {
return token.attributes.type === 'font';
}
});
En este caso puesto que la categoría (size) es demasiado genérica, necesitamos apoyarnos en el tipo del token para poder filtrar solo los tokens que pertenezcan al tipo “font”.
Todo esto funciona en el caso de que estemos usando CTI. Si quieres saber más te recomendamos que leas esto.
Generando los nuevos ficheros
Y con esto estaría todo configurado. Ya solo necesitamos ejecutar:
npm run build
Y si todo ha ido bien, veremos en nuestra carpeta dist dos nuevos ficheros:
colors.css,
fonts.css.
Y dentro de ellos tendremos las variables solo para el tipo del token que agrupan. Justo lo que buscábamos:
// colors.css
:root {
--color-base-primary: #007bff;
--color-base-secondary: #00ff00;
--color-background-button-primary: #007bff;
--color-background-button-secondary: #00ff00;
--color-text-primary: #ffffff;
--color-text-secondary: #333333;
}
// fonts.css
:root {
--size-font-base: 16rem;
--size-font-medium: 18rem;
--size-font-large: 24rem;
}
Conclusión
Como has podido comprobar una vez más, Style Dictionary es sumamente flexible y nos permite hacer casi cualquier cosa que se nos pueda ocurrir relativo al mundo de los design tokens.
Para los que queráis, os hemos dejado el código en el siguiente repositorio de GitHub.
Espero que os haya gustado este post y os pedimos el favor de siempre: Dadle like si os ha gustado y compartirlo en vuestras redes sociales!