UD5 · Arquitectura CSS (BEM) y Utility-First con Tailwind CSS
Presentación de clase · 46 diapositivas para seguir la unidad
Resultado de aprendizaje y criterios cubiertos
Esta unidad cierra RA2 — Crea interfaces Web homogéneas definiendo y aplicando estilos.
| Criterio | Enunciado | Cobertura |
|---|---|---|
| RA2.g | Se han creado clases de estilos. | ✅ Completa (BEM y clases de utilidad) |
| RA2.h | Se han utilizado herramientas de validación de hojas de estilos. | ✅ Completa (W3C, Stylelint, Prettier) |
| RA2.i | Se ha utilizado y actualizado la guía de estilo. | ✅ Completa |
Cierre del Resultado de Aprendizaje 2
Con esta unidad RA2 queda completado. En la UD6 empieza el RA3: recursos multimedia y rendimiento.
1. Dónde estamos: AURIA ya tiene piel, hoy ordenamos el armario
flowchart LR
A["<b>HTML + SASS</b><br/>UD3<br/><i>Estructura y carpetas</i>"] --> B["<b>CSS3</b><br/>UD4<br/><i>Cascada, layout, responsive</i>"]
B --> C["<b>Arquitectura</b><br/>UD5 · HOY<br/><i>Nombres, utilidades, validación</i>"]
C --> D["<b>Multimedia</b><br/>UD6<br/><i>Imágenes y rendimiento</i>"]
style C fill:#FBBC04,stroke:#202124,color:#202124
En la UD4 la página de AURIA ganó colores, layout y responsive. En esta unidad casi no cambia cómo se ve: cambia cómo está escrito el CSS, para que se pueda mantener cuando crece y cuando lo toca más de una persona.
La pregunta de la unidad
¿Cómo escribimos CSS que otra persona pueda tocar sin miedo dentro de seis meses? La contestamos en cuatro bloques:
- El problema: qué pasa cuando no hay convención (sección 2).
- BEM: nombres que explican la estructura del componente (sección 3).
- Tailwind CSS: componer el aspecto con clases de utilidad (sección 4).
- Validar y documentar: W3C, Stylelint, Prettier y la guía de estilo (secciones 5 y 6).
1.1 Por qué importa en el mundo real
El CSS de un proyecto real no deja de crecer: unas decenas de líneas el primer mes, miles al segundo año, y tocadas por muchas personas distintas.
- Varias personas, un mismo CSS. Lo que escribes hoy lo modificará otra persona mañana.
- Se lee más de lo que se escribe. Un nombre claro ahorra horas de buscar "qué hace esta clase".
- Miedo a borrar. Sin convención nadie sabe si una regla se usa, así que nadie borra nada: el CSS solo crece.
- Revisión automática. En las empresas el CSS pasa un linter antes de entrar al proyecto.
- Diseño y código sincronizados. La guía de estilo es el contrato entre diseño y desarrollo.
Dato clave
El problema del CSS no es escribirlo: es mantenerlo cuando crece y cuando lo toca más de una persona.
2. El problema: CSS que nadie se atreve a tocar
2.1 Tres síntomas de un CSS sin convención
Este es el aspecto típico de un styles.css con dos años de vida:
.card { … }
.card2 { … }
.btn-2 { … }
.rojo { color: red; }
.nuevo-btn-ok { … }
#main .card .btn:hover { … }
div.productos ul li a span { … }
.card .title { … }
.modal .title { … } /* ¿cuál gana? */
| Síntoma | Ejemplo | Qué provoca |
|---|---|---|
| Nombres sin significado | .card2, .btn-2, .rojo |
Nadie sabe qué son ni dónde se usan. ¿Y si .rojo tiene que pasar a ser azul? |
| Especificidad que sube | #main .card .btn:hover |
Solo se pueden vencer con un selector aún más largo… o con !important |
| Estilos que se pisan | .card .title y .modal .title |
El aspecto depende de dónde está el HTML: lo mueves y cambia |
Resultado
Nadie se atreve a borrar nada: se añade otra regla "por si acaso" y el fichero solo crece. El CSS que ya no se usa pero sigue ahí se llama código muerto.
2.2 Recuerda la UD4: la especificidad es una escalada
| Selector | Especificidad |
|---|---|
.title |
(0,1,0) |
.card .title |
(0,2,0) |
#main .card .title |
(1,2,0) |
#main .card .title { … !important } |
fuera de la escala |
Cada vez que alguien "gana" subiendo el peso, el siguiente tiene que subirlo más. Es una escalada que nadie gana.
La idea de toda la unidad
Si todos los selectores pesan lo mismo, (0,1,0), nadie tiene que pelear: si dos reglas chocan, decide el orden. Y lo que evita que choquen es el nombre.
2.3 Dos respuestas al mismo problema
flowchart TD
A["<b>CSS sin convención</b><br/>difícil de mantener"] --> B["<b>BEM</b><br/>Semántica y estructura<br/><code>.card__title</code>"]
A --> C["<b>Utility-first (Tailwind)</b><br/>Composición atómica<br/><code>text-xl font-semibold</code>"]
style B fill:#FBBC04,stroke:#202124,color:#202124
style C fill:#38BDF8,stroke:#202124,color:#202124
- BEM: nombras cada pieza según lo que es dentro de su componente y escribes su CSS una vez.
- Utility-first: cada clase hace una sola cosa y el aspecto se monta combinándolas en el HTML.
No son rivales que haya que elegir para siempre: son dos respuestas al mismo problema. Existen otras arquitecturas (OOCSS, SMACSS, ITCSS), pero BEM y Tailwind son las más extendidas.
3. BEM: Bloque, Elemento, Modificador (RA2.g)
3.1 Las tres piezas
| Pieza | Qué es | Ejemplo en AURIA |
|---|---|---|
| Bloque | Componente independiente con sentido propio. Se puede llevar a otra página y sigue teniendo sentido. | .card |
| Elemento | Parte del bloque, sin sentido fuera de él. | .card__image, .card__title, .card__price, .card__button |
| Modificador | Variante de aspecto o de estado del bloque o de un elemento. | .card--destacada, .card__button--primary |
BEM lo creó Yandex (el buscador ruso) hacia 2009, para que equipos grandes pudieran compartir el mismo CSS sin pisarse.
3.2 La sintaxis: dos guiones bajos y dos guiones
card__button--primary
│ │ │
│ │ └── --primary → modificador
│ └── __button → elemento
└── card → bloque
Se lee: "el botón de la tarjeta, en su variante principal".
| Patrón | Ejemplo | Significado |
|---|---|---|
.bloque |
.card |
El componente |
.bloque__elemento |
.card__title |
Una parte del componente |
.bloque--modificador |
.card--destacada |
Variante del componente entero |
.bloque__elemento--modificador |
.card__button--primary |
Variante de una parte |
Nombres compuestos
Todo en minúsculas y con guion simple dentro de cada palabra compuesta: .product-card__add-button--disabled. Los separadores son dobles precisamente para no confundirse con ese guion simple.
3.3 La tarjeta de AURIA con nombres BEM
<article class="card card--destacada">
<img class="card__image" src="auria-pro.webp"
alt="Auriculares AURIA Pro">
<h3 class="card__title">AURIA Pro</h3>
<p class="card__price">199 €</p>
<button class="card__button card__button--primary">
Añadir al carrito
</button>
</article>
- Modificador + base:
class="card card--destacada". El modificador siempre acompaña a la clase base: la base trae lo común y el modificador solo lo que cambia. - Se lee en el HTML: sin abrir el CSS ya sabes qué es cada pieza y a qué componente pertenece.
- El HTML semántico sigue:
article,h3ybuttonson las etiquetas de la UD3. BEM ordena los nombres de las clases; no cambia qué etiqueta usas.
3.4 BEM y SASS encajan: el & de la UD3
El & de SASS se sustituye por el selector en el que estás, pegado. Con BEM, todo el componente queda en un fichero:
.card {
display: flex;
flex-direction: column;
gap: $space-md;
&--destacada {
border: 2px solid $color-brand-primary;
}
&__title { font-size: 1.25rem; }
&__button--primary {
background: $color-brand-primary;
}
}
.card { display: flex; flex-direction: column; gap: 16px; }
.card--destacada { border: 2px solid #1E88E5; }
.card__title { font-size: 1.25rem; }
.card__button--primary { background: #1E88E5; }
El & tiene que ir pegado
&__title genera .card__title: una clase, (0,1,0). Si escribes .title dentro de .card (sin &), SASS genera .card .title: dos clases, (0,2,0), y vuelves al problema. Y si dejas un espacio (& __title), sale .card __title, que no funciona.
¿Y el anidamiento del CSS nativo?
El CSS nativo ya admite anidamiento, pero su & no pega texto: &__title no funciona en CSS puro. Para escribir BEM con &, SASS.
3.5 Especificidad plana: todas las reglas pesan (0,1,0)
| Sin convención | Especificidad | Con BEM | Especificidad |
|---|---|---|---|
#main .card .title |
(1,2,0) | .card__title |
(0,1,0) |
.modal div .title |
(0,2,1) | .modal__title |
(0,1,0) |
.card .title |
(0,2,0) | .card__button |
(0,1,0) |
.title |
(0,1,0) | .card--destacada |
(0,1,0) |
Con BEM todas las reglas pesan igual: si dos chocan, decide el orden. Y como cada componente tiene sus propios nombres, casi nunca chocan.
La alternativa al !important de la UD4
No se gana subiendo el peso: se consigue que nadie tenga que ganar.
3.6 Cuatro reglas que evitan el 90 % de los errores
| Regla | ❌ Incorrecto | ✅ Correcto | Por qué |
|---|---|---|---|
| Sin nietos en el nombre | .card__body__title |
.card__title |
El nombre refleja el bloque, no el árbol del HTML |
| El modificador no va solo | class="card--destacada" |
class="card card--destacada" |
El modificador solo trae lo que cambia |
| Sin selectores de etiqueta | .card h3 { … } |
.card__title { … } |
Si cambias h3 por h2, el estilo no se pierde |
| Bloque dentro de bloque | .card .btn { … } |
class="card__button btn btn--primary" |
Mezcla (mix): el botón es pieza de la tarjeta y un bloque propio |
En una mezcla, la clase del elemento (card__button) aporta la colocación dentro del bloque (márgenes, posición) y la del otro bloque (btn) aporta el aspecto.
3.7 ¿Bloque o elemento? Una pregunta lo decide
flowchart LR
Q1{"Si lo recorto y lo pego<br/>en otra parte de la web,<br/>¿tiene sentido solo?"} -->|Sí| B["<b>Bloque</b><br/>.btn · .search · .card"]
Q1 -->|No| Q2{"¿Es una parte que solo<br/>existe dentro de ese bloque?"}
Q2 -->|Sí| E["<b>Elemento</b><br/>.card__price · .search__input"]
style B fill:#FBBC04,stroke:#202124,color:#202124
style E fill:#1E88E5,stroke:#202124,color:#ffffff
Ejemplos de AURIA:
- Botón "Añadir": bloque
.btn(hay botones iguales en toda la web). - Precio: elemento
.card__price. - Valoración con estrellas: bloque
.rating(aparece también en las reseñas). - Etiqueta "Oferta": elemento
.card__badge, o bloque.badgesi se reutiliza.
No hay una respuesta única
Lo importante es que el equipo lo decida y lo escriba en la guía de estilo (sección 6).
3.8 Estados: modificador, pseudoclase o atributo ARIA
| Tipo | Para qué | Ejemplos |
|---|---|---|
| Pseudoclases | Estados que el navegador ya conoce | &:hover, &:focus-visible, &:disabled |
| Modificadores | Variantes que decides tú | .card--destacada, .btn--secondary, .alert--error |
| Atributos ARIA | Estados que también debe oír el lector de pantalla (UD9) | [aria-expanded="true"], [aria-current="page"], [aria-invalid="true"] |
.menu {
&__list { display: none; }
// El estado lo marca el atributo ARIA del botón: CSS y lector de pantalla leen el mismo dato
&__toggle[aria-expanded="true"] + &__list { display: block; }
}
Regla
Si el estado ya existe en HTML o en ARIA, úsalo para el CSS en vez de inventar una clase. Así el aspecto y lo que oye el lector de pantalla no se desincronizan.
3.9 Ventajas y límites de BEM
| ✅ Ventajas | ⚠️ Límites |
|---|---|
| Nombres que se explican solos en el HTML | Nombres largos: .card__button--primary |
| Especificidad plana y predecible | Exige disciplina: un solo .card .title y se rompe |
| Un componente = un fichero SASS | Hay que decidir qué es bloque y qué es elemento |
| Se puede borrar un bloque entero sin miedo | No guarda los valores: necesita los tokens de la UD2 |
| Encaja con los componentes de la UD7 (Atomic Design) |
Clave
Los nombres largos son el precio de la claridad: se escriben una vez y se leen cien.
4. Utility-first con Tailwind CSS (RA2.g)
4.1 Cambio de paradigma: del nombre a la composición
| BEM | Utility-first |
|---|---|
1. Pones un nombre: .card__button |
1. No escribes CSS nuevo |
2. Escribes su CSS en _card.scss |
2. Combinas clases ya hechas: bg-blue-600 text-white px-4 py-2 |
| 3. Lo usas en el HTML | 3. El aspecto se lee en el HTML |
| HTML limpio · la lógica visual está en el CSS | CSS mínimo · la lógica visual está en el HTML |
Una clase de utilidad es, básicamente, una declaración CSS con nombre corto: flex → display: flex; p-4 → padding: 1rem.
Tailwind CSS nació en 2017 (Adam Wathan). La versión actual, la 4 (2025), se configura desde el propio CSS.
4.2 La misma tarjeta de AURIA, en Tailwind
<article class="flex flex-col gap-2 rounded-lg
border-2 border-brand-primary p-4">
<img class="rounded-md" src="auria-pro.webp"
alt="Auriculares AURIA Pro">
<h3 class="text-xl font-semibold">AURIA Pro</h3>
<p class="text-gray-600">199 €</p>
<button class="rounded-md bg-brand-primary px-4 py-2
text-white hover:bg-blue-700
focus-visible:outline-2">
Añadir al carrito
</button>
</article>
Mismo resultado visual que la versión BEM, y ni una línea de CSS propio: todo el aspecto está en el atributo class. Si borras la tarjeta del HTML, su "CSS" desaparece con ella: no queda código muerto.
¿No es lo mismo que el style=\"\" en línea?
Se parece, pero hay tres diferencias importantes: las utilidades usan la escala de tokens (no cualquier valor), admiten estados y responsive (hover:, md:), que el atributo style no puede, y pesan lo que una clase, no la prioridad máxima del estilo en línea.
4.3 Las clases son CSS con nombre corto
| CSS | Tailwind |
|---|---|
display: flex |
flex |
flex-direction: column |
flex-col |
justify-content: space-between |
justify-between |
gap: 0.5rem (8 px) |
gap-2 |
padding: 1rem (16 px) |
p-4 |
padding-inline: 1rem |
px-4 |
border-radius: 0.5rem |
rounded-lg |
font-weight: 600 |
font-semibold |
display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) |
grid grid-cols-3 |
La escala de espaciado: cada número son 0,25 rem (4 px). p-1 = 4 px, p-2 = 8 px, p-4 = 16 px, p-6 = 24 px, p-8 = 32 px. Igual que los tokens de espaciado de la UD2, te mueves por una escala en lugar de inventar valores. La x y la y indican el eje: px-4 (izquierda y derecha), py-2 (arriba y abajo).
No hay que memorizarlas
La extensión Tailwind CSS IntelliSense de VS Code autocompleta las clases y, al pasar el ratón por encima, enseña el CSS que genera cada una.
4.4 Variantes: estados y responsive con un prefijo
| Prefijo | Ejemplo | Equivale a |
|---|---|---|
hover: |
hover:bg-blue-700 |
&:hover de SASS |
focus-visible: |
focus-visible:outline-2 |
&:focus-visible |
md: |
md:flex-row |
@media (min-width: 48rem) |
lg: |
lg:grid-cols-3 |
@media (min-width: 64rem) |
dark: |
dark:bg-gray-900 |
@media (prefers-color-scheme: dark) |
Mobile-first, como en la UD4:
<div class="flex flex-col md:flex-row">…</div>
- Sin prefijo se aplica siempre (la base, el móvil).
md:añade a partir de 768 px.- Se combinan:
md:hover:bg-blue-700.
| Breakpoint | sm |
md |
lg |
xl |
2xl |
|---|---|---|---|---|---|
| Desde | 640 px | 768 px | 1024 px | 1280 px | 1536 px |
md: no significa «solo en tablet»
Significa «a partir de md». Es exactamente el min-width de la UD4. Para algo que solo se vea en móvil, se pone en la base y se deshace en el breakpoint: block md:hidden.
4.5 Instalar Tailwind CSS v4 con su CLI
Igual que Dart Sass en la UD3: con npm, dentro del proyecto.
# 1. Instalar
npm install tailwindcss @tailwindcss/cli
/* 2. Importar: src/input.css */
@import "tailwindcss";
# 3. Compilar en modo vigilancia
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch
<!-- 4. Enlazar el CSS generado -->
<link rel="stylesheet" href="output.css">
Tailwind lee tu HTML, busca las clases que usas y genera output.css.
Para prototipos
Existe un <script> que compila en el navegador sin instalar nada (@tailwindcss/browser). Sirve para probar algo en cinco minutos, no para producción.
Tailwind y SASS, por separado
Tailwind v4 ya hace el papel del preprocesador (variables, anidamiento, importaciones) y su documentación no recomienda combinarlo con SASS. Por eso en el ejercicio la versión Tailwind va en una carpeta aparte.
4.6 Los tokens de la UD2 entran por @theme
/* src/input.css */
@import "tailwindcss";
@theme {
--color-brand-primary: #1e88e5;
--color-brand-dark: #1565c0;
--spacing-md: 1rem;
--font-display: "Inter", sans-serif;
}
Variable en @theme |
Clases que crea |
|---|---|
--color-brand-primary |
bg-brand-primary · text-brand-primary · border-brand-primary |
--spacing-md |
p-md · m-md · gap-md |
--font-display |
font-display |
El prefijo de la variable (--color-, --spacing-, --font-, --text-, --radius-, --breakpoint-) decide qué clases se crean.
Tailwind no sustituye a los tokens: los implementa
El nombre semántico de la UD2 (color-brand-primary) pasa a ser una clase. Si mañana cambia el azul de AURIA, se cambia en un solo sitio. Además, queda disponible como variable CSS normal: var(--color-brand-primary).
Si ves tailwind.config.js en un tutorial
Es Tailwind 3: la configuración se hacía en un fichero JavaScript. La idea es la misma; en la v4 se escribe en el CSS con @theme.
4.7 Solo genera las clases que usas
flowchart LR
A["<b>Tus ficheros</b><br/>index.html, *.js…"] --> B["<b>Tailwind los lee</b><br/>busca nombres de clases"] --> C["<b>output.css</b><br/>solo lo que aparece"]
style C fill:#FBBC04,stroke:#202124,color:#202124
Tailwind tiene miles de clases posibles, pero el CSS final solo trae las que encuentra en tu código: suele quedar en pocos KB.
La trampa: Tailwind busca texto, no ejecuta tu código
Una clase montada a trozos no existe para Tailwind:
// ❌ Tailwind nunca ve "bg-red-600" escrito entero
const clase = `bg-${color}-600`;
// ✅ Nombres completos
const clase = color === "rojo" ? "bg-red-600" : "bg-blue-600";
Si una clase "no hace nada", lo primero: ¿aparece escrita entera en algún fichero que Tailwind lee? (Y después: ¿está corriendo el --watch? ¿Enlazas output.css y no input.css?)
4.8 Salidas de emergencia: valores arbitrarios y @apply
<div class="w-[327px] top-[13px] bg-[#ff5a1f]">…</div>
Escriben un valor exacto entre corchetes. Útiles para un caso puntual… y peligrosos: son los valores mágicos de la UD2 otra vez. Si lo repites, crea un token en @theme.
.btn {
@apply rounded-md bg-brand-primary px-4 py-2 text-white;
}
Junta varias utilidades en una clase propia. Sirve cuando no tienes componentes y el mismo HTML se repite a mano. Con moderación: si todo es @apply, vuelves a escribir CSS de siempre con otra sintaxis.
Clave
Si en tu HTML hay muchos corchetes, no estás usando el sistema de diseño: te lo estás saltando.
4.9 ¿BEM o Tailwind? Depende del proyecto
| BEM + SASS | Tailwind CSS | |
|---|---|---|
| Dónde está el aspecto | En el .scss |
En el HTML |
| HTML | Limpio, nombres con significado | Largo, muchas clases |
| Velocidad para maquetar | Media | Alta |
| Tamaño del CSS | Crece con cada componente | Pequeño y estable |
| Tokens | Variables SASS / custom properties | @theme |
| Encaja mejor con | Webs con HTML escrito a mano, CMS | Proyectos con componentes (React, Vue…) |
Y se pueden mezclar. Muchos equipos usan BEM (o componentes) para la estructura y unas pocas utilidades para ajustes puntuales: márgenes entre piezas, ocultar algo en móvil… Lo que no funciona es no decidir: medio proyecto con cada sistema y sin norma.
Clave
No hay un ganador: hay una decisión de equipo, escrita en la guía de estilo y vigilada por las herramientas.
5. Validar: herramientas que revisan el CSS por ti (RA2.h)
5.1 Tres herramientas, tres preguntas distintas
| Herramienta | Pregunta que contesta | Qué detecta | Cuándo |
|---|---|---|---|
| Validador W3C | ¿Es CSS válido según el estándar? | Propiedades que no existen, valores mal escritos, llaves sin cerrar | A mano: jigsaw.w3.org/css-validator |
| Stylelint | ¿Cumple las normas del equipo? | Nombres que no son BEM, colores repetidos, selectores duplicados o demasiado específicos | En el editor y antes de cada commit |
| Prettier | ¿Está bien formateado? | Sangría, espacios, comillas, una declaración por línea (lo corrige solo) | Automático al guardar |
El validador W3C entiende CSS, no SCSS
Si le pegas un .scss con $variables y &, se llena de errores. Se le pasa el css/main.css compilado. Stylelint, en cambio, sí sabe leer el .scss. Los avisos (warnings) del W3C no son errores: a veces el validador va por detrás de las novedades de CSS.
5.2 Stylelint: instalar y enseñarle BEM
npm install --save-dev stylelint stylelint-config-standard-scss
Fichero .stylelintrc.json en la raíz del proyecto:
{
"extends": "stylelint-config-standard-scss",
"rules": {
"selector-class-pattern": [
"^[a-z][a-z0-9-]*(__[a-z0-9-]+)?(--[a-z0-9-]+)?$",
{
"resolveNestedSelectors": true,
"message": "Usa BEM: bloque__elemento--mod"
}
]
}
}
| Parte | Qué hace |
|---|---|
extends |
Parte de un conjunto de reglas ya hecho: el estándar, en su versión para SCSS |
selector-class-pattern |
Expresión regular que deben cumplir todos los nombres de clase |
| La regex, en castellano | Minúsculas (con guiones), y opcionalmente un __elemento y un --modificador. Los "nietos" (card__body__title) y las mayúsculas (cardTitle) no pasan |
resolveNestedSelectors |
Revisa también los nombres que se forman con &__ y &-- en SASS. Sin esta opción, un &_title mal escrito se escaparía |
message |
El mensaje que se muestra cuando falla |
Para pasarlo a todos los ficheros SCSS:
npx stylelint "scss/**/*.scss"
Clave
La convención ya no depende de que alguien se acuerde: la máquina la revisa en cada fichero.
5.3 Leer lo que dice Stylelint
scss/components/_card.scss
12:3 ✖ Expected ".cardTitle" to match pattern selector-class-pattern
18:5 ✖ Unexpected duplicate selector ".card__price" no-duplicate-selectors
24:14 ✖ Expected "#ffffff" to be "#fff" color-hex-length
✖ 3 problems (3 errors, 0 warnings)
| Parte | Significado |
|---|---|
12:3 |
Línea y columna: dónde está el problema (en VS Code, Ctrl+G) |
✖ Expected… |
Qué ha encontrado, en inglés |
selector-class-pattern |
El nombre de la regla: búscalo en stylelint.io para saber por qué existe |
npx stylelint "scss/**/*.scss" --fix
--fix arregla lo automático (por ejemplo, #ffffff → #fff). Los nombres los cambias tú, porque también hay que tocar el HTML.
Truco
Con la extensión Stylelint de VS Code, los errores aparecen subrayados mientras escribes, como las faltas de ortografía.
5.4 El circuito: que la máquina revise antes que las personas
flowchart LR
A["<b>Escribes</b><br/>en VS Code"] --> B["<b>Prettier</b><br/>formatea al guardar"] --> C["<b>Stylelint</b><br/>subraya en el editor"] --> D["<b>Commit</b><br/>se revisa antes de guardar en git"] --> E["<b>CI</b><br/>el servidor lo vuelve a pasar"]
style C fill:#FBBC04,stroke:#202124,color:#202124
En una revisión de código nadie debería perder tiempo discutiendo sangrías o nombres mal escritos. Las herramientas se encargan de lo mecánico y las personas revisan lo que importa: si el componente está bien pensado.
En clase
Nos quedamos en el editor (Prettier + Stylelint). La revisión en el commit (hooks de git) y la integración continua (CI) las veréis en los proyectos.
6. Documentar: la guía de estilo viva (RA2.i)
6.1 De Penpot al código
La guía de estilo que creaste en la UD2 con Penpot no se queda en el diseño: cada parte tiene su sitio en el código.
| Elemento | En el diseño (UD2) | En el código (UD4 – UD5) |
|---|---|---|
| Colores | Tokens de color en Penpot | $color-brand-primary · --color-brand-primary en @theme |
| Tipografía | Escala de estilos de texto | Variables de tamaño · clamp() (UD4) · text-xl |
| Espaciado | Tokens de espaciado | $space-md · --spacing-md |
| Componentes | Componentes de la librería | Bloques BEM (_card.scss) · catálogo en Storybook (UD7) |
| Convenciones | — | BEM, Stylelint y cuándo usar utilidades |
RA2.i pide UTILIZAR y ACTUALIZAR la guía
Si el código cambia y la guía no, la guía miente. Un token que cambia en Penpot y no llega al código (o al revés) es justo la divergencia que hay que evitar. Quien hace el cambio actualiza la guía en ese mismo cambio.
6.2 Qué contiene una guía de estilo de código
| Apartado | Qué recoge |
|---|---|
| Tokens | Colores, tipografía, espaciado y radios, con su nombre en código |
| Componentes | Cada bloque: para qué sirve, sus elementos y sus modificadores |
| Convención de nombres | BEM, prefijos, idioma (inglés o castellano, pero uno) |
| Hacer / no hacer | Ejemplos correctos e incorrectos, como en Material Design o GOV.UK |
| Herramientas | Cómo compilar, qué linter se usa y con qué reglas |
| Cambios | Qué se ha modificado y cuándo, para que nadie se pierda |
Un esqueleto mínimo de README.md para AURIA:
# Guía de estilo · AURIA
## Tokens
| Token | SASS | Tailwind (@theme) | Valor |
| --- | --- | --- | --- |
| Color de marca | $color-brand-primary | --color-brand-primary | #1e88e5 |
| Espaciado medio | $space-md | --spacing-md | 1rem |
## Componentes
### card
Tarjeta de producto. Elementos: __image, __title, __price, __button.
Modificadores: --destacada (borde de marca).
## Convenciones
- Nombres BEM en kebab-case. Stylelint: selector-class-pattern.
- Utilidades solo para ajustes puntuales de espaciado.
## Herramientas
- Compilar: sass --watch scss:css
- Revisar: npx stylelint "scss/**/*.scss"
## Cambios
- 2026-10-28 · Añadido card--destacada.
En la UD7 esta guía se convierte en un catálogo navegable de componentes con Storybook.
7. Caso real: arquitecturas CSS en producción
| Caso | Qué hacen |
|---|---|
| GOV.UK Frontend | El sistema de diseño del gobierno británico usa BEM con prefijo: .govuk-button--secondary |
| Tailwind en componentes | Librerías como shadcn/ui o Tailwind Plus reparten componentes hechos solo con utilidades |
| Linter obligatorio | Los proyectos serios rechazan un cambio si no pasa Stylelint en la integración continua |
| Tokens como fuente única | Los mismos tokens alimentan la herramienta de diseño, el CSS y las apps móviles |
| Guías públicas | Material Design, GOV.UK o Atlassian publican sus guías con ejemplos de hacer / no hacer |
| Borrar sin miedo | Con componentes aislados se puede eliminar código muerto sabiendo que no rompe nada |
Pruébalo
Abre gov.uk, pulsa F12 y mira las clases de cualquier botón o del buscador: es BEM de libro.
8. Errores comunes
8.1 Trampas de BEM
| Error | Qué pasa | ❌ Incorrecto | ✅ Correcto |
|---|---|---|---|
| Nietos en el nombre | Copias el árbol del HTML en la clase | .card__body__footer__button |
.card__button |
| Modificador sin base | Se pierden los estilos comunes | <div class="card--destacada"> |
<div class="card card--destacada"> |
| Volver a anidar | La especificidad sube otra vez | .card .card__title { … } |
.card__title { … } |
| Estilar por etiqueta | Cambias h3 por h2 y se rompe |
.card h3 { … } |
.card__title { … } |
& separado en SASS |
Sale un selector descendiente que no funciona | & __title { … } |
&__title { … } |
8.2 Trampas de Tailwind y herramientas
| Error | Qué pasa | ❌ Incorrecto | ✅ Correcto |
|---|---|---|---|
| Clases montadas a trozos | Tailwind no las encuentra y no las genera | `text-${tono}-600` |
tono === "ok" ? "text-green-600" : … |
| Corchetes por todas partes | Valores mágicos otra vez | p-[13px] text-[#1e88e5] |
p-md text-brand-primary (@theme) |
Token sin prefijo en @theme |
No se crean las clases | --brand-primary: #1e88e5; |
--color-brand-primary: #1e88e5; |
Validar el .scss en el W3C |
El validador no entiende SASS | Pegar _card.scss en el validador |
Validar css/main.css (el compilado) |
| Desactivar la regla que molesta | El linter deja de proteger | "selector-class-pattern": null |
Renombrar la clase según BEM |
9. Ejercicio práctico guiado: la tarjeta de AURIA, con dos arquitecturas
Actividad de consolidación (no evaluable)
Objetivo: aplicar las dos arquitecturas a la tarjeta de producto de la UD4. La versión BEM se hace en tu proyecto (scss/); la versión Tailwind, en una carpeta aparte (auria-tailwind/) para comparar las dos sin mezclarlas.
Pasos, en este orden:
- Refactor a BEM: renombra la tarjeta con
card,card__image,card__title,card__priceycard__button(y un modificadorcard--destacada). Cambia el HTML y el SCSS. - BEM en SASS: todo el CSS de la tarjeta en
components/_card.scss, con&__y&--. Comprueba encss/main.cssque todas las reglas son de una sola clase, (0,1,0). - Versión Tailwind: en
auria-tailwind/, instala Tailwind v4 con su CLI y rehaz la misma tarjeta solo con utilidades. - Tokens en
@theme: al menos 4 colores y 3 espaciados de tu guía de la UD2, y úsalos en la tarjeta (bg-brand-primary,p-md…). - Stylelint: en el proyecto BEM, con la configuración de la sección 5.2, hasta 0 errores.
- Guía de estilo: un
README.mdcon los tokens, el bloquecarddocumentado (elementos y modificadores) y cuándo usarías BEM y cuándo Tailwind.
Criterio de éxito
Las dos tarjetas se ven igual, Stylelint no da errores y cambiar el azul de marca es tocar una sola línea en cada versión (_tokens.scss y @theme).
10. Comprueba lo aprendido
1. ¿Qué es bloque, qué es elemento y qué es modificador en .card__button--primary?
Bloque card (la tarjeta), elemento button (el botón de la tarjeta) y modificador primary (su variante principal).
2. ¿Por qué con BEM no hace falta !important? Explícalo con la especificidad de la UD4.
Porque todos los selectores son de una sola clase, (0,1,0): ninguna regla pesa más que otra y no hay que "ganar" a nadie. Además, como cada componente tiene sus propios nombres, casi nunca chocan; si chocan, decide el orden.
3. ¿Qué diferencia hay entre escribir &__title dentro de .card en SASS y escribir .card .title?
&__title genera .card__title: una clase, (0,1,0), que no depende de dónde esté el elemento. .card .title es un selector descendiente de dos clases, (0,2,0), que solo funciona si .title está dentro de .card.
4. En Tailwind, ¿qué significa md:flex-row y a qué media query de la UD4 equivale?
flex-direction: row a partir del breakpoint md (768 px). Equivale a @media (min-width: 48rem) { … }. No significa "solo en tablet".
5. ¿Qué revisa el validador W3C, qué revisa Stylelint y qué hace Prettier?
El W3C comprueba que el CSS cumple el estándar. Stylelint comprueba que cumple las normas del equipo (nombres BEM, duplicados, colores…). Prettier no comprueba: reescribe el código con un formato uniforme.
Mini-reto (20 min, en parejas): detectives de arquitecturas
- Detectives: encontrad 3 webs: una con BEM, una con Tailwind y una sin convención.
- BEM: renombrad en BEM un componente de la que no tiene convención.
- Tailwind: rehaced ese componente solo con utilidades.
- Stylelint: pasad vuestro BEM por Stylelint: 0 errores.
- Veredicto: ¿cuál elegiríais para ese proyecto y por qué?
Pistas para reconocerlas (F12 → Elements → atributos class): dobles guiones bajos y dobles guiones → BEM; flex px-4 md:… → Tailwind; clases tipo .box2 o ids con estilo → sin convención.
Éxito: la otra pareja entiende vuestro componente BEM sin abrir el CSS y vuestra elección está justificada.
11. Glosario
Arquitectura CSS y BEM
| Término | Definición |
|---|---|
| Arquitectura CSS | Normas para organizar y nombrar el CSS de un proyecto. |
| Convención | Acuerdo de equipo sobre cómo se escribe el código. |
| BEM | Block, Element, Modifier: convención de nombres de clases. |
| Bloque | Componente independiente con sentido propio: .card. |
| Elemento | Parte de un bloque, sin sentido fuera de él: .card__title. |
| Modificador | Variante de un bloque o elemento: .card--destacada. |
| Mezcla (mix) | Un nodo con clases de dos bloques: card__button btn. |
| Especificidad plana | Todas las reglas con una sola clase: (0,1,0). |
& |
En SASS, el selector actual: &__title → .card__title. |
| OOCSS · SMACSS · ITCSS | Otras arquitecturas CSS conocidas. |
| Código muerto | CSS que ya no se usa y nadie se atreve a borrar. |
| Guía de estilo | Documento vivo con tokens, componentes y normas. |
Tailwind, validación y formato
| Término | Definición |
|---|---|
| Utility-first | Componer el aspecto con clases de una sola función. |
| Clase de utilidad | Clase que aplica una sola declaración: p-4. |
| Tailwind CSS | Framework utility-first; la v4 se configura desde el CSS. |
| Variante | Prefijo que condiciona una utilidad: hover:, md:, dark:. |
@theme |
Bloque de Tailwind v4 donde se definen los tokens. |
| Valor arbitrario | Valor exacto entre corchetes: w-[327px]. |
@apply |
Junta utilidades dentro de una clase propia. |
| Linter | Herramienta que revisa reglas y errores del código. |
| Stylelint | Linter de CSS y SCSS configurable por reglas. |
| Prettier | Formateador automático de código. |
| Validador W3C | Comprueba que el CSS cumple el estándar. |
| CI | Integración continua: revisión automática en el servidor. |
12. Resumen y conexión con la siguiente unidad
Dentro de seis meses, ¿quién va a leer tu CSS? Probablemente tú, y no te vas a acordar de por qué pusiste .btn-2. Escribe el CSS para esa persona:
- Nombres: BEM o utilidades, que cada clase diga lo que es.
- Tokens: los valores salen de la guía de estilo, no de la cabeza.
- Herramientas: Stylelint y Prettier revisan lo mecánico.
Igual que con los tokens de la UD2: el ahorro no se ve el día que lo escribes, se ve el día que hay que cambiarlo.
- Sin convención, el CSS crece, la especificidad sube y nadie se atreve a borrar.
- BEM:
.bloque__elemento--modificador, todo con una sola clase (0,1,0). - En SASS,
&__y&--generan nombres BEM sin anidar selectores. - Tailwind compone con utilidades; los tokens entran por
@theme. - W3C valida el estándar, Stylelint las normas del equipo, Prettier el formato.
- La guía de estilo se usa y se actualiza: si el código cambia y ella no, miente.
Con la UD5 se cierra el RA2. En la UD6 empieza el RA3: licencias y derechos de autor, formatos WebP, AVIF y SVG, imágenes adaptables con picture, srcset y sizes, y audio y vídeo que pesen poco y carguen rápido.
flowchart LR
UD5["UD5<br/>BEM +<br/>Tailwind CSS"] --> UD6["UD6<br/>Multimedia<br/>WebP · AVIF · SVG"]
13. Referencias y fuentes
- BEM — Metodología oficial (en.bem.info) y getbem.com.
- Tailwind CSS — Documentación oficial, Instalación con la CLI y Variables de tema (
@theme). - Stylelint — Guía de uso y reglas y
selector-class-pattern. - Prettier — prettier.io.
- W3C — Servicio de validación CSS.
- GOV.UK Design System — design-system.service.gov.uk.
- MDN Web Docs — Organizar el CSS.
- Ruiz, M. — Diseño de Interfaces Web.