Fichas de etiquetas y barras de filtros: manejo de desbordamiento, ajuste, desplazamiento, estados seleccionados

¿Te fue útil?

Las fichas de etiquetas parecen inofensivas hasta el día en que devoran tu diseño en una pantalla pequeña, tapan el botón “Aplicar” y convierten tu panel de analíticas en una escena del crimen por desplazamiento horizontal. Entonces recibes un ticket: “Filtros rotos en móvil.” Siempre “roto”, nunca “ligeramente subóptimo”.

Esta es la guía orientada a producción sobre fichas y barras de filtros: cómo decidir entre ajuste y desplazamiento, cómo manejar desbordamiento sin engañar a los usuarios y cómo implementar estados seleccionados sin caer en deuda de accesibilidad.

Qué estamos construyendo realmente (fichas, etiquetas, barras de filtros)

Fichas de etiquetas son elementos UI compactos que representan una categoría, atributo o un filtro seleccionado. Normalmente tienen una etiqueta, a veces un icono, a veces una “x” para eliminar, y casi siempre una opinión sobre el padding.

Barras de filtros son contenedores que albergan fichas (y a menudo otros controles) para permitir que los usuarios refinen un conjunto de datos. Si construyes software B2B, tu barra de filtros es un mini sistema operativo: debe sobrevivir etiquetas largas, docenas de selecciones, localización y al usuario que pone el zoom del navegador al 200% porque valora sus ojos.

El contrato oculto

Las fichas no son solo decoración. Forman un contrato:

  • Estado: seleccionado vs no seleccionado debe ser inequívoco.
  • Capacidad: el contenedor debe manejar N fichas con gracia, donde N nunca es el número que probaste.
  • Control: los usuarios deben poder añadir/quitar selecciones sin perder el contexto.
  • Estabilidad: la barra no debe reflujo tanto que la página “salte” y el usuario haga clic por error.

Línea base con opinión

Si construyes una barra de filtros para un producto denso (analíticas, tickets, inventario, seguridad), por defecto:

  • Ajustar en escritorio cuando la barra está sobre el contenido y el espacio vertical es barato.
  • Una sola fila con desplazamiento horizontal en móvil con pistas visuales y un control de reserva “Más filtros”.
  • Resumen colapsado cuando las selecciones crecen (“Filtros (12)”) más un panel dedicado para editar.

Hechos rápidos e historia útil para discusiones

  • Hecho 1: Los patrones de “ficha” se popularizaron en los sistemas de diseño a mediados de los 2010 como tokens compactos pensados para el tacto—mitad botón, mitad etiqueta.
  • Hecho 2: Las interfaces con desplazamiento horizontal existían mucho antes del móvil; simplemente se desalentaban porque las ruedas de ratón y los trackpads no eran consistentes entre plataformas.
  • Hecho 3: El diseño responsive inicial se inclinó por el ajuste porque los patrones de overflow en CSS eran rudimentarios y el desplazamiento con inercia en móviles no era estable en todas partes.
  • Hecho 4: El auge de cabeceras pegajosas y webviews en aplicaciones empujó las barras de filtros a superficies “siempre presentes”—genial para UX, peligroso para los bugs de layout.
  • Hecho 5: Las “píldoras” (tags redondeados) en UI datan de toolkits de escritorio; la forma no es nueva, lo que cambió es la densidad de uso.
  • Hecho 6: La truncación con puntos suspensivos se hizo común en parte porque las fuentes de ancho variable y la localización hacen que las etiquetas de ancho fijo sean una fantasía.
  • Hecho 7: Los patrones de accesibilidad para “grupos de botones toggle” maduraron conforme ARIA evolucionó; las fichas a menudo se comportan como toggles, no como enlaces.
  • Hecho 8: A medida que los productos pasaron a apps renderizadas en el cliente, el estado de filtros se ató a URLs; las fichas se volvieron artefactos de navegación además de visuales.
  • Hecho 9: Las fichas se convirtieron en eventos analíticos (“filtro aplicado”), lo que significa que un bug de UI puede convertirse en un bug de datos. Así es como los ejecutivos empiezan a gritar sobre “salud del pipeline”.

Cita (idea parafraseada): “La esperanza no es una estrategia.” — a menudo atribuida a líderes de fiabilidad/operaciones; trátalo como una mentalidad, no como una cita literal.

Un dato más que no puedes poner en una diapositiva: la cantidad de fichas se correlaciona con el caos organizacional. Cuando los equipos no se ponen de acuerdo en la taxonomía, añaden otro filtro. Es la versión UI de añadir discos porque alguien olvidó borrar logs.

Tomar las decisiones difíciles: ajustar vs desplazar vs colapsar

El manejo del desbordamiento no es una elección de CSS. Es una decisión de producto con una implementación en CSS. Decide qué modo de fallo prefieres cuando el número de fichas se dispara.

Opción A: Ajustar (multi-línea)

Mejor cuando: las fichas son secundarias, hay espacio vertical disponible y quieres visibilidad completa de las etiquetas sin gestos.

Costes: salto de contenido, cambio de layout y “¿dónde está la tabla?” cuando los usuarios añaden la novena ficha.

Realidad operativa: el ajuste interactúa con cabeceras pegajosas. Si la barra crece mientras está pegajosa, puede cubrir contenido o empujarlo de formas raras. Tendrás bugs que digan “no puedo hacer clic en la primera fila.” Tendrán razón.

Opción B: Una sola fila con desplazamiento (horizontal)

Mejor cuando: el espacio vertical es limitado (móvil), las fichas se usan con frecuencia y puedes mostrar una clara pista de desplazamiento.

Costes: descubribilidad y complejidad en la navegación con teclado. Además: es fácil crear una trampa de desplazamiento accidentalmente.

Regla: si eliges desplazamiento, debes proporcionar una pista visual de que hay más contenido (máscara de degradado, ficha parcialmente cortada, botones de flecha o un menú “Más”).

Opción C: Colapsar (resumen + panel)

Mejor cuando: las selecciones pueden crecer mucho, los filtros son complejos y necesitas una altura de layout determinística.

Costes: un clic extra, además debes diseñar un panel que no se sienta como un castigo.

Regla: colapsar no es “ocultar selecciones.” Muestra un resumen: el conteo y quizá las primeras 1–2 fichas como vista previa.

Mi jerarquía preferida

  1. Escritorio: ajustar hasta 2 líneas, luego colapsar en “+N más” o en un menú de desbordamiento.
  2. Móvil: fila única con desplazamiento + botón “Filtros” que abre un panel completo.
  3. Cualquier lugar: nunca permitir que los controles “Aplicar/Limpiar” queden fuera de pantalla.

Broma #1: El desplazamiento horizontal es como la deuda: a veces necesario, siempre necesita un plan de pago.

Patrones de manejo de desbordamiento (y qué falla)

Patrón 1: Ajustar con limitación de líneas para etiquetas

Ajustar fichas en varias líneas es sencillo hasta que las etiquetas son demasiado largas. La decisión correcta suele ser truncar las etiquetas dentro de la ficha, no las fichas en sí. Los usuarios todavía verán tokens distintos; simplemente no verán la etiqueta completa a menos que ofrezcas un tooltip o una pulsación larga.

Modo de fallo: cadenas localizadas largas crean fichas gigantes que dominan la fila, forzando al resto a la siguiente línea. Eso no es “responsivo”; es tu UI siendo intimidada.

Patrón 2: Ajustar con líneas máximas + indicador de desbordamiento

Deja que el contenedor ajuste, pero limítalo a (por ejemplo) dos líneas. Luego muestra una ficha o botón “+N más”. Esto mantiene el layout estable y preserva la legibilidad.

Modo de fallo: implementar “+N más” midiendo el ancho del DOM en cada resize y en cada actualización de ficha puede destrozar el layout. Si lo haces, usa debounce y no lo ejecutes en cada pulsación en un filtro de búsqueda.

Patrón 3: Contenedor desplazable con inercia

Para móvil, una fila única con overflow-x auto es el estándar. Pero “estándar” no significa “seguro.” Debes asegurar:

  • el desplazamiento no impida el scroll vertical de la página (touch-action importa)
  • el foco por teclado permanezca visible (scrollIntoView al tabular)
  • haya una señal de que el contenido se desborda

Modo de fallo: una región de scroll anidada dentro de una cabecera pegajosa puede dar la sensación de una página rota. Los usuarios intentan desplazar la página; la fila de fichas roba el gesto.

Patrón 4: Menú de desbordamiento (“Más”)

Cuando las fichas representan muchas opciones, considera un control “Más” que abra un menú o cajón con el resto. Esto es efectivamente una estrategia de virtualización para tu UI: mantiene el camino crítico rápido y saca los casos extremos fuera del camino crítico.

Modo de fallo: si “Más” contiene fichas seleccionadas pero la barra principal no indica que están seleccionadas, creas estado invisible. El estado invisible causa tickets.

Patrón 5: Cambio responsivo (ajustar → desplazar)

Cambiar comportamiento según puntos de quiebre está bien, pero evita sorpresas de “mismo marcado, física diferente”. Una fila de fichas que ajusta en escritorio pero desplaza en móvil debe preservar el estado de selección, la semántica del teclado y el orden de foco. De lo contrario parece dos componentes distintos pegados.

No dependas de “probablemente no se desbordará”

Asumir que no habrá desbordamiento es el equivalente UI a asumir que los discos no se llenarán. Te equivocarás, y la persona que reporta el bug adjuntará una captura con 47 fichas etiquetadas “Enterprise – North America – West – Secondary”.

Estados seleccionados: UX, accesibilidad y “¿por qué esta ficha es azul?”

Define el tipo de ficha antes de estilizarla

“Ficha” es una forma. El comportamiento importa más. Comportamientos comunes:

  • Fichas toggle: seleccionado/no seleccionado; al hacer clic se alterna el estado.
  • Fichas de acción: ejecutan una acción (añadir fila de filtro, abrir un diálogo).
  • Fichas de entrada: representan entrada del usuario (como destinatarios seleccionados) y son removibles.
  • Fichas de navegación: se comportan como pestañas/enlaces (cuidado: no las confundas con toggles).

El estado seleccionado debe corresponder al comportamiento. No diseñes una ficha de navegación como si fuera una ficha toggle. Los usuarios aprenden patrones. Luego te castigan cuando los rompes.

Seleccionado ≠ activo ≠ enfocado

Tres estados que se confunden:

  • Seleccionado: parte del conjunto de filtros actual.
  • Activo/pressed: siendo clicado/tocado ahora (transitorio).
  • Enfocado: foco de teclado (debe ser visible incluso cuando está seleccionado).

No reutilices el mismo tratamiento visual para seleccionado y foco. Así es como los usuarios de teclado se pierden, y cómo QA presenta bugs de “teclado roto” difíciles de reproducir si no usas realmente un teclado. Lo cual deberías hacer.

El color no basta

El estado seleccionado debe percibirse sin depender únicamente del color. Opciones prácticas:

  • icono de marca de verificación (con nombre accesible)
  • cambio de peso de fuente (sutil pero útil)
  • cambio de estilo de borde
  • cambio de forma (menos común; puede causar salto de layout)

También: mantén el contraste sensato. Una “azul claro sobre azul un poco más claro” parece moderna y falla en la realidad.

La eliminación (“x”) es un objetivo separado

Para fichas de entrada removibles, el botón de eliminar debe ser su propio elemento interactivo. De lo contrario creas un comportamiento ambiguo de clic/tap: ¿seleccionaste la ficha o la eliminaste? Ambos son sorpresas desagradables.

Broma #2: Si tu estado seleccionado depende de un borde de 1px, felicidades—has construido el filtro de Schrödinger.

Realidades móviles: pulgares, barras pegajosas y áreas seguras

Barras de filtros pegajosas: trátalas como infraestructura

Las cabeceras pegajosas son UI persistente. La UI persistente tiene que ser aburridamente correcta. Si la barra de fichas es pegajosa:

  • limita su altura máxima (especialmente en modo de ajuste)
  • evita el redimensionado dinámico durante el scroll
  • reserva espacio para que el contenido no quede cubierto

Una barra pegajosa que crece es como un archivo de logs sin rotación: eventualmente consumirá el mundo.

Tamaño de objetivo y espaciado

Las fichas necesitan un tamaño de toque adecuado. Pero no infles el padding sin criterio; eso aumenta la altura de la fila y empeora el desbordamiento. Mejores opciones:

  • mantén cómodo el cuerpo de la ficha, pero mueve interacciones densas a un panel
  • usa un único control “Filtros” que abra una hoja dedicada para selección compleja
  • limita las etiquetas en la barra a lo necesario; muestra detalle completo en el panel

Áreas seguras y muescas

Si tu barra de filtros está cerca del borde inferior (común en móvil), respeta los insets de área segura. Si no, “Limpiar” quedará bajo el indicador de inicio. Los usuarios lo descubrirán mayormente jurando.

Cuándo cambiar a un panel

Mi regla: si un usuario puede seleccionar razonablemente más de ~8 fichas en un flujo típico, la “barra principal” debería convertirse en un resumen y puerta a un panel. Deja que las fichas en la barra actúen como toggles rápidos para filtros frecuentes; empuja la larga cola al panel.

Accesibilidad y comportamiento con teclado que no te avergonzará

Elige la semántica deliberadamente

No dejes que tu librería de componentes decida la semántica por accidente. Mapeos comunes:

  • Fichas toggle: botones con aria-pressed="true|false" (o semántica de checkbox si es claramente una lista).
  • Grupo de selección única: radiogroup + radio puede ser apropiado, pero solo si realmente se comporta como radio.
  • Fichas de navegación: enlaces reales o pestañas, según el comportamiento de intercambio de contenido.

Sea lo que sea que elijas, mantén los estilos de foco visibles y consistentes entre estado seleccionado/no seleccionado.

Filas de fichas desplazables y visibilidad de foco

Si las fichas están en una región de desplazamiento horizontal, los usuarios de teclado aún deben ver la ficha enfocada. Eso significa que tu JS debe desplazar la ficha enfocada dentro de la vista cuando cambia el foco.

Y sí, esto es un tema de producción. Un indicador de foco oculto es como un log de errores oculto: existe, pero no ayuda a nadie.

Anunciar cambios

Cuando seleccionar una ficha cambia resultados, a los usuarios de lector de pantalla les beneficia un anuncio (“Resultados actualizados”). Mantenlo corto. No narres todo tu plan de consulta.

No atrapes el scroll

Las regiones de scroll anidadas pueden atrapar tanto el tacto como el teclado. Si tienes una fila de fichas desplazable dentro de una página desplazable, verifica:

  • los gestos táctiles aún pueden desplazar la página
  • las teclas de flecha se comportan de forma predecible en el grupo de fichas
  • Tabulador avanza lógicamente y no se queda atrapado en un bucle

Rendimiento y fiabilidad: las fichas como superficie de producción

Por qué a SREs les debería importar

Las barras de filtros parecen adorno frontend hasta que las conectas a un backend en vivo y al comportamiento real de usuarios:

  • Cada toggle puede disparar una llamada a la API.
  • Cada estado seleccionado puede serializarse en una URL.
  • Cada etiqueta puede venir de contenido generado por usuarios.

Ahora tu barra de fichas es parte UI, parte constructor de consultas, parte generador de claves de caché. Si se comporta mal, no es “cosmético.” Es carga, latencia y, ocasionalmente, una caída.

Debounce y agrupar cambios

Para filtros multi-select, evita disparar una consulta en cada toggle si los usuarios suelen seleccionar varios seguidos. Opciones:

  • botón Aplicar explícito
  • auto-aplicación con debounce (pero asegúrate de poder cancelar)
  • actualizaciones por lotes (aplicar cuando el usuario pausa)

Desde la perspectiva de ops: agrupar reduce tormentas de peticiones y estabiliza el comportamiento de caché.

Cuida tus claves de caché

Las fichas a menudo mapean a parámetros de consulta. Si el orden es inconsistente (por ejemplo, tag=a&tag=b vs tag=b&tag=a), las cachés los tratan como distintos. Ordena las selecciones antes de generar URLs y payloads de solicitudes.

La virtualización no es solo para listas

Si tienes cientos de fichas posibles (común en e‑commerce o búsqueda de logs), no las renderices todas en una lista horizontal. Renderiza un conjunto pequeño y mueve el resto a un panel buscable. Esto reduce el tamaño del DOM y hace el layout más predecible.

Tres mini-historias corporativas desde las trincheras

Mini-historia 1: El incidente causado por una suposición equivocada

El equipo de producto añadió “filtros rápidos” a una página de informes. Fichas agradables arriba: Región, Segmento, Estado. Se lanzó rápido porque el dataset era pequeño en staging y todos probaron con etiquetas en inglés.

Luego un gran cliente empresarial activó un conjunto de etiquetas personalizadas extraídas de su taxonomía interna. De repente la lista de fichas pasó de 8 ítems a “tantos como su organigrama.” Las etiquetas eran largas, localizadas e incluían puntuación. La barra se ajustó a cinco líneas, empujó la tabla hacia abajo y la cabecera pegajosa empezó a cubrir las primeras filas.

Llegaron tickets de soporte describiendo “datos faltantes” porque las filas superiores estaban literalmente ocultas detrás de la barra de filtros expandida. Los usuarios no se dieron cuenta de que podían desplazarse; la UI parecía haberse detenido en la cabecera. El incidente no fue una caída, pero fue un evento de confianza. Esos son peores: nadie te alerta, pero los clientes lo recuerdan.

La suposición equivocada fue simple: “los filtros no excederán una línea.” La solución fue igualmente simple pero requirió disciplina: limitar la barra a dos líneas, añadir “+N más” y mover filtros de larga cola a un panel. Además, dejar de permitir que contenedores pegajosos crezcan automáticamente. Pegajoso significa estable.

Mini-historia 2: La optimización que salió mal

Un equipo frontend notó que medir el layout era caro en resize. Implementaron un algoritmo ingenioso de overflow de fichas: medir anchos de fichas, empaquetar tantas como quepan y ocultar el resto detrás de una ficha “Más”. Funcionó genial en sus máquinas.

En dispositivos de gama baja y algunos webviews embebidos, la medición se ejecutaba repetidamente durante el scroll porque el layout se recalculaba por carga de fuentes y cambios dinámicos del viewport. La barra de fichas se convirtió en una pequeña denegación de servicio contra el hilo principal. El desplazamiento se trabó. El lag de entrada subió. Los usuarios describían “la página se congela cuando intento filtrar”.

En monitorización de producción, la latencia backend parecía normal. El cuello de botella era el hilo UI. La optimización falló porque trató la medición del DOM como determinista y barata. No lo es. Especialmente en los momentos raros: cambio de orientación, intercambio de fuentes, zoom y apertura/cierre del teclado.

La solución eventual fue aburrida: eliminar mediciones por frame, permitir un patrón más simple de ajustar con líneas máximas, y recomputar overflow solo en eventos explícitos (cambió la lista de filtros, cruce de breakpoint) con debounce. También añadieron una entrada manual al panel “Filtros”. El rendimiento mejoró y el componente quedó más simple. La lección: “ingenioso” y “fiable” rara vez coinciden en la misma invitación de calendario.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Un equipo diferente tenía la costumbre: cada componente UI que puede desbordarse se entrega con un “modo tortura” en staging. Es solo un toggle que inyecta etiquetas absurdas y 50+ fichas. A nadie le encanta, pero todos lo usan justo antes del release.

Durante un rediseño, un desarrollador cambió el contenedor de filtros de grid CSS a flex y eliminó min-width: 0 de un elemento hijo porque “parecía innecesario”. Con datos normales, nada se rompió.

El modo tortura mostró inmediatamente la falla: las etiquetas largas se negaban a contraerse, desbordaban el contenedor y crearon una barra de desplazamiento horizontal en toda la página. Ese tipo de bug es caro si llega a producción porque suele aparecer solo con ciertos contenidos y anchos de viewport.

Restauraron la restricción faltante, añadieron una prueba de regresión con capturas en puntos de quiebre comunes y siguieron. Sin incidente. Sin drama. Solo la satisfacción tranquila de un sistema que se comporta. Aburrido es bueno. En ops y UI, aburrido es una característica.

Tareas prácticas: más de 12 comandos para diagnosticar el cuello de botella

Estas son tareas operativas reales que puedes ejecutar cuando una barra de fichas “se siente rota” en producción. El objetivo no es adivinar. El objetivo es localizar el cuello de botella: CSS/layout, runtime JS, red/API o explosión de consultas en backend.

Tarea 1: Verificar patrones de acceso en Nginx para toggles de filtros

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/access.log
10.0.12.34 - - [29/Dec/2025:14:01:12 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen HTTP/2.0" 200 8123 "-" "Mozilla/5.0"
10.0.12.34 - - [29/Dec/2025:14:01:13 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen&tag=segment%3Aenterprise HTTP/2.0" 200 8451 "-" "Mozilla/5.0"

Qué significa: cada toggle de ficha dispara una petición. Si ves múltiples peticiones secuenciales por interacción de usuario, tu UI puede estar aplicando cambios demasiado agresivamente.

Decisión: añade batching (botón Aplicar) o debounce; también normaliza/ordena los parámetros de tag para que las cachés funcionen.

Tarea 2: Contar tasa de peticiones por endpoint para detectar tormentas

cr0x@server:~$ sudo awk '{print $7}' /var/log/nginx/access.log | cut -d'?' -f1 | sort | uniq -c | sort -nr | head
  8421 /api/search
  1190 /api/filters
   402 /static/app.js

Qué significa: /api/search domina. Si esto subió tras un cambio UI, la barra de filtros probablemente aumentó la churn de consultas.

Decisión: verifica el comportamiento UI (auto-apply) e implementa cache o limitación si es necesario.

Tarea 3: Inspeccionar la cardinalidad de la query string (mata-cachés)

cr0x@server:~$ sudo grep -o 'GET /api/search?[^ ]*' /var/log/nginx/access.log | head -n 5
GET /api/search?tag=region%3Ana&tag=status%3Aopen
GET /api/search?tag=status%3Aopen&tag=region%3Ana
GET /api/search?tag=region%3Ana&tag=status%3Aopen&sort=desc
GET /api/search?sort=desc&tag=region%3Ana&tag=status%3Aopen
GET /api/search?tag=region%3Ana&tag=status%3Aopen&tag=segment%3Aenterprise

Qué significa: el orden de parámetros varía, produciendo múltiples claves de caché para la misma consulta lógica.

Decisión: canonicaliza el orden de parámetros en cliente; opcionalmente canonicaliza en servidor con redirecciones para GET.

Tarea 4: Revisar ratio HIT/MISS del CDN/cache (si lo tienes)

cr0x@server:~$ sudo grep -E 'HIT|MISS' /var/log/nginx/access.log | tail -n 10
10.0.10.9 - - [29/Dec/2025:14:02:11 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen HTTP/2.0" 200 8123 "-" "Mozilla/5.0" cache=MISS
10.0.10.9 - - [29/Dec/2025:14:02:12 +0000] "GET /api/search?tag=status%3Aopen&tag=region%3Ana HTTP/2.0" 200 8123 "-" "Mozilla/5.0" cache=MISS

Qué significa: misses en consultas lógicamente idénticas suelen indicar params no canonicalizados o TTL bajo.

Decisión: canonicaliza y considera cachear resultados de búsqueda por ventanas cortas si es aceptable.

Tarea 5: Medir distribución de latencia del backend (no solo el promedio)

cr0x@server:~$ sudo awk '{print $NF}' /var/log/nginx/access.log | tail -n 5
rt=0.091
rt=0.104
rt=1.902
rt=0.088
rt=0.095

Qué significa: tienes latencia de cola (1.9s) aunque la mayoría de peticiones rondan ~100ms. La UI de fichas hace la latencia de cola muy visible porque los usuarios la golpean.

Decisión: optimiza consultas peores y considera actualizaciones optimistas en UI o estados de carga que no provoquen thrash en el layout.

Tarea 6: Identificar consultas lentas en Postgres (ejemplo)

cr0x@server:~$ sudo -u postgres psql -c "select calls, mean_exec_time, query from pg_stat_statements order by mean_exec_time desc limit 5;"
 calls | mean_exec_time |                     query
-------+----------------+------------------------------------------------
   412 |        988.123 | select * from events where tags @> $1 ...
  1022 |        212.044 | select distinct tag from events_tags where ...

Qué significa: los filtros impulsados por fichas suelen traducirse a consultas por tags; los patrones más lentos aparecen aquí.

Decisión: añade índices (GIN para JSONB/arrays de tags), reescribe consultas o restringe filtros costosos detrás de un “Aplicar”.

Tarea 7: Comprobar saturación de CPU (síntomas cliente también pueden ser servidor)

cr0x@server:~$ top -b -n 1 | head -n 12
top - 14:05:21 up 12 days,  3:18,  1 user,  load average: 5.21, 4.98, 4.10
Tasks: 212 total,   2 running, 210 sleeping,   0 stopped,   0 zombie
%Cpu(s): 86.2 us,  3.1 sy,  0.0 ni, 10.3 id,  0.0 wa,  0.0 hi,  0.4 si,  0.0 st
MiB Mem :  32123.8 total,   1120.4 free,  18002.3 used,  13001.1 buff/cache

Qué significa: la CPU está alta; si esto coincide con uso de filtros, el trabajo backend por toggle es demasiado caro.

Decisión: reduce frecuencia de peticiones y/o optimiza la estrategia de consultas/índices.

Tarea 8: Comprobar p95 de latencia rápidamente con journalctl (servicio de ejemplo)

cr0x@server:~$ sudo journalctl -u search-api --since "30 min ago" | grep "request_time=" | tail -n 5
request_time=0.112 path=/api/search
request_time=0.138 path=/api/search
request_time=1.744 path=/api/search
request_time=0.121 path=/api/search
request_time=0.109 path=/api/search

Qué significa: estás viendo picos; correlaciona con tags o combinaciones específicas.

Decisión: añade métricas por consulta claveadas por conjuntos de filtros normalizados.

Tarea 9: Confirmar gzip/brotli para payloads grandes de filtros

cr0x@server:~$ curl -sI -H "Accept-Encoding: gzip" https://app.example.internal/api/filters | grep -i -E "content-encoding|content-length"
Content-Encoding: gzip
Content-Length: 18234

Qué significa: las definiciones de filtros pueden ser pesadas (etiquetas, conteos, metadatos). La compresión importa.

Decisión: asegura que la compresión esté habilitada; reduce campos del payload para la barra de fichas vs el panel completo.

Tarea 10: Validar que el bundle frontend no se infló

cr0x@server:~$ ls -lh /srv/www/static/ | grep app
-rw-r--r-- 1 www-data www-data 1.9M Dec 29 13:40 app.js
-rw-r--r-- 1 www-data www-data 255K Dec 29 13:40 app.css

Qué significa: si tu componente de fichas arrastró una dependencia enorme (iconos, librerías de medición), el tamaño del bundle puede perjudicar la latencia de interacción.

Decisión: tree-shake, code-split el panel de filtros y mantén la barra ligera.

Tarea 11: Detectar quejas por shift de layout vía logs de errores frontend

cr0x@server:~$ sudo tail -n 20 /var/log/frontend-errors.log
2025-12-29T14:03:09Z WARN ui layout_shift chipbar_resize loops=34 route=/reports
2025-12-29T14:03:10Z WARN ui long_task 247ms route=/reports action=toggle_chip

Qué significa: redimensionados repetidos de la barra de fichas y tareas largas indican thrash en el cliente.

Decisión: elimina mediciones por frame; reduce reflows; limita altura; evita animar ancho/alto.

Tarea 12: Verificar que “Limpiar filtros” sea siempre accesible (chequeo sintético)

cr0x@server:~$ node /opt/synthetics/check-filterbar.js
PASS route=/reports viewport=390x844 clear_button_visible=true chips_overflow=true
PASS route=/reports viewport=768x1024 clear_button_visible=true chips_overflow=true
FAIL route=/reports viewport=1280x720 clear_button_visible=false chips_overflow=true

Qué significa: a 1280×720 el botón limpiar queda oculto. Eso es un bug real con dolor real para el usuario.

Decisión: fija los botones de acción, restringe el ancho del contenedor de fichas o mueve acciones a un área fija.

Tarea 13: Comprobar picos de HTTP 429/5xx tras release de UI

cr0x@server:~$ sudo awk '$9 ~ /429|500|502|503/ {print $9, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
  184 429 /api/search
   27 503 /api/search

Qué significa: tu UI de filtros está generando suficiente tráfico para activar límites de tasa o sobrecargar.

Decisión: revertir auto-apply, añadir debounce/batching y optimizar consultas costosas.

Tarea 14: Confirmar que el tamaño del payload de consulta no está explotando (POST de filtros)

cr0x@server:~$ sudo grep "Content-Length" /var/log/nginx/access.log | tail -n 3
"POST /api/search HTTP/2.0" 200 9123 "-" "Mozilla/5.0" cl=842
"POST /api/search HTTP/2.0" 200 9130 "-" "Mozilla/5.0" cl=9421
"POST /api/search HTTP/2.0" 200 9201 "-" "Mozilla/5.0" cl=18822

Qué significa: los usuarios pueden seleccionar tantas fichas que los cuerpos de petición se hacen grandes. Eso aumenta latencia y puede golpear límites.

Decisión: aplica límites de selección, o pasa de “enviar todas las tags” a conjuntos de filtros guardados en servidor.

Guía rápida de diagnóstico

Cuando alguien dice “las fichas de filtros están rotas”, no debatas estética. Encuentra el cuello de botella rápido.

Primero: Determina la clase de fallo

  • Fallo de layout: fichas se solapan, acciones desaparecen, barras de desplazamiento raras.
  • Fallo de interacción: taps no registran, botón eliminar falla, foco perdido.
  • Fallo de rendimiento: jank UI, stutter, congelamientos tras toggles.
  • Fallo de datos: fichas seleccionadas no coinciden con resultados, mismatch de URL, conteos erróneos.

Segundo: Reproduce bajo “estrés”

  • viewport pequeño + texto grande (zoom del navegador / escala de fuente del SO)
  • etiquetas largas (localización, tags generados por usuarios)
  • muchas selecciones (20+)
  • red lenta (simular si es posible)

Tercero: Localiza dónde se va el tiempo

  1. Hilo principal cliente: tareas largas alrededor del toggle? sospecha medición de layout, rerenders, actualizaciones de estado pesadas.
  2. Red: múltiples peticiones por toggle? sospecha auto-apply, falta de debounce, falta de batching.
  3. Backend: latencia cola o picos de CPU? sospecha problemas de consulta/índice, misses de caché.

Qué comprobar primero/segundo/tercero (orden práctico)

  1. Primero: ¿El área de acciones (Aplicar/Limpiar) es estable y siempre visible? Si no, arregla restricciones de layout antes que nada.
  2. Segundo: ¿Una interacción dispara una única petición? Si no, arregla debounce/batching y canonicalización de URL.
  3. Tercero: ¿La p95 de latencia backend es aceptable para toggles interactivos? Si no, indexa/optimiza y considera “Aplicar”.

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

1) La página obtiene una barra de desplazamiento horizontal tras seleccionar fichas

Síntomas: la página entera se desplaza lateralmente; el contenido se siente “fuera de lugar”.

Causa raíz: un hijo flex se niega a encoger porque min-width por defecto lo impide, o las etiquetas largas no se truncan.

Solución: permitir encogimiento (min-width: 0 en el ítem flex que contiene las fichas); truncar etiquetas; evitar anchos fijos en contenidos de fichas.

2) “Limpiar” o “Aplicar” desaparece cuando las fichas se ajustan

Síntomas: las acciones pasan a una nueva línea o son empujadas fuera de vista.

Causa raíz: las acciones están en el mismo contenedor que se ajusta con las fichas, sin área fijada.

Solución: separar regiones de layout: área de fichas (ajustable/desplazable) + área de acciones fija. Si es necesario, permite que las fichas se desborden mientras las acciones permanecen fijas.

3) La fila de fichas roba el scroll y atrapa a los usuarios

Síntomas: el usuario intenta desplazar la página; solo se mueven las fichas; la página se siente atascada.

Causa raíz: región de desplazamiento horizontal en una cabecera pegajosa con manejo táctil agresivo.

Solución: asegura que el scroll vertical siga siendo dominante; ajusta touch-action; mantén la región de desplazamiento de fichas pequeña; ofrece un panel alternativo “Filtros”.

4) El estado seleccionado es poco claro o inconsistente

Síntomas: los usuarios no saben qué está aplicado; tickets de soporte indican “resultados erróneos”.

Causa raíz: el estilo depende de cambios sutiles de color; foco y seleccionado se solapan; fichas seleccionadas están ocultas por desbordamiento.

Solución: añade señales no basadas en color (checkmark/borde), mantiene el anillo de foco distinto y asegura que las fichas seleccionadas sean visibles en el resumen (“Filtros (N)”).

5) Hacer clic en la “x” de eliminar alterna la selección en su lugar

Síntomas: la ficha alterna cuando el usuario intenta eliminar; cambios accidentales de filtro.

Causa raíz: el icono eliminar no es un botón separado o la propagación de eventos no está manejada.

Solución: haz que eliminar sea un botón dedicado con su propio label; detén la propagación; mantén objetivos lo suficientemente grandes para el tacto.

6) El backend se derrite cuando los usuarios juegan con filtros

Síntomas: picos 429/5xx; CPU sube; colas de latencia empeoran.

Causa raíz: auto-apply dispara una petición por toggle; caché pobre por params no canonicalizados; consultas por tags costosas.

Solución: debounce/batch, canonicaliza la serialización de filtros, añade índices y considera conjuntos guardados de filtros en servidor.

7) La localización rompe el layout

Síntomas: etiquetas alemanas o finesas convierten fichas en bloques; la truncación no funciona.

Causa raíz: fichas dimensionadas para inglés; sin max-width; sin truncado; iconos consumen demasiado espacio.

Solución: aplica max-width a etiquetas, asegura que ellipsis funcione y prueba con pseudo-localización y cadenas largas.

Listas de verificación / plan paso a paso

Lista: Elige tu estrategia de desbordamiento (no lo sobrepienses, pero decide)

  • ¿La barra de filtros es pegajosa? Si sí, limita la altura y prefiere layout estable (ajuste máximo 2 líneas o desplazamiento).
  • ¿Los usuarios suelen seleccionar muchos filtros? Si sí, proporciona un panel y muestra un conteo resumen.
  • ¿El espacio vertical es valioso (móvil)? Si sí, prefiere fila única con desplazamiento más panel.
  • ¿Las etiquetas son largas o están localizadas? Si sí, exige truncado y anchos máximos para etiquetas.
  • ¿Necesitas “Aplicar”? Si el coste backend es alto, sí. Si no, considera auto-apply con debounce.

Lista: Implementa estados seleccionado/foco/pressed limpiamente

  • El estado seleccionado incluye una pista no basada en color (icono, borde, peso).
  • El anillo de foco siempre es visible y distinto del estilo de seleccionado.
  • Pressed/active es transitorio y no se parece a seleccionado.
  • El botón eliminar es separado y accesible por teclado cuando aplica.
  • Los atributos ARIA coinciden con el comportamiento (botones toggle usan aria-pressed).

Paso a paso: Enviar una barra de fichas resistente

  1. Define la semántica: toggle vs navegación vs fichas de entrada. Escríbelo.
  2. Elige modo de desbordamiento por breakpoint: ajuste en escritorio (líneas máximas) y desplazamiento en móvil, o colapsar a resumen.
  3. Reserva espacio para acciones: Aplicar/Limpiar deben estar fijados, no a merced del ajuste.
  4. Restringe etiquetas: max-width + ellipsis; tooltip/presión larga para texto completo si es necesario.
  5. Maneja muchas selecciones: muestra conteo resumen y un panel para editar el conjunto completo.
  6. Normaliza la serialización de filtros: orden estable para parámetros URL y payloads de solicitud.
  7. Controla la tasa de peticiones: debounce o Aplicar; cancela peticiones en vuelo al cambiar.
  8. Prueba casos de estrés: etiquetas largas, 50 fichas, zoom 200%, viewport pequeño, red lenta.
  9. Añade hooks de monitorización: cuenta toggles por sesión, ráfagas de peticiones, tareas largas UI y p95 backend.
  10. Escribe un plan de rollback: puedes feature-flaguear auto-apply y volver a Aplicar si el coste backend se dispara.

Preguntas frecuentes

1) ¿Las fichas deben ajustarse o desplazarse por defecto?

Ajusta en escritorio si el espacio vertical es aceptable. Desplaza en móvil si puedes proporcionar una pista obvia y evitar atrapamiento de scroll. Para flujos con muchas selecciones, colapsa a un resumen con panel.

2) ¿Cuántas fichas son “demasiadas”?

Más de las que caben cómodamente sin ocultar acciones primarias. Prácticamente: si los usuarios pueden seleccionar con frecuencia más de 8–12, tu barra principal debería convertirse en resumen y puerta a un panel.

3) ¿“+N más” es mejor que desplazamiento horizontal?

A menudo sí en escritorio porque mantiene la página estable y evita que selecciones ocultas se pierdan. En móvil, el desplazamiento horizontal es aceptable, pero aún considera “Filtros (N)” como control primario.

4) ¿Cómo evito shift de layout cuando se añaden/quitan fichas?

Limita la altura del contenedor (líneas máximas), evita animar la altura y mantén acciones en una región fija separada. Si debes animar, prefiere opacidad/transformaciones en vez de cambios de altura.

5) ¿Por qué las etiquetas largas rompen la truncación en layouts flex?

Los ítems flex pueden negarse a encoger a menos que lo permitas. La solución clásica es min-width: 0 en el hijo flex que contiene el texto, además de un max-width en la región de la etiqueta.

6) ¿Las fichas seleccionadas deben ser removibles con una “x”?

Sólo para fichas estilo entrada que el usuario “posee” (como destinatarios elegidos o filtros aplicados en una lista removible). Para fichas toggle, hacer clic para deseleccionar suele ser suficiente. Si añades una “x”, debe ser un botón separado.

7) ¿Auto-apply o botón Aplicar?

Si las consultas backend son baratas y puedes debouncear y cancelar peticiones en vuelo, el auto-apply puede funcionar. Si las consultas son costosas o los usuarios seleccionan múltiples filtros seguidos, el botón Aplicar es la opción fiable. También es más fácil de razonar en respuesta a incidentes.

8) ¿Cómo hago accesible una fila de fichas desplazable?

Usa semántica de botón adecuada, mantiene un anillo de foco visible y asegura que las fichas enfocadas se desplazan dentro de la vista al tabular. También proporciona un punto de entrada alternativo (panel Filtros) para que los usuarios no estén obligados a desplazarse horizontalmente.

9) ¿La selección de fichas debería reflejarse en la URL?

Sí, para compartir y resiliencia al recargar, especialmente en herramientas B2B. Canonicaliza el orden de parámetros para evitar fragmentación de caché y comportamientos confusos con el botón atrás.

10) ¿Cuál es el diseño más seguro para filtrado de nivel empresarial?

Una cabecera estable con un resumen (“Filtros (N)”), un par de fichas rápidas de alta frecuencia y un panel dedicado de filtros con búsqueda, agrupación y acciones claras. La predictibilidad vence a la ingeniosidad.

Conclusión: próximos pasos que puedes enviar

Las fichas de etiquetas y las barras de filtros fallan de formas previsibles: el desbordamiento oculta controles, el estado seleccionado se vuelve ambiguo y los “toggles rápidos” silenciosamente DDoS-ean tu propio backend. Nada de esto es misterioso. Son restricciones descuidadas.

Próximos pasos que pagan retorno inmediato:

  1. Decide tu estrategia de desbordamiento por breakpoint (ajustar con líneas máximas, desplazar con pista o colapsar a resumen).
  2. Fija tus controles de acción para que nunca sean víctimas del ajuste.
  3. Haz que estados seleccionado/foco/pressed sean distintos y accesibles sin depender solo del color.
  4. Canonicaliza la serialización de filtros y agrupa peticiones para evitar tormentas y misses de caché.
  5. Añade un modo de prueba de estrés (etiquetas largas, muchas fichas, texto grande) y ejecútalo antes de cada release.

Si haces esas cinco cosas, tu barra de fichas deja de ser un generador intermitente de informes de incidentes y se vuelve lo que debía haber sido desde el principio: aburrida, rápida y fiable.

© 2025

← Anterior
Informes DMARC: Cómo leerlos y detectar la suplantación temprano
Siguiente →
WireGuard hub-and-spoke para 3 oficinas con acceso por roles

Deja un comentario