№ 008

Ember Nav

A liquid-glass tab bar with a glowing amber orb on a spring; the bar bulges around it like liquid.

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

Usage

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

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

A liquid-glass tab bar: a glowing amber orb rides on a spring, and the bar's outline is a smooth
union of the pill and a circle around the orb, so the edges bulge around it like liquid.

Source

/* Ember nav — liquid glass tab bar with a glowing amber orb. Sizes come from the bar's width (set in JS). */

.en {
  position: relative;
  width: 100%;
  max-width: 380px;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: pan-y;
  cursor: pointer;
}

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

/* warm pool of light the orb throws onto the page, spreading sideways from the bar's bottom edge */
.en-glow {
  position: absolute;
  left: 0;
  top: 0;
  width: 62%;
  aspect-ratio: 3.4;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 236, 190, 0.95), rgba(255, 186, 96, 0.75) 32%, rgba(255, 150, 70, 0.28) 66%, rgba(255, 140, 60, 0));
  filter: blur(9px);
  opacity: 0.75;
  pointer-events: none;
  will-change: transform;
}

/* softer, wider halo around the orb's side of the bar */
.en-halo {
  position: absolute;
  left: 0;
  top: 0;
  width: 70%;
  aspect-ratio: 1.9;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 170, 90, 0.42), rgba(255, 160, 90, 0.12) 60%, rgba(255, 160, 90, 0));
  filter: blur(14px);
  opacity: 0.7;
  pointer-events: none;
  will-change: transform;
}

/* hot flare on the crest of the top bulge */
.en-flare {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--en-orb) * 1.9);
  height: calc(var(--en-orb) * 0.55);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 255, 248, 1), rgba(255, 236, 190, 0.95) 22%, rgba(255, 190, 100, 0.55) 48%, rgba(255, 160, 70, 0.15) 75%, rgba(255, 160, 70, 0));
  filter: blur(2px);
  opacity: 0.78;
  pointer-events: none;
  will-change: transform;
}

.en-shape {
  position: absolute;
  top: 0;
  overflow: visible;
  pointer-events: none;
  filter:
    drop-shadow(0 18px 22px rgba(46, 26, 70, 0.32))
    drop-shadow(0 4px 6px rgba(30, 18, 44, 0.3));
}

.en-tabs {
  position: absolute;
  inset: 0;
}

.en-tab {
  position: absolute;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: #d6cfe8;
  cursor: pointer;
}

.en-tab:focus-visible { outline: 2px solid rgba(255, 180, 106, 0.8); outline-offset: -6px; }

.en-icon {
  display: grid;
  width: var(--en-icon);
  height: var(--en-icon);
  transition: transform 0.25s cubic-bezier(0.3, 1.6, 0.5, 1);
}

.en-icon svg { width: 100%; height: 100%; filter: drop-shadow(0 0 4px rgba(190, 175, 255, 0.18)); }

/* glowing smear behind the moving orb */
.en-trail {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--en-orb) * 1.1);
  height: calc(var(--en-orb) * 0.8);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 170, 80, 0.85), rgba(255, 130, 40, 0));
  filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}

.en-orb {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--en-orb);
  height: var(--en-orb);
  pointer-events: none;
  will-change: transform;
}

.en-ball {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(ellipse 58% 34% at 50% 16%, rgba(255, 246, 220, 0.85), rgba(255, 230, 180, 0.12) 70%, transparent 72%),
    radial-gradient(circle at 50% 108%, rgba(255, 214, 140, 0.95), rgba(255, 190, 100, 0) 46%),
    radial-gradient(circle at 50% 58%, #ffc25c 0%, #ff9d2f 30%, #f7801b 58%, #e0620f 82%, #b84a0c 100%);
  box-shadow:
    inset 0 0 0 1.5px rgba(255, 200, 130, 0.55),
    inset 0 -6px 12px rgba(255, 210, 140, 0.45),
    inset 0 4px 10px rgba(255, 255, 255, 0.25),
    0 0 12px rgba(255, 176, 80, 0.75),
    0 0 28px rgba(255, 150, 50, 0.42),
    0 0 56px rgba(255, 130, 40, 0.18);
  will-change: transform;
}

.en-glyph {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
}

.en-glyph svg {
  width: calc(var(--en-orb) * 0.46);
  height: calc(var(--en-orb) * 0.46);
  stroke-width: 1.9;
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.8)) drop-shadow(0 0 8px rgba(255, 200, 120, 0.9));
}

.en-glyph.is-pop svg { animation: en-pop 0.42s cubic-bezier(0.3, 1.7, 0.5, 1); }

@keyframes en-pop {
  from { opacity: 0.2; transform: scale(0.55); }
}

@media (prefers-reduced-motion: reduce) {
  .en-glyph.is-pop svg { animation: none; }
  .en-icon { transition: none; }
}