Skip to Content
WebHTML모던 HTML18. 실험적 기능: Customizable select로 필터 만들기

이번 편의 결과물: 프로젝트 필터 select가 지원 브라우저에서는 커스텀 스타일로, 미지원 브라우저에서는 평범한 네이티브 select로 그대로 동작한다 · 다루는 개념: appearance: base-select, selectedcontent, 실험적 기능의 폴백 설계

실험적 기능 표기: 이 편의 Customizable select는 2026-09 기준 Chrome/Edge 135+에서만 안정적으로 동작하고, Safari는 Technology Preview 단계, Firefox는 플래그 뒤 프로토타입이다. “이제 select를 자유롭게 스타일링할 수 있다”고 단정하지 않는다. 집필 시점과 읽는 시점의 지원 상태가 다를 수 있으니 MDN selectedcontent 호환성 표 를 02편 방법으로 직접 확인한다.

이 편에서 만드는 파일

web-practice/portfolio/ ├── projects.html ~ (태그 필터 select 신규 추가, button+selectedcontent, 카드마다 data-tags 추가) ├── js/main.js ~ (태그 select change 필터 로직, base-select 지원 감지 추가) └── css/styles.css ~ (appearance: base-select 스타일, @supports 폴백)

개념 정리

지금까지 <select>는 브라우저마다 드롭다운 목록의 모양을 직접 그렸고, CSS로 목록 안쪽을 꾸밀 방법이 없었다. Customizable <select><select> 안에 <button>을 첫 자식으로 넣고, 그 버튼 안에 <selectedcontent>를 두면 선택된 옵션의 내용을 그대로 미러링해서 보여준다. CSS에서 appearance: base-select를 주면 브라우저 기본 렌더링을 걷어내고 일반 요소처럼 스타일링할 수 있다.

요소/속성역할
<button> (select의 첫 자식)닫혀 있을 때 보이는 버튼 부분
<selectedcontent>선택된 <option>의 내용을 실시간으로 복제해 표시
appearance: base-select브라우저 기본 select 렌더링을 해제하고 커스텀 스타일을 허용
::picker(select)열렸을 때 드롭다운 목록 부분을 스타일링하는 의사 요소

폴백이 반드시 필요한 이유: appearance: base-select는 가산적(additive)이다. 이 속성을 모르는 브라우저는 그냥 무시하고, <select> 안에 들어간 <button>/<selectedcontent>도 표준 select 콘텐츠 모델에서 벗어난 부분이라 무시한 채 <option> 목록만 정상적으로 보여준다. 즉 아무 것도 하지 않아도 미지원 브라우저는 평범한 select로 동작한다. 이 편에서 확인해야 할 것은 “커스텀 스타일이 보이는가”가 아니라 “미지원 브라우저에서도 선택 기능 자체는 멀쩡한가”다.

실습

1. 파일 열기

web-practice/portfolio/projects.htmlcss/styles.css를 연다. 이번 편에서 프로젝트 태그 필터 <select id="tag-filter">를 처음 만든다.

2. 코드 작성

08편의 <search> 아래에 태그 필터 <select>를 추가하고, <select> 안 첫 자리에 <button><selectedcontent>를 넣는다. <option> 값은 06편에서 카드마다 붙인 기술 배지 이름과 맞춘다.

<!-- projects.html --> <!-- ...기존 코드 유지 --> <search> <label for="project-search">프로젝트 이름으로 찾기</label> <input type="search" id="project-search" placeholder="예: bookshelf" /> </search> <label for="tag-filter">태그로 거르기</label> <select id="tag-filter"> <button type="button"> <selectedcontent></selectedcontent> </button> <option value="all">전체</option> <option value="JavaScript">JavaScript</option> <option value="React">React</option> <option value="Web APIs">Web APIs</option> <option value="HTML">HTML</option> <option value="CSS">CSS</option> <option value="Node.js">Node.js</option> </select> <!-- ...기존 코드 유지 -->

각 프로젝트 카드(<article class="project-card">)에도 필터가 읽을 data-tags를 추가한다. 오늘의 날씨 카드 기준이며, 나머지 5개도 06편 기술 배지와 같은 값으로 채운다.

<!-- projects.html의 project-grid 안, 카드 1 --> <article class="project-card" data-tags="JavaScript,PWA"> <!-- ...09편까지 만든 이미지·제목·설명·tags·btn-detail 버튼 유지... --> </article>

CSS에서는 @supports로 지원 여부를 나눠, 지원하는 브라우저에서만 appearance: base-select::picker(select)를 적용한다.

/* css/styles.css */ /* ...기존 코드 유지 */ /* Customizable select를 지원하는 브라우저에서만 커스텀 스타일 적용 */ @supports (appearance: base-select) { #tag-filter { appearance: base-select; border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-2) var(--space-3); } #tag-filter::picker(select) { border: 1px solid var(--color-border); border-radius: var(--radius); box-shadow: var(--shadow); } #tag-filter option:checked { background: var(--color-surface); font-weight: bold; } } /* ...기존 코드 유지 */

<selectedcontent>는 브라우저가 자동으로 채워 주므로 별도 JS가 필요 없다. 대신 change 이벤트로 카드를 걸러내는 로직을 js/main.js에 새로 추가한다. 지원 여부만 감지해 문서 최상단에 클래스를 붙여 두면, 나중에 CSS에서 “지원 브라우저 전용” 안내 문구를 보여주는 데도 쓸 수 있다.

// js/main.js // ...기존 코드 유지 const tagFilter = document.querySelector('#tag-filter') const projectCards = document.querySelectorAll('.project-card') tagFilter?.addEventListener('change', () => { const tag = tagFilter.value projectCards.forEach((card) => { const tags = (card.dataset.tags || '').split(',') card.hidden = tag !== 'all' && !tags.includes(tag) }) }) if (window.CSS?.supports?.('appearance', 'base-select')) { document.documentElement.classList.add('supports-base-select'); } // ...기존 코드 유지

3. 실행

npx serve web-practice/portfolio로 서버를 띄우고 projects.html을 Chrome/Edge 135 이상과, 지원하지 않는 브라우저(또는 개발자 도구의 이전 렌더링 모드) 두 곳에서 각각 연다.

4. 확인

  • Chrome/Edge 135+에서는 태그 필터 select를 열면 옵션 목록에 styles.css로 준 테두리·그림자 스타일이 적용된다.
  • 지원하지 않는 브라우저에서는 select가 평범한 네이티브 드롭다운으로 보이지만, 옵션을 고르면 js/main.js의 필터 로직이 똑같이 동작해 프로젝트 카드가 걸러진다.
  • 개발자 도구 콘솔에서 CSS.supports('appearance', 'base-select')를 직접 실행해 참/거짓 값을 확인한다.
  • 두 브라우저 모두에서 옵션을 선택했을 때 <selectedcontent> 안쪽 텍스트(또는 네이티브 select의 표시값)가 선택한 옵션과 일치한다.

직접 해보기

  1. <select><option> 안에 이모지나 짧은 아이콘 문자를 추가해 <selectedcontent>가 옵션 내용을 그대로 복제하는지 확인해 본다.

답 보기

<option value="React">⚛️ React</option>처럼 옵션 안에 이모지를 넣으면, 지원 브라우저에서는 <selectedcontent>도 이모지를 포함한 텍스트를 그대로 복제해서 보여준다. <selectedcontent>는 선택된 옵션의 내용을 실시간으로 미러링하기 때문이다.

  1. @supports not (appearance: base-select) 블록을 추가해, 미지원 브라우저에서만 적용할 대체 스타일(예: 테두리 색만 살짝 다르게)을 만들어 본다.

자주 하는 실수

증상원인고치는 법
커스텀 스타일이 모든 브라우저에서 다르게 보임@supports로 감싸지 않고 무조건 appearance: base-select 적용지원 여부를 @supports (appearance: base-select)로 분기한다
<button>을 select 밖에 따로 만들어 둠customizable select 구조를 오해<button>은 반드시 <select>의 첫 자식이어야 한다
이 기능을 프로덕션에 바로 적용Baseline 미도달 상태를 확인하지 않음02편 방법으로 MDN 호환성 표를 다시 확인하고 결정한다
<selectedcontent>에 직접 텍스트를 넣어 관리하려 함자동 미러링 동작을 모름내용은 항상 선택된 <option>에서 자동으로 채워지므로 직접 수정하지 않는다

확인 문제

문제 14지선다
Customizable select에서 selectedcontent 요소의 역할은?
문제 24지선다
appearance: base-select를 미지원 브라우저에 적용했을 때 벌어지는 일로 알맞은 것은?
문제 34지선다
Customizable select 구조에서 button 요소가 반드시 지켜야 하는 위치는?
문제 44지선다
2026-09 시점에서 이 편의 태도로 가장 알맞은 것은?

참고 자료

Last updated on