Crear componentes visuales reutilizables parece tarea sencilla pero la realidad es que construirlos de manera que sean previsibles, extensibles y con una API intuitiva es más difícil de lo que parece.
Consideraciones previas
Vamos a hablar de componentes altamente reutilizables. También podemos llamarlos primitivos, componentes que podrían ser parte de una librería, sistema de diseño o de un gran proyecto. Componentes que van a utilizarse en innumerables lugares y a los cuales queremos dotar de todo lo necesario para que los consumidores puedan usarlos sin necesidad de requerir cambios en los mismos continuamente.
Aunque sepamos el contexto en el que un componente se va a usar a día de hoy, no sabemos en qué contexto se usará dentro de un tiempo. Por ello buscaremos un nivel de flexibilidad que evite modificaciones innecesarias de nuestros componentes en el futuro. En el caso de una librería de componentes evitar estas modificaciones es crucial para evitar la publicación de nuevas versiones de nuestros paquetes muy a menudo y que nuestros consumidores se cansen.
Estos componentes primitivos son la base para construir otros componentes más complejos y son los que permiten a nuestros consumidores crear sus propios componentes y patrones sin necesidad de modificar la librería.
Los ejemplos de código están escritos con React pero los conceptos son extrapolables a otros frameworks como Vue o Svelte.
Antes de crear componentes visuales reutilizables primero tenemos que definir nuestros objetivos. Buscamos:
- API intuitiva y estable
- Comportamiento previsible
- Extensibilidad
- Encapsulación
- Accesibilidad
API intuitiva y estable
Nombrar props como atributos del DOM
Algunos componentes tienen una correlación directa con elementos del DOM. Por ejemplo, un componente Button y un elemento <button>. Otros componentes quizás no tengan una correlación directa pero sí lo suficientemente cercana, como por ejemplo Avatar. Avatar renderiza una imagen y esto tiene una correlación con la etiqueta <img> de HTML. Podemos hacer uso de esa correlación y ese conocimiento ya existente de HTML para crear parte de la API de nuestros componentes de manera que incluso nuestros consumidores pueden estar familizados con ella de antemano. Cuanto más "aburrida" sea nuestra API, mejor.
Imagina un componente Button al que queremos poder establecer su estado disabled. El elemento <button> del DOM ya tienen un atributo disabled por lo tanto usaremos ese nombre para nuestra prop.
✅ Do
return <button className={cn('btn', { disabled })} disabled={disabled} {...rest} />;
}
cn en los ejemplos de código se refiere a classnames, un diminuto y genial paquete de npm que ayuda a unir nombres de clases de manera condicional. Lo verás en los ejemplos a lo largo del artículo.
...rest nos sirve para pasar a nuestro componente todas las props que no hemos usado explícitamente. En un siguiente apartado explicamos su importancia.
En el caso de un componente Avatar al que le tenemos que pasar la url del avatar, podemos usar una prop src, como el atributo de las etiquetas <img>, en vez de usar un nombre como url.
✅ Do
return <img className="avatar" src={src} {...rest} />;
}
En el caso de que prefiramos no adherirnos a nombres de atributos existentes en el DOM, lo importante es mantener una coherencia. Si por ejemplo para nuestro componente Button preferimos usar una prop isDisabled en vez de disabled, debemos mantener esa nomenclatura en todos nuestros componentes: <Button isDisabled>, <Checkbox isDisabled>, <Select isDisabled>, etc.
Usa children
Llamamos children al contenido que se incluye entre la "etiqueta" de apertura y cierre de un componente (dependiendo del framework que usemos puede también puede llamarse slot).
Así es como funciona HTML: lo que se incluye dentro de una etiqueta es lo que renderiza esa etiqueta salvo elementos con cierre automáticos como <img />, <input />, <br />, etc. Debemos respetar este comportamiento siempre que sea posible para pasar contenido a nuestros componentes en vez de usar props específicas en cada componente. Además tendremos una misma API para pasar el contenido a muchos de nuestros componentes.
🛑 Don't
return (
<h1 className="heading" {...rest}>
{text}
</h1>
);
}
// <Heading text="¿Wot?" />
✅ Do
return (
<h1 className="heading" {...rest}>
{children}
</h1>
);
}
// <Heading>Mucho mejor, claro</Heading>
Además, children nos permite componer de manera muy intuitiva tal y cómo lo haríamos con HTML.
<IconHome /> Bienvenido a Octuweb
</Heading>
Previsible
Singel: Single Element Pattern
Diego Haz escribió el que para mi es un artículo altamente influyente: Introducing the Single Element Pattern.
Muchos de los principios descritos en el artículo de Diego Haz están reflejados en este mismo artículo. Diego los aplica en su librería open source Reakit. Se trata de una librería de componentes que solamente provee funcionamiento (agnóstica en cuanto a estilos) y 100% accesible. Un proyecto referente y una auténtica maravilla de la composición. Algunos de sus hilos en Twitter son joyas. Muy recomendable seguir su trabajo.
La primera regla que nombra el artículo es: "Renderiza sólo un elemento". ¿Qué quiere decir esto? Veamos un ejemplo. Vamos a crear un componente Tabs. Primero vamos a mostrar una API que no cumple con el patrón Singel. El componente recibe un array de objetos con las tabs a renderizar. El componente internamente se encarga de renderizar correctamente los títulos de las pestañas y su respectivo contenido. Parece una API muy cómoda a la hora de usar este componente, ¡tan sólo una prop!
🛑 Don't
items={[
{
title: 'One',
content: <p>one!</p>,
},
{
title: 'Two',
content: <p>two!</p>,
},
]}
/>
¿Cuál es el problema? Que no es extensible:
- Si necesitamos añadir un icono al título de la primera tab, tendríamos que cambiar la API de nuestro componente.
- Si queremos añadir un className a una de las tabs no podemos.
- Si necesitamos añadir un atributo aria tampoco podemos sin cambiar la API.
Vamos a ver cómo luciría la API de nuestro componente siguiendo el patrón Singel.
✅ Do
<TabList>
<Tab>One</Tab>
<Tab>Two</Tab>
</TabList>
<TabPanels>
<TabPanel>
<p>one!</p>
</TabPanel>
<TabPanel>
<p>two!</p>
</TabPanel>
</TabPanels>
</Tabs>
Exactamente así es como luce la API de Tabs en Chakra UI. Quizás puedas pensar que de esta forma escribimos más código y exigimos más a nuestros consumidores. Sí, es cierto, pero damos libertad para extender el componente lo cual era uno de nuestros objetivos. Esto nos permitirá no estar modificando la API de nuestro componente continuamente. Queremos una API estable.
Eliminamos muchos problemas y futuros cambios. Y ahora:
- Si necesitamos añadir un icono al título de la primera tab, simplemente lo pasamos en el children.
<Tab>
<IconStar /> One
</Tab> - Si queremos añadir un className a una de las tabs, simplemente la pasamos como prop.
<Tab className="awesome">One</Tab>
- Si queremos añadir un atributo aria lo añadimos.
<TabPanel aria-labelledby="title">
<p>one!</p>
</TabPanel>
El principio Singel, como cualquier otro, está para romperlo. Todo depende del contexto. Recordemos que en nuestro caso estamos en un contexto de componentes visuales de bajo nivel altamente reutilizables los cuales van a ser la fundación de nuestra librería de componentes y con los cuales vamos a componer para crear componentes más complejos. Queremos estabilidad.
Otra librería de componentes para React que sigue el principio Singel es Reach UI. Es agnóstica en cuanto a estilos y es toda una referencia en cuanto a accesibilidad. Sus principios, escritos por Ryan Florence, son una joya.
Spread props
Habrás visto que en los ejemplos de código se hace spread del resto de las props que no hemos usado en nuestros componentes. ¿Por qué? Porque no queremos estar definiendo cada prop que el consumidor de nuestro componente quiera que acabe como un atributo del DOM, es algo que va a pasar continuamente. Además cuando hacemos spread de props en un elemento como span esas props acaban en el DOM, es algo que nuestros consumidores van a esperar que pase.
Pongamos otra vez Button como ejemplo. Imagina que alguien quiere pasarle una prop id:
return (
<button className={cn('btn', { disabled })} id={id}>
{children}
</button>
);
}
Más tarde se necesita pasar un title:
return (
<button className={cn('btn', { disabled })} id={id} title={title}>
{children}
</button>
);
}
Esto ocurrirá por cada atributo del DOM que queramos que acepte nuestro componente Button: type, aria-label, value, etc. No queremos estar definiendo cada prop que existe como atributo en el DOM, por ello hacemos spread del resto de las props que no usamos al definir el marcado de nuestro componente.
✅ Do
return <button className={cn('btn', { disabled })} {...rest} />;
}
En React, children es una prop más por lo que en este ejemplo está incluido en el objeto rest. No es necesario pasarla explícitamente.
En el caso de que nuestro componente devuelva más de un elemento, deberemos elegir el elemento principal sobre el que haremos el spread de props. Pero si es posible, es recomendable adherirse al patrón Singel.
Cuidado con las falsas boolean props
De nuevo ponemos Button como ejemplo. En un momento dado tenemos que añadir una variante primary y pensamos que lo mejor es añadir una prop primary que acepta true/false.
return <button className={cn('btn', { primary })} {...rest} />;
}
Dos semanas después necesitas añadir una nueva variante: secondary y añades una prop secondary. Después te das cuenta de que hace falta una variante outline y añades una prop outline. Acabamos con algo así:
🛑 Don't
return <button className={cn('btn', { primary, secondary, outline })} {...rest} />;
}
Nuestras tres variantes son excluyentes y sin embargo el código permite usarlas al mismo tiempo: <Button primary secondary outline>. No bueno. ¿Cuál tiene prioridad? Deberíamos dejar claro en la API de nuestro componente que las tres variantes son excluyentes. Podemos usar una única prop variant para ello (o cualquier otro nombre, lo importante es ser consistente con el nombre en todos nuestros componentes que acepten una prop que cumpla la misma finalidad).
✅ Do
return <button className={cn('btn', variant)} {...rest} />;
}
De esta forma nos aseguramos de que sólo podemos asignar una variante a nuestro componente y los consumidores no estarán confundidos sobre qué variante usar o si estas se pueden combinar.
En el caso de que nuestros componentes empiecen a crecer en variantes y posibles combinaciones de props es bueno pararse a pensar si de verdad queremos que nuestro componente valga para todos esos casos o si vale la pena crear versiones específicas de nuestro componente que cumplan un propósito y lo cumplan bien: Button, ButtonOutline, ButtonIcon, etc.
Extensible
Componentes polimórficos
En ocasiones queremos que un componente se renderice como un elemento diferente dependiendo del uso que le vayamos a dar o del contexto en el que se encuentre. Pongamos como ejemplo un componente <Text variant="body" />. Recibe una prop variant que establece el estilo visual entre uno de los que hemos definido en nuestro imaginario sistema de diseño: body, heading, overline, etc. Pero, ¿con qué etiqueta HTML acaba en el DOM? ¿Será siempre un <p>? ¿Creamos un componente por cada etiqueta?
Por suerte tenemos el concepto de componentes polimórficos. Mediante la as prop (fue popularizada por styled-components) podemos decirle a nuestro componente qué etiqueta queremos que renderice. Le damos por defecto el valor de p, para que se renderice como un párrafo en caso de que no se le pase la prop as.
Puedes usar un nombre diferente a as para la prop. Otras librerías de componentes usan otros nombres como component o tag, pero as es la más extendida.
✅ Do
const Component = as;
return <Component className={`text-${variant}`} {...rest} />;
}
// Por defecto es un <p>
// <Text variant="body" />
// Heading como <h1>
// <Text variant="heading" as="h1" />
// Heading como <span>
// <Text variant="heading" as="span" />
// Heading como cualquier otro componente
// <Text variant="heading" as={CustomComponent} />
Esta técnica es muy útil en el caso de un componente Button ya que nos permite renderizarlo como <button>, <a> o un <Link> de react-router, por ejemplo.
Si usas React + TypeScript quizás quieras agradecerle a Kristóf Poduszló su maravillosa librería react-polymorphic-box para crear componentes polimórficos (casi) perfectamente tipados. Es algo muy difícil de conseguir.
Combina className y estilos inline
Un error bastante común es no pasar la prop className a nuestro componente o que al pasarla sobreescribimos el className interno de nuestro componente.
🛑 Don't
return <li className="listItem" {...props} />;
}
En el ejemplo anterior si le pasamos className a ListItem, sobreescribirá nuestro className interno de manera que el componente perderá sus estilos.
Cuando pasamos un className, debemos asegurarnos de que lo combinamos con el className interno de nuestro componente.
✅ Do
return <li className={cn('listItem', className)} {...rest} />;
}
En caso de que estemos definiendo estilos inline para nuestro componente también debemos de combinarlos con los que el componente reciba por props. Este caso es mucho menos común, pero en ocasiones es necesario agregar estilos inline para estilos computados en tiempo de ejecución que dependen de alguna prop o estado interno.
✅ Do
return (
<li
className={cn('listItem', className)}
style={{ '--delay': `${index * 0.1}s`, ...style }}
{...rest}
/>
);
}
Combinar los className y style internos con los que recibimos por props permite que los consumidores puedan extender los estilos de nuestro componente. Recordad que no sabemos cómo se van a utilizar nuestros componentes y no damos nada por sentado.
Encapsulación
Construye tus componentes aislados del mundo
Probablemente la herramienta más usada para desarrollar componentes de manera aislada es Storybook. Es compatible con React, Vue, Svelte, Web Componentes, etc. Es una auténtica gozada trabajar en Storybook ya que te permite centrarte única y exclusivamente en el componente que estás construyendo en ese momento y documentar los estados de tu componente.
Lo bueno de Storybook es que podemos subirlo a cualquier servicio como Netlify, Vercel o Surge y compartir la url de nuestro Storybook con el mundo. Esto ayuda mucho en la colaboración entre desarrollo y otras disciplinas como diseño o marketing. Es una buena forma de tener tus componentes documentados sin dedicar un gran esfuerzo a construir una documentación personalizada. Además podemos incluir stories no sólo de nuestros componentes, sino de cualquier cosa que necesitemos. Por ejemplo, una página donde mostramos todos los tokens de nuestro sistema de diseño: colores, escala tipográfica, espaciados, etc.
Los addons disponibles nos pueden ayudar a nosotros y a personas no técnicas a explorar nuestra librería de componentes:
- Controls: modifica de manera visual las props que recibe tu componente para ver cómo se comporta
- Actions: visualiza eventos que lanzan tus componentes
- Backgrounds: probar nuestros componentes con diferentes colores de fondo
- Viewport: cambia el tamaño de viewport para simular el comportamiento del componente en diferentes dispositivos
- Accessibility: comprueba la accesibilidad de tus componentes con axe
Y muchos otros oficiales y de la comunidad. También podemos añadir tests de regresión visuales usando Chromatic, Backstop, Loki o una de las muchas otras opciones que existen para cazar cualquier cambio no deseado que ocurra en nuestros componentes.
Componentes indestructibles
Ya hemos visto que podemos usar Storybook para construir nuestros componentes en un entorno aislado. Eso tiene un pega: el entorno aislado puede no ser igual al entorno donde los componentes van a ser usados. Aunque en un mundo orientado a componentes lo ideal es no tener estilos globales, eso no siempre es posible sobre todo cuando el entorno en el que se van a usar está fuera de nuestro control. Por eso, debemos intentar cargar los mismos estilos globales que sabemos se cargarán en el entorno final.
Si nuestros componentes se ven alterados por estilos globales significa que no hemos conseguido una buena encapsulación. Tendremos que ver si nuestros estilos son poco específicos y si dependen demasiado de estilos globales. Por ejemplo, si el componente Button depende de un font-family global, estamos rompiendo el principio de encapsulación. Si ese estilo global cambia nuestro componente se romperá. Para solucionarlo simplemente declaramos font-family en los estilos de Button. Debemos ser lo más específicos posibles a la hora de escribir los estilos de nuestros componentes.
Olvídate de margin
Este título tiene un poco de clickbait pero deja que me explique. Por lo general, un componente no debería afectar a nada que esté fuera del mismo. La encapsulación debe funcionar en ambos sentidos. ¿Qué propiedad CSS rompe este principio por encima de todas? margin. Los márgenes son peligrosos y no sólo por el margin collapse.
Hablamos de márgenes que afectan al exterior de nuestro componente. Si dentro de nuestro componente hay varios elementos y usamos margin para espaciarlos sin afectar al exterior no hay problema.
El espacio en diseño se podría definir como la distancia entre dos elementos. Para saber la distancia entre esos elementos debemos saber de qué elementos estamos hablando. Esto convierte el espacio en circunstancial y dependiente del contexto. El espacio entre los componentes <Label> y <Select> no tiene porque ser el mismo que entre <Label> y <Text>. Por lo tanto no tiene sentido que definamos márgenes en nuestros componentes sin saber el contexto en el que serán usados.
¿Qué podemos hacer entonces? Definir el espacio en el contexto de uso de los componentes, es decir, normalmente a nivel de app y no en la librería de componentes. Tenemos muchísimas maneras de hacerlo y todo depende de cómo estemos escribiendo nuestros estilos. Mi manera preferida para gestionar el espaciado es usar layout components, esto es, componentes que se encargan de espaciar y colocar elementos siguiendo las consignas marcadas por nuestro diseño. El sistema de diseño Braid tiene varios ejemplos:
- Columns: distribuye los elementos en columnas. Permite definir diferentes espaciados por breakpoint y decidir si queremos apilar los elementos a partir de cierto breakpoint.
<Columns space={['small', 'large']} collapseBelow="tablet">
<Column>First</Column>
<Column>Second</Column>
<Column>Third</Column>
</Columns> - Stack: distribuye los elementos uno debajo del otro con un espaciado entre ellos. Al igual que Columns permite definir un espaciado diferente por breakpoint.
<Stack space={['small', 'large']}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</Stack> - Inline: distribuye los elementos inline, pasando ocupar varias líneas si es necesario. También permite pasarle espaciados diferentes por breakpoint.
<Inline space={['small', 'large']}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</Inline>
Esto son sólo unos ejemplos de lo que podemos conseguir con los componentes de layout. El cielo es el límite. Podemos crear tantos componentes de layout como necesitemos para intentar forzar las convenciones de nuestro sistema de diseño (si lo tenemos) o crear opciones más genéricas como Flex o Grid. Dependerá de a quien esté orientada nuestra librería de componentes. Si conseguimos que los consumidores de nuestra librería de componentes usen los componentes de layout habremos ganado mucho en consistencia en el terreno de los espaciados. Además se construyen UIs a una velocidad de vértigo una vez tenemos estos componentes ya que no hay que perder el tiempo escribiendo CSS ad hoc en cada pantalla o componente que estemos construyendo en nuestra app.
Si los componentes de layout no son lo tuyo, dale 5 minutos. Si todavía no son lo tuyo, siempre puedes usar clases de utilidad. Si estás usando Tailwind, Tachyons o cualquier framework similar ya lo estás haciendo. Si estás usando Styled System, Theme UI, Stitches u otra librería CSS-in-JS que permita leer valores directamente de un theme, puedes usar sus propios mecanismos en vez de clases de utilidad. Lo que es poco productivo es definir clases específicas para cada contexto en el que quieras aplicar un espaciado.
🛑 Don't
<Button type="button">Cancel</Button>
<Button variant="primary" type="submit">
Save
</Button>
</div>
margin-left: var(--spacing-small);
@media screen and (min-width: 768px) {
margin-left: var(--spacing-large);
}
}
✅ Do
<Inline space={['small', 'large']}>
<Button type="button">Cancel</Button>
<Button variant="primary" type="submit">Save</Button>
</Inline>
// Con clases de utilidad
<div>
<Button type="button">Cancel</Button>
<Button variant="primary" type="submit" className="ml-small ml-large@tablet">
Save
</Button>
</div>
// Con librería CSS-in-JS que accede a tokens
<div>
<Button type="button">Cancel</Button>
<Button variant="primary" type="submit" sx={{ marginLeft: ['small', 'large'] }}>
Save
</Button>
</div>
Escribir CSS para cada caso específico en nuestras apps dificulta muchísimo refactorizar y mover código de un componente a otro. Además, lleva más tiempo (naming things is hard) y requiere estar cambiando de contexto continuamente entre la vista y CSS. Con los componentes de layout o clases de utilidad podemos crear un lenguaje común entre diseño y desarrollo. Uno de los objetivos de una librería de componentes es evitar tener que escribir tanto CSS como sea posible a los consumidores. Los componentes de layout cumplen esta premisa a la perfección.
Si las clases de utilidad te dan grima, intenta leer el post CSS Utility Classes and "Separation of Concerns" con una mente abierta. Está escrito por Adam Wathan, creador de Tailwind CSS.
Bonus point: Playroom ✨
Si usas React, Playroom es una herramienta maravillosa para tu librería de componentes, para tu equipo y para ti. Es un playground en el que escribes código JSX mientras te autocompleta los nombres de componentes, props y sus valores. Previsualizas el código escrito al instante y te permite compartir la url del código que has escrito 🤯 Además, puedes ver varios viewports a la vez y definir snippets.
Con esto se prototipa a la velocidad de la luz si los componentes tienen una API intuitiva y tenemos a nuestra disposición componentes de layout. Podemos crear pantallas enteras sin necesidad de levantar el proyecto. Un ejemplo para ver cómo funciona el Playroom de Braid.
Existe un addon que integra Playroom en Storybook.
Accesibilidad
Crear una librería de componentes accesibles desde cero es un esfuerzo enorme, además de ser muy difícil. Es tentador ya que es todo un reto. Esto no debe desanimar a nadie sino prevenirnos de pegarnos un tiro en el pie.
Para construir componentes accesibles siguiendo la especificación WAI-ARIA hace falta mucho testing en diferentes combinaciones de dispositivos, navegadores y sistemas operativos. En mi opinión, la accesibilidad (no básica) es una de las facetas más difíciles del desarrollo web.
Cualquier componente con un grado mínimo de interacción es más complicado de construir de lo que parece. Un Tooltip por ejemplo. Podemos pensar: "simplemente mostramos una cajita con texto cuando hacemos hover". Pero echando un vistazo al código fuente del Tooltip de Reach UI nos podemos hacer una idea de todo lo que hay que tener en cuenta. Nunca es tan sencillo.
Es bueno saber elegir en qué emplear nuestros esfuerzos y hacernos las preguntas adecuadas. ¿Me beneficia en algo construir este componente desde cero en vez de usar uno existente al que puedo añadirle la capa visual que necesito? ¿De verdad voy a tener el tiempo y los recursos para poder testear apropiadamente mis componentes en las combinaciones de dispositivos, navegadores y sistemas operativos necesarias? Me temo que muy pocos desarrolladores estamos en esa disposición.
En terreno React (es con el que más familiarizado estoy, por si no se había notado) a día de hoy existen librerías agnósticas en cuanto a estilos que nos permiten construir componentes interactivos perfectamente accesibles:
- React Aria, de Adobe
- Reakit, de Diego Haz
- Reach UI, de React Training
Sí, son nuevas dependencias y es añadir un buen puñado de los kilobytes de JavaScript, pero por desgracia hace falta mucho JavaScript para hacer nuestros componentes accesibles.
Antes de embarcarte en la aventura de crear una librería de componentes piensa si puedes usar una base existente de calidad contrastada para ahorrar tiempo y esfuerzo que puedes dedicar a cosas que impacten más en tu equipo, producto o cliente.
Conclusión
Lo expuesto en el artículo no es ni mucho menos un conjunto de reglas inquebrantables. Las reglas están para romperlas cuando es necesario. Todo depende del contexto y nadie mejor que tú para saber cuándo tendrá sentido aplicarlas o no. Pero en el terreno de componentes visuales reutilizables, ponerse siempre en el peor escenario es lo seguro.
Hay argumentos a favor y en contra para crear componentes extensibles. Salvo que sepas con certeza qué uso exacto se le va a dar a tus componentes, crearlos de manera extensible te va a permitir descansar mejor ya que sabrás que quien los use podrá adaptarlos a sus necesidades sin necesidad de modificarlos.
Nos hemos dejado muchas cosas en el tintero, por ejemplo hacer forward de refs, pero lo dejamos para otro día. Hablar de componentes no tiene fin. Si quieres, continuamos la conversación 🙂
Recursos
Lecturas
- Introducing the Single Element Pattern, por Diego Haz
- Margin considered harmful, por Max Stoiber
- Reach UI Philosophy, por Ryan Florence
- Avoid soul-crushing components, por Kent C. Dodds
- CSS Utility Classes and "Separation of Concerns", por Adam Wathan
- About HTML semantics and front-end architecture, por Nicolas Gallagher
Charlas
- Designing with Components, por Mark Dalgleish
- Rethinking Design Practices, por Mark Dalgleish
- In Defense of Utility-First CSS, por Sarah Dayan
Librerías
- React Aria, de Adobe
- Reakit, de Diego Haz
- Reach UI, de React Training
Agradecimientos
- Diego Haz, Mark Dalgleish y Devon Govett y muchos otros autores por compartir su conocimiento constantemente.
- Alex Jover, Arian Zargaran, Héctor Gómez, Julián Salgado, Miguel Molina y Paco Segovia por su valioso feedback.