CSSStyleSheet: Módulos CSS nativos

Importar CSS desde Javascript de forma nativa


En Javascript, tenemos varias API para trabajar con CSS de forma nativa. Una que no es muy conocida es la API CSSStyleSheet, que nos permite trabajar con hojas de estilos desde Javascript.

Existe desde hace mucho tiempo, pero recientemente se han hecho modificaciones muy interesantes, que nos permiten trabajar de forma similar a CSS modules pero sin necesidad de preprocesar el código, sino haciéndolo directamente desde el navegador.

El objeto CSSStyleSheet

El objeto CSSStyleSheet existe desde hace tiempo y tiene buena compatibilidad en navegadores, sin embargo, recientemente se le ha dado soporte a un método replace() y replaceSync() para reemplazar todo el código CSS de uno de estos objetos.

Esto nos permite, por ejemplo, crear una nueva hoja de estilos vacía, e insertarle un que contenga un fragmento de código CSS. Observa el siguiente ejemplo:

const styles = /* css */`
body {
  background: indigo;
  color: white;
  padding: 1rem;
}

.container {
  background: black;
  color: gold;
}
`;

const css = new CSSStyleSheet();
await css.replace(styles);

En primer lugar, construimos un objeto CSSStyleSheet(), luego ejecutamos la función asíncrona replace() con await y una vez termina, obtenemos un objeto CSSStyleSheet. Este objeto tiene, entre otras cosas, una propiedad cssRules que contiene una lista de objetos CSSRule que contienen las reglas de CSS que hemos insertado en el de texto.

En el ejemplo anterior, la lista cssRules contiene dos reglas, una para body y otra para .container. También podemos añadir, eliminar o modificar reglas manualmente, utilizando métodos como .insertRule(), deleteRule(), o accediendo a la regla específica:

const css = new CSSStyleSheet();

// Insertar reglas
css.insertRule(".container { background: red }");
css.insertRule(".title { background: deeppink }");

// Eliminar regla (están indexados por número)
css.deleteRule(0);

// Modificar regla
const firstRule = css.cssRules.item(0);
firstRule.style.setProperty("background", "red");

Como puedes ver, podemos modificar los estilos a nuestro antojo de forma muy sencilla.

Adoptando estilos en la página

Sin embargo, hemos creado un objeto de hojas de estilo pero aún no hemos hecho nada con dicho objeto. Sería interesante conocer una forma de aplicarlo a una página. Para ello, utilizaremos el objeto document.adoptedStyleSheets de la página en la que trabajamos.

Por ejemplo, tras crear nuestro objeto CSSStyleSheet y añadirle los estilos que queremos, podemos simplemente utilizar el método .push() (funciona igual que un array) para «adoptar» la hoja de estilos que hemos creado junto a la que tenemos ya existente aplicada:

const css = new CSSStyleSheet();
await css.replace(styles);
document.adoptedStyleSheets.push(css);

Si necesitas hacer composición, puedes insertar múltiples hojas de estilo utilizando document.adoptedStyleSheets.push(css, css2, ...).

Adoptando estilos locales

Ten en cuenta que lo anterior adoptaría los estilos de forma global. Cuando trabajamos por componentes o por partes específicas de la web, una de las cosas que más nos puede interesar es que los estilos que definimos afecten solo a esa región y no a toda la web.

Esto también se puede conseguir de forma nativa, utilizando el Shadow DOM. Observa el siguiente fragmento de código:

// Creamos una hoja de estilos
const styles = `h1 { background: indigo; color: white }`;
const css = new CSSStyleSheet();
await css.replace(styles);

// Este sería el HTML global
document.body.setHTMLUnsafe(`<h1>Titular global</h1><div class='container'></div>`);

// En el .container, creamos un Shadow DOM y los estilos sólo afectan a este elemento
const container = document.querySelector(".container");
container.attachShadow({ mode: "open" });
container.shadowRoot.setHTMLUnsafe("<h1>Titular local</h1>");
container.shadowRoot.adoptedStyleSheets.push(css);

Si quieres comprender mejor como funciona este mecanismo, echa un vistazo al post de Shadow DOM y al de Shadow DOM declarativo. Es especialmente interesante en el contexto de WebComponents.

Importando estilos CSS

Ya sabemos como crear hojas de estilos e incorporarlas en nuestras páginas. Ahora... ¡sería fantástico si pudieramos importar los estilos desde un fichero .css externo! ¿Verdad? ¿Se puede?

Aunque el soporte aún no es completo y quedan navegadores por implementarlo, ya podemos hacerlo con los import attributes de Javascript:

import styles from "./component.css" with { type: "css" };

document.adoptedStyleSheets.push(styles);

Como puedes ver, el import de Javascript, con la sintaxis with e indicándole el tipo de fichero que se espera importar, puede importar módulos ESM, sólo que en lugar de Javascript, se trata de CSS. De esta forma, en styles en lugar de tener un de texto con el contenido del fichero .css, tendríamos un objeto CSSStyleSheet, que es mucho más potente y práctico para manipular y gestionar.

Luego, simplemente tenemos que adoptar los estilos importados en el documento o en el Shadow DOM donde queramos incluirlos.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev