Saltar al contenido

Product design, liderazgo y la serie Harness Design

Serie Harness DesignArtículo n.º 06

Lo que la base de tokens escondió: cerrando el Hito 1 del Harness Design System

John Morais, .

Taller de tipografía en gouache: manos sobre una caja de tipos de madera con muchos compartimentos, entre rodillos de tinta, plantas en la ventana y un delantal colgado

Continuando la primera semana de dedicación exclusiva al proyecto, el trabajo fue todo en el Hito 1, que llamamos base técnica de tokens. Ya conté en los artículos anteriores por qué empezamos por ahí. Los tokens son la unidad más pequeña del sistema. En una lectura de Atomic Design, son los átomos: vienen antes que cualquier componente, y todo lo que construyamos encima hereda lo que ellos definen. Arreglar primero esa base no es perfeccionismo, es la única forma de no multiplicar el problema cuando la IA entra encima.

La estimación era cerrar tokens en una semana. No se cerró en una semana. Y el motivo por el que no se cerró es lo que vale la pena contar.

Los tokens parecían la parte simple

Crear y organizar tokens, en abstracto, parece la parte fácil del proyecto. Color, tipografía, espaciado, radio de borde. Cosa pequeña.

Lo que fuimos descubriendo es que el problema no es crear el token. Es migrar el sistema a él con seguridad y actualizar eso en producción sin romper lo que ya está funcionando. Eso deja de ser una tarea de diseño y se vuelve una tarea de sistema. Cada decisión pequeña sobre un token abre un abanico de decisiones de infraestructura, de código y de proceso que no habíamos dimensionado. Por eso el Hito 1 terminó convirtiéndose en una secuencia de actividades mucho mayor que la lista inicial, y nos fuimos realineando casi todos los días para dar abasto con lo que iba apareciendo.

Tres de esas sorpresas merecen registro, porque son exactamente el tipo de cosa que nadie cuenta.

La tipografía, que se trabó más que cualquier color

El caso más emblemático fue la tipografía. Y fue el más molesto justamente por parecer trivial.

En Figma, la cursiva no es una propiedad separada. Figma fusiona peso y estilo en una sola cadena, "regular itálico", "médium itálico", así que el peso que eliges ya trae la cursiva incorporada. Pero en la exportación, cuando el código va a consumirlo, eso tiene que venir descompuesto: por un lado el peso, por otro si es normal o cursiva. Habíamos modelado la cursiva dentro del campo de peso, y eso generaba un token equivocado en la salida.

Pasamos un buen rato intentando resolver eso dentro de las variables. Crear un tipo para normal y cursiva, mapear peso por peso, tamaño por tamaño. Yo iba ejecutando, Glauber iba probando, y con cada intento la organización quedaba más confusa en lugar de más limpia. En algún momento paramos a mirar cómo tratan esto otros design systems (Ant Design, Nitro), y la respuesta fue un poco humillante: no tokenizan la tipografía en variables. La resuelven en los estilos.

La decisión que cerró el asunto fue abandonar las variables de tipografía y consumir directamente de los text styles de Figma. El estilo ya trae todo junto (fuente, peso, cursiva, line height, letter spacing), y eso es lo que React y Flutter pasan a leer. Me importa decir que esta no es una victoria linda. Movimos, deshicimos, rehicimos, y al final sacamos algo que habíamos construido. Eso rompió el plugin de export, que se había hecho sobre el documento con todas las variables, y nos obligó a actualizarlo.

Hubo incluso un detalle de nomenclatura en medio de esto. Usábamos "medium" como nombre de un tamaño de fuente, pero "medium" también es nombre de un peso. Para un LLM que lo lee, esa ambigüedad es una invitación al error. Lo cambiamos por "Base", al estilo de Tailwind (text-base). Parece una tontería. El nombre de un token importa más de lo que parece cuando quien lee es la máquina, y eso ya había aparecido antes en el proyecto.

El bind, que no logramos automatizar por fuera

En el artículo pasado dejé el prompt de bind corriendo vía Figma Agents, funcionando parcialmente. La continuación de esa historia es que probamos varios caminos para automatizar el bind por fuera (Claude Code vía MCP, scripts por Scripter, el propio Figma Agents) y ninguno entregó lo suficiente para escala. Figma Agents llegaba a un 80%, lo que ayuda pero no cierra.

Lo que destrabó fue llevar el bind al plugin que ya habíamos construido internamente, en lugar de dejarlo como un prompt suelto. Ahí se pudieron poner reglas que el prompt tenía dificultad para garantizar: fallback al valor más cercano cuando el valor exacto no existe (un radio de borde en 9 pasa a 8, y no pasa a ser invención) y, para el color, comparación por distancia en el espacio RGB para encontrar la variable semántica correcta.

El resultado concreto del lado del diseño es lo que más tranquilidad me da del Hito 1: con este plugin, logramos hacer el bind de casi el 100% de los componentes que estaban en el archivo de Design System de Figma con los tokens correctos. No es la arquitectura definitiva de bind del universo. Es la que funciona para nuestro archivo, con nuestro equipo, ahora.

Dónde vive el JSON, y por qué eso se volvió discusión

La última sorpresa fue de infraestructura, y es la que más conversa con lo que escribí en "Antes de AI-first, human-first".

Cuando exportamos el JSON de los tokens, llegó a cerca de 40 mil tokens de tamaño, demasiado grande para que un LLM lo lea directamente. Rodolfo puso un script para reducirlo, y Gabriel propuso minificar el archivo, quitando los espacios: para que la máquina lo parsee da lo mismo, el problema de legibilidad es solo nuestro.

Y hubo un debate sobre dónde debía vivir ese JSON. La opción fácil era dejar todo en Prolog Cloud, el repositorio que centraliza el contexto del producto, porque cualquier PD podría ejecutar la skill y hacer commit sin entender de infra. El problema es que tirar ahí un montón de archivos de tokens puede perjudicar la indexación del LLM, que ya tiene bastante con qué lidiar.

El camino que cerramos fue mantener Prolog Cloud como fuente de la verdad de los tokens y, a partir de un commit allí, replicar el archivo a un repositorio separado de infraestructura del Design System, que luego propaga las actualizaciones a React, a Flutter y a Glorp. Aceptamos aumentar la complejidad del pipeline para bajar la complejidad de uso. Glauber, o cualquier designer, ejecuta la skill y hace commit, sin necesidad de clonar la infra ni entender cómo funciona. Es el mismo tipo de intercambio que ya habíamos hecho con la gobernanza: la solución ideal en el papel pierde frente a la solución que el equipo puede operar sin ahogarse.

Lo que probamos, y lo que apareció

Con la base en pie, probamos la aplicación de harness a los tokens. La idea fue pedirle a Claude que mostrara visualmente cómo entendió cada token a partir de la documentación, y pedirle que armara interfaces de prueba (un componente de alerta, por ejemplo) solo para ver cómo aplicaba los tokens en la práctica.

Los resultados fueron positivos. Quedó razonablemente claro que Claude estaba entendiendo la intención de cada token: cuándo usarlo, cuándo no usarlo, qué combina con qué. No voy a inflar esto más allá de lo que fue. Fue una prueba inicial, en un recorte pequeño, y el objetivo era solo tener una señal de que el schema que escribimos produce el entendimiento correcto del otro lado. Lo produjo.

La forma de probar fue simple, tomé toda la documentación de harness de los tokens y le pedí a Claude que dijera qué entendió, jajaja, un poco más complejo que eso, pero esa fue la idea, lo que generé fue un reporte con el resultado.

A continuación un resumen del reporte, y un detalle, Glorp es el nombre del Design System de Prolog.

El alcance honesto de todo esto

Aquí vale la pena ser directo sobre lo que el Hito 1 entrega y lo que todavía no entrega.

Lo que entrega es una base de tokens escalable y semiautomatizada, con gobernanza tanto en Figma como en el código, tokens consumibles por un LLM, y casi todos los componentes del archivo de diseño amarrados a los tokens correctos. Es una base sólida.

Lo que todavía no entrega es el resultado que da nombre al proyecto: un LLM armando pantallas con dirección. Ese retorno no viene del harness de tokens. Como ya había quedado claro antes, el harness de tokens es diferente del harness de componentes, y la mayor parte de las decisiones de interfaz va a venir de los componentes, no de los tokens sueltos. El token pesa cuando Claude necesita crear algo desde cero, sin un componente listo para reutilizar. La ganancia real de producción vive en el harness de componentes, que es una etapa posterior. Esta primera parte es la que hace posible aquella parte, no la que cosecha su fruto.

Lo que viene

Cerramos el Hito 1 y el Hito 2, que era el harness de los tokens en sí, para Prolog Cloud, React y Flutter. El Hito 3, aplicación de los tokens a los componentes, ya está en marcha.

No sé decir los resultados. Está la migración de los tokens en los componentes de React y Flutter, que vamos a atacar con un mapeo de equivalencias asistido por IA. Hay una idea de crear harness también de los tokens antiguos, para darle a Claude una referencia de comparación y aumentar la precisión de esa migración. Está la duda abierta de si ese JSON minificado va a ser bien consumido a escala. Y está el rollout en producción, con los tokens nuevos y antiguos coexistiendo por un tiempo. Cada uno de esos frentes puede salir bien, salir mal, o revelar un problema que yo todavía no veo. Es la parte del proyecto que menos puedo prever, y probablemente la más interesante de documentar cuando ocurra.