Navigation

Breadcrumbs

<oblyx-breadcrumbs> wraps a trail of <oblyx-breadcrumb-item> rows in a <nav> landmark and an <ol>. Each item renders as a real <a> when it has an href, or plain text for the current page. There is no JavaScript click handling anywhere in the family, so the trail works with JavaScript disabled and composes with HTMX the same way any other real anchor does.

Basic breadcrumb trail

Home Docs Components Breadcrumbs
HTMLKotlin
<oblyx-breadcrumbs>
  <oblyx-breadcrumb-item href="/">Home</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item href="/docs/">Docs</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item href="/docs/components/">Components</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item current>Breadcrumbs</oblyx-breadcrumb-item>
</oblyx-breadcrumbs>
oblyxBreadcrumbs {
  oblyxBreadcrumbItem(href = "/") { +"Home" }
  oblyxBreadcrumbItem(href = "/docs/") { +"Docs" }
  oblyxBreadcrumbItem(href = "/docs/components/") { +"Components" }
  oblyxBreadcrumbItem(current = true) { +"Breadcrumbs" }
}

Current item

Set current on the item that represents the page the user is already on. It renders as plain text with aria-current="page" instead of a link, even if href is also set. Leaving current unset entirely (not current="false", which is a real "not current") falls back to auto-detection: the last item in the trail is treated as current, as long as no earlier item explicitly claims it. A server-rendered page almost always knows which crumb is current and should set it explicitly; auto-detection exists for markup that doesn't bother.

Auto-detected current item

No item sets current explicitly. The last item is treated as current.

Home Docs Breadcrumbs
HTMLKotlin
<oblyx-breadcrumbs>
  <oblyx-breadcrumb-item href="/">Home</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item href="/docs/">Docs</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item>Breadcrumbs</oblyx-breadcrumb-item>
</oblyx-breadcrumbs>
oblyxBreadcrumbs {
  oblyxBreadcrumbItem(href = "/") { +"Home" }
  oblyxBreadcrumbItem(href = "/docs/") { +"Docs" }
  oblyxBreadcrumbItem { +"Breadcrumbs" }
}

Variant

default and supporting control item text size. variant is read by every descendant <oblyx-breadcrumb-item> through a plain CSS descendant selector, not JavaScript: an item with no <oblyx-breadcrumbs> ancestor at all simply falls back to default sizing.

Supporting variant

Home Docs Breadcrumbs
HTMLKotlin
<oblyx-breadcrumbs variant="supporting">
  <oblyx-breadcrumb-item href="/">Home</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item href="/docs/">Docs</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item current>Breadcrumbs</oblyx-breadcrumb-item>
</oblyx-breadcrumbs>
oblyxBreadcrumbs(variant = OblyxBreadcrumbsVariant.SUPPORTING) {
  oblyxBreadcrumbItem(href = "/") { +"Home" }
  oblyxBreadcrumbItem(href = "/docs/") { +"Docs" }
  oblyxBreadcrumbItem(current = true) { +"Breadcrumbs" }
}

Custom separator

separator replaces the default / glyph between items.

Custom separator

Home Docs Breadcrumbs
HTMLKotlin
<oblyx-breadcrumbs separator="›">
  <oblyx-breadcrumb-item href="/">Home</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item href="/docs/">Docs</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item current>Breadcrumbs</oblyx-breadcrumb-item>
</oblyx-breadcrumbs>
oblyxBreadcrumbs(separator = "›") {
  oblyxBreadcrumbItem(href = "/") { +"Home" }
  oblyxBreadcrumbItem(href = "/docs/") { +"Docs" }
  oblyxBreadcrumbItem(current = true) { +"Breadcrumbs" }
}

Disabled item

disabled omits href from that item's rendered <a>, the same disabled-link contract oblyx-link uses: the item stays visible in the trail but is not a tab stop and has no default action.

Disabled item

Home Archived section Breadcrumbs
HTMLKotlin
<oblyx-breadcrumbs>
  <oblyx-breadcrumb-item href="/">Home</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item href="/docs/archived/" disabled>Archived section</oblyx-breadcrumb-item>
  <oblyx-breadcrumb-item current>Breadcrumbs</oblyx-breadcrumb-item>
</oblyx-breadcrumbs>
oblyxBreadcrumbs {
  oblyxBreadcrumbItem(href = "/") { +"Home" }
  oblyxBreadcrumbItem(href = "/docs/archived/", disabled = true) { +"Archived section" }
  oblyxBreadcrumbItem(current = true) { +"Breadcrumbs" }
}

Keyboard model

There is no composite widget here: every linked item is a real <a> and the current item is a plain <span>. Tab moves through the linked items in document order using the browser's native anchor semantics, the same as any other row of links on the page. Unlike tabs and side nav, there are no arrow keys, no roving tabindex, and nothing to activate with Enter or Space beyond what a plain <a> already does.

How the separator reaches each item

separator is set once on <oblyx-breadcrumbs> and travels down to every <oblyx-breadcrumb-item> as an inherited CSS custom property, consumed by a ::before-generated glyph on each item, rather than being read via closest('oblyx-breadcrumbs') in the item's own render. An item's first render can genuinely happen while it is still sitting inside a detached wrapper mid-upgrade, at which point closest() returns null and nothing ever re-renders the item afterward to correct it. A plain CSS custom property has no such window, because it resolves at paint time once the DOM has settled. The practical effect: separator is a per-trail setting, not a per-item one, and a standalone <oblyx-breadcrumb-item> with no <oblyx-breadcrumbs> ancestor always falls back to the default /, since there is no inherited custom property for it to pick up.

Reference

<oblyx-breadcrumbs>

AttributeTypeDefaultDescription
separatorstring | undefined/Decorative separator text rendered between items.
variantdefaultsupportingdefaultItem text size. Drives <oblyx-breadcrumb-item> typography.
labelstring | undefinedBreadcrumb<nav> landmark accessible name.

<oblyx-breadcrumb-item>

AttributeTypeDefaultDescription
hrefstring | undefinedLink destination. Omitted from the rendered <a> when disabled. Omit entirely for the current page or a non-interactive crumb.
disabledbooleanfalseDisables the item's link. No effect when href is unset, since there's nothing to disable.
currentboolean | undefinedExplicit "this is the current page" marker. Tri-state: leaving it unset (the default) falls through to DOM auto-detection rather than being treated as a hard "false".