No.129
MICRO CSSJS

macOS Dock

カーソルとの距離に応じてアイコンが滑らかに拡大するmacOS風ドック。距離を正規化して倍率に変換するだけで、隣のアイコンまで自然に膨らむ挙動を再現できる。

DEMO

アイコンにカーソルを近づけてください

CODE

HTML
<div class="demo-stage mdk-stage">
  <div class="mdk-dock" id="mdk-dock">
    <button class="mdk-item" type="button" aria-label="Finder"><span>🗂️</span></button>
    <button class="mdk-item" type="button" aria-label="Mail"><span>✉️</span></button>
    <button class="mdk-item" type="button" aria-label="Calendar"><span>📅</span></button>
    <button class="mdk-item" type="button" aria-label="Music"><span>🎵</span></button>
    <button class="mdk-item" type="button" aria-label="Camera"><span>📷</span></button>
    <button class="mdk-item" type="button" aria-label="Terminal"><span>💻</span></button>
    <button class="mdk-item" type="button" aria-label="Settings"><span>⚙️</span></button>
  </div>
  <p class="mdk-note">アイコンにカーソルを近づけてください</p>
</div>
CSS
.mdk-stage { display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:1.4rem; padding:3rem 1rem 2.2rem; background:linear-gradient(180deg,#12121A 0%,#0B0B10 100%); min-height:280px; }
.mdk-dock {
  display:flex; align-items:flex-end; gap:.5rem;
  padding:.6rem .8rem; border-radius:20px;
  background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.12);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.mdk-item {
  --mdk-scale:1;
  width:44px; height:44px; padding:0; border:none; border-radius:12px; cursor:pointer;
  background:rgba(255,255,255,.08);
  display:flex; align-items:center; justify-content:center;
  font-size:1.3rem; line-height:1;
  transform:scale(var(--mdk-scale)); transform-origin:bottom center;
  transition: transform .18s cubic-bezier(.22,1,.36,1), background .2s ease;
}
.mdk-item:hover, .mdk-item:focus-visible { background:rgba(233,27,137,.22); }
.mdk-item:focus-visible { outline:2px solid #E91B89; outline-offset:3px; }
.mdk-note { font-size:.72rem; color:#6E6E7A; letter-spacing:.06em; }
@media (prefers-reduced-motion: reduce) {
  .mdk-item { transition-duration:.01ms; }
}
JS
(function(){
  var dock = document.getElementById('mdk-dock');
  if (!dock) return;
  var items = Array.prototype.slice.call(dock.querySelectorAll('.mdk-item'));
  var RANGE = 130;   // 影響が及ぶ距離(px)
  var MAXUP = 0.85;  // 最大拡大量(1 + MAXUP 倍)
  function reset(){ items.forEach(function(el){ el.style.setProperty('--mdk-scale', 1); }); }
  dock.addEventListener('pointermove', function(e){
    items.forEach(function(el){
      var r = el.getBoundingClientRect();
      var center = r.left + r.width / 2;
      var d = Math.abs(e.clientX - center);
      var t = Math.max(0, 1 - d / RANGE);
      el.style.setProperty('--mdk-scale', (1 + MAXUP * t * t).toFixed(3));
    });
  });
  dock.addEventListener('pointerleave', reset);
  window.addEventListener('blur', reset);
})();

AI PROMPT

Claude Code Cursor v0
PROMPT
macOSのDockのように、カーソルとの距離に応じてアイコンが拡大するナビゲーションを作って。ドックの pointermove で、各アイコンの中心X座標とカーソルX座標の距離 d を求める。影響範囲 RANGE(130px程度)で正規化して t = max(0, 1 - d / RANGE) とし、倍率を 1 + MAXUP * t * t に設定する(t を2乗すると中心付近だけが鋭く膨らみ、実機の挙動に近づく)。倍率は CSSカスタムプロパティ(--scale)で渡し、CSS側は transform: scale(var(--scale)); transform-origin: bottom center; transition: transform .18s とする。JSでは transform を直接書かず変数だけ更新するのがポイントで、CSS側のトランジションが補間してくれるためカクつかない。pointerleave と window の blur で全アイコンを 1 に戻すこと(戻し忘れると拡大したまま固まる)。アイコンは button 要素にして aria-label を付け、:focus-visible のアウトラインを残すこと。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 マイクロインタラクション ―UI/UXデザインの神が宿る細部 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

RELATED EFFECTS

↗ MORE
→ MAGNETIC HOVER → SEGMENTED CONTROL → ELASTIC TAB INDICATOR

macOS Dock(カーソル追従の拡大ドック)とは

カーソルとの距離に応じてアイコンが連続的に拡大する、macOS のDockでおなじみの挙動です。Magic UI の Dock として React 実装が広まりましたが、距離の正規化と倍率の計算はごく短いコードで書けるため、バニラJSとCSSカスタムプロパティだけで再現できます。

当サイトの Magnetic Hover はカーソルに要素が引き寄せられる表現で、対象は単体です。本ページは「近傍の複数要素が距離に応じて同時に変形する」別テーマで、ナビゲーションやツールバーなど要素が並ぶ場所が対象になります。

JS側では transform を直接書かずCSSカスタムプロパティだけを更新し、補間はCSSのトランジションに任せるのが滑らかさの鍵です。また pointerleave での復帰処理を省くと、カーソルが外れた瞬間に拡大したまま固まります。

よくある質問

なぜ距離を2乗するのですか?

線形(t のまま)だと影響範囲の端まで緩やかに膨らみ、ドック全体がぼんやり波打ちます。2乗すると中心付近だけが急に立ち上がり、離れた場所ではほぼ変化しないため、実際のmacOSに近い「隣接1〜2個だけがはっきり膨らむ」挙動になります。もっと鋭くしたい場合は3乗、緩やかにしたい場合は sqrt を使います。

スマートフォンではどうなりますか?

pointermove が継続的に発火しないため、拡大は起こらず通常のアイコン列として表示されます。レイアウトは崩れないので実害はありません。タッチ環境でも押した感触を出したい場合は :active に scale を当てるか、@media (hover: hover) でホバー環境限定の装飾として扱ってください。

コードは無料で使えますか?

はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。