// FoxCheckIcon — 시안: 통합관리자페이지 디자인시스템(KE9UILWhB8qTBe14uXUn4F)
// Figma check-icon
//
// 체크박스의 네모 표시만 담당한다. 라벨·부가설명까지 묶은 것은 `fox-checkbox`다.
//
// 상자는 CSS로 그리고 안쪽 표시만 SVG다 — 상자는 테두리·배경·모서리로 끝나지만 체크는 임의의
// path라 CSS로 그릴 수 없다. 실무에서도 이 조합이 표준이다(`fox-radio-icon`은 안쪽이 원이라
// 전부 CSS였다).
//
// 마크업 계약 (React 밖 소비자용):
//
//
//
//
//
// 두 표시를 **항상 마크업에 둔다.** 켜졌을 때만 넣는 게 아니라 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);
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);
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);
}