Sidebar
Sidebar
Usage
Template macros available
This component ships a sidebar() macro for Jinja and Nunjucks.
Sidebar uses a native <aside> next to your page content. 5h3ll-ui owns the fixed positioning, overlay behavior, active states, nested lists, and details submenu styling. Your app still owns the navigation data and current route.
Include CSS
Import Tailwind and one full 5h3ll-ui style bundle.
@import "tailwindcss";
@import "5h3ll-ui/vega.css";Or import only the base CSS, Sidebar component CSS, and one style pack.
@import "tailwindcss";
@import "5h3ll-ui/base.css";
@import "5h3ll-ui/components/sidebar.css";
@import "5h3ll-ui/styles/vega.css";Using CDN or bundler imports? See the Installation page.
Include JavaScript
Copy or serve the full 5h3ll-ui JavaScript bundle.
<script src="/assets/js/all.min.js" defer></script>Or copy or serve the 5h3ll-ui runtime and Sidebar script.
<script src="/assets/js/5h3ll-ui.min.js" defer></script>
<script src="/assets/js/sidebar.min.js" defer></script>Using CDN or bundler imports? See the Installation page.
Add your sidebar HTML
<aside id="sidebar" class="sidebar" data-side="left">
<nav aria-label="Sidebar navigation">
<section class="scrollbar-sm">
<div role="group" aria-labelledby="group-label-content-1">
<h3 id="group-label-content-1">Getting started</h3>
<ul>
<li>
<a href="#">
<svg class="lucide lucide-square-terminal" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 11 2-2-2-2" /><path d="M11 13h4" /><rect width="18" height="18" x="3" y="3" rx="2" ry="2" /></svg>
<span>Playground</span>
</a>
</li>
<li>
<a href="#">
<svg class="lucide lucide-bot" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8" /><rect width="16" height="12" x="4" y="8" rx="2" /><path d="M2 14h2" /><path d="M20 14h2" /><path d="M15 13v2" /><path d="M9 13v2" /></svg>
<span>Models</span>
</a>
</li>
<li>
<details id="submenu-content-1-3">
<summary aria-controls="submenu-content-1-3-content"><svg class="lucide lucide-settings" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
</summary>
<ul id="submenu-content-1-3-content">
<li><a href="#"><span>General</span></a></li>
<li><a href="#"><span>Team</span></a></li>
<li><a href="#"><span>Billing</span></a></li>
<li><a href="#"><span>Limits</span></a></li>
</ul>
</details>
</li>
</ul>
</div>
</section>
</nav>
</aside>
<main>
<button type="button" onclick="document.getElementById('sidebar')?.toggle()">Toggle sidebar</button>
<h1>Content</h1>
</main> RTL
Set dir="rtl" on the sidebar or a parent element. Logical spacing, nested borders, and item content follow document direction. data-side is physical: left and right refer to viewport sides.
Differences with shadcn/ui
5h3ll-ui intentionally supports the stable HTML sidebar surface: fixed left/right sidebars, mobile overlay behavior, grouped navigation, active states, and native details submenus. It does not expose shadcn/ui’s React provider, rail, inset/floating variants, icon-only collapse mode, menu actions, menu badges, or menu skeleton API.
HTML structure
5h3ll-ui maps shadcn/ui’s sidebar composition to regular navigation HTML: an <aside> root, one <nav>, optional header/footer regions, grouped lists, item controls, and native <details> for nested menus.
The docs shell uses the same sidebar header markup in two places: the sidebar header and the sticky page header. Mark the sidebar copy with data-sidebar-brand="sidebar" and the sticky-header copy with data-sidebar-brand="header". The Sidebar script hides the sticky-header brand while the sidebar is open, then shows it before the toggle button when the sidebar closes.
<aside class=“sidebar”>Sidebar root. Supports
id,data-side=“left|right”,data-initial-open=“false”,data-initial-mobile-open=“true”, anddata-breakpoint. JavaScript managesaria-hidden,inert, and the internaldata-sidebar-initializedflag.<nav aria-label=”…”>Navigation landmark inside the sidebar.
<header>- Optional header for branding, workspace controls, user controls, or persistent actions.
<section>Scrollable content region between the optional header and footer.
<div role=“group” aria-labelledby=”…”>- Named navigation group. Use a heading element as the group label.
<ul>+<li>Menu and menu item structure. Top-level lists and nested submenu lists receive different spacing and borders.
<a>/<button>- Menu item control. Use links for navigation and buttons for actions. Supports
data-variant=“default|outline”,data-size=“default|sm|lg”,data-active=“true”,disabledon buttons, andaria-disabled=“true”on custom-disabled controls. For links, prefer<a aria-current=“page”>for the current page. Adddata-keep-mobile-sidebar-openon a clicked link, button, or ancestor when that control should not close the mobile sidebar. <details>/<summary>- Native disclosure for collapsible submenus. Put the nested
<ul>directly after<summary>. The<summary>supports the same active, variant, and size attributes as menu item controls. Addidandaria-controlswhen the relationship is not obvious from nearby markup. <hr role=“separator”>- Optional separator between groups or menu sections.
<footer>- Optional footer for account controls, status, or persistent actions.
<main>- Sibling content wrapper. The desktop margin applies to the sibling immediately after the sidebar.
Set --sidebar-width and --sidebar-mobile-width on :root or the sidebar root to override the default 16rem desktop width and 18rem mobile width.
JavaScript API
Header brand mirroring
Use this pattern when a fixed or sticky page header should inherit the sidebar brand after the sidebar closes.
<aside id="sidebar" class="sidebar" data-side="left">
<nav aria-label="Sidebar navigation">
<header>
<a href="/" class="btn h-12 w-full justify-start p-2" data-sidebar-brand="sidebar" data-variant="ghost" data-size="lg">
<span class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">...</span>
<span class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">5h3ll-ui</span>
<span class="truncate text-xs text-muted-foreground">v0.1.5</span>
</span>
</a>
</header>
</nav>
</aside>
<main>
<header class="sticky top-0 flex items-center gap-2">
<a href="/" class="btn h-10 max-w-52 shrink-0 justify-start gap-2 px-2" data-sidebar-brand="header" data-variant="ghost" data-size="sm" hidden>
<span class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-7 items-center justify-center rounded-md">...</span>
<span class="grid min-w-0 text-left text-sm leading-tight">
<span class="truncate font-semibold">5h3ll-ui</span>
<span class="truncate text-xs text-muted-foreground">v0.1.5</span>
</span>
</a>
<button type="button" onclick="document.getElementById('sidebar')?.toggle()">Toggle sidebar</button>
</header>
</main>