Shell - Primeros pasos
ShellEl chasis de un panel de administración completo: sidebar de navegación, barra superior y el layout que los coordina. Con estas piezas montas un admin sin escribir layout a mano.
Los componentes
Seis piezas que se combinan entre sí. Todas viven bajo el namespace kore-ui, ninguna requiere registro manual.
| Componente | Para qué |
|---|---|
<x-kore::shell> | El layout. Coloca los sidebars y reserva el espacio del contenido |
<x-kore::navbar> | La barra superior. Trae el botón de menú incorporado |
<x-kore::sidebar> | El sidebar. Colapsable, con drawer en móvil |
<x-kore::sidebar.item> | Un enlace de navegación (o un desplegable, si le metes hijos) |
<x-kore::sidebar.group> | Una sección con su título |
<x-kore::sidebar.toggle> | El botón de colapsar / abrir. Funciona desde cualquier parte de la página |
El principio: el servidor decide
El estado lo resuelve el servidor y lo estampa en el HTML. Alpine.js solo cambia atributos, nunca decide nada por primera vez.
Todo lo que se ve en el primer paint — el ancho del sidebar, qué item está activo, qué sub-menú sale abierto, si el layout es de escritorio o de móvil — se calcula en PHP y llega ya resuelto en el HTML. Alpine.js arranca después y se limita a reaccionar a interacciones: un clic, un hover, una tecla. Nunca decide el estado inicial, porque para cuando arranca, el estado inicial ya está en el DOM.
Es la diferencia entre un sidebar que aparece correcto y uno que aparece mal y se corrige de un salto medio segundo después.
La cookie kore_sidebar
El sidebar guarda si está colapsado en una cookie, no en localStorage.
Laravel lee la cookie kore_sidebar al renderizar y emite el HTML con el ancho correcto en el primer paint. No hay parpadeo ni salto de layout, y funciona aunque el JavaScript tarde en cargar o no cargue nunca.
Con localStorage esto sería imposible: el estado solo se conocería después de que arrancara Alpine, así que el sidebar aparecería ancho y se encogería a la vista del usuario en cada carga. Es el mismo patrón que usan shadcn/ui y Nuxt UI, y en Laravel sale gratis.
La cookie guarda todos los sidebars de la app en un solo mapa ({"main":1,"tools":0}) y va registrada en EncryptCookies::except(). Es entrada no confiable — cualquiera puede editarla desde la consola — así que se valida su tamaño, su forma y sus claves antes de que llegue al HTML.
Layout móvil con CSS puro
Por debajo del breakpoint el sidebar se convierte en un drawer, y eso lo resuelven media queries, no JavaScript.
Es correcto en el primer paint y en cada cambio de tamaño de ventana, sin depender de matchMedia ni de ningún cálculo en el cliente. El breakpoint que separa escritorio de móvil se declara una vez (prop breakpoint del sidebar) y de ahí sale el CSS que decide cuándo el sidebar es parte de la página y cuándo es un drawer.
Ejemplo completo
Shell + sidebar + navbar + items, todo junto.
<x-kore::shell>
<x-slot:sidebar>
<x-kore::sidebar :navigate="true">
<x-slot:header>
<span class="kore-sidebar-label font-bold">Mi App</span>
<span class="kore-sidebar-mini font-bold">M</span>
</x-slot:header>
<x-kore::sidebar.item label="Panel" icon="layout-dashboard" route="dashboard" />
<x-kore::sidebar.group label="Gestión">
<x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" match="users.*" badge="12" />
<x-kore::sidebar.item label="Roles" icon="shield" route="roles.index" match="roles.*" />
</x-kore::sidebar.group>
<x-slot:footer>
<x-kore::sidebar.item label="Cerrar sesión" icon="log-out" href="/logout" />
</x-slot:footer>
</x-kore::sidebar>
</x-slot:sidebar>
<x-slot:navbar>
<x-kore::navbar>
<x-slot:end>
<x-kore::theme-switch size="sm" />
</x-slot:end>
</x-kore::navbar>
</x-slot:navbar>
{{ $slot }}
</x-kore::shell><x-kore::shell>
<x-slot:sidebar>
<x-kore::sidebar :navigate="true">
<x-slot:header>
<span class="kore-sidebar-label font-bold">Mi App</span>
<span class="kore-sidebar-mini font-bold">M</span>
</x-slot:header>
<x-kore::sidebar.item label="Panel" icon="layout-dashboard" route="dashboard" />
<x-kore::sidebar.group label="Gestión">
<x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" match="users.*" badge="12" />
<x-kore::sidebar.item label="Roles" icon="shield" route="roles.index" match="roles.*" />
</x-kore::sidebar.group>
<x-slot:footer>
<x-kore::sidebar.item label="Cerrar sesión" icon="log-out" href="/logout" />
</x-slot:footer>
</x-kore::sidebar>
</x-slot:sidebar>
<x-slot:navbar>
<x-kore::navbar>
<x-slot:end>
<x-kore::theme-switch size="sm" />
</x-slot:end>
</x-kore::navbar>
</x-slot:navbar>
{{ $slot }}
</x-kore::shell>Nada de esto se renderiza en vivo en esta página: el shell es un layout de página completa y montarlo dentro de otra página (como esta) rompería el layout de los docs. Pruébalo en la demo funcionando.
Instalación
Los componentes vienen con la librería; no hay que registrar nada. Solo dos cosas.
1. @koreScripts en el layout, como el resto de KoreUi
<body>
{{-- tu contenido --}}
@livewireScripts
@koreScripts
</body><body>
{{-- tu contenido --}}
@livewireScripts
@koreScripts
</body>2. Que Tailwind vea las vistas del paquete, en tu CSS
@import 'tailwindcss';
@import '../../vendor/koreui/kore-ui/resources/css/kore-theme.css';
@source '../../vendor/koreui/kore-ui/resources/**/*.blade.php';@import 'tailwindcss';
@import '../../vendor/koreui/kore-ui/resources/css/kore-theme.css';
@source '../../vendor/koreui/kore-ui/resources/**/*.blade.php';Sin el @source, Tailwind no generará las clases que usan los componentes y el sidebar se verá sin estilos.
Una limitación que conviene conocer
El sidebar es position: fixed.
Eso deja de funcionar si algún ancestro del <x-kore::shell> tiene transform, filter, perspective, contain o will-change: cualquiera de esas propiedades convierte al ancestro en el bloque contenedor, y el sidebar se posicionaría respecto a él en vez de respecto a la ventana.
No es un capricho de KoreUi, es cómo funciona CSS. Si el sidebar aparece en un sitio raro, busca un transform en los contenedores de arriba.
Demo funcionando
El shell completo, en vivo, en su propia página pública.
La demo en vivo monta el ejemplo completo: sidebar con grupos, un desplegable de tres niveles, badges, footer con «Cerrar sesión» y navbar con theme-switch y avatar. Se abre en página completa (es un layout que ocupa el viewport).
- Colapsa el sidebar y recarga con F5: sigue colapsado, sin ningún salto.
- Mira el código fuente de la página (no DevTools):
data-kore-sidebarya viene resuelto del servidor. - Entra en «Perfil» o «Seguridad»: el grupo «Ajustes» sale ya abierto, sin parpadeo.
- Estrecha la ventana por debajo de 1024px: el sidebar se convierte en un drawer.
- Con el drawer abierto, la página de detrás no hace scroll.