Skip to content
wdj

Tabs

Implemented Navigation

Segmented and underline tabs with a sliding indicator, responsive overflow and local Alpine or URL/HTMX state.

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

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

Variants (default)

Segmented (default)

Underline

Show template
{% load wdj %}
<div class="flex w-full flex-col items-center gap-6 py-2">
  <div><p class="mb-2 text-base text-kumo-subtle">Segmented (default)</p>{% include "wdj/components/tabs.html" with id="default-segmented-tabs" items=demo_tab_items mode="local" only %}</div>
  <div><p class="mb-2 text-base text-kumo-subtle">Underline</p>{% include "wdj/components/tabs.html" with id="default-underline-tabs" items=demo_tab_items mode="local" variant="underline" only %}</div>
</div>

Segmented

Show template
{% load wdj %}
{% include "wdj/components/tabs.html" with id="segmented-demo-tabs" items=demo_tab_items mode="local" only %}

Underline

Show template
{% load wdj %}
{% include "wdj/components/tabs.html" with id="underline-demo-tabs" items=demo_tab_items mode="local" variant="underline" only %}

Small

Segmented sm

Underline sm

Show template
{% load wdj %}
<div class="grid gap-6">
  <div><p class="mb-2 text-base text-kumo-subtle">Segmented sm</p>{% include "wdj/components/tabs.html" with id="small-segmented-tabs" items=demo_tab_items mode="local" size="sm" only %}</div>
  <div><p class="mb-2 text-base text-kumo-subtle">Underline sm</p>{% include "wdj/components/tabs.html" with id="small-underline-tabs" items=demo_tab_items mode="local" size="sm" variant="underline" only %}</div>
</div>

Controlled

Active tab: tab1

Show template
{% load wdj %}
<div class="grid gap-4" x-data="{ value: 'tab1' }" @wdj:tab-change="value = $event.detail.value">
  <div x-ref="local">{% include "wdj/components/tabs.html" with id="controlled-demo-tabs" mode="local" items=demo_tab_items only %}</div>
  <p class="text-base text-kumo-subtle">Active tab: <code data-tabs-value class="font-mono text-[0.9em]" x-text="value">tab1</code></p>
  <div><button type="button" class="rounded-md bg-kumo-base px-2.5 py-1 text-base ring-1 ring-kumo-line hover:bg-kumo-tint" @click="$refs.local.querySelector('[data-wdj-tabs]').dispatchEvent(new CustomEvent('wdj:set-tab', { detail: { value: 'tab1' } }))">Reset selected tab</button></div>
</div>

Many tabs

Show template
{% load wdj %}
<div class="w-full max-w-md">{% include "wdj/components/tabs.html" with id="many-demo-tabs" mode="local" items=many_local_tabs only %}</div>

Horizontal overflow

Show template
{% load wdj %}
<div class="w-full max-w-xs">{% include "wdj/components/tabs.html" with id="overflow-demo-tabs" mode="local" items=many_local_tabs only %}</div>

Dynamic tab count

10 tabs
Show template
{% load wdj %}
<div class="grid min-w-0 gap-3" x-data="{ extra: true }">
  <div x-ref="tabs" class="min-w-0 w-full max-w-[588px]">{% include "wdj/components/tabs.html" with id="dynamic-demo-tabs" mode="local" selected="settings" items=dynamic_local_tabs only %}</div>
  <div class="flex flex-wrap items-center gap-3">
    <button type="button" class="rounded-md bg-kumo-base px-2.5 py-1 text-base ring-1 ring-kumo-line hover:bg-kumo-tint" @click="extra = !extra; $refs.tabs.querySelectorAll('[data-tab-extra]').forEach(tab => tab.hidden = !extra)">Toggle extra tabs</button>
    <span data-tabs-count class="text-base text-kumo-subtle" x-text="extra ? '10 tabs' : '7 tabs'">10 tabs</span>
  </div>
</div>

Icons and badges

Show template
{% load wdj %}
{% include "wdj/components/tabs.html" with items=decorated_tabs only %}

Disabled tab

Show template
{% load wdj %}
{% include "wdj/components/tabs.html" with items=disabled_tabs only %}

HTMX content

Analytics

Inspect synthetic request and cache statistics.

Show template
{% load wdj %}
{% include "showcase/_tabs_example.html" %}

URL state

Show template
{% load wdj %}
<div class="grid gap-3">{% include "wdj/components/tabs.html" with items=tab_items variant="underline" label="Current URL tabs" only %}<p class="text-base text-kumo-subtle">Current tab: {{ selected_tab }}</p></div>

Overflow

Show template
{% load wdj %}
{% include "wdj/components/tabs.html" with items=long_tabs class="w-full max-w-xs" only %}

Local panels (Alpine)

Project configuration, rendered locally.
Preferences change without a server request.
Disabled panel.
Show template
{% load wdj %}
{% include "wdj/components/tabs.html" with id="local-project-tabs" mode="local" items=local_tab_items label="Local project tabs" only %}

Local rich content

Local panels can contain any wdj control.

This panel cannot be selected.
Show template
{% load wdj %}
{% wdj_component "wdj/components/tabs.html" id="rich-project-tabs" mode="local" variant="underline" items=local_tab_items label="Local settings tabs" %}
  {% wdj_component "wdj/components/tab_panel.html" key="overview" %}<p>Local panels can contain any wdj control.</p>{% endwdj_component %}
  {% wdj_component "wdj/components/tab_panel.html" key="settings" %}
    {% include "wdj/components/switch.html" with name="tabs_notifications" label="Notifications" checked=True only %}
  {% endwdj_component %}
  {% include "wdj/components/tab_panel.html" with key="disabled" text="This panel cannot be selected." only %}
{% endwdj_component %}

API Reference

Prop Type Default Description
list[dict] [] Local: key/label/content?/disabled?/icon?/badge?/attrs?. Navigation: label/href/active?/disabled?/hx_get?/hx_target?.
navigation | local navigation Real URL/HTMX links or local Alpine tab buttons and panels.
segmented | underline segmented Filled track with a sliding pill, or a moving underline.
base | sm base Segmented: 36/26px. Underline: 30/26px. Text: 14/12px.
string generated Unique local root ID; tab/panel IDs derive from it.
string first enabled Initial local item key; navigation selection comes from items[].active.
bool False Local mode: activate on focus instead of requiring Enter/Space.
string Tabs Accessible label for the navigation or local tablist.
string Scroll tabs left / right Localized accessible names for segmented overflow controls.
string Additional literal root classes; width is constrained by the parent.

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