// FoxPhoneNumber — 시안: 통합관리자페이지 디자인시스템(KE9UILWhB8qTBe14uXUn4F) // Figma form-phonenumber (섹션 391:3156) // // 두 모양이 있다. // - unit(136:2926): 상자 셋을 하이픈으로 잇는다. 앞자리는 선택, 뒤 둘은 입력이며 // 시안에서도 select·input 컴포넌트의 인스턴스라 그대로 조합한다. 그래서 이 파일은 // 줄 배치와 하이픈만 갖고, 상자 모양·상태는 FoxSelect·FoxInput이 가진 것을 쓴다. // - combine(190:1772): 상자 하나 안에 세 칸을 두고 각 칸을 가운데 정렬한다. 이건 // 시안이 별도 상자라 여기서 직접 그린다. // // 마크업 계약 (React 밖 소비자용): //
// 레이블 //
//
…select…
// - //
…input…
// - //
…input…
//
//
// // - // … //
//

…

//
// // 상태는 FoxInput과 같은 두 갈래다(자동 / `data-state` 고정). combine 상자에만 규칙이 // 필요하다 — unit은 조각이 알아서 따라온다. // // 시안에 크기 축이 없다. 상자는 md 하나뿐이다. // // 토큰이 아닌 값(FoxInput과 동일한 근거로 승인된 항목): // - line-height / letter-spacing: 시안 텍스트 스타일의 값이나 Figma가 변수로 // export하지 않는다. // - box-sizing / flex / text-align: 구조 규칙. @use "abstracts" as fox; .fox-phone-number { display: flex; flex-direction: column; gap: fox.gap(3); inline-size: 100%; &__label { color: fox.color(font-neutral-default); font-family: fox.font-family(body); font-size: fox.font-size(label-md); font-weight: fox.font-weight(medium); line-height: 1; letter-spacing: -0.025em; } // ── unit ────────────────────────────────────────────────────────────────── &__row { display: flex; align-items: center; gap: fox.gap(2); inline-size: 100%; } // 세 조각이 남는 폭을 똑같이 나눈다. &__part { flex: 1 0 0; min-inline-size: 0; } &__separator { flex-shrink: 0; color: fox.color(font-neutral-default); font-family: fox.font-family(body); font-size: fox.font-size(body-md); font-weight: fox.font-weight(regular); line-height: 1.5; letter-spacing: -0.025em; } // ── combine ─────────────────────────────────────────────────────────────── &__box { box-sizing: border-box; display: flex; align-items: center; gap: fox.gap(2); inline-size: 100%; block-size: fox.form(height-md); padding-inline: fox.form(padding-md); border: fox.border(1) solid fox.color(input-border); border-radius: fox.form(radius-md); background: fox.color(input-surface); } &__segment { flex: 1 0 0; min-inline-size: 0; margin: 0; padding: 0; border: none; background: none; color: fox.color(font-neutral-default); font-family: fox.font-family(body); font-size: fox.font-size(label-md); font-weight: fox.font-weight(regular); line-height: 1; letter-spacing: -0.025em; text-align: center; &::placeholder { color: fox.color(font-neutral-subtler); } // 포커스는 상자 테두리가 표시하므로 기본 외곽선을 지운다. &:focus { outline: none; } &:disabled { color: fox.color(font-neutral-disabled-strong); cursor: default; } } &__message { display: flex; gap: fox.number(3); align-items: flex-start; margin: 0; color: fox.color(font-neutral-subtle); font-family: fox.font-family(body); font-size: fox.font-size(body-sm); font-weight: fox.font-weight(regular); line-height: 1.5; letter-spacing: -0.025em; } &__message-icon { display: inline-flex; flex-shrink: 0; align-items: flex-start; padding-block-start: fox.padding(1); > * { inline-size: fox.icon(3); block-size: fox.icon(3); } } } // ── 상태 · 자동 (combine 상자) ────────────────────────────────────────────── // 뒤에 오는 규칙이 이긴다: 포커스 < 오류 < 읽기전용 < 비활성. .fox-phone-number:not([data-state]) { .fox-phone-number__box:focus-within { border-width: fox.border(2); border-color: fox.color(input-border-active); } .fox-phone-number__box:has(.fox-phone-number__segment[aria-invalid="true"]) { border-width: fox.border(2); border-color: fox.color(input-border-error); } // 비활성도 `:read-only`에 걸리므로 떼어낸다. .fox-phone-number__box:has(.fox-phone-number__segment:read-only:not(:disabled)) { border-width: fox.border(1); border-color: fox.color(input-border-disabled); background: fox.color(input-surface-disabled); } .fox-phone-number__box:has(.fox-phone-number__segment:disabled) { border-width: fox.border(1); border-color: fox.color(input-border-disabled); background: fox.color(input-surface-disabled); } &:focus-within .fox-phone-number__message { color: fox.color(font-system-information); } &:has([aria-invalid="true"]) .fox-phone-number__message { color: fox.color(font-system-danger); } } // ── 상태 · 고정 ───────────────────────────────────────────────────────────── .fox-phone-number[data-state="default"] .fox-phone-number__box, .fox-phone-number[data-state="completed"] .fox-phone-number__box { border-width: fox.border(1); border-color: fox.color(input-border); background: fox.color(input-surface); } .fox-phone-number[data-state="focused"] .fox-phone-number__box { border-width: fox.border(2); border-color: fox.color(input-border-active); background: fox.color(input-surface); } .fox-phone-number[data-state="error"] .fox-phone-number__box { border-width: fox.border(2); border-color: fox.color(input-border-error); background: fox.color(input-surface); } .fox-phone-number[data-state="view"] .fox-phone-number__box, .fox-phone-number[data-state="disabled"] .fox-phone-number__box { border-width: fox.border(1); border-color: fox.color(input-border-disabled); background: fox.color(input-surface-disabled); } .fox-phone-number[data-state="completed"] .fox-phone-number__segment::placeholder, .fox-phone-number[data-state="focused"] .fox-phone-number__segment::placeholder, .fox-phone-number[data-state="error"] .fox-phone-number__segment::placeholder, .fox-phone-number[data-state="view"] .fox-phone-number__segment::placeholder { color: fox.color(font-neutral-default); } .fox-phone-number[data-state="disabled"] .fox-phone-number__segment, .fox-phone-number[data-state="disabled"] .fox-phone-number__segment::placeholder { color: fox.color(font-neutral-disabled-strong); } .fox-phone-number[data-state="focused"] .fox-phone-number__message { color: fox.color(font-system-information); } .fox-phone-number[data-state="completed"] .fox-phone-number__message { color: fox.color(font-system-success); } .fox-phone-number[data-state="error"] .fox-phone-number__message { color: fox.color(font-system-danger); }