네이티브 select 화살표를 전 플랫폼에서 통일하기 (iOS 는 왜 양방향 화살표일까)
iOS Safari 에서만 양방향 화살표로 보이는 select 를 appearance 리셋으로 통일하고, data-uri SVG 에는 CSS 변수가 먹지 않아 DOM 오버레이를 쓰게 된 이유를 정리했습니다.
같은 <select> 마크업인데 기기마다 드롭다운 화살표 모양이 달랐습니다. 데스크톱 Chrome 과 안드로이드 Chrome 은 아래 방향 화살표(⌄)인데, iOS Safari 만 위아래 양방향 화살표(↕)였습니다.
코드가 틀린 건 아닙니다. 브라우저와 OS 가 native form control 을 각자 다르게 그리는 것뿐이죠. 디자인을 맞추고 싶으면 직접 그려야 합니다.
먼저 기본 모양부터 지웁니다
select {
appearance: none;
-webkit-appearance: none; /* iOS Safari 의 ↕ 제거에 필수 */
-moz-appearance: none;
padding-right: 28px; /* 커스텀 화살표 자리 */
}appearance: none 이 OS 기본 화살표를 없앱니다. 그다음 화살표를 직접 얹으면 되는데, 방법이 둘입니다.
방법 A, background-image 에 SVG data-uri
select {
background-image: url("data:image/svg+xml,<svg ...><path stroke='%23888' .../></svg>");
background-repeat: no-repeat;
background-position: right 10px center;
}간단하지만 함정이 하나 있습니다. 바로 아래에서 다룹니다.
방법 B, DOM 오버레이
<span style={{ position: "relative", display: "inline-flex" }}>
<select style={{ appearance: "none", paddingRight: 28 }}>…</select>
<ChevronDown
style={{ position: "absolute", right: 10, top: "50%",
transform: "translateY(-50%)", pointerEvents: "none" }}
color="var(--icon-color)"
/>
</span>pointer-events: none 을 줘서 화살표가 클릭을 가로채지 않게 하면 select 가 정상으로 동작합니다.
data-uri SVG 에는 CSS 변수가 안 먹습니다
디자인 시스템이 색을 CSS 변수(var(--c-gray))로 관리한다면, 방법 A 의 data-uri stroke 에 그 변수를 넣어도 적용되지 않습니다.
/* 작동 안 함: data-uri 안의 var() 는 페이지 CSS 변수를 못 본다 */
background-image: url("data:image/svg+xml,<svg><path stroke='var(--c-gray)'/></svg>");data-uri 로 불러온 SVG 는 별도 문서(resource document)라서, 임베드한 페이지의 CSS 커스텀 프로퍼티를 상속하지 않습니다. currentColor 도 안 먹는 경우가 많습니다.
결국 data-uri 방식은 색을 하드코딩해야 하고, 다크모드에서 화살표만 안 바뀌는 버그로 이어집니다.
그래서 색을 토큰이나 다크모드에 묶고 싶다면 방법 B(실제 DOM 의 인라인 SVG 오버레이)를 씁니다. 실제 DOM 요소의 stroke 와 color 는 페이지 CSS 변수를 정상적으로 해석하거든요.
메모
폼 컨트롤 모양이 기기마다 다르면 십중팔구 native appearance 차이입니다. appearance: none 으로 지우고 직접 그리면 됩니다.
화살표 색을 디자인 토큰에 묶어야 하면 DOM 오버레이로, 그 오버레이에는 pointer-events: none 을 잊지 않는 것까지 한 세트입니다.