// 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); }