№ 003

Leaf Nav

A paper-craft tab bar: a sage sheet stretches between tabs and its leading corner peels up.

Version
1.0.0
Download
10 KB · 3 files
Added
Oct 26, 2026
Built with
HTML, CSS, JS
Needs
Google Fonts
Tags
tab bar, paper craft, spring

Usage

Copy leaf-nav.css and leaf-nav.js (and assets/, if there is one) into your project, then:

Leaf — a paper-craft tab bar. The active tab is a sage sheet lying on top of overlapping cream tiles.
Its two edges are springs: the leading edge runs ahead, the trailing edge lags, so the sheet stretches
as it travels — and the leading corner peels up, showing the paper's underside.

  new LeafNav(element, { tabs: [{ label, icon }], active: 0, onChange(tab, index) {} })
  `icon` is a key of LeafNav.icons or a raw SVG string.

Source

/* Leaf — paper-craft tab bar. The bar is an SVG that scales to its container;
   icons and labels are real buttons laid over it. */

.lf {
  position: relative;
  width: 100%;
  max-width: 760px;
  container-type: inline-size;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: pan-y;
}

.lf::after {
  content: "";
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: -6%;
  height: 14%;
  z-index: -1;
  border-radius: 50%;
  background: rgba(110, 85, 50, 0.22);
  filter: blur(18px);
}

.lf.is-dragging { cursor: grabbing; }

.lf-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.lf-tile {
  fill: url(#lf-cream);
  stroke: rgba(255, 255, 255, 0.35);
  stroke-width: 1;
}

.lf-grain { mix-blend-mode: multiply; opacity: 0.07; }
.lf-sheet-grain { mix-blend-mode: multiply; opacity: 0.22; }

.lf-tabs {
  position: absolute;
  left: 0;
  width: 100%;
}

.lf-tab {
  position: absolute;
  top: 0;
  height: 100%;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: rgb(75, 68, 56);
  font: inherit;
  cursor: pointer;
}

.lf-tab:focus-visible {
  outline: 2px solid #3f5a37;
  outline-offset: -6px;
}

.lf-icon {
  position: absolute;
  left: 50%;
  top: 20%;
  display: grid;
  width: clamp(22px, 5.6cqw, 40px);
  aspect-ratio: 1;
  transform: translateX(-50%);
}

.lf-icon svg { width: 100%; height: 100%; }

.lf-label {
  position: absolute;
  left: 50%;
  top: 61%;
  font-size: clamp(11px, 2.5cqw, 18px);
  font-weight: 500;
  letter-spacing: 0.005em;
  white-space: nowrap;
  transform: translate(-50%, calc(var(--k, 0) * -0.9cqw));
}

.lf-dot {
  position: absolute;
  left: 50%;
  top: 84%;
  width: clamp(4px, 1cqw, 7px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: currentColor;
  opacity: var(--k, 0);
  transform: translateX(-50%) scale(var(--k, 0));
}