Tablas responsivas para documentación técnica que no fallan en producción

¿Te fue útil?

Publicas una referencia de API con una tabla ordenada. Se ve perfecta en tu monitor de 32 pulgadas. Luego soporte envía una captura de pantalla desde un teléfono donde la tabla
se ha escapado de la página como un gato callejero, empujando la navegación fuera de la pantalla y dejando los botones “Copy” inservibles.

Las tablas son el modo silencioso de fallo en la documentación técnica: funcionan hasta que no, y cuando dejan de hacerlo arrastran el resto del diseño con ellas.
Esta es una forma práctica y de nivel operativo para construir tablas responsivas: un contenedor de desplazamiento que se comporte, cabeceras fijas que no parpadeen y celdas de código que sigan siendo legibles.

Principios innegociables: qué significa realmente “tabla responsiva”

“Tabla responsiva” no es “hacerla más pequeña hasta que quepa.” En la documentación técnica, las tablas contienen información densa y de alto riesgo:
parámetros, valores por defecto, matrices de compatibilidad, códigos de salida, límites de almacenamiento y el tipo de notas al pie que evitan páginas de fin de semana.
Tu trabajo es mantener la tabla veraz y usable en distintos tamaños de pantalla y métodos de entrada.

Principio 1: No conviertas la verdad en ficción

Muchas recetas de “tabla responsiva” colapsan columnas en tarjetas apiladas. Eso puede funcionar en marketing.
En docs, a menudo convierte comparaciones en una búsqueda del tesoro. Si el lector necesita comparar valores entre columnas, preserva la cuadrícula.
El desplazamiento horizontal es aceptable cuando es intencional y no rompe el resto.

Principio 2: La tabla no puede escapar de su contenedor

El modo de fallo principal es el overflow: cadenas ininterrumpidas (UUIDs, base64, SHA256, rutas de archivo, comandos) obligan a la tabla a ser más ancha que la ventana.
Una vez que eso ocurre, tu diseño se vuelve “creativo”. Arréglalo a nivel de contenedor y a nivel de celda. Ambos.

Principio 3: Las cabeceras fijas deben mantenerse alineadas con las columnas

Las cabeceras fijas son una característica de productividad—hasta que se desalinean por anchos inconsistentes, modelos de borde o contextos de desplazamiento anidados.
Una cabecera fija que no coincide con su columna es peor que no tener cabecera fija. Convierte la lectura en adivinanza.

Principio 4: Las celdas de código no son prosa

El código inline es a menudo largo, copiado y pegado, y leído por escaneo. Necesita monoespacio, reglas de salto sensatas y un fondo que funcione en modo oscuro.
“Simplemente dejar que se ajuste” no es una estrategia; es la forma de obtener tokens rotos (y despliegues fallidos).

Principio 5: Si no es accesible por teclado, no está terminado

Los contenedores de desplazamiento pueden atrapar el foco, las cabeceras fijas pueden superponer los contornos de foco y los botones de copiar pueden volverse inalcanzables.
Asegúrate de que exista un estado de foco visible y una experiencia de desplazamiento estable para táctil y teclado.

Una cita que uso cuando la gente intenta “publicarlo y ver”: La esperanza no es una estrategia. — General Gordon R. Sullivan

Broma #1: Las cabeceras fijas son como las rotaciones de guardia: geniales hasta que se desalinean, y luego todos discuten de quién fue la culpa.

Algunos hechos e historia (porque la web tiene pruebas)

  • Las tablas HTML son anteriores a los layouts con CSS y se usaban para maquetar páginas en los 90; la reacción es la razón por la que el estilo de tablas sigue siendo quisquilloso en casos límite.
  • position: sticky fue diseñado para reducir handlers de scroll en JavaScript—una ganancia de rendimiento que se volvió esencial para cabeceras fijas de tablas y barras laterales.
  • Los navegadores móviles históricamente manejaron overflow y encadenamiento de scroll diferente, por eso “funciona en escritorio” no implicaba “funciona en móvil”.
  • Los tokens largos se hicieron comunes en docs por la infraestructura moderna: digests de contenedor, IDs de recursos en la nube, JWTs, checksums e IDs de trazas están pensados para ser cadenas no separadas.
  • Los patrones de UI para copiar al portapapeles se popularizaron en docs con Rise de herramientas DevOps; ahora se esperan para comandos y fragmentos de configuración—incluso dentro de tablas.
  • El truco de table-layout: fixed es anterior a muchos sistemas de diseño, y sigue siendo una de las palancas más útiles para anchos de columna predecibles bajo estrés.
  • CLS (Cumulative Layout Shift) se convirtió en métrica formal con Core Web Vitals; las tablas con fuentes web que cargan tarde y contenido diferido son frecuentes causantes de CLS.
  • Muchos sitios de documentación son builds estáticos (SSG), pero las tablas aún pueden “renderizar lento” por hidratación del cliente, resaltado de sintaxis y CSS pesado.

Patrón básico: contenedor de desplazamiento + tabla resistente

La opción más segura por defecto para tablas en documentación técnica es aburrida:
envuelve la tabla en un contenedor de desplazamiento que gestione el overflow horizontal, mantiene el ancho de la tabla estable y aplica reglas de ajuste a nivel de celda
para evitar que un solo token detone el diseño.

Estructura HTML que puedes desplegar

El envoltorio no es opcional. Poner overflow-x: auto directamente en la tabla es poco fiable y puede romper cabeceras fijas y el dimensionado.
Haz que el envoltorio sea el contenedor de desplazamiento; mantén la tabla como tabla.

cr0x@server:~$ cat responsive-table.html
<div class="table-wrap" role="region" aria-label="API parameters table">
  <table class="doc-table">
    <thead>
      <tr>
        <th scope="col">Parameter</th>
        <th scope="col">Type</th>
        <th scope="col">Default</th>
        <th scope="col">Example</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">timeout</th>
        <td>integer</td>
        <td>30</td>
        <td><code>timeout=60</code></td>
      </tr>
    </tbody>
  </table>
</div>

CSS que se comporta bajo presión

El objetivo es estabilidad: dimensionado de columnas predecible, overflow limpio y un contenedor de desplazamiento que no secuestre la página.
También: affordance visible. Si los usuarios no se dan cuenta de que la tabla se desplaza, asumen que la doc está rota.

cr0x@server:~$ cat responsive-table.css
.table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
  border-radius: 10px;
  background: Canvas;
  max-width: 100%;
}

.doc-table {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  min-width: 720px;
  table-layout: fixed;
}

.doc-table th,
.doc-table td {
  padding: 0.75rem 0.9rem;
  vertical-align: top;
  border-bottom: 1px solid color-mix(in srgb, CanvasText 15%, transparent);
}

.doc-table thead th {
  background: color-mix(in srgb, Canvas 92%, CanvasText 8%);
  font-weight: 650;
}

.doc-table tbody tr:hover td,
.doc-table tbody tr:hover th[scope="row"] {
  background: color-mix(in srgb, Canvas 94%, CanvasText 6%);
}

.doc-table th[scope="row"] {
  font-weight: 600;
  text-align: left;
}

.doc-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}

.doc-table code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  font-size: 0.95em;
  background: color-mix(in srgb, Canvas 88%, CanvasText 12%);
  padding: 0.12em 0.35em;
  border-radius: 6px;
  white-space: normal;
  overflow-wrap: anywhere;
}

Decisiones clave que deberías tomar deliberadamente:

  • min-width en la tabla evita que “todo se comprima en una sopa ilegible”. En pantallas pequeñas, el envoltorio desplaza en su lugar.
  • table-layout: fixed reduce el coste de reflujo y mantiene los anchos de columna predecibles. También hace que la elipsis funcione de manera consistente.
  • Política de overflow por celda está dividida: la prosa normal puede ajustarse; el código y los identificadores deben usar overflow-wrap: anywhere y nunca forzar ancho.
  • La elipsis es una herramienta, no una mentira. Úsala cuando una columna sea secundaria y ofrezcas una forma de ver el valor completo (atributo title, expandible o control de copia).

Cabeceras fijas que no parpadeen, manchen ni engañen

Las cabeceras fijas valen la pena para tablas largas—matrices de compatibilidad, inventarios de parámetros, listas de códigos de error.
Pero las cabeceras fijas tienen tres problemas clásicos: (1) dejan de fijarse porque el ancestro equivocado hace scroll,
(2) se superponen al contenido y ocultan el foco, o (3) se desalinean con las columnas.

Haz que el envoltorio sea el contenedor de desplazamiento, luego fija dentro de él

position: sticky se fija respecto al ancestro desplazable más cercano. Si pones overflow en algún padre que olvidaste,
tu cabecera se fijará al elemento equivocado—o no se fijará.

cr0x@server:~$ cat sticky-header.css
.table-wrap {
  max-height: 60vh;
  overflow: auto;
}

.doc-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
}

.doc-table thead th::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: color-mix(in srgb, CanvasText 18%, transparent);
}

.doc-table thead th {
  box-shadow: 0 2px 0 color-mix(in srgb, CanvasText 10%, transparent);
}

El z-index y una sombra sutil no son decoración. Son claridad. Sin ellos, los usuarios no pueden decir dónde termina la cabecera,
especialmente en modo oscuro y cuando el fondo de la cabecera está cerca del fondo del cuerpo.

Alineación de cabeceras fijas: evita anchos fraccionarios y caos de bordes

La desalineación suele venir de mezclar modos de border-collapse, elementos anidados con box-sizing distinto, o contenido que provoca cambio de tamaño de columnas.
Si quieres cabeceras fijas, quieres anchos estables. Aquí es donde table-layout: fixed demuestra su valía.

Si tu primera columna es una “nombre de parámetro” y debe seguir siendo legible, fíjala con un ancho:

cr0x@server:~$ cat column-widths.css
.doc-table th[scope="row"] {
  width: 14rem;
}

.doc-table td:nth-child(2) {
  width: 10rem;
}

.doc-table td:nth-child(3) {
  width: 8rem;
}

Primera columna fija: es posible, pero no lo hagas a la ligera

Las primeras columnas fijas son tentadoras para tablas anchas. También introducen solapamientos y requieren pintura de fondo, capas de z-index,
y manejo cuidadoso de bordes. Si la necesitas, impléméntala—pero trátala como una característica con plan de pruebas, no como un truco CSS lindo.

cr0x@server:~$ cat sticky-first-column.css
.doc-table th[scope="row"] {
  position: sticky;
  left: 0;
  z-index: 1;
  background: Canvas;
}

.doc-table thead th:first-child {
  left: 0;
  z-index: 3;
  background: color-mix(in srgb, Canvas 92%, CanvasText 8%);
}

Esa última regla (la celda superior izquierda obtiene el z-index más alto) es el impuesto “evitar artefacto extraño de solapamiento”.
Págalo por adelantado. Pagarás más tarde si no lo haces.

Celdas de código legibles: tokens largos, posibilidad de copiar y saltos de línea

Las tablas en docs contienen frecuentemente: comandos, flags, fragmentos JSON, variables de entorno, digests, regexes y rutas.
Esos valores no están pensados para leerse como un párrafo; están pensados para copiarse, compararse y escanearse.
Una celda de código que se ajusta al azar puede crear corrupción invisible. Así llegan los tickets de “funcionaba en staging”.

Decide: ¿envolver, desplazar o recortar para código?

Tienes tres opciones sensatas para valores tipo código:

  • Envolver en cualquier parte para identificadores donde los saltos de línea no cambian el significado (IDs de recursos, hashes). Esto preserva el diseño.
  • Desplazamiento horizontal dentro de la celda para comandos y configuraciones donde los saltos pueden confundir. Esto preserva la copia y la semántica.
  • Recortar con elipsis cuando el valor es secundario y existe una affordance para “copiar el valor completo”.

Lo que no debes hacer: permitir que la tabla crezca más allá de su envoltorio porque un token se niega a ajustarse.
Eso no es “responsivo”, es “hostil”.

Patrón: código desplazable dentro de una celda

Un enfoque simple: renderizar el código en un elemento tipo bloque dentro de la celda con su propio desplazamiento.
Esto evita forzar que toda la tabla se desplace solo porque una celda es larga.

cr0x@server:~$ cat code-cell.css
.doc-table td .cell-code {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  white-space: nowrap;
  padding: 0.35rem 0.5rem;
  border-radius: 8px;
  background: color-mix(in srgb, Canvas 88%, CanvasText 12%);
  border: 1px solid color-mix(in srgb, CanvasText 12%, transparent);
}

.doc-table td .cell-code code {
  white-space: inherit;
  background: transparent;
  padding: 0;
}

Patrón: preservar la capacidad de copiar y mostrar intención

Si renderizas saltos de línea en una celda de comandos, los usuarios copiarán los saltos. Luego pegarán en una shell, y la shell hará cosas.
No siempre las que querían.

Usa white-space: nowrap para comandos de una sola línea; usa saltos explícitos solo cuando muestres un script multi-línea y quieras que se copie así.

Evita bugs de “parece un signo menos” en el código

Algunas fuentes renderizan hyphen-minus, en-dash y em-dash de forma distinta. Si tu pipeline de docs “embellece” la puntuación,
puedes terminar con flags como —help que se ven bien pero no funcionan. La solución es mayormente editorial (desactivar puntuación inteligente en código),
pero el renderizado también importa: mantén el código en elementos code para que los motores tipográficos no se vuelvan creativos.

Broma #2: Lo único más peligroso que una cadena larga sin romper es la misma cadena envuelta justo en el carácter equivocado.

Accesibilidad y UX: no castigues a los usuarios de teclado

Un contenedor de desplazamiento es un pequeño modelo de interacción. Si no lo diseñas, los usuarios lo descubrirán por las malas.
Eso significa: descubribilidad, comportamiento del foco, contraste legible y desplazamiento predecible.

Da al envoltorio un role y una etiqueta

Si la tabla desborda, los usuarios con tecnologías de asistencia deberían entender que están dentro de una región con su propio desplazamiento.
Etiquétala: “API parameters table”, “Error codes table”, no “tabla” (ya saben que es una tabla).

Foco visible: no dejes que las cabeceras fijas lo oculten

Las cabeceras fijas pueden cubrir el contenido enfocado cuando un usuario navega por tabs en enlaces dentro de celdas.
Proporciona suficiente padding en la parte superior de la región de desplazamiento, o asegúrate de que los elementos enfocados se desplacen a la vista con margen.

cr0x@server:~$ cat focus.css
.table-wrap {
  scroll-padding-top: 3rem;
}

.doc-table a:focus-visible,
.doc-table button:focus-visible,
.doc-table code:focus-visible {
  outline: 2px solid color-mix(in srgb, Highlight 80%, CanvasText 20%);
  outline-offset: 2px;
}

No dependas del hover para transmitir significado

El striping por hover es agradable en escritorio. No hace nada en táctil y poco para usuarios de teclado.
Usa bordes consistentes, rayado alternado si es necesario, y mantiene los encabezados de fila/columna explícitos (scope attributes).

Respeta reduced motion y evita el scroll-jank

Las cabeceras fijas y las sombras están bien. El parallaxe dentro de un envoltorio de tabla no lo está.
Cualquier animación ligada al scroll se convierte en “mi teléfono está caliente y la tabla aún no se desplaza”.

Rendimiento y modos de fallo: cuando las tablas se vuelven un incidente de renderizado

Las tablas son engañosamente caras. El navegador debe calcular anchos de columna entre filas, pintar bordes, manejar contextos de apilamiento para sticky,
y reflow cuando cargan fuentes o cuando scripts del cliente “mejoran” el marcado.
Si tu plataforma de docs hace hidratación en cliente, una tabla grande puede causar una pausa notable en el hilo principal.

Qué suele perjudicar

  • Tablas enormes con cientos de filas y contenido complejo en celdas (iconos, bloques anidados, resaltado de sintaxis).
  • Fuentes web que cargan tarde y cambian métricas y fuerzan relayout, especialmente con position: sticky.
  • “Plugins” de tabla en JavaScript que reescriben el DOM, miden anchos en scroll o adjuntan listeners sin throttling.
  • Sombreados pesados y filtros aplicados a muchas celdas.
  • Botones de copiar por celda renderizados como controles interactivos en cada fila sin virtualización.

Qué suele ayudar

  • Mantén el HTML simple. Las tablas ya tienen reglas de layout complejas; no anides dashboards interactivos dentro de ellas.
  • Usa table-layout: fixed para dimensionado predecible y menos recálculos de layout.
  • Prefiere sticky en CSS sobre JS. Los handlers de scroll son la forma de convertir una “página de docs” en “minero de crypto”.
  • Sé intencional con el resaltado de sintaxis. Si resaltas miles de tokens dentro de tablas en cliente, eliges dolor.
  • Limita la altura para tablas monstruo para que el scroll de la página siga siendo sensato y el scroll de la tabla se mantenga localizado.

Si tu sitio de docs tiene un presupuesto de rendimiento (debería), las tablas forman parte de él. Nadie abre un incidente porque “la lista de parámetros es lenta”,
pero sí lo hacen porque el tren de lanzamiento se retrasó cuando la gente no pudo leer la documentación.

Tres micro-historias corporativas desde las trincheras

Incidente: una suposición errónea sobre “los usuarios móviles no leen tablas”

Un equipo de plataforma interno lanzó un nuevo tema de docs con una “simplificación móvil”. En pantallas por debajo de un breakpoint, las tablas se convertían en tarjetas apiladas.
Cada fila se volvía una tarjeta; cada columna, un par etiqueta/valor. Se veía ordenado. Producto lo aprobó. Nadie intentó comparar dos columnas lado a lado.

Los primeros usuarios reales fueron ingenieros de guardia consultando una matriz de compatibilidad durante una actualización.
Necesitaban comparar “client version”, “server version” y “supported cipher suites”. La vista de tarjetas requería desplazamiento interminable y memoria.
La gente empezó a enviar capturas de pantalla de la versión de escritorio desde sus portátiles al teléfono porque era más rápido que la vista móvil.

Luego llegó la falla de segundo orden: el convertidor de tarjetas eliminó scope="row" y aplanó el HTML.
Los usuarios de lector de pantalla recibieron valores sin etiqueta. El equipo se enteró no por una auditoría de accesibilidad, sino por una escalada de soporte.

La solución fue contundente y correcta: preservar la cuadrícula de la tabla en móvil usando un contenedor de desplazamiento, añadir una affordance visible de “Deslizar horizontalmente”,
y mantener la cabecera fija. La conversión a tarjetas quedó solo para un subconjunto pequeño de tablas marcadas explícitamente como “no comparativas”.

Optimización que salió mal: “vamos a autoajustar columnas con JavaScript”

Un equipo de docs quería anchos de columna perfectos: “Type” estrecha, “Description” más amplia y “Example” lo justo. Implementaron un script que medía
la celda más ancha por columna después del render, y luego fijaba anchos explícitos en las celdas de cabecera. Funcionaba en sus páginas de prueba.

En producción, el sitio de docs usaba navegación del lado del cliente y carga diferida de fuentes. Cada navegación desencadenaba re-medición.
Cuando la fuente cambiaba, los anchos variaban de nuevo. Las cabeceras fijas ahora temblaban porque los anchos se estaban actualizando a mitad de desplazamiento.
En dispositivos de gama baja, el bucle de medición causó jank notable—justo donde quieres un desplazamiento suave.

Un bug particularmente feo apareció cuando las celdas de código contenían bloques desplazables horizontalmente.
El script midió el scrollWidth, no el ancho visible, y expandió columnas hasta la longitud completa del comando.
El envoltorio dejó de contener el overflow. La tabla “se escapó”, otra vez.

La corrección fue deliciosamente poco sexy: eliminar el dimensionado por JavaScript, adoptar table-layout: fixed, establecer unos pocos anchos de columna,
y usar elipsis + copia para ejemplos extremadamente largos. La perfección se cambió por predictibilidad, y todos durmieron mejor.

Práctica aburrida pero correcta que salvó el día: probar tablas con contenido worst-case

Otra organización ejecutaba docs como parte de un producto regulado. Su proceso de revisión no era glamuroso, pero sí disciplinado.
Cada cambio mayor de tema tenía una “página de tortura de tablas”: identificadores enormes, JSON multi-línea, rutas largas, escenarios de puntuación tipo RTL,
y scripts mixtos. Nada sofisticado. Solo lo peor que habían visto en producción.

Durante un rediseño, una nueva regla CSS puso white-space: nowrap en todos los td para “mantener las cosas ordenadas.”
La página de tortura mostró inmediatamente overflow horizontal en todo el sitio, no solo dentro de los envoltorios de tabla.
La regresión se capturó antes de mergear.

También tenían una comprobación de accesibilidad: tabular por la región de la tabla hasta alcanzar el último enlace de la última fila.
Si el foco desaparecía detrás de la cabecera fija, el cambio no se publicaba.

La práctica aburrida—mantener una página worst-case y tratarla como suite de pruebas—evitó un despliegue que habría roto cada tabla de referencia de API.
Nadie escribió un postmortem porque nada se rompió. Ese es el mejor tipo de trabajo de fiabilidad.

Guía de diagnóstico rápido: qué comprobar primero/segundo/tercero

Cuando una tabla está “rota” (desbordando, lenta, desalineada, ilegible), quieres evitar el whack-a-mole aleatorio de CSS.
Ejecuta esto en orden. Está diseñado para encontrar el cuello de botella rápidamente.

1) Identifica el dueño del scroll

  • ¿Es el envoltorio el elemento con overflow-x/overflow?
  • ¿Algún padre está creando accidentalmente un contenedor de desplazamiento?
  • ¿La tabla es más ancha que el envoltorio por un token inquebrantable?

2) Encuentra la celda más ancha (el sospechoso habitual)

  • Busca identificadores largos, base64, JSON minificado o comandos sin cortes.
  • Comprueba si white-space: nowrap está aplicado demasiado en general.
  • Busca min-width en celdas o elementos hijos que forcen ancho.

3) Valida el comportamiento sticky en el contexto real de desplazamiento

  • Las cabeceras fijas necesitan position: sticky; top: 0 en th y un contenedor de desplazamiento definido.
  • Revisa contextos de apilamiento (z-index) para que la cabecera pinte por encima de las filas del cuerpo.
  • Confirma que el fondo de la cabecera sea opaco; de lo contrario el texto del cuerpo se filtrará durante el desplazamiento.

4) Comprueba la estabilidad del layout y el coste de renderizado

  • Si hay jank, revisa si JavaScript está midiendo anchos o reaccionando al scroll.
  • Comprueba el intercambio de fuentes (FOIT/FOUT) y si causa relayout de la tabla.
  • Revisa si el resaltado de sintaxis ocurre en cliente para cientos de celdas.

5) Confirma la usabilidad de teclado/táctil

  • ¿Puedes enfocar dentro de la región y ver el anillo de foco?
  • ¿Funciona el desplazamiento horizontal táctil sin activar gestos de navegación de la página?
  • ¿La cabecera fija cubre elementos enfocados?

Tareas prácticas: comandos, salidas y decisiones (12+)

Estas son tareas “hacer la cosa” que puedes ejecutar en un repo de docs o en un sitio construido. Cada una incluye: un comando, salida de ejemplo, lo que significa,
y la decisión que tomas. Asumen un build de sitio estático típico, con artefactos HTML/CSS/JS.

Tarea 1: Encuentra tablas que no estén envueltas (la raíz de la mayoría de los bugs de overflow)

cr0x@server:~$ rg -n "

Qué significa: Tienes múltiples variantes de tabla; algunas pueden faltar del envoltorio.

Decisión: Estandarizar en un único componente envoltorio (.table-wrap) y fallar builds cuando aparezca una tabla sin envolver en contenido.

Tarea 2: Identificar tablas sin envolver vía parsing HTML

cr0x@server:~$ python3 - <<'PY'
from bs4 import BeautifulSoup
import glob, sys
bad=[]
for f in glob.glob("public/**/*.html", recursive=True):
    html=open(f,encoding="utf-8").read()
    s=BeautifulSoup(html,"html.parser")
    for t in s.find_all("table"):
        p=t.parent
        if not (p and p.name=="div" and "table-wrap" in (p.get("class") or [])):
            bad.append((f, t.get("class")))
            break
print("unwrapped_files", len(set(x[0] for x in bad)))
for f,_ in bad[:10]:
    print(f)
PY
unwrapped_files 2
public/errors.html
public/compat.html

Qué significa: Dos páginas tienen tablas sin el contenedor de desplazamiento esperado.

Decisión: Arreglar esas plantillas/contenido ahora; de lo contrario cualquier token largo puede romper todo el layout de la página.

Tarea 3: Detectar regresiones de CSS “nowrap en todas partes”

cr0x@server:~$ rg -n "white-space:\s*nowrap" public/assets/*.css | head -n 20
public/assets/app.css:1132:.doc-table td { white-space: nowrap; }
public/assets/app.css:1180:.cell-code { white-space: nowrap; }

Qué significa: La primera regla es peligrosa; fuerza a cada celda a negarse a envolver.

Decisión: Eliminar nowrap de td genéricos; mantener nowrap solo para contenedores de código explícitos.

Tarea 4: Confirmar que existen reglas de cabecera sticky y targetean el elemento correcto

cr0x@server:~$ rg -n "position:\s*sticky" public/assets/*.css
public/assets/app.css:1202:.doc-table thead th { position: sticky; top: 0; z-index: 2; }

Qué significa: Sticky está aplicado a thead th, que es el objetivo correcto.

Decisión: Mantenerlo. Si sticky falla en tiempo de ejecución, el problema probablemente sea el ancestro de scroll o el clipping por overflow, no CSS ausente.

Tarea 5: Encontrar ancestros con overflow que rompen sticky

cr0x@server:~$ rg -n "overflow:\s*(hidden|clip)" public/assets/*.css | head
public/assets/theme.css:221:.content { overflow: hidden; }
public/assets/theme.css:418:.doc-container { overflow: clip; }

Qué significa: Reglas de overflow en contenedores externos pueden recortar cabeceras fijas o barras de scroll.

Decisión: Eliminar o limitar el clipping de overflow. Si debes recortar, asegúrate de que el envoltorio de la tabla no esté dentro del contexto de clipping.

Tarea 6: Detectar minas terrestres de tokens largos en contenido

cr0x@server:~$ rg -n "[A-Za-z0-9+/]{120,}={0,2}" content/ | head
content/auth.md:77:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....
content/trace.md:54:Zm9vYmFyYmF6cXV4cXV1eGJhc2U2NA==...

Qué significa: Existen cadenas estilo base64/JWT. Provocarán problemas en tablas a menos que permitas wrapping o desplazamiento local.

Decisión: Asegurar que las celdas de código usen overflow-wrap: anywhere o desplazamiento interno; añadir affordances de copia cuando corresponda.

Tarea 7: Verificar la política table-layout (fixed vs auto)

cr0x@server:~$ rg -n "table-layout" public/assets/*.css
public/assets/app.css:1099:.doc-table { table-layout: fixed; }
public/assets/app.css:2055:.matrix { table-layout: auto; }

Qué significa: Tienes modos de layout inconsistentes. auto puede causar reflow costoso y sorpresas de ancho.

Decisión: Para tablas de docs, por defecto usar fixed. Mantener auto solo donde realmente necesites sizing intrínseco (raro).

Tarea 8: Medir cuántas tablas existen (la escala importa para el rendimiento)

cr0x@server:~$ find public -name "*.html" -print0 | xargs -0 rg -c "

Qué significa: 47 tablas en todo el sitio construido. No es enorme, pero suficiente para que mejoras JS globales dañen.

Decisión: Evitar scripts pesados por tabla; mantener el comportamiento primero en CSS y progresivo.

Tarea 9: Detectar mejoras de tabla en cliente (plugins JS, bucles de medición)

cr0x@server:~$ rg -n "(getBoundingClientRect|offsetWidth|scrollWidth).*(table|thead|th)" public/assets/*.js | head
public/assets/app.js:8812:const w = th.getBoundingClientRect().width;
public/assets/app.js:8820:table.style.width = table.scrollWidth + "px";

Qué significa: JavaScript está midiendo anchos de cabecera y posiblemente forzando el ancho de la tabla a scrollWidth. Es una bandera roja.

Decisión: Eliminar o condicionar esa lógica. Reemplazar con dimensionado por CSS. Si la mantienes, ejecútala una vez, no en bucles de scroll/resize.

Tarea 10: Validar que las tablas permanecen dentro de la ventana en una comprobación headless

cr0x@server:~$ node - <<'NODE'
const fs = require("fs");
const { JSDOM } = require("jsdom");
const html = fs.readFileSync("public/api.html","utf8");
const dom = new JSDOM(html);
const tables = [...dom.window.document.querySelectorAll("table")];
console.log("tables", tables.length);
console.log("wrapped", [...dom.window.document.querySelectorAll(".table-wrap table")].length);
NODE
tables 3
wrapped 2

Qué significa: Una tabla en api.html no está envuelta.

Decisión: Arreglar la generación HTML. Quieres “wrapped equals tables” para tablas de docs salvo exención explícita.

Tarea 11: Revisar riesgo de CLS por carga tardía de fuentes en HTML construido

cr0x@server:~$ rg -n "rel=\"preload\" as=\"font\"" public/*.html | head
public/index.html:18:<link rel="preload" as="font" href="/assets/fonts/Inter.woff2" type="font/woff2" crossorigin>

Qué significa: Las fuentes se pre-cargan en la página índice, pero no necesariamente en las páginas de docs.

Decisión: Asegurar que las páginas de docs con tablas grandes también pre-carguen fuentes críticas, o usar una stack de fuentes con métricas estables.

Tarea 12: Auditar contraste insuficiente en fondos de cabecera de tabla

cr0x@server:~$ rg -n "thead th.*background" public/assets/*.css
public/assets/app.css:1110:.doc-table thead th { background: #f7f7f7; }
public/assets/dark.css:90:.doc-table thead th { background: #151515; }

Qué significa: Tienes colores explícitos; el contraste depende del color del texto y las superficies circundantes.

Decisión: Validar contraste en ambos temas; preferir colores del sistema (Canvas, CanvasText) o mezclas con protecciones.

Tarea 13: Detectar tablas dentro de contenedores que deshabilitan scroll horizontal

cr0x@server:~$ rg -n "\.table-wrap\s*\{[^}]*overflow-x:\s*(hidden|clip)" public/assets/*.css

Qué significa: Sin coincidencias. Bien: el envoltorio puede desplazarse.

Decisión: Mantenerlo así. Si un refactor añade clipping de overflow aquí, trátalo como regresión.

Tarea 14: Asegurar que el envoltorio de la tabla tenga una política de max-width

cr0x@server:~$ rg -n "\.table-wrap\s*\{[^}]*max-width" public/assets/*.css
public/assets/app.css:1072:.table-wrap { max-width: 100%; }

Qué significa: El envoltorio restringe el ancho al viewport/contenedor.

Decisión: Mantener max-width: 100%. Sin ello, los layouts anidados pueden estirarse inesperadamente.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: La tabla empuja toda la página hacia los lados

Causa raíz: Tabla sin envolver o faltando overflow-x: auto en el envoltorio; tokens inquebrantables o nowrap global.

Solución: Envolver tablas en un contenedor de desplazamiento dedicado. Añadir wrapping a nivel de celda para código/IDs (overflow-wrap: anywhere), y eliminar nowrap global.

2) Síntoma: La cabecera fija no se fija

Causa raíz: Elemento sticky dentro de un contexto no desplazable; ancestro con overflow: hidden/clip; o las celdas de cabecera no son los elementos sticky.

Solución: Hacer que el envoltorio de la tabla sea el contenedor de desplazamiento (overflow: auto) y poner position: sticky en thead th. Eliminar clipping de overflow de ancestros.

3) Síntoma: La cabecera fija se fija pero las columnas no se alinean

Causa raíz: Los anchos de columna cambian por sizing intrínseco; mezcla de modelos de bordes; JS midiendo y fijando anchos inconsistente.

Solución: Usar table-layout: fixed, establecer anchos explícitos para columnas clave y dejar de manipular anchos con JS.

4) Síntoma: Los comandos largos se envuelven y resultan engañosos

Causa raíz: Código renderizado con white-space: normal y sin política de desplazamiento interno; se inserta un salto suave en medio de un flag.

Solución: Usar un bloque .cell-code con white-space: nowrap y overflow-x: auto, además de un botón de copia si procede.

5) Síntoma: Copiar desde celdas de tabla produce espacios/saltos raros

Causa raíz: Los estilos insertan pseudo-elementos, saltos de línea o puntuación inteligente; elementos anidados añaden texto oculto.

Solución: Mantener el código en <code>/<pre> con mínima decoración; evitar pseudo-contenido dentro de código; desactivar sustitución tipográfica en el pipeline de renderizado de código.

6) Síntoma: En iOS, el desplazamiento horizontal compite con el scroll de la página

Causa raíz: Encadenamiento de scroll; envoltorio sin desplazamiento momentum; contenedor demasiado pequeño o anidado en otro scroller.

Solución: Añadir -webkit-overflow-scrolling: touch y overscroll-behavior-x: contain. Evitar contenedores de desplazamiento anidados cuando sea posible.

7) Síntoma: El contorno de foco desaparece detrás de la cabecera fija

Causa raíz: La cabecera fija se superpone; no hay scroll-padding; la cabecera tiene z-index alto y fondo opaco.

Solución: Añadir scroll-padding-top en el envoltorio y asegurar que los elementos enfocables tengan un estilo de foco visible.

8) Síntoma: La página se vuelve lenta al desplazar una tabla larga

Causa raíz: Handlers de scroll en JS; sombras pesadas en muchas celdas; resaltado de sintaxis/hidratación en un DOM grande.

Solución: Eliminar handlers de scroll; simplificar contenido de celdas; pre-renderizar resaltado en tiempo de build; limitar la altura del envoltorio y mantener layout fijo.

9) Síntoma: El texto de la cabecera se mezcla con el del cuerpo durante el scroll

Causa raíz: Fondo de cabecera transparente; artefactos GPU/compositado.

Solución: Usar un fondo opaco en thead th y un separador sutil (borde o sombra).

10) Síntoma: La tabla parece bien, pero los usuarios no se dan cuenta de que se desplaza

Causa raíz: Sin affordance visual; barras de scroll ocultas por el SO; el envoltorio se funde con el fondo de la página.

Solución: Añadir un borde y una ligera diferencia de fondo al envoltorio; opcionalmente añadir una pequeña pista “Desliza” para pantallas estrechas.

Listas de verificación / plan paso a paso

Paso a paso: publicar un componente de tabla responsiva

  1. Crea un único componente envoltorio de tabla que renderice <div class="table-wrap" role="region" aria-label="..."> alrededor de la tabla.
  2. Haz que el envoltorio sea el dueño del scroll: overflow-x: auto, max-width: 100%, y considera max-height para tablas largas.
  3. Usa dimensionado estable de tabla: table-layout: fixed, width: 100%, y un min-width realista para legibilidad.
  4. Implementa cabeceras fijas en thead th con top: 0, z-index adecuado y fondo opaco.
  5. Establece la política de overflow de celdas:
    • Columnas de prosa: envolver normalmente.
    • Columnas de ID/hash: overflow-wrap: anywhere.
    • Columnas de comando/config: usar un scroller anidado .cell-code con nowrap.
  6. Mantén la semántica correcta: th scope="col" y th scope="row" para encabezados de fila.
  7. Prueba de teclado: tabula dentro de la tabla, desplaza el envoltorio y asegúrate de que el foco permanezca visible bajo la cabecera fija.
  8. Prueba móvil: verifica que el swipe horizontal desplaza la tabla sin tirar de toda la página hacia los lados.
  9. Prueba de rendimiento: abre la página con la tabla más grande; asegura que el desplazamiento sea fluido y no haya jitter de re-dimensionado tras la carga de fuentes.
  10. Fijarlo: añade una comprobación en CI que rechace tablas sin envolver y reglas globales de nowrap en celdas de tabla.

Checklist de lanzamiento (la edición “no me hagas pagarte”)

  • Todas las tablas están envueltas en .table-wrap salvo exención explícita.
  • Las cabeceras fijas funcionan en el contexto de scroll del envoltorio y permanecen alineadas.
  • Los tokens largos no expanden el layout; se envuelven o desplazan dentro de las celdas.
  • Los contornos de foco son visibles; la cabecera fija no oculta elementos enfocados.
  • Los colores en modo oscuro preservan contraste para cabecera y chips de código.
  • No hay JavaScript midiendo anchos durante el scroll.
  • La “página de tortura de tablas” renderiza correctamente en un viewport estrecho.

Preguntas frecuentes

1) ¿Es aceptable el desplazamiento horizontal en documentación técnica?

Sí, cuando la tabla es comparativa y preservas la cuadrícula. Haz el contenedor de desplazamiento obvio y mantén las cabeceras fijas.
Forzar un layout de tarjetas suele destruir el valor principal de la tabla: la comparación.

2) ¿Por qué no usar una librería JS de tablas?

Porque la mayoría de las tablas de docs no necesitan ordenado, paginación o filtrado, y las librerías JS tienden a añadir peso, trabajo de reflow y bugs.
Si realmente necesitas esas funciones, impleméntalas de forma progresiva y mantén la tabla base legible sin JS.

3) ¿Debería poner overflow-x: auto en la <table>?

No. Pon el overflow en un envoltorio. Las tablas tienen un comportamiento especial de layout; las cabeceras fijas y los cálculos de ancho se comportan más predeciblemente
cuando la tabla permanece como elemento table dentro de un contenedor de desplazamiento.

4) ¿Cuál es la mejor forma de manejar comandos largos en una celda?

Usa un bloque de código desplazable interno (.cell-code) con white-space: nowrap. Si el comando es crítico, añade un botón de copia.
Evita envolver comandos a menos que estés presentando explícitamente un script multi-línea.

5) ¿Por qué table-layout: fixed? ¿No vuelve las columnas raras?

Puede, si nunca asignas anchos y metes contenido muy diverso en las columnas. Pero para docs, la predictibilidad vence a la ingeniosidad.
Establece anchos para columnas clave y deja que el resto comparta el espacio sobrante. Reduce el thrash del layout y mantiene alineadas las cabeceras fijas.

6) ¿Cómo mantener una cabecera fija legible en modo oscuro?

Dale un fondo opaco y un separador sutil (borde o sombra). Las cabeceras transparentes en modo oscuro tienden a emborronarse visualmente durante el scroll.
Prefiere colores del sistema (Canvas/CanvasText) o valores mezclados con cuidado.

7) ¿Qué pasa con la impresión?

Los estilos de impresión deben desactivar la posición sticky y eliminar límites de altura para que el contenido fluya. Si tus tablas son anchas, considera permitir que
escalen o que las páginas se rompan limpiamente. Añade una hoja de estilos para impresión que ponga .table-wrap { overflow: visible; max-height: none; }.

8) ¿Cómo muestro el valor completo cuando uso elipsis?

Proporciona una affordance explícita: un botón de copia, un toggle “Expand” o un panel de detalles. No confíes solo en el atributo title;
es inconsistente en móvil y no es ideal para accesibilidad.

9) ¿Necesito cabeceras fijas para cada tabla?

No. Úsalas para tablas largas donde la cabecera aporta significado (columnas de parámetros, matrices de compatibilidad). Para tablas pequeñas, las cabeceras fijas
pueden ser ruido visual y añadir complejidad que no necesitas.

Conclusión: qué cambiar el lunes por la mañana

Si recuerdas una cosa: las tablas no “se vuelven responsivas” por arte de magia. Las haces responsivas controlando el overflow y estabilizando el layout.
Envuelve las tablas en un contenedor de desplazamiento, fija las cabeceras en ese contexto de scroll y trata las celdas de código como un problema de renderizado separado.

Pasos prácticos:

  • Implementar un componente estándar .table-wrap y migrar cada tabla de docs a él.
  • Adoptar table-layout: fixed para tablas de docs y establecer anchos para columnas clave.
  • Añadir políticas de celdas de código: wrap-anywhere para IDs, scroll interno para comandos/config.
  • Crear una “página de tortura de tablas” y ejecutarla en cada cambio de tema antes de publicar.
  • Eliminar medición de anchos en JavaScript y cualquier comportamiento ligado al scroll salvo que puedas probar su necesidad.

Tus docs son parte de tu sistema de producción. Trata el componente de tablas como una dependencia crítica: predecible, testeable y aburrida en el mejor sentido.

← Anterior
Debian 13: /etc/pve parece vacío tras reinicio — por qué fallan los montajes y cómo recuperar
Siguiente →
PostgreSQL vs Redis: cómo evitar que las estampidas de caché colapsen tu base de datos

Deja un comentario