Kodokon kodokon.com

Arquitectura CSS: BEM, utility-first, organización de archivos

Compara BEM y el enfoque utility-first, sus compromisos reales en equipo, y estructura tus archivos con capas de cascada.

11 min · 3 preguntas

Abrir esta lección en Kodokon

El CSS no se rompe a escala de un componente, se rompe a escala de un proyecto: especificidad que trepa solo para "ganar" frente a una regla existente, miedo a borrar código cuyo alcance desconoces, estilos que se filtran de una pantalla a otra. Una arquitectura CSS es ante todo una estrategia para gestionar la especificidad y el alcance. Las dos escuelas dominantes, BEM y utility-first, responden al mismo problema por caminos opuestos.

BEM (Block, Element, Modifier) nombra cada nodo con estilo: .card (un bloque autónomo), .card__title (un elemento que pertenece al bloque), .card--featured (una variante del bloque). La convención garantiza una especificidad plana - una sola clase en todas partes, nunca un selector de descendiente - de modo que no hay conflictos de orden, y un nombre que documenta la pertenencia. El coste: inventar nombres constantemente, y una duplicación real de valores (el mismo margin-top: 16px reescrito en veinte bloques).

CSS
.card {
  padding: 16px;
  border-radius: 8px;
}

.card__title {
  font-size: 1.25rem;
}

.card__title--muted {
  color: #6b7280;
}

.card--featured {
  border: 2px solid var(--brand);
}
BEM: especificidad plana, pertenencia explícita

El enfoque utility-first (siendo Tailwind su encarnación dominante) invierte la lógica: se acabaron los nombres, solo clases atómicas de responsabilidad única compuestas en el HTML. Las ventajas son medibles: cero esfuerzo de nomenclatura, estilos ubicados junto al marcado (borrar el componente borra sus estilos) y un paquete CSS final que se mantiene casi constante en tamaño, ya que las utilidades se comparten. Los compromisos también: HTML verboso, duplicación trasladada al marcado que exige una capa de componentes (React, Vue, parciales) para mantenerse DRY, y una curva de aprendizaje para el vocabulario.

HTML
<article class="flex flex-col gap-4 rounded-lg
  border border-gray-200 p-4 shadow-sm">
  <h2 class="text-lg font-semibold">Invoice</h2>
  <p class="text-sm text-gray-600">
    Paid on March 12
  </p>
</article>
Utility-first: los estilos viven en el marcado

Estos enfoques no se excluyen mutuamente: muchos equipos maduros los combinan - utilidades para el layout y el espaciado, clases de componente (BEM o CSS Modules) para los patrones y estados complejos. En cuanto a la organización de archivos, el principio ITCSS sigue siendo la referencia: ordenar de lo genérico a lo específico (reinicio, elementos base, componentes, utilidades). La regla nativa @layer convierte esta convención en una garantía a nivel del motor: la prioridad entre capas es independiente de la especificidad de los selectores que contienen.

CSS
@layer reset, base, components, utilities;

@layer base {
  h2 { font-size: 1.5rem; }
}

@layer components {
  #hero .btn { padding: 12px 24px; }
}

@layer utilities {
  .p-0 { padding: 0; }
}
Capas de cascada: el orden vence a la especificidad

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. En .card__title--muted, ¿qué denota el segmento --muted?
    • Una propiedad personalizada de CSS
    • Un modificador: una variante del elemento card__title
    • Un elemento hijo de card__title
    • Un estado gestionado por JavaScript
  2. ¿Cuál es la aportación principal de @layer a la cascada?
    • Aumenta la especificidad de los selectores de la capa
    • Hace que el orden de las capas tenga prioridad sobre la especificidad de los selectores
    • Aísla los estilos de un componente como lo haría un Shadow DOM
    • Carga las capas en paralelo para acelerar el renderizado
  3. ¿Qué compromiso aceptas al adoptar un enfoque utility-first?
    • Un archivo CSS que crece en proporción al número de páginas
    • Una especificidad alta y difícil de sobrescribir
    • HTML verboso cuya deduplicación depende de una capa de componentes