39 CSS Breadcrumbs39 / 39

Pure CSSMIT licensed

Modern Micro-Interaction Animated Hover Breadcrumbs

A breadcrumb built around a delightful hover micro-interaction: each crumb lifts with a subtle translateY, gains a soft shadow, and its separator chevron nudges forward — small, spring-eased motions that make the trail feel responsive and alive without being distracting. Compositor-only and reduced-motion-safe.

Published Updated

Live Demo
Try it

The code

<nav class="bc-39" aria-label="Breadcrumb">
  <ol>
    <li><a href="#">Home</a></li>
    <li><a href="#">Products</a></li>
    <li><a href="#">Hardware</a></li>
    <li><a href="#" aria-current="page">Keyboards</a></li>
  </ol>
</nav>
.bc-39 {
  display: grid;
  width: 100%;
  min-height: 100vh;
  --accent: oklch(0.58 0.15 285);
  font-family: system-ui,'Segoe UI',sans-serif;
  padding: 34px 30px;
  background: oklch(0.98 0.004 285);
  place-items: center;
}

.bc-39 * {
  box-sizing: border-box;
}

.bc-39 ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

.bc-39 li {
  display: flex;
  align-items: center;
}

.bc-39 li:not(:last-child)::after {
  content: "›";
  margin: 0 8px;
  color: oklch(0.7 0.02 285);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1),color .18s ease;
}

.bc-39 li:has(a:hover):not(:last-child)::after {
  transform: translateX(3px);
  color: var(--accent);
}

.bc-39 a {
  display: inline-block;
  padding: 7px 12px;
  border-radius: 9px;
  font: 600 13.5px/1 system-ui,sans-serif;
  color: oklch(0.45 0.04 285);
  text-decoration: none;
  background: transparent;
  box-shadow: 0 0 0 rgba(40,20,80,0);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease,color .18s ease,background-color .18s ease;
}

.bc-39 a:hover,
.bc-39 a:focus-visible {
  transform: translateY(-2px);
  color: var(--accent);
  background: #fff;
  box-shadow: 0 6px 16px rgba(40,20,80,.14);
  outline: none;
}

.bc-39 a:focus-visible {
  box-shadow: 0 6px 16px rgba(40,20,80,.14),0 0 0 2px var(--accent);
}

.bc-39 li[aria-current="page"] a {
  color: oklch(0.3 0.02 285);
  font-weight: 700;
}

@media (prefers-reduced-motion: reduce) {
  .bc-39 a,
    .bc-39 li::after {
    transition: none;
  }

  .bc-39 a:hover,
    .bc-39 a:focus-visible {
    transform: none;
  }
}
/* No JavaScript — a 2px translateY lift + soft shadow on hover, and a :has()-driven chevron nudge toward the next crumb, all spring-eased and compositor-only. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Breadcrumb from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Modern Micro-Interaction Animated Hover Breadcrumbs
Source: https://codefronts.com/navigation/css-breadcrumbs/scale-peek/

A breadcrumb built around a delightful hover micro-interaction: each crumb lifts with a subtle translateY, gains a soft shadow, and its separator chevron nudges forward — small, spring-eased motions that make the trail feel responsive and alive without being distracting. Compositor-only and reduced-motion-safe.
## HTML
```html
<nav class="bc-39" aria-label="Breadcrumb">
  <ol>
    <li><a href="#">Home</a></li>
    <li><a href="#">Products</a></li>
    <li><a href="#">Hardware</a></li>
    <li><a href="#" aria-current="page">Keyboards</a></li>
  </ol>
</nav>
```
## CSS
```css
.bc-39 {
  display: grid;
  width: 100%;
  min-height: 100vh;
  --accent: oklch(0.58 0.15 285);
  font-family: system-ui,'Segoe UI',sans-serif;
  padding: 34px 30px;
  background: oklch(0.98 0.004 285);
  place-items: center;
}

.bc-39 * {
  box-sizing: border-box;
}

.bc-39 ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

.bc-39 li {
  display: flex;
  align-items: center;
}

.bc-39 li:not(:last-child)::after {
  content: "›";
  margin: 0 8px;
  color: oklch(0.7 0.02 285);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1),color .18s ease;
}

.bc-39 li:has(a:hover):not(:last-child)::after {
  transform: translateX(3px);
  color: var(--accent);
}

.bc-39 a {
  display: inline-block;
  padding: 7px 12px;
  border-radius: 9px;
  font: 600 13.5px/1 system-ui,sans-serif;
  color: oklch(0.45 0.04 285);
  text-decoration: none;
  background: transparent;
  box-shadow: 0 0 0 rgba(40,20,80,0);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease,color .18s ease,background-color .18s ease;
}

.bc-39 a:hover,
.bc-39 a:focus-visible {
  transform: translateY(-2px);
  color: var(--accent);
  background: #fff;
  box-shadow: 0 6px 16px rgba(40,20,80,.14);
  outline: none;
}

.bc-39 a:focus-visible {
  box-shadow: 0 6px 16px rgba(40,20,80,.14),0 0 0 2px var(--accent);
}

.bc-39 li[aria-current="page"] a {
  color: oklch(0.3 0.02 285);
  font-weight: 700;
}

@media (prefers-reduced-motion: reduce) {
  .bc-39 a,
    .bc-39 li::after {
    transition: none;
  }

  .bc-39 a:hover,
    .bc-39 a:focus-visible {
    transform: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — a 2px translateY lift + soft shadow on hover, and a :has()-driven chevron nudge toward the next crumb, all spring-eased and compositor-only. */
```

How this works

The interaction stacks three tiny motions on :hover/:focus-visible, all compositor-friendly: the crumb rises with transform:translateY(-2px), a soft box-shadow fades in beneath it (depth cue), and — the detail that sells it — the following separator chevron nudges forward via :has() (li:has(+ li a:hover)::after or the sibling chevron shifts) to imply momentum toward the next level. Each uses a gentle spring-like cubic-bezier so the motion has a little settle rather than a linear slide.

The philosophy is restraint: one well-orchestrated 2px lift with a shadow reads as 'this is interactive and pleasant' far better than large or numerous animations. Durations sit around 180–220ms — fast enough to feel immediate, slow enough to perceive. Everything is transform/opacity/box-shadow, so there's no layout thrash, and prefers-reduced-motion strips the movement to a simple colour change for users who opt out.

Make it yours

  • Lift height + shadow depth are the two dials — 2px/soft is subtle; 4px/deeper is playful.
  • Easing personality: a slight overshoot cubic-bezier adds bounce; a smooth ease reads more corporate.
  • Add an icon that rotates or slides on hover for an extra layer — keep total motion small.
  • Chevron nudge distance (2–3px) is the momentum cue; drop it for a calmer trail.

Gotchas — read before shipping

  • Animate only transform/opacity/box-shadow — never top/margin, which force layout every frame.
  • Keep motion small and fast — big or slow hover animations feel laggy and draw the eye away from content.
  • :has()-driven neighbour effects need the modern selector; provide the plain hover as the base so it degrades to a still-nice lift.
  • Always ship the reduced-motion fallback — vestibular-sensitive users need the option to drop the movement.

Browser support

ChromeSafariFirefoxEdge
111+15.4+121+111+

Floor set by :has(), oklch() as this demo is written. The core technique itself goes back further — Chrome 105+.

:has() (2023) drives the neighbour-chevron nudge; the core lift+shadow works everywhere. Degrades to a plain hover lift without :has().

Techniques used in this demo

Search CodeFronts

Loading…