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.
Setup — External Scripts
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>Code
<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>.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%);
}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
| Name | Type | Default | Description |
|---|---|---|---|
| .nav-item | class | — | Each dock item. The JS adds .hover, .sibling-close, or .sibling-far to control the magnification level via CSS width transitions. |
| .nav-item.hover | class | — | Applied to the item directly under the cursor. Sets the maximum width (default 8em). |
| .nav-item.sibling-close | class | — | Applied to the items immediately adjacent (±1) to the hovered item. Sets a medium width (default 7em). |
| .nav-item.sibling-far | class | — | Applied to items two positions away (±2) from the hovered item. Sets a slight width increase (default 6em). |
| .nav-item__tooltip | class | — | Optional 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.