// FoxRadioIcon — 시안: 통합관리자페이지 디자인시스템(KE9UILWhB8qTBe14uXUn4F)
// Figma radio-icon
//
// 라디오의 동그란 표시만 담당한다. 라벨·부가설명까지 묶은 것은 `fox-radio`다.
//
// SVG를 쓰지 않고 CSS로 그린다. 시안이 바깥 원과 안쪽 점을 별개 레이어로 두었고 네 상태가
// 모양이 아니라 색만 다르므로, `border` + `border-radius`로 전부 표현된다. SVG로 가면
// 배경·테두리 두 색을 `currentColor` 하나로 몰 수 없고 원시 hex가 박힌다.
//
// 마크업 계약 (React 밖 소비자용):
//
//
//
//
// 안쪽 점은 **항상 마크업에 둔다.** 선택됐을 때만 넣는 게 아니라 CSS가 보이기/숨기기를
// 정한다 — 그래야 비제어 입력에서도(React가 선택 여부를 모르는 상태에서도) 모양이 맞는다.
// 바깥 원이 flex 컨테이너라 점은 저절로 가운데 놓인다.
//
// 상태를 거는 방법이 둘이다.
// - `fox-radio` 안에서는 네이티브 ``가 형제로 앞에 있고, 그 요소의
// `:checked`·`:disabled`가 그대로 스타일 훅이 된다(규약 3). 클래스를 붙일 필요가 없다.
// - 단독으로 쓸 때(예: 상태 비교 예제)는 `--checked`·`--disabled` 모디파이어로 고정한다.
//
// 네 상태 모두 테두리 두께는 `border-1`이다 — 선택은 테두리 색만, 비활성은 테두리·배경
// 색만 바꾼다. 비활성도 기본·선택 두 가지가 있고, 선택된 채 비활성이면 점이 남되 색이
// 흐려진다.
//
// ⚠️ 포커스 표시는 시안에 없다. 네이티브 input을 시각적으로 숨기므로 브라우저 기본 링도
// 사라져서, 키보드 사용자를 위해 이 파일에서 만들어 준다.
//
// 토큰이 아닌 값(사용자 승인):
// - md의 안쪽 점 `1rem`(10px): `form-icon-md` 지름의 절반이라 lg(`form-icon-lg` 지름에
// `icon-2` 점)과 같은 비율인데, icon 스케일이 `icon-1`에서 `icon-2`로 건너뛰어 그 값에
// 맞는 토큰이 없다.
// - box-sizing / display / flex: 구조 규칙.
@use "@fox/styles/abstracts" as fox;
.fox-radio-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(max);
background: fox.color(element-neutral-inverse);
// 안쪽 점. 크기는 크기 모디파이어가, 보이기는 선택 상태가 정한다.
&__dot {
display: none;
flex-shrink: 0;
border-radius: fox.radius(max);
background: fox.color(element-theme-primary);
}
// ── 크기 ────────────────────────────────────────────────────────────────────
// 시안이 바깥 지름에 `form-icon-*`을 건다 — 반응형 토큰이라 모바일에서 한 단계 작아진다.
&--lg {
inline-size: fox.form(icon-lg);
block-size: fox.form(icon-lg);
}
&--lg &__dot {
inline-size: fox.icon(2);
font-size: fox.icon(2);
block-size: fox.icon(2);
}
&--md {
inline-size: fox.form(icon-md);
block-size: fox.form(icon-md);
}
// 시안 10px — 지름의 1/2로 lg과 같은 비율인데, icon 스케일이 `icon-1`에서 `icon-2`로
// 건너뛰어 맞는 토큰이 없다(위 승인 목록 참고).
&--md &__dot {
inline-size: 1rem;
block-size: 1rem;
}
}
// ── 선택 ──────────────────────────────────────────────────────────────────────
// 두 선택자를 함께 쓴다 — `fox-radio` 안에서는 네이티브 `:checked`가, 단독으로 쓸 때는
// 모디파이어가 같은 모양을 만든다. 다른 블록(`fox-radio__input`)을 지목하는 것은
// `fox-button-panel`이 안쪽 `.fox-button`을 지목하는 것과 같은 방식이다.
.fox-radio-icon--checked,
.fox-radio__input:checked ~ .fox-radio-icon {
border: fox.border(2) solid fox.color(element-theme-primary);
}
.fox-radio-icon--checked .fox-radio-icon__dot,
.fox-radio__input:checked ~ .fox-radio-icon .fox-radio-icon__dot {
display: block;
}
// ── 비활성 ────────────────────────────────────────────────────────────────────
// 선택 규칙보다 뒤에 와야 이긴다 — 비활성이면 선택 여부와 무관하게 바깥 원 색이 같다.
.fox-radio-icon--disabled,
.fox-radio__input:disabled ~ .fox-radio-icon {
border: fox.border(2) solid fox.color(border-neutral-disabled);
background: fox.color(element-neutral-disabled);
}
// 점은 숨기지 않는다 — 선택된 채 비활성이어도 선택 여부가 보여야 한다. 색만 흐려진다.
// 보이기/숨기기는 위 선택 규칙이 정하므로 여기서는 색만 건드린다.
.fox-radio-icon--disabled .fox-radio-icon__dot,
.fox-radio__input:disabled ~ .fox-radio-icon .fox-radio-icon__dot {
background: fox.color(element-neutral-disabled-strong);
}
// ── 포커스 ────────────────────────────────────────────────────────────────────
// input이 시각적으로 숨겨져 브라우저 기본 링이 보이지 않으므로 여기서 대신 그린다.
// 투명이 아니라 실제 링인 이유: 선택 시 테두리도 같은 primary 색이라, 테두리 변화만으로는
// 포커스와 선택을 구분할 수 없다.
.fox-radio__input:focus-visible ~ .fox-radio-icon {
outline: fox.border(2) solid fox.color(border-theme-primary);
outline-offset: fox.border(1);
}