세그먼티드 컨트롤

Segmented control

알약 모양 배경이 선택된 항목 뒤로 미끄러지는, 라디오처럼 정확히 하나만 고르는 버튼 묶음.

다른 이름: Button groupToggle button groupiOS segmented control
···
html
<div class="seg" role="radiogroup" aria-label="기간">
  <i class="pill"></i>
  <button role="radio" aria-checked="true">일간</button>
  <button role="radio" aria-checked="false">주간</button>
  <button role="radio" aria-checked="false">월간</button>
</div>
css
.seg{position:relative;display:flex;width:min(220px,88%);background:var(--bg);border:1px solid var(--line);border-radius:11px;padding:3px}
.seg button{flex:1;position:relative;z-index:1;border:0;background:none;padding:8px 0;font-size:12px;font-weight:600;color:var(--muted);cursor:pointer}
.seg button[aria-checked=true]{color:#fff}
.pill{position:absolute;top:3px;left:3px;width:calc(100%/3 - 2px);height:calc(100% - 6px);background:var(--accent);border-radius:8px;transition:transform .25s cubic-bezier(.3,.8,.4,1)}
js
const seg = document.querySelector('.seg');
const btns = [...seg.querySelectorAll('button')];
const pill = seg.querySelector('.pill');
function select(i) {
  btns.forEach((b, idx) => b.setAttribute('aria-checked', String(idx === i)));
  pill.style.transform = 'translateX(' + (i * 100) + '%)';
}
let auto = true, cur = 0;
btns.forEach((b, i) => b.addEventListener('click', () => { auto = false; cur = i; select(i); }));
setInterval(() => { if (!auto) return; cur = (cur + 1) % btns.length; select(cur); }, 1600);

기능은 라디오 버튼과 완전히 같습니다 — 그룹 안에서 정확히 하나만 선택됩니다. role="radiogroup"으로 감싸고 각 버튼에 role="radio"와 aria-checked를 쓰거나, 네이티브 radio input을 시각적으로 버튼처럼 감추고 스타일링합니다.

탭과의 차이는 처음에 다룬 대로입니다: 탭은 콘텐츠 영역을 완전히 바꾸는 내비게이션처럼 보이고 컨테이너 위쪽에 붙지만, 세그먼티드 컨트롤은 버튼 그룹처럼 독립적으로 떠 있고 "일간/주간/월간"처럼 같은 화면 안의 보기 상태를 바꿀 때 씁니다.

선택된 배경이 옆 세그먼트로 미끄러지는 애니메이션은 transform: translateX만으로 충분하고, 세그먼트 개수가 바뀌면 각 세그먼트 너비(100% / N)도 같이 계산해야 합니다.