K
KoreUI

Sidebar Item & Group

Shell

x-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:

  1. active explícito (puede forzar true o false)
  2. matchrequest()->routeIs(...), admite comodines
  3. route → nombre de ruta
  4. href → comparación de URL
  5. 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.

Correcto: separar route de match
<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 badgeMax se muestra como 99+. Con 100000000 el 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 EndPrimer / último item
Abrir un sub-menú; si ya está abierto, entrar en él
Cerrar el sub-menú; desde un hijo, subir al padre
EscapeCerrar 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> con aria-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é esUna secciónUn menú desplegable
¿Navega?NoSí, puede tener su propio destino
AspectoTítulo pequeño en mayúsculasUn item normal, con icono y flecha
Al colapsarEl título desaparece; sus items siguen ahí como iconosEl 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)
Cargando