Arquitectura CSS
CSS sin estructura funciona hasta cierto punto. Luego empiezan los problemas: estilos que se pisan sin saber por qué, !important por todas partes, miedo a tocar cualquier regla porque algo puede romperse. La arquitectura CSS no es burocracia — es la diferencia entre código que escala y código que se convierte en deuda técnica.
Entender la cascada y la especificidad
La cascada es el algoritmo que decide qué estilo gana cuando hay conflicto. En orden de prioridad:
- Importancia —
!importantsiempre gana (y siempre es una señal de alarma) - Origen — estilos del autor > estilos del navegador
- Especificidad — cuánto de específico es el selector
- Orden — si todo lo demás es igual, gana el que aparece último
La especificidad se calcula con tres contadores: (IDs, clases/atributos/pseudoclases, elementos/pseudoelementos):
p /* 0,0,1 */
.card /* 0,1,0 */
#header /* 1,0,0 */
.card p /* 0,1,1 */
.card .title /* 0,2,0 */
#header .nav a:hover /* 1,1,2 */
El problema de la especificidad alta es que para sobreescribirla necesitas especificidad aún más alta, y así escalas hasta !important:
/* ❌ Espiral de especificidad */
#sidebar .widget .widget-title { color: blue; }
#sidebar .widget .widget-title.active { color: red; }
#sidebar .widget .widget-title.active:hover { color: green !important; }
La solución: mantener la especificidad baja desde el principio.
La regla del selector plano
El CSS más mantenible usa selectores de especificidad baja y predecible. El objetivo es que la mayoría de reglas sean de clase única:
/* ❌ Selector anidado profundo — especificidad alta, difícil de sobreescribir */
.sidebar .menu .menu-item .menu-link:hover span { color: red; }
/* ✅ Clase directa — especificidad mínima, fácil de razonar */
.menu-link:hover { color: red; }
Cuanto más anidas, más acoplado está el CSS a la estructura HTML. Si mueves el elemento a otro contenedor, el estilo deja de funcionar.
BEM — Block Element Modifier
BEM es una convención de nomenclatura que hace la relación entre HTML y CSS explícita en el nombre de las clases:
.bloque {} /* componente independiente */
.bloque__elemento {} /* parte interna del bloque (doble guión bajo) */
.bloque--modificador {} /* variación del bloque (doble guión) */
.bloque__elemento--mod {} /* variación de un elemento */
/* Componente: .card */
.card {}
.card__image {} /* imagen dentro de la card */
.card__title {} /* título dentro de la card */
.card__body {} /* cuerpo dentro de la card */
.card__footer {} /* footer dentro de la card */
/* Modificadores del bloque */
.card--featured {} /* card destacada */
.card--horizontal {} /* card en layout horizontal */
/* Modificadores de elemento */
.card__title--large {}
<article class="card card--featured">
<img class="card__image" src="..." alt="">
<div class="card__body">
<h2 class="card__title card__title--large">Título</h2>
</div>
<footer class="card__footer">...</footer>
</article>
Ventajas de BEM:
- Especificidad siempre 0,1,0 — predecible y fácil de sobreescribir
- Sabes qué hace cada clase con solo leer el nombre
- No importa dónde esté el HTML — el estilo no depende de la estructura
No es necesario ser estricto: BEM es una guía, no una religión. Lo importante es la coherencia dentro del proyecto.
Estructura de archivos CSS
Para proyectos medianos y grandes, separar los estilos en archivos por responsabilidad hace el código mucho más navegable:
styles/
tokens/
_colors.css /* paleta de colores */
_typography.css /* escala tipográfica */
_spacing.css /* escala de espaciado */
_shadows.css /* sombras */
base/
_reset.css /* reset / normalize */
_body.css /* estilos del documento */
_typography.css /* estilos de text elements: p, h1-h6, a, ul... */
layout/
_container.css /* contenedor principal */
_grid.css /* sistema de grid */
_header.css
_footer.css
_sidebar.css
components/
_button.css
_card.css
_modal.css
_form.css
_badge.css
utilities/
_display.css /* .hidden, .block, .flex... */
_spacing.css /* .mt-4, .px-2... */
_text.css /* .text-center, .text-muted... */
global.css /* solo @import de todo lo anterior */
El archivo global.css es el único que importas en el layout. Solo contiene @imports en el orden correcto:
/* global.css */
@import './tokens/_colors.css';
@import './tokens/_typography.css';
@import './tokens/_spacing.css';
@import './base/_reset.css';
@import './base/_body.css';
@import './base/_typography.css';
@import './layout/_container.css';
@import './layout/_header.css';
@import './layout/_footer.css';
@import './components/_button.css';
@import './components/_card.css';
@import './components/_modal.css';
@import './utilities/_display.css';
@import './utilities/_spacing.css';
Utilidades vs componentes
Las clases de utilidad hacen una sola cosa y la hacen siempre igual. Son predecibles porque su nombre describe exactamente su efecto:
.hidden { display: none; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
.text-center { text-align: center; }
.text-muted { color: var(--color-text-muted); }
.mt-auto { margin-top: auto; }
.flex { display: flex; }
.items-center { align-items: center; }
Los componentes encapsulan la lógica visual completa de un patrón repetible:
/* Componente: no sabes qué hace solo por el nombre, pero encapsula complejidad */
.card { ... }
.btn { ... }
.badge { ... }
La regla práctica: para variaciones pequeñas usa utilidades, para patrones completos usa componentes.
Evitar !important
!important resuelve el síntoma pero no el problema. Cuando te encuentres queriendo usarlo, la pregunta correcta es: ¿por qué la especificidad está tan alta aquí?
Las únicas dos situaciones legítimas para !important:
- Overrides de librerías externas donde no controlas el CSS original y no puedes modificarlo
- Clases de estado de alta prioridad que siempre deben ganar:
.is-hidden { display: none !important; }para ocultar algo independientemente del contexto
El CSS que no tocas no se rompe
La arquitectura CSS no es solo cómo escribes el CSS — también es cómo decides cuándo no escribirlo. Antes de añadir una regla nueva, pregúntate:
- ¿Ya existe una clase de utilidad para esto?
- ¿Debería ser una variante del componente existente?
- ¿Estoy sobreescribiendo algo que debería haber sido diferente desde el principio?
El código CSS que no existe no tiene bugs, no tiene conflictos de especificidad y no hay que mantenerlo.
Con esto termina el Curso de CSS Intermedio. Tienes las herramientas para escribir CSS que escala, que se comporta de forma predecible y que cualquier persona del equipo puede entender y mantener.
El siguiente curso, Curso de JavaScript, añade la capa de comportamiento: variables, condicionales, funciones, arrays y objetos. Es el siguiente paso natural cuando ya entiendes estructura y estilo y quieres hacer que la interfaz responda.
Y si quieres una guía rápida de consulta mientras construyes, tienes la Cheat Sheet completa de CSS.