
2026/03/19 3:23
**カスタマイズ可能なドロップダウンを乱用する**
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
この記事は、最近のChromiumベースブラウザが開発者にJavaScriptを一切使わずにインタラクティブで完全にスタイラブルな
<select> 要素を作成できる方法を説明しています。任意のHTMLを <option> タグ内に埋め込み、::picker() や sibling-index() などの新しいCSS機能を使用することで、デザイナーはデフォルトのドロップダウンをカスタムアニメーション、画像、カードスタイルレイアウトに置き換えつつ、ネイティブアクセシビリティを保持できます。
主なテクニックは次のとおりです:
- ピッカーアイコンを隠す (
) し、select::picker-icon{display:none;}
を直接スタイル付けする(背景グラデーション、影など)。<select>
を設定して、オプションが従来のリストではなくフロートするようにする。::picker(select){background:transparent;border:none;overflow:visible;}- カスタムチェックマーク (
) や絵文字アイコンをoption::checkmark{content:"●";color:#222;}
で追加する。option::before - 「曲がったフォルダの山」デモでオプションを回転させるために
を使用して漸進的な回転オフセットを計算し、開いたときにその回転をsibling-index()
でアニメートする。@starting-style
内に空の<select>
を挿入(デフォルト選択内容を上書き)し、<button>
で中央配置、その後各カードをposition-area:center center; inset:0;
とsibling-index()
から派生した変換で位置決めすることで、ファン状のカードデッキを作成。sibling-count()- 「放射状絵文字ピッカー」でオプションを円形にレイアウトするには、
とsibling-index()
から計算された角度に対して三角関数(cos, sin)を使用。sibling-count()
すべてのデモは、これらカスタマイズ可能なセレクトが優雅に退化することを示しています。古いブラウザは埋め込まれたHTMLを無視しプレーンテキストを表示しますが、新しいエンジンではリッチなスタイリング付きのフルHTMLコンテンツがレンダリングされます。記事は、こうしたネイティブサポートがさらに進化し(より多くの疑似要素やCSS変数を追加)複雑なUIパターンを簡素化し、開発者のJavaScriptオーバーヘッドを削減できると結論付けています。
本文
ウェブブラウザは常に新機能を導入していますが、その機能で「ばかげた」楽しいことが作れないと何の意味がありますでしょう?
この記事では、カスタマイズ可能な
<select> を使って実装した数例を紹介し、それぞれの実装手順やテクニックを丁寧に解説します。自分だけでなく、皆さんがより役立つアイデアを思いつくこともあるでしょう。私自身は少し大げさな発想を試すことで学びを深めています。
まず、ブラウザ互換性について一言。この記事のデモは、現在カスタマイズ可能な
<select> が実装されている最新の Chromium 系ブラウザでのみ動作します。ただし、この機能は「非対応ブラウザを壊さない」設計になっており、未対応の場合は通常の <select> とオプションが表示されます。つまり、デモ自体は進化した見た目になるかどうかだけで、基本的な動作は保証されています。
1. 曲げられたフォルダスタック
最初に紹介するデモは、選択肢を「フォルダの山」にして、クリックすると曲がるように見えるものです。
HTML
<select> <option value="documents"><span>Documents</span></option> <option value="photos"><span>Photos</span></option> <option value="music"><span>Music</span></option> <option value="videos"><span>Videos</span></option> <option value="downloads"><span>Downloads</span></option> <option value="desktop"><span>Desktop</span></option> <option value="projects"><span>Projects</span></option> <option value="backups"><span>Backups</span></option> <option value="trash"><span>Trash</span></option> </select>
フォルダ名は
<span> で包み、後でスタイルを付けやすくしています。古いブラウザでは余計な要素が無視され、テキストだけが表示されます。
基本スタイリング
/* カスタマイズを有効化 */ select, ::picker(select) { appearance: base-select; } /* デフォルトのピッカーアイコンを隠す */ select::picker-icon { display: none; } /* ボタン風に見せる */ select { background: linear-gradient( 135deg, rgba(40,40,50,.4) 0%, rgba(60,60,70,.25) 50%, rgba(50,50,60,.35) 100% ); backdrop-filter: blur(12px) saturate(180%); box-shadow: 0 8px 32px rgba(0,0,0,.2), inset 0 1px 1px rgba(255,255,255,.15), inset 0 -1px 1px rgba(0,0,0,.1); border: 1px solid rgba(255,255,255,.2); color: white; min-inline-size: 12rem; }
ドロップダウン部分
/* 背景を透明にしてオーバーフロー許可 */ ::picker(select) { background: transparent; border: none; box-shadow: none; overflow: visible; } /* チェックマークをディスクアイコンに差し替え */ option::checkmark { content: "●"; color: #222; } /* 各オプションの前にフォルダ絵文字を追加 */ option::before { content: "📁"; margin-right: .5em; }
スタックを曲げる
/* 隣接する兄弟要素のインデックスで回転角を決める */ option { --rotation-offset: -4deg; rotate: calc(sibling-index() * var(--rotation-offset)); transform-origin: right calc(sibling-index() * -1.5rem); } /* セレクトが開いたときに回転をアニメーション化 */ option { rotate: 0deg; transition: rotate .3s cubic-bezier(.34,1.56,.64,1); } select:open option { rotate: calc(sibling-index() * -1 * var(--rotation-offset)); } /* 表示時にトランジションが走るように */ @starting-style { select:open option { rotate: 0deg; } } /* アニメーションを遅延させて段階的に表示 */ option { transition-delay: calc((sibling-index() - 1) * .01s); }
結果として、ボタンをクリックするとアニメーションで曲がったフォルダの山が現れます。
2. フラットなカードデッキ
次に紹介するのは、カードゲーム風に「開くと広がる」デッキです。
HTML
<select> <button></button> <!-- デフォルトの selectedcontent を抑制 --> <option class="red" value="QH"> <span class="rank">Q</span> <span class="suit">♥</span> </option> <!-- 他のカードも同様に追加… --> </select>
空の
<button> を入れることで、ブラウザが自動で挿入する <selectedcontent> を防ぎ、デッキの裏面を静的に保ちます。
基本スタイリング
select { background: repeating-linear-gradient(45deg, transparent, transparent 1vmin, rgba(255,255,255,.05) 1vmin, rgba(255,255,255,.05) 2vmin), repeating-linear-gradient(-45deg, transparent, transparent 1vmin, rgba(255,255,255,.05) 1vmin, rgba(255,255,255,.05) 2vmin), linear-gradient(135deg,#8b0000 0%,#dc143c 50%,#8b0000 100%); }
ドロップダウンの位置決め
::picker(select) { position-area: center center; inset: 0; /* 利用可能な全領域を使う */ } select:open::picker(select) { display: flex; }
カードのファン回転・平行移動
option { --card-fan-rotation: 7deg; --card-fan-spread: -11vmin; --option-index: calc(sibling-index() - 1); --center: calc(sibling-count() / 2); --offset-from-center: calc(var(--option-index) - var(--center)); rotate: calc(var(--offset-from-center) * var(--card-fan-rotation)); translate: calc(var(--offset-from-center) * var(--card-fan-spread)) 0; transform-origin: center 75vmin; }
アニメーション
@property --card-fan-rotation { syntax: '<angle>'; inherits: false; initial-value: 7deg; } @starting-style { select:open option { --card-fan-rotation: 0deg; } } option { transition: --card-fan-rotation .2s ease-out; } select:open option { --card-fan-rotation: initial; }
<select> を開くと、カードが滑らかに広がっていきます。
3. 放射状絵文字ピッカー
最後に紹介するのは、円形に並ぶ絵文字を選択できるデモです。
ドロップダウンを円の中に配置
::picker(select) { top: calc(anchor(top) - var(--radius)); left: calc(anchor(left) - var(--radius)); width: calc(var(--radius)*2 + var(--option-size)); height: calc(var(--radius)*2 + var(--option-size)); }
オプションを円周上に配置
option { position: absolute; --angle: calc((sibling-index() - 2) * (360deg / (sibling-count() - 1)) - 90deg); top: 50%; left: 50%; translate: calc(-50% + cos(var(--angle)) * var(--radius)), calc(-50% + sin(var(--angle)) * var(--radius)); }
兄弟インデックスを使って三角関数で位置決めすることで、完全な円形に配置されます。
まとめ
以上が今回紹介したカスタマイズ可能
<select> のサンプルです。要素自体はまだ
<select> そのままで、非対応ブラウザでも通常のセレクトボックスとして機能します。そのため「段階的拡張」(progressive enhancement) として非常に安全かつ強力な手法と言えるでしょう。
ぜひ自身で色々試し、楽しい UI を作ってみてください。 Happy coding!