Apuntes sobre Html2

Cuando uno enseña, dos aprenden.

Media queries

¿Qué son las media queries?

Una media query aplica un bloque de CSS solo cuando se cumple una condición: el ancho de la pantalla está por debajo de cierto valor, el dispositivo tiene pantalla táctil, el usuario prefiere el modo oscuro... Son la herramienta que convierte un diseño fijo en un diseño adaptable (responsive).

El meta viewport

Antes de las media queries, hay un detalle imprescindible. Los móviles, por defecto, simulan una pantalla ancha (normalmente 980px) y reducen el zoom para que la página "quepa". Sin este comportamiento la web de escritorio se vería en miniatura. Pero cuando diseñas para móvil, quieres que el ancho real del dispositivo sea el ancho de referencia.

La etiqueta meta viewport le dice al navegador que use el ancho físico del dispositivo:

<meta name="viewport" content="width=device-width, initial-scale=1">

Sin esta línea en el <head>, las media queries funcionan técnicamente pero el resultado en móvil será incorrecto. Debe estar en todas las páginas que usen diseño responsive.

Sintaxis

@media tipo and (condición) {
    /* reglas que se aplican solo si se cumple la condición */
}

El tipo más común es screen (pantallas). print aplica al imprimir la página. Si se omite el tipo, se aplica a todos los medios.

Las condiciones más usadas:

/* Pantallas de 768px o menos */
@media (max-width: 768px) { ... }

/* Pantallas de 768px o más */
@media (min-width: 768px) { ... }

/* Entre dos valores */
@media (min-width: 480px) and (max-width: 1024px) { ... }

/* Orientación del dispositivo */
@media (orientation: portrait)  { ... }
@media (orientation: landscape) { ... }

/* Pantallas de alta densidad (Retina) */
@media (min-resolution: 2dppx) { ... }

CSS moderno permite una sintaxis de rango más legible:

/* Equivale a min-width: 768px */
@media (width >= 768px) { ... }

/* Equivale a max-width: 1024px */
@media (width <= 1024px) { ... }

/* Equivale al rango con and */
@media (480px <= width <= 1024px) { ... }

Breakpoints

Un breakpoint es el ancho en el que el diseño cambia. No hay un estándar universal, pero los rangos habituales se corresponden aproximadamente con el tamaño de los dispositivos más comunes:

/* Móvil pequeño: hasta 480px */
/* Móvil grande / phablet: 481px - 767px */
/* Tablet: 768px - 1023px */
/* Escritorio: 1024px - 1279px */
/* Escritorio grande: 1280px+ */

La recomendación actual es no basarse en dispositivos concretos sino en el propio contenido: añade un breakpoint donde el diseño empieza a verse mal, no donde crees que estará el próximo iPhone.

Enfoque mobile-first

Hay dos formas de estructurar las media queries:

Desktop-first: defines los estilos para pantalla grande y usas max-width para adaptarlos a pantallas más pequeñas. Es el enfoque más intuitivo si ya tienes un diseño de escritorio.

/* Estilos base: escritorio */
.contenedor {
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 2rem;
}

/* Adaptar para móvil */
@media (max-width: 768px) {
    .contenedor {
        grid-template-columns: 1fr;
    }
}

Mobile-first: defines los estilos base para móvil y usas min-width para añadir complejidad a pantallas más grandes. Es el enfoque recomendado porque el CSS base es el más simple (el móvil) y se enriquece progresivamente.

/* Estilos base: móvil (una columna) */
.contenedor {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}

/* Tablet y superiores */
@media (min-width: 768px) {
    .contenedor {
        grid-template-columns: 250px 1fr;
        gap: 2rem;
    }
}

El argumento técnico para mobile-first es que los dispositivos móviles descargan y procesan el CSS base sin necesidad de evaluar las media queries. Con desktop-first, el móvil descarga también todos los estilos de escritorio aunque no los use.

Unidades de viewport y funciones de valor

Las unidades relativas al viewport completan el cuadro de herramientas para el diseño responsivo:

  • vw: 1% del ancho de la ventana. 100vw = ancho completo.
  • vh: 1% del alto de la ventana. 100vh = alto completo.
  • vmin / vmax: 1% del lado más pequeño o más grande respectivamente.
  • ch: ancho del carácter "0" de la fuente actual. width: 60ch es una anchura de columna de lectura cómoda.
/* Sección hero que ocupa la pantalla completa */
.hero {
    width: 100vw;
    min-height: 100vh;
}

/* Título cuyo tamaño escala con el ancho de la ventana */
h1 { font-size: 5vw; }

Nota: En móviles, vh puede provocar saltos cuando la barra del navegador aparece o desaparece. Los navegadores modernos ofrecen dvh (dynamic, se actualiza en tiempo real), svh (small, asume barra visible) y lvh (large, asume barra oculta). Con soporte todavía irregular, min-height: 100vh sigue siendo la opción más compatible.

calc() permite combinar unidades distintas en un mismo valor, algo que de otro modo es imposible en CSS:

/* Ancho completo menos el espacio que ocupa la barra lateral */
.contenido {
    width: calc(100% - 280px);
}

/* Padding que combina una base fija y una parte proporcional a la ventana */
.seccion {
    padding: calc(1rem + 2vw);
}

min(a, b) y max(a, b) devuelven el menor o mayor de los valores. Útiles para imponer límites sin media queries:

/* Flexible, pero nunca más de 800px */
.columna { width: min(100%, 800px); }

/* Al menos 200px aunque el contenedor sea muy estrecho */
.tarjeta { width: max(200px, 30%); }

clamp(mínimo, ideal, máximo) mantiene un valor dentro de un rango. Es la herramienta moderna para tipografía fluida: el tamaño crece con la pantalla sin salirse de los extremos definidos, sin necesidad de un solo breakpoint:

h1 {
    font-size: clamp(1.5rem, 5vw, 3rem);
    /* nunca menos de 1.5rem, nunca más de 3rem */
}

.contenido {
    width: clamp(300px, 80%, 900px);
}

Consultas de preferencia del usuario

Las media queries no se limitan a tamaños de pantalla. También exponen preferencias del sistema operativo del usuario:

/* Modo oscuro */
@media (prefers-color-scheme: dark) {
    :root {
        --fondo: #1a1a1a;
        --texto: #e8e8e8;
    }
}

/* Reducir movimiento (epilepsia, sensibilidad vestibular) */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* Contraste alto */
@media (prefers-contrast: high) {
    body {
        --color-texto: #000000;
        --fondo: #ffffff;
    }
}

/* Usuario prefiere menos datos (conexión lenta) */
@media (prefers-reduced-data: reduce) {
    .imagen-decorativa { display: none; }
}

Container queries

Las media queries tienen un límite: solo pueden consultar el tamaño de la ventana, no el del contenedor del elemento. Un componente de tarjeta que puede aparecer en una columna ancha o en una columna estrecha tiene el mismo aspecto en ambos casos aunque el espacio disponible sea muy distinto.

Las container queries resuelven esto: aplican estilos según el tamaño del contenedor del elemento, no de la ventana. Primero se declara el contenedor:

.columna {
    container-type: inline-size;  /* habilita consultas de ancho */
    container-name: columna;      /* nombre opcional */
}

Luego se usan con @container:

/* La tarjeta cambia de layout cuando su contenedor tiene 500px o más */
@container (min-width: 500px) {
    .tarjeta {
        display: flex;
        flex-direction: row;
    }
    .tarjeta img {
        width: 200px;
        flex-shrink: 0;
    }
}

Las container queries están soportadas en todos los navegadores modernos desde 2023 y cambian fundamentalmente cómo se diseñan componentes reutilizables: el componente responde a su entorno, no a la pantalla.

Recapitulación

  • Las media queries aplican CSS condicionalmente según características del medio. La más común: el ancho de la ventana.
  • El meta <meta name="viewport" content="width=device-width, initial-scale=1"> es imprescindible para que el diseño responsive funcione en móvil.
  • Condiciones habituales: min-width, max-width, orientation. La sintaxis de rango (width >= 768px) es más legible.
  • Mobile-first: estilos base para móvil, min-width para añadir complejidad. Es el enfoque recomendado.
  • Unidades de viewport: vw y vh relativas al tamaño de la ventana. Precaución con vh en móviles; usar min-height: 100vh o dvh donde haya soporte.
  • calc() mezcla unidades distintas. min() y max() imponen límites. clamp(min, ideal, max) crea valores fluidos sin breakpoints.
  • Las consultas de preferencia exponen ajustes del sistema: prefers-color-scheme, prefers-reduced-motion, prefers-contrast.
  • Las container queries (@container) aplican estilos según el tamaño del contenedor del elemento, no de la ventana. Ideales para componentes reutilizables.

Tu proyecto

Haz que el portfolio sea responsive con un enfoque mobile-first. Añade estas media queries al final de estilos.css:

/* ---- Estilos base: móvil ---- */

/* La nav se convierte en columna en móvil */
.nav-principal {
    flex-direction: column;
    gap: 0.5rem;
}

header {
    flex-direction: column;
    align-items: flex-start;
}

/* Una sola columna en móvil */
.grid-proyectos {
    grid-template-columns: 1fr;
}

/* ---- Tablet: 600px o más ---- */
@media (min-width: 600px) {
    .nav-principal {
        flex-direction: row;
    }

    header {
        flex-direction: row;
        align-items: flex-end;
    }

    .grid-proyectos {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ---- Escritorio: 960px o más ---- */
@media (min-width: 960px) {
    body {
        max-width: 1100px;
        margin: 0 auto;
        padding: 0 2rem;
    }

    .grid-proyectos {
        grid-template-columns: repeat(3, 1fr);
    }
}

Redimensiona la ventana del navegador mientras tienes la página abierta y observa cómo el layout cambia en los puntos de ruptura. Con el inspector puedes activar la vista de dispositivo móvil (Ctrl+Shift+M en Chrome y Firefox).

En la próxima lección: metadatos y SEO: cómo preparar el <head> del portfolio para que sea indexable y se vea bien al compartirlo en redes sociales.

TOP