テック技報最新号へ →
CSS

offset-path: ray() で円形メニューの項目を放射状に並べる(sibling-index() と linear() でバネ付きに)

各項目に offset-path: ray(角度) を付けて offset-distance を 0 から 100% に変えると、中心から飛び出す円形(ラジアル)メニューが座標計算なしで作れる。

@gkurttech · 投稿 2026-11-05

どんな時に使う?

フローティングボタンを押すと周りにアイコンが広がるメニューや、画面の角から扇形に開くメニューを作りたいとき。三角関数で top と left を計算する代わりに、「角度」と「中心からの距離」で位置を決められる。項目数が変わっても sibling-index() と sibling-count() で角度が自動で割り振られる。

コード

css
.radial {
  position: relative;
  width: 240px;
  aspect-ratio: 1;
}

.radial > .item {
  position: absolute;
  /* 何番目かで角度を決め、中心から伸びる線の上に置く */
  offset-path: ray(calc(360deg / sibling-count() * sibling-index()) closest-side at center);
  offset-rotate: 0deg;     /* 既定の auto だと線の向きに回転してしまう */
  offset-distance: 0%;     /* 閉じているときは中心に重ねる */
  transition: offset-distance 0.6s
    linear(0, 0.6 15%, 1.1 30%, 0.97 45%, 1.01 60%, 1);  /* はね返るバネ */
  transition-delay: calc(sibling-index() * 40ms);       /* 1個ずつずらす */
}

.radial.is-open > .item {
  offset-distance: 100%;   /* closest-side(中心から一番近い辺まで)だけ外へ */
}

注意点・対応状況

  • offset-path: ray() は Chrome 116、Firefox 122、Safari 16 から対応(Interop 2024 対象で、2024-01 から全主要ブラウザ)。at <位置> の指定は Safari 17.2 から。
  • 足を引っ張るのは sibling-index() / sibling-count() の方。未対応ブラウザも考えるなら、style="--i: 0" のように番号をカスタムプロパティで渡す書き方に切り替える。
  • offset-* は transform と同じく見た目だけを動かすので、クリック範囲も一緒に動くが、レイアウト上の場所は元のまま。閉じているときは pointer-events: none や inert で重なった項目を押せないようにする。
  • prefers-reduced-motion: reduce のときはバネと遅延を外すのが無難。

出典

X の元の投稿を開く