// FoxButton — 시안: 통합관리자페이지 디자인시스템(KE9UILWhB8qTBe14uXUn4F) Figma btn (컴포넌트 세트 106:1606 · 섹션 390:439) // // 마크업 계약 (React 밖 소비자용): // // 비활성은 네이티브 `disabled`, 로딩은 앞 아이콘 자리에 `fox-button__spinner` + `disabled`. // 그 안에 `fox-spinner`가 들어가며, 색은 `currentColor`라 계열별 글자색을 따라간다. // 아이콘 SVG는 `currentColor`로 그려야 계열별 색이 적용된다. // // 토큰이 아닌 값 3건(사용자 승인): // - line-height / letter-spacing: 시안 텍스트 스타일 label/*-strong의 값이나 Figma가 // 변수로 export하지 않는다. 네 크기가 같은 값이라 규칙 하나로 끝난다. // - box-sizing: 높이 고정 + secondary·default의 1px 테두리 때문에 필수. 없으면 계열별 // 바깥 높이가 2px 어긋난다. // - cursor / white-space / flex-shrink: 구조·상호작용 규칙. @use "abstracts" as fox; .fox-button { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; border: none; cursor: pointer; font-family: fox.font-family(body); font-weight: fox.font-weight(medium); line-height: 1; letter-spacing: -0.025em; white-space: nowrap; &:disabled { cursor: default; } // 라벨만 줄어들게 해 아이콘이 밀리지 않게 한다. &__label { min-inline-size: 0; } &__icon, &__spinner { display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; } &__icon > * { inline-size: 100%; block-size: 100%; } &--full-width { inline-size: 100%; } // ── 크기 (높이만 고정, 너비는 가변) ──────────────────────────────────────── &--lg { block-size: fox.form(height-lg); padding-inline: fox.form(padding-lg); border-radius: fox.form(radius-lg); gap: fox.gap(2); font-size: fox.font-size(label-lg); .fox-button__icon, .fox-button__spinner { inline-size: fox.form(icon-lg); block-size: fox.form(icon-lg); } } &--md { block-size: fox.form(height-md); padding-inline: fox.form(padding-md); border-radius: fox.form(radius-md); gap: fox.gap(2); font-size: fox.font-size(label-md); .fox-button__icon, .fox-button__spinner { inline-size: fox.form(icon-md); block-size: fox.form(icon-md); } } &--sm { block-size: fox.form(height-sm); padding-inline: fox.form(padding-sm); border-radius: fox.form(radius-sm); gap: fox.gap(2); font-size: fox.font-size(label-sm); .fox-button__icon, .fox-button__spinner { inline-size: fox.form(icon-sm); block-size: fox.form(icon-sm); } } // xsm만 gap이 한 단계 좁다. &--xsm { block-size: fox.form(height-xsm); padding-inline: fox.form(padding-xsm); border-radius: fox.form(radius-xsm); gap: fox.gap(1); font-size: fox.font-size(label-xsm); .fox-button__icon, .fox-button__spinner { inline-size: fox.form(icon-xsm); block-size: fox.form(icon-xsm); } } // ── 계열 ────────────────────────────────────────────────────────────────── // 아이콘 색은 시안이 라벨과 별개 토큰으로 지정한다(지금은 값이 같다). &--primary { background: fox.color(button-primary-surface); color: fox.color(button-primary-font); .fox-button__icon { color: fox.color(icon-neutral-static-inverse); } &:hover:not(:disabled) { background: fox.color(button-primary-surface-hover); } &:active:not(:disabled) { background: fox.color(button-primary-surface-pressed); } } &--secondary { border: fox.border(1) solid fox.color(button-secondary-border); background: fox.color(button-secondary-surface); color: fox.color(button-secondary-font); .fox-button__icon { color: fox.color(icon-theme-primary-strong); } &:hover:not(:disabled) { background: fox.color(button-secondary-surface-hover); } &:active:not(:disabled) { background: fox.color(button-secondary-surface-pressed); } } &--default { border: fox.border(1) solid fox.color(button-default-border); background: fox.color(button-default-surface); color: fox.color(button-default-font); .fox-button__icon { color: fox.color(icon-neutral-default); } &:hover:not(:disabled) { background: fox.color(button-default-surface-hover); } &:active:not(:disabled) { background: fox.color(button-default-surface-pressed); } } // ⚠️ error 계열은 시안 미정. &--error { } } // 계열과 무관하게 같고 테두리가 없다(시안에서 disabled-border 미사용). // 계열 모디파이어를 이기려면 특이도가 높아야 해 블록과 함께 지목한다. .fox-button:disabled { border: none; background: fox.color(button-disabled-surface); color: fox.color(button-disabled-font); .fox-button__icon { color: fox.color(icon-neutral-disabled-strong); } }