K
KoreUI

Shell - Primeros pasos

Shell

El 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.

Ruta activa y sub-menús abiertos

El item que apunta a la página actual se marca solo, en PHP.

Si un sub-item está activo, su padre — y el padre de su padre — salen ya abiertos en el HTML. Nada de menús que se despliegan de golpe al arrancar el JS: la rama completa hasta la hoja activa llega desplegada desde el servidor.

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.

resources/views/layouts/admin.blade.php
<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-sidebar ya 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.
Abrir la demo del shell
Cargando