// FoxButton 스타일 — 시안: 관리자페이지 Figma, node 390:439 (`btn`) // // **프레임워크 무관.** 클래스명이 `fox-` 접두사 + BEM이라 전역에 그대로 풀어도 충돌하지 // 않는다. React 없이 SCSS만 쓰는 프로젝트도 이 파일 하나만 `@use`하고 아래 마크업 계약을 // 지키면 동일한 버튼을 얻는다. // // // // - 비활성은 네이티브 `disabled` 속성으로 표현한다(별도 모디파이어 없음). // - 로딩은 앞쪽 아이콘 자리에 `fox-button__spinner`를 두고 `disabled`를 함께 건다. // - 아이콘 SVG는 `currentColor`로 그려야 계열별 색이 적용된다. 크기는 슬롯이 정한다. // // **모든 디자인 값은 토큰으로만 지목한다.** 시안의 Figma 변수가 우리 토큰과 1:1로 // 대응하므로 예외가 필요 없었고, 아래 세 가지만 토큰이 아니다(사용자 승인 후 사용): // // 1. `line-height: 1` / `letter-spacing: -0.025em` // 시안 텍스트 스타일 `label/*-strong`의 값이다(lineHeight 1, letterSpacing -2.5%). // Figma에서 텍스트 스타일로만 존재하고 변수로 export되지 않아 토큰이 없다. // 네 크기가 모두 같은 값이라 스케일이 아니고 규칙 하나로 끝난다. // 2. `box-sizing: border-box` // 높이가 고정인데 secondary·default가 1px 테두리를 가져, 없으면 계열별로 바깥 // 높이가 2px 어긋난다. 디자인 값이 아니라 레이아웃 메커니즘이다. // 3. `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; // 시안 label/*-strong. 크기별 font-size는 아래 크기 모디파이어가 정한다. 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%; } // ── 크기 ────────────────────────────────────────────────────────────────── // 높이만 고정이고 너비는 내용에 따라 가변이다(시안도 좌우 padding만 지정). &--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` 블록과 함께 지목한다. .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); } }