Diseño web adaptativo¶
En este documento abordaremos el tema del diseño web adaptativo o responsive, aunque en breve veremos que estos dos conceptos no son exactamente iguales. Básicamente, la temática a abordar es cómo diseñar una web para que se vea adecuadamente en diferentes tipos de dispositivos o resoluciones, tales como smartphones, tablets o monitores de alta resolución.
1. Introducción al diseño adaptativo¶
Como acabamos de comentar, abordaremos la problemática de cómo diseñar una web para distintos tipos de dispositivos. Esta afirmación en sí plantea dos preguntas:
- ¿Qué tipos principales de dispositivos podemos o debemos considerar?
- ¿Qué estrategias hay para abordar el diseño web para esos tipos de dispositivos?
1.1. Tipos de resoluciones¶
Tratemos de responder a la primera pregunta: ¿qué tipos principales de dispositivos podemos o debemos considerar?. Realmente no es esa la pregunta adecuada, sino más bien a cuántos tipos de resoluciones diferentes podemos o debemos adaptar nuestra web. Y, para responder a esta pregunta, podemos hacer un análisis rápido de los distintos tipos de pantalla en los que habitualmente podemos consultar una página web:
- Por un lado, tenemos smartphones de baja resolución o resolución extra-pequeña (normalmente abreviada xs, extra small). La resolución máxima típica en esta categoría no llega a unos 576px de ancho.
- El siguiente escalón estaría formado por dispositivos pequeños (normalmente smartphones también) de mayor resolución. Conformarían una resolución pequeña (a menudo abreviada como sm de small), con valores máximos de hasta unos 768px de anchura.
- A continuación están las pantallas de pequeño tamaño, como las de las tablets, que formarían una resolución media (normalmente abreviada como md), con valores máximos de hasta unos 992px.
- Después vendrían pantallas más grandes (abreviadas lg), como los monitores de gama media, con resoluciones de hasta unos 1200px.
- Le siguen las pantallas aún más grandes (abreviadas xl), con resoluciones de hasta unos 1400px.
- Finalmente, podríamos hablar de pantallas todavía más grandes (abreviadas xxl), para resoluciones mayores que la anterior.
1.2. Estrategias de diseño. Responsive vs adaptativo¶
Vayamos ahora con la segunda pregunta formulada anteriormente: a la hora de abordar el diseño de una web para dar respuesta a todos los tipos de resoluciones posibles (o un grupo de ellos), tenemos dos estrategias:
- La estrategia responsive consiste en definir un único diseño que automáticamente se va adaptando a las distintas resoluciones que indiquemos, y re-posicionando sus elementos conforme a esas resoluciones. Para abordar esta estrategia podemos hacer uso de elementos como flexbox, que veremos a continuación, y también de frameworks específicos de diseño web, como Bootstrap.
- La estrategia adaptativa consiste en definir un diseño para cada una de las posibles resoluciones que queremos tratar, de forma que se activa o carga uno u otro en función de la resolución. Esta estrategia se aborda mediante el uso de media queries, que también trataremos a continuación.
1.3. Gestionando el viewport¶
Una de las etiquetas meta que comentamos en las primeras sesiones de este curso es la etiqueta viewport. Esta etiqueta permite a los diseñadores web controlar el área visible de una web. En general, la etiqueta que se recomienda utilizar tiene este aspecto:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
El parámetro width=device-width hace que la anchura de la página se iguale a la anchura de la pantalla del dispositivo. Por otra parte, el parámetro initial-scale=1.0 establece el nivel de zoom inicial en 1, es decir, en el zoom del 100%, cuando la página se carga por primera vez.
2. Diseño adaptativo. Uso de media queries¶
Las media queries (en español, consulta sobre medios) son una técnica proporcionada por CSS3 que permite adaptar la visualización del contenido de una página a las características del dispositivo en que se va a mostrar. Estas características incluyen, sobre todo, la resolución de la pantalla, pero también podemos hablar de otras, como el tipo de medio. Así, por ejemplo, podemos definir un estilo diferente para una web que se vaya a imprimir en papel y eliminar el color de fondo, entre otras cosas.
2.1. Estructura general de una media query¶
Las media queries se definen en el propio documento CSS, a través de la expresión @media seguida de las condiciones que debe cumplir el dispositivo para aplicar los estilos incluidos en ella. En concreto, su sintaxis general es:
@media not|only tipo_medio and (expresiones)
{
estilos CSS
}
donde:
tipo_medioalude al tipo de medio donde se va a mostrar el contenido. Típicamente es screen (pantalla), pero puede valer también print (impreso en papel), speech (hablado) u all.- Las expresiones consisten en una serie de condiciones que debe cumplir el dispositivo. Algunos de los parámetros más habituales que se pueden especificar aquí son:
- Anchura mínima o máxima, que suele ser lo más habitual. Se indican con
min-width/max-widthy el valor suele especificarse en píxeles (por ejemplo,min-width: 700px). También podemos especificar altura mínima/máxima, o un rango de anchura/altura (por ejemplo,500px < width < 800px). - Resolución del dispositivo (
resolution), habitualmente expresada en dpi. Podemos especificar un rango o bien establecer una resolución mínima o máxima (min-resolution,max-resolution). - Orientación del dispositivo (
orientation), que puede serportrait(vertical) olandscape(horizontal) - Estilo de tema (
prefers-color-scheme), útil para aplicar estilos distintos a temas claros (light) u oscuros (dark) - Efectos de contraste o transparencia (
prefers-reduced-transparencyoprefers-contrast)
- Anchura mínima o máxima, que suele ser lo más habitual. Se indican con
Por ejemplo, esta media query se aplicaría únicamente a pantallas con una resolución mínima de 768px:
@media only screen and(min-width: 768px)
{
...
}
También podemos enlazar condiciones con and o con comas (equivalente a un or):
/* Estilos para pantallas entre 320 y 480px de ancho y resolución de 150dpi */
@media only screen and (320px <= width <= 480px) and (resolution = 150dpi)
{
...
}
/* Estilos para pantallas con anchura máxima de 600px u orientación vertical */
@media (max-width: 600px), (orientation: portrait)
{
...
}
Para el uso habitual que se hace de las media queries puede bastar con especificar una anchura mínima o máxima, por lo que podemos dejarlas con este formato:
@media (min-width: 768px)
{
...
}
Nota
Algunos de estos efectos, como por ejemplo los que se refieren al tema claro/oscuro, transparencia o contraste, se aplicarán dependiendo de la configuración del navegador o del sistema operativo. Por ejemplo, Google Chrome aplica el tema de color en Windows dependiendo de cómo esté configurado dicho tema en Windows. Para cambiarlo, podemos hacer clic derecho en el escritorio y elegir Personalizar. Luego, en el panel izquierdo elegimos Personalización y en el panel central vamos a la opción de Colores para elegir el tema deseado.
El resto de opciones mencionadas también se configuran de forma distinta dependiendo del navegador y sistema operativo.
2.2. Estrategia de uso de las media queries¶
A la hora de aplicar media queries a nuestra web para definir diferentes disposiciones y diseños de elementos acordes a distintas resoluciones de pantalla, una estrategia general es:
- Fuera de las media queries (por ejemplo, al inicio del documento CSS) definimos los estilos generales que no van a cambiar entre resoluciones (colores de fondo, tipos de letra, etc) y también la disposición de los elementos para la resolución más baja (o más alta) que queramos tratar.
- Definimos una media query por cada resolución superior (o inferior) que queramos contemplar, y dentro, redefinimos únicamente los estilos que queramos adaptar a esa resolución. Por ejemplo, la anchura o tamaño de ciertas cajas, etc.
Por ejemplo, supongamos el siguiente contenido HTML:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Ejemplo media queries</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<section id="contenedor">
<section id="caja1">Caja 1</section>
<section id="caja2">Caja 2</section>
</section>
</body>
</html>
Vamos a definir dos tipos de resoluciones: para pantallas de hasta 768px de ancho, mostraremos una caja debajo de la otra. En cambio, para pantallas de mayor anchura, las mostraremos en paralelo, usando para ello un grid de dos columnas. Nuestro CSS quedaría así:
- Primero definimos los estilos generales y la disposición de las cajas para baja resolución:
body
{
font-family: Arial;
margin: 10px 5%;
}
#caja1
{
background-color:lightcoral;
padding: 10px;
margin-bottom: 10px;
border: 1px solid black;
}
#caja2
{
background-color:lightskyblue;
padding: 10px;
margin-bottom: 10px;
border: 1px solid black;
}
- Después, definimos la media query para resoluciones mayores, y definimos la disposición en rejilla de las dos cajas en este caso:
@media(min-width: 768px)
{
body
{
margin: 100px 20%;
}
#contenedor
{
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto;
gap: 10px;
}
}
Y obtendríamos un resultado como este (baja resolución / alta resolución):
Ejercicio 1
Descarga este ejemplo y define un documento CSS donde, ayudándote de media queries, definamos tres posibles disposiciones:
- Para resoluciones bajas (hasta 768px), mostraremos las cajas una debajo de otra
- Para resoluciones intermedias (hasta 992px), mostraremos la caja 1 arriba, la caja 4 abajo, y las dos intermedias en paralelo. Además, cambiaremos el color de las cajas 1 y 4 a gris (ver imagen resultado)
- Para resoluciones mayores, mostraremos las 4 cajas en paralelo con sus colores originales
2.3. Ficheros CSS y diseño adaptativo¶
Podemos centralizar todo el diseño adaptativo de nuestra web en un solo CSS, o definir un fichero CSS para cada configuración de pantalla. En este último caso, podemos emplear la etiqueta link de HTML para especificar en qué condiciones cargar cada CSS:
<link rel="stylesheet" href="movil.css" media="(max-width: 640px)">
<link rel="stylesheet" href="tablet.css" media="(min-width: 640px) and (max-width: 1280px)">
<link rel="stylesheet" href="escritorio.css" media="(min-width: 1280px)">
2.4. Uso de nesting en media queries¶
Notar que también podemos aplicar CSS nesting si queremos y ubicar las media queries dentro de los elementos sobre los que se aplican. Por ejemplo, para el caso anterior podríamos haber hecho algo así:
#contenedor
{
display: block;
margin: 10px 10%;
@media(min-width: 768px)
{
&
{
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto;
gap: 10px;
}
}
}
2.5. Las container queries¶
Un cambio importante que se ha dado en versiones recientes de CSS es la incorporación de container queries, a través de la regla @container. Esto va a permitir que los estilos que apliquemos a los elementos ya no sólo dependan de la pantalla sobre las que los estemos visualizando, sino también del tamaño dado al elemento que los contiene.
Para poder trabajar con estos elementos, primero debemos configurar el elemento contenedor con un par de propiedades:
container-type: especificamos que este contenedor va a ser consultable para ajustar las características de lo que tiene. Los posibles valores soninline-size(si nos interesa sólo la anchura) osize(para anchura y altura).container-name: no es obligatorio, pero sí es útil. Se le asigna un nombre a este contenedor para luego usarlo en las queries
Por ejemplo, así configuramos el elemento #contenedor para que sea consultable en anchura, y le damos un nombre:
#contenedor
{
container-type: inline-size;
container-name: mi-contenedor;
}
A partir de aquí, los elementos que estén dentro de este contenedor pueden admitir configuraciones dependiendo del tamaño (anchura) del contenedor padre. Por ejemplo, si tenemos una sección #contenido dentro de este contenedor, podemos hacer algo así:
@container mi-contenedor (min-width: 800px)
{
#contenido
{
...
}
}
Ejercicio 2
En el siguiente código HTML hay un elemento de clase evento que se muestra en dos contenedores diferentes. En el CSS se ha definido un grid de 2 columnas, y debes añadir el código CSS necesario para que, si la anchura del contenedor es inferior a 300px, sólo se muestre el título principal (con un tipo de letra más pequeño) y el subtítulo, rodeado todo por un borde negro redondeado. Esto hará que en el contenedor de la izquierda se vea reducido, y en el de la derecha ampliado.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF--8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ejemplo de Container Queries</title>
<style>
body
{
font-family: system-ui;
margin: 10px 5%;
}
#contenedor
{
display: grid;
grid-template-columns: 25% 75%;
grid-template-rows: auto;
gap: 50px;
}
.evento
{
margin: 10px;
padding: 10px;
}
</style>
</head>
<body>
<header>
<h1>Próximos Eventos</h1>
</header>
<section id="contenedor">
<aside id="miniaturas">
<section class="evento">
<h2>Concierto de Jazz Nocturno</h2>
<p class="subtitulo">25 Octubre 2025, Gran Teatro</p>
<div class="contenido">
<p>Una noche mágica con El Reno Renardo es un grupo musical español de heavy metal humorístico o freak metal, nacido de un proyecto en línea en solitario de Jevo Jevardo. Tras dos años de bastante éxito en Internet, se decidió crear un concierto, para lo cual se reclutó al resto de miembros y se formó la banda en sí misma.</p>
</div>
</section>
<section class="evento">
<h2>Cuentos de Alberto Celdrán</h2>
<p class="subtitulo">12 Noviembre 2025, Explanada</p>
<div class="contenido">
<p>El gran cuentacuentos Alberto Celdrán nos deleitará en la tarde noche del 12 de noviembre con una serie de cuentos fantásticos basados en la historia de África. Apto para grandes y pequeños, el autor interpretará diversos cuentos tradicionales africanos que cuentan la historia y creencias de esa tierra.</p>
</div>
</section>
</aside>
<main id="principal">
<section class="evento">
<h2>Concierto de Jazz Nocturno</h2>
<p class="subtitulo">25 Octubre 2025, Gran Teatro</p>
<div class="contenido">
<p>Una noche mágica con El Reno Renardo es un grupo musical español de heavy metal humorístico o freak metal, nacido de un proyecto en línea en solitario de Jevo Jevardo. Tras dos años de bastante éxito en Internet, se decidió crear un concierto, para lo cual se reclutó al resto de miembros y se formó la banda en sí misma.</p>
</div>
</section>
<section class="evento">
<h2>Cuentos de Alberto Celdrán</h2>
<p class="subtitulo">12 Noviembre 2025, Explanada</p>
<div class="contenido">
<p>El gran cuentacuentos Alberto Celdrán nos deleitará en la tarde noche del 12 de noviembre con una serie de cuentos fantásticos basados en la historia de África. Apto para grandes y pequeños, el autor interpretará diversos cuentos tradicionales africanos que cuentan la historia y creencias de esa tierra.</p>
</div>
</section>
</main>
</div>
</body>
</html>
Este debería ser el aspecto final aproximado:
3. Diseño responsive. Uso de flexbox¶
Flexbox (flexible boxes) es un mecanismo de diseño web responsive facilitado por CSS3, que permite especificar una serie de propiedades en las cajas para que se adapten automáticamente al tamaño de pantalla existente, dependiendo de si van a tener suficiente espacio para mostrarse de un modo u otro.
Del mismo modo que ocurre con el grid de CSS, es necesario que las cajas involucradas en el proceso responsive estén contenidas en otra, de forma que podemos especificar las propiedades generales de flexbox en la caja contenedora, y luego definir, adicionalmente, cómo queremos disponer las cajas en ese contenedor.
Como veremos a continuación, definiremos cómo distribuir los elementos en una dimensión (horizontal o verticalmente), no es posible definir rejillas bidimensionales como se hace con grid.
3.1. Configurando la caja contenedora¶
Entre las propiedades que podemos definir en la caja contenedora, podemos destacar las siguientes:
display: en este caso, la propiedad display deberá tener el valor de flex para habilitar el contenedor como un contenedor de elementos flexibles. Alternativamente, también se le puede dar el valor inline-flex si queremos que los elementos de dentro se comporten como elementos en línea (no en bloque).flex-direction: indica cómo se van a disponer las cajas dentro del contenedor. El valor por defecto es row, que hace que se dispongan de izquierda a derecha, pero también podemos utilizar row-reverse (horizontal pero invertido), column (de arriba a abajo) y column-reverse (vertical pero invertido). Como decíamos antes, con Flexbox vamos a poder disponer los elementos en una dirección (bien horizontal, bien vertical) de forma flexible.justify-content: indica cómo se va a rellenar el espacio entre cajas a lo largo del eje principal (horizontal o vertical). Puede tomar los valores flex-start (valor por defecto, se agrupan las cajas al inicio del espacio disponible), flex-end (al final), center (centradas en medio del espacio disponible), space-between (justificadas con espacio entre ellas), space-around (justificadas con espacio también a los lados)...flex-wrap: indica cómo vamos a adaptar las cajas en caso de que no quepan todas en una misma fila (o columna). Su valor por defecto es nowrap, lo que indica que no hay ningún ajuste: las cajas se disponen según lo indicado en las propiedades anteriores, y si no caben se siguen disponiendo en esa dirección. Pero si lo establecemos a wrap, entonces se redistribuirán si no hay espacio suficiente. Es el valor más habitual si queremos establecer un diseño responsive. También existe la opción wrap-reverse.align-items: indica la alineación de los elementos a lo largo del eje cruzado o secundario. Posibles valores son stretch (estira los elementos para que ocupen todo el alto/ancho del contenedor), flex-start (al inicio del eje cruzado), flex-end (al final del eje cruzado) o center.flex-flow: es un atajo para poner de una vez, separados por espacios, los valores deflex-directionyflex-wrap. Por ejemplo:flex-flow: row wrap;.row-gapycolumn-gappermiten establecer la separación entre filas (para eje principal vertical) o entre columnas (para eje principal horizontal). Alternativamente podemos usargappara especificar ambas separaciones (un solo valor para las dos, o uno para X y otro para Y).
Existen algunas webs como esta donde podemos probar el comportamiento de estas y otras propiedades, y ver cómo funciona cada una en realidad.
3.2. Configurando las cajas internas¶
Para cada caja contenida en el elemento contenedor, podemos configurar sus propiedades flexbox para indicar dónde se va a ubicar y cuánto espacio va a ocupar. Algunas de estas propiedades son:
order: indica el orden de la caja dentro de la secuencia de cajas contenidas. Si no indicamos nada, cada caja se coloca a continuación de la anterior, siguiendo la disposición indicada en la caja contenedora. Pero podemos alterar ese orden natural reubicando las cajas. Se trata simplemente de un número; cuanto mayor sea, la caja se colocará más hacia el final de la secuencia.flex-grow: capacidad del elemento para crecer y ocupar el espacio disponible en el eje principal. Funciona de forma proporcional, como las unidades fr de las rejillas (grid).flex-shrink: capacidad del elemento para encogerse si no hay espacio para todos. Similar al anteriorflex-basis: tamaño inicial del elemento antes de distribuir el espacio sobrante. Por defecto es auto, pero lo podemos poner en píxeles o porcentaje, por ejemplo.flex: es un atajo que combina las tres propiedades anteriores. indica el tamaño de la caja respecto al resto. Son tamaños relativos, igual que ocurre con las unidades fr en la rejilla grid CSS.
Respecto a las unidades de algunas de estas propiedades, veamos un ejemplo: si todas las cajas tienen un tamaño flex de 1, todas ocuparán lo mismo. Si a alguna le asignamos un tamaño de 2, ocupará el doble que el resto. Alternativamente, también podemos utilizar esta propiedad para dar un tamaño fijo en píxeles (por ejemplo, flex:200px) o en porcentaje (flex: 40%). Esta última opción es útil si queremos controlar mejor cuántas columnas queremos que haya en cada fila.
Además, podemos emplear otras propiedades CSS habituales, como min-width. Esto indicará una anchura mínima en los elementos y, si esta anchura no se cumple y tenemos habilitada la opción flex-wrap: wrap en el contenedor, entonces hará que vaya a la siguiente fila/columna disponible para mostrarse.
Echemos un vistazo a este ejemplo:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Ejemplo flexbox</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<section id="contenedor">
<section id="caja1">Caja 1</section>
<section id="caja2">Caja 2</section>
<section id="caja3">Caja 3</section>
</section>
</body>
</html>
Imaginemos que queremos poner las tres cajas en horizontal, con la caja 2 ocupando el doble de espacio, y que todas tengan una anchura mínima de 250px. Si eso no puede cumplirse, queremos que las cajas se redistribuyan para cumplir con esa anchura mínima (normalmente una debajo de otra).
En primer lugar, definimos las características del elemento contenedor:
#contenedor
{
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
}
Después, establecemos las propiedades de las cajas contenidas. Además de sus características particulares (color de fondo, padding, etc), indicamos sus propiedades flexbox: anchura (propiedad flex) y anchura mínima.
#caja1, #caja3
{
background-color:lightcoral;
padding: 10px;
flex: 1;
min-width: 250px;
}
#caja2
{
background-color:lightskyblue;
padding: 10px;
flex: 2;
min-width: 500px;
}
Si vamos variando el tamaño de la ventana del navegador, podemos comprobar cómo las propias cajas se van ajustando a distintos tamaños y posiciones para cumplir con lo establecido. Aquí vemos la evolución desde pantallas grandes a pequeñas:
Notar que, si eliminamos la propiedad flex-wrap: wrap del elemento contenedor, las cajas ya no se redistribuyen. Se quedan ocupando su espacio original, y si encogemos demasiado la pantalla nos tocará hacer scroll para verlas.
Notar también que, utilizando flexbox, podemos reajustar las cajas automáticamente en cuanto el tamaño de la pantalla "choque" con los requisitos de cada caja, pero no podemos controlar del todo dónde va a parar cada caja. En el ejemplo anterior, llega un punto en que la caja 3 baja a la segunda fila, quedando la caja 1 y la 2 en primera fila... pero quizá preferiríamos otra distribución para esa situación intermedia.
Puedes leer más información sobre Flexbox en otras webs interesantes, como por ejemplo esta.
Ejercicio 3
Descarga este ejemplo que ya hemos utilizado en un ejercicio previo. Ahora vamos a configurarlo mediante flexbox para que:
- Las dos cajas centrales ocupen el doble de tamaño que las laterales. Las laterales van a tener un color de fondo gris y las centrales amarillo-naranja.
- Las cajas laterales tengan una anchura mínima de 200px, y las centrales de 300px. Cuando este tamaño no sea posible, se redistribuirán
Aquí tienes un ejemplo de cómo puede quedar, a algunas de las resoluciones posibles:
4. Uso avanzado de la rejilla (grid)¶
En documentos anteriores ya hemos hablado del uso de la rejilla o grid desde CSS3 para poder posicionar nuestros elementos en la web de forma cómoda. Entonces simplemente definíamos en qué fila(s) o columna(s) queríamos ubicar cada elemento. Sin embargo, la rejilla tiene otros usos algo más avanzados o complementarios, que vamos a comentar aquí.
4.1. Uso de repeat al definir la rejilla¶
La instrucción repeat en la construcción de rejillas nos permite repetir una misma estructura de filas y/o columnas las veces que hagan falta. Por ejemplo, de este modo creamos una rejilla con 4 columnas de igual ancho, y 3 filas de altura automática:
#contenedor
{
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, auto);
...
}
También tenemos la opción de usar el parámetro auto-fit para no limitar el número de filas o columnas. En este caso, todo dependerá del tamaño que tengan las celdas en las filas/columnas. Es muy habitual utilizarlo con la función CSS minmax, para indicar el rango de tamaño permitido. Por ejemplo, la siguiente declaración:
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
Añadirá tantas columnas como quepan, siempre que todas tengan al menos 200px de ancho. Es decir, suponiendo que el contenedor tenga 1000px de anchura disponible, con la configuración anterior podrían caber hasta 5 columnas de 200px. Pero:
- Si añadimos menos de 5 columnas, las que haya se expanden para ocupar todo el ancho disponible
- Si añadimos más de 5 columnas, las que no quepan se irán a la siguiente fila, con las mismas características fijadas en
grid-template-columns, y con alturaautopara que se ajusten al tamaño necesario para mostrar su contenido
4.2. Configuración de la rejilla por áreas¶
Podemos definir áreas en el grid usando la propiedad grid-template-areas en el elemento contenedor. Esto nos permite asignar un nombre a cada área, y posicionar luego los elementos en un área concreta, indicando su nombre.
Por ejemplo, imaginemos que definimos estas áreas en nuestro contenedor:
#contenedor
{
display: grid;
grid-template-areas:
"arriba arriba"
"izquierda derecha"
"abajo abajo";
}
Hemos definido una malla de 3 filas y 2 columnas en cada fila. Si hiciéramos algo como esto:
#elemento1 { grid-area: arriba; }
#elemento2 { grid-area: izquierda; }
#elemento3 { grid-area: derecha; }
#elemento4 { grid-area: abajo; }
Entonces el elemento1 ocuparía toda la fila superior, y el elemento4 toda la fila inferior. Los elementos 2 y 3 se repartirían la zona central a izquierda y derecha, respectivamente.
Notar que los elementos ocupan automáticamente toda el área que se le ha asignado con ese nombre, lo que permite hacer expansiones tanto en horizontal como en vertical:
#contenedor
{
display: grid;
grid-template-areas:
"izquierda derecha"
"izquierda derecha"
"abajo1 abajo2";
}
En este caso, un item colocado en el área izquierda ocuparía la primera columna de las dos primeras filas.
El uso de áreas permite una mejor legibilidad de la estructura de la web, y un mantenimiento más sencillo. Además, es ideal para disposiciones adaptables a distintas resoluciones, ya que se pueden definir distintas disposiciones de las áreas en diversas media queries.
4.3. La rejilla implícita¶
Cuando definimos una rejilla como por ejemplo esta:
#contenedor
{
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-rows: 50px auto;
...
}
Hemos definido una rejilla de 2 filas y 3 columnas. Es lo que se conoce como la rejilla explícita, la que hemos definido en el código CSS. Sin embargo, en algunas ocasiones puede que queramos añadir más elementos y no tengamos previsto espacio para ellos en esa rejilla. Es entonces cuando entra en funcionamiento la rejilla implícita.
¿Cómo se disponen los elementos que no caben en el grid? Para controlarlo podemos hacer uso de estas propiedades:
grid-auto-rows: define el alto de las siguientes filas que se añadan para colocar los elementos que no caben en la rejilla explícitagrid-auto-columns: define el ancho de las columnas que se añadan para esa rejilla adicional.grid-auto-flow: indica en qué orden se disponen los elementos en la rejilla implícita a medida que se añadan (si no indicamos una casilla concreta). Sus posibles valores son row (por filas, valor por defecto) o column (por columnas). También podemos indicar el sufijo dense (row dense o column dense) para forzar a que, si un elemento no cabe en las casillas que tiene asignadas en esa disposición, pase a la siguiente fila/columna automáticamente. Por ejemplo, si un elemento tiene span 2 y no cabe en la fila en la que le tocaría ir, congrid-auto-flow: row denseautomáticamente ocupará las 2 primeras columnas de la siguiente fila (si están libres).
Aquí tenemos un ejemplo donde ampliamos el anterior, indicando que las nuevas filas tendrán una altura de 100px, y las nuevas columnas de 80px:
#contenedor
{
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-rows: 50px auto;
grid-auto-rows: 100px;
grid-auto-columns: 80px;
}
Si colocamos al elemento 5 en la columna 5 de la primera fila:
#elemento5
{
grid-row: 1;
grid-column: 5;
}
Como las 3 primeras columnas son explícitas y con tamaño definido, se crearía una cuarta columna vacía de 80px y ubicaría al elemento en una quinta columna, también de 80px.
Nota
Notar que estas propiedades encajan muy bien con otras que hemos visto antes, como el auto-fit. Podemos, de este modo, definir tantas columnas como quepan, y que las nuevas filas que se generen tengan una altura determinada (y no auto, que es el valor por defecto):
#contenedor
{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-auto-rows: 100px;
}
Ejercicio 4
Dado el siguiente contenido HTML:
<div class="layout">
<header>Header</header>
<aside>Sidebar</aside>
<main>
<h2>Main content</h2>
<section class="gallery">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
<div>Card 4</div>
<div>Card 5</div>
</section>
</main>
<footer>Footer</footer>
</div>
Cópialo en una página HTML completa, y enlaza un CSS aparte que realice la siguiente distribución, empleando únicamente grid y sus elementos:
┌─────────────────────────────┐
│ header │
├─────────────┬───────────────┤
│ sidebar │ main │
│ │ + gallery │
│ │ (auto-fit) │
├─────────────┴───────────────┤
│ footer │
└─────────────────────────────┘
Haz que las tarjetas (cards) dentro del main tengan una forma cuadrada o rectangular, con bordes redondeados, y que se distribuyan con auto-fit en la zona que tengan asignada.
5. Conclusiones. Flexbox vs grid vs media queries¶
En general, flexbox es una buena opción para una distribución flexible unidimensional (es decir, colocar componentes en una misma fila o columna y que se auto-distribuyan dependiendo del tamaño disponible). Pero para disposiciones bidimensionales (tablas), flexbox se queda corto por sí mismo, si queremos controlar la distribución concreta de los elementos. Una alternativa a esto podría ser utilizar grid y media-queries para diferentes posibles tamaños.
Con lo visto hasta ahora, podemos optar por un diseño adaptativo (media queries + grid) si queremos controlar mejor la disposición bidimensional de los elementos de la web, o por un diseño responsive (flexbox) si sólo queremos controlar la disposición unidimensional (fila a fila, o columna a columna). El inconveniente de lo primero es que nos "obliga" a plantear distintos diseños complementarios para elegir automáticamente cuál de ellos cargar. Una alternativa a esto es utilizar algún framework de diseño web, como Bootstrap, que simplifica bastante esta tarea.
Pero, por encima de todo, hay que tener en cuenta que todas estas opciones no son enemigas entre sí, sino que se pueden complementar. Es muy habitual utilizar media queries y grid para el diseño general de la página y luego flexbox para organizar los elementos en un componente determinado.
Bonus: menús colapsables¶
Vamos a ver una aplicación práctica de lo que hemos visto en esta sección combinando algunas técnicas para realizar un menú colapsable. Este tipo de menús es muy habitual en webs adaptativas/responsive que, cuando el tamaño de la pantalla es demasiado pequeño, colapsan su menú superior en un botón, para que se muestre desplegado sólo si el usuario pulsa el botón, ahorrando así espacio en pantalla.
La idea es pasar de una barra de navegación con este aspecto en resoluciones medias-grandes...
... a un botón que colapse el menú en pantallas pequeñas:
Al pulsar en el botón se mostrará desplegado el menú, en vertical, desde un lateral:
Ejercicio 5
Se propone como ejercicio que sigas los pasos que se van detallando a continuación, para elaborar una página con un menú colapsable.
En primer lugar, plantearemos el contenido HTML base para este ejemplo:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ejemplo menú colapsable</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<header>
<a class="marca" href="#">Tienda online</a>
<nav>
<a href="#">Inicio</a>
<a href="#">Tienda</a>
<a href="#">Ofertas</a>
<a href="#">Contacto</a>
</nav>
</header>
<main>
<p>Contenido de ejemplo</p>
</main>
</body>
</html>
Definiremos también en la hoja de estilos CSS unos estilos básicos de inicio para la página:
*
{
box-sizing:border-box;
}
body
{
max-width: 1200px;
margin:auto;
padding: 10px;
font-family:system-ui;
}
a
{
color:inherit; text-decoration:none
}
/* Header */
header
{
padding: 10px;
background:#e5e7eb;
height:64px;
display:flex;
align-items:center;
justify-content:space-between;
gap:22px;
nav
{
display:flex;
gap:28px;
& a:hover{ color:#fff }
}
.marca
{
font-size:2rem;
font-weight:800;
color:#2d7df6;
}
}
Ahora vamos a definir el mecanismo del menú colapsable. Empezaremos por mostrar el "botón de hamburguesa" y ocultar el menú de navegación cuando el tamaño de pantalla sea pequeño. El botón de hamburguesa se llama así porque suele aparecer con tres franjas horizontales, que simulan el panecillo y la hamburguesa dentro. Para ello añadimos dicho botón en nuestro código HTML (al final del header, por ejemplo). Los span que contiene dentro se encargarán de mostrar cada una de las tres barras horizontales.
<label for="nav-toggle" class="hamburguesa" aria-label="Abrir menú" role="button">
<span></span>
<span></span>
<span></span>
</label>
</header>
Nota
El atributo for="nav-toggle" lo usaremos a continuación para conectar este elemento con otro que hará la "magia".
Ahora lo configuramos en CSS para que esté inicialmente oculto:
header
{
...
.hamburguesa
{
width: 44px;
height: 44px;
display: none; /* inicialmente oculto */
flex-direction: column; /* las barras se ponen una bajo la otra */
justify-content: center;
align-items: center;
gap: 6px; /* espacio entre barras */
cursor: pointer;
border-radius: 8px;
/* Barras de la hamburguesa */
span
{
display: block;
width: 26px;
height: 3.5px; /* grosor */
background: #000;
border-radius: 2px;
}
}
}
Ahora vamos a definir una media query dentro del header que, para pantallas pequeñas, oculte el menú de navegación y muestre el botón.
header
{
...
@media (max-width:900px)
{
nav { display:none; }
.hamburguesa { display:flex; }
}
}
Con esto ya conseguiremos que se muestre el botón y se oculte el menú en resoluciones pequeñas. Llega el momento de conectar con el menú lateral. Para ello vamos a añadir un checkbox oculto, enlazado con el label de la hamburguesa:
<!-- Esto lo ponemos antes del header -->
<input id="nav-toggle" type="checkbox" aria-hidden="true">
Añadimos inicialmente un pequeño CSS para sacarlo fuera de la zona de visión (podemos añadirlo al final del CSS actual):
#nav-toggle
{
position:absolute;
left:-9999px;
}
Definimos ahora el panel con el menú lateral. Repetimos en él las opciones que queramos mostrar del menú superior. Podemos añadirlo, por ejemplo, al final del cuerpo del documento HTML:
<aside class="menulateral" aria-label="Menú móvil">
<label for="nav-toggle">✕</label>
<a href="#">Inicio</a>
<a href="#">Tienda</a>
<a href="#">Noticias</a>
<a href="#">Contacto</a>
</aside>
Notar que el panel ya tiene una "X" para hacer clic en ella y cerrar el menú (está también asociada al checkbox anterior). La idea es que, cuando el checkbox esté desmarcado, no se muestre el menú lateral, y cuando pulsemos la etiqueta (hamburguesa), se marque automáticamente y eso haga que, con pseudoclases, se muestre el menú lateral. Cuando pulsemos la "X", nuevamente se desmarcará el checkbox y se ocultará el panel.
Vamos a definir todo este comportamiento en CSS. En primer lugar definimos las características generales del panel de menú lateral (al final del CSS, por ejemplo):
.menulateral
{
position: fixed;
top: 0;
right: 0;
height: 100vh;
width: 70vw;
max-width: 320px;
background: #e5e7eb;
box-shadow: -4px 0 12px rgba(0,0,0,0.2);
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
z-index: 100; /* por encima del contenido */
/* Estado inicial: se mueve fuera de la pantalla por la derecha */
transform: translateX(100%);
transition: transform 0.25s ease-out;
label
{
align-self: flex-end;
cursor: pointer;
font-size: 1.4rem;
margin-bottom: 8px;
}
a
{
padding: 8px 4px;
&:hover{ color:#fff }
}
/* Nos aseguramos de que no se vea el menú si la resolución es alta */
@media (min-width: 901px)
{
&
{
display: none;
}
}
}
Ahora vamos a usar la pseudoclase :checked en el checkbox oculto para que, cuando esté marcada, este panel se desplace hacia la izquierda:
#nav-toggle:checked ~ .menulateral
{
transform: translateX(0);
}
Con esto ya tendremos el efecto deseado.