DISEÑO DE INTERFACES WEB
LAYOUTS, FLEX, GRID, RESPONSIVE, MEDIA QUERIES
- 1. Introducción
- 2. Flex
- 3. Grid
- 4. Media queries
- 5. Container queries
- 6. Optimizaciones
- 7. Desarrollo de web accesibles
- 8. Recursos
Se entiende como layout a la disposición de elementos en una página web. En sus inicios, los diseñadores web maquetaban usando etiquetas html como table, frameset y otras parecidas. Con la llegada de los móviles estas formas de crear el layout se demostraron claramente inadecuadas.
Aparecieron entonces dos nuevas formas de maquetar mucho más amigables con el diseño, tanto en pantallas de PC/portátil como en pantallas de móviles. Estas dos nuevas técnicas se conoce como flexbox (o simplemente flex) y grid.
Aunque ambas técnicas tienen bastantes similitudes, y hay casos en los que puede emplearse una u otra de forma indistinta, podemos resumir y aconsejar usar:
- flex para disposición de elementos en una dimensión
- grid para disposición de elementos en dos dimensiones
Debido a la gran cantidad de funcionalidades que proporcionan cada una de estas técnicas, se aconseja al lector consultar en profundidad los enlaces proporcionados en este tema. Por mi parte, para no crear confusión al intentar abordar todas las propiedades disponibles, sólo nombraré las más habituales y útiles y dejaré para su consulta las más específicas.
Disposición flexible
- Referencia: A Complete Guide to Flexbox
En flex trabajaremos principalmente con el eje principal, que es el eje a lo largo del cual se colocan los elementos flexibles. Ojo, no es necesariamente horizontal; Depende de la propiedad flex-direction, que puede tomar los siguientes valores: row, columm, row-reverse, columm-reverse.
El eje transversal es el eje perpendicular al eje principal.
Ejemplo:
display: flex;
gap: 10px;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
align-content: center;Note
También existen las propiedades row-gap y column-gap de forma independiente.
Important
justify-contentdistribuye elementos en el eje principal, ¡no necesariamente horizontal!align-itemsdistribuye elementos en el eje transversal, ¡no necesariamente vertical!
Por ejemplo, para ver con se comporta la propiedad justify-content en distintas orientaciones, prueba a cambiar la propiedad flex-direction usando cada uno de los siguientes valores: row, column, row-reverse y column-reverse
<div style="display: flex; flex-direction: column; height: 100vh; justify-content: space-between;">
<div style="width: 20px; height: 20px; background-color: lightgray;"> 1 </div>
<div style="width: 20px; height: 20px; background-color: lightgray;"> 2 </div>
<div style="width: 20px; height: 20px; background-color: lightgray;"> 3 </div>
</div>Ejemplo:
order: 1; /* default is 0 */
flex-grow: 2; /* default 0 */
flex-shrink: 2; /* default 1 */
flex-basis: 100px; /* default auto */
flex: 0 1 auto; /* shorthand para grow shrink basis */
align-self: flex-end;Disposición en cuadricula
- Referencia: A Complete Guide to Grid
A diferencia de flex, en grid los ejes no cambian de dirección. Los ejes en grid son:
el eje de fila, eje horizontal o inlineel eje de columna, eje vertical o block
Podemos concebir la cuadrícula o grid de dos maneras:
- mediante líneas de cuadrícula
- mediante áreas
display: grid;
gap: 10px;
place-content: center center; /* shorthand para align-content justify-content */
grid-template-columns: 50px 50px 50px 50px;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto;
grid-template-areas:
"header header header header"
"main main . sidebar"
"footer footer footer footer";
justify-items: stretch;
align-items: stretch;Note
También existen las propiedades row-gap y column-gap de forma independiente.
Note
El orden inicial de los elementos de la cuadrícula no importa. Con CSS se pueden colocar en cualquier orden, lo que hace que sea muy fácil reorganizar la cuadrícula usando media queries. Imagina definir el diseño de toda tu página y luego reorganizarla completamente para acomodar un ancho de pantalla diferente, todo con solo un par de líneas de CSS.
Un aspecto más avanzado es la posibilidad de definir un número automático de columnas sin la necesidad de usar media query alguna. Para ello usamos la función repeat junto a la función minmax que establece un ancho mínimo y máximo para cada columna.
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));Los valores auto-fill y auto-fit deciden si debe rellenarse o no una fila que dispone de espacio sobrante para la cual no hay elementos hijo que usar.
.item-a {
grid-column-start: 2;
grid-column-end: 5;
grid-row-start: 1;
grid-row-end: 3;
}
.item-d {
grid-area: footer;
justify-self: stretch;
align-self: stretch;
}Note
Sólo hemos comentados las propiedades, tanto del contenedor como del item, más habituales. Existen muchas más propiedades, que podrás consultar en el enlace anterior.
Una de las funcionalidades más usadas es el cambio de disposición o layout de elementos en la página según la resolución de la pantalla.
No podemos decir que exista una resolución estándar para los dispositivos, pero sí existen algunos puntos de interrupción comúnmente utilizados en la programación diaria. Si está utilizando un framework CSS como Bootstrap, también puede utilizar sus puntos de interrupción.
Veamos algunos puntos de interrupción comunes para anchos de dispositivos:
- 320px — 480px: dispositivos móviles
- 481px — 768px: iPads, tabletas
- 769px — 1024px: pantallas pequeñas, portátiles
- 1025px — 1200px: escritorios, pantallas grandes
- 1201px y más — Pantallas extra grandes, TV
Estos puntos de interrupción pueden diferir y no existe un estándar exactamente definido, pero estos son algunos de los que se usan comúnmente.
Existen 2 formas de especificar el punto de interrupción:
- Usando las propiedades
min-widthymax-width - Usando la propiedad
widthy algún comparador como>=,>,<=,<
/* Estas 2 media queries significan lo mismo */
@media (max-width: 640px) {
/* … */
}
@media (width <= 640px) {
/* … */
}/* Estas 2 media queries significan lo mismo */
@media (min-width: 640px) and (max-width: 1024px) {
/* … */
}
@media (640px <= width <= 1024px) {
/* … */
}En cuanto a las estrategias disponibles a la hora de abordar el diseño responsive, disponemos de:
- Mobile first
- Desktop first
Diseño responsive a partir del diseño más pequeño (el de móvil)
Si seguimos esta estrategia, diseñaremos primero para dispositivos móviles e iremos escalando hacia arriba en los distitos puntos de interrupción.
A continuación se muestra un ejemplo:
/*
Pantallas muy pequeñas (móviles en portrait de menos de 576px)
No hace falta media-query porque será nuestro diseño por defecto
*/
/* Pantallas pequeñas (móviles en landscape de más de 576px) */
@media (min-width: 576px) { ... }
/* Pantallas medianas (tablets de más de 768px) */
@media (min-width: 768px) { ... }
/* Pantallas grandes (desktops de más de 992px) */
@media (min-width: 992px) { ... }
/* Pantallas muy grandes (desktops de más de 1200px) */
@media (min-width: 1200px) { ... }Diseño responsive a partir del diseño más grande (el de escritorio)
Si seguimos esta estrategia, diseñaremos primero para dispositivos con pantalla grande e iremos escalando hacia abajo en los distitos puntos de interrupción.
A continuación se muestra un ejemplo:
/*
Pantallas muy grandes (desktops de más de 1200px de ancho)
No hace falta media-query porque será nuestro diseño por defecto
*/
/* Pantallas grandes (desktops de menos de 1200px) */
@media (max-width: 1199px) { ... }
/* Pantallas medianas (tablets de menos de 992px) */
@media (max-width: 991px) { ... }
/* Pantallas pequeñas (móviles en landscape de menos de 768px) */
@media (max-width: 767px) { ... }
/* Pantallas muy pequeñas (móviles en portrait de menos de 576px) */
@media (max-width: 575px) { ... }Las consultas de tamaño con @container son relativamente nuevas y proporcionan una forma de consultar el tamaño de un contenedor y aplicar CSS condicionalmente al contenido de ese contenedor, tanto sus hijos como otros descendientes.
.contenedor {
container-type: inline-size;
container-name: tarjeta; /* nombre del container */
resize: horizontal; /* esto es opcional */
}
.contenido {
/* estilos css */
}
@container tarjeta (width > 480px) {
.contenido {
/* otros estilos css */
}
}Con la propiedad container-type indicamos el contexto de contención, que nos permite indicar el tamaño a monitorizar. Tiene 2 valores:
inline-size, para el tamaño horizontalsize, para el tamaño horizontal y vertical
La propiedad resize nos permite redimensionar con el ratón un elemento.
- Referencia: Video acerca de container query
A continuación se comentan brevemente algunos detalles que podemos afinar para una mejor experiencia del usuario.
En algunas situaciones, sobre todo si tenemos el mismo fondo fijo en distintas páginas, puede observarse que éste se desplaza ligeramente a la izquierda al aparecer la barra de scroll en páginas con mucho contenido y vuelve a la derecha al desaparecer esta barra en páginas con poco contenido. Para evitar este molesto efecto usamos la propiedad scrollbar-gutter dentro de la etiqueta html.
html { scrollbar-gutter: stable; }
En algunas páginas y aplicaciones web disponemos de un menú en la parte superior con enlaces a diferentes secciones de la página. Podemos conseguir que, cuando el usuario pulse en uno de estos enlaces, se produzca un desplazamiento suave a la sección correspondiente, en lugar del desplazamiento inmediato que es el comportamiento por defecto. Para ello usaremos la propiedad scroll-behavior con el valor smooth.
body { scroll-behavior: smooth; }
Cuando esta propiedad se especifica en el pseudoelemento :root, se aplica a la ventana gráfica. Si se especifica en el elemento body, no se propaga a la ventana gráfica.
En ciertas circunstancias, la propiedad resize resulta de gran utilidad. Esta propiedad nos permite definir si un elemento va a permitir el redimiensionado o no. Los valores pueden ser none, horizontal, vertical o both.
Aunque en la mayoría de los casos se emplea con elementos textarea,
textarea {
resize: none;
}es también posible emplearlo en otro tipo de elementos, como div, p, etc
p {
overflow: auto; /* recomendable para evitar desbordamiento del texto */
resize: both;
border: 1px solid black;
}En ciertos casos puede ser útil recortar una línea de texto, quizás porque la información que contiene es una previsualización. Para ello podemos usar la propiedad text-overflow con los valores ellipsis o clip.
Esta propiedad requiere también el uso de white-space: nowrap y overflow: hidden.
p {
text-overflow: ellipsis;
/* Las 2 propiedades siguientes son necesarias para text-overflow */
white-space: nowrap;
overflow: hidden;
border: 1px solid black;
}La accesibilidad busca que todas las personas (incluyendo con discapacidad visual, auditiva, motriz o cognitiva) puedan percibir, comprender, navegar e interactuar con un sitio web.
Se apoya en las normas WCAG 2.1 y el estándar WAI-ARIA.
Principios básicos (POUR):
- Perceptible: la información debe presentarse de manera que todos puedan percibirla.
- Operable: la interfaz debe poder usarse con diferentes dispositivos (ratón, teclado, lector de pantalla).
- Comprensible: el contenido y la navegación deben ser claros y predecibles.
- Robusto: debe funcionar con diferentes navegadores, dispositivos y tecnologías de asistencia.
Los atributos ARIA (Accessible Rich Internet Applications) mejoran la accesibilidad en aplicaciones web dinámicas.
Ejemplos comunes:
aria-label="..."→ etiqueta accesible para describir un elemento.aria-hidden="true"→ oculta un elemento a los lectores de pantalla.aria-expanded="true|false"→ indica si un menú/elemento está desplegado o no.aria-current="page"→ señala la página actual en una navegación.
<nav aria-label="Menú principal">
<ul>
<li><a href="/" aria-current="page">Inicio</a></li>
<li><a href="/servicios">Servicios</a></li>
<li><a href="/contacto">Contacto</a></li>
</ul>
</nav>El atributo role (definido en la especificación WAI-ARIA) permite describir la función o propósito de un elemento en la interfaz web, sobre todo cuando el HTML semántico por sí solo no es suficiente.
¿Por qué es importante?
- Ayuda a los lectores de pantalla y otras tecnologías asistivas a interpretar correctamente los elementos.
- Permite mejorar la semántica en interfaces dinámicas (menús, diálogos, pestañas, sliders, etc.).
- Útil cuando usamos elementos genéricos como
<div>o<span>y necesitamos darles un propósito.
Ejemplos:
- Roles de estructura o landmark
Identifican secciones importantes de la página.role="banner"→ cabecera del sitio.role="navigation"→ menú de navegación.role="main"→ contenido principal.role="contentinfo"→ pie de página.role="complementary"→ información adicional (ej. barra lateral).
<header role="banner">
<h1>Mi sitio web</h1>
</header>
<nav role="navigation">
<ul>
<li><a href="/">Inicio</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
</nav>
<main role="main">
<h2>Artículo destacado</h2>
<p>Contenido principal de la página.</p>
</main>
<footer role="contentinfo">
<p>© 2025 Mi sitio web</p>
</footer>- Roles de documento
Describen contenido estructurado.
role="article"→ artículo independiente.role="note"→ nota informativa.role="presentation"→ ignora la semántica visual (ej. tablas usadas solo para maquetar).
<article role="article">
<h2>Novedades</h2>
<p>Este es un artículo con información relevante.</p>
</article>Tip
Buenas prácticas con role
Usar primero HTML semántico. Muchos elementos ya tienen un role implícito.
<header>→role="banner"<nav>→role="navigation"<main>→role="main"<footer>→role="contentinfo"
No es necesario duplicar el atributo.
✅ Añadir role solo cuando sea necesario: Especialmente en elementos genéricos como <div> o <span> que se usan como botones, menús o pestañas.
<div role="button" tabindex="0" aria-pressed="false">
Hacer clic aquí
</div>Los atributos data-* permiten almacenar información personalizada en los elementos HTML, útil para JavaScript o CSS sin romper la semántica.
- Ejemplo de uso:
<button data-toggle="modal" data-target="#loginModal">
Iniciar sesión
</button>- Acceso con JS:
const btn = document.querySelector("button");
console.log(btn.dataset.toggle); // "modal"
console.log(btn.dataset.target); // "#loginModal"La clase sr-only (usada en frameworks como Bootstrap) oculta visualmente un elemento, pero lo mantiene accesible para lectores de pantalla.
- Ejemplo:
<label for="busqueda" class="sr-only">Buscar en la web</label>
<input type="text" id="busqueda" placeholder="Buscar...">👉 El usuario visual solo ve el campo de búsqueda, pero el lector de pantalla anuncia el texto del label.
- Usar HTML semántico: encabezados
<h1>–<h6>, listas, tablas con<thead>y<th>. - Proporcionar texto alternativo en imágenes (
alt). - Asegurar contraste suficiente entre texto y fondo.
- Hacer que todo sea navegable con teclado (evitar depender solo del ratón).
- Evitar usar solo color para transmitir información.
- Añadir descripciones ARIA en botones e iconos sin texto.
- Validar accesibilidad con herramientas como Lighthouse, WAVE o el validador de la W3C.
<header>
<h1>Portal de Cursos Online</h1>
<nav aria-label="Menú de navegación principal">
<ul>
<li><a href="/" aria-current="page">Inicio</a></li>
<li><a href="/cursos">Cursos</a></li>
<li><a href="/contacto">Contacto</a></li>
</ul>
</nav>
</header>
<main>
<h2>Buscar curso</h2>
<form>
<label for="busqueda" class="sr-only">Buscar curso</label>
<input type="text" id="busqueda" placeholder="Buscar..." data-type="search">
<button type="submit" aria-label="Buscar curso">🔍</button>
</form>
</main>- Juego - Flexbox Froggy
- Juego - CSS Grid Garden
- Juegos Flex Box Adventure y Grid Attack
- Generador de Grid
- MDN - CSS Layout
- MDN - Flexbox
- MDN - Grid
- MDN - Media queries
- Flexbox: flex-grow, flex-shrink y flex-basis
- A Complete Guide to Flexbox
- A Complete Guide to Grid
- Media queries
- Flexbox Cheatshet
- W3C Web Accessibility Initiative (WAI)
- WCAG 2.1 de un vistazo
- Vídeo: Menú lateral responsive con iconos Material Symbols Outlined
- Extensión para navegador de Web Accessibility Evaluation Tool



