체크박스

Checkbox

여러 항목 중 몇 개든 자유롭게 고를 수 있는 사각형 선택 컨트롤. 하위가 일부만 선택되면 "부분 선택(-)" 상태도 가집니다.

다른 이름: Tick boxSelection box
···
html
<div class="cw">
  <label class="cbl parent"><input type="checkbox" id="pAll"><span class="box"><svg viewBox="0 0 16 16"><path d="M3 8l3.5 3.5L13 4.5"/></svg></span>전체 선택</label>
  <div class="children">
    <label class="cbl"><input type="checkbox" class="child" checked><span class="box"><svg viewBox="0 0 16 16"><path d="M3 8l3.5 3.5L13 4.5"/></svg></span>디자인</label>
    <label class="cbl"><input type="checkbox" class="child"><span class="box"><svg viewBox="0 0 16 16"><path d="M3 8l3.5 3.5L13 4.5"/></svg></span>개발</label>
    <label class="cbl"><input type="checkbox" class="child"><span class="box"><svg viewBox="0 0 16 16"><path d="M3 8l3.5 3.5L13 4.5"/></svg></span>마케팅</label>
  </div>
</div>
css
.cw{width:min(200px,88%);font-size:13px;color:var(--fg)}
.cbl{display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 0}
.parent{font-weight:700;border-bottom:1px solid var(--line);margin-bottom:6px;padding-bottom:8px}
.children{display:grid;gap:2px;padding-left:6px}
.cbl input{position:absolute;opacity:0;width:1px;height:1px}
.box{width:18px;height:18px;border-radius:5px;border:1.5px solid var(--line);display:grid;place-items:center;flex-shrink:0;background:var(--surface);transition:background .15s,border-color .15s}
.box svg{width:12px;height:12px;fill:none;stroke:#fff;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:20;stroke-dashoffset:20;transition:stroke-dashoffset .15s}
input:checked + .box{background:var(--accent);border-color:var(--accent)}
input:checked + .box svg{stroke-dashoffset:0}
input:indeterminate + .box{background:var(--accent);border-color:var(--accent)}
input:indeterminate + .box::after{content:"";width:9px;height:2px;background:#fff;border-radius:1px}
js
const parent = document.getElementById('pAll');
const kids = [...document.querySelectorAll('.child')];
function syncParent() {
  const n = kids.filter(k => k.checked).length;
  parent.checked = n === kids.length;
  parent.indeterminate = n > 0 && n < kids.length;
}
syncParent();
let auto = true;
[parent, ...kids].forEach((el) => el.addEventListener('click', () => { auto = false; if (el === parent) kids.forEach(k => k.checked = parent.checked); syncParent(); }));

let i = 0;
setInterval(() => {
  if (!auto) return;
  kids[i % kids.length].checked = !kids[i % kids.length].checked;
  syncParent();
  i++;
}, 1000);

네이티브 <input type="checkbox">를 쓰면 키보드·폼 제출·스크린리더 지원을 공짜로 얻습니다. 부분 선택은 HTML 속성이 아니라 JS로 element.indeterminate = true를 설정해야 하는 유일한 상태입니다 — 상위 항목 체크박스가 자식 중 일부만 체크됐을 때 대시(-) 모양으로 보여줍니다.

라디오 버튼과의 차이는 개수 제약입니다. 체크박스는 0개부터 전부까지 자유롭게 고르고, 라디오 버튼은 같은 그룹에서 정확히 하나만 고를 수 있습니다. "둘 다 좋아요"가 가능하면 체크박스, "하나만 골라야 한다"면 라디오입니다.

레이블은 반드시 <label for="">로 체크박스와 연결해서 레이블 텍스트를 눌러도 토글되게 합니다.