No.199
MICRO CSSJS

Customizable Select

ネイティブの select のまま、選択肢にアイコンを入れ、開いたメニューをふわっと出す CSS の appearance: base-select。キーボード操作やフォームの送信はそのまま使え、未対応のブラウザでは普通の select に戻ります。

MY COLLECTION RANDOM ↗

DEMO

選択中:テキスト

CODE

HTML
<div class="demo-stage csl-stage">
  <div class="csl-field">
    <label class="csl-label" for="csl-select">エフェクトの種類</label>
    <select class="csl-select" id="csl-select">
      <button><selectedcontent></selectedcontent></button>
      <option value="text"><span class="csl-ic" aria-hidden="true" data-ic="Aa"></span><span class="csl-name">テキスト</span></option>
      <option value="scroll"><span class="csl-ic" aria-hidden="true" data-ic="↕"></span><span class="csl-name">スクロール</span></option>
      <option value="loading"><span class="csl-ic" aria-hidden="true" data-ic="◌"></span><span class="csl-name">ローディング</span></option>
      <option value="modal"><span class="csl-ic" aria-hidden="true" data-ic="▣"></span><span class="csl-name">モーダル</span></option>
    </select>
    <p class="csl-status" id="csl-status" aria-live="polite">選択中:テキスト</p>
  </div>
</div>
CSS
.csl-stage { position:relative; overflow:hidden; display:flex; align-items:flex-start; justify-content:center; padding:2.2rem 1.25rem; background:#0B0B10; min-height:340px; }
.csl-field { display:flex; flex-direction:column; gap:.5rem; width:100%; max-width:280px; }
.csl-label { color:#EDEDED; font-size:.82rem; font-weight:600; }
.csl-select { min-height:48px; padding:0 .9rem; border-radius:12px; border:1px solid rgba(255,255,255,.2); background:#16161D; color:#EDEDED; font:inherit; font-size:.9rem; }
.csl-select:focus-visible { outline:2px solid #fff; outline-offset:3px; }
.csl-status { margin:0; color:#A1A1AA; font-size:.78rem; }
/* ここから下は、ブラウザが対応している時だけ有効(未対応なら普通の select のまま) */
@supports (appearance: base-select) {
  .csl-select, .csl-select::picker(select) { appearance: base-select; }
  .csl-select { display:flex; align-items:center; gap:.6rem; cursor:pointer; transition: border-color .2s; }
  .csl-select:open { border-color:#F472B6; }
  .csl-select::picker-icon { color:#F472B6; transition: rotate .3s cubic-bezier(.22,1,.36,1); }
  .csl-select:open::picker-icon { rotate:180deg; }
  .csl-select::picker(select) { margin-top:6px; padding:.35rem; border:1px solid rgba(255,255,255,.14); border-radius:12px; background:#1E1E26; color:#EDEDED;
    box-shadow:0 16px 40px rgb(0 0 0 / .5);
    opacity:0; translate:0 -6px; transition: opacity .2s, translate .25s cubic-bezier(.22,1,.36,1), overlay .25s allow-discrete, display .25s allow-discrete; }
  .csl-select:open::picker(select) { opacity:1; translate:0 0; }
  @starting-style { .csl-select:open::picker(select) { opacity:0; translate:0 -6px; } }
  .csl-select option { display:flex; align-items:center; gap:.7rem; min-height:44px; padding:0 .7rem; border-radius:8px; font-size:.88rem; cursor:pointer; }
  .csl-select option:hover { background:rgba(209,23,122,.22); }
  .csl-select option:focus-visible { background:rgba(209,23,122,.22); outline:2px solid #F472B6; outline-offset:-2px; }   /* キーボードの現在地は3:1以上の枠で示す */
  .csl-select option:checked { font-weight:700; }
  .csl-select option::checkmark { order:1; margin-left:auto; color:#F472B6; }
  .csl-ic { display:inline-flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:8px; background:#D1177A; color:#fff; font-size:.72rem; font-weight:700; }
  /* アイコンの文字は CSS で出す=未対応ブラウザの普通の select に「Aa」などが混ざらない */
  .csl-ic::before { content: attr(data-ic); }
  .csl-select selectedcontent { display:flex; align-items:center; gap:.6rem; }
}
@media (prefers-reduced-motion: reduce) {
  .csl-select::picker(select), .csl-select::picker-icon { transition:none; }
}
JS
(function(){
  var sel = document.getElementById('csl-select');
  var status = document.getElementById('csl-status');
  if (!sel || !status) return;
  sel.addEventListener('change', function(){
    var o = sel.options[sel.selectedIndex];
    var name = o.querySelector('.csl-name');
    status.textContent = '選択中:' + (name ? name.textContent : o.textContent.trim());
  });
})();

AI PROMPT

Claude Code Cursor v0
PROMPT
ネイティブの select のまま、選択肢にアイコンを入れて開くメニューをふわっと出す、カスタマイズ可能な select を作って。select の最初の子に <button><selectedcontent></selectedcontent></button> を置き(閉じた select に選んだ option の中身が複製される)、各 option には aria-hidden="true" の小さなアイコンの span(中身は空にして data-ic 属性に記号を入れ、::before の content: attr(data-ic) で表示する=未対応ブラウザの普通の select に記号が混ざらない)と名前の span を入れる。スタイルは全て @supports (appearance: base-select) の中に書き、select と select::picker(select) に appearance: base-select を指定する。select:open で枠の色を変え、::picker-icon(下向き矢印)は開くと rotate:180deg で回す。::picker(select) は opacity:0・translate:0 -6px から :open で opacity:1・translate:0 0 へ、@starting-style で開く瞬間の値を指定し、overlay と display に allow-discrete を付けて閉じる時も消える動きを見せる。option は display:flex・高さ44px 以上にし、キーボードで移動した時の :focus-visible は背景色だけでなく 2px の枠線(背景との差が3:1以上)で示し、:checked は太字、::checkmark は order:1 と margin-left:auto で右端に置く。selectedcontent も flex でアイコンと名前を横に並べる。change イベントで「選択中:名前」を aria-live の欄に表示する(option の中に要素があっても名前の span から文字を取る)。未対応のブラウザでは button や span が無視されて普通の select になることを前提にする。prefers-reduced-motion: reduce の時は transition を無効化する。

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アソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

CREATOR TOOLS · スクール(PR)

🎬 Adobe Creative Cloud — After Effects / Photoshop 業界標準ツール → 💻 SkillHacks — プログラミング・動画編集オンラインスクール → 🚀 デイトラ — Web制作スクール・実案件レベルまで習得 →

※本サイトはアフィリエイトリンクを含みます。リンク経由のご購入・お申し込みで運営者が紹介料を受け取る場合があります。

RELATED EFFECTS

↗ MORE
→ SEGMENTED CONTROL → FLOATING LABEL INPUT → POPOVER API MENU

カスタマイズ可能な select(appearance: base-select)とは

カスタマイズ可能な select は、HTML の select 要素のまま、閉じた時のボタン・開いたメニュー・矢印・選択中の印・各選択肢の見た目を CSS で作り変えられる仕組みです。select と ::picker(select) に appearance: base-select を指定すると有効になり、MDN の解説では、開いたメニューを ::picker(select)、矢印を ::picker-icon、選択中の印を ::checkmark で指定し、option の中には画像などの要素も入れられると説明されています。

当サイトの Segmented Control は選択中の背景が弾むように滑る切り替えのタブ、Floating Label Input はフォーカスするとラベルが上へ移動する入力欄、Popover API Menu は popover 属性で JavaScript なしに開閉するポップオーバーです。本ページはフォームの select そのものの見た目を変える別テーマです。

これまで select の見た目を大きく変えるには、div と JavaScript でドロップダウンを作り直すしかなく、キーボード操作や読み上げへの対応が抜けがちでした。select のまま作り変えられるので、国や言語の選択、アイコン付きの種類の選択のように、見た目にこだわりたいフォームの部品に向いています。

よくある質問

未対応のブラウザではどうなりますか?

MDN の説明では、この書き方は未対応のブラウザで従来の select に戻るように作られていて、先頭の button と selectedcontent は無視され、option の中の要素は文字だけが残ります。MDN の互換性データ(browser-compat-data 2026-09-24 版)では、appearance: base-select は Chrome・Edge 135 と Safari 27 から使え、Firefox は 149 以降でもフラグを有効にした時だけです。本実装はスタイルを @supports (appearance: base-select) の中にまとめています。

独自のドロップダウンを JavaScript で作るのと比べて何が違いますか?

select のまま見た目を変えるので、キーボード操作(矢印キー・Enter・Esc)、フォームへの値の送信、読み上げソフトでの読み上げ、スマートフォンでの操作など、select に元からある動きがそのまま使えます。div と JavaScript で作り直すと、これらを全て自分で作る必要があり、抜けがあると使えない人が出ます。

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

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