Volver a la wiki

Patrón UX mobile: lista compacta → detalle full-screen con URL sync

Patrón UX mobile: lista compacta → detalle full-screen con URL sync

¿Qué es este patrón?

En CreaRack Pro, varias tools usan un layout split desktop (lista a la izquierda, panel de detalle a la derecha). En pantallas pequeñas ese layout colapsa en un single-column ilegible. Este patrón define cómo adaptar esas tools a mobile de forma consistente.

Origen: Cuaderno mobile (commit 9a0c41b). Primera reutilización: Direcciones mobile (commit 031c209).


Principio general

En mobile, mostrar primero una lista simplificada solo con nombres. Al tocar un ítem, navegar a una vista detalle full-screen. El botón back del navegador vuelve a la lista.


Estructura de vistas

window.innerWidth > 768
  └─ Vista DESKTOP: split layout (1fr 420px)

window.innerWidth ≤ 768 && selectedId === null
  └─ Vista MOBILE LISTA: <ul> compacto (nombre + chip + chevron)

window.innerWidth ≤ 768 && selectedId !== null
  └─ Vista MOBILE DETALLE: position: fixed; inset: 0; z-index: 40

URL como fuente de verdad

La URL refleja siempre el estado visible, lo que permite:

/tools/<nombre>           → vista lista
/tools/<nombre>?id=N      → vista detalle (ficha N)

Primitivas de URL

// Lee el id de la URL actual
function readIdFromUrl(): number | null {
  const sp = new URLSearchParams(window.location.search);
  const raw = sp.get('id');
  if (!raw) return null;
  const n = parseInt(raw, 10);
  return Number.isFinite(n) ? n : null;
}

// Escribe el id en la URL (pushState, NO reload)
function pushIdToUrl(id: number | null) {
  const sp = new URLSearchParams(window.location.search);
  if (id === null) sp.delete('id'); else sp.set('id', String(id));
  const qs = sp.toString();
  const newUrl = `${window.location.pathname}${qs ? '?' + qs : ''}`;
  if (newUrl !== window.location.pathname + window.location.search) {
    window.history.pushState({ id }, '', newUrl);
  }
}

Hooks necesarios

// 1. Detección de breakpoint reactiva
const [isMobile, setIsMobile] = useState<boolean>(() => detectMobile());

useEffect(() => {
  const onResize = () => setIsMobile(detectMobile());
  window.addEventListener('resize', onResize);
  return () => window.removeEventListener('resize', onResize);
}, []);

// 2. Sincronización URL ↔ estado al montar y en popstate
useEffect(() => {
  if (!isMobile) return;
  const initial = readIdFromUrl();
  if (initial !== null) setSelectedId(initial);
  const onPop = () => setSelectedId(readIdFromUrl());
  window.addEventListener('popstate', onPop);
  return () => window.removeEventListener('popstate', onPop);
}, [isMobile]);

Comportamiento fetchAll en mobile

Las tools con auto-selección del primer ítem en desktop deben no auto-seleccionar en mobile (para que arranquen en vista lista):

setSelectedId((current) => {
  if (current && data.find((d) => d.id === current)) return current;
  if (detectMobile()) return null;   // mobile arranca en lista
  return data[0]?.id ?? null;        // desktop auto-selecciona
});

CSS base del patrón

/* Lista compacta */
.directions-mobile-name-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.directions-mobile-name-row  { display: flex; align-items: center; gap: 12px; min-height: 56px; /* tap area */ }

/* Detalle full-screen */
.directions-mobile-detail {
  position: fixed; inset: 0; z-index: 40;
  background: var(--bg);
  display: flex; flex-direction: column;
}
.directions-mobile-bar {
  backdrop-filter: blur(20px);
  padding: calc(8px + env(safe-area-inset-top)) 12px 8px;  /* safe area iOS */
  border-bottom: 1px solid var(--border-soft);
}
.directions-mobile-body { flex: 1; overflow-y: auto; padding: 1rem; }

Corrección crítica breakpoint tablet

Al añadir mobile, el colapso de columnas desktop debe acotarse para no activarse en móvil:

/* ✗ Antes */
@media (max-width: 1100px) { .split { grid-template-columns: 1fr; } }

/* ✓ Después — solo tablet */
@media (max-width: 1100px) and (min-width: 769px) { .split { grid-template-columns: 1fr; } }

Checklist de implementación en una nueva tool


Tools que implementan este patrón

ToolCommitFecha
Cuaderno9a0c41banterior a 2026-05-13
Direcciones031c2092026-05-13

Véase también

Subir