Sidebar Item & Group
Shellx-kore::sidebar.item es un enlace de navegación (o un desplegable, si le metes hijos), con detección de ruta activa resuelta en el servidor. x-kore::sidebar.group agrupa items bajo un título de sección — no navega, solo organiza.
No se renderizan en vivo en esta página. Ambos componentes solo funcionan dentro de un <x-kore::sidebar>, que a su vez requiere el layout completo del shell. Pruébalos en la demo en vivo.
Uso básico
Un enlace, con icono opcional. href para una URL directa, route para el nombre de una ruta de Laravel.
<x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" />
<x-kore::sidebar.item label="Documentación" icon="book" href="https://…" target="_blank" /><x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" />
<x-kore::sidebar.item label="Documentación" icon="book" href="https://…" target="_blank" />Ruta activa
El item se marca solo cuando apunta a la página actual. Se resuelve en el servidor, así que sale ya marcado en el HTML.
Orden de prioridad:
activeexplícito (puede forzartrueofalse)match→request()->routeIs(...), admite comodinesroute→ nombre de rutahref→ comparación de URL- Un hijo activo marca al padre
Cuidado con los comodines. route="users.*" no es un nombre de ruta resoluble: sirve para comparar, no para generar la URL. Si lo usas como route, el enlace se queda sin destino.
<x-kore::sidebar.item label="Usuarios" route="users.index" match="users.*" /><x-kore::sidebar.item label="Usuarios" route="users.index" match="users.*" />Así el enlace apunta a users.index y sigue marcándose activo en cualquier ruta users.*.
Sub-items a cualquier profundidad
Mete items dentro de un item y se convierte en un desplegable.
<x-kore::sidebar.item label="Ajustes" icon="settings">
<x-kore::sidebar.item label="Perfil" route="settings.profile" />
<x-kore::sidebar.item label="Seguridad">
<x-kore::sidebar.item label="Contraseña" route="settings.password" />
<x-kore::sidebar.item label="Dos factores" route="settings.2fa" />
</x-kore::sidebar.item>
</x-kore::sidebar.item><x-kore::sidebar.item label="Ajustes" icon="settings">
<x-kore::sidebar.item label="Perfil" route="settings.profile" />
<x-kore::sidebar.item label="Seguridad">
<x-kore::sidebar.item label="Contraseña" route="settings.password" />
<x-kore::sidebar.item label="Dos factores" route="settings.2fa" />
</x-kore::sidebar.item>
</x-kore::sidebar.item>La rama que contiene la página actual sale ya abierta del servidor. Si estás en «Contraseña», tanto «Ajustes» como «Seguridad» aparecen desplegados en el HTML, sin esperar a que arranque Alpine. Esto es lo que evita el típico menú que se despliega de golpe al cargar la página.
Con el sidebar colapsado
Los sub-items no caben en línea, así que salen en un panel flotante al pasar el ratón.
Los menús anidados se abren uno al lado del otro: entrar en «Seguridad» no cierra el panel de «Ajustes». Los items sin hijos muestran su nombre en un tooltip.
Badges
Contenido arbitrario, con color y variante configurables.
<x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" badge="12" />
<x-kore::sidebar.item label="Errores" icon="bug" href="/errors" badge="3" badge-color="destructive" badge-variant="solid" />
<x-kore::sidebar.item label="Mensajes" icon="mail" href="/inbox" badge="9 sin leer" badge-variant="dot" badge-color="destructive" /><x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" badge="12" />
<x-kore::sidebar.item label="Errores" icon="bug" href="/errors" badge="3" badge-color="destructive" badge-variant="solid" />
<x-kore::sidebar.item label="Mensajes" icon="mail" href="/inbox" badge="9 sin leer" badge-variant="dot" badge-color="destructive" />Al colapsar, un badge numérico se muda a la esquina del icono, que es el único hueco que queda:
- Un número por encima de
badgeMaxse muestra como99+. Con100000000el contador reventaría el icono, y de todos modos nadie lee nueve dígitos a ese tamaño. - Un texto corto (
!,new) pasa tal cual. - Uno que no cabe ni acortado se degrada a un punto: al menos queda constancia de que ese item tiene algo.
El valor que se acorta es solo el visual. Un lector de pantalla sigue anunciando el número real, no 99+. La variante dot se ve en los dos estados, porque con el sidebar ancho no hay ninguna píldora que la sustituya.
Teclado
Navegación completa sin ratón.
| Tecla | Acción |
|---|---|
| ↑ ↓ | Moverse entre items |
| Home End | Primer / último item |
| → | Abrir un sub-menú; si ya está abierto, entrar en él |
| ← | Cerrar el sub-menú; desde un hijo, subir al padre |
| Escape | Cerrar el flyout o el drawer |
En un sidebar a la derecha (placement="right"), ← y → se invierten.
Focus trap solo en el drawer móvil. En escritorio el sidebar es parte de la página y atrapar el foco sería un error — el usuario debe poder tabular hacia el contenido. En móvil, con el drawer abierto sobre el contenido, el foco queda contenido dentro hasta que se cierra.
Accesibilidad
- El item activo lleva
aria-current="page". - Los desplegables son
<button>conaria-expanded, no enlaces: no navegan a ninguna parte. - Al colapsar, la etiqueta sale del DOM visible, pero el enlace conserva su nombre mediante un texto
sr-only. Sin eso, un lector de pantalla anunciaría solo «enlace». - Los enlaces de un sub-menú cerrado salen del orden de tabulación. De lo contrario se podría tabular hasta items invisibles.
Props de sidebar.item
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
label
|
string | — | El texto del item |
icon
|
string | — | Icono de Lucide (users, settings…) |
href
|
string | — | URL directa |
route
|
string | — | Nombre de ruta de Laravel |
routeParams
|
array | [] | Parámetros de la ruta |
match
|
string|array | — | Patrón para decidir si está activo (users.*) |
active
|
bool | — | Forzar el estado activo, ignorando la detección |
opened
|
bool | false | Abrir el desplegable por defecto |
badge
|
string|int | — | Contenido del badge |
badgeVariant
|
soft|solid|dot | soft | Estilo del badge |
badgeColor
|
string | primary | primary, destructive, success, warning, info |
badgeMax
|
int | config(99) | Tope del contador en modo iconos. Por encima, «99+» |
disabled
|
bool | false | Deshabilitar |
target
|
string | — | _blank, etc. Añade rel=noopener noreferrer |
navigate
|
bool | heredado | wire:navigate. Por defecto lo hereda del sidebar |
Grupo o item con hijos
Se parecen, pero no son lo mismo.
| sidebar.group | sidebar.item con hijos | |
|---|---|---|
| Qué es | Una sección | Un menú desplegable |
| ¿Navega? | No | Sí, puede tener su propio destino |
| Aspecto | Título pequeño en mayúsculas | Un item normal, con icono y flecha |
| Al colapsar | El título desaparece; sus items siguen ahí como iconos | El item sigue ahí; los hijos salen en un panel flotante |
En resumen: usa un grupo para etiquetar un bloque de navegación, y un item con hijos cuando el padre sea en sí mismo una opción del menú.
Grupo plegable
Con collapsible, la sección entera se puede plegar.
<x-kore::sidebar.group label="Informes" icon="bar-chart-2" :collapsible="true" :collapsed="true">
<x-kore::sidebar.item label="Ventas" route="reports.sales" />
<x-kore::sidebar.item label="Métricas" route="reports.metrics" />
</x-kore::sidebar.group><x-kore::sidebar.group label="Informes" icon="bar-chart-2" :collapsible="true" :collapsed="true">
<x-kore::sidebar.item label="Ventas" route="reports.sales" />
<x-kore::sidebar.item label="Métricas" route="reports.metrics" />
</x-kore::sidebar.group>Aunque lo declares collapsed, si contiene la página actual se abre solo — y lo hace ya en el HTML del servidor, no al arrancar Alpine.
Al colapsar el sidebar, el título de sección se encoge hasta desaparecer, con animación. Sus items siguen visibles como iconos; lo único que se va es el rótulo. No se oculta de golpe a propósito: hacerlo así haría que todo lo de abajo pegara un salto hacia arriba.
Un grupo plegable es un <button> con aria-expanded y aria-controls. Una vez plegado —o con el sidebar en modo iconos— su contenido sale del orden de tabulación.
Props de sidebar.group
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
label
|
string | — | Título de la sección |
icon
|
string | — | Icono de Lucide junto al título |
collapsible
|
bool | false | Permitir plegar la sección entera |
collapsed
|
bool | false | Empezar plegada |
separator
|
line|space|none | line | Separador visual antes del grupo |
header
|
string | — | Título personalizado (sustituye a label + icon) |