Clases y estilos CSS

Fundamentos de CSS en React


React es agnóstico a la forma de dar estilo con CSS, es decir, que React no recomienda ninguna forma particular, y la estrategia utilizada corre a cargo del desarrollador. Sin embargo, las formas tradicionales de CSS no suelen encajar 100% con React, ya que es una librería Javascript y de forma tradicional CSS se gestiona a través del HTML.

Así pues, veamos como podemos gestionar el CSS desde React, desde las formas más básicas y sencillas a algunas más complejas y potentes.

CSS usando className

La forma tradicional de gestionar CSS es a través de las clases de HTML. Sin embargo, una diferencia que suele causar problemas es que en JSX no podemos hacer referencia a class, porque es una palabra reservada para las clases de Javascript. Por esta razón, en su lugar, utilizamos la propiedad Javascript .className.

Observa que los datos que utilizamos los obtenemos a través de props, para poder reutilizar el componente en el futuro:

import "./UserCard.css";

export function UserCard({ user, url, children }) {
  return (
    <article className="usercard">
      <img src={user.image} alt={user.name} />
      <div className="data">
        <h1>{user.name}</h1>
        {children}
        <footer className="links">
          <a href={url.site}>Site</a>
          <a href={url.twitch}>Twitch</a>
          <a href={url.twitter}>Twitter</a>
        </footer>
      </div>
    </article>
  );
}
.usercard {
  display: inline grid;
  grid-template-columns: 150px 1fr;
  background: #262626;
  padding: 1.5rem;
  max-width: 450px;

  & img {
    --size: 128px;

    width: var(--size);
    height: var(--size);
    border-radius: 50%;
  }

  & h1 {
    color: #fff;
    font-size: 2.25rem;
    margin: 0;
    line-height: 75%;
  }

  & .links {
    display: inline flex;
    gap: 0.25rem;

    & a {
      background: deeppink;
      padding: 0 0.5rem;
      text-decoration: none;
      color: #fff;

      &:hover {
        background: gold;
        color: #222;
      }
    }
  }
}

Observa que estamos importando desde Javascript un fichero .css y que no lo guardamos en un objeto o elemento, sino simplemente importamos el fichero directamente. Esto es algo especial de React. Lo que estamos haciendo es importar un fichero CSS que se añadirá de forma global en un bloque <style> en nuestro HTML.

En el fichero .css estamos utilizando CSS Nesting sobre la clase principal para evitar conflictos y colisiones con otros elementos, pero aún podríamos tener problemas si tenemos elementos .usercard en otra parte de la página que no queremos que tomen estos estilos.

Quizás lo ideal sería utilizar algún mecanismo que se encargue de que los estilos CSS importados no se apliquen globalmente, sino que sólo se apliquen al componente en cuestión donde estamos importando. Algo que hace esto de forma estupenda es CSS Modules.

CSS Modules

Una estrategia muy utilizada en ecosistemas React es utilizar CSS Modules. Se trata de una variación de lo que ya hemos visto, pero con algunos cambios que hacen que los estilos CSS importados sólo afecten al componente en cuestión. De esta forma, hablamos de «módulos CSS» y no de CSS global.

Observa nuestro componente UserCard.jsx, ya que el fichero CSS no cambiaría, excepto que su nombre debería ser UserCard.module.css. Al detectar esa extensión .module.css, nuestro automatizador Vite hará algunos cambios al gestionar el CSS y lo tratará como un módulo CSS:

import styles from "./UserCard.module.css";

export function UserCard({ user, url, children }) {
  const { image, name } = user;

  return (
    <article className={styles.usercard}>
      <img src={image} alt={name} />
      <div>
        <h1>{name}</h1>
        {children}
        <footer className={styles.links}>
          <a href={url.site}>Site</a>
          <a href={url.twitch}>Twitch</a>
          <a href={url.twitter}>Twitter</a>
        </footer>
      </div>
    </article>
  );
}

Al importar el fichero UserCard.module.css lo guardamos en un objeto styles que contendrá las clases utilizadas en ese fichero .module.css, haciendo referencia a una clase generada pseudoaleatoriamente (para evitar colisiones CSS con las clases).

Observa que en lugar de definir los nombres de clases, lo que hacemos es hacer referencia al objeto style y a la propiedad con el nombre de clase utilizado. Por ejemplo, styles.usercard hará referencia a la clase .usercard de nuestro fichero .module.css.

Si quieres aprender más, echa un vistazo a la sección de CSS-in-JS donde hablamos de CSS Modules.

Estilos dinámicos

En muchos casos nos encontraremos con una situación donde queremos dar estilo dependiendo del estado o de la lógica de Javascript. En este caso, no nos sirve simplemente lo anterior, sino que tenemos que utilizar algún mecanismo que nos permita mezclarlo.

Probablemente, la forma más adecuada y eficiente para gestionar los estilos de forma dinámica es el uso de Variables CSS. Las variables CSS se pueden definir tanto desde:

  • 🟧 Desde HTML: A través de un atributo style.
  • 🟪 Desde CSS: Definiendo una variable CSS.
  • 🟨 Desde Javascript: Usando el método .setProperty().

En React, la forma más cómoda de hacerlo, probablemente sea desde JSX en un atributo style. Observa que tenemos un dato color en Javascript y queremos hacerlo llegar al CSS. Este dato podría ser dinámico, obtenerse desde una fuente externa de datos o generarse en la lógica Javascript:

export function UserCard() {
  /* ... */

  const color = "red";

  return (
    <article className={styles.usercard} style={{ "--bg-color": color }}>
      <!-- ... -->
    </article>
  );
}

Mediante un atributo style creamos la variable CSS --bg-color que existirá para todo el elemento <article> y sus descendientes, de forma similar a un contexto.

Otra forma de cambiarlas podría ser utilizar un hook personalizado o el hook useRef, el cuál veremos un poco más adelante.

Mediante la microlibrería clsx podemos gestionar estilos dinámicos en React de forma más cercana a la «programación». Esta pequeña librería (de apenas 239 bytes) nos permite construir strings de forma condicional para utilizarla en nuestro JSX para mezclar clases o estilos.

export function UserCard() {
  /* ... */

  const isHighlight = true;   // Puede ser cualquier lógica o estado

  return (
    <article className={clsx(styles.usercard, { [styles.highlight]: isHighlight })}>
      <!-- ... -->
    </article>
  );
}
.usercard {
  background: #262626;
  padding: 1.5rem;
  max-width: 450px;
}

.highlight {
  border: 2px solid gold;
  box-shadow: 0 0 10px #ffd700;
}

Observa que clsx() se encarga de mezclar los estilos de styles.usercard y styles.highlight, sólo si la variable isHighlight es verdadera. De esta forma, evitamos tratar manualmente estos datos y hacerlo más legible y visual en nuestro código.

Existen otras alternativas para estilar elementos en React, pero requieren la instalación de librerías externas más complejas o con más dependencias. Abordaremos esos detalles en el próximo post.

¿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