Navigation

Side nav

<oblyx-side-nav> is a <nav> landmark that lays out whatever <oblyx-side-nav-heading>,<oblyx-side-nav-section>, and <oblyx-side-nav-item> children it's given, in document order. <oblyx-side-nav-item> can nest further <oblyx-side-nav-item> children of its own for multi-level menus. This is the sidebar container and its items only. There is no sidebar-level collapsed (icon-rail) mode, no drag-to-resize handle, and no app-shell wiring; those are real, larger scopes this component doesn't attempt yet.

Basic side nav

HTMLKotlin
<oblyx-side-nav label="Docs navigation">
  <oblyx-side-nav-heading heading="Oblyx" superheading="Documentation"></oblyx-side-nav-heading>
  <oblyx-side-nav-section label="Get started">
    <oblyx-side-nav-item label="Installation" href="/docs/install/"></oblyx-side-nav-item>
    <oblyx-side-nav-item label="Foundations" href="/docs/foundations/" aria-current="page"></oblyx-side-nav-item>
  </oblyx-side-nav-section>
  <oblyx-side-nav-section label="Components">
    <oblyx-side-nav-item label="Button" href="/docs/components/button/"></oblyx-side-nav-item>
    <oblyx-side-nav-item label="Link" href="/docs/components/link/"></oblyx-side-nav-item>
  </oblyx-side-nav-section>
</oblyx-side-nav>
oblyxSideNav(label = "Docs navigation") {
  oblyxSideNavHeading(heading = "Oblyx", superheading = "Documentation")
  oblyxSideNavSection(label = "Get started") {
    oblyxSideNavItem(label = "Installation", href = "/docs/install/")
    oblyxSideNavItem(label = "Foundations", href = "/docs/foundations/", current = "page")
  }
  oblyxSideNavSection(label = "Components") {
    oblyxSideNavItem(label = "Button", href = "/docs/components/button/")
    oblyxSideNavItem(label = "Link", href = "/docs/components/link/")
  }
}

Current item

Set the standard aria-current="page" attribute directly on the <oblyx-side-nav-item> that matches the current route. Unlike breadcrumbs, this component never compares its own href against the current URL or infers which item is current: the value is always server-computed and passed straight through onto the item's rendered <a>/<button>/<span>. The example above sets it on "Foundations".

Collapsible items

Set collapsible on an item that has nested <oblyx-side-nav-item> children (it has no effect on a childless item) and expanded to control whether those children currently show. When the item also has an href, the link and the expand/collapse toggle render as separate, sibling controls in the same row, specifically to avoid nesting a <button> inside an <a>: clicking the label navigates, clicking the chevron toggles.

Expandable group with a link

The row is both a link (navigates to Navigation's own href, if any) and a separate toggle button.

HTMLKotlin
<oblyx-side-nav label="Docs navigation">
  <oblyx-side-nav-section label="Components">
    <oblyx-side-nav-item label="Navigation" href="/docs/components/link/" collapsible expanded>
      <oblyx-side-nav-item label="Link" href="/docs/components/link/"></oblyx-side-nav-item>
      <oblyx-side-nav-item label="Tabs" href="/docs/components/tabs/"></oblyx-side-nav-item>
      <oblyx-side-nav-item label="Breadcrumbs" href="/docs/components/breadcrumbs/" aria-current="page"></oblyx-side-nav-item>
    </oblyx-side-nav-item>
  </oblyx-side-nav-section>
</oblyx-side-nav>
oblyxSideNav(label = "Docs navigation") {
  oblyxSideNavSection(label = "Components") {
    oblyxSideNavItem(label = "Navigation", href = "/docs/components/link/", collapsible = true, expanded = true) {
      oblyxSideNavItem(label = "Link", href = "/docs/components/link/")
      oblyxSideNavItem(label = "Tabs", href = "/docs/components/tabs/")
      oblyxSideNavItem(label = "Breadcrumbs", href = "/docs/components/breadcrumbs/", current = "page")
    }
  }
}

When a collapsible item has no href of its own, the whole row is a single <button> that both labels the group and toggles it.

Expandable group with no link

HTMLKotlin
<oblyx-side-nav label="Docs navigation">
  <oblyx-side-nav-item label="Settings" collapsible>
    <oblyx-side-nav-item label="Profile" href="/settings/profile/"></oblyx-side-nav-item>
    <oblyx-side-nav-item label="Billing" href="/settings/billing/"></oblyx-side-nav-item>
  </oblyx-side-nav-item>
</oblyx-side-nav>
oblyxSideNav(label = "Docs navigation") {
  oblyxSideNavItem(label = "Settings", collapsible = true) {
    oblyxSideNavItem(label = "Profile", href = "/settings/profile/")
    oblyxSideNavItem(label = "Billing", href = "/settings/billing/")
  }
}

Disabled

disabled follows the same disabled-link contract as oblyx-link: an item with href omits it entirely rather than merely looking disabled.

Disabled item

HTMLKotlin
<oblyx-side-nav label="Docs navigation">
  <oblyx-side-nav-item label="Active item" href="/docs/"></oblyx-side-nav-item>
  <oblyx-side-nav-item label="Locked section" href="/docs/enterprise/" disabled></oblyx-side-nav-item>
</oblyx-side-nav>
oblyxSideNav(label = "Docs navigation") {
  oblyxSideNavItem(label = "Active item", href = "/docs/")
  oblyxSideNavItem(label = "Locked section", href = "/docs/enterprise/", disabled = true)
}

Sizes

Item sizes

HTMLKotlin
<oblyx-side-nav label="Docs navigation">
  <oblyx-side-nav-item label="Small" href="/docs/" size="sm"></oblyx-side-nav-item>
  <oblyx-side-nav-item label="Medium" href="/docs/" size="md"></oblyx-side-nav-item>
  <oblyx-side-nav-item label="Large" href="/docs/" size="lg"></oblyx-side-nav-item>
</oblyx-side-nav>
oblyxSideNav(label = "Docs navigation") {
  oblyxSideNavItem(label = "Small", href = "/docs/", size = OblyxSideNavItemSize.SM)
  oblyxSideNavItem(label = "Medium", href = "/docs/", size = OblyxSideNavItemSize.MD)
  oblyxSideNavItem(label = "Large", href = "/docs/", size = OblyxSideNavItemSize.LG)
}

Hidden section header

header-hidden visually hides a section's title row while keeping it in the accessibility tree via role="group"/aria-labelledby. It is never display: none, so the section is still announced to assistive tech even though sighted users don't see the heading.

Section with hidden header

HTMLKotlin
<oblyx-side-nav label="Docs navigation">
  <oblyx-side-nav-section label="Quick links" header-hidden>
    <oblyx-side-nav-item label="Installation" href="/docs/install/"></oblyx-side-nav-item>
    <oblyx-side-nav-item label="Foundations" href="/docs/foundations/"></oblyx-side-nav-item>
  </oblyx-side-nav-section>
</oblyx-side-nav>
oblyxSideNav(label = "Docs navigation") {
  oblyxSideNavSection(label = "Quick links", headerHidden = true) {
    oblyxSideNavItem(label = "Installation", href = "/docs/install/")
    oblyxSideNavItem(label = "Foundations", href = "/docs/foundations/")
  }
}

Keyboard model

Side nav is not a composite widget: every row is a native <a>,<button>, or <span>, so Tab moves through every interactive row in document order using ordinary browser tab-stop behavior. There is no roving tabindex and no arrow-key navigation, unlike tabs. An expandable row's toggle <button> activates with Enter or Space for free, being a real button; a collapsed group's children carry inert as well as aria-hidden while closed, which removes the entire hidden subtree from the Tab order in one step, including any nested items several levels deep.

Reference

<oblyx-side-nav>

AttributeTypeDefaultDescription
labelstring | undefinedSidebar navigationNav landmark accessible name.

<oblyx-side-nav-section>

AttributeTypeDefaultDescription
labelstring | undefinedSection title, also the group's accessible name via aria-labelledby. Required; a section with no label has nothing to label its role="group" with.
subtitlestring | undefinedOptional secondary line under the title.
header-hiddenbooleanfalseVisually hides the header row while keeping it in the accessibility tree.

<oblyx-side-nav-heading>

AttributeTypeDefaultDescription
headingstring | undefinedPrimary heading text. Required.
superheadingstring | undefinedOptional line above the heading, e.g. a workspace name.
subheadingstring | undefinedOptional line below the heading.
heading-hrefstring | undefinedMakes the heading row a link. Omitted, heading renders as plain text.
superheading-hrefstring | undefinedMakes the superheading row a link. Omitted, it renders as plain text.
subheading-hrefstring | undefinedMakes the subheading row a link. Omitted, it renders as plain text.

<oblyx-side-nav-item>

AttributeTypeDefaultDescription
labelstring | undefinedVisible row text and default accessible name. Required.
hrefstring | undefinedLink destination. Omitted from the rendered <a> when disabled. Absent entirely renders a non-interactive <span> (or, if expandable, a toggle-only <button>).
disabledbooleanfalseDisabled state. Drives the href-omission contract on the <a> shape, and native disabled on the <button> shapes.
collapsiblebooleanfalseWhether this item's children can be expanded/collapsed. Only takes effect when this item actually has children.
expandedbooleanfalseCurrent expand state of a collapsible item's children.
sizesmmdlgmdElement height / horizontal padding band.
aria-currentstring | undefinedaria-current passthrough, server-computed by the caller. Only 'page' has any visual effect; any other value is still forwarded verbatim, since aria-current also has legal non-boolean values (step, location, ...) this component has no business rejecting. Named current, not ariaCurrent, since the latter collides with the standard ARIAMixin.ariaCurrent: string | null property every Element already has, which TypeScript's DOM lib types incompatibly with Lit's own optional-property convention.