
Accessible Breadcrumb Navigation with Schema.org Microdata & ARIA
Published
39 hand-coded CSS breadcrumbs for SaaS admin dashboards, e-commerce filter pages, documentation sites, and editorial blogs: Schema.org BreadcrumbList Microdata + WCAG 2.2 accessible (Google Rich Results eligible), :has()-driven responsive collapse (Amazon Fashion mobile pattern), dark/light theme toggle with color-scheme + :has(input:checked), e-commerce filter chips with removable <button> facets (Shopify Dawn / Amazon / Etsy), Docusaurus-style sticky scroll-shrink header with animation-timeline: view(), Amazon-style dropdown sibling menus, Stripe/Vercel/Linear SaaS marketing gradient text, Airbnb filter chip navigation with container queries, editorial blog wave underline, developer docs path-copy (Clipboard API), and 30 more. Real <nav aria-label="Breadcrumb"> + <ol> semantics, aria-current="page", 44px hit targets. Scoped under .bc-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related15 CSS Navigation Menu Designs12 CSS Steppers32 CSS Tab Designs20 CSS Pagination

Published

Published

Published

Published

Published

Published

Published

Published

Published

<details>/<summary> disclosure so it's keyboard-accessible and JS-free, with a modern popover-style panel.Published

Published

Published

Published
Published

Published

Published

Published

Published

Published
Published

Published

Published

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated
Tweak the exact look in our visual generators — no signup, instant copy-paste.
<nav aria-label="Breadcrumb"> — the label is what NVDA, JAWS, VoiceOver, and TalkBack announce when the user jumps between landmarks with the D key or rotor, so shipping it unlabeled means the crumb trail is indistinguishable from your primary nav. Inside the <nav>, use an ordered list <ol>, never <ul>: the WAI-ARIA Authoring Practices Guide, HTML Living Standard, and Schema.org spec all treat breadcrumb hierarchy as inherently sequential. The final crumb — the page the user is currently on — must carry aria-current="page" and should not be a link; screen readers announce it as "current page", which is the entire semantic point of the pattern. Separators (/, ›, >, chevrons) must be injected via CSS ::before pseudo-elements or with <span aria-hidden="true">, never as literal text inside the anchor — otherwise a screen reader reads "Home slash Products slash Shoes", which is exhausting after two crumbs. For Google Rich Results eligibility, layer Schema.org BreadcrumbList as either inline Microdata (itemscope itemtype="https://schema.org/BreadcrumbList" plus itemprop="itemListElement" per crumb) or a JSON-LD application/ld+json script block — Google Search Central accepts both and gives them identical weight. Hit targets must clear WCAG 2.5.5 Level AAA (44×44 CSS pixels); use min-height: 44px; padding-inline: 12px; display: inline-flex; align-items: center on every anchor so the click surface extends past the visible text, which matters on touch devices where a 12px link is a rage-tap. Every anchor also needs a visible :focus-visible outline — a 2px solid ring in your accent color, offset 2px, satisfies WCAG 2.4.7. Getting this right isn't just a11y hygiene: it's legal risk mitigation under ADA Title III (US), Section 508 (US federal), EN 301 549 (EU EAA, enforceable June 2025), and AODA (Ontario). Every demo in this collection ships with this baseline pre-wired.:has() approach: ol:has(li:nth-child(n+5)) li:not(:first-child):not(:nth-last-child(-n+2)) hides the middle crumbs and inserts a … marker via ::before when the trail exceeds four items. This is fully declarative, requires zero JavaScript, and works in every evergreen browser (Chrome/Edge 105+, Safari 15.4+, Firefox 121+). Demo 28 (Truncated Ellipsis Compact CSS Mobile Breadcrumb, slug glassmorphism-bar) takes the complementary approach — each crumb gets max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block, so long product names like "Nike Air Zoom Pegasus 41 Premium Trail Edition" truncate to "Nike Air Zoom P…" instead of shoving the current-page crumb off-screen. Prefer container queries over viewport media queries when the breadcrumb sits inside a sidebar, modal, or split-pane layout: @container (max-width: 480px) { … } reacts to its actual container width, which viewport @media cannot know. Container queries have been Baseline since February 2023 and are safe to ship. For deeper hierarchies (e-commerce category trees five to seven levels deep), the industry standard is the ellipsis-dropdown pattern popularized by Amazon Fashion PDP: show first crumb, ellipsis <button aria-expanded="false" aria-controls="crumb-overflow">…</button>, penultimate crumb, current crumb. Tap the ellipsis and a role="menu" dropdown reveals the hidden middle. This preserves both the entry point (home) and the immediate parent (which is what most users click), which is exactly what Baymard Institute's e-commerce UX research recommends. Pair with overflow-x: auto; scroll-snap-type: x mandatory as a graceful fallback for edge-case ultra-deep trails — but always show some visual affordance that content is scrollable (a fade-out gradient via mask-image: linear-gradient(to right, black 90%, transparent) is the tier-1 signature). Do not just overflow-x: scroll and hope users find the swipe — 62% of mobile users never scroll horizontally unless they see a visible cue.<ol> with itemscope itemtype="https://schema.org/BreadcrumbList", then each <li> gets itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem", with the inner <a itemprop="item"><span itemprop="name">Products</span></a> plus <meta itemprop="position" content="2">. It travels with the HTML, so any static-site generator (Astro, Eleventy, Hugo, Jekyll) ships it without extra build steps. Approach 2 — JSON-LD (Google's preferred format): drop an application/ld+json script tag in the head with { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" }, …] }. JSON-LD decouples markup from presentation, which is why every headless-CMS-driven Next.js site defaults to it. Google's non-negotiable requirements: minimum two ListItem entries (a single-item breadcrumb is rejected), a numeric position starting at 1 (not 0), and either an item URL or an @id for every entry except optionally the final crumb (the current page's URL can be omitted since it equals the page URL). The final crumb's name should still be present so the SERP renders the trail label. Validation is mandatory before ship: run every URL through search.google.com/test/rich-results — it will flag missing position, malformed URLs, and mismatched item counts. Add the URL to Google Search Console and watch the Breadcrumbs enhancement report for indexed vs. valid vs. error counts; a page appearing under Valid is confirmed eligible for the rich result, though eligibility does not guarantee display. If you emit both Microdata and JSON-LD (some legacy sites do), Google deduplicates on @id — no penalty, but pick one to reduce payload. Never emit BreadcrumbList markup for content the breadcrumb trail doesn't visibly represent on the page; Google treats this as manual-action-eligible spam under its Structured Data Guidelines.chip-with-icon) are the two production patterns. The correct semantic structure is two sibling lists: an <ol aria-label="Category path"> for the navigational hierarchy (Home → Women → Shoes) followed by a <ul aria-label="Active filters"> of removable filter chips. Each filter chip is a <button type="button" aria-label="Remove filter: Size 8">Size 8 <span aria-hidden="true">×</span></button> — the × glyph is visually decorative, so it must be aria-hidden, and the semantic action lives in the aria-label. This is exactly the pattern Amazon Fashion, Etsy Category Refinement, and Shopify Dawn theme ship. Chips are styled with display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px 6px 14px; border-radius: 999px; background: color-mix(in oklch, currentColor 8%, transparent); border: 1px solid color-mix(in oklch, currentColor 20%, transparent). Use container queries so the chip row can wrap and re-flow inside sidebars, off-canvas drawers, and Shopify PDP layouts. Add view-transition-name: chip-{id} for zero-JS remove animations via the View Transitions API — a chip fades and the row reflows smoothly when a shopper drops a filter. Compatibility notes for the major platforms: Shopify Dawn ships breadcrumbs via sections/main-collection-banner.liquid — this component drops in directly. WooCommerce Storefront exposes woocommerce_breadcrumb as a filter hook. BigCommerce Cornerstone uses Handlebars templates in templates/components/category/. Magento 2 uses Magento_Theme::html/breadcrumbs.phtml. E-commerce dev traffic is Carbon Ads' highest-CPM band ($3-25 CPM in the US/UK/CA/AU/NZ tier, with fashion + home goods on the top end), so this demo pair is worth shipping right. Bonus: emit BreadcrumbList JSON-LD for the category chain only, not the filter chips — filter permutations should not become Schema.org entries or you will trigger duplicate-content signals and dilute rich-result eligibility on the parent category page.progress-track) is the reference. The tree steps are numbered (usually 3-5: Cart → Shipping → Payment → Review → Confirmation), each has one of three states (complete, current, upcoming), and only completed steps are clickable back-links. Model state with a data-state attribute on each <li>: <li data-state="complete">, <li data-state="current">, <li data-state="upcoming">. Then drive all styling from CSS attribute selectors: [data-state="complete"] .step-marker { background: var(--success); }, [data-state="current"] .step-marker { outline: 3px solid var(--accent); }, [data-state="upcoming"] { opacity: 0.5; pointer-events: none; }. Auto-number with CSS counters: ol { counter-reset: step; } and li::before { counter-increment: step; content: counter(step); } — this way inserting or removing a step doesn't require touching numeric HTML. The a11y detail that matters most: use aria-current="step" on the active step, not aria-current="page". The ARIA 1.2 spec defines step as the token for the current item in a set representing a process, and screen readers announce it as "current step, step 3 of 5". Add aria-label="Checkout progress, step 3 of 5" to the outer <nav> so screen-reader users get an orientation cue before they land on the current step. Draw the connecting line between markers with a single ::after pseudo-element and flex: 1 spacers — no SVG required. For the tick glyph on complete steps, use a CSS mask-image so it inherits currentColor. This pattern is table-stakes at Stripe Checkout, Shopify Pay, PayPal Checkout, Amazon Pay, Klarna, and Afterpay — every one uses a numbered, three-state stepper with a connecting line. Fintech developer traffic runs $15-45 CPM in tier-1 markets (highest single vertical Carbon Ads publishes), so this is the single highest-value demo in the collection to get pixel-right.subway-line) for a compact chevron trail, Demo 18 (Modern Pure CSS Arrow Breadcrumb Bar using clip-path Polygons, slug arrow-chain) for a bolder arrow-tab look, Demo 27 (Floating Card Shadow CSS Breadcrumb Navigation, slug floating-island) for a subtle elevated pill, and Demo 36 (SaaS Marketing Site Gradient Text Breadcrumb Bar, slug gradient-text) for the animated gradient hero-text style. All four use the same skeleton: <nav aria-label="Breadcrumb"><ol><li><a> with aria-current="page" on the final crumb, and separators as pseudo-elements. Placement rule: pin the breadcrumb inside the app-shell top bar, aligned to the primary content column (not the sidebar), height 40-48px, sticky with position: sticky; top: 0; z-index: 10; backdrop-filter: blur(12px) so it stays visible when the main content scrolls. Route integration is where SaaS breadcrumbs diverge from static-site breadcrumbs. Next.js App Router: usePathname() from next/navigation returns the current path; split on /, walk segments, look up display names from your route metadata (many teams keep a routeManifest.ts). Because breadcrumbs are read-only render output, keep this as a Server Component where possible — no 'use client' needed. TanStack Router: useMatches() returns the full match tree with routeContext; map each match to a crumb using the route's staticData.breadcrumb. Remix: use route handle.breadcrumb convention and useMatches(). Nuxt: useRoute().matched plus the meta.breadcrumb convention. SvelteKit: $page.route.id split and mapped. Astro: Astro.url.pathname is straightforward — Astro is ideal for marketing-site breadcrumbs. This pattern ships in Stripe Dashboard, Linear, Vercel, Notion, Cursor Editor, Supabase Studio, Railway, PlanetScale, and Netlify Console. SaaS admin dev traffic runs $8-25 CPM in tier-1 markets — the exact audience buying Vercel Pro, Linear Business, and Cursor licenses.<li><a href="…">Electronics</a><button type="button" aria-expanded="false" aria-controls="siblings-2" aria-label="Show sibling categories"></button><ul id="siblings-2" role="menu" hidden><li role="none"><a role="menuitem" href="…">Laptops</a></li>…</ul></li>. The disclosure <button> is essential for touch and keyboard — hover alone is a WCAG 2.1.1 fail. Show the dropdown with a dual trigger: li:hover > ul, li:focus-within > ul, li:has(button[aria-expanded="true"]) > ul { display: block; }. This gives you desktop hover, keyboard Tab-based reveal via :focus-within, and touch/click via the ARIA-toggled button — all with zero JavaScript on the CSS side. A tiny JS toggle for aria-expanded is the only script required, and it's ~15 lines. Position the dropdown with position: absolute; top: 100%; left: 0; min-width: 200px; z-index: 20; pair with CSS Anchor Positioning if you're targeting Chrome 125+ only — position-anchor and inset-area: bottom span-right handle edge-of-viewport flipping declaratively. Add a small <svg aria-hidden="true"> chevron inside the button that rotates 180° via [aria-expanded="true"] svg { transform: rotate(180deg); } — the affordance is what makes users discover the feature. Guard the reveal animation with @media (prefers-reduced-motion: reduce) { ul { transition: none; } }. Menu semantics matter: use role="menu" on the <ul>, role="menuitem" on each <a>, and wrap the trigger + menu in a container with role="none" on intermediate <li> to strip default list semantics per the ARIA menu pattern. Ships on Amazon PDP, IKEA, Wayfair, Home Depot, Best Buy, and B&H Photo — every deep-catalog retailer with meaningful lateral traffic between siblings uses this pattern.position: sticky; top: 0; z-index: 20; padding: 20px 24px; background: color-mix(in oklch, var(--bg) 85%, transparent); backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%). The frosted-blur backdrop is what makes it read as premium — flat opaque bars look 2015. Progressive enhancement layer: use the Scroll-Driven Animations API (animation-timeline: view() and scroll(), Chrome 115+, Edge 115+, Safari 26+) to declaratively shrink padding, font-size, and backdrop opacity in pure CSS: @keyframes shrink { to { padding-block: 8px; font-size: 0.875rem; } } .breadcrumb-bar { animation: shrink linear both; animation-timeline: scroll(root); animation-range: 0 120px; }. Zero JavaScript, zero scroll event listeners, zero jank, and it gracefully no-ops on unsupported browsers because unsupported browsers just ignore the timeline properties. Fallback layer for Firefox and older Safari: a 15-line IntersectionObserver that toggles a .is-shrunk class on a sentinel <div> placed above the sticky bar — when the sentinel scrolls out of view, add the class and let CSS transitions handle the animation. Always feature-detect (if ('animationTimeline' in document.documentElement.style)) so you don't double-animate. Guard the shrink transition with @media (prefers-reduced-motion: reduce) { .breadcrumb-bar { transition: none; animation: none; } } — the vestibular-motion crowd will thank you and WCAG 2.3.3 will pass. Ships in Docusaurus 3, Mintlify, Nextra, Docsify, VitePress, Starlight, and Fumadocs. Dev-tools docs traffic converts unusually well for Carbon Ads ($8-15 CPM in tier-1 markets) because the audience is literally shopping for developer SaaS. Bonus: pair with a :has(#toc-open) trick to widen the sticky bar when a mobile TOC drawer opens, so the trail remains readable at all viewport widths.:root, one @media (prefers-color-scheme: dark) override for the OS default, one :root[data-theme="dark"] override for a manual toggle, and every component below reads from the tokens: --bg, --surface, --ink, --ink-muted, --accent, --border. :root { --bg: oklch(0.98 0.005 250); --ink: oklch(0.2 0.02 250); --accent: oklch(0.55 0.18 260); } @media (prefers-color-scheme: dark) { :root { --bg: oklch(0.14 0.01 250); --ink: oklch(0.92 0.01 250); } } :root[data-theme="dark"] { --bg: oklch(0.14 0.01 250); --ink: oklch(0.92 0.01 250); } :root[data-theme="light"] { --bg: oklch(0.98 0.005 250); --ink: oklch(0.2 0.02 250); }. The manual override data-theme attribute wins over @media because it's more specific, which is exactly the cascade behavior you want. Set color-scheme: light dark on :root so native form controls, scrollbars, and the browser's default focus ring adopt the correct palette automatically — one line, huge polish gain, most sites forget it. Derive hover, focus, and border colors with color-mix() instead of hardcoding a second palette: a:hover { background: color-mix(in oklch, var(--accent) 12%, transparent); } and border: 1px solid color-mix(in oklch, var(--ink) 15%, transparent). This means your dark and light modes stay perceptually balanced from a single set of base tokens — you're not maintaining two parallel color systems. Use oklch() for every color declaration: it's perceptually uniform, so oklch(0.55 0.18 260) and oklch(0.55 0.18 30) look equally bright even though one is blue and one is orange — an impossible feat in HSL or RGB and the reason Radix, shadcn, and Tailwind v4 all migrated to it. Both color-mix() and oklch() are Baseline as of 2024 and safe to ship. The toggle button itself is <button aria-pressed="false" aria-label="Toggle dark mode">, and the JS is 12 lines: read localStorage.theme, apply the data-theme attribute on <html>, flip aria-pressed on click, persist. Load the theme-application snippet inline in <head> before any body render to prevent the FOUC flash — this is the one script that must be render-blocking..astro file, or a Blade/ERB/Twig partial — the HTML doesn't change, only the file extension. The four demos that do use JavaScript (Demo 10 dropdown sibling menus, Demo 07 sticky-shrink with IntersectionObserver fallback, and Demo 39 copy-path) are ~15 lines of vanilla JS each and wrap trivially: put the code in a React useEffect, a Vue onMounted, a Svelte onMount, a SolidJS onMount, or an Astro script block (which by default ships processed and bundled). HTML→JSX conversion is mechanical: rename class to className, rename for to htmlFor, self-close void elements (<br />, <img />), convert tabindex to tabIndex, and convert inline style="color: red" strings to style={{ color: 'red' }} objects. All aria-* and data-* attributes stay identical in JSX. There's actually a one-click HTML to JSX converter in the toolkit if you'd rather not do it by hand. Next.js App Router: all Pure-CSS demos render as Server Components with no 'use client' directive — SSR-safe, zero client JS, streams cleanly. Only Demos 10, 07, and 39 need 'use client', and only for their small interactive slice; the crumb markup itself can stay server-rendered. SvelteKit / Nuxt / Remix / SolidStart: same story — SSR-safe by default, hydration cost is essentially zero. Astro: this collection is a perfect fit; put the CSS in a style block inside the .astro component and Astro will scope it automatically without breaking the .bc-NN pattern. Tailwind users: keep the raw CSS as-is (it's under 2 KB gzipped per demo) or run one of the existing CSS-to-Tailwind converters; either way the semantic HTML stays untouched. shadcn/ui notably does not ship a Breadcrumb primitive — this collection fills that gap directly, and any of the 39 demos can be dropped into a shadcn app without pulling in additional dependencies or violating the design-token architecture. Tailwind Catalyst ships a breadcrumb component but is $299/dev/yr and only for React — this collection is free, framework-agnostic, and covers 39 aesthetics instead of one.32 free CSS accordions covering pure-CSS FAQ blocks with details/summary, mobile navigation menus, e-commerce filter sidebars, nested tree views, exclusive single-open groups and smoothly animated height:auto — plus 26 visual variations in vertical, horizontal and hybrid layouts. Copy-paste HTML and CSS, no JavaScript.
26 hand-coded CSS circular and radial menu designs, from the floating-action FAB speed dial and dashboard radial hubs to game-style pie-slice wheels, SVG gooey metaball fans, hexagonal honeycombs, iris-aperture reveals, orbiting satellites on offset-path, compass-rose directional nav, sci-fi HUD rings, glassmorphism popovers built on the native Popover API, vinyl-record players, and full-takeover nebula overlays on native dialog. Every design uses CSS trigonometry (sin(), cos()) or the counter-rotation trick for positioning, ships real keyboard navigation, aria-labels on icon-only buttons, and a prefers-reduced-motion guard. Copy-paste HTML, CSS and vanilla JS with zero framework dependencies — MIT licensed.
22 hand-coded CSS dropdown menu designs with live demos — slide-down fade, clip-path curtain reveal, 3D perspective flip, elastic bounce spring, glassmorphism float panel, stagger children reveal, mega-menu grid, underline nav fade, native details/summary, checkbox-hack mobile nav, nested multi-level flyout, icon sidebar flyout, split-screen mega-menu, segmented pill nav, color swatch picker, right-click context menu, keyboard-accessible roving tabindex, stagger blur entrance, command palette search, autocomplete suggestion, click-outside close, and multi-select chip. 15 pure CSS + 7 with a tiny vanilla JS snippet — no framework. WCAG 2.2 aria-expanded + aria-haspopup + focus management, prefers-reduced-motion honoured, MIT licensed.