Scoping en Javascript

Estrategias de aislar CSS en la industria Javascript


Debido a que CSS carecía de un sistema de alcance nativo, la industria Javascript ha ido buscando formas no oficiales de añadir esa característica en tiempos donde era inexistente. En esta sección vamos a hablar de algunas de esas formas y estrategias.

Algunas de las principales son las siguientes:

EstrategiaDescripciónMás info
BEMMetodología de nomenclatura para organizar clases, evitar conflictos y reducir especificidad.Ver
CSS ModulesEstrategia para renombrar clases mediante hashes y evitar conflictos de nombres.Ver
CSS-in-JSLibrerías Javascript que permiten encapsular estilos por componentes.Ver
CSS AtómicoEstrategia para aplicar múltiples clases únicas y así evitar conflictos CSS.Ver
WebComponentsUtilizar Shadow DOM para aislar HTML y CSS, obteniendo encapsulación real.Ver

Vamos a explicar por encima cada una de ellas.

BEM (Block Element Modifier)

BEM es una estrategia que se basa en una serie de recomendaciones para escribir clases y nombrar elementos. De esa forma, limitas las posibilidades de que ocurran conflictos de CSS y haces más predecible e intuitiva la forma de escribir CSS.

<div class="card">
  <div class="card__image">
    <img src="image.png" alt="Imagen de ejemplo">
  </div>
  <div class="card__content">
    <h2 class="card__title">Título de la Card</h2>
    <p class="card__description">
      Descripción breve de la card.
      Aquí puedes incluir información.
    </p>
  </div>
  <div class="card__actions">
    <button class="card__button card__button--primary">Aceptar</button>
    <button class="card__button card__button--secondary">Cancelar</button>
  </div>
</div>
.card {
  /* Estilos de la clase "card" */
  &__image {
    /* Estilos de la clase "card__image" */
  }
  &__content {
    /* Estilos de la clase "card__content" */
  }
  /* ... */
}

Si observa el CSS parcial indicado en el ejemplo anterior, comprobarás que uno de los puntos interesantes de BEM es que se suele combinar con preprocesadores CSS como Sass, de modo que a la hora de dar estilos, no tengas que escribir todo el selector, sino que aproveches el nesting de Sass y Sass genere los selectores finales.

Ventajas/Desventajas:

  • 💖 Conflictos: Se centra en limitar el nombrado de clases y evitar conflictos.
  • 💖 Especificidad: Al crear 1 selector por clase, reduce la especificidad.
  • 💔 Dificultad: Suele resultar complejo acostumbrarse a la nomenclatura de BEM.
  • 💔 Legibilidad: La sintaxis es algo verbosa y poco semántica.

Existen otras metodologías de nomenclatura similares a BEM, como por ejemplo CubeCSS, que aunque es menos conocida, está más enfocada en el código semántico.

CSS Modules

CSS Modules es una estrategia para poder dar estilo CSS sólo a zonas concretas del HTML, evitando conflictos entre clases. Se basa en la creación de módulos por archivos con extensión .module.css. Estos archivos contienen el CSS de una zona de la página (generalmente, un componente):

.button {
  background: indigo;
  color: white;
  padding: 1rem;
}

.primary {
  background: dodgerblue;
}
import styles from "./Button.module.css";

const button = document.createElement("button");
button.className = `${styles.button} ${styles.primary}`;
button.textContent = "Click me!";

document.body.append(button);

Si observas el Javascript, comprobarás que realiza una importación del .module.css creando un objeto, donde todas las clases CSS que contiene, son propiedades del objeto. De esta forma, luego puede asignarle la clase correspondiente dependiendo de la lógica de Javascript necesaria.

A nivel interno, este sistema utiliza un diccionario donde mapea cada nombre de clase, cambiándola por una cadena de carácteres y números aleatorios (por ejemplo, button-175oi2r), con el objetivo de ser único y que no haya nunca riesgo de conflicto con otras clases.

Ventajas/Desventajas:

  • 💖 Se evitan los conflictos de forma muy cómoda.
  • 💖 Permite tener separados los estilos en módulos CSS (organizado).
  • 💔 Requiere herramientas externas (webpack, parcel, rollup, vite...) para poder usarse.
  • 💔 La generación de clases es poco semántica y puede complicar la depuración.

CSS-in-JS

CSS-in-JS es una estrategia que permite usar Javascript para generar los estilos CSS, teniendo mayor control sobre los estilos. De forma muy similar al anterior, se añade el código CSS en un string template, y utilizando una librería (en este caso, emotion) se procesa el código CSS y se devuelve un nombre de clase autogenerado, que le asignaremos al elemento que queremos darle estilos:

import { css } from "@emotion/css";

const cssButton = css`
  background: indigo;
  color: white;
  padding: 1rem;
`;

const button = document.querySelector("button");
button.className = cssButton;

Esta forma de trabajar suele adaptarse muy bien a los frameworks Javascript, por lo que junto a la anterior, es una de las estrategias más utilizadas por ellos. Librerías populares pueden ser Emotion, Styled Components, Vanilla Extract o Panda CSS.

Ventajas/Desventajas:

  • 💖 Se evitan los conflictos de forma muy cómoda.
  • 💖 Se puede utilizar la potencia de Javascript para estilos dinámicos.
  • 💔 Depende de librerías de terceros
  • 💔 La generación de clases es poco semántica y puede complicar la depuración.

CSS Atómico

El CSS Atómico es una estrategia para escribir CSS que se basa en las clases de utilidad, es decir, en lugar de escribir código CSS, escribes clases predefinidas por una librería en el HTML, de modo que evitas que surjan conflictos. Un caso muy popular del CSS atómico o las clases de utilidad es Tailwind CSS.

<div class="max-w-sm bg-white rounded-lg shadow-md overflow-hidden">
  <div>
    <img class="w-full h-48 object-cover" src="image.png" alt="Imagen de ejemplo">
  </div>
  <div class="p-4">
    <h2 class="text-xl font-semibold text-gray-800">Título de la Card</h2>
    <p class="mt-2 text-gray-600">
      Descripción breve de la card. Aquí puedes incluir información.
    </p>
  </div>
  <div class="p-4 flex gap-2">
    <button class="bg-blue-500 text-white px-4 py-2 rounded">Aceptar</button>
    <button class="bg-gray-200 text-gray-800 px-4 py-2 rounded">Cancelar</button>
  </div>
</div>

Como puedes ver, no escribes CSS, sino que escribes clases que realizan tareas predefinidas y específicas.

Ventajas/Desventajas:

  • 💖 Permite hacer prototipos de forma muy rápida. No tienes que hacer «switch» del HTML al CSS.
  • 💖 Utilizar clases predefinidas asegura que los estilos sean consistentes y no generen conflictos.
  • 💔 El HTML se vuelve complejo, poco semántico, difícil de leer y cachear.
  • 💔 A largo plazo y en proyectos medio-grandes, suele ser difícil de mantener.

WebComponents

WebComponents es una forma nativa de crear componentes y aislar el código CSS para que sólo afecte a ese componente utilizando únicamente HTML, CSS y Javascript. Hace uso de Shadow DOM, una forma de encapsular CSS dentro de un DOM particular.

class BaseButton extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    this.shadowRoot.innerHTML = `
      <style>
        button {
          background: indigo;
          color: white;
          padding: 1rem;
        }
      </style>
      <button><slot>Click me!</slot></button>
    `;
  }
}

customElements.define("base-button", BaseButton);
<base-button>Hello!</base-button>

Ventajas/Desventajas:

  • 💖 Tienes encapsulamiento de HTML/CSS real, con features potentes.
  • 💖 Es nativo, no requieres librerías externas ni frameworks.
  • 💔 Tiene una curva de aprendizaje compleja, que requiere conocimientos.
  • 💔 La experiencia de desarrollo aún no es tan buena como la de los frameworks.

Existen muchas formas de escribir WebComponents, algunas más complejas y otras más sencillas. Además, también tienes librerías como Lit o Atomico. Puedes aprender más aquí sobre WebComponents.

¿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