// 디자인 시스템 카탈로그 스타일. // // 토큰 목록은 이 파일이 아니라 **CSSOM에서 런타임에 읽는다**(`_components/token-catalog.tsx`). // SCSS 값을 JS로 내보내는 CSS Modules의 `:export`를 처음 썼으나, Next.js 16의 기본 // 번들러인 Turbopack은 Lightning CSS로 CSS를 파싱해 `:export`(webpack css-loader 시절의 // ICSS 확장)를 인식하지 못한다 — 빌드는 통과하지만 규칙이 통째로 버려져 값이 `undefined`가 // 된다. `--fox-*` 커스텀 프로퍼티를 CSSOM에서 직접 열거하면 같은 목적을 번들러 의존 없이 // 달성한다. @use "@fox/styles/abstracts" as fox; .page { flex: 1; padding: fox.spacing(top-lg) fox.grid(margin-default) fox.spacing(bottom-xlg); } .header { display: flex; flex-wrap: wrap; gap: fox.gap(5); align-items: baseline; justify-content: space-between; padding-block-end: fox.padding(7); border-block-end: fox.border(1) solid fox.color(border-neutral-default); } .title { font-size: fox.font-size(heading-lg); font-weight: fox.font-weight(bold); } .lede { margin-block-start: fox.gap(2); color: fox.color(font-neutral-subtle); font-size: fox.font-size(body-md); } // ── 테마 전환 ─────────────────────────────────────────────────────────────── .themeSwitch { display: flex; gap: fox.gap(1); padding: fox.padding(1); border: fox.border(1) solid fox.color(border-neutral-default); border-radius: fox.radius(max); background: fox.color(surface-neutral-gray); } .themeButton { padding: fox.padding(3) fox.padding(6); border-radius: fox.radius(max); color: fox.color(font-neutral-subtle); font-size: fox.font-size(label-sm); font-weight: fox.font-weight(medium); &[aria-pressed="true"] { background: fox.color(button-primary-surface); color: fox.color(button-primary-font); } &:focus-visible { outline: fox.border(2) solid fox.color(border-theme-primary); outline-offset: fox.border(2); } } // ── 섹션 ──────────────────────────────────────────────────────────────────── .section { padding-block: fox.section(spacing-xsm); border-block-end: fox.border(1) solid fox.color(border-neutral-subtle); } .sectionTitle { margin-block-end: fox.gap(1); font-size: fox.font-size(heading-sm); font-weight: fox.font-weight(bold); } .sectionNote { margin-block-end: fox.gap(5); color: fox.color(font-neutral-subtle); font-size: fox.font-size(body-sm); } .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(fox.grid(wrap-xsm), 1fr)); gap: fox.gap(4); @include fox.pc { grid-template-columns: repeat(auto-fill, minmax(24rem, 1fr)); } } // ── 색상 스와치 ───────────────────────────────────────────────────────────── .swatch { overflow: hidden; border: fox.border(1) solid fox.color(border-neutral-subtle); border-radius: fox.radius(3); background: fox.color(surface-neutral-default); } .swatchChip { block-size: fox.icon(7); border-block-end: fox.border(1) solid fox.color(border-neutral-overlay); } .swatchMeta { padding: fox.padding(5); } .name { display: block; color: fox.color(font-neutral-default); font-size: fox.font-size(label-sm); font-weight: fox.font-weight(medium); word-break: break-all; } .ref { display: block; margin-block-start: fox.gap(1); color: fox.color(font-neutral-subtler); font-size: fox.font-size(body-xsm); word-break: break-all; } // ── 값 테이블 ─────────────────────────────────────────────────────────────── .table { inline-size: 100%; border-collapse: collapse; text-align: start; } .table th, .table td { padding: fox.padding(3) fox.padding(5); border-block-end: fox.border(1) solid fox.color(border-neutral-subtle); } .table th { color: fox.color(font-neutral-subtle); font-size: fox.font-size(label-xsm); font-weight: fox.font-weight(medium); } .table td { color: fox.color(font-neutral-default); font-size: fox.font-size(body-sm); } .tdValue { color: fox.color(font-neutral-subtle); } // 크기 토큰을 눈으로 비교하기 위한 막대. 폭은 인라인 style이 토큰을 참조해 넣는다. .bar { display: block; block-size: fox.icon(2); border-radius: fox.radius(1); background: fox.color(surface-theme-primary); }