Breadcrumb
Composable breadcrumb trail (nav/list/item/link/page/separator/ellipsis). Static, no JS.
Installation
bin/rails g wabi:add breadcrumbExample
render Components::UI::Breadcrumb.new do
render Components::UI::BreadcrumbList.new do
render Components::UI::BreadcrumbItem.new do
render Components::UI::BreadcrumbLink.new(href: "/") { "Home" }
end
render Components::UI::BreadcrumbSeparator.new
render Components::UI::BreadcrumbItem.new do
render Components::UI::BreadcrumbLink.new(href: "/docs/components") { "Components" }
end
render Components::UI::BreadcrumbSeparator.new
render Components::UI::BreadcrumbItem.new do
render Components::UI::BreadcrumbPage.new { "Breadcrumb" }
end
end
end
Source
app/components/ui/breadcrumb.rb
# frozen_string_literal: true
module Components
module UI
class Breadcrumb < Wabi::Base
def initialize(**attrs) = @attrs = attrs
def view_template(&)
user_class = @attrs.delete(:class)
nav("aria-label": "breadcrumb", **@attrs, class: merge_class(user_class), &)
end
end
end
end
app/components/ui/breadcrumb_list.rb
# frozen_string_literal: true
module Components
module UI
class BreadcrumbList < Wabi::Base
variants do
base "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5"
end
def initialize(**attrs) = @attrs = attrs
def view_template(&)
user_class = @attrs.delete(:class)
ol(**@attrs, class: merge_class(tokens, user_class), &)
end
end
end
end
app/components/ui/breadcrumb_item.rb
# frozen_string_literal: true
module Components
module UI
class BreadcrumbItem < Wabi::Base
variants do
base "inline-flex items-center gap-1.5"
end
def initialize(**attrs) = @attrs = attrs
def view_template(&)
user_class = @attrs.delete(:class)
li(**@attrs, class: merge_class(tokens, user_class), &)
end
end
end
end
app/components/ui/breadcrumb_link.rb
# frozen_string_literal: true
module Components
module UI
class BreadcrumbLink < Wabi::Base
variants do
base "transition-colors motion-reduce:transition-none hover:text-foreground"
end
def initialize(**attrs) = @attrs = attrs
def view_template(&)
user_class = @attrs.delete(:class)
a(**@attrs, class: merge_class(tokens, user_class), &)
end
end
end
end
app/components/ui/breadcrumb_page.rb
# frozen_string_literal: true
module Components
module UI
class BreadcrumbPage < Wabi::Base
variants do
base "font-normal text-foreground"
end
def initialize(**attrs) = @attrs = attrs
def view_template(&)
user_class = @attrs.delete(:class)
span("aria-current": "page", **@attrs, class: merge_class(tokens, user_class), &)
end
end
end
end
app/components/ui/breadcrumb_separator.rb
# frozen_string_literal: true
module Components
module UI
class BreadcrumbSeparator < Wabi::Base
variants do
base "[&>svg]:size-3.5"
end
def initialize(**attrs) = @attrs = attrs
def view_template(&block)
user_class = @attrs.delete(:class)
li(role: "presentation", "aria-hidden": "true",
**@attrs, class: merge_class(tokens, user_class)) do
if block
yield
else
raw(safe('<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>'))
end
end
end
end
end
end
app/components/ui/breadcrumb_ellipsis.rb
# frozen_string_literal: true
module Components
module UI
class BreadcrumbEllipsis < Wabi::Base
variants do
base "flex size-9 items-center justify-center"
end
def initialize(**attrs) = @attrs = attrs
def view_template
user_class = @attrs.delete(:class)
span(role: "presentation",
**@attrs, class: merge_class(tokens, user_class)) do
raw(safe('<svg aria-hidden="true" focusable="false" class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/><circle cx="5" cy="12" r="1.5"/></svg>'))
span(class: "sr-only") { "More" }
end
end
end
end
end
Accessibility
- Breadcrumb renders a <nav> with aria-label="breadcrumb" so screen readers announce it as navigation.
- BreadcrumbList renders an <ol> so screen readers enumerate items.
- BreadcrumbPage adds aria-current="page" to mark the active crumb.
- BreadcrumbSeparator is aria-hidden to keep screen-reader output clean.