ComponentsThemes
Palette
Default
Stone
Rose
Blue
Green
Violet
Yellow
Orange
6

← Components

Breadcrumb

Composable breadcrumb trail (nav/list/item/link/page/separator/ellipsis). Static, no JS.

Installation

bin/rails g wabi:add breadcrumb

Example

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
# 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.