// FoxCheckIcon — 시안: 통합관리자페이지 디자인시스템(KE9UILWhB8qTBe14uXUn4F) // Figma check-icon // // 체크박스의 네모 표시만 담당한다. 라벨·부가설명까지 묶은 것은 `fox-checkbox`다. // // 상자는 CSS로 그리고 안쪽 표시만 SVG다 — 상자는 테두리·배경·모서리로 끝나지만 체크는 임의의 // path라 CSS로 그릴 수 없다. 실무에서도 이 조합이 표준이다(`fox-radio-icon`은 안쪽이 원이라 // 전부 CSS였다). // // 마크업 계약 (React 밖 소비자용): // // …svg… // …svg… // // // 두 표시를 **항상 마크업에 둔다.** 켜졌을 때만 넣는 게 아니라 CSS가 보이기/숨기기를 정한다 — // 그래야 비제어 입력에서도(React가 켜짐 여부를 모르는 상태에서도) 모양이 맞는다. // // 상태를 거는 방법이 둘이다. // - `fox-checkbox` 안에서는 네이티브 ``가 형제로 앞에 있고, 그 // 요소의 `:checked`·`:indeterminate`·`:disabled`가 그대로 스타일 훅이 된다(규약 3). // - 단독으로 쓸 때(예: 상태 비교 예제)는 `--checked`·`--indeterminate`·`--disabled` // 모디파이어로 고정한다. // // 선택과 부분선택은 상자 모양이 같다 — 시안이 두 상태에 같은 값을 준다. 안쪽 표시만 다르다. // 선택된 상자에는 시안이 테두리를 주지 않지만, 테두리를 없애는 대신 배경과 같은 색으로 둔다 — // 두께가 그대로라 상태가 바뀌어도 안쪽 표시 자리가 흔들리지 않는다. // // ⚠️ 포커스 표시는 시안에 없다. 네이티브 input을 시각적으로 숨기므로 브라우저 기본 링도 // 사라져서, 키보드 사용자를 위해 이 파일에서 만들어 준다(`fox-radio-icon`과 같은 처리). // // ⚠️ md의 안쪽 표시 크기는 시안에서 확인하지 못했다. lg의 비율(상자 `form-icon-lg` : 표시 // `icon-4`)을 그대로 적용해 `icon-3`으로 두었다. // // ⚠️ 비활성이면서 켜졌을 때 안쪽 표시의 색도 시안 미확인이다. `fox-radio-icon`이 같은 자리에 // 쓰는 `element-neutral-disabled-strong`으로 맞췄다. // // 토큰이 아닌 값(사용자 승인): // - box-sizing / display / flex: 구조 규칙. @use "@fox/styles/abstracts" as fox; .fox-check-icon { // 테두리가 바깥 크기를 늘리지 않게 한다 — 크기는 `form-icon-*`이 정한 값이어야 한다. box-sizing: border-box; display: flex; flex-shrink: 0; align-items: center; justify-content: center; border: fox.border(1) solid fox.color(element-neutral-strong); border-radius: fox.radius(2); background: fox.color(element-neutral-inverse); // 안쪽 표시 둘. 크기는 크기 모디파이어가, 보이기는 상태가 정한다. `currentColor`라 색은 // 상자 규칙이 정한 값을 그대로 물려받는다. &__check, &__dash { display: none; flex-shrink: 0; } // ── 크기 ──────────────────────────────────────────────────────────────────── // 시안이 바깥 크기에 `form-icon-*`을 건다 — 반응형 토큰이라 모바일에서 한 단계 작아진다. &--lg { inline-size: fox.form(icon-lg); block-size: fox.form(icon-lg); } &--lg &__check, &--lg &__dash { inline-size: fox.icon(4); font-size: fox.icon(4); block-size: fox.icon(4); } &--md { inline-size: fox.form(icon-md); block-size: fox.form(icon-md); } &--md &__check, &--md &__dash { inline-size: fox.icon(3); font-size: fox.icon(3); block-size: fox.icon(3); } } // ── 선택 · 부분선택 ─────────────────────────────────────────────────────────── // 상자는 둘이 같다. 두 선택자를 함께 쓴다 — `fox-checkbox` 안에서는 네이티브 상태가, // 단독으로 쓸 때는 모디파이어가 같은 모양을 만든다. .fox-check-icon--checked, .fox-check-icon--indeterminate, .fox-checkbox__input:checked ~ .fox-check-icon, .fox-checkbox__input:indeterminate ~ .fox-check-icon { border-color: fox.color(element-theme-primary); background: fox.color(element-theme-primary); // 채워진 상자 위에 얹히므로 안쪽 표시는 반전색이다. color: fox.color(element-neutral-inverse); } .fox-check-icon--checked .fox-check-icon__check, .fox-checkbox__input:checked ~ .fox-check-icon .fox-check-icon__check { display: block; } // 부분선택이 선택보다 뒤에 와야 이긴다 — DOM에서는 둘 다 켜져 있을 수 있는데, 그때 보여야 // 하는 건 부분선택이다. .fox-check-icon--indeterminate .fox-check-icon__check, .fox-checkbox__input:indeterminate ~ .fox-check-icon .fox-check-icon__check { display: none; } .fox-check-icon--indeterminate .fox-check-icon__dash, .fox-checkbox__input:indeterminate ~ .fox-check-icon .fox-check-icon__dash { display: block; } // ── 비활성 ──────────────────────────────────────────────────────────────────── // 선택 규칙보다 뒤에 와야 이긴다 — 비활성이면 켜짐 여부와 무관하게 상자 색이 같다. .fox-check-icon--disabled, .fox-checkbox__input:disabled ~ .fox-check-icon { border-color: fox.color(border-neutral-disabled); background: fox.color(element-neutral-disabled); } // 안쪽 표시는 숨기지 않는다 — 켜진 채 비활성이어도 켜짐 여부가 보여야 한다. 색만 흐려진다. .fox-check-icon--disabled, .fox-checkbox__input:disabled ~ .fox-check-icon { color: fox.color(element-neutral-disabled-strong); } // ── 포커스 ──────────────────────────────────────────────────────────────────── // input이 시각적으로 숨겨져 브라우저 기본 링이 보이지 않으므로 여기서 대신 그린다. .fox-checkbox__input:focus-visible ~ .fox-check-icon { outline: fox.border(2) solid fox.color(border-theme-primary); outline-offset: fox.border(1); }