Apple Dock Navigation Bar

A macOS-inspired dock navigation where hovering an icon magnifies it and proportionally scales the two nearest neighbors. The hovered item grows the most, immediate siblings grow moderately, and second-degree siblings grow slightly. A tooltip fades in above each icon on hover.

navigationdockgsaphovermagnificationtooltip

Setup — External Scripts

External Scripts
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>

Code

HTML
html
<nav class="nav-bar">
  <ul class="nav-list">
    <li class="nav-item">
      <a href="#" class="nav-item__link">
        <img src="your-icon-1.png" loading="eager" alt="App icon" class="image">
      </a>
      <div class="nav-item__tooltip"><div>App Name</div></div>
    </li>
    <li class="nav-item">
      <a href="#" class="nav-item__link">
        <img src="your-icon-2.png" loading="eager" alt="App icon" class="image">
      </a>
      <div class="nav-item__tooltip"><div>App Name</div></div>
    </li>
    <li class="nav-item">
      <a href="#" class="nav-item__link">
        <img src="your-icon-3.png" loading="eager" alt="App icon" class="image">
      </a>
      <div class="nav-item__tooltip"><div>App Name</div></div>
    </li>
    <li class="nav-item">
      <a href="#" class="nav-item__link">
        <img src="your-icon-4.png" loading="eager" alt="App icon" class="image">
      </a>
      <div class="nav-item__tooltip"><div>App Name</div></div>
    </li>
    <li class="nav-item">
      <a href="#" class="nav-item__link">
        <img src="your-icon-5.png" loading="eager" alt="App icon" class="image">
      </a>
      <div class="nav-item__tooltip"><div>App Name</div></div>
    </li>
  </ul>
</nav>
CSS
css
.nav-list {
  flex-flow: row;
  justify-content: center;
  align-items: flex-end;
  margin-bottom: 0;
  padding-left: 0;
  display: flex;
  font-size: 1.4vw;
}

.nav-item {
  justify-content: center;
  align-items: center;
  width: 5em;
  transition: width .5s cubic-bezier(.16, 1, .3, 1);
  display: flex;
  position: relative;
}

.nav-item__link {
  z-index: 1;
  pointer-events: auto;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  padding-left: .5em;
  padding-right: .5em;
  display: flex;
  position: relative;
}

.image {
  object-fit: contain;
  width: 100%;
}

.nav-item__tooltip {
  z-index: 0;
  background-color: var(--color-neutral-100);
  opacity: 0;
  white-space: nowrap;
  border-radius: .25em;
  padding: .4em .5em;
  font-size: 1em;
  transition: transform .5s cubic-bezier(.16, 1, .3, 1), opacity .5s cubic-bezier(.16, 1, .3, 1);
  position: absolute;
  top: 0;
  transform: translate(0, -80%);
  font-weight: 400;
}

.nav-item.sibling-far {
  width: 6em;
}

.nav-item.sibling-close {
  width: 7em;
}

.nav-item.hover {
  width: 8em;
}

.nav-item:hover .nav-item__tooltip {
  opacity: 1;
  transform: translate(0px, -140%);
}
JavaScript
javascript
function initAppleDockNavigationBar() {
  const navItems = document.querySelectorAll('.nav-item');

  const toggleSiblingClass = (items, index, offset, className, add) => {
    const sibling = items[index + offset];
    if (sibling) {
      sibling.classList.toggle(className, add);
    }
  };

  navItems.forEach((item, index) => {
    item.addEventListener('mouseenter', () => {
      item.classList.add('hover');
      toggleSiblingClass(navItems, index, -1, 'sibling-close', true);
      toggleSiblingClass(navItems, index,  1, 'sibling-close', true);
      toggleSiblingClass(navItems, index, -2, 'sibling-far',   true);
      toggleSiblingClass(navItems, index,  2, 'sibling-far',   true);
    });

    item.addEventListener('mouseleave', () => {
      item.classList.remove('hover');
      toggleSiblingClass(navItems, index, -1, 'sibling-close', false);
      toggleSiblingClass(navItems, index,  1, 'sibling-close', false);
      toggleSiblingClass(navItems, index, -2, 'sibling-far',   false);
      toggleSiblingClass(navItems, index,  2, 'sibling-far',   false);
    });
  });
}

document.addEventListener('DOMContentLoaded', () => {
  initAppleDockNavigationBar();
});

Attributes

NameTypeDefaultDescription
.nav-itemclassEach dock item. The JS adds .hover, .sibling-close, or .sibling-far to control the magnification level via CSS width transitions.
.nav-item.hoverclassApplied to the item directly under the cursor. Sets the maximum width (default 8em).
.nav-item.sibling-closeclassApplied to the items immediately adjacent (±1) to the hovered item. Sets a medium width (default 7em).
.nav-item.sibling-farclassApplied to items two positions away (±2) from the hovered item. Sets a slight width increase (default 6em).
.nav-item__tooltipclassOptional tooltip element inside each nav item. Fades in and translates upward on :hover via CSS transitions.

Notes

  • GSAP is listed as a dependency but the magnification and tooltip are driven entirely by CSS class toggling — GSAP is available if you want to layer additional animations.
  • The sibling effect only reaches ±2 positions; items beyond that are unaffected.
  • All classes are removed on mouseleave, so the dock resets cleanly even if the cursor moves quickly across multiple items.
  • The font-size: 1.4vw on .nav-list makes the dock fluid — it scales with the viewport width automatically.

Guide

Magnification via width

Unlike scale-based approaches, this dock uses width transitions on each .nav-item. Because items are flex children aligned to the bottom, growing the width pushes neighbors apart and the icon naturally appears larger as it fills more horizontal space.

Tuning the effect

Adjust these three CSS values to change how dramatic the magnification is. Smaller differences between levels make it subtle; larger differences make it more exaggerated.

.nav-item.sibling-far   { width: 6em; }
.nav-item.sibling-close { width: 7em; }
.nav-item.hover         { width: 8em; }

Font-size scaling

The .nav-list uses font-size: 1.4vw. Since all item widths are in em, this single value controls the entire dock's physical size. Increase it for a larger dock, decrease it for a smaller one.

Tooltip

Place a .nav-item__tooltip div inside each .nav-item. It starts at opacity: 0 and transform: translate(0, -80%) and transitions to opacity: 1 and translate(0, -140%) on :hover. The tooltip is z-index: 0 so it sits behind the icon during the transition.