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. Añades al bloque .card una variante destacada, respetando estrictamente la convención BEM. ¿Qué selector escribes en el hueco?
    .card {
      padding: 16px;
      border: 1px solid #e5e7eb;
    }
    
    ___ {
      border: 2px solid var(--brand);
    }
    • .card--featured
    • .card__featured
    • .card-featured
    • .card .featured
  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