webdevpuneet.com

Breadcrumbs — Collapsing Trail HTML CSS JS Snippet

Breadcrumb Trail with Collapsing Middle Items · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="demo">
  <nav class="crumbs" id="crumbsShort" aria-label="Breadcrumb">
    <ol>
      <li><a href="#">Home</a></li>
      <li><span class="sep">/</span><a href="#">Settings</a></li>
      <li><span class="sep">/</span><span class="current" aria-current="page">Profile</span></li>
    </ol>
  </nav>

  <nav class="crumbs" id="crumbsLong" aria-label="Breadcrumb">
    <ol id="crumbsLongList"></ol>
  </nav>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Semantic <nav><ol> structure with aria-current="page" marking the non-interactive final segment
Real DOM separator elements instead of CSS-generated content, for consistent screen-reader behavior
Automatic collapsing of middle segments on narrow viewports, matching the OS file-browser convention
Collapse ellipsis is a real interactive button, not static text — clicking it reveals the full path
Recalculates and re-renders on window resize, expanding back automatically on wider viewports
Single shared render function for both collapsed and expanded views, preventing the two from drifting apart
Hover and focus states on every ancestor link
Zero dependencies — pure HTML, CSS, and JavaScript

About this UI Snippet

Breadcrumb Trail — aria-current Navigation with Collapsing Middle Segments

Screenshot of the Breadcrumb Trail with Collapsing Middle Items snippet rendered live

Breadcrumbs show a user where they are in a site's hierarchy and let them jump back up to any ancestor level in one click — standard on any site with nested categories, file paths, or multi-step settings. This snippet covers both the simple three-level case and the harder problem: what happens when the path is eight levels deep and doesn't fit on one line.

Semantic structure

The trail is a <nav aria-label="Breadcrumb"> wrapping an <ol> — an ordered list, not an unordered one, because a breadcrumb path has a meaningful sequence. Every visited ancestor is a real <a href="...">, and only the final, current segment is a plain <span> with aria-current="page" — marking the current location as non-interactive is correct because linking a page to itself serves no purpose and aria-current tells assistive technology which segment represents "here" without needing to infer it from position.

Separators as siblings, not pseudo-elements

Each / separator is a real <span class="sep"> element placed before its link, rather than a ::before CSS pseudo-element. This is a deliberate accessibility choice: CSS-generated content is inconsistently exposed to screen readers across browsers, so a visible, real DOM separator guarantees consistent behavior, at the minor cost of one extra element per crumb.

The collapsing problem

A breadcrumb trail with eight segments doesn't fit in a typical sidebar or mobile viewport width. This snippet's renderLong() checks window.innerWidth < 480 && LONG_PATH.length > 4 and, when both are true, renders only the first segment, a … button standing in for the hidden middle, and the last two segments — the same convention Windows Explorer and macOS Finder's breadcrumb bars use. The first segment stays because it anchors the user to the top of the hierarchy; the last two stay because they show exactly where the user currently is and the level directly above it.

The collapse button is interactive, not decorative

The … is a real <button>, not static text, with aria-label="Show hidden path segments" — clicking it calls expandCrumbs(), which re-renders the full uncollapsed list. A common mistake is rendering the ellipsis as plain text, which discards the ability to ever see the hidden segments; making it a button preserves full navigability while still solving the width problem.

Recalculating on resize

window.addEventListener('resize', renderLong) means resizing the browser (or rotating a device) re-evaluates the collapse condition and re-renders — a trail collapsed on a narrow mobile viewport automatically expands back to full when the window widens past the 480px threshold, without requiring a page reload.

Building the HTML string safely

crumbHtml() is the single function responsible for rendering one segment, called identically whether building the collapsed or expanded view — this avoids duplicating the link-vs-current-span branching logic in two places, which is a common source of the two views silently drifting apart from each other over time.

Truncating long individual labels

This snippet handles too many *segments*; a segment with a very long individual label (a long file name, for instance) is a separate problem, best solved with text-overflow: ellipsis and a max-width on that specific <a> or <span>, combined with a native title attribute so the full text is still available on hover.

React integration

In React, pass the path as an array of { label, href } objects, derive the collapsed/expanded view with a small useMemo keyed on a window.innerWidth value tracked via a resize listener in useEffect, and render the last item as a <span aria-current="page"> exactly as this snippet does.

See also the breadcrumb dropdown for a variant where each segment itself opens a dropdown of sibling pages, and the table of contents snippet for a different kind of "where am I" navigation aid.

Step by step

How to Use

  1. 1
    Copy the nav/ol structureUse <nav aria-label="Breadcrumb"><ol>...</ol></nav> with one <li> per path segment — links for ancestors, a plain aria-current span for the current page.
  2. 2
    Replace the LONG_PATH arraySwap LONG_PATH for your real route hierarchy, either hardcoded per page or generated from your router's current path segments.
  3. 3
    Add the CSSPaste the CSS once — it styles both the short static example and the dynamically rendered long trail identically.
  4. 4
    Adjust the collapse thresholdChange the 480px width check and the "> 4 segments" condition in renderLong() to match when your layout actually needs to collapse.
  5. 5
    Wire real hrefsReplace the demo's href="#" placeholders with real ancestor URLs so each breadcrumb link actually navigates.

Real-world uses

Common Use Cases

🗂️
Nested content hierarchies
Show where a reader is within documentation or category trees, using semantic nav and ol markup with aria-current on the final item.
📁
File and folder navigation
Pair with a file manager UI so users can jump to any ancestor folder with one click.
⚙️
Multi-step settings pages
Orient users inside deep multi-step settings pages, alongside a sidebar nav that lists the sibling sections.
🛍️
E-commerce categories
Display paths like Home, Shoes and Running, with collapsing of middle segments on narrow screens and a real button for the ellipsis.
Related: Dropdown Navbar — CSS Only Hover & Focus-Within (No JavaScript)
See the Dropdown Navbar — CSS Only Hover & Focus-Within (No JavaScript) for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

A link to the page the user is already on serves no purpose and can confuse screen reader users into thinking it navigates somewhere new. Using a plain span with aria-current="page" correctly marks it as the non-interactive current location.

Edit the condition in renderLong() — currently window.innerWidth < 480 && LONG_PATH.length > 4 — and the slice indices in the collapsed branch (head = first 1, tail = last 2) to whatever combination fits your layout.

CSS-generated content is not consistently exposed to assistive technology across all browsers and screen readers. A real <span class="sep"> guarantees the separator (or its absence, since it can be hidden from screen readers with aria-hidden if desired) behaves predictably everywhere.

Split your current route pathname on "/", map each segment to a { label, href } pair by accumulating the path so far for each href, and optionally translate URL slugs into human-readable labels via a lookup table or your CMS data.

Open the Export menu on the snippet page for a React component accepting a path array as a prop with the same collapse-on-resize logic in a useEffect, a React + Tailwind version, a Vue 3 SFC, or an Angular standalone component with an @Input() path array.

Yes. While the Breadcrumb Trail with Collapsing Middle Items snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.