componente · @solu30/ui-kit/navigation
MobileTabNavigation
Tira de pestañas con scroll horizontal para móvil, que centra sola la pestaña activa.
Seguir leyendo — 3 párrafos más
MobileTabNavigation provides horizontal scrollable tabs for mobile navigation.
Features:
- Horizontal scroll with snap points
- Touch-friendly targets (>=44px)
- Scroll shadows when content overflows
- Auto-centers active tab
- Badge support with pulse animation
- WCAG 2.1 accessible (role="tablist", keyboard nav)
Theming via CSS variables:
- --ui-accent: active tab color
- --ui-bg-raised: container background
- --ui-border: border color
- --ui-content-primary: text color
- --ui-content-muted: inactive text color
3 sitios de import en 3 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- cmp-packages
- elliot
- gestorsalon
La pieza, con lugar de pantalla
Cargando el espécimen…
export function MobileTabNavigationSpecimen() {
const [tab, setTab] = useState('invitados')
const acento = 'oklch(0.72 0.17 30)'
const items: MobileTabItem[] = [
{ id: 'invitados', label: 'Invitados', icon: IconTeam, badge: 84, color: acento },
{ id: 'muro', label: 'Muro', icon: IconChat, color: acento },
{ id: 'servicios', label: 'Servicios', icon: IconCuisine, color: acento },
{ id: 'pagos', label: 'Pagos', icon: IconChart, dot: 'warning', color: acento },
{ id: 'contrato', label: 'Contrato', icon: IconClipboard, dot: 'error', color: acento },
]
return (
<Stage label="MobileTabNavigation · la barra flotante del portal de la clienta de GestorSalón, con el color del salón" tall>
<div className="flex flex-col items-center gap-2 px-1 py-1">
<PhoneFrame className="w-[260px]">
<div className="relative flex h-[360px] flex-col overflow-hidden bg-bkg">
<div className="flex-1 space-y-2 p-3">
<div className="h-20 rounded-lg bg-container" />
<div className="h-3 w-24 rounded bg-container" />
<div className="h-12 rounded-lg bg-container" />
<div className="h-12 rounded-lg bg-container" />
</div>
<div
className="pointer-events-none absolute inset-x-0 bottom-0 z-10 flex justify-center px-3 pb-2"
style={{ '--portal-glow': acento } as React.CSSProperties}
>
<MobileTabNavigation
items={items}
activeItem={tab}
onItemChange={setTab}
haptic
className={[
'pointer-events-auto flex w-full max-w-md justify-center md:!flex',
'[&_[role=tablist]]:[justify-content:safe_center] [&_[role=tablist]]:w-full [&_[role=tablist]]:px-1.5 [&_[role=tablist]]:py-2',
'[&_[role=tablist]]:rounded-2xl [&_[role=tablist]]:border [&_[role=tablist]]:border-default [&_[role=tablist]]:bg-container/85 [&_[role=tablist]]:backdrop-blur-xl',
'[&_[role=tablist]]:shadow-[0_1px_2px_rgba(0,0,0,0.06),0_12px_28px_-12px_rgba(0,0,0,0.28),0_10px_32px_-16px_var(--portal-glow,transparent)]',
'[&_[role=tab]]:min-w-0 [&_[role=tab]]:flex-1 [&_[role=tab]]:flex-col [&_[role=tab]]:gap-0.5 [&_[role=tab]]:px-1 [&_[role=tab]]:text-[10px] [&_[role=tab]]:leading-tight',
].join(' ')}
/>
</div>
</div>
</PhoneFrame>
<p className="max-w-[420px] text-center text-[11px] text-content/60">
El color no es del kit: es el de marca del salón, puesto por ítem. Los overrides que la
hacen flotar son del consumidor — lo que falta en el componente es una prop
`fit`/`stacked`/`floating`.
</p>
</div>
</Stage>
)
}De specimens/navigation.tsx · MobileTabNavigationSpecimen: el espécimen del Muestrario, no un ejemplo oficial del paquete. Muestra cómo se usa la pieza de verdad, montada contra los tipos instalados —eso es lo que vale— pero no es documentación del kit.
Cómo se usa
import { MobileTabNavigation } from '@solu30/ui-kit/navigation'Props
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| items | sí | MobileTabItem<T>[] | Tab items to render |
| activeItem | sí | T | Currently active tab id |
| onItemChange | sí | (item: T) => void | Callback when a tab is selected |
| className | opcional | string | — |
| leadingAction | opcional | React__default.ReactNode | Content rendered to the left of the tab strip, separated by a vertical divider. Intended for a back-button or similar leading action. |
| haptic | opcional | boolean | When `true`, calls `navigator.vibrate(10)` on tab change (best-effort — silently ignored on browsers/devices that do not support the Vibration API). |
Ejemplo del paquete
// La barra de abajo en móvil. `items[].id` es lo que llega a onItemChange.
<MobileTabNavigation
items={[{ id: 'hoy', label: 'Hoy', icon: CalendarDays, badge: 3 },
{ id: 'clientes', label: 'Clientes', icon: Users }]}
activeItem={pestania}
onItemChange={setPestania}
/>Del @example del JSDoc de @solu30/ui-kit, sin editar: son 65 de los 541 símbolos. La sangría y los renglones en blanco son los del paquete.