Skip to content
wdj

Sidebar

Implemented Navigation

Recursive navigation groups, account/zone views, search, loading, resize and full-screen mobile breadcrumbs.

Kumo reference: kumo-ui.com/components/sidebar/

Presentation commands show demo feedback. Read the interaction guide to connect real actions.

Basic

Reference groups with nested Compute / Workers & Pages navigation.

Main content area
Show template
{% load wdj %}
<div class="flex h-auto flex-col sm:h-[500px] sm:flex-row [&>.wdj-sidebar]:h-[400px] sm:[&>.wdj-sidebar]:h-full w-full min-w-0 overflow-hidden rounded-lg ring-1 ring-kumo-line" x-data="{ selected: 'Main content area' }" @wdj:sidebar-select="selected = $event.detail.label">
  {% include "wdj/components/sidebar.html" with brand_mark=True groups=sidebar_reference_groups contained=True show_header=False show_trigger=False aria_label="Basic navigation" default_width=260 persist_key="sidebar-reference-basic" only %}
  <div class="flex min-w-0 flex-1 items-center justify-center p-4 text-center text-base text-kumo-subtle" aria-live="polite" x-text="selected">Main content area</div>
</div>

Grouped, collapsible rail

Labelled groups, icon-only collapse, hover-peek and drag-to-resize.

Labelled groups with an icon rail: collapse with the control at the bottom, hover the rail to peek, and drag the edge (or use the arrow keys) to resize.
Show template
{% load wdj %}
<div class="w-full sm:h-96 overflow-hidden rounded-lg border border-kumo-hairline">
  <div class="flex flex-col sm:flex-row h-full">
    {% include "wdj/components/sidebar.html" with brand_mark=True groups=sidebar_groups aria_label="Grouped navigation" brand_label="Console" brand_initials="C" collapsible="icon" peekable=True resizable=True contained=True default_width=232 persist_key="wdj-ex-sidebar-groups" %}
    <div class="min-w-0 flex-1 p-4 text-base text-kumo-subtle">
      Labelled groups with an icon rail: collapse with the control at the bottom,
      hover the rail to peek, and drag the edge (or use the arrow keys) to resize.
    </div>
  </div>
</div>

Sliding views

Swap the nav body between views inside one panel footprint.

Sliding views: the segmented control at the top of the rail swaps the nav content with a horizontal slide, keeping a single panel footprint.
Show template
{% load wdj %}
<div class="w-full sm:h-96 overflow-hidden rounded-lg border border-kumo-hairline">
  <div class="flex flex-col sm:flex-row h-full">
    {% include "wdj/components/sidebar.html" with brand_mark=True views=sidebar_views active_view="main" aria_label="Sliding views" brand_label="Console" brand_initials="C" contained=True persist_key="wdj-ex-sidebar-views" %}
    <div class="min-w-0 flex-1 p-4 text-base text-kumo-subtle">
      Sliding views: the segmented control at the top of the rail swaps the nav
      content with a horizontal slide, keeping a single panel footprint.
    </div>
  </div>
</div>

Nested items

Expandable sub-menus built on native <details>.

Nested groups use native <details> disclosures, so they open and close without JavaScript.
Show template
{% load wdj %}
<div class="w-full sm:h-88 overflow-hidden rounded-lg border border-kumo-hairline">
  <div class="flex flex-col sm:flex-row h-full">
    {% include "wdj/components/sidebar.html" with brand_mark=True items=sidebar_items_nested aria_label="Nested navigation" brand_label="Console" brand_initials="C" collapsible="none" contained=True persist_key="wdj-ex-sidebar-nested" %}
    <div class="min-w-0 flex-1 p-4 text-base text-kumo-subtle">
      Nested groups use native &lt;details&gt; disclosures, so they open and close
      without JavaScript.
    </div>
  </div>
</div>

Static rail

Show template
{% load wdj %}
{% include "wdj/components/sidebar.html" with brand_mark=True contained=True aria_label="Static rail" persist_key="sidebar_static_rail" items=sidebar_items collapsible="none" only %}

Right side

Show template
{% load wdj %}
{% include "wdj/components/sidebar.html" with brand_mark=True contained=True aria_label="Right-side rail" persist_key="sidebar_right_side" groups=sidebar_groups side="right" only %}

Loading

Show template
{% load wdj %}
<div class="flex flex-col gap-3">
  <button type="button" data-wdj-sidebar-loading-toggle aria-pressed="true" class="h-9 w-fit rounded-lg bg-kumo-fill px-3 text-base">Toggle loading</button>
  <div class="h-96 overflow-hidden rounded-lg ring-1 ring-kumo-line">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True aria_label="Loading rail" persist_key="sidebar_loading" groups=sidebar_reference_groups loading=True only %}</div>
</div>

Initially collapsed

Show template
{% load wdj %}
<div class="flex flex-col sm:flex-row sm:h-80 w-full overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True items=sidebar_items persist_key="sidebar_initially_collapsed" aria_label="Initially collapsed" default_open=False only %}<div class="min-w-0 flex-1 p-4 text-kumo-subtle">Initially collapsed</div></div>

Peek on hover

Show template
{% load wdj %}
<div class="flex flex-col sm:flex-row sm:h-80 w-full overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True items=sidebar_items persist_key="sidebar_peek_on_hover" aria_label="Peek on hover" default_open=False peekable=True only %}<div class="min-w-0 flex-1 p-4 text-kumo-subtle">Peek on hover</div></div>

Keyboard resizing

Show template
{% load wdj %}
<div class="flex flex-col sm:flex-row sm:h-80 w-full overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True items=sidebar_items persist_key="sidebar_keyboard_resizing" aria_label="Keyboard resizing" resizable=True only %}<div class="min-w-0 flex-1 p-4 text-kumo-subtle">Keyboard resizing</div></div>

Wide rail

Show template
{% load wdj %}
<div class="flex flex-col sm:flex-row sm:h-80 w-full overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True items=sidebar_items persist_key="sidebar_wide_rail" aria_label="Wide rail" default_width=300 only %}<div class="min-w-0 flex-1 p-4 text-kumo-subtle">Wide rail</div></div>

Nested navigation

Show template
{% load wdj %}
<div class="h-[500px] overflow-hidden rounded-lg ring-1 ring-kumo-line">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True aria_label="Nested navigation example" persist_key="sidebar_nested_navigation" groups=sidebar_reference_groups only %}</div>

Settings view

Settings view
Show template
{% load wdj %}
<div class="flex flex-col sm:flex-row sm:h-80 w-full overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True contained=True items=sidebar_items persist_key="sidebar_settings_view" aria_label="Settings view" views=sidebar_views active_view="settings" only %}<div class="min-w-0 flex-1 p-4 text-kumo-subtle">Settings view</div></div>

Full example

Account/zone views, quick search, nested navigation, badges, resizing and collapse.

Main content area
Show template
{% load wdj %}
<div x-data="{ selected: 'Main content area', query: '' }" @wdj:sidebar-select="selected = $event.detail.label">
  <div class="mb-3 flex flex-wrap gap-2">
    {% include "wdj/components/button.html" with label="Quick search" icon="search" attrs=sidebar_search_attrs only %}
    <button type="button" data-wdj-sidebar-scroll="zone-security" class="h-9 rounded-lg bg-kumo-fill px-3 text-base">Go to zone security</button>
  </div>
  <div class="flex h-auto flex-col sm:h-[500px] sm:flex-row [&>.wdj-sidebar]:h-[400px] sm:[&>.wdj-sidebar]:h-full min-w-0 overflow-hidden rounded-lg ring-1 ring-kumo-line">
    {% include "wdj/components/sidebar.html" with brand_mark=True views=sidebar_reference_views contained=True brand_label="Console" view_controls="header" resizable=True peekable=True auto_scroll=True aria_label="Full example navigation" default_width=240 persist_key="sidebar-reference-full" only %}
    <div class="flex min-w-0 flex-1 items-center justify-center p-4 text-center text-base text-kumo-subtle" aria-live="polite" x-text="selected">Main content area</div>
  </div>
  {% wdj_component "wdj/components/dialog.html" id="sidebar-reference-search" title="Quick search" %}
    <div class="flex flex-col gap-3">
      <input type="search" aria-label="Search navigation" x-model="query" placeholder="Search navigation…" class="h-9 w-full rounded-lg bg-kumo-control px-3 text-base ring-1 ring-kumo-line">
      {% for group in sidebar_reference_groups %}{% for item in group.items %}
        {% if item.value %}<button type="button" class="min-h-9 rounded-lg px-3 text-left text-base hover:bg-kumo-tint" x-show="$el.textContent.toLowerCase().includes(query.toLowerCase())" @click="selected = $el.textContent.trim()" data-wdj-dialog-close>{{ item.label }}</button>{% endif %}
      {% endfor %}{% endfor %}
    </div>
  {% endwdj_component %}
</div>

Mobile

Mobile drawer preview with focus trapping, Escape and dismiss controls.

Main content area
Show template
{% load wdj %}
<div class="flex flex-col gap-3">
  <button type="button" data-wdj-sidebar-open="sidebar-reference-mobile" aria-expanded="false" class="h-9 w-fit rounded-lg bg-kumo-fill px-3 text-base">Open mobile navigation</button>
  <div class="relative h-[500px] w-full overflow-hidden rounded-lg ring-1 ring-kumo-line">
    <div class="absolute inset-0 flex items-center justify-center p-4 text-base text-kumo-subtle">Main content area</div>
    {% include "wdj/components/sidebar.html" with brand_mark=True groups=sidebar_reference_groups contained=True mobile_preview=True mobile_breakpoint=9999 show_trigger=False brand_label="Console" aria_label="Mobile preview navigation" persist_key="sidebar-reference-mobile" only %}
  </div>
</div>

Full-screen mobile preview

Adjust the phone width, open nested navigation and return with the breadcrumb controls.

Main content area
Show template
{% load wdj %}
<div class="flex flex-col gap-3" x-data="{ viewport: 375, selected: 'Main content area' }" @wdj:sidebar-select="selected = $event.detail.label">
  <label class="flex flex-wrap items-center gap-2 text-base">Viewport width <input type="range" min="320" max="768" value="375" x-model="viewport" aria-label="Mobile viewport width" class="min-w-0 max-w-full"><output x-text="viewport + 'px'">375px</output></label>
  <button type="button" data-wdj-sidebar-open="sidebar-reference-mobile-full" aria-expanded="false" class="h-9 w-fit rounded-lg bg-kumo-fill px-3 text-base">Open full-screen mobile preview</button>
  <div class="relative h-[500px] max-w-full overflow-hidden rounded-lg ring-1 ring-kumo-line" :style="'width:' + viewport + 'px'">
    <div class="absolute inset-0 flex items-center justify-center p-4 text-base text-kumo-subtle" x-text="selected" aria-live="polite">Main content area</div>
    {% include "wdj/components/sidebar.html" with brand_mark=True groups=sidebar_reference_groups contained=True mobile_preview=True mobile_breakpoint=9999 full_screen_mobile=True show_trigger=False brand_label="Console" aria_label="Full-screen mobile preview navigation" persist_key="sidebar-reference-mobile-full" only %}
  </div>
</div>

Auto scroll to active item

Show template
{% load wdj %}
<div class="h-80 overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True groups=sidebar_auto_groups contained=True auto_scroll=True brand_label="Project navigation" persist_key="audit-sidebar-auto" only %}</div>

Scroll to item

Show template
{% load wdj %}
<button type="button" data-wdj-sidebar-scroll="/guide/?section=30" class="h-9 w-fit rounded-lg bg-kumo-fill px-3 text-base">Scroll to section 30</button><div class="h-80 overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True items=audit_sidebar_items contained=True brand_label="Sections" persist_key="audit-sidebar-scroll" only %}</div>

Transition completion

Show template
{% load wdj %}
<div x-data="{ state: 'Choose a view' }" @wdj:sidebar-transition="state = 'Transition complete: ' + $event.detail.activeView"><div class="h-96 overflow-hidden rounded-lg ring-1 ring-kumo-hairline">{% include "wdj/components/sidebar.html" with brand_mark=True views=sidebar_views contained=True brand_label="Console" persist_key="audit-sidebar-transition" only %}</div><p class="mt-2 text-base" aria-live="polite" x-text="state"></p></div>

Full-screen mobile

Show template
{% load wdj %}
<button type="button" data-wdj-sidebar-open="audit-sidebar-fullscreen" class="h-9 w-fit rounded-lg bg-kumo-fill px-3 text-base lg:hidden">Open full-screen navigation</button><div class="flex min-w-0"><div class="lg:h-80">{% include "wdj/components/sidebar.html" with brand_mark=True items=audit_sidebar_items full_screen_mobile=True class="lg:static! lg:h-full!" brand_label="Full-screen navigation" persist_key="audit-sidebar-fullscreen" only %}</div><p class="min-w-0 flex-1 p-3 text-base text-kumo-subtle">This instance fills the phone viewport and restores focus when closed.</p></div>

API Reference

Prop Type Default Description
list[dict] unset Sidebar: label/items groups; items support recursive children, id, value, href, view, icon, badge, active and open.
list[dict] [] Component-specific item dictionaries; see each rendered source example and integration context.
list[dict] unset Views; pass the documented value shown in the examples.
string unset Active view; pass the documented value shown in the examples.
string unset Aria label; pass the documented value shown in the examples.
string unset Brand label; pass the documented value shown in the examples.
string unset Brand href; pass the documented value shown in the examples.
string unset Brand initials; pass the documented value shown in the examples.
string unset Brand mark; pass the documented value shown in the examples.
HTML slot unset Brand slot; pass the documented value shown in the examples.
HTML slot unset Header slot; pass the documented value shown in the examples.
HTML slot unset Footer slot; pass the documented value shown in the examples.
string / HTML slot unset Rich HTML supplied by the wdj_component block tag, rendered in an isolated context.
string icon Collapsible; pass the documented value shown in the examples.
bool True Default open; pass the documented value shown in the examples.
bool False Peekable; pass the documented value shown in the examples.
bool False Resizable; pass the documented value shown in the examples.
bool False Contained; pass the documented value shown in the examples.
number 240 Default width; pass the documented value shown in the examples.
number 180 Min width; pass the documented value shown in the examples.
number 420 Max width; pass the documented value shown in the examples.
string component-specific Side; pass the documented value shown in the examples.
bool False Loading; pass the documented value shown in the examples.
string unset Persist key; pass the documented value shown in the examples.
bool False Auto scroll; pass the documented value shown in the examples.
bool False Full screen mobile; pass the documented value shown in the examples.
bool True Show header; pass the documented value shown in the examples.
bool True Show trigger; pass the documented value shown in the examples.
string tabs Sidebar: tabs (default) or header buttons for account/zone switching.
bool False Sidebar: contain the mobile backdrop and focus trap within the example.
number 1024 Sidebar: enter drawer mode below this viewport width; default 1024px.
string unset Search target; pass the documented value shown in the examples.
string unset Search label; pass the documented value shown in the examples.
string unset Search aria label; pass the documented value shown in the examples.
string "" Class; pass the documented value shown in the examples.

Accessibility notes

Every interactive component keeps real semantics and keyboard support: native controls where possible, ARIA only where necessary, and a visible focus ring. See the Accessibility page for the shared contract.

Read the accessibility contract

Components

Components overview
AutocompleteFree-form input with local or HTMX-filtered suggestions.
BadgeSolid, tinted, outline and dot status labels.
BannerInline message bar with variants and optional action.
BreadcrumbsTrail navigation in two sizes.
ButtonVariants, sizes, shapes, icons and loading state.
ButtonGroupJoins a small set of tightly-coupled buttons into a single control (split buttons).
CheckboxSingle checkbox and indeterminate state.
ClipboardTextRead-only text field with a one-click copy-to-clipboard button.
Code / CodeHighlightedEscaped Pygments grammar highlighting, multiline tokens, line numbers, highlighted lines and localized copying.
CollapsibleComposable disclosure component for showing and hiding content.
Combobox / AutocompleteKumo-styled input/button triggers, search inside the popup, grouped local choices and debounced HTMX search.
CommandPaletteAccessible spotlight search overlay with Cmd+K keyboard shortcut.
DatePickerKumo-styled date and datetime popups; inline single, multiple and range calendars with bounds/presets.
DateRangePickerStyled popup or inline range calendar with bounds, presets and separately named form values.
DialogNative <dialog> modal with focus management.
DrawerEdge-anchored modal panel. LayerDialog has its own layered component.
DropdownMenuNative details menu with delegated keyboard navigation and dismissal.
EmptyEmpty-state placeholder with icon and action.
FieldLabel + description + error wrapper. Built into each control.
FlowDirected diagrams with branches, SVG connectors, custom anchors and responsive local scrolling.
GridResponsive CSS grid layout container with preset column configurations.
InlineCopyTextCompact borderless copy control for IDs and short values.
InputText field with label, description, leading/trailing icons.
InputAreaMulti-line textarea with auto-resize option.
InputGroupInput field with seamless prefix, suffix, and addon attachments.
LabelForm field label, optional marker and tooltip.
LayerCardCompound surface card with distinct primary and secondary layers.
LayerDialogLayered native modal with actions, forms, nested dialogs and pending states.
LinkInline, current and plain text links.
LoaderAnimated spinner in three sizes.
MenuBarCompact view selector using native radio semantics.
MeterProgress bar showing a measured value within a known range.
PaginationA page navigation component for navigating through paginated content.
PopoverAnchored popup with interactive content.
RadioRadio group, vertical and horizontal.
SelectKumo-styled Alpine dropdown with groups, rich options and multiple selection; native form values and fallback.
SensitiveInputPassword/secret input with reveal toggle and copy button.
SeparatorHorizontal / vertical rules with optional label.
SidebarRecursive navigation groups, account/zone views, search, loading, resize and full-screen mobile breadcrumbs.
SkeletonShimmer placeholder.
SliderInset single/range selection with vertical grips, value badges, endpoint labels and native keyboard/form behavior.
SurfaceBordered surface panel with header slot.
SwitchOn/off toggle in three sizes.
TableServer-driven table: sort, filter, select and paginate the full dataset.
Table of contentsPage section navigation tree with active link indicator.
TabsSegmented and underline tabs with a sliding indicator, responsive overflow and local Alpine or URL/HTMX state.
TagInputMulti-value badge input for tags and recipients.
TextHeading, body, secondary, success, error and mono text.
ToastyExpandable notification stack with variants, paused expiry and a live region.
ToolbarCompact grouped controls with shared borders, composed filters and keyboard arrow navigation.
TooltipHover and focus tooltip with grouped delays, Escape dismissal and viewport bounds.
↑ ↓ Navigate ↵ Select