Biblioteca de componentes
16 patrones congelados + 18 · Movimiento · Centro Deportivo Aurora · datos dummy de un centro deportivo chileno
Índice de patrones
1 · Tabla de datos con paginación
Lista de planes de los alumnos. Filtros en la cabecera, fila densa de 36 px, acciones a la derecha y paginación en .table-foot.
Alumnos y sus planes
| Alumno | Plan | Inicio | Término | Estado | Cupos | Acciones |
|---|---|---|---|---|---|---|
| CFCamila Fuentes Rojas | Full Mensual | 01-09-2026 | 30-09-2026 | Activo | 18 / 30 | Perfil |
| TATomás Aguirre Lillo | 12 Sesiones | 14-08-2026 | 14-09-2026 | Por vencer | 3 / 12 | Perfil |
| JCJosefa Cáceres Núñez | Full Trimestral | 02-07-2026 | 02-10-2026 | Activo | 61 / 90 | Perfil |
| DSDiego Salinas Ortiz | Estudiante | 10-08-2026 | 10-09-2026 | Vencido | 0 / 12 | Perfil |
HTML para copiar:
Ver el HTML para copiar
<div class="card">
<div class="card-head">
<h3 class="card-title">Alumnos y sus planes</h3>
<div class="card-tools">
<div class="field field-search field-search-inline">
<i data-lucide="search" class="nav-ico size-3"></i>
<input type="search" placeholder="Buscar alumno o plan…" aria-label="Buscar alumno o plan">
</div>
<select class="field" aria-label="Estado del plan"><option selected>Todos los estados</option></select>
</div>
</div>
<div class="table-wrap">
<table class="table-dense">
<thead>
<tr>
<th><span class="th-sort is-desc">Alumno<i data-lucide="chevron-down" class="nav-ico"></i></span></th>
<th>Plan</th><th>Inicio</th><th>Término</th><th>Estado</th>
<th class="td-num">Cupos</th><th class="td-actions">Acciones</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="cell-strong"><span class="ava">CF</span>Camila Fuentes Rojas</span></td>
<td class="td-main">Full Mensual</td>
<td><span class="mono text-mini text-text-muted">01-09-2026</span></td>
<td><span class="mono text-mini text-text-muted">30-09-2026</span></td>
<td><span class="badge badge-mint">Activo</span></td>
<td class="td-num">18 / 30</td>
<td class="td-actions"><span class="row-actions">
<a class="link-action" href="#">Perfil<i data-lucide="arrow-up-right" class="nav-ico size-3"></i></a>
<a class="btn btn-xs btn-ghost btn-icon" href="tel:+56984127735" title="Llamar"><i data-lucide="phone" class="nav-ico size-3"></i></a>
</span></td>
</tr>
</tbody>
</table>
</div>
<div class="table-foot">
<span class="pager-info">Mostrando <strong>1–20</strong> de <strong>412</strong> alumnos</span>
<nav class="pager" aria-label="Paginación">
<button type="button" class="pager-btn" disabled aria-label="Anterior"><i data-lucide="chevron-left" class="nav-ico size-3"></i></button>
<button type="button" class="pager-btn is-active" aria-current="page">1</button>
<button type="button" class="pager-btn">2</button>
<button type="button" class="pager-btn" aria-label="Siguiente"><i data-lucide="chevron-right" class="nav-ico size-3"></i></button>
</nav>
<span class="pager-size">
<label class="text-mini text-text-muted" for="por-pagina">Por página</label>
<select class="field" id="por-pagina"><option selected>20</option><option>50</option></select>
</span>
</div>
</div>
1.b · Estado CARGANDO
Cinco filas de esqueleto, sin texto. Se marca con aria-busy="true" en el tbody.
| Alumno | Plan | Vencimiento | Cupos |
|---|---|---|---|
HTML para copiar:
Ver el HTML para copiar
<div class="card">
<div class="table-wrap">
<table class="table-dense">
<thead><tr><th>Alumno</th><th>Plan</th><th>Vencimiento</th><th class="td-num">Cupos</th></tr></thead>
<tbody aria-busy="true">
<tr>
<td><span class="cell-loading"><span class="skel-ava"></span><span class="skel skel-md"></span></span></td>
<td><span class="skel skel-sm"></span></td>
<td><span class="skel skel-md"></span></td>
<td class="td-num"><span class="skel skel-xs"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
1.c · Estado VACÍO (sin datos jamás)
0 registros y sin filtros aplicados: fila única con colspan + estado vacío del patrón 8 + acción primaria de alta.
Plantillas de WhatsApp
| Nombre | Cuerpo del mensaje | Activo | Acciones |
|---|---|---|---|
|
Aún no hay plantillas creadas
Crea la primera plantilla para responder por WhatsApp a los alumnos del centro.
|
|||
HTML para copiar:
Ver el HTML para copiar
<div class="table-wrap">
<table class="table-dense">
<thead><tr><th>Nombre</th><th>Cuerpo del mensaje</th><th>Activo</th><th class="td-actions">Acciones</th></tr></thead>
<tbody>
<tr>
<td class="table-state" colspan="4">
<div class="empty">
<i data-lucide="inbox" class="empty-ico"></i>
<span class="empty-title">Aún no hay plantillas creadas</span>
<span class="empty-text">Crea la primera plantilla para responder por WhatsApp a los alumnos del centro.</span>
<span class="empty-action">
<button type="button" class="btn btn-primary"><i data-lucide="plus" class="nav-ico size-3"></i>Nueva plantilla</button>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
1.d · Estado SIN RESULTADOS (filtro activo)
Hay datos, pero el filtro no devolvió filas: se nombra el término y se ofrece limpiar los filtros.
Alumnos inactivos
| Alumno | Último plan | Fecha de término | Clases restantes |
|---|---|---|---|
|
Sin resultados para «plan anual»
Revisa el término o limpia los filtros para ver los 158 alumnos inactivos.
|
|||
HTML para copiar:
Ver el HTML para copiar
<tr>
<td class="table-state" colspan="4">
<div class="empty is-search">
<i data-lucide="search-x" class="empty-ico"></i>
<span class="empty-title">Sin resultados para «plan anual»</span>
<span class="empty-text">Revisa el término o limpia los filtros para ver los 158 alumnos inactivos.</span>
<span class="empty-action">
<button type="button" class="btn btn-outline"><i data-lucide="rotate-ccw" class="nav-ico size-3"></i>Limpiar filtros</button>
</span>
</div>
</td>
</tr>
1.e · Estado ERROR (falló la carga)
La carga falló: badge rojo + mensaje + botón outline para reintentar.
Registro de pagos y boletas
| Servicio | Fecha Boleta | Total | Acciones |
|---|---|---|---|
|
No pudimos cargar los pagos
Hubo un problema al consultar el registro. Vuelve a intentarlo en unos segundos.
Error 500
|
|||
HTML para copiar:
Ver el HTML para copiar
<tr>
<td class="table-state" colspan="4">
<div class="empty is-error">
<i data-lucide="alert-circle" class="empty-ico"></i>
<span class="empty-title">No pudimos cargar los pagos</span>
<span class="empty-text">Hubo un problema al consultar el registro. Vuelve a intentarlo en unos segundos.</span>
<span class="empty-action">
<span class="badge badge-red">Error 500</span>
<button type="button" class="btn btn-outline"><i data-lucide="refresh-cw" class="nav-ico size-3"></i>Reintentar</button>
</span>
</div>
</td>
</tr>
2 · Formulario (label · ayuda · error · requerido · campo con icono)
Alta de alumno. Todos los estados visibles: requerido, ayuda, error, campo con icono, select, textarea, checkbox y switch.
HTML para copiar:
Ver el HTML para copiar
<form class="form" action="#">
<div class="form-section">
<div class="form-section-title"><i data-lucide="user" class="nav-ico"></i>Datos del alumno</div>
<div class="form-grid">
<div class="field-wrap">
<label class="field-label" for="f-nombre">Nombre completo<span class="field-req">*</span></label>
<div class="field field-lg field-block"><input id="f-nombre" type="text" value="Valentina Ríos Peña" required></div>
<span class="field-help">Como aparecerá en la lista de alumnos y en la app.</span>
</div>
<div class="field-wrap">
<label class="field-label" for="f-correo">Correo electrónico<span class="field-req">*</span></label>
<div class="field field-lg field-block"><input id="f-correo" type="email" value="v.rios" required></div>
<span class="field-msg"><i data-lucide="alert-circle" class="nav-ico"></i>Falta el dominio del correo.</span>
</div>
<div class="field-wrap">
<label class="field-label" for="f-fono">Teléfono</label>
<div class="field field-lg field-block">
<i data-lucide="phone" class="field-ico"></i>
<input id="f-fono" type="tel" value="+56 9 6234 8811">
</div>
<span class="field-help">Se usa para avisos por WhatsApp.</span>
</div>
<div class="field-wrap">
<label class="field-label" for="f-plan">Plan</label>
<select class="field field-lg field-block" id="f-plan">
<option>Full Mensual · $45.000</option>
<option selected>Full Trimestral · $120.000</option>
</select>
</div>
<div class="field-wrap form-col-full">
<label class="field-label" for="f-obs">Observaciones</label>
<div class="field field-lg field-textarea field-block"><textarea id="f-obs" rows="3"></textarea></div>
</div>
</div>
</div>
<div class="form-section">
<label class="check" for="f-welcome">
<input id="f-welcome" type="checkbox" checked>
<span class="check-box"><i data-lucide="check" class="nav-ico"></i></span>
<span class="check-text">
<span>Enviar correo de bienvenida</span>
<span class="check-help">Le llega el acceso a la app y el link de autorregistro.</span>
</span>
</label>
<label class="switch" for="f-activo">
<input id="f-activo" type="checkbox" checked>
<span class="switch-track"></span>
<span>Alumno activo</span>
</label>
</div>
<div class="form-foot">
<button type="button" class="btn btn-ghost form-foot-left"><i data-lucide="arrow-left" class="nav-ico size-3"></i>Volver</button>
<button type="button" class="btn btn-outline">Cancelar</button>
<button type="button" class="btn btn-primary"><i data-lucide="check" class="nav-ico size-3"></i>Agregar alumno</button>
</div>
</form>
3 · Modal (confirmar, avisar, pedir un dato · ≤ 3 campos)
Confirmación destructiva. En una página real el overlay se muestra con .is-open (lo alterna el runtime); aquí se agrega .is-static solo para poder verlo dentro del catálogo. El overlay completo usa .modal-overlay.is-open a pantalla completa.
HTML para copiar:
Ver el HTML para copiar
<div class="modal-overlay is-open">
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-eliminar-titulo">
<div class="modal-head">
<span class="modal-icon is-danger"><i data-lucide="alert-triangle" class="nav-ico"></i></span>
<div class="min-w-0 flex-1">
<h3 class="modal-title" id="modal-eliminar-titulo">Eliminar alumno</h3>
<p class="modal-desc">Esta acción no se puede deshacer.</p>
</div>
<button type="button" class="modal-x" aria-label="Cerrar"><i data-lucide="x" class="nav-ico size-3"></i></button>
</div>
<div class="modal-body">
<p>¿Confirmas eliminar a <strong>Josefa Cáceres Núñez</strong>? Se borrarán su ficha, su plan <span class="mono">Full Trimestral</span> vigente y su historial de reservas.</p>
</div>
<div class="modal-foot">
<button type="button" class="btn btn-ghost">Cancelar</button>
<button type="button" class="btn btn-danger"><i data-lucide="trash-2" class="nav-ico size-3"></i>Eliminar alumno</button>
</div>
</div>
</div>
<!-- catálogo: agregar is-static al overlay para verlo embebido -->
4 · Drawer / panel lateral (formulario largo o detalle · > 3 campos)
Editar un plan. Mismo criterio que el modal: .is-open en producción, .is-static para verlo aquí. Variantes: .drawer-left y .drawer-wide.
Editar plan Full Trimestral
Josefa Cáceres Núñez · vigente hasta el 02-10-2026
HTML para copiar:
Ver el HTML para copiar
<div class="drawer-overlay is-open"></div>
<div class="drawer is-open" role="dialog" aria-modal="true" aria-labelledby="drawer-plan-titulo">
<div class="drawer-head">
<div class="min-w-0 flex-1">
<h3 class="drawer-title" id="drawer-plan-titulo">Editar plan Full Trimestral</h3>
<p class="drawer-desc">Josefa Cáceres Núñez · vigente hasta el 02-10-2026</p>
</div>
<button type="button" class="drawer-x" aria-label="Cerrar"><i data-lucide="x" class="nav-ico size-3"></i></button>
</div>
<div class="drawer-body">
<div class="form-section">
<div class="form-grid">
<div class="field-wrap">
<label class="field-label" for="d-plan">Plan</label>
<select class="field field-lg field-block" id="d-plan"><option selected>Full Trimestral · $120.000</option></select>
</div>
<div class="field-wrap">
<label class="field-label" for="d-inicio">Inicio</label>
<div class="field field-lg field-block"><input id="d-inicio" type="text" value="02-07-2026"></div>
</div>
<div class="field-wrap form-col-full">
<label class="field-label" for="d-nota">Nota interna</label>
<div class="field field-lg field-textarea field-block"><textarea id="d-nota" rows="2"></textarea></div>
</div>
</div>
</div>
<div class="form-section">
<div class="bar-label"><span>Ocupación del plan</span><span class="bar-pct">68 %</span></div>
<div class="bar bar-lg is-warn"><span style="width:68%"></span></div>
</div>
</div>
<div class="drawer-foot">
<button type="button" class="btn btn-ghost">Cancelar</button>
<button type="button" class="btn btn-primary"><i data-lucide="check" class="nav-ico size-3"></i>Guardar cambios</button>
</div>
</div>
5 · KPI / stat card
Fila compacta de 3 (o 4) y stat card aislada con tile de icono y tendencia.
Alumnos de septiembre
HTML para copiar:
Ver el HTML para copiar
<div class="card">
<div class="card-head"><h3 class="card-title">Alumnos de septiembre</h3></div>
<div class="kpi-row">
<div class="kpi">
<span class="kpi-label"><span class="dot bg-primary"></span>ACTIVOS</span>
<span class="kpi-value">412</span>
<span class="kpi-foot"><i data-lucide="trending-up" class="nav-ico size-3"></i>positivo</span>
</div>
<div class="kpi">
<span class="kpi-label"><span class="dot bg-secondary"></span>PRUEBA</span>
<span class="kpi-value">37</span>
<span class="kpi-foot">advertencia</span>
</div>
<div class="kpi">
<span class="kpi-label"><span class="dot bg-destructive"></span>INACTIVOS</span>
<span class="kpi-value">158</span>
<span class="kpi-foot">negativo</span>
</div>
</div>
</div>
<!-- stat card aislada -->
<div class="card">
<div class="kpi-card">
<div class="kpi-card-head">
<span class="kpi-tile"><i data-lucide="dollar-sign" class="nav-ico"></i></span>
<span class="kpi-label">Ingresos del mes</span>
</div>
<span class="kpi-value">$8.914.500</span>
<span class="kpi-foot">
<span class="kpi-trend is-up"><i data-lucide="trending-up" class="nav-ico size-3"></i>+12,4 %</span>
vs. agosto 2026
</span>
</div>
</div>
6 · Badges de estado (todos los del diccionario)
Regla de color única: mint positivo · lima advertencia · rojo negativo/lleno · neutro sin estado · info asistió/integración. Siempre forma + color + texto.
HTML para copiar:
Ver el HTML para copiar
<span class="badge badge-mint">Activo</span> <span class="badge badge-red">Inactivo</span> <span class="badge badge-lime">Prueba</span> <span class="badge badge-neutral">Sin estado</span> <span class="badge badge-info">Consumida</span> <!-- badge con icono + punto --> <span class="badge badge-mint"><i data-lucide="check" class="nav-ico size-3"></i>Confirmada</span> <span class="badge badge-mint"><span class="badge-dot"></span>Con cupo</span>
7 · Avatares (iniciales de color)
Base 22 px · .ava-sm 18 px · .ava-lg 36 px · grupo con .ava-more. En tablas usar el avatar neutro.
HTML para copiar:
Ver el HTML para copiar
<span class="ava">CF</span> <span class="ava ava-sm">MH</span> <span class="ava ava-lg">JC</span> <span class="ava ava-mint">VR</span> <span class="ava ava-lime">TA</span> <span class="ava ava-red">DS</span> <span class="ava ava-info">AB</span> <!-- grupo --> <span class="ava-group"> <span class="ava">CF</span><span class="ava">MH</span><span class="ava">JC</span><span class="ava ava-more">+9</span> </span> <!-- nombre + avatar en una celda --> <span class="cell-strong"><span class="ava">FE</span>Fernanda Espinoza Díaz</span>
8 · Estados vacíos con acción
Tres variantes: sin datos (.empty), sin resultados (.empty.is-search) y error (.empty.is-error). .empty-compact reduce el padding.
HTML para copiar:
Ver el HTML para copiar
<div class="empty">
<i data-lucide="calendar-x" class="empty-ico"></i>
<span class="empty-title">Sin clases programadas</span>
<span class="empty-text">No hay bloques para hoy en el Centro Deportivo Aurora.</span>
<span class="empty-action">
<button type="button" class="btn btn-primary"><i data-lucide="plus" class="nav-ico size-3"></i>Crear bloque</button>
</span>
</div>
<div class="empty is-search empty-compact">…</div>
<div class="empty is-error empty-compact">…</div>
9 · Toasts / confirmaciones
Zona fija abajo-derecha (.toast-zone, autocierre ~4 s) y sus 4 tonos. Lo destructivo se confirma con modal (patrón 3), nunca con toast. .toast-zone.is-static solo para el catálogo.
HTML para copiar:
Ver el HTML para copiar
<div class="toast-zone">
<div class="toast toast-ok" role="status">
<i data-lucide="check-circle" class="toast-ico"></i>
<div class="toast-body">
<div class="toast-title">Plan asignado</div>
<div class="toast-text">Full Trimestral quedó vigente hasta el 02-10-2026.</div>
</div>
<button type="button" class="toast-x" aria-label="Cerrar"><i data-lucide="x" class="nav-ico"></i></button>
</div>
<div class="toast toast-warn" role="status">…</div>
<div class="toast toast-err" role="alert">
<i data-lucide="x-circle" class="toast-ico"></i>
<div class="toast-body">
<div class="toast-title">No se pudo programar la notificación</div>
<div class="toast-text">Revisa la fecha de envío e inténtalo otra vez.</div>
<button type="button" class="toast-action"><i data-lucide="refresh-cw" class="nav-ico size-3"></i>Reintentar</button>
</div>
<button type="button" class="toast-x" aria-label="Cerrar"><i data-lucide="x" class="nav-ico"></i></button>
</div>
<div class="toast toast-info" role="status">…</div>
</div>
10 · Filtros, búsqueda y chips de filtros activos
Barra de filtros compacta: buscador con icono, selects con label y chips que muestran los filtros aplicados con su «x».
HTML para copiar:
Ver el HTML para copiar
<div class="toolbar">
<div class="field field-search field-search-inline">
<i data-lucide="search" class="nav-ico size-3"></i>
<input type="search" placeholder="Buscar alumno, plan o clase…" aria-label="Buscar alumno, plan o clase">
</div>
<label class="toolbar-label" for="fl-plan">Plan</label>
<select class="field" id="fl-plan"><option selected>Full Mensual</option></select>
<label class="toolbar-label" for="fl-estado">Estado</label>
<select class="field" id="fl-estado"><option selected>Activo</option></select>
<div class="toolbar-right">
<button type="button" class="btn btn-ghost btn-xs"><i data-lucide="rotate-ccw" class="nav-ico size-3"></i>Resetear</button>
</div>
</div>
<div class="toolbar">
<span class="chips">
<span class="chip-filter">Plan: Full Mensual<button type="button" class="chip-filter-x" aria-label="Quitar filtro"><i data-lucide="x" class="nav-ico"></i></button></span>
<span class="chip-filter is-neutral">Sede: Ñuñoa<button type="button" class="chip-filter-x" aria-label="Quitar filtro"><i data-lucide="x" class="nav-ico"></i></button></span>
</span>
<a class="chips-clear" href="#">Limpiar los 3 filtros</a>
<span class="card-sub mono ml-auto">137 resultados</span>
</div>
11 · Acciones por fila y menú de acciones
Una acción de texto con icono + hasta 2 icon-buttons; con más de 3 acciones, un menú kebab. La acción destructiva va etiquetada dentro del menú.
| Alumno | Plan | Cupos | Acciones |
|---|---|---|---|
| CVConstanza Vega Pizarro | Estudiante | 9 / 12 |
Ir a perfil
Acciones
|
| ABAndrés Bustos Mella | Full Mensual | 18 / 30 | Ir a perfil |
HTML para copiar:
Ver el HTML para copiar
<td class="td-actions">
<span class="row-actions">
<a class="link-action" href="#">Ir a perfil<i data-lucide="arrow-up-right" class="nav-ico size-3"></i></a>
<a class="btn btn-xs btn-ghost btn-icon" href="tel:+56988451177" title="Llamar"><i data-lucide="phone" class="nav-ico size-3"></i></a>
<a class="btn btn-xs btn-ghost btn-icon" href="mailto:[email protected]" title="Enviar correo"><i data-lucide="mail" class="nav-ico size-3"></i></a>
<details class="dropdown">
<summary class="btn btn-xs btn-ghost btn-icon" title="Más acciones" aria-label="Más acciones"><i data-lucide="more-vertical" class="nav-ico size-3"></i></summary>
<div class="dropdown-panel">
<div class="dropdown-label">Acciones</div>
<button type="button" class="dropdown-item"><i data-lucide="credit-card" class="nav-ico"></i>Renovar plan</button>
<button type="button" class="dropdown-item"><i data-lucide="snowflake" class="nav-ico"></i>Congelar plan</button>
<div class="dropdown-sep"></div>
<button type="button" class="dropdown-item is-danger"><i data-lucide="trash-2" class="nav-ico"></i>Eliminar alumno</button>
</div>
</details>
</span>
</td>
12 · Tabs (secciones de una misma entidad)
Ficha de alumno. Tabs solo para secciones de la misma entidad en la misma URL; si cambia el recurso, es navegación del sidebar.
Josefa Cáceres Núñez
HTML para copiar:
Ver el HTML para copiar
<div class="tabs">
<div class="tabs-list" role="tablist">
<button type="button" class="tabs-trigger is-active" role="tab" aria-selected="true">Datos</button>
<button type="button" class="tabs-trigger" role="tab" aria-selected="false">Planes <span class="tabs-count">3</span></button>
<button type="button" class="tabs-trigger" role="tab" aria-selected="false">Próximas clases <span class="tabs-count">2</span></button>
<button type="button" class="tabs-trigger" role="tab" aria-selected="false">Historial</button>
</div>
<div class="tabs-panel">
<!-- contenido de la pestaña activa -->
</div>
</div>
13 · Encabezado de página (sin breadcrumbs)
Toda vista arranca con esta cabecera: título con icono, contexto y acciones a la derecha. Las clases viven en base.src.css (es layout) y NO se redefinen en la capa de componentes. Sin breadcrumbs desde el 15-09-2026: el sidebar está siempre a la vista y repetir la ruta era ruido.
Lista de Alumnos
412 activos · 37 en prueba · 158 inactivos
HTML para copiar:
Ver el HTML para copiar
<header class="page-head">
<div class="page-head-main">
<h1 class="page-title"><i data-lucide="users" class="page-title-ico"></i>Lista de Alumnos</h1>
<p class="page-sub">412 activos · 37 en prueba · 158 inactivos</p>
</div>
<div class="page-actions">
<span class="page-date">11-09-2026</span>
<button type="button" class="btn btn-outline"><i data-lucide="download" class="nav-ico size-3"></i>Descargar</button>
<button type="button" class="btn btn-primary"><i data-lucide="plus" class="nav-ico size-3"></i>Agregar alumno</button>
</div>
</header>
14 · Tooltip
Dos formas: .tooltip + .tooltip-bubble (controlada por estado) y .has-tip[data-tip] (CSS puro al hover/focus, sin JS).
HTML para copiar:
Ver el HTML para copiar
<!-- controlada por estado --> <span class="tooltip is-open"> <button type="button" class="btn btn-xs btn-ghost btn-icon" aria-label="Asistencia"><i data-lucide="clipboard-check" class="nav-ico size-3"></i></button> <span class="tooltip-bubble">Cuenta las reservas consumidas del periodo</span> </span> <!-- CSS puro, sin JS --> <span class="has-tip badge badge-neutral" data-tip="Volvió a clases el 03-09-2026">Alumno reactivado</span>
15 · Dropdown
Menú de usuario y filtro de selección. .dropdown-panel aparece con .is-open (o dentro de <details class="dropdown">, sin JS). .is-left lo alinea a la izquierda.
Ordenar por
HTML para copiar:
Ver el HTML para copiar
<div class="dropdown is-open">
<button type="button" class="user-chip">
<span class="ava ava-mint">RB</span>
<span class="user-chip-text">
<span class="user-chip-name">Raúl Berríos</span>
<span class="user-chip-role">Administrador</span>
</span>
<i data-lucide="chevron-down" class="nav-ico size-3"></i>
</button>
<div class="dropdown-panel">
<div class="dropdown-label">Administrador</div>
<button type="button" class="dropdown-item"><i data-lucide="user" class="nav-ico"></i>Mi cuenta</button>
<button type="button" class="dropdown-item"><i data-lucide="building" class="nav-ico"></i>Mi centro deportivo</button>
<div class="dropdown-sep"></div>
<button type="button" class="dropdown-item is-danger"><i data-lucide="log-out" class="nav-ico"></i>Cerrar sesión</button>
</div>
</div>
<!-- sin JS: el toggle lo hace el <details> -->
<details class="dropdown">
<summary class="btn btn-outline"><i data-lucide="arrow-down-up" class="nav-ico size-3"></i>Ordenar por</summary>
<div class="dropdown-panel is-left">…</div>
</details>
16 · Barras de progreso / ocupación
El ancho va inline con style="width:X%" derivado del dato (única excepción permitida). Tono por variante: .is-warn, .is-danger, .is-info, .is-full.
Ocupación de las clases de hoy
Alumnos activos por género
HTML para copiar:
Ver el HTML para copiar
<div class="bar-label"><span>Cross Training · 07:00</span><span class="bar-pct">18 / 20 · 90 %</span></div> <div class="bar bar-lg"><span style="width:90%"></span></div> <!-- barra en tono advertencia y llena --> <div class="bar bar-lg is-warn"><span style="width:84%"></span></div> <div class="bar bar-lg is-full"><span style="width:100%"></span></div> <!-- desglose (patrón 16 en tablas/listas) --> <div class="bd-row"> <div class="bd-top"><span class="bd-name">Femenino</span><span class="bd-num">209</span><span class="bd-pct">50,7 %</span></div> <div class="bd-track"><span style="width:50.7%"></span></div> </div> <!-- barra segmentada --> <div class="bar-stack"> <span class="bg-primary" style="width:52%"></span> <span class="bg-secondary" style="width:26%"></span> <span class="bg-destructive" style="width:22%"></span> </div>
Anexo A · Botones, campos y cards
HTML para copiar:
Ver el HTML para copiar
<button type="button" class="btn btn-primary">Primario</button> <button type="button" class="btn">Neutro</button> <button type="button" class="btn btn-outline">Outline</button> <button type="button" class="btn btn-ghost">Ghost</button> <button type="button" class="btn btn-danger">Destructivo</button> <button type="button" class="btn btn-lg btn-primary">Grande</button> <button type="button" class="btn btn-xs">Chico</button> <button type="button" class="btn btn-ghost btn-icon" aria-label="Icono"><i data-lucide="settings" class="nav-ico size-4"></i></button> <button type="button" class="btn btn-primary" disabled>Deshabilitado</button> <div class="field"><i data-lucide="search" class="field-ico"></i><input type="text" placeholder="Campo con icono"></div> <div class="field field-lg"><input type="text" placeholder="Campo cómodo 40px"></div> <select class="field"><option>Select 28px</option></select>
Anexo B · Calendario semanal y chip de clase
Grilla de 7 columnas, columna de hoy marcada con .is-today, chip con franja de color por tipo y barra de ocupación (.is-full = lleno).
Clases de Hoy
HTML para copiar:
Ver el HTML para copiar
<div class="cal">
<div class="cal-day is-today">
<div class="cal-head">VIE<span class="badge badge-mint">HOY · 11-09</span><span class="cal-count">3</span></div>
<div class="chip type-cross">
<div class="chip-top"><span class="chip-time">07:00</span><span class="chip-occ">18/20</span></div>
<div class="chip-name">Cross Training</div>
<div class="bar"><span style="width:90%"></span></div>
</div>
<div class="chip type-pilates is-full">
<div class="chip-top"><span class="chip-time">19:00</span><span class="chip-occ">16/16</span></div>
<div class="chip-name">Pilates</div>
<div class="bar"><span style="width:100%"></span></div>
<span class="badge badge-red mt-0.5 justify-center">LLENO</span>
</div>
</div>
<div class="cal-day"><div class="cal-head">DOM<span class="cal-count">0</span></div><div class="cal-empty">Sin clases programadas</div></div>
</div>
Patrón 18 · Movimiento
Las duraciones y las curvas son tokens (--dur-* · --ease-*): ninguna página escribe un número. Las entradas son @keyframes y las salidas las sostiene .is-closing, porque una vez que la capa sale de pantalla no hay nada que animar. Prueba los cuatro casos: lo de este recuadro se anima, el resto del catálogo no.
El chip responde al mouse y al teclado con un cambio de fondo y 1 px de elevación (--dur-micro · 120 ms). Es la única animación de la muestra que no es una capa.
| Qué se anima | Token | Entra | Sale | Movimiento |
|---|---|---|---|---|
| Modal (fondo + panel) | --dur-layer | 200 ms | 130 ms | El fondo se aclara; el panel llega desde 8 px abajo |
| Panel inferior | --dur-layer | 200 ms | 130 ms | Sube desde el borde de abajo |
| Aviso (toast) | --dur-control | 160 ms | 130 ms | Aparece desde 6 px abajo |
| Panel en línea | --dur-control | 160 ms | 130 ms | Se despliega desde 4 px arriba |
| Chip | --dur-micro | 120 ms | — | Fondo + 1 px de elevación |
Mientras una capa se cierra deja de capturar clics en el primer cuadro, y con prefers-reduced-motion activado todo aparece en el mismo cuadro. Nunca se anima al entrar a la pantalla: solo responde a un gesto.
Ver el HTML para copiar
<div class="modal-overlay" id="mi-modal">…</div>
<script src="../_shared/prototype.js"></script>
<script>
nfitMov.abrir(document.getElementById('mi-modal')); // abre
nfitMov.cerrar(document.getElementById('mi-modal'), function () { … }); // cierra animado
</script>
Panel inferior con movimiento
Sube desde el borde de abajo y vuelve a bajar en 130 ms. Mientras baja no captura clics: nadie espera a que termine una animación.