# Patrón de listados compactos — v2 Fecha: 22 de octubre de 2026. **Contrato de diseño vigente; primera implementación compartida adoptada en el dashboard de puesto.** Complementa el [patrón de listados de entidades](patron-de-listados-de-entidades.md), que continúa siendo normativo para exploración y gestión. [DESIGN.md](../DESIGN.md) gobierna tokens, tipografía e identidad; el [patrón de diálogos](patron-de-dialogos.md) gobierna las interacciones modales. Este documento no crea componentes, rutas, permisos ni APIs. ## 1. Objetivo y selección del patrón Un **Listado compacto (Compact List)** permite reconocer, consultar y actuar sobre elementos relacionados en una ficha, dashboard, tarjeta o panel contextual. Selecciona los atributos útiles para esa relación y permite profundizar sin reproducir las herramientas de una pantalla de exploración. No sustituye un formulario ni un picker. Un **Listado de entidades (Entity Listing)** permite encontrar y gestionar registros de una colección completa mediante las bandas y capacidades del estándar existente. Activos, Personas, Recursos y Compañías en sus pantallas principales conservan ese patrón. | Criterio | Listado de entidades | Listado compacto | | --- | --- | --- | | Tarea principal | Encontrar, filtrar, comparar muchos atributos y gestionar | Reconocer relaciones y ejecutar acciones contextuales | | Ámbito | Colección completa dentro de su ámbito funcional | Relación o resumen de una entidad/contexto explícito | | Encabezado | Cabecera de pantalla con título, subtítulo y acciones | Título de sección normalmente aportado por el contenedor | | Composición | Cuatro bandas normativas | Cabecera opcional, filas y salida al conjunto completo cuando proceda | | Búsqueda/filtros | Herramientas del patrón completo | Ausentes por defecto; búsqueda local solo con justificación | | Columnas | Configurables, con identidad y acciones protegidas | Seleccionadas por el consumidor, sin configurador | | Ordenación | Contrato actual de cabeceras y selector | Orden estable del contexto; ordenación local opcional | | Rango y paginación | Rango superior/inferior, tamaño y paginación | Sin esos controles; ampliación o acceso completo según necesidad | | Estado navegable | URL/History en consumidores habilitados por L03.1 | Estado local del resumen; conserva retorno al abrir destinos reales | | Volumen | Preparado para exploración sostenida | Resumen o colección legible con estrategia explícita de acceso completo | | Presentación | Tabla de entidades | Compact Table o Compact Item List | | Estado técnico | EntityTable implementada | CompactTable, CompactList y CompactListItem implementados; adopción inicial en puestos | Decidir en este orden: 1. Identificar la pregunta del usuario y la relación que necesita consultar. Si debe explorar repetidamente el conjunto, aplicar listado de entidades aunque haya pocas filas. 2. Elegir atributos prioritarios y tareas: comparar columnas favorece Compact Table; reconocer títulos con metadatos favorece Compact Item List. 3. Evaluar crecimiento esperado, frecuencia de uso, acciones y ancho del contenedor. Definir cómo alcanzar todo el conjunto antes de limitar filas. 4. Si necesita filtros combinados, configuración de columnas, tamaño de página y paginación recurrente, ofrecer una vista completa con el patrón de entidades. La tarjeta puede conservar un resumen; no acumular esas capacidades en él. Estar en una ficha no obliga a compactar una colección que requiere exploración compleja. Tener 60 perfiles no obliga a cambiar de patrón: puede mantenerse un resumen compacto, siempre que el conjunto completo resulte accesible y el alcance visible esté explicado. Un selector continúa sujeto al patrón de diálogos/PickerList; no pierde búsqueda, selección o paginación por parecerse visualmente a una lista corta. ## 2. Estructura y variantes El contenedor aporta el ámbito y normalmente el encabezado: título accesible, contador conocido opcional y acción contextual opcional. No repetir dentro de la colección el título de `DetailPanel`. El cuerpo contiene filas, estados y acciones. Al final puede aparecer «Mostrar más» o «Ver todos» con alcance explícito. **Compact Table:** comparación de atributos homogéneos. Tabla HTML, nombre accesible, cabeceras de columna y filas con identidad estable. Las cabeceras aportan significado; no omitirlas en tablas comparativas ni usar una tabla para maquetar tarjetas. | Perfil | Persona | Cuenta | Estado | | --- | --- | --- | --- | | Administración | Ángela Muñoz | angela.pruebas | Activo | | Consultas | Íñigo Fernández | — | Activo | **Compact Item List:** lectura lineal de elementos con título, metadatos y acciones. Usar `ul`/`li`; `ol` solo si la secuencia tiene significado. Por ejemplo, portátil principal con estado técnico y uso, monitor asociado, o aplicación con versión y estado. Puede componer `dl` dentro de un elemento para pares etiqueta/valor. Un único elemento conserva la misma semántica; no requiere una tarjeta nueva para cada fila. Ambas variantes comparten reglas de datos, densidad y estados, pero mantienen semánticas y estructuras independientes. No convertir automáticamente una tabla en lista al crecer el número de filas. ## 3. Contrato visual Valores orientativos derivados de las primitivas y tokens instalados. La primera receta compartida se concreta en el contrato implementado al final de este documento. Los equivalentes en px usan la escala actual; el contenido y la ampliación de texto pueden aumentar las alturas. Consultar [las fuentes inspeccionadas](auditoria-patrones-de-listado-v2.md). | Elemento | Receta orientativa | Criterio | | --- | --- | --- | | Contenido principal | Inter, `text-sm` (14 px), título `font-medium` | No reducir todo a 12 o 13 px para ganar densidad | | Metadatos | `text-xs` (12 px), `text-muted-foreground` | Mantener legibilidad y contraste efectivo | | Cabecera tabular | `text-sm font-medium`, altura orientativa `h-10` (40 px) | Etiquetas breves; no nuevo estilo de títulos globales | | Celdas | `px-2 py-2` (8 px) | Menor padding vertical que `EntityTable` (`py-3`); sin alterar vendor | | Fila simple | Altura natural, alrededor de 40 px de texto; 48 px con acción de 32 px y `py-2` | No fijar altura máxima; aceptar filas multilínea y zoom | | Elemento de lista | `py-2`, separación título/metadatos `gap-1`; entre grupos `gap-2`/`gap-3` | Ritmo de 4/8/12 px según contenido | | Acciones | `Button size="sm"` o `size="icon-sm"` (32 px) | Conservar foco y separación; ampliar superficie si el contexto táctil lo exige | | Separadores | `border-border`, divisores horizontales discretos | Evitar cuadrícula completa, sombras y tarjeta anidada innecesaria | | Superficie | Heredar `bg-card`/`text-card-foreground` o fondo del contenedor | Sin paleta alternativa ni tokens nuevos de éxito/advertencia | Texto a la izquierda; números comparables a la derecha con `tabular-nums`; acciones al final y alineadas a la derecha. La columna de identidad recibe el espacio flexible (`min-w-0`); estado y acciones tienen ancho controlado por su contenido. Evitar anchos mínimos heredados de inventario que hagan inutilizable una tarjeta estrecha. No añadir un configurador para resolver un exceso de atributos: elegir los esenciales y ofrecer detalle. Permitir saltos de línea en nombres largos y valores técnicos. Si se trunca, conservar el valor completo en el árbol accesible y ofrecer lectura visual completa mediante expansión, detalle o tooltip accesible también por foco. `title` o hover aislados no bastan. No recortar cuentas diferentes hasta hacerlas indistinguibles. Ausencias en datos: exclusivamente `—`, preservando cero y estados reales. Un error de resolución de referencias debe explicarse al nivel de la fuente, sin convertirlo en «sin relaciones». Estado textual además de color; conservar estados técnicos, uso y revisión por separado. `EntityStatus.active` es binario: no representa por sí solo todos los estados de configuración o disponibilidad. ## 4. Capacidades obligatorias y opcionales Obligatorio: ámbito reconocible, identidad estable, información prioritaria, semántica correcta, estados honestos, acciones accesibles y acceso completo a los elementos que no se muestran. El contador es opcional; si existe, debe indicar si cuenta el conjunto, los elementos visibles o las coincidencias. Omitir el total cuando se desconoce. No componer por defecto barra de búsqueda, barra de filtros, configurador de columnas, selector de tamaño, paginación, rangos duplicados ni menús de configuración. **Omitirlos en la composición**, sin controles ocultos por CSS ni elementos invisibles enfocables. | Capacidad opcional | Cuándo y condiciones | | --- | --- | | Ordenación local | Comparación útil y conjunto local completo; estable para empates, indicador `aria-sort` y botones de cabecera. No confundir ordenación con búsqueda. | | Búsqueda local | Localización frecuente dentro de la relación; campos y alcance explícitos, limpieza y estado sin coincidencias. No filtrar una página como si fuera el conjunto entero. | | Expansión de fila | Metadatos secundarios vinculados al elemento; botón con `aria-expanded`/`aria-controls`, contenido asociado y foco estable. | | Mostrar más | Revelar una porción adicional de la misma colección; explicar cantidad visible si se conoce. Tras activar, no mover foco inesperadamente; al contraer evitar retirar el elemento enfocado. | | Ver todos | Ruta real con ámbito conservado, o diálogo/panel existente que permita recorrer todo el conjunto. Distinguir navegar de revelar contenido. | | Scroll interno | Proteger el presupuesto de una tarjeta de consulta repetida; límite de altura responsive, indicador de continuidad, acceso por teclado y foco sin recortes. Evitar scroll anidado innecesario. | | Límite inicial | Solo con orden y criterio explícitos y una salida que alcance todos los registros. Nunca un `slice` silencioso. | | Acciones por elemento | Operaciones reales y permitidas; principal visible, secundarias en menú accesible, sin duplicar la principal. No ocultarlas exclusivamente al hover. | | Selección simple | Alimenta una acción o detalle concreto; radio o control equivalente semántico, nombre y estado visibles. Enlaces y botones internos mantienen su comportamiento. | | Selección múltiple | Existe una operación colectiva real y se define su alcance y confirmación. Casillas primero, restricciones y selección parcial explícitas; considerar PickerList si se está eligiendo en un formulario. | Cada consumidor documentará qué capacidad necesita y por qué. Un nuevo requisito de exploración compleja debe resolverse en la vista completa antes de ampliar el resumen con otra barra. Búsqueda y ordenación opcionales no implican persistencia global nueva. ## 5. Volumen y acceso completo Escenarios de diseño, **no umbrales automáticos**: | Escenario | Estrategia | | --- | --- | | 0 | Vacío real tras respuesta válida, explicación contextual y alta/vinculación si existe y está permitida. No mostrar tabla de controles sin propósito. | | 1 | Mantener fila o elemento del mismo patrón; tabla si las etiquetas comparables siguen aportando significado. | | 2–5 | Mostrar normalmente todo; sin herramientas de exploración añadidas por defecto. | | 6–15 | Evaluar altura, anchura y frecuencia; mostrar todo si resulta legible o ampliar progresivamente con alcance explícito. | | Decenas/cientos | Resumen inicial determinista y acceso completo mediante expansión, región desplazable o destino completo real. Para investigación sostenida, utilizar listado de entidades en ese destino. | Si no existe una ruta que conserve la relación, no inventar un enlace ni enlazar «Ver todos» a un listado sin ese filtro. Puede mostrarse todo con desplazamiento contenido o proponerse una interacción local de ampliación para implementación posterior. La ausencia de destino completo bloquea un recorte que haría inaccesibles registros, no obliga a inventar contratos HTTP. En datos remotos paginados indicar «elementos cargados» o «hay más» según el contrato. «Cargar más» obtiene páginas reales y conserva orden/identidades; no es paginación local oculta. Si se necesita cambiar página/tamaño o filtrar todo el servidor, llevar esa tarea a la vista completa. No derivar total global de `items.length` de una página. ## 6. Estados y fuentes | Situación | Presentación y recuperación | | --- | --- | | Carga inicial | Skeleton de geometría plausible, decorativo y sin nombres, acciones o totales inventados; mensaje de carga de la colección. | | Actualización con datos previos | Conservar filas, selección, expansión y foco; aviso discreto. Si falla, explicar que se muestran últimos datos disponibles y ofrecer reintento dirigido. | | Vacío real | Cero únicamente confirmado por respuesta válida del ámbito completo; CTA pertinente con permisos. | | Sin coincidencias | Mantener criterio visible y acción de limpiar búsqueda; no afirmar ausencia de relaciones. | | Error parcial | Mantener fuentes válidas; identificar la fallida, las limitaciones del resumen y su reintento. No mostrar un total definitivo incompleto. | | Fuente no disponible | Mensaje contextual y recuperación si procede; no reutilizar vacío. Si no existe caché autorizada, no mostrar filas inventadas. | | Información incompleta | Conservar registros válidos y `—` en datos ausentes; explicar referencia no resuelta o metadatos pendientes en su contexto. No equiparar ausencia y error. | | Datos paginados | Identificar alcance cargado, total solo si es conocido y acceso al resto. | | Ausencia de permisos | Respetar denegación real, retirar datos no autorizados y explicar acceso según contrato del módulo. No usar caché anterior como sustituto de autorización. | `EntityListState` existente cubre loading/ready/refreshing/unavailable/stale. No cubre por sí solo total desconocido, permisos, error de otra fuente o parcialidad de datos: esos hechos pertenecen al adaptador consumidor. Reutilizar la unión cuando encaje y componer la información adicional; este estándar no añade variantes a su API. Ubicar feedback y reintento fuera de la región horizontal desplazable. Conservar un destino de foco estable cuando desaparezca el botón de reintento. Usar anuncios moderados por colección, sin una región live por celda ni toasts repetidos en refetch. Los datos remotos siguen en TanStack Query; no duplicarlos en estado React. ## 7. Responsive y accesibilidad Adaptar por capacidad real del **contenedor**, sin copiar presupuestos de Activos o Recursos ni definir un breakpoint global. Mantener identidad, estado y acciones; los atributos secundarios pueden ir al detalle o a una fila expandida con acceso explícito. En lista, apilar título, metadatos y acciones conservando orden DOM. En tabla, preferir salto de línea y columnas prioritarias; usar scroll horizontal contenido cuando la comparación lo requiera. Si se diseña una alternativa móvil como lista, conservar etiquetas y relaciones, una sola presentación accesible e identidad/foco estables. - Tabla nativa con nombre mediante `caption`, `aria-label` o `aria-labelledby`, `th` con `scope="col"` y cabecera de fila cuando resulte pertinente. Etiquetas accesibles para columnas visuales de iconos/acciones, aunque usen `sr-only`. - Lista nativa con nombre de sección; no simular tabla con `div` ni añadir `role="grid"` sin implementar su modelo de interacción. No usar la semántica de tabla para composición. - Enlace para destino real, botón para acciones, expansión y diálogos. Navegación del cliente mediante `BrowserLink` desde el consumidor; la biblioteca genérica no conoce rutas. - Acciones por teclado, etiquetas traducidas para iconos funcionales y decorativos `aria-hidden`. Una fila clicable conserva controles internos y alternativa de teclado. - Foco visible no recortado, sin dependencia de hover; retorno al origen tras modal y conservación del registro al ampliar/cargar más. No introducir saltos de foco en refetch. - Contraste efectivo según DESIGN.md: texto normal 4,5:1 y texto grande 3:1; comprobar también controles, selección y foco según los criterios aplicables del proyecto. - Contenido completo legible con teclado y tacto; zoom 200/400 %, nombres largos, espacios y valores técnicos sin desbordamiento global. La compactación no justifica reducir controles ni fijar alturas que recorten texto. La futura implementación debe verificarse con el [procedimiento Playwright MCP](playwright-mcp.md), datos sintéticos aislados y volumen de AGENTS.md, además de escenarios específicos vacíos y pequeños. Las reglas aquí escritas no equivalen a una validación en navegador ni a cumplimiento WCAG demostrado. ## 8. Arquitectura de componentes **Compartir primitivas y comportamientos transversales; diferenciar composición y capacidades.** No añadir un modo universal a `EntityTable` con flags para quitar barras. | Pieza existente | Reutilización y límite | | --- | --- | | Table, TableHeader, TableBody, TableRow, TableHead, TableCell | Base semántica de Compact Table. Componer desde código propio; no editar vendor. | | DetailPanel | Contenedor opcional con título/acciones/contador; `density="compact"` reduce espaciado, sin gestionar filas ni estados. Evitar doble tarjeta. | | EntityStatus / Badge | Estado textual y visual cuando la semántica encaja; el booleano de EntityStatus no modela un estado complejo. | | EntityRowActions / EntityAction | Acciones existentes, menú Base UI y restricciones. La navegación conserva un enlace real, no se transforma en callback de botón por usar primaryAction. | | EntityListState / EntityListStatus | Disponibilidad, refetch, error y reintento cuando el contrato encaja; el consumidor conserva permisos y parcialidad. | | EmptyState, Skeleton | Composición de estados; EmptyState tiene presentación de incorporación y no es obligatorio para todo vacío mínimo. | | BrowserLink y contratos de retorno | El módulo aporta enlaces y contexto real; no acoplar rdx-ui a core o DTO. | | EntityTableContent / useEntityTable | Acoplados a TanStack y al patrón completo; no reutilizarlos obligando a inicializar paginación/visibilidad que el compacto no necesita. | | DashboardTable | Precedente local sobre Table; no es todavía API compartida ni receta normativa del compacto. | Responsabilidades del patrón; la API mínima implementada se describe en §11. Las capacidades opcionales anteriores no se consideran implementadas por aparecer aquí: | Componente | Responsabilidad y entradas conceptuales | | --- | --- | | CompactTable | Tabla comparable: nombre accesible, colección readonly con identidad estable y definición mínima de columnas (ID, etiqueta y render). Alineación y prioridad explícitas cuando proceda. | | CompactList | Lista semántica de elementos con identidad estable, nombre accesible y contenido compuesto por el consumidor. Sin motor de tabla. | | CompactListItem | Título, metadatos y acciones mediante composición; independiente de entidades, consultas y traducciones. Extraer solo ante repetición real. | Los nombres de ficheros siguen kebab-case y componentes PascalCase. La implementación genérica pertenece a `@repo/rdx-ui`; las adaptaciones de entidades viven en su operación conforme a la estructura web vigente. No crear barrels vacíos, factories, contexto React o un motor de columnas universal de antemano. Los renderizadores reciben datos presentacionales, textos traducidos y acciones del módulo, sin endpoints, ORM ni DTO específicos. No exigir `EntityColumn` con capacidades de exploración para describir una fila compacta. Selección, búsqueda o ampliación se añaden como composición explícita cuando haya consumidores; no mediante una colección de flags `hide*`/`show*`. La consulta se comparte por query keys existentes. Calcular filtrados, contadores y filas durante render; solo criterio, selección y expansión necesitan estado local. Sin efectos para copiar datos remotos, consultas por fila, memoización preventiva ni nuevas dependencias. No persistir preferencias nuevas ni introducir paginación/virtualización sin necesidad medida. ## 9. Casos de referencia y ejemplos La ficha de Puesto de trabajo es referencia funcional, **no la única especificación visual**. La tabla conserva el diagnóstico anterior y la transformación adoptada en la primera implementación, descrita en el informe de refinamiento: | Colección | Actual inspeccionado | Propuesta | | --- | --- | --- | | Personas y perfiles | WorkstationProfiles usa EntityTable completa con búsqueda, contador, columnas, ordenación y paginación dentro de DetailPanel | Compact Table: perfil, persona, cuenta, estado y acción contextual; resumen de configuraciones solo si aporta a la tarea y cabe. Conservar alta, archivados, revisión, enlaces y estados independientes. | | Equipamiento | WorkstationAssets usa EntityTable completa | Compact Item List: nombre y enlace, plantilla, condición de principal, estado técnico y uso separados; editar composición sigue siendo una acción explícita. | | Software del principal | WorkstationSoftware ya presenta ul/li con scroll y gestión en diálogo | Normalizar como Compact Item List preservando versión, licencia, estado, revisión, errores por fuente y gestión existente. No sustituir su diálogo de gestión completa por resumen. | | Configuraciones por perfil | WorkstationConfigurationFamily presenta listas por familia, selector de perfil y destinos de gestión | Colecciones contextuales con selección de perfil fuera de las filas; conservar familias, estados de configuración, cobertura documental y revisión, sin inferir permisos efectivos. | Con 60 perfiles: indicar alcance (por ejemplo, activos y archivados separados), mantener orden estable y permitir recorrerlos todos. Hasta disponer de una ampliación o destino real que conserve la relación con el ordenador principal, no limitar el resumen sin salida. Una ruta de perfiles de compañía sin ese filtro no equivale a «todos los perfiles del puesto». | Correcto | Incorrecto | | --- | --- | | Tres activos con título, estado técnico, uso y enlaces en lista | EntityTable completa dentro de tarjeta solo para mostrar tres nombres | | Resumen de perfiles con Mostrar más que permite alcanzar los 60 | Recortar a cinco sin explicar que faltan registros | | Ver todos abre un destino real con el mismo ámbito | Enlace inventado o listado general presentado como si conservara la relación | | Herramientas omitidas de la composición | Ocultar buscador y paginación de EntityTable con selectores CSS | | Reintento de la familia fallida y datos válidos conservados | Fallo de metadatos convertido en «0 configuraciones» | | Acción visible y menú operable con teclado | Acción disponible únicamente al pasar el ratón | | Vista completa para filtrar muchas relaciones | Añadir sucesivamente todas las barras del inventario al resumen | ## 10. Compatibilidad y decisiones de implementación pendientes Este patrón no altera los listados de entidades existentes, la excepción de pickers `layout="fill"`, L03.1, contratos HTTP, i18n es/en, permisos ni políticas de actualización de consultas. Mantener contexto de compañía y retorno al navegar; el resumen no hereda sort/página del listado padre ni añade parámetros URL por defecto. Una interacción contextual con contrato URL propio lo conserva. Modal de alta/edición sigue el patrón de diálogos. Para futuras adopciones debe concretarse por consumidor: - Prioridad y anchura efectiva de columnas, altura disponible y forma de ampliación. La receta orientativa de densidad debe validarse con texto largo, tacto y zoom. - Destino o interacción para consultar todos los perfiles del principal y tratamiento de archivados; no asumir que existe un filtro de ruta aún no implementado. - Evoluciones de la API mínima y extracción real de renderizadores/estados comunes. Los nombres propuestos no comprometen una API universal. - Búsqueda local si se necesita: V07 sigue pendiente. La normalización del selector de columnas no aprueba automáticamente la de entidades; conservar la semántica vigente hasta una entrega específica y documentar los campos buscables. La [auditoría v2](auditoria-patrones-de-listado-v2.md) contiene candidatos adicionales, evidencias estáticas, discrepancias y verificaciones realizadas en esta fase documental. ## 11. Primera implementación compartida Adoptada en [el refinamiento del dashboard de puesto](workplace-dashboard-implementacion.md#refinamiento-visual-y-adopción-del-listado-compacto). Primitivas de `@repo/rdx-ui/components/`: | API disponible | Contrato implementado | | --- | --- | | `CompactTable` | `title`, `items` readonly, `columns` readonly y `emptyMessage`; opcionales `className` sobre la tabla, `state`, `onRetry` y `retryLabel`. | | `CompactColumn` | `id`, `label`, `render(item)` y `align` opcional: start o end. Sin metadatos de búsqueda, visibilidad o paginación. | | `CompactList` | `title` accesible y `children`; lista nativa con divisores y tipografía estándar. | | `CompactListItem` | `title`, `metadata?`, `icon?`, `actions?`, `children?`; identidad flexible y acciones independientes. | | `CollectionPreview` | `items`, `limit`, `expandLabel`, `collapseLabel` y función hija que recibe los elementos visibles; amplía al conjunto completo cargado. | `CompactTable` conserva mensajes de `EntityListState` mediante `EntityListStatus`, reintento con foco estable fuera del scroll y valores ausentes mediante el mismo helper que `EntityTable`. El consumidor aporta textos y no debe proporcionar un callback de reintento sin su etiqueta. El padding implementado de celda es `px-2 py-1.5` (8/6 px), ajustado para filas de texto y acciones legibles; la altura continúa siendo natural. No hay herramientas ocultas, motor TanStack, consultas ni dependencia de Workstation. `CollectionPreview` muestra inicialmente el límite elegido por la tarea (cinco en el resumen de puesto) y ofrece Mostrar todos con cantidad y Mostrar menos, con `aria-expanded`/`aria-controls`. No pagina, no filtra ni presume que los elementos cargados sean un total remoto. Mantiene el botón y su foco al ampliar o contraer. Límite no entero positivo implica mostrar todo, evitando recortes inaccesibles. Ordenación local, búsqueda, selección y carga de páginas remotas siguen siendo capacidades de diseño opcionales **no implementadas por estos componentes**. No se añadieron flags a EntityTable. Las adopciones restantes de la auditoría v2 continúan pendientes.