Sidebar

x-teleport 渲染到 body 末尾的抽屉类组件。点击 trigger slot 打开,Escape / × / 背景点击关闭。side(左/右)、size(sm/md/lg)、backdrop 3 个轴生成变体。x-trap.inert.noscroll 和 role=dialog/aria-modal=true 保障 a11y。backdrop=false 时整个叠加层 pointer-events-none,仅面板 pointer-events-auto,后台页面可操作(适用于永久检查器、工具调色板)。

1. 最简: 附带 trigger slot (default = side=left, size=md, 有 backdrop)

<x-sidebar>
    <x-slot:trigger>
        <x-button>Open</x-button>
    </x-slot:trigger>
    <p>Drawer content</p>
</x-sidebar>
<div x-data="{ open: false }" x-on:open-sidebar-sidebar_6a7e9b7b83fa9.window="open = true" x-on:close-sidebar-sidebar_6a7e9b7b83fa9.window="open = false">
<div @click="open = true" class="cursor-pointer">
<button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer">Open</button>
</div>
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50" x-cloak="">
<div class="absolute inset-0 bg-black/50" @click="open = false"></div>
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[-100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[-100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 left-0 h-full w-80 bg-base-100 text-base-content border-r border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Drawer content
                </p>
</div>
</div>
</template>
</div>

2. 右侧配置 + lg 尺寸

<x-sidebar side="right" size="lg">
    <x-slot:trigger><x-button>Open details</x-button></x-slot:trigger>
    <p>Details panel</p>
</x-sidebar>
<div x-data="{ open: false }" x-on:open-sidebar-sidebar_6a7e9b7b8bdb1.window="open = true" x-on:close-sidebar-sidebar_6a7e9b7b8bdb1.window="open = false">
<div @click="open = true" class="cursor-pointer">
<button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer">Open details</button>
</div>
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50" x-cloak="">
<div class="absolute inset-0 bg-black/50" @click="open = false"></div>
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 right-0 h-full w-96 bg-base-100 text-base-content border-l border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Details panel
                </p>
</div>
</div>
</template>
</div>

3. 无 backdrop — 用于常驻 inspector / 工具面板场景。overlay 为 pointer-events-none,仅 panel 为 pointer-events-auto,因此可在保持 drawer 打开的同时操作主页面

<x-sidebar :backdrop="false">
    <x-slot:trigger><x-button>Inspector</x-button></x-slot:trigger>
    <p>Inspector content — the page behind stays interactive.</p>
</x-sidebar>
<div x-data="{ open: false }" x-on:open-sidebar-sidebar_6a7e9b7b93928.window="open = true" x-on:close-sidebar-sidebar_6a7e9b7b93928.window="open = false">
<div @click="open = true" class="cursor-pointer">
<button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer">Inspector</button>
</div>
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50 pointer-events-none" x-cloak="">
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[-100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[-100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 left-0 h-full w-80 bg-base-100 text-base-content border-r border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto pointer-events-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Inspector content — the page behind stays interactive.
                </p>
</div>
</div>
</template>
</div>

4. 指定 id + 通过 dispatch 从别处开启

<x-sidebar id="filters" side="right">
    <p>Filter UI</p>
</x-sidebar>

<x-button x-on:click="$dispatch('open-sidebar-filters')">Filters</x-button>
<div x-data="{ open: false }" x-on:open-sidebar-filters.window="open = true" x-on:close-sidebar-filters.window="open = false">
<template x-teleport="body">
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 z-50" x-cloak="">
<div class="absolute inset-0 bg-black/50" @click="open = false"></div>
<div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="translate-x-[100%]" x-transition:enter-end="translate-x-0" x-transition:leave="transition ease-in duration-200" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-[100%]" x-trap.inert.noscroll="open" @keydown.escape.window="open = false" class="absolute top-0 right-0 h-full w-80 bg-base-100 text-base-content border-l border-base-300 shadow-[0_0_40px_-8px_rgb(0_0_0_/_0.20),var(--shadow-box)] p-lg overflow-y-auto" role="dialog" aria-modal="true">
<button type="button" @click="open = false" class="absolute top-lg right-lg z-10 text-base-content/50 hover:text-base-content transition-colors rounded-[var(--radius-field)] p-1 hover:bg-base-200" aria-label="Close"><svg class="w-5 h-5" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg></button>
<p>
                    Filter UI
                </p>
</div>
</div>
</template>
</div><button class="inline-flex items-center justify-center whitespace-nowrap font-medium tracking-wide transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-[var(--radius-field)] border-[length:var(--border)] h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] gap-2 bg-primary text-primary-content border-primary hover:bg-primary/90 focus-visible:ring-primary cursor-pointer" x-on:click="$dispatch('open-sidebar-filters')">Filters</button>

side=left / size=md / backdrop=true / closeOnBackdrop=true / escape=true — 在各 iframe 内点击 trigger 查看效果

左侧 (default) — side=left, size=md, backdrop=true

右侧 — side=right, size=md, backdrop=true

no-backdrop — :backdrop="false" / 背景可操作

with-content — 实用的导航示例 (logo + menu + user)

props 一览

  • id — string|null (default: sidebar_{uniqid()})。通过 dispatch 从外部开关时指定
  • side'left' | 'right' (default: 'left')
  • size'sm' (w-64) | 'md' (w-80) | 'lg' (w-96) (default: 'md')
  • backdrop — bool (default: true)。false 时无半透明黑色蒙层,背景可操作
  • closeOnBackdrop — bool (default: true)。仅在 backdrop=true 时生效
  • escape — bool (default: true)。按 Escape 键关闭

props: id, side='left', size='md', backdrop=true, closeOnBackdrop=true, escape=true / size: sm (w-64) / md (w-80, default) / lg (w-96) / a11y: role=dialog, aria-modal=true, x-trap.inert.noscroll / 渲染: 通过 x-teleport 传送至 body 直属 / slide-in 动画