CSS At-Rules y media queries
Cómo hacer CSS condicional. @media queries para responsive, @supports para feature queries, @container para container queries, @keyframes, @import, @layer
Esta nota cubre el capítulo 21 del libro: las reglas at-rules que permiten hacer CSS condicional. @media queries para diseño responsive, @supports para feature detection, @container para container queries, @keyframes para animaciones, @import, @layer y @property. Esenciales para CSS moderno.
¿Qué son las at-rules?
Las at-rules son directivas especiales que empiezan con @:
@media (max-width: 768px) {
/* reglas para móvil */
}
@import url("base.css");
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@layer reset, base, components;
At-rules principales:
- @media: media queries.
- @import: importar hojas.
- @supports: feature queries.
- @keyframes: definir animaciones.
- @container: container queries.
- @layer: cascade layers.
- @property: custom properties tipadas.
- @charset: encoding.
- @namespace: XML namespaces.
- @font-face: definir fuentes.
- @page: páginas para imprimir.
- @counter-style: estilos de contadores.
@media queries
Las media queries permiten aplicar CSS según el contexto:
/* Sintaxis básica */
@media (max-width: 768px) {
.sidebar {
display: none;
}
}
@media queries:
- Condición + reglas.
- Si la condición es verdadera, las reglas aplican.
- Sintaxis: @media <type> and (<condition>) and ...
Tipos de media
@media screen { /* pantallas */ }
@media print { /* impresoras */ }
@media speech { /* lectores de pantalla */ }
@media all { /* default, todos */ }
Tipos:
- screen: pantallas.
- print: impresión.
- speech: lectores de pantalla.
- all: default.
- En la práctica, solo screen y print se usan.
Media features
/* Ancho del viewport */
@media (max-width: 768px) { }
@media (min-width: 769px) and (max-width: 1024px) { }
/* Ancho y alto */
@media (min-width: 768px) and (max-height: 1024px) { }
/* Orientación */
@media (orientation: portrait) { }
@media (orientation: landscape) { }
/* Relación de aspecto */
@media (aspect-ratio: 16/9) { }
/* Color */
@media (prefers-color-scheme: dark) { }
@media (prefers-color-scheme: light) { }
/* Hover */
@media (hover: hover) { } /* dispositivo con hover */
@media (hover: none) { } /* touch-only */
/* Pointer */
@media (pointer: fine) { } /* ratón o lápiz */
@media (pointer: coarse) { } /* touch */
/* Update frequency */
@media (update: fast) { } /* gaming, video */
@media (update: slow) { } /* e-readers */
Media features:
- width, height, device-width, device-height.
- aspect-ratio, device-aspect-ratio.
- orientation.
- resolution.
- color, color-index, monochrome.
- prefers-color-scheme: dark/light.
- prefers-reduced-motion: reduce.
- prefers-contrast: more/less.
- hover, pointer.
- update: fast/slow.
Operadores lógicos
/* AND (default) */
@media (min-width: 768px) and (max-width: 1024px) { }
/* OR */
@media (max-width: 768px), (orientation: portrait) { }
/* NOT */
@media not (prefers-color-scheme: dark) { }
Operadores:
- and: ambas condiciones.
- or (coma): alguna condición.
- not: niega la condición.
- Sin operador: como and.
Mobile-first
/* Base: mobile */
.button {
padding: 1em;
font-size: 1em;
}
/* Tablet */
@media (min-width: 768px) {
.button {
padding: 1.5em;
font-size: 1.2em;
}
}
/* Desktop */
@media (min-width: 1024px) {
.button {
padding: 2em;
font-size: 1.4em;
}
}
Mobile-first:
- Estilos base para el caso más pequeño (mobile).
- Media queries min-width para los siguientes.
- El código crece con el viewport.
- El libro recomienda este enfoque.
El libro es claro: mobile-first es el patrón moderno. Empieza por el caso difícil (mobile) y añade complejidad cuando tienes más espacio.
prefers-color-scheme
/* Default: light */
:root {
--bg: white;
--text: black;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: white;
}
}
body {
background: var(--bg);
color: var(--text);
}
prefers-color-scheme:
- dark: el usuario quiere tema oscuro.
- light: el usuario quiere tema claro.
- no-preference: sin preferencia.
- Detecta la preferencia del sistema operativo.
prefers-reduced-motion
.element {
animation: fade 1s ease infinite;
}
@media (prefers-reduced-motion: reduce) {
.element {
animation: none;
transition: none;
}
}
prefers-reduced-motion:
- reduce: el usuario prefiere menos movimiento.
- no-preference: sin preferencia.
- Accesibilidad: respeta siempre.
El libro insiste: respeta siempre prefers-reduced-motion. Usuarios con vestibular disorders se marean con animaciones.
@supports
@supports (display: grid) {
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
}
@supports not (display: grid) {
.container {
display: flex;
flex-wrap: wrap;
}
}
@supports:
- Feature detection.
- Aplica reglas según si el navegador soporta una feature.
- Útil para progressive enhancement.
- Anidable con not.
Operadores en @supports
@supports (display: grid) and (gap: 1rem) { }
@supports (display: grid) or (display: flexbox) { }
@supports not (display: grid) { }
Operadores:
- and: ambas features.
- or: alguna feature.
- not: niega.
- Igual que en @media.
@container queries
Las container queries son el “media queries dentro de un componente”:
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
@container card (max-width: 399px) {
.card {
display: block;
}
}
@container:
- Aplica CSS según el tamaño del contenedor.
- container-type: inline-size o size.
- container-name: identifica el contenedor.
- Mucho más potente que media queries para componentes.
container-type
.parent {
container-type: inline-size;
container-name: card;
}
.parent {
container-type: size; /* inline + block */
container-name: card;
}
container-type:
- inline-size: solo el ancho afecta.
- size: ancho y alto afectan.
- normal: no se comporta como contenedor.
- Sin container-type, @container no funciona.
¿Cuándo usar container queries?
Usa @container:
- Para componentes que cambian según el espacio disponible.
- Sidebars que colapsan a topbar en móvil.
- Cards que muestran/ocultan información según el espacio.
- Cualquier cosa con estado contextual.
Usa @media:
- Para el layout de página completo.
- Cuando el cambio es global.
- Responsive design tradicional.
El libro señala: container queries han cambiado el juego. Permiten componentes verdaderamente reutilizables que saben cuándo cambiar según el espacio disponible.
@keyframes
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.element {
animation: fade-in 1s ease;
}
@keyframes:
- Define estados de una animación.
- from / to: 0% / 100%.
- 0%, 50%, 100%: puntos intermedios.
- Múltiples keyframes para movimientos complejos.
@import
@import url("base.css");
@import url("https://fonts.googleapis.com/css2?family=Inter");
@import url("print.css") print;
@import url("screen.css") screen;
@import:
- Importa otra hoja de estilos.
- Solo válido al inicio del archivo.
- Bloquea el render: usar <link> en HTML.
- print/screen: aplica condicionalmente.
El libro advierte: @import fuerza al navegador a bloquear el render hasta que la hoja importada se cargue. Usa <link> en HTML para mejor performance.
@layer
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
}
@layer base {
body { font-family: sans-serif; }
}
@layer components {
.button { padding: 1em; }
}
@layer utilities {
.hidden { display: none; }
}
@layer:
- Cascade layers.
- Orden de prioridad: el último gana dentro del mismo layer.
- Entre layers: el último layer gana.
- Sin layer: menor prioridad que cualquier layer.
- Útil para organizar CSS de gran tamaño.
Layer anidados
@layer components {
.button { padding: 1em; }
@layer themes {
.button.dark { background: black; }
}
}
Anidamiento:
- Layers dentro de layers.
- Útil para organizar temas.
- El orden de declaración importa.
@property
@property --gradient-angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
.element {
--gradient-angle: 90deg;
background: linear-gradient(var(--gradient-angle), red, blue);
transition: --gradient-angle 0.3s;
}
.element:hover {
--gradient-angle: 180deg;
}
@property:
- Declara el tipo de una custom property.
- syntax: el tipo (length, color, angle, etc.).
- initial-value: el valor por defecto.
- inherits: si se hereda.
- Permite animaciones reales sobre variables.
@font-face
@font-face {
font-family: "MiFuente";
src: url("font.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face:
- Define una fuente personalizada.
- Múltiples src para compatibilidad.
- font-display: comportamiento de carga.
- Ver la nota 12 sobre fonts para más detalle.
@page
@page {
size: A4;
margin: 2cm;
}
@page :first {
/* La primera página */
margin-top: 4cm;
}
@page book {
/* Para páginas dentro del elemento con class="book" */
@top-right {
content: counter(page);
}
}
@page:
- Para CSS de impresión.
- Define márgenes, tamaño, headers/footers.
- Pseudo-classes: :first, :left, :right.
- Útil para imprimir artículos.
@counter-style
@counter-style fancy {
system: cyclic;
symbols: "🌟" "🌙" "☀️";
suffix: " ";
}
ol {
list-style: fancy;
}
@counter-style:
- Define un estilo de numeración personalizado.
- system: cyclic, alphabetic, symbolic, additive, etc.
- symbols: los símbolos a usar.
- suffix, prefix: puntuación.
@charset
@charset "UTF-8";
@charset:
- Solo al inicio del archivo.
- Define el encoding.
- UTF-8 es el estándar.
- Raramente necesario hoy.
@namespace
@namespace svg url("http://www.w3.org/2000/svg");
@namespace html url("http://www.w3.org/1999/xhtml");
svg | circle { /* selectores del namespace SVG */ }
@namespace:
- Para SVG, MathML, etc.
- Permite selectores con prefijo.
- Raramente necesario en HTML.
Cascada y at-rules
Orden de la cascada:
1. Transitions (animaciones).
2. !important user agent.
3. !important user.
4. !important author.
5. Animations.
6. Author normal.
7. User normal.
8. User agent.
Dentro del "author normal":
- @layer (orden importa).
- Specificity.
- Orden de declaración.
@media vs @container
@media:
- Responde al viewport.
- Cambia el layout según el tamaño de pantalla.
- Bueno para layouts globales.
@container:
- Responde al contenedor.
- Cambia el componente según el espacio.
- Bueno para componentes reutilizables.
Mobile-first vs Desktop-first
/* Mobile-first: empezamos con el caso pequeño */
.button {
padding: 0.5em;
}
@media (min-width: 768px) {
.button {
padding: 1em;
}
}
/* Desktop-first: empezamos con el caso grande */
.button {
padding: 1em;
}
@media (max-width: 767px) {
.button {
padding: 0.5em;
}
}
El libro es claro: empiezar por mobile es más fácil. Es más simple añadir que quitar.
Errores comunes
/* Mal: @media sin condiciones */
@media {
.button { color: red; }
}
/* Mal: breakpoint random */
@media (max-width: 767px) { } /* 767? */
@media (max-width: 768px) { }
/* Mal: anidamiento de @media */
@media (max-width: 768px) {
@media (orientation: landscape) { } /* no funciona */
}
/* Mejor: condiciones combinadas */
@media (max-width: 768px) and (orientation: landscape) { }
Trucos comunes
Hover solo en dispositivos con hover
.button:hover {
background: red;
}
@media (hover: none) {
/* En touch-only, no hacer nada con hover */
.button:hover {
background: inherit;
}
}
Theme switcher
:root {
--bg: white;
--text: black;
}
:root[data-theme="dark"] {
--bg: #1a1a1a;
--text: white;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #1a1a1a;
--text: white;
}
}
Container queries para cards
.card-grid {
container-type: inline-size;
container-name: grid;
}
@container grid (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
Soporte progresivo
/* Sin grid */
.container {
display: flex;
flex-wrap: wrap;
}
/* Con grid */
@supports (display: grid) {
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
}
Resumen en tres frases
- @media queries aplican CSS según el viewport. Mobile-first con
min-widthes el patrón estándar. - @container queries son el “media queries para componentes”. Permiten que un componente cambie según el espacio disponible.
- @supports y @layer permiten feature detection y organización de cascade. Combinados con @property, dan CSS moderno y mantenible.
Próximos pasos
- Glosario y referencias: cierre de la wiki. Glosario CSS, libros de referencia, sitios web clave y herramientas para profundizar.
- Epílogo y claves: cierre con ideas recurrentes y cómo seguir.