WA3.1 Variables, funciones y selectores modernos¶
Cómo se han comprobado los ejemplos
Todos los ejemplos son editores con vista previa que se ejecutan en tu navegador: cámbialos y mira el resultado. Los valores que se citan en el texto (tamaños, número de columnas, qué color gana) se han comprobado en un navegador real. Las funciones de esta unidad funcionan en los navegadores actuales; si tu proyecto debe funcionar en navegadores antiguos, mira Can I use antes de usarlas.
Variables CSS (propiedades personalizadas)¶
Una variable CSS se declara con dos guiones (--acento: #2563eb;) y se usa con var(--acento). A diferencia de las variables de un preprocesador, viven en el navegador: se heredan, se pueden cambiar según el contexto y JavaScript puede modificarlas en directo.
Tres ideas en un ejemplo:
- Un tema entero (claro/oscuro) es solo redefinir las variables bajo otro selector; ninguna regla de las tarjetas cambia.
- La variable se declara en
:root(el<html>) para que todo el documento la herede, pero puedes redefinirla en cualquier elemento y solo afectará a sus descendientes. - JavaScript la cambia con
elemento.style.setProperty("--acento", valor)y el navegador actualiza todo lo que la use.
var() acepta un valor de reserva: var(--acento, hotpink) usa hotpink si la variable no está definida.
Funciones matemáticas: calc(), min(), max() y clamp()¶
| Función | Qué hace | Ejemplo |
|---|---|---|
calc() |
Operaciones, mezclando unidades | width: calc(100% - 2rem) |
min(a, b) |
El menor de los valores | width: min(100%, 40rem) |
max(a, b) |
El mayor | padding: max(1rem, 3vw) |
clamp(mín, ideal, máx) |
ideal, pero nunca menor que mín ni mayor que máx |
font-size: clamp(1.2rem, 1rem + 3vw, 2.5rem) |
clamp() es la base de la tipografía fluida: el tamaño crece con la pantalla sin saltos y sin escribir ninguna consulta de medios. Con clamp(1.2rem, 1rem + 3vw, 2.5rem) (y 1 rem = 16 px), la fórmula 1rem + 3vw da:
| Ancho de la ventana | 1rem + 3vw |
Tamaño final |
|---|---|---|
| 200 px | 16 + 6 = 22 px | 22 px |
| 400 px | 16 + 12 = 28 px | 28 px |
| 1000 px | 16 + 30 = 46 px | 40 px (se corta en el máximo, 2,5 rem) |
Tamaños fluidos en rem, no solo en vw
Si usas solo vw para el texto (font-size: 3vw), un usuario que aumenta el tamaño de letra de su navegador no verá ningún cambio: es un problema de accesibilidad. Mezclar rem con vw (como en 1rem + 3vw) lo respeta.
Selectores modernos¶
:is() y :where(): agrupar sin repetir¶
Los dos hacen lo mismo (aceptan una lista de selectores y coinciden con cualquiera) pero no pesan igual en la cascada:
| Selector | Especificidad que aporta |
|---|---|
:is(.a, #b) |
La del más específico de la lista (aquí, la de #b) |
:where(.a, #b) |
Cero, siempre |
Sin ellos, article h2, section h2, aside h2 { ... } se repite. Con :is: :is(article, section, aside) h2 { ... }. Y :where es ideal para estilos base que se puedan pisar fácilmente:
Las dos reglas dicen lo mismo (rojo), pero no pesan igual. :is(.x, #nada) toma la especificidad del más específico de su lista (el #nada, un id), así que pesa (1,0,1) y gana a .aviso p (0,1,1) aunque ese vaya después: el párrafo sale rojo. :where(...) aporta cero, pesa solo (0,0,1), y pierde contra .aviso p: sale azul. Por eso :where() es ideal para estilos base fáciles de pisar (una librería, un reset): cualquier regla del usuario gana sin pelear.
:has(): el selector «padre»¶
:has() selecciona un elemento según lo que contiene. Es lo que se pedía desde siempre: «estiliza la tarjeta si tiene una imagen», o «el campo si su input es inválido».
Sin :has() habría que escribir JavaScript que añadiera y quitara una clase a la etiqueta. Aquí el CSS reacciona solo a lo que ocurre dentro.
| Selector | Significado |
|---|---|
.tarjeta:has(img) |
Tarjeta que contiene una imagen |
li:has(> a.activo) |
li con un hijo directo a.activo |
h2:has(+ p) |
h2 seguido de un párrafo |
label:has(:focus-visible) |
Etiqueta cuyo control tiene el foco del teclado |
body:has(dialog[open]) |
Toda la página, si hay un diálogo abierto (para bloquear el scroll) |
:focus-visible y :focus-within¶
:focus-visible: el foco solo cuando se nota que es del teclado. Evita el anillo al hacer clic con el ratón, sin quitárselo a quien navega con el teclado (nunca pongasoutline: nonea secas).:focus-within: un contenedor cuando cualquiera de sus hijos tiene el foco (resaltar la fila de un formulario).
Anidamiento nativo¶
El CSS ya permite anidar reglas, como en Sass, sin preprocesador. & representa al selector padre:
No anides sin medida
Cada nivel aumenta la especificidad y ata el CSS a la estructura del HTML. Más de dos o tres niveles suele ser señal de que conviene usar clases más concretas.
Propiedades lógicas: adiós a «izquierda» y «derecha»¶
margin-left y padding-right suponen que el texto va de izquierda a derecha. En árabe o hebreo (de derecha a izquierda) o en escritura vertical, «inicio» y «final» son los que se mantienen. Las propiedades lógicas usan esos conceptos:
| Física (fija) | Lógica (se adapta) |
|---|---|
margin-left / margin-right |
margin-inline-start / margin-inline-end |
margin-top / margin-bottom |
margin-block-start / margin-block-end |
padding: 0 1rem (horizontal) |
padding-inline: 1rem |
padding: 1rem 0 (vertical) |
padding-block: 1rem |
width / height |
inline-size / block-size |
top: 0; left: 0; right: 0; bottom: 0 |
inset: 0 |
La barra azul está a la izquierda en el primer párrafo y a la derecha en el segundo, con la misma regla.
Errores frecuentes¶
| Error | Cómo evitarlo |
|---|---|
| Valores repetidos por toda la hoja de estilos | Variables CSS en :root |
Tamaño de letra solo en vw |
Mezclar con rem: clamp(1rem, 0.8rem + 2vw, 2rem) |
Creer que :is() y :where() pesan igual |
:where() pesa cero: ideal para estilos base |
| Usar JavaScript para marcar un contenedor según su contenido | :has() |
outline: none para quitar el anillo de foco |
:focus-visible para controlarlo sin quitarlo |
| Anidar CSS en cinco niveles | Máximo dos o tres; clases más concretas |
margin-left/right en interfaces que pueden ser RTL |
Propiedades lógicas |
Para practicar¶
Los ejercicios WA3.1 a WA3.3 de WA3 · Ejercicios practican variables, clamp() y :has().