Breadcrumbs
SimpleNavigation breadcrumbs with separators and current page highlighting
Live Demo
Basic Breadcrumbs
Simple breadcrumb navigation with arrows
Resize to see how the component adapts to different screen sizes
📖 Basic Breadcrumbs Overview
Simple breadcrumb navigation with arrows
💡 Key Features
- • Basic Breadcrumbs functionality with Stimulus controller
- • Navigation bars, sidebars, breadcrumbs, and layout components
- • Responsive design optimized for all screen sizes
- • Accessible markup with proper semantic HTML
- • Modern CSS transitions and interactive effects
- • Enhanced with breadcrumbs capabilities
- • Enhanced with navigation capabilities
- • Enhanced with path capabilities
- • Enhanced with current capabilities
- • Enhanced with separators capabilities
🏗️ Basic Breadcrumbs HTML Structure
This basic breadcrumbs component uses semantic HTML structure with Stimulus data attributes to connect HTML elements to JavaScript functionality.
📋 Stimulus Data Attributes
data-controller="navigation-breadcrumbs-basic"
Connects this HTML element to the Basic Breadcrumbs Stimulus controller
data-action="click->navigation-breadcrumbs-basic#method"
Defines click events that trigger basic breadcrumbs controller methods
data-navigation-breadcrumbs-basic-target="element"
Identifies elements that the Basic Breadcrumbs controller can reference and manipulate
♿ Accessibility Features
- • Semantic HTML elements provide screen reader context
- • ARIA attributes enhance basic breadcrumbs accessibility
- • Keyboard navigation fully supported
- • Focus management for interactive elements
🎨 Basic Breadcrumbs Tailwind Classes
This basic breadcrumbs component uses Tailwind CSS utility classes with the forest theme color palette for styling and responsive design.
🎨 Navigation & Layout Colors
bg-moss-600
Primary
bg-moss-100
Light
bg-honey-400
Accent
📐 Layout & Spacing
p-4 - Padding for basic breadcrumbs contentmb-4 - Margin bottom between elementsspace-y-2 - Vertical spacing in listsrounded-lg - Rounded corners for modern look📱 Responsive Basic Breadcrumbs Design
- •
sm:- Small screens (640px+) optimized for basic breadcrumbs - •
md:- Medium screens (768px+) enhanced layout - •
lg:- Large screens (1024px+) full functionality - • Mobile-first approach ensures basic breadcrumbs works on all devices
⚡ Basic Breadcrumbs JavaScript Logic
The Basic Breadcrumbs component uses a dedicated Stimulus controller (navigation-breadcrumbs-basic) to handle basic breadcrumbs interactions and manage component state.
🎯 Basic Breadcrumbs Controller Features
Targets
DOM elements the basic breadcrumbs controller can reference and manipulate
Values
Configuration data for basic breadcrumbs behavior passed from HTML
Actions
Methods triggered by basic breadcrumbs user events and interactions
Lifecycle
Setup and cleanup methods for basic breadcrumbs initialization
🔄 Basic Breadcrumbs Event Flow
- 1. User interacts with basic breadcrumbs element (click, hover, input, etc.)
- 2. Stimulus detects event through
data-actionattribute - 3. Basic Breadcrumbs controller method executes with access to targets and values
- 4. Controller updates DOM with new basic breadcrumbs state or visual changes
- 5. CSS transitions provide smooth visual feedback for basic breadcrumbs interactions
<nav class="py-3" data-controller="breadcrumbs-basic">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ol class="flex items-center space-x-2 text-sm">
<li>
<a href="#" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-basic#navigate">
Home
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<a href="#" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-basic#navigate">
Components
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<a href="#" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-basic#navigate">
Navigation
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<span class="text-green-900 font-semibold">Breadcrumbs</span>
</li>
</ol>
</div>
</nav>
import { Controller } from "@hotwired/stimulus"
// Basic Breadcrumbs Controller
// Handles breadcrumb navigation with path tracking
export default class extends Controller {
connect() {
console.log("Basic breadcrumbs connected!")
this.setupBreadcrumbTracking()
}
setupBreadcrumbTracking() {
// Mark current page in breadcrumbs
const currentItem = this.element.querySelector('span')
if (currentItem) {
currentItem.setAttribute('aria-current', 'page')
}
// Add semantic markup
this.element.setAttribute('aria-label', 'Breadcrumb navigation')
}
navigate(event) {
event.preventDefault()
const linkText = event.currentTarget.textContent.trim()
console.log(`Breadcrumb navigation: ${linkText}`)
// Add click feedback
event.currentTarget.classList.add('text-green-900')
setTimeout(() => {
event.currentTarget.classList.remove('text-green-900')
}, 150)
}
// Method to update breadcrumbs dynamically (useful for SPA navigation)
updatePath(pathArray) {
const breadcrumbList = this.element.querySelector('ol')
if (!breadcrumbList) return
// Clear existing breadcrumbs
breadcrumbList.innerHTML = ''
pathArray.forEach((item, index) => {
const isLast = index === pathArray.length - 1
// Create breadcrumb item
const li = document.createElement('li')
if (isLast) {
// Current page item
li.innerHTML = `<span class="text-green-900 font-semibold" aria-current="page">${item.name}</span>`
} else {
// Clickable breadcrumb
li.innerHTML = `<a href="${item.url}" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200" data-action="click->breadcrumbs-basic#navigate">${item.name}</a>`
}
breadcrumbList.appendChild(li)
// Add separator (except for last item)
if (!isLast) {
const separator = document.createElement('li')
separator.className = 'text-green-400'
separator.innerHTML = `
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
`
breadcrumbList.appendChild(separator)
}
})
console.log(`Breadcrumbs updated with ${pathArray.length} items`)
}
}
Breadcrumbs with Icons
Breadcrumbs with home icon and separators
Resize to see how the component adapts to different screen sizes
📖 Breadcrumbs with Icons Overview
Breadcrumbs with home icon and separators
💡 Key Features
- • Breadcrumbs with Icons functionality with Stimulus controller
- • Navigation bars, sidebars, breadcrumbs, and layout components
- • Responsive design optimized for all screen sizes
- • Accessible markup with proper semantic HTML
- • Modern CSS transitions and interactive effects
- • Enhanced with breadcrumbs capabilities
- • Enhanced with navigation capabilities
- • Enhanced with path capabilities
- • Enhanced with current capabilities
- • Enhanced with separators capabilities
🏗️ Breadcrumbs with Icons HTML Structure
This breadcrumbs with icons component uses semantic HTML structure with Stimulus data attributes to connect HTML elements to JavaScript functionality.
📋 Stimulus Data Attributes
data-controller="navigation-breadcrumbs-icons"
Connects this HTML element to the Breadcrumbs with Icons Stimulus controller
data-action="click->navigation-breadcrumbs-icons#method"
Defines click events that trigger breadcrumbs with icons controller methods
data-navigation-breadcrumbs-icons-target="element"
Identifies elements that the Breadcrumbs with Icons controller can reference and manipulate
♿ Accessibility Features
- • Semantic HTML elements provide screen reader context
- • ARIA attributes enhance breadcrumbs with icons accessibility
- • Keyboard navigation fully supported
- • Focus management for interactive elements
🎨 Breadcrumbs with Icons Tailwind Classes
This breadcrumbs with icons component uses Tailwind CSS utility classes with the forest theme color palette for styling and responsive design.
🎨 Navigation & Layout Colors
bg-moss-600
Primary
bg-moss-100
Light
bg-honey-400
Accent
📐 Layout & Spacing
p-4 - Padding for breadcrumbs with icons contentmb-4 - Margin bottom between elementsspace-y-2 - Vertical spacing in listsrounded-lg - Rounded corners for modern look📱 Responsive Breadcrumbs with Icons Design
- •
sm:- Small screens (640px+) optimized for breadcrumbs with icons - •
md:- Medium screens (768px+) enhanced layout - •
lg:- Large screens (1024px+) full functionality - • Mobile-first approach ensures breadcrumbs with icons works on all devices
⚡ Breadcrumbs with Icons JavaScript Logic
The Breadcrumbs with Icons component uses a dedicated Stimulus controller (navigation-breadcrumbs-icons) to handle breadcrumbs with icons interactions and manage component state.
🎯 Breadcrumbs with Icons Controller Features
Targets
DOM elements the breadcrumbs with icons controller can reference and manipulate
Values
Configuration data for breadcrumbs with icons behavior passed from HTML
Actions
Methods triggered by breadcrumbs with icons user events and interactions
Lifecycle
Setup and cleanup methods for breadcrumbs with icons initialization
🔄 Breadcrumbs with Icons Event Flow
- 1. User interacts with breadcrumbs with icons element (click, hover, input, etc.)
- 2. Stimulus detects event through
data-actionattribute - 3. Breadcrumbs with Icons controller method executes with access to targets and values
- 4. Controller updates DOM with new breadcrumbs with icons state or visual changes
- 5. CSS transitions provide smooth visual feedback for breadcrumbs with icons interactions
<nav class="py-3" data-controller="breadcrumbs-icons">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ol class="flex items-center space-x-2 text-sm">
<li>
<a href="#" class="flex items-center text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-icons#navigate">
<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
Home
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<a href="#" class="flex items-center text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-icons#navigate">
<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>
Components
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<a href="#" class="flex items-center text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-icons#navigate">
<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17V7m0 10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m0 10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H9m0 0V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2M9 7h6" />
</svg>
Navigation
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<div class="flex items-center">
<svg class="w-4 h-4 mr-2 text-green-700" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5M3.75 17.25h16.5" />
</svg>
<span class="text-green-900 font-semibold">Breadcrumbs</span>
</div>
</li>
</ol>
</div>
</nav>
import { Controller } from "@hotwired/stimulus"
// Icons Breadcrumbs Controller
// Enhanced breadcrumb navigation with icon support and animations
export default class extends Controller {
connect() {
console.log("Icons breadcrumbs connected!")
this.setupBreadcrumbTracking()
this.addHoverEffects()
}
setupBreadcrumbTracking() {
// Mark current page in breadcrumbs
const currentItem = this.element.querySelector('span')
if (currentItem) {
currentItem.setAttribute('aria-current', 'page')
}
// Add semantic markup
this.element.setAttribute('aria-label', 'Breadcrumb navigation with icons')
}
addHoverEffects() {
// Add subtle animations to icons on hover
const links = this.element.querySelectorAll('a')
links.forEach(link => {
const icon = link.querySelector('svg')
if (icon) {
link.addEventListener('mouseenter', () => {
icon.classList.add('transform', 'scale-110', 'transition-transform', 'duration-200')
})
link.addEventListener('mouseleave', () => {
icon.classList.remove('transform', 'scale-110')
})
}
})
}
navigate(event) {
event.preventDefault()
const linkElement = event.currentTarget
const linkText = linkElement.textContent.trim()
console.log(`Icon breadcrumb navigation: ${linkText}`)
// Add click feedback with icon animation
const icon = linkElement.querySelector('svg')
if (icon) {
icon.classList.add('animate-pulse')
setTimeout(() => {
icon.classList.remove('animate-pulse')
}, 300)
}
// Highlight clicked breadcrumb
linkElement.classList.add('text-green-900')
setTimeout(() => {
linkElement.classList.remove('text-green-900')
}, 200)
}
// Method to update breadcrumbs with icons
updatePathWithIcons(pathArray) {
const breadcrumbList = this.element.querySelector('ol')
if (!breadcrumbList) return
// Icon mapping for different page types
const iconMap = {
home: `<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>`,
components: `<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>`,
navigation: `<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17V7m0 10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m0 10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H9m0 0V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2M9 7h6" />
</svg>`,
default: `<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-4.5B7.125 8.25 5.25 10.125 5.25 13.5v2.625m14.25-2.625v2.625A2.625 2.625 0 0 1 17.25 18H6.75A2.625 2.625 0 0 1 4.125 15.375V12.75" />
</svg>`
}
// Clear existing breadcrumbs
breadcrumbList.innerHTML = ''
pathArray.forEach((item, index) => {
const isLast = index === pathArray.length - 1
const iconType = item.icon || item.name.toLowerCase()
const iconHtml = iconMap[iconType] || iconMap.default
// Create breadcrumb item
const li = document.createElement('li')
if (isLast) {
// Current page item
li.innerHTML = `
<div class="flex items-center">
${iconHtml.replace('mr-2', 'mr-2 text-green-700')}
<span class="text-green-900 font-semibold" aria-current="page">${item.name}</span>
</div>
`
} else {
// Clickable breadcrumb with icon
li.innerHTML = `
<a href="${item.url}" class="flex items-center text-green-600 hover:text-green-900 font-medium transition-colors duration-200" data-action="click->breadcrumbs-icons#navigate">
${iconHtml}
${item.name}
</a>
`
}
breadcrumbList.appendChild(li)
// Add separator (except for last item)
if (!isLast) {
const separator = document.createElement('li')
separator.className = 'text-green-400'
separator.innerHTML = `
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
`
breadcrumbList.appendChild(separator)
}
})
// Re-apply hover effects to new elements
this.addHoverEffects()
console.log(`Icon breadcrumbs updated with ${pathArray.length} items`)
}
}
Dropdown Breadcrumbs
Breadcrumbs with collapsible intermediate paths
Resize to see how the component adapts to different screen sizes
📖 Dropdown Breadcrumbs Overview
Breadcrumbs with collapsible intermediate paths
💡 Key Features
- • Dropdown Breadcrumbs functionality with Stimulus controller
- • Navigation bars, sidebars, breadcrumbs, and layout components
- • Responsive design optimized for all screen sizes
- • Accessible markup with proper semantic HTML
- • Modern CSS transitions and interactive effects
- • Enhanced with breadcrumbs capabilities
- • Enhanced with navigation capabilities
- • Enhanced with path capabilities
- • Enhanced with current capabilities
- • Enhanced with separators capabilities
🏗️ Dropdown Breadcrumbs HTML Structure
This dropdown breadcrumbs component uses semantic HTML structure with Stimulus data attributes to connect HTML elements to JavaScript functionality.
📋 Stimulus Data Attributes
data-controller="navigation-breadcrumbs-dropdown"
Connects this HTML element to the Dropdown Breadcrumbs Stimulus controller
data-action="click->navigation-breadcrumbs-dropdown#method"
Defines click events that trigger dropdown breadcrumbs controller methods
data-navigation-breadcrumbs-dropdown-target="element"
Identifies elements that the Dropdown Breadcrumbs controller can reference and manipulate
♿ Accessibility Features
- • Semantic HTML elements provide screen reader context
- • ARIA attributes enhance dropdown breadcrumbs accessibility
- • Keyboard navigation fully supported
- • Focus management for interactive elements
🎨 Dropdown Breadcrumbs Tailwind Classes
This dropdown breadcrumbs component uses Tailwind CSS utility classes with the forest theme color palette for styling and responsive design.
🎨 Navigation & Layout Colors
bg-moss-600
Primary
bg-moss-100
Light
bg-honey-400
Accent
📐 Layout & Spacing
p-4 - Padding for dropdown breadcrumbs contentmb-4 - Margin bottom between elementsspace-y-2 - Vertical spacing in listsrounded-lg - Rounded corners for modern look📱 Responsive Dropdown Breadcrumbs Design
- •
sm:- Small screens (640px+) optimized for dropdown breadcrumbs - •
md:- Medium screens (768px+) enhanced layout - •
lg:- Large screens (1024px+) full functionality - • Mobile-first approach ensures dropdown breadcrumbs works on all devices
⚡ Dropdown Breadcrumbs JavaScript Logic
The Dropdown Breadcrumbs component uses a dedicated Stimulus controller (navigation-breadcrumbs-dropdown) to handle dropdown breadcrumbs interactions and manage component state.
🎯 Dropdown Breadcrumbs Controller Features
Targets
DOM elements the dropdown breadcrumbs controller can reference and manipulate
Values
Configuration data for dropdown breadcrumbs behavior passed from HTML
Actions
Methods triggered by dropdown breadcrumbs user events and interactions
Lifecycle
Setup and cleanup methods for dropdown breadcrumbs initialization
🔄 Dropdown Breadcrumbs Event Flow
- 1. User interacts with dropdown breadcrumbs element (click, hover, input, etc.)
- 2. Stimulus detects event through
data-actionattribute - 3. Dropdown Breadcrumbs controller method executes with access to targets and values
- 4. Controller updates DOM with new dropdown breadcrumbs state or visual changes
- 5. CSS transitions provide smooth visual feedback for dropdown breadcrumbs interactions
<nav class="py-3" data-controller="breadcrumbs-dropdown">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ol class="flex items-center space-x-2 text-sm">
<li>
<a href="#" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-dropdown#navigate">
Home
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<!-- Collapsed breadcrumbs with dropdown -->
<li class="relative" data-breadcrumbs-dropdown-target="dropdownContainer">
<button
class="flex items-center text-green-600 hover:text-green-900 font-medium transition-colors duration-200 px-2 py-1 rounded hover:bg-green-50"
data-action="click->breadcrumbs-dropdown#toggleDropdown"
data-breadcrumbs-dropdown-target="dropdownButton"
>
<span class="text-green-500">...</span>
<svg class="w-3 h-3 ml-1 transform transition-transform duration-200" data-breadcrumbs-dropdown-target="chevron" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</button>
<!-- Dropdown Menu -->
<div class="absolute left-0 mt-1 w-48 bg-white border border-green-200 rounded-md shadow-lg opacity-0 invisible transform -translate-y-1 transition-all duration-200 z-10"
data-breadcrumbs-dropdown-target="dropdownMenu">
<div class="py-1">
<a href="#" class="block px-4 py-2 text-sm text-green-600 hover:text-green-900 hover:bg-green-50 transition-colors duration-200"
data-action="click->breadcrumbs-dropdown#selectPath">
Components
</a>
<a href="#" class="block px-4 py-2 text-sm text-green-600 hover:text-green-900 hover:bg-green-50 transition-colors duration-200"
data-action="click->breadcrumbs-dropdown#selectPath">
Interactive Elements
</a>
<a href="#" class="block px-4 py-2 text-sm text-green-600 hover:text-green-900 hover:bg-green-50 transition-colors duration-200"
data-action="click->breadcrumbs-dropdown#selectPath">
Form Components
</a>
</div>
</div>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<a href="#" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200"
data-action="click->breadcrumbs-dropdown#navigate">
Navigation
</a>
</li>
<li class="text-green-400">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</li>
<li>
<span class="text-green-900 font-semibold">Breadcrumbs</span>
</li>
</ol>
</div>
</nav>
import { Controller } from "@hotwired/stimulus"
// Dropdown Breadcrumbs Controller
// Handles collapsible breadcrumb paths with dropdown menu for intermediate items
export default class extends Controller {
static targets = ["dropdownContainer", "dropdownButton", "dropdownMenu", "chevron"]
connect() {
console.log("Dropdown breadcrumbs connected!")
this.isOpen = false
this.setupBreadcrumbTracking()
this.setupEventListeners()
}
disconnect() {
this.removeEventListeners()
}
setupBreadcrumbTracking() {
// Mark current page in breadcrumbs
const currentItem = this.element.querySelector('span')
if (currentItem) {
currentItem.setAttribute('aria-current', 'page')
}
// Add semantic markup
this.element.setAttribute('aria-label', 'Collapsible breadcrumb navigation')
// Mark dropdown button with proper ARIA attributes
if (this.hasDropdownButtonTarget) {
this.dropdownButtonTarget.setAttribute('aria-expanded', 'false')
this.dropdownButtonTarget.setAttribute('aria-haspopup', 'menu')
}
}
setupEventListeners() {
// Close dropdown when clicking outside
this.handleOutsideClick = this.handleOutsideClick.bind(this)
// Handle escape key
this.handleEscapeKey = this.handleEscapeKey.bind(this)
}
removeEventListeners() {
document.removeEventListener('click', this.handleOutsideClick)
document.removeEventListener('keydown', this.handleEscapeKey)
}
toggleDropdown(event) {
event.preventDefault()
event.stopPropagation()
this.isOpen ? this.closeDropdown() : this.openDropdown()
}
openDropdown() {
this.isOpen = true
// Show dropdown with animation
this.dropdownMenuTarget.classList.remove('opacity-0', 'invisible', '-translate-y-1')
this.dropdownMenuTarget.classList.add('opacity-100', 'visible', 'translate-y-0')
// Rotate chevron
this.chevronTarget.classList.add('rotate-180')
// Update ARIA state
this.dropdownButtonTarget.setAttribute('aria-expanded', 'true')
// Add event listeners
setTimeout(() => {
document.addEventListener('click', this.handleOutsideClick)
document.addEventListener('keydown', this.handleEscapeKey)
}, 0)
console.log("Breadcrumb dropdown opened")
}
closeDropdown() {
this.isOpen = false
// Hide dropdown with animation
this.dropdownMenuTarget.classList.add('opacity-0', 'invisible', '-translate-y-1')
this.dropdownMenuTarget.classList.remove('opacity-100', 'visible', 'translate-y-0')
// Reset chevron
this.chevronTarget.classList.remove('rotate-180')
// Update ARIA state
this.dropdownButtonTarget.setAttribute('aria-expanded', 'false')
// Remove event listeners
document.removeEventListener('click', this.handleOutsideClick)
document.removeEventListener('keydown', this.handleEscapeKey)
console.log("Breadcrumb dropdown closed")
}
selectPath(event) {
event.preventDefault()
const selectedText = event.currentTarget.textContent.trim()
console.log(`Selected breadcrumb path: ${selectedText}`)
// Close dropdown after selection
this.closeDropdown()
// Optional: Update breadcrumb display to show selected path
this.updateBreadcrumbAfterSelection(selectedText)
}
updateBreadcrumbAfterSelection(selectedPath) {
// Find the dropdown button and update it to show the selected path
const ellipsis = this.dropdownButtonTarget.querySelector('span')
if (ellipsis) {
// Briefly show the selected path name
const originalText = ellipsis.textContent
ellipsis.textContent = selectedPath
ellipsis.classList.add('text-green-900', 'font-medium')
// Reset after a short delay
setTimeout(() => {
ellipsis.textContent = originalText
ellipsis.classList.remove('text-green-900', 'font-medium')
}, 1500)
}
}
navigate(event) {
event.preventDefault()
const linkText = event.currentTarget.textContent.trim()
console.log(`Breadcrumb navigation: ${linkText}`)
// Add click feedback
event.currentTarget.classList.add('text-green-900')
setTimeout(() => {
event.currentTarget.classList.remove('text-green-900')
}, 150)
}
handleOutsideClick(event) {
if (!this.dropdownContainerTarget.contains(event.target)) {
this.closeDropdown()
}
}
handleEscapeKey(event) {
if (event.key === 'Escape' && this.isOpen) {
this.closeDropdown()
this.dropdownButtonTarget.focus()
}
}
// Method to dynamically create collapsed breadcrumbs
createCollapsedBreadcrumbs(pathArray, maxVisible = 3) {
if (pathArray.length <= maxVisible) {
// No need to collapse - show all items
return this.createFullBreadcrumbs(pathArray)
}
const breadcrumbList = this.element.querySelector('ol')
if (!breadcrumbList) return
// Clear existing breadcrumbs
breadcrumbList.innerHTML = ''
// Show first item (usually Home)
this.addBreadcrumbItem(breadcrumbList, pathArray[0], false)
this.addSeparator(breadcrumbList)
// Add collapsed items dropdown
const collapsedItems = pathArray.slice(1, -2) // Middle items
if (collapsedItems.length > 0) {
this.addDropdownBreadcrumb(breadcrumbList, collapsedItems)
this.addSeparator(breadcrumbList)
}
// Show last few items
const lastItems = pathArray.slice(-2)
lastItems.forEach((item, index) => {
const isLast = index === lastItems.length - 1
this.addBreadcrumbItem(breadcrumbList, item, isLast)
if (!isLast) {
this.addSeparator(breadcrumbList)
}
})
console.log(`Created collapsed breadcrumbs with ${pathArray.length} items`)
}
addBreadcrumbItem(container, item, isLast) {
const li = document.createElement('li')
if (isLast) {
li.innerHTML = `<span class="text-green-900 font-semibold" aria-current="page">${item.name}</span>`
} else {
li.innerHTML = `<a href="${item.url}" class="text-green-600 hover:text-green-900 font-medium transition-colors duration-200" data-action="click->breadcrumbs-dropdown#navigate">${item.name}</a>`
}
container.appendChild(li)
}
addSeparator(container) {
const separator = document.createElement('li')
separator.className = 'text-green-400'
separator.innerHTML = `
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
`
container.appendChild(separator)
}
addDropdownBreadcrumb(container, collapsedItems) {
const dropdownHtml = `
<li class="relative" data-breadcrumbs-dropdown-target="dropdownContainer">
<button class="flex items-center text-green-600 hover:text-green-900 font-medium transition-colors duration-200 px-2 py-1 rounded hover:bg-green-50" data-action="click->breadcrumbs-dropdown#toggleDropdown" data-breadcrumbs-dropdown-target="dropdownButton">
<span class="text-green-500">...</span>
<svg class="w-3 h-3 ml-1 transform transition-transform duration-200" data-breadcrumbs-dropdown-target="chevron" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</button>
<div class="absolute left-0 mt-1 w-48 bg-white border border-green-200 rounded-md shadow-lg opacity-0 invisible transform -translate-y-1 transition-all duration-200 z-10" data-breadcrumbs-dropdown-target="dropdownMenu">
<div class="py-1">
${collapsedItems.map(item => `
<a href="${item.url}" class="block px-4 py-2 text-sm text-green-600 hover:text-green-900 hover:bg-green-50 transition-colors duration-200" data-action="click->breadcrumbs-dropdown#selectPath">
${item.name}
</a>
`).join('')}
</div>
</div>
</li>
`
container.insertAdjacentHTML('beforeend', dropdownHtml)
}
}
Installation & Usage
Copy the ERB template
Copy the ERB code from the template tab above and paste it into your Rails view file.
Add the Stimulus controller
Create the Stimulus controller file in your JavaScript controllers directory.