Tienes un panel con barra lateral y un contenido principal, todo con Flexbox. Pones dentro una tabla ancha y, en lugar de aparecer una barra de desplazamiento horizontal en la tabla, ocurre esto:
- Toda la página se ensancha y aparece un scroll horizontal en el navegador, no en la tabla.
- Aparece además un segundo scroll vertical fuera del contenido: al bajar se mueven la barra lateral y la cabecera, y debajo queda una zona en blanco.
- En un modal con altura limitada, las filas de una lista se aplastan hasta quedar de unos pocos píxeles.
Esta es una estructura típica que produce el problema:
<div class="flex h-screen">
<aside class="hidden w-56 shrink-0 bg-slate-900 p-4 text-white md:block">Menú</aside>
<div class="flex flex-1 flex-col">
<header class="h-14 shrink-0 border-b px-4">Cabecera</header>
<main class="flex-1 p-4">
<div class="overflow-y-auto">
<div class="overflow-x-auto">
<table class="w-[2600px] table-fixed text-sm">...</table>
</div>
</div>
</main>
</div>
</div>
Al medirla en el navegador con una ventana de 1366 × 768 px, estos fueron los valores:
columna de contenido: 2632 px de ancho (la tabla mide 2600 px)
página: scrollWidth 2856, clientWidth 1351
contenedor de la tabla: scrollWidth 2600 = clientWidth 2600 -> nunca hay barra
Los tres síntomas tienen una raíz común en cómo Flexbox calcula el tamaño mínimo de sus elementos.
Por qué pasa
Los elementos hijos de un contenedor flex tienen por defecto min-width: auto y min-height: auto. Según MDN, para un elemento flex ese mínimo automático es el tamaño mínimo de su contenido (min-content), salvo que el elemento sea un contenedor de scroll: en ese caso el mínimo automático es 0. La especificación (CSS Flexible Box Layout, sección "Automatic Minimum Size of Flex Items") lo expresa igual: el mínimo automático es el tamaño mínimo basado en el contenido y, para los contenedores de scroll, cero.
Esto explica los tres síntomas:
- La tabla ensancha todo. La columna de contenido (
flex-1) no es un contenedor de scroll, así que no puede ser más estrecha que el contenido más ancho que lleve dentro: la tabla de 2600 px. El contenedor conoverflow-x: autoque envuelve la tabla recibe un ancho de 2600 px, no desborda y por eso no muestra barra. Es la columna la que desborda a la página. - El segundo scroll vertical. Si el
<main>es un elemento flex en columna y el scroll lo tiene un elemento interior (o ninguno), su altura mínima automática es la del contenido:<main>crece y empuja todo. Con un contenedor de altura100vh, el desbordamiento lo gestiona la página, y la barra lateral (que mide lo mismo que el contenedor) se desplaza con ella. - Las filas aplastadas.
flex-shrinkvale1por defecto (MDN), así que los elementos se encogen para no desbordar el contenedor. Normalmentemin-height: autoimpide que bajen del tamaño de su contenido, pero una fila conoverflow: hidden(por ejemplo, para recortar texto contruncate) es un contenedor de scroll, su mínimo automático es0y se encoge hasta quedarse en su relleno (padding). Conpy-2eso son 16 px.
Solución
-
Estructura del layout con Tailwind. Este es el esqueleto completo (puedes ponerlo en el
layout.tsxde tu panel en Next.js):<div class="flex h-dvh overflow-hidden"> <aside class="hidden w-56 shrink-0 bg-slate-900 p-4 text-white md:block">Menú</aside> <div class="flex min-w-0 flex-1 flex-col"> <header class="h-14 shrink-0 border-b px-4">Cabecera</header> <main class="min-h-0 flex-1 overflow-y-auto p-4"> <div class="overflow-x-auto"> <table class="w-[2600px] table-fixed text-sm"> <!-- filas --> </table> </div> </main> </div> </div>h-dvh overflow-hiddenen el contenedor raíz: la página ocupa exactamente la ventana y nunca tiene scroll propio.h-dvhequivale aheight: 100dvh(Tailwind lo documenta como la altura del viewport que cambia cuando se expande o se contrae la interfaz del navegador); si tu versión no lo incluye, usah-screen.min-w-0en el hijo flex que contiene la tabla: devuelve el mínimo a0y deja que la columna se ajuste al espacio disponible.min-h-0 flex-1 overflow-y-autoen<main>: es el único elemento con scroll vertical.overflow-x-autoen el contenedor directo de la tabla: es el único elemento con scroll horizontal. Un contenedor de scroll por eje y por tabla; no repitasoverflowen los ancestros.shrink-0en la barra lateral y la cabecera, para que no se encojan.
-
El mismo layout en CSS puro:
.shell { display: flex; height: 100dvh; overflow: hidden; } .sidebar { width: 14rem; flex-shrink: 0; } .content { display: flex; flex-direction: column; flex: 1; min-width: 0; } .header { flex-shrink: 0; height: 3.5rem; } .main { flex: 1; min-height: 0; overflow-y: auto; padding: 1rem; } .table-scroll { overflow-x: auto; } .table-scroll table { width: 2600px; }En Tailwind 4.3,
min-w-0generamin-width: 0,min-h-0generamin-height: 0,shrink-0generaflex-shrink: 0yh-dvhgeneraheight: 100dvh. -
Listas con scroll dentro de un modal o un panel de altura limitada: evita que las filas se encojan.
<div class="flex max-h-72 w-80 flex-col rounded border bg-white"> <div class="shrink-0 border-b p-3">Buscar</div> <ul class="flex flex-col overflow-y-auto"> <li class="shrink-0 overflow-hidden px-3 py-2">Resultado 1</li> <!-- más filas --> </ul> </div>En CSS:
li { flex-shrink: 0; }. Así la lista conserva la altura de cada fila y desborda, y el scroll aparece en el<ul>.
Cómo verificar que quedó resuelto
En la consola del navegador, con la vista que falla abierta:
const s = document.scrollingElement;
({ paginaScrollX: s.scrollWidth > s.clientWidth, paginaScrollY: s.scrollHeight > s.clientHeight })
Ambos valores deben ser false. Después comprueba que el único scroll vertical está en <main> (main.scrollHeight > main.clientHeight) y que el contenedor de la tabla desborda por dentro (wrapper.scrollWidth > wrapper.clientWidth). Repite con el ancho de un móvil.
En la prueba, con ventanas de 1366 × 768 y de 375 × 812 px (Chromium), la versión que falla tenía scroll de página en ambos ejes y la columna medía 2632 px. Con el arreglo, la página no tenía scroll en ningún eje, <main> era el único con scroll vertical y la tabla de 2600 px se desplazaba dentro de su contenedor (1095 px visibles en escritorio, 328 px en móvil). En el modal, las filas pasaron de 16 px a su altura natural (entre 40 y 64 px, según si el texto ocupaba una o dos líneas).
Variantes y errores relacionados
overflow-hidden en el contenedor raíz sin min-w-0
Si pones overflow-hidden en el contenedor raíz pero olvidas min-w-0, el scroll de página desaparece y el problema queda oculto: en la prueba la columna seguía midiendo 2647 px, el contenedor de la tabla no mostraba barra y la tabla quedaba recortada por la ventana. Si ves una tabla cortada sin forma de desplazarla, revisa min-w-0.
¿Hace falta min-h-0 si ya hay overflow-y-auto?
Si <main> es el propio contenedor de scroll, no: su mínimo automático ya es 0 (en la prueba funcionó igual sin min-h-0). Sí hace falta cuando el scroll lo tiene un elemento interior, por ejemplo un <div class="h-full overflow-y-auto"> dentro de <main>: sin min-h-0, <main> creció hasta el alto de su contenido (2304 px); con min-h-0 se quedó en el espacio disponible. Dejar ambas clases es inocuo y evita depender de ese detalle.
Lo mismo en CSS Grid
MDN indica que el mínimo automático también afecta a los elementos de una rejilla. En la prueba, con grid-template-columns: 14rem 1fr la columna medía 2600 px y la página desbordaba; con 14rem minmax(0, 1fr) la columna medía 1142 px y la tabla se desplazaba dentro de su contenedor. En Tailwind: grid-cols-[14rem_minmax(0,1fr)].
Texto largo sin espacios en un elemento flex
Una URL o un identificador largo sin espacios puede ensanchar un elemento flex por el mismo motivo. En la prueba, con solo overflow-wrap: break-word (clase break-words en Tailwind) la columna siguió desbordando la página en un ancho de 375 px; al añadir min-w-0 a ese elemento el texto se partió y la página dejó de desbordar. Si prefieres cortar el texto con puntos suspensivos, usa truncate junto con min-w-0.
Resumen
- Los elementos flex no se encogen por debajo del tamaño de su contenido (
min-width: autoymin-height: auto), salvo que sean contenedores de scroll. - Una tabla ancha estira la columna de contenido; pon
min-w-0en el hijo flex que la contiene yoverflow-x-autoen su envoltorio directo. - Evita el segundo scroll vertical con un contenedor raíz
h-dvh overflow-hiddenymin-h-0 flex-1 overflow-y-autoen<main>. - Las filas de una lista con scroll necesitan
shrink-0para no aplastarse cuando el contenedor tiene altura limitada. - Verifica con
scrollWidthyclientWidthde la página: ambos ejes sin scroll de página y un solo contenedor de scroll por eje.
Comentarios
Publicar un comentario