CSS 드롭다운 스타일링 가이드 - (HTML <select> 요소)

CSS 드롭다운 스타일링 가이드 - (HTML <select> 요소)

드롭다운(dropdown), 리스트박스(listbox), 콤보박스(combobox), 등 여러 가지 이름으로 불리우는 HTML의 <select> 엘리먼트와 CSS를 이용하여 아래와 같이 만들어보겠습니다.

HTML 작성

사실 웹에서 드롭다운를 구현하는 방법은 여러 가지가 있지만, 본 포스팅에서는 가장 기본적인 HTML의 <select> 엘리먼트를 사용하도록 하겠습니다. 웹 접근성(accessibility)을 위해서 <label> 엘러먼트도 추가한 후에 <select> 엘리먼트와 연결을 해줍니다. 이렇게 해주면 <label> 영역뿐만 아니라 <label> 영역을 선택했을 때도, 동일하게 초점(focus)을 받게 됩니다.

선택 사항은 <option> 엘리먼트를 이용해서 <select> 엘리먼트의 자식으로 추가해주면 되고, <optgroup> 엘리먼트로 분류도 해줄 수 있습니다.

<form>
  <label for="fruit"> 좋아하는 과일 </label>
  <select id="fruit" name="fruit">
    <option value="">-- 선택하세요 --</option>
    <optgroup label="여름">
      <option value="strawberry">딸기</option>
      <option value="banana">바나나</option>
    </optgroup>
    <optgroup label="여름">
      <option value="mango">망고</option>
      <option value="melon">멜론</option>
      <option value="grape">포도</option>
      <option value="watermelon">수박</option>
    </optgroup>
    <optgroup label="가을">
      <option value="apple">사과</option>
      <option value="pear">배</option>
    </optgroup>
    <optgroup label="겨울">
      <option value="mandarine">귤</option>
    </optgroup>
  </select>
</form>

디폴트 스타일 제거

대부분의 브라우저는 User Agent Style이라고 불리는 디폴트(default) 스타일을 <select> 엘리먼트에 적용해줍니다. 아이러니하게도 커스텀 스타일을 적용할 때는 이러한 브라우저 별로 조금씩 상이한 이 디폴트 스타일이 걸림돌이 되곤 합니다.

select {
  -moz-appearance: none;
  -webkit-appearance: none;
  appearance: none;
}

위와 같이 appearance 속성을 사용해서 이러한 브라우저에서 디폴트로 제공하는 스타일을 제거합니다.

기본 스타일링

폰트, 여백, 색상, 테두리와 같은 기본적인 스타일을 <select> 엘리먼트에 적용합니다.

@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap");

select {
  /* 생략 */
  font-family: "Noto Sansf KR", sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;

  color: #444;
  background-color: #fff;

  padding: 0.6em 1.4em 0.5em 0.8em;
  margin: 0;

  border: 1px solid #aaa;
  border-radius: 0.5em;
  box-shadow: 0 1px 0 1px rgba(0, 0, 0, 0.04);
}

상태 스타일링

<select/> 엘리먼트는 hover, focus, disabled와 같은 상태를 가질 수 있습니다. 상태에 따라 약간의 시각적인 변화를 주면 보다 긍정적인 사용자 경험을 제공할 수 있습니다.

select:hover {
  border-color: #888;
}

select:focus {
  border-color: #aaa;
  box-shadow: 0 0 1px 3px rgba(59, 153, 252, 0.7);
  box-shadow: 0 0 0 3px -moz-mac-focusring;
  color: #222;
  outline: none;
}

select:disabled {
  opacity: 0.5;
}

전체 코드

appearance: base-select로 옵션 목록까지 꾸미기

지금까지 살펴본 방식에는 큰 한계가 하나 있습니다. 우리가 꾸밀 수 있는 건 닫혀 있는 <select> 버튼 부분뿐이고, 클릭했을 때 펼쳐지는 옵션 목록은 운영체제가 그리는 네이티브 UI라 CSS가 닿지 않는다는 점인데요. 그래서 오랫동안 옵션마다 아이콘을 넣거나 목록 배경을 바꾸려면 자바스크립트로 가짜 드롭다운을 만드는 수밖에 없었습니다.

그런데 최근 브라우저에 Customizable Select라는 기능이 들어오면서 이 한계가 풀렸습니다. appearance 속성에 base-select 값을 주면 펼쳐지는 목록, 각 옵션, 체크 표시, 화살표 아이콘까지 전부 CSS만으로 스타일링할 수 있게 된 거죠.

먼저 마크업이 살짝 달라집니다. <select> 안에 <button><selectedcontent> 엘리먼트를 첫 자식으로 넣어주는데요. <selectedcontent>는 현재 선택된 <option>의 내용을 그대로 복제해서 버튼 자리에 보여주는 역할을 합니다. 그리고 <option> 안에는 이제 텍스트뿐만 아니라 아이콘 같은 풍부한 HTML도 넣을 수 있습니다.

<form>
  <label for="fruit">좋아하는 과일</label>
  <select id="fruit" name="fruit">
    <button>
      <selectedcontent></selectedcontent>
    </button>

    <option value="">-- 선택하세요 --</option>
    <option value="strawberry">
      <span class="icon" aria-hidden="true">🍓</span>
      <span>딸기</span>
    </option>
    <option value="banana">
      <span class="icon" aria-hidden="true">🍌</span>
      <span>바나나</span>
    </option>
    <option value="grape">
      <span class="icon" aria-hidden="true">🍇</span>
      <span>포도</span>
    </option>
  </select>
</form>

여기서 아이콘 <span>aria-hidden="true"를 준 점에 주목하세요. 이건 WebKit 팀이 강조하는 Customizable Select의 황금률과 연결됩니다. 아이콘 같은 시각 요소는 어디까지나 텍스트를 보강하는 것이지 대체하는 게 아니어야 한다는 원칙인데요. 그래야 스크린 리더 사용자나 폴백 환경에서도 선택지의 의미가 온전히 전달됩니다.

이제 CSS로 base-select 모드를 켭니다. 중요한 점은 <select> 엘리먼트와, 펼쳐지는 목록을 가리키는 ::picker(select) 가상 요소 둘 다 켜줘야 한다는 것입니다.

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

모드를 켜면 새로운 가상 요소들이 손에 들어옵니다. 펼쳐지는 목록 박스는 ::picker(select), 화살표 아이콘은 ::picker-icon, 선택된 항목 옆 체크 표시는 option::checkmark로 각각 스타일링하는데요. 옵션이 펼쳐진 상태는 :open 가상 클래스로 잡아냅니다.

/* 펼쳐지는 목록 박스 */
::picker(select) {
  border: none;
  border-radius: 0.5em;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

/* 화살표 아이콘: 펼치면 180도 회전 */
select::picker-icon {
  color: #888;
  transition: rotate 0.3s;
}
select:open::picker-icon {
  rotate: 180deg;
}

/* 각 옵션을 flex로 배치 */
option {
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.6em 0.8em;
}
option:hover {
  background-color: #f0f6ff;
}

/* 선택된 항목 표시 */
option:checked {
  font-weight: 700;
}
option::checkmark {
  order: 1;
  margin-left: auto;
  content: "✓";
}

/* 버튼 영역에서는 아이콘을 숨겨 텍스트만 깔끔하게 */
selectedcontent .icon {
  display: none;
}

점진적 향상으로 안전하게 적용하기

여기서 꼭 짚어야 할 부분이 있습니다. Customizable Select는 2026년 기준으로 아직 모든 브라우저에서 쓸 수 있는 기능이 아닙니다. Chrome과 Edge는 135 버전부터 정식 지원하지만, Safari는 곧 나올 27 버전에서 도입될 예정이고 Firefox는 아직 플래그 뒤에서 실험 중인데요. 다행히 미지원 브라우저는 자동으로 기존 네이티브 <select>로 폴백되기 때문에, 앞서 만든 클래식 스타일을 바닥에 깔아두고 모던 스타일을 그 위에 얹는 식으로 안전하게 적용할 수 있습니다.

이때 쓰는 것이 @supports 규칙입니다. appearance: base-select를 지원하는 브라우저에서만 모던 스타일이 적용되도록 통째로 감싸주면 됩니다.

/* 모든 브라우저: 앞서 만든 클래식 스타일이 기본값(폴백) */
select {
  appearance: none;
  /* ...기본 스타일과 상태 스타일... */
}

/* 지원하는 브라우저에서만: 모던 스타일로 향상 */
@supports (appearance: base-select) {
  select,
  ::picker(select) {
    appearance: base-select;
  }

  /* ::picker(select), ::picker-icon, option::checkmark ... */
}

이렇게 해두면 최신 브라우저 사용자는 옵션 목록까지 완전히 커스텀된 드롭다운을 보고, 그렇지 않은 사용자도 깔끔하게 스타일된 네이티브 <select>를 그대로 쓸 수 있습니다. 기능이 더 많은 브라우저로 퍼질수록 똑같은 코드가 알아서 점점 더 멋지게 보이는, 점진적 향상(progressive enhancement)의 좋은 예라고 할 수 있겠네요.

마치며

HTML의 <select> 엘리먼트는 전통적으로 CSS로 스타일하기 까다로운 것으로 알려져 왔습니다. 펼쳐지는 옵션 목록을 건드릴 수 없어 브라우저의 영향을 크게 받았기 때문인데요. 그래서 오랫동안 닫힌 버튼 부분만 다듬는 선에서 타협하거나, 자바스크립트로 직접 드롭다운을 만들어야 했습니다.

하지만 앞서 살펴봤듯이 appearance: base-select가 등장하면서 이 오랜 한계가 풀리고 있습니다. 아직 모든 브라우저가 지원하는 단계는 아니지만, 클래식 스타일을 폴백으로 두고 @supports로 점진적으로 향상하는 방식이라면 지금 당장 프로덕션에도 도입할 수 있습니다.

폼 요소를 자바스크립트 없이 CSS로 다듬는 다른 주제로는 토글 스위치 만들기라디오 버튼 스타일링도 함께 살펴보면 좋습니다. Customizable Select의 더 자세한 문법과 브라우저별 지원 현황은 MDN의 Customizable select 문서에서 확인할 수 있습니다.

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord