Beneficios de usar propiedades personalizadas en sistemas de diseño y bibliotecas de componentes
Mi nombre es Dave y soy desarrollador de frontend sénior en Nordhealth. Trabajo en el diseño y desarrollo de nuestro sistema de diseño Nord, que incluye la creación de componentes web para nuestra biblioteca de componentes. Me gustaría compartir cómo resolvimos los problemas relacionados con el diseño de componentes web con las propiedades personalizadas de CSS, así como algunos de los otros beneficios de usar propiedades personalizadas en los sistemas de diseño y las bibliotecas de componentes.
Cómo compilamos componentes web
Para compilar nuestros componentes web, usamos Lit, una biblioteca que proporciona mucho código de plantilla, como estado, estilos centrados, plantillas y mucho más. Lit no solo es ligero, sino que también está basado en las APIs nativas de JavaScript, lo que significa que podemos ofrecer un paquete de código eficiente que aproveche las funciones que ya posee el navegador.
Sin embargo, lo más atractivo de los componentes web es que funcionan con casi cualquier framework de JavaScript existente o incluso sin ningún framework. Una vez que se hace referencia al paquete principal de JavaScript en la página, usar un componente web es muy similar a usar un elemento HTML nativo. El único indicador real de que no es un elemento HTML nativo es el guion constante dentro de las etiquetas, que es un estándar para indicarle al navegador que se trata de un componente web.
Encapsulamiento de estilo Shadow DOM
De la misma manera en que los elementos HTML nativos tienen un Shadow DOM, los componentes web también lo hacen. Shadow DOM es un árbol oculto de nodos dentro de un elemento. La mejor manera de visualizarlo es abrir el inspector web y activar la opción "Show Shadow DOM tree". Una vez que hayas hecho esto, intenta observar un elemento de entrada nativo en el inspector. Ahora tendrás la opción de abrir esa entrada y ver todos los elementos que contiene. Incluso puedes probar esto con uno de nuestros componentes web: inspecciona nuestro componente de entrada personalizado para ver su Shadow DOM.
Una de las ventajas (o desventajas, según tu perspectiva) de Shadow DOM es la encapsulación de estilo. Si escribes CSS dentro de tu componente web, esos estilos no pueden filtrarse ni afectar la página principal u otros elementos; están completamente contenidos en el componente. Además, el CSS escrito para la página principal o un componente web superior no puede filtrarse en tu componente web.
Este encapsulamiento de estilos es un beneficio de nuestra biblioteca de componentes. Nos brinda una mayor garantía de que, cuando alguien use uno de nuestros componentes, se verá como queríamos, independientemente de los estilos aplicados a la página superior. Para asegurarnos aún más, agregamos all: unset;
a la raíz, o "host", de todos nuestros componentes web.
Sin embargo, ¿qué sucede si alguien que usa tu componente web tiene un motivo legítimo para cambiar ciertos estilos? Quizá hay una línea de texto que necesita más contraste debido a su contexto o un borde más grueso. Si ningún estilo puede entrar en tu componente, ¿cómo puedes desbloquear esas opciones de estilo?
Ahí es donde entran en juego las propiedades personalizadas de CSS.
Propiedades personalizadas de CSS
Las propiedades personalizadas tienen un nombre muy apropiado: son propiedades de CSS que puede nombrar por su cuenta y aplicar el valor que sea necesario. El único requisito es que les antepongas dos guiones. Una vez que hayas declarado tu propiedad personalizada, el valor se puede usar en tu CSS con la función var()
.
En lo que respecta a la herencia, se heredan todas las propiedades personalizadas, lo que sigue el comportamiento típico de las propiedades y los valores de CSS normales. Cualquier propiedad personalizada aplicada a un elemento superior o al elemento en sí se puede usar como valor en otras propiedades. Hacemos un uso intensivo de las propiedades personalizadas para nuestros tokens de diseño, ya que los aplicamos al elemento raíz a través de nuestro framework de CSS, lo que significa que todos los elementos de la página pueden usar estos valores de token, ya sea un componente web, una clase auxiliar de CSS o un desarrollador que desee extraer un valor de nuestra lista de tokens.
Esta capacidad de heredar propiedades personalizadas, con el uso de la función var()
, es la forma en que penetramos en el Shadow DOM de nuestros componentes web y permitimos que los desarrolladores tengan un control más detallado cuando aplican diseño a nuestros componentes.
Propiedades personalizadas en un componente web Nord
Cada vez que desarrollamos un componente para nuestro sistema de diseño, adoptamos un enfoque reflexivo en su CSS. Nos gusta apuntar a un código ágil, pero muy fácil de mantener. Los tokens de diseño que tenemos están definidos como propiedades personalizadas en nuestro framework de CSS principal en el elemento raíz.
Luego, se hace referencia a estos valores de token dentro de nuestros componentes. En algunos casos, aplicaremos el valor directamente en la propiedad CSS, pero en otros, definiremos una nueva propiedad personalizada contextual y aplicaremos el valor a esa propiedad.
También abstraeremos algunos valores que son específicos del componente, pero no en nuestros tokens, y los convertiremos en una propiedad personalizada contextual. Las propiedades personalizadas que son contextuales al componente nos proporcionan dos beneficios clave. En primer lugar, significa que podemos ser más "sencillos" con nuestro CSS, ya que ese valor se puede aplicar a varias propiedades dentro del componente.
En segundo lugar, hace que los cambios de estado y variación del componente sean muy claros: solo se debe cambiar la propiedad personalizada para actualizar todas esas propiedades cuando, por ejemplo, aplicas diseño a un estado activo o de desplazamiento del mouse o, en este caso, a una variación.
Sin embargo, el beneficio más potente es que, cuando definimos estas propiedades personalizadas contextuales en un componente, creamos una especie de API de CSS personalizada para cada uno de nuestros componentes, que el usuario de ese componente puede aprovechar.
En el ejemplo anterior, se muestra uno de nuestros componentes web con una propiedad personalizada contextual modificada mediante un selector. El resultado de todo este enfoque es un componente que le brinda al usuario suficiente flexibilidad de diseño y, al mismo tiempo, mantiene la mayoría de los estilos reales bajo control. Además, como beneficio adicional, nosotros, como desarrolladores de componentes, tenemos la capacidad de interceptar esos estilos que aplica el usuario. Si queremos ajustar o extender una de esas propiedades, podemos hacerlo sin que el usuario deba cambiar su código.
Consideramos que este enfoque es muy potente, no solo para nosotros como creadores de los componentes de nuestro sistema de diseño, sino también para nuestro equipo de desarrollo cuando los usa en nuestros productos.
Llevamos más allá las propiedades personalizadas
En el momento de escribir este artículo, no revelamos estas propiedades personalizadas contextuales en nuestra documentación. Sin embargo, planeamos hacerlo para que nuestro equipo de desarrollo más amplio pueda comprender y aprovechar estas propiedades. Nuestros componentes se empaquetan en npm con un archivo de manifiesto que contiene todo lo que se debe saber sobre ellos. Luego, consumimos el archivo de manifiesto como datos cuando se implementa nuestro sitio de documentación, lo que se hace con Eleventy y su función de datos globales. Planeamos incluir estas propiedades personalizadas contextuales en este archivo de datos de manifiesto.
Otro aspecto que queremos mejorar es la forma en que estas propiedades personalizadas contextuales heredan valores. Actualmente, por ejemplo, si deseas ajustar el color de dos componentes divisores, debes orientar ambos componentes específicamente con selectores o aplicar la propiedad personalizada directamente en el elemento con el atributo de estilo. Esto podría parecer bien, pero sería más útil si el desarrollador pudiera definir esos estilos en un elemento contenedor o incluso en el nivel raíz.
La razón por la que debes establecer el valor de la propiedad personalizada directamente en el componente es porque los definimos en el mismo elemento a través del selector de host de componentes. Los tokens de diseño globales que usamos directamente en el componente pasan directamente, no se ven afectados por este problema e incluso se pueden interceptar en elementos superiores. ¿Cómo podemos obtener lo mejor de ambos mundos?
Propiedades personalizadas privadas y públicas
Las propiedades personalizadas privadas crearon una colección de Lea Verou, que es una propiedad personalizada contextual "privada" en el componente, pero configurada como "pública" con un resguardo.
Definir nuestras propiedades personalizadas contextuales de esta manera significa que aún podemos hacer todo lo que hacíamos antes, como heredar valores de tokens globales y volver a usar valores en todo el código de nuestro componente. Sin embargo, el componente también heredará de forma fluida nuevas definiciones de esa propiedad en sí mismo o en cualquier elemento superior.
Si bien se puede argumentar que este método no es realmente “privado”, creemos que es una solución bastante elegante para un problema que nos preocupaba. Cuando tengamos la oportunidad, lo abordaremos en nuestros componentes para que nuestro equipo de desarrollo tenga más control sobre el uso de los componentes y, al mismo tiempo, se beneficie de las barreras de seguridad que implementamos.
Espero que esta estadística sobre el uso de los componentes web con las propiedades personalizadas de CSS te haya resultado útil. Cuéntanos qué te parece y, si decides usar alguno de estos métodos en tu propio trabajo, puedes encontrarme en Twitter @DavidDarnes. También puedes encontrar a Nordhealth @NordhealthHQ en Twitter, así como al resto de mi equipo, que trabajó arduamente para crear este sistema de diseño y ejecutar las funciones mencionadas en este artículo: @Viljamis, @WickyNilliams y @eric_habich.
Hero image de Dan Cristian Pădure tema