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
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
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 .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.