CSSでselectの見た目を整えて、矢印を好きな形に変える方法

​​CSS​HTML,フォーム

select の右端の矢印を変えるには、appearance: none でブラウザーの見た目を外してから、背景の SVG か、包む要素の ::after で矢印を描きます。フォームのセレクトボックスを、プルダウンの矢印までサイトのデザインに合わせたいときに使える方法です。対応しているブラウザーなら、appearance: base-select を使うと、矢印は ::picker-icon という擬似要素になり、形の差し替えや、開いたときの回転を CSS だけで書けます。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。計算値やクリックの結果は、Playwright で Chromium を動かして読みました。Firefox と Safari での表示、スマートフォンでの選択の画面は試していません。各ブラウザーの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。

4つの select を縦に並べた画面。1つ目はブラウザーの既定の見た目で、右端に黒い小さな矢印がある。2つ目は appearance: none だけを書いたもので、灰色の地に細い枠の四角になり、矢印も余白もない。3つ目は背景に SVG を置いたもので、右端に水色の矢印がある。4つ目は ::after で描いたもので、右端にピンクの矢印がある
上から、既定・appearance: none だけ・背景の SVG・::after で描いた矢印

まず文字の大きさをそろえる

select は、何も書かないと、親の文字の大きさを受け継ぎません。検証環境で、body を 16px にしたページに select を置くと、select の font-size は 13.3333px でした。font: inherit を書くと 16px になり、フォントも親と同じになりました。

css
select {
  font: inherit;
}

appearance: none で、ブラウザーの見た目を外す

appearance: none を書くと、右端の矢印のような、ブラウザー独自の見た目が外れます。上の画像の2つ目のように、灰色の地と細い枠だけの箱になるので、border や padding、background を書いて整えます。MDN の appearance のページにあるとおり、見た目を変えても、select としての動きは変わりません。

検証環境では、同じ選択肢を入れた select の幅は、既定の 71px から 51px に縮みました。矢印のための場所もなくなるので、矢印を描くときは、右の padding で場所を空けておきます。

背景に SVG の矢印を置く

いちばん手軽なのは、矢印の SVG を background に置く書き方です。HTML はそのままで済みます。

css
select {
  font: inherit;
  appearance: none;
  border: 1px solid #9aa5ab;
  border-radius: 6px;
  padding: .5em 2.5em .5em .75em;
  background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23067a98' stroke-width='2'/%3E%3C/svg%3E")
    no-repeat right .75em center / .75em auto;
}

SVG は、< を %3C、# を %23 のように書き換えて、url() の中に直接書いています。right .75em center で右端から .75em の位置の上下中央に置き、/ .75em auto で幅を決めています。

右の padding は、矢印の分まで空けます。検証環境で、幅 200px の select に長い選択肢を出し、右端から 40px の範囲に描かれた文字の画素を数えました。右の padding が .75em のときは 38 個あり、文字が矢印と重なる位置まで続いていました。2.5em にすると 0 個になりました。

背景の SVG の線の色は、CSS の color に合わせて変わりません。検証環境で、SVG の線の色を currentColor にし、select の color を赤にしても、矢印は黒で描かれました。上の例では、線の色 #067a98 を SVG の中に直接書いています。

矢印の色を文字の色に合わせるなら ::after で描く

select を span などで包み、その ::after に矢印を描きます。線の色を currentColor にすれば、包んだ要素の color で矢印の色が変わります。

html
<span class="select-wrap">
  <select>
    <option>りんご</option>
    <option>みかん</option>
    <option>ぶどう</option>
  </select>
</span>
css
.select-wrap {
  position: relative;
  display: inline-block;
  color: #d85878;
}
.select-wrap select {
  color: #1f2a30;
  /* font や appearance: none、padding は上の例と同じ。background は #fff だけにする */
}
.select-wrap::after {
  content: "";
  position: absolute;
  right: .9em;
  top: 50%;
  width: .5em;
  height: .5em;
  translate: 0 -75%;
  rotate: 45deg;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  pointer-events: none;
}

矢印は、正方形の右と下の枠線だけを描き、45度回して作っています。検証環境で .select-wrap の color を #067a98 にすると、::after の線の色は rgb(6, 122, 152) になりました。

pointer-events: none は省けません。この矢印は select の上に重なっているので、これがないと、矢印の上でクリックしても select に届きません。検証環境で、矢印の位置をクリックして比べました。pointer-events: none がないと、その位置で一番上にある要素は span で、クリックしても select にフォーカスが移りませんでした。書いたときは、一番上の要素が select になり、クリックで select にフォーカスが移りました。MDN の pointer-events のページにあるとおり、none を付けた要素は、クリックなどのポインターイベントの対象になりません。

appearance: base-select で、矢印だけを差し替える

ここまでの方法では、開いたときに出る選択肢の一覧は、ブラウザーの見た目のままです。MDN のカスタマイズ可能な select 要素の解説によると、この一覧や、既定の矢印そのものは、これまで CSS で対象にできませんでした。appearance: base-select を使うと、一覧も CSS で整えられ、矢印にも ::picker-icon で CSS を当てられるようになります。同じ解説にあるとおり、select と、開いたときの一覧を表す ::picker(select) の両方に書きます。

css
select,
::picker(select) {
  appearance: base-select;
}

select {
  font: inherit;
  width: 200px;
  border: 1px solid #9aa5ab;
  border-radius: 6px;
  padding: .5em .75em;
  background: #fff;
}

select::picker-icon {
  content: "";
  width: .5em;
  height: .5em;
  border-right: 2px solid #067a98;
  border-bottom: 2px solid #067a98;
  rotate: 45deg;
  transition: rotate .3s;
}

select:open::picker-icon {
  rotate: 225deg;
}

::picker(select) {
  border: 1px solid #9aa5ab;
  border-radius: 6px;
  margin-top: 4px;
  padding: 4px 0;
}

option {
  padding: .4em .75em;
}
option:checked {
  background: #eefafd;
}
option:hover {
  background: #00c8e8;
  color: #fff;
}
水色の矢印の付いた select を、カーソルでクリックして開き、「ぶどう」をクリックして選ぶ。クリックするたびに、左上の「クリック 1回目/2回」がピンクの地になる。1回目で一覧が開き、矢印が下向きから上向きへ回る。2回目で一覧が閉じ、矢印が下向きに戻り、右上の「選んだ値:ぶどう」がピンクの地になる
クリックで一覧を開き、選択肢を選んで閉じる様子。開いている間だけ矢印が上を向く

MDN の ::picker-icon のページにあるとおり、::picker-icon は、閉じているときに下を向いている矢印を表し、content で中身を差し替えられます。上の例では content: "" で既定の矢印を消し、::after の例と同じく、枠線を回して矢印を描いています。

検証環境で、この select をクリックすると、select:open に当てはまるようになり、::picker-icon の rotate は 45deg から 225deg に変わりました。transition を書いているので、矢印は 0.3 秒かけて回ります。一覧の「ぶどう」をクリックすると、select の value は「ぶどう」になり、一覧は閉じました。開いた一覧は select のすぐ下に出て、select の下端から最初の選択肢の上端までは 9px でした(margin-top の 4px、一覧の枠線と padding の分)。

選んでいる選択肢の左には、何も書かなくても「✓」が出ます。検証環境で、base-select だけを書いたときの option::checkmark の content には「✓」が入っていました(計算値は "✓" / "")。消したいときは、option::checkmark { display: none; } と書きます。検証環境では、「✓」の分の空きがなくなり、選択肢の文字の左端は、option の左端から 30px の位置から 8px の位置に寄りました。

対応していないブラウザーでは、背景の矢印に戻す

互換性データでは、appearance: base-select と ::picker() に対応しているのは、Chrome 135、Edge 135、Safari 27(iOS 版も 27)からです。Firefox は、appearance: base-select だけが 149 から設定のフラグを有効にしたときに使え、::picker() には対応していません。:open は Chrome 133、Edge 133、Firefox 136、Safari 26.5 からです。

先ほどの MDN の解説にあるとおり、対応していないブラウザーでは、従来の select として表示されます。@supports で囲み、ふだんは背景の SVG の矢印を使い、対応しているときだけ切り替えます。

css
select {
  font: inherit;
  appearance: none;
  padding: .5em 2.5em .5em .75em;
  background: #fff url("data:image/svg+xml,…(上の例と同じ)") no-repeat right .75em center / .75em auto;
}

@supports (appearance: base-select) {
  select,
  ::picker(select) {
    appearance: base-select;
  }
  select {
    background-image: none;
  }
  /* ::picker-icon や ::picker(select) の指定は、ここに書く */
}

background-image: none を忘れると、背景の矢印と ::picker-icon の矢印が2つ並びます。検証環境の Chromium 153 では、@supports の中が使われ、appearance は base-select、background-image は none になりました。@supports の条件を、どのブラウザーも対応していない値に書き換えて試すと、appearance は none のままで、背景の SVG の矢印が残りました。

​Posted by 86