Saltar a contenido

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 pongas outline: none a 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().