Los hypertokens son la capa que siempre faltó entre los tokens de diseño y los…
El azar con reglas: lo que la función random() de CSS puede enseñar a los sistemas de diseño
Artículo original en inglés: Embracing Playful Imperfection With The CSS random() Function, de Brecht De Ruyte en Smashing Magazine.
Música para aeropuertos, hojas de estilo para humanos
En 1978 Brian Eno publicó Music for Airports y con ello inauguró una forma de componer que no consistía en escribir notas, sino en escribir reglas. Varias cintas de distinta longitud girando en bucle, cada una con una frase breve; el sistema decidía en qué momento coincidían. Eno no sabía qué iba a sonar exactamente. Sabía qué podía sonar y qué no. A eso lo llamó música generativa, y durante décadas ha sido una idea de museo. Hoy cabe en una propiedad CSS.
Brecht De Ruyte, desarrollador front-end belga, firma en Smashing Magazine la primera guía seria sobre la función random() de CSS, recién llegada de la especificación CSS Values and Units Level 5. Su punto de partida es una incomodidad que cualquiera que diseñe para la web reconoce: los objetos físicos (una impresión en tipografía móvil, un puñado de polaroids sobre la mesa) tienen irregularidades mínimas que los hacen humanos, y nuestras interfaces, en cambio, salen de fábrica con la perfección estéril de un catálogo de muebles. Su propuesta no es romper nada. La aleatoriedad en CSS no tiene por qué significar tirar el sistema de diseño por la ventana, escribe. Es imperfección controlada. Es Eno con sus cintas.
Un dado con instrucciones
La función es engañosamente sencilla: se le da un mínimo, un máximo y, si se quiere, un paso. Una rotación entre menos tres y tres grados. Un ancho entre 20 y 80 píxeles que solo pueda caer en múltiplos de diez. Una duración de animación entre segundo y medio y tres segundos, en saltos de dos décimas. Sin JavaScript, sin librerías, sin esos bucles de Sass que generaban kilómetros de CSS estático para fingir variedad. El navegador tira el dado en el momento de pintar.
Las reglas del juego son pocas pero importan. Mínimo y máximo deben compartir tipo de unidad: no se puede pedir un valor entre dos rem y cien píxeles sin pasar por calc(). El paso opcional convierte el rango continuo en una rejilla de valores permitidos, que es exactamente lo que un sistema de diseño necesita para no perder el control de sus tokens. Y luego está el detalle que separa al que ha leído la especificación del que no: por defecto, cada aparición de random() genera un valor independiente. Si el ancho y el alto de una tarjeta tiran cada uno su dado, salen rectángulos desparejados. Para que compartan resultado hay que darles una clave común, y para que esa clave no convierta todas las tarjetas de la página en clones hay que añadir el modificador element-scoped, que mantiene el vínculo dentro de cada elemento pero da a cada elemento su propia tirada.
Seis maneras de desafinar a propósito
De Ruyte acompaña la teoría con demos, y conviene describir un par en prosa porque explican mejor que el código dónde está la gracia. La primera es tipográfica: un titular en el que cada letra recibe una rotación, un desplazamiento y una opacidad ligeramente distintos, imitando la presión desigual de una prensa antigua. Al pasar el ratón, las letras se enderezan. Para que un lector de pantalla no recite el titular letra a letra, el contenedor lleva un aria-label con la palabra completa y los fragmentos visuales quedan ocultos a la tecnología asistiva. El azar nunca llega a la semántica.
La segunda es más musical. Un texto en el que cada letra bota con su propia altura de salto, su inclinación, su duración y su retardo, de modo que el conjunto no se mueve como una onda uniforme sino como una fila de niños con distintas ganas. El truco que me parece más elegante es el de los retardos negativos: al generar retardos de animación por debajo de cero, las letras aparecen ya en mitad de su salto al cargar la página, en lugar de esperar congeladas a que les toque. Hay también tarjetas inclinadas cuya sombra sigue el ángulo de inclinación, una baraja desplegada en abanico en la que la estructura la calcula sibling-index() y el temblor de mano lo pone random(), filas de texto que se desplazan a velocidades distintas al hacer scroll y un efecto bokeh con desenfoques, opacidades y escalas aleatorias que se enfocan al pasar por encima.
Un sistema de diseño no se define por lo que prohíbe, sino por lo que es capaz de permitir sin perder la cabeza.
Wabi-sabi con tokens
Aquí es donde el artículo toca una herida real de nuestro oficio. Los sistemas de diseño nacieron para eliminar la variación accidental: el botón que un desarrollador hizo dos píxeles más alto, el gris que nadie recordaba de dónde salía. Han ganado esa batalla, y el premio es una web en la que todo se parece a todo. La estética japonesa del wabi-sabi lleva siglos sosteniendo lo contrario: la taza con el esmalte irregular vale más que la perfecta porque lleva impresa la mano. William Morris se levantó contra la fábrica por motivos parecidos. Y la web de los noventa, con sus GIF torcidos y sus fondos imposibles, era fea, pero nadie la confundía con otra.
random() no es una vuelta a aquello. Es la posibilidad de que la variación vuelva a ser una decisión y no un accidente. La diferencia entre una interfaz con alma y una interfaz rota es exactamente el paso, el rango y la clave: la variación que el diseñador ha acotado frente a la que se ha colado. Un token de rotación decorativa de más-menos dos grados es tan sistemático como un token de espaciado de ocho píxeles. Solo que admite que la vida tiene ruido.
Dónde no tirar el dado
De Ruyte es tajante con los límites, y hace bien. La aleatoriedad va a lo decorativo: polaroids inclinadas, notas adhesivas, titulares de portada, balizas de estado que ya no parpadean todas a la vez, campos de estrellas, confeti, grano. Nunca a la rejilla, a los márgenes, al ancho de los formularios ni al relleno de los contenedores. Nunca al cuerpo de texto, porque un interlineado que cambia de párrafo en párrafo es fatiga de lectura disfrazada de personalidad. Y nunca a un elemento interactivo que se quede torcido sin enderezarse al recibir foco o ratón. prefers-reduced-motion sigue mandando: se apagan las animaciones y, en lo posible, se aplanan los ángulos, porque una página llena de inclinaciones distintas puede marear al hacer scroll.
El soporte, por cierto, está a medio camino. Safari fue el primero en implementarlo, Chrome lo despliega con la versión 156 y Firefox da señales positivas sin haber lanzado nada. La hermana random-item(), que elegiría al azar entre una lista de colores o palabras clave, no funciona aún en ningún navegador. La receta es la de siempre: envolver el uso en una consulta @supports y dejar un valor fijo de respaldo. Quien no vea el temblor verá la versión perfecta, que es la que teníamos. Eno tardó años en que alguien tomara en serio sus cintas; a las hojas de estilo les costará menos.