Dar estilo a los componentes web mediante una hoja de estilo compartida
Publicado: 2022-03-10Los componentes web son una característica nueva y asombrosa de la web, que permite a los desarrolladores definir sus propios elementos HTML personalizados. Cuando se combinan con una guía de estilo, los componentes web pueden crear una API de componente, lo que permite a los desarrolladores dejar de copiar y pegar fragmentos de código y, en su lugar, utilizar un elemento DOM. Al usar el DOM en la sombra, podemos encapsular el componente web y no tener que preocuparnos por las guerras de especificidad con cualquier otra hoja de estilo en la página.
Sin embargo, los componentes web y las guías de estilo actualmente parecen estar en desacuerdo entre sí. Por un lado, las guías de estilo proporcionan un conjunto de reglas y estilos que se aplican globalmente a la página y garantizan la coherencia en todo el sitio web. Por otro lado, los componentes web con el shadow DOM evitan que los estilos globales penetren en su encapsulación, evitando así que la guía de estilo los afecte.
Lectura adicional en SmashingMag:
- Aplicación de mejores prácticas en sistemas basados en componentes
- Cómo usar el preprocesador LESS CSS para hojas de estilo más inteligentes
- Una inmersión profunda en Adobe Edge Reflow
Entonces, ¿cómo pueden coexistir los dos, con guías de estilo globales que continúan brindando consistencia y estilos, incluso a los componentes web con el shadow DOM? Afortunadamente, hay soluciones que funcionan hoy y más soluciones por venir, que permiten que las guías de estilo globales proporcionen estilo a los componentes web. (En el resto de este artículo, usaré el término "componentes web" para referirme a los elementos personalizados con el shadow DOM).
¿Qué debe diseñar una guía de estilo global en un componente web?
Antes de discutir cómo obtener una guía de estilo global para aplicar estilo a un componente web, debemos analizar qué debe y qué no debe tratar de diseñar.
En primer lugar, las mejores prácticas actuales para componentes web establecen que un componente web, incluidos sus estilos, debe encapsularse, de modo que no dependa de ningún recurso externo para funcionar. Esto permite que se use en cualquier lugar dentro o fuera del sitio web, incluso cuando la guía de estilo no esté disponible.
A continuación se muestra un componente web de formulario de inicio de sesión simple que encapsula todos sus estilos.
<template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } p { margin: 0; } p + p { margin-top: 20px; } a { color: #1f66e5; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } input[type="submit"] { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <div class="container"> <form action="#"> <p> <label for="username">User Name</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit" value="Login"> </p> <p class="footnote">Not registered? <a href="#">Create an account</a></p> </form> </div> </template> <script> const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); const root = this.attachShadow({mode: 'closed'}); const temp = document.importNode(template.content, true); root.appendChild(temp); } }); </script>
Nota: Los ejemplos de código están escritos en la especificación de la versión 1 para componentes web.

Sin embargo, encapsular por completo cada componente web conduciría inevitablemente a una gran cantidad de CSS duplicado, especialmente cuando se trata de configurar la tipografía y el estilo de los elementos nativos. Si un desarrollador quiere usar un párrafo, una etiqueta de anclaje o un campo de entrada en su componente web, debe tener el mismo estilo que el resto del sitio web.
Si encapsulamos por completo todos los estilos que necesita el componente web, entonces el CSS para diseñar párrafos, etiquetas de anclaje, campos de entrada, etc., se duplicaría en todos los componentes web que los usan. Esto no solo aumentaría los costos de mantenimiento, sino que también conduciría a tamaños de descarga mucho mayores para los usuarios.
En lugar de encapsular todos los estilos, los componentes web solo deben encapsular sus estilos únicos y luego depender de un conjunto de estilos compartidos para manejar estilos para todo lo demás. Estos estilos compartidos se convertirían esencialmente en una especie de Normalize.css, que los componentes web podrían usar para garantizar que los elementos nativos tengan el estilo de acuerdo con la guía de estilo.
En el ejemplo anterior, el componente web del formulario de inicio de sesión declararía los estilos solo para sus dos clases únicas: .container
y .footnote
. El resto de los estilos pertenecerían a la hoja de estilo compartida y darían estilo a los párrafos, etiquetas de anclaje, campos de entrada, etc.
En resumen, la guía de estilo no debe tratar de diseñar el componente web, sino que debe proporcionar un conjunto de estilos compartidos que los componentes web pueden usar para lograr una apariencia uniforme.
Cómo se solía hacer el estilo del Shadow DOM con hojas de estilo externas
La especificación inicial para los componentes web (conocida como versión 0) permitía que cualquier hoja de estilo externa penetrara en el DOM oculto mediante el uso de los selectores ::shadow
o /deep/
CSS. El uso de ::shadow
y /deep/
le permitió tener una guía de estilo que penetrara en el DOM de la sombra y configurara los estilos compartidos, ya sea que el componente web lo quisiera o no.
/* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }
Con la llegada de la versión más reciente de la especificación de componentes web (conocida como versión 1), los autores han eliminado la capacidad de las hojas de estilo externas para penetrar el DOM oculto y no han proporcionado ninguna alternativa. En cambio, la filosofía ha cambiado de usar dragones para diseñar componentes web a usar puentes. En otras palabras, los autores de componentes web deben estar a cargo de qué reglas de estilo externas se permiten para diseñar su componente, en lugar de verse obligados a permitirlas.
Desafortunadamente, esa filosofía aún no se ha puesto al día con la web, lo que nos deja en un pequeño lío. Afortunadamente, algunas soluciones disponibles en la actualidad, y algunas que llegarán en un futuro no muy lejano, permitirán una hoja de estilo compartida para diseñar un componente web.
Lo que puedes hacer hoy
Hay tres técnicas que puede usar hoy que permitirán que un componente web comparta estilos: @import
, elementos personalizados y una biblioteca de componentes web.
Usando @importar
Hoy en día, la única forma nativa de llevar una hoja de estilo a un componente web es usar @import
. Aunque esto funciona, es un anti-patrón. Sin embargo, para los componentes web, es un problema de rendimiento aún mayor.
<template> <style> @import "styleguide.css" </style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>
Normalmente, @import
es un antipatrón porque descarga todas las hojas de estilo en serie, en lugar de en paralelo, especialmente si están anidadas. En nuestra situación, no se puede evitar descargar una sola hoja de estilo en serie, por lo que, en teoría, debería estar bien. Pero cuando probé esto en Chrome, los resultados mostraron que el uso de @import
hizo que la página se procesara hasta medio segundo más lento que cuando simplemente incrustaba los estilos directamente en el componente web.
Nota: Debido a las diferencias en la forma en que funciona el polyfill de las importaciones de HTML en comparación con las importaciones de HTML nativo, WebPagetest.org solo se puede usar para brindar resultados confiables en navegadores que admiten de forma nativa las importaciones de HTML (es decir, Chrome).

@import
hace que el navegador se reproduzca hasta medio segundo más lento que cuando simplemente incrusta los estilos directamente en el componente web. Al final, @import
sigue siendo un antipatrón y puede ser un problema de rendimiento en los componentes web. Entonces, no es una gran solución.
No uses el Shadow DOM
Dado que el problema de intentar proporcionar estilos compartidos a los componentes web se deriva del uso del DOM en la sombra, una forma de evitar el problema por completo es no usar el DOM en la sombra.
Al no utilizar el DOM en la sombra, creará elementos personalizados en lugar de componentes web (vea el apartado a continuación), la única diferencia es la falta del DOM en la sombra y el alcance. Su elemento estará sujeto a los estilos de la página, pero ya tenemos que lidiar con eso hoy, por lo que no es nada que no sepamos cómo manejar. Los elementos personalizados son totalmente compatibles con el polyfill de webcomponentjs, que tiene una excelente compatibilidad con el navegador.
El mayor beneficio de los elementos personalizados es que puede crear una biblioteca de patrones usándolos hoy, y no tiene que esperar hasta que se resuelva el problema del estilo compartido. Y debido a que la única diferencia entre los componentes web y los elementos personalizados es el DOM en la sombra, siempre puede habilitar el DOM en la sombra en sus elementos personalizados una vez que esté disponible una solución para el estilo compartido.
Si decide crear elementos personalizados, tenga en cuenta algunas diferencias entre los elementos personalizados y los componentes web.
Primero, debido a que los estilos para el elemento personalizado están sujetos a los estilos de página y viceversa, querrá asegurarse de que sus selectores no causen ningún conflicto. Si sus páginas ya utilizan una guía de estilo, deje los estilos para el elemento personalizado en la guía de estilo y haga que el elemento genere el DOM y la estructura de clases esperados.
Al dejar los estilos en la guía de estilo, creará una ruta de migración fluida para sus desarrolladores, ya que pueden continuar usando la guía de estilo como antes, pero luego migrar lentamente para usar el nuevo elemento personalizado cuando puedan hacerlo. Una vez que todos estén usando el elemento personalizado, puede mover los estilos para que residan dentro del elemento a fin de mantenerlos juntos y permitir una refactorización más sencilla a los componentes web más adelante.
En segundo lugar, asegúrese de encapsular cualquier código JavaScript dentro de una expresión de función inmediatamente invocada (IFFE), de modo que no desangre ninguna variable en el ámbito global. Además de no proporcionar el alcance de CSS, los elementos personalizados no proporcionan el alcance de JavaScript.
En tercer lugar, deberá usar la función de devolución de llamada connectedCallback
del elemento personalizado para agregar la plantilla DOM al elemento. De acuerdo con la especificación del componente web, los elementos personalizados no deben agregar elementos secundarios durante la función de constructor, por lo que deberá diferir la adición del DOM a la función de devolución de llamada connectedCallback
.
Por último, el elemento <slot>
no funciona fuera del DOM oculto. Esto significa que tendrá que usar un método diferente para que los desarrolladores puedan insertar su contenido en su elemento personalizado. Por lo general, esto implica simplemente manipular el DOM usted mismo para insertar su contenido donde lo desee.
Sin embargo, debido a que no hay separación entre el DOM de sombra y el DOM de luz con elementos personalizados, también deberá tener mucho cuidado de no cambiar el estilo del DOM insertado, debido a los estilos en cascada de sus elementos.
<!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
<!-- index.html --> <link rel="stylesheet" href="styleguide.css"> <link rel="import" href="login-form.html"> <login-form></login-form>
En términos de rendimiento, los elementos personalizados son casi tan rápidos como los componentes web que no se usan (es decir, vincular la hoja de estilo compartida en el head
y usar solo elementos DOM nativos). De todas las técnicas que puede usar hoy en día, esta es, con mucho, la más rápida.

Aparte: un elemento personalizado sigue siendo un componente web para todos los efectos. El término "componentes web" se usa para describir cuatro tecnologías separadas: elementos personalizados, etiquetas de plantilla, importaciones de HTML y el DOM oculto.
Desafortunadamente, el término se ha utilizado para describir todo lo que utiliza una combinación de las cuatro tecnologías. Esto ha generado mucha confusión sobre lo que la gente quiere decir cuando dice "componente web". Tal como lo descubrió Rob Dodson, me ha resultado útil usar diferentes términos cuando hablo de elementos personalizados con y sin el shadow DOM.
La mayoría de los desarrolladores con los que he hablado tienden a asociar el término "componente web" con un elemento personalizado que usa el shadow DOM. Entonces, para los propósitos de este artículo, he creado una distinción artificial entre un componente web y un elemento personalizado.
Uso de una biblioteca de componentes web
Otra solución que puede usar hoy es una biblioteca de componentes web, como Polymer, SkateJS o X-Tag. Estas bibliotecas ayudan a llenar los vacíos del soporte actual y también pueden simplificar el código necesario para crear un componente web. También suelen proporcionar características adicionales que facilitan la escritura de componentes web.
Por ejemplo, Polymer le permite crear un componente web simple con solo unas pocas líneas de JavaScript. Un beneficio adicional es que Polymer brinda una solución para usar el shadow DOM y una hoja de estilo compartida. Esto significa que puede crear componentes web hoy que comparten estilos.
Para hacer esto, crea lo que llaman un módulo de estilo, que contiene todos los estilos compartidos. Puede ser una etiqueta <style>
con los estilos compartidos en línea o una etiqueta <link rel=“import”>
que apunta a una hoja de estilo compartida. En cualquier caso, incluya los estilos en su componente web con una etiqueta <style include>
, y luego Polymer analizará los estilos y los agregará como una etiqueta <style>
en línea a su componente web.

<!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
<!-- login-form.html --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module> <template> <!-- Include the shared styles --> <style include="shared-styles"></style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> Polymer({ is: 'login-form' }); </script> </dom-module>
El único inconveniente de usar una biblioteca es que puede retrasar el tiempo de procesamiento de sus componentes web. Esto no debería ser una sorpresa porque descargar el código de la biblioteca y procesarlo lleva tiempo. Los componentes web de la página no pueden comenzar a procesarse hasta que la biblioteca termine de procesarse.
En el caso de Polymer, puede retrasar el tiempo de renderizado de la página hasta medio segundo en comparación con los componentes web nativos. Un módulo de estilo que incrusta los estilos es un poco más lento que un módulo de estilo que vincula los estilos, e incrustar los estilos directamente en el componente web es tan rápido como usar un módulo de estilo.
Una vez más, Polymer no hace nada en particular para que el tiempo de renderizado sea más lento. La descarga de la biblioteca de Polymer y el procesamiento de todas sus increíbles funciones, además de la creación de todos los enlaces de plantilla, lleva tiempo. Es solo el intercambio que tendrá que hacer para usar una biblioteca de componentes web.

Los resultados de las pruebas de rendimiento muestran que, al usar Polymer, los componentes web se procesarán hasta medio segundo más lento que los componentes web nativos.
La promesa del futuro
Si ninguna de las soluciones actuales funciona para usted, no se desespere. Si todo va bien, dentro de algunos meses a algunos años, podremos usar estilos compartidos usando algunos enfoques diferentes.
Propiedades personalizadas
Las propiedades personalizadas (o variables CSS, como se les ha llamado) son una forma de establecer y usar variables en CSS. Esta noción no es nueva para los preprocesadores de CSS, pero como característica nativa de CSS, las propiedades personalizadas son en realidad más poderosas que una variable de preprocesador.
Para declarar una propiedad personalizada, use la notación de propiedad personalizada de –my-variable: value
y acceda a la variable usando property: var(–my-variable)
. Una propiedad personalizada se conecta en cascada como cualquier otra regla de CSS, por lo que su valor se hereda de su padre y se puede anular. La única advertencia sobre las propiedades personalizadas es que deben declararse dentro de un selector y no pueden declararse por sí mismas, a diferencia de una variable de preprocesador.
<style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>
Una cosa que hace que las propiedades personalizadas sean tan poderosas es su capacidad para perforar el DOM de la sombra. Esta no es la misma idea que los selectores /deep/
y ::shadow
porque no se abren camino en el componente web. En su lugar, el autor del componente web debe usar la propiedad personalizada en su CSS para que se aplique. Esto significa que el autor de un componente web puede crear una API de propiedad personalizada que los consumidores del componente web pueden usar para aplicar sus propios estilos.
<template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>
El soporte del navegador para propiedades personalizadas es sorprendentemente bueno. La única razón por la que no es una solución que pueda usar hoy es que no hay un polyfill que funcione sin la versión 1 de Custom Elements. El equipo detrás del polyfill de webcomponentjs está trabajando actualmente para agregarlo, pero aún no se ha lanzado y está construido. lo que significa que si hash sus activos para la producción, no puede usarlos. Por lo que entiendo, se lanzará a principios del próximo año.
Aun así, las propiedades personalizadas no son un buen método para compartir estilos entre componentes web. Debido a que solo se pueden usar para declarar un único valor de propiedad, el componente web aún necesitaría incrustar todos los estilos de la guía de estilo, aunque con sus valores sustituidos por variables.
Las propiedades personalizadas son más adecuadas para las opciones de temas que para los estilos compartidos. Debido a esto, las propiedades personalizadas no son una solución viable para nuestro problema.
/* Usar la propiedad personalizada */ input { background: var(–main-bg-color); } </estilo>
Una cosa que hace que las propiedades personalizadas sean tan poderosas es su capacidad para perforar el DOM de la sombra. Esta no es la misma idea que los selectores /deep/
y ::shadow
porque no se abren camino en el componente web. En su lugar, el autor del componente web debe usar la propiedad personalizada en su CSS para que se aplique. Esto significa que el autor de un componente web puede crear una API de propiedad personalizada que los consumidores del componente web pueden usar para aplicar sus propios estilos.
<template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>
El soporte del navegador para propiedades personalizadas es sorprendentemente bueno. La única razón por la que no es una solución que pueda usar hoy es que no hay un polyfill que funcione sin la versión 1 de Custom Elements. El equipo detrás del polyfill de webcomponentjs está trabajando actualmente para agregarlo, pero aún no se ha lanzado y está construido. lo que significa que si hash sus activos para la producción, no puede usarlos. Por lo que entiendo, se lanzará a principios del próximo año.
Aun así, las propiedades personalizadas no son un buen método para compartir estilos entre componentes web. Debido a que solo se pueden usar para declarar un único valor de propiedad, el componente web aún necesitaría incrustar todos los estilos de la guía de estilo, aunque con sus valores sustituidos por variables.
Las propiedades personalizadas son más adecuadas para las opciones de temas que para los estilos compartidos. Debido a esto, las propiedades personalizadas no son una solución viable para nuestro problema.
@aplicar reglas
Además de las propiedades personalizadas, CSS también obtiene reglas @apply
. Las reglas de aplicación son esencialmente mixins para el mundo CSS. Se declaran de manera similar a las propiedades personalizadas, pero se pueden usar para declarar grupos de propiedades en lugar de solo valores de propiedad. Al igual que las propiedades personalizadas, sus valores se pueden heredar y anular, y deben declararse dentro de un selector para que funcionen.
<style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>
El soporte del navegador para las reglas @apply
es básicamente inexistente. Chrome actualmente lo admite detrás de un indicador de función (que no pude encontrar), pero eso es todo. Tampoco hay polyfill en funcionamiento por la misma razón que no hay polyfill para propiedades personalizadas. El equipo de polyfill de webcomponentjs también está trabajando para agregar reglas @apply
, junto con propiedades personalizadas, por lo que ambas estarán disponibles una vez que se publique la nueva versión.
A diferencia de las propiedades personalizadas, las reglas @apply
son una solución mucho mejor para compartir estilos. Debido a que pueden configurar un grupo de declaraciones de propiedades, puede usarlos para configurar el estilo predeterminado para todos los elementos nativos y luego usarlos dentro del componente web. Para hacer esto, tendría que crear una regla @apply
para cada elemento nativo.
Sin embargo, para consumir los estilos, tendría que aplicarlos manualmente a cada elemento nativo, lo que aún duplicaría la declaración de estilo en cada componente web. Si bien eso es mejor que incrustar todos los estilos, tampoco es muy conveniente porque se convierte en estándar en la parte superior de cada componente web, que debe recordar agregar para que los estilos funcionen correctamente.
/* styleguide.css */ html { --typography: { color: #333333; font: 16px Arial, sans-serif; } --paragraph: { margin: 0; } --label { display: block; margin-bottom: 5px; } --input-text { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } --input-submit { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } /* And so on for every native element */ }
<!-- login-form.html --> <template> <style> :host { @apply --typography; } p { @apply --paragraph; } label { @apply --label; } input-text { @apply --input-text; } .input-submit { @apply --input-submit; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template>
Debido a la necesidad de un repetitivo extenso, no creo que las reglas @apply
sean una buena solución para compartir estilos entre componentes web. Sin embargo, son una gran solución para la tematización.
en la sombra DOM
De acuerdo con la especificación del componente web, los navegadores ignoran cualquier etiqueta <link rel=“stylesheet”>
en el shadow DOM, tratándolas como lo harían dentro de un fragmento de documento. Esto nos impidió vincular estilos compartidos en nuestros componentes web, lo cual fue desafortunado, es decir, hasta hace unos meses, cuando el Grupo de trabajo de componentes web propuso que las etiquetas <link rel=“stylesheet”>
deberían funcionar en la sombra DOM. Después de solo una semana de discusión, todos acordaron que deberían hacerlo y unos días después lo agregaron a la especificación HTML.
<template> <link rel="stylesheet" href="styleguide.css"> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>
Si eso suena demasiado rápido para que el grupo de trabajo acuerde una especificación, es porque no era una propuesta nueva. Hacer que las etiquetas de link
funcionen en el DOM en la sombra se propuso en realidad hace al menos tres años, pero se retrasó hasta que pudieron asegurarse de que no fuera un problema para el rendimiento.
Si la aceptación de la propuesta no es lo suficientemente emocionante, Chrome 55 (actualmente Chrome Canary) agregó la funcionalidad inicial de hacer que las etiquetas de link
funcionen en el DOM oculto. Incluso parece que esta funcionalidad ha aterrizado en la versión actual de Chrome. Incluso Safari ha implementado la función en Safari 18.
Ser capaz de vincular los estilos compartidos es, con mucho, el método más conveniente para compartir estilos entre componentes web. Todo lo que tendría que hacer es crear la etiqueta de link
, y todos los elementos nativos se diseñarían en consecuencia, sin requerir ningún trabajo adicional.
Por supuesto, la forma en que los fabricantes de navegadores implementen la función determinará si esta solución es viable. Para que esto funcione correctamente, las etiquetas de link
deben desduplicarse, de modo que varios componentes web que soliciten el mismo archivo CSS solo generarán una solicitud HTTP. El CSS también tendría que analizarse solo una vez, de modo que cada instancia del componente web no tuviera que volver a calcular los estilos compartidos, sino que reutilizaría los estilos calculados.
Chrome ya hace ambas cosas. Entonces, si todos los demás fabricantes de navegadores lo implementan de la misma manera, entonces las etiquetas de link
funcionan en el DOM oculto definitivamente resolverían el problema de cómo compartir estilos entre los componentes web.
Hojas de estilo construibles
Puede que le resulte difícil de creer, ya que aún no lo tenemos, pero una etiqueta de link
que funcione en el DOM oculto no es una solución a largo plazo. En cambio, es solo una solución a corto plazo para llevarnos a la solución real: hojas de estilo construibles.
Las hojas de estilo construibles son una propuesta para permitir la creación de objetos StyleSheet
en JavaScript a través de una función constructora. La hoja de estilo construida podría luego agregarse al DOM oculto a través de una API, lo que permitiría que el DOM oculto use un conjunto de estilos compartidos.
Desafortunadamente, esto es todo lo que pude deducir de la propuesta. Traté de obtener más información acerca de qué hojas de estilo construibles preguntaba al Grupo de Trabajo de Componentes Web, pero me redireccionaron a la lista de correo del Grupo de Trabajo de CSS del W3C, donde volví a preguntar, pero nadie respondió. Ni siquiera pude entender cómo estaba progresando la propuesta, porque no se ha actualizado en más de dos años.
Aun así, el Grupo de Trabajo de Componentes Web lo utiliza como la solución para compartir estilos entre componentes web. Con suerte, la propuesta se actualizará o el Grupo de trabajo de componentes web publicará más información al respecto y su adopción. Hasta entonces, la solución “a largo plazo” parece que no sucederá en un futuro previsible.
Lecciones aprendidas
Después de meses de investigación y pruebas, tengo muchas esperanzas para el futuro. Es reconfortante saber que después de años de no tener una solución para compartir estilos entre componentes web, finalmente hay respuestas. Es posible que esas respuestas no se establezcan hasta dentro de unos años más, pero al menos están ahí.
Si desea usar una guía de estilo compartida para diseñar componentes web hoy, no puede usar el DOM oculto y, en su lugar, crear elementos personalizados, o puede usar una biblioteca de componentes web que llene la compatibilidad para compartir estilos. Ambas soluciones tienen sus pros y sus contras, así que utilice la que funcione mejor para su proyecto.
Si decide esperar un tiempo antes de profundizar en los componentes web, en unos años deberíamos tener algunas soluciones excelentes para compartir los estilos entre ellos. Por lo tanto, siga revisando cómo está progresando.
Cosas a tener en cuenta
Tenga en cuenta algunas cosas si decide usar elementos personalizados o componentes web hoy.
Lo que es más importante, la especificación del componente web todavía se está desarrollando activamente, lo que significa que las cosas pueden cambiar y cambiarán. Los componentes web siguen estando a la vanguardia, así que prepárate para estar alerta mientras desarrollas con ellos.
Si decide utilizar el DOM en la sombra, sepa que es bastante lento y de bajo rendimiento en los navegadores polillenados. Fue por esta razón que los desarrolladores de Polymer crearon su implementación DOM sospechosa y la convirtieron en su predeterminada.
Chrome, Opera y, recientemente, Safari son los únicos navegadores que soportan el shadow DOM versión 0. Firefox todavía está en desarrollo, aunque lo ha soportado detrás de un experimento desde la versión 29. Microsoft todavía lo está considerando para Edge y lo tiene como alta prioridad en su hoja de ruta.
Sin embargo, shadow DOM versión 0 es la especificación anterior. Shadow DOM versión 1 es la nueva, y solo Chrome, Safari y Opera la admiten por completo. Sin mencionar que la versión 0 de elementos personalizados pasó por la misma actualización, y solo Chrome es totalmente compatible con la versión 1 de elementos personalizados, mientras que la vista previa técnica de Safari lo admite a partir de la versión 17. La versión 1 de elementos personalizados tiene algunos cambios importantes en la forma en que se escriben los componentes web, así que asegúrese de entender completamente lo que eso implica.
Por último, el polyfill de webcomponentjs solo admite la implementación de la versión 0 del shadow DOM y los elementos personalizados. Una rama de la versión 1 del polyfill admitirá la versión 1, pero aún no se ha lanzado.