// FoxRadio — 시안: 통합관리자페이지 디자인시스템(KE9UILWhB8qTBe14uXUn4F) // Figma radio // // 라디오 버튼 한 칸이다 — 동그란 표시(`fox-radio-icon`) + 라벨 + 부가설명. // // 마크업 계약 (React 밖 소비자용): // // // // // 라벨 // 부가 설명 // // // // 루트가 ``이라 어디를 눌러도 브라우저가 선택을 처리한다. 상태는 전부 네이티브 // ``가 소유한다 — `name`이 같으면 그룹·화살표 이동·폼 전송까지 브라우저가 // 알아서 하므로 별도 group 컴포넌트가 없다(규약 3). // // input은 순서상 **맨 앞**에 와야 한다. 동그란 표시와 본문이 `~` 형제 선택자로 그 상태를 // 따라가기 때문이다. 시각적으로만 숨기고 포커스는 살려 둔다 — `display: none`으로 지우면 // 키보드로 도달할 수 없다. // // 크기 모디파이어는 안쪽 `fox-radio-icon`의 크기까지 맞춰 준다. 자식에 직접 크기 클래스를 // 붙이는 것은 React 래퍼가 자동화하지만, 순수 SCSS 소비자는 둘을 같은 단계로 적어야 한다. // // 토큰이 아닌 값(FoxButton과 동일한 근거로 승인된 항목): // - line-height / letter-spacing: 라벨·부가설명의 시안 텍스트 스타일 값이나 Figma가 변수로 // export하지 않는다. // // 토큰이 아닌 값(사용자 승인): // - 아이콘의 위쪽 여백 `0.1rem`(1px): 시안 icon-area의 `padding-top` 값이고 변수로 바인딩 // 되어 있지 않다. 크기·브레이크포인트가 바뀌어도 필요한 보정이 0.75~1.25px 범위라 하나로 // 쓴다(1rem = 10px). // - cursor / display / flex / position: 구조·상호작용 규칙. // - 숨긴 input의 `inline-size: 0`: 크기가 아니라 "보이지 않게" 하는 구조 값이다. @use "@fox/styles/abstracts" as fox; .fox-radio { // 숨긴 input의 위치 기준. 안 주면 input이 조상까지 올라가 엉뚱한 곳에 붙는다. position: relative; display: flex; // 부가설명이 여러 줄이어도 동그란 표시는 첫 줄에 붙어 있어야 한다. align-items: flex-start; gap: fox.gap(3); cursor: pointer; // 시각적으로만 숨긴다. `opacity: 0`은 포커스를 유지하므로 키보드로 도달할 수 있고, // 클릭은 루트 ``이 받는다. &__input { position: absolute; inline-size: 0; block-size: 0; margin: 0; opacity: 0; pointer-events: none; } // 시안이 아이콘을 감싼 icon-area에 위쪽 여백 1px을 준다. 라벨의 line box(글자 크기 × 1.5)가 // 아이콘보다 조금 높아서, 그만큼 내려야 글자 첫 줄과 시각 중심이 맞는다. // // 감싸는 요소를 따로 두지 않고 여백으로 처리한다 — 1px 내리는 것 말고는 하는 일이 없어 // React 밖 소비자에게 마크업을 하나 더 요구할 이유가 없다. .fox-radio-icon { margin-block-start: 0.1rem; } &__body { display: flex; flex-direction: column; gap: fox.gap(1); align-items: flex-start; } // 자간은 px이 아니라 em이다 — 라벨은 크기 수식어(lg·md)마다, 부가설명은 브레이크포인트마다 // 글자 크기가 달라지는데, px으로 박으면 그 자리에서 비율이 어긋난다. &__label { color: fox.color(font-neutral-strong); font-family: fox.font-family(body); font-weight: fox.font-weight(regular); line-height: 1.5; letter-spacing: -0.025em; } &__description { color: fox.color(font-neutral-subtle); font-family: fox.font-family(body); font-weight: fox.font-weight(regular); font-size: fox.font-size(body-sm); line-height: 1.5; letter-spacing: -0.025em; } // ── 크기 ──────────────────────────────────────────────────────────────────── &--lg &__label { font-size: fox.font-size(body-lg); } &--md &__label { font-size: fox.font-size(body-md); } } // ── 비활성 ──────────────────────────────────────────────────────────────────── // 글자색도 함께 흐려진다. 동그란 표시는 `_fox-radio-icon.scss`가 같은 `:disabled`를 보고 // 처리하므로 여기서 건드리지 않는다. .fox-radio:has(.fox-radio__input:disabled) { cursor: default; } .fox-radio__input:disabled ~ .fox-radio__body .fox-radio__label, .fox-radio__input:disabled ~ .fox-radio__body .fox-radio__description { color: fox.color(font-neutral-disabled); }