Components / Motion / Dock
Dock beta
The macOS dock, in vanilla: tiles swell as the cursor approaches and settle as it leaves.
Sweep the cursor across — slowly. The wave is the part.
<div style="background:var(--k-zinc-950);border-radius:14px;min-height:170px;display:flex;align-items:center;justify-content:center;padding:18px"> <div class="k-dock" data-dock><button class="k-dock-i" type="button" data-label="Home" aria-label="Home" style="background:var(--k-ink)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="m3 11 9-8 9 8"/><path d="M5 9.5V20a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9.5"/></svg></button><button class="k-dock-i" type="button" data-label="Calendar" aria-label="Calendar" style="background:var(--k-accent)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><rect x="3.5" y="5" width="17" height="16" rx="2"/><path d="M3.5 10h17M8 3v4M16 3v4"/></svg></button><button class="k-dock-i" type="button" data-label="Reports" aria-label="Reports" style="background:var(--k-zinc-500)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="M6 20v-6M12 20V4M18 20v-9"/><path d="M3.5 20.5h17"/></svg></button><button class="k-dock-i" type="button" data-label="Docs" aria-label="Docs" style="background:var(--k-danger)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5M9 13h6M9 17h6"/></svg></button><button class="k-dock-i" type="button" data-label="Alerts" aria-label="Alerts" style="background:var(--k-zinc-700)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="M18 16H6c.6-1.2 1-2.6 1-4.5V10a5 5 0 0 1 10 0v1.5c0 1.9.4 3.3 1 4.5Z"/><path d="M10 20a2 2 0 0 0 4 0"/></svg></button><button class="k-dock-i" type="button" data-label="Team" aria-label="Team" style="background:var(--k-success)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><circle cx="12" cy="8" r="3.6"/><path d="M5 20c.8-3.2 3.6-5 7-5s6.2 1.8 7 5"/></svg></button><button class="k-dock-i" type="button" data-label="Settings" aria-label="Settings" style="background:var(--k-zinc-800)"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="M3 6h10M17 6h4M3 12h4M11 12h10M3 18h12M19 18h2"/><circle cx="15" cy="6" r="2"/><circle cx="9" cy="12" r="2"/><circle cx="17" cy="18" r="2"/></svg></button></div> </div> <p style="font-size:12.5px;color:var(--k-ink-3);margin-top:9px">Sweep the cursor across — slowly. The wave is the part.</p>
/* KATA Dock — tiles swell by proximity. */
.k-dock{display:inline-flex;align-items:flex-end;gap:7px;padding:9px 10px;background:var(--k-bg-soft);border:1px solid var(--k-line);border-radius:20px;font-family:var(--k-font)}
.k-dock-i{position:relative;width:46px;height:46px;border:0;border-radius:13px;color:var(--k-on-color);display:flex;align-items:center;justify-content:center;cursor:pointer;transform-origin:bottom center;transition:transform .14s ease-out;flex:none}
.k-dock-i::after{content:attr(data-label);position:absolute;bottom:calc(100% + 9px);left:50%;transform:translateX(-50%) translateY(3px);background:var(--k-ink);color:var(--k-bg);font-size:11px;font-weight:550;padding:4px 9px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s,transform .15s}
.k-dock-i:hover::after,.k-dock-i:focus-visible::after{opacity:1;transform:translateX(-50%) translateY(0)}
// KATA Dock — scale by distance from the cursor; rest when it leaves.
document.querySelectorAll('[data-dock]').forEach(function (dock) {
var tiles = Array.prototype.slice.call(dock.querySelectorAll('.k-dock-i'));
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
dock.addEventListener('pointermove', function (e) {
tiles.forEach(function (t) {
var r = t.getBoundingClientRect();
var d = Math.abs(e.clientX - (r.left + r.width / 2));
var s = 1 + 0.55 * Math.max(0, 1 - d / 120);
t.style.transform = 'scale(' + s.toFixed(3) + ')';
});
});
dock.addEventListener('pointerleave', function () {
tiles.forEach(function (t) { t.style.transform = ''; });
});
});
Installation
CLI
node kata-ui.mjs add dock
Writes the part’s CSS and JS into your project. New to the CLI? See the CLI doc.
Manual
- Load
tokens.cssonce, before any part. - Copy the CSS tab into your stylesheet.
- Copy the HTML where it belongs. The part has a script — include it after the markup; it initializes itself.
Usage
For app launchers and playful navigation. The script measures each tile’s distance from the cursor and scales by proximity — near tiles grow, the wave falls off within about two tiles. Tiles are buttons or links with real names; the label above a hovered tile is its name, not a decoration.
Accessibility
Magnification is pointer theatre: focus order, names and layout never move, and a keyboard user tabs through ordinary buttons. Under prefers-reduced-motion the wave is off and every tile sits at rest size.
Anatomy
The public surface of this part, read from its own source. Class names are the API — the CSS tab above is the full contract.
.k-dock | class |
.k-dock-i | class |
data-dock | data hook |
data-label | data hook |
aria-label | ARIA |
aria-hidden | ARIA |
Related parts
Accordion Gallery beta
Five scenes in a row; the one you touch opens up and tells its caption.
Aurora beta
A slow wash of color behind a hero — the accent and the core, drifting under glass.
Beam border beta
A single point of light travels the edge of a card. The beam is the core color — the one accent, in motion.
Blur In beta
Words arrive out of focus and sharpen one after another, like eyes adjusting.