# 컴포넌트 작성 규약 ## 파일 배치 컴포넌트 하나당 폴더 하나. **스타일은 여기에 두지 않는다** — `@fox/styles/_.scss`가 소유한다. ``` core/components/ fox-button/ fox-button.tsx 컴포넌트 (React) index.ts export { FoxButton } from "./fox-button"; index.ts 배럴 — export * from "./fox-button"; styles/ _fox-button.scss 모양 규칙 (프레임워크 무관) components.scss 전부 묶음 진입점 — 새 컴포넌트를 여기에 @use로 추가 ``` **스타일과 컴포넌트를 갈라 둔 이유**는 디자인이 React에 묶이면 안 되기 때문이다. SCSS만 쓰는 프로젝트(Vue·Svelte·서버 템플릿 등)도 `@use "@fox/styles/fox-button"` 하나로 같은 버튼을 얻어야 한다. ## 작성 규칙 **1. 클래스명은 `fox-` 접두사 + BEM.** ``` .fox-button 블록 .fox-button__label 엘리먼트 .fox-button--primary 모디파이어 ``` CSS Modules를 쓰지 않는다. 이름이 전역 유일해서 스코프가 필요 없고, 스코프가 없어야 React 밖에서도 같은 이름으로 쓸 수 있다. **충돌 방지는 접두사 규율이 담당한다.** **2. 모양 값은 전부 `fox.*()` 토큰으로 지목한다.** ```scss @use "abstracts" as fox; .fox-button { padding-inline: fox.form(padding-md); background: fox.color(button-primary-surface); border-radius: fox.form(radius-md); } ``` 원시 값(hex·px·rem 리터럴) 금지. 없는 토큰 이름은 빌드가 실패시킨다. 맞는 토큰이 없으면 쓰지 말고 Figma에 먼저 추가한다 — 토큰 파일은 자동 생성물이다. 토큰으로 표현할 수 없는 값(시안에 있으나 변수화되지 않은 것 등)이 나오면 **사용자와 상의한 뒤** 넣고, 파일 상단 주석에 근거를 남긴다. **3. 상태는 네이티브 속성으로.** `disabled`·`aria-busy` 같은 표준 속성을 쓰고 `.is-disabled` 같은 클래스를 만들지 않는다. SCSS만 쓰는 소비자도 별도 규칙 없이 같은 결과를 얻는다. **4. 마크업 계약을 스타일 파일 상단에 적는다.** React 밖 소비자는 그 주석만 보고 DOM을 짜야 한다. 필수 구조와 선택 요소를 예시로 남긴다. **5. 기본은 Server Component.** `'use client'`는 상호작용(상태·이벤트 핸들러)이 실제로 필요할 때만 붙인다. **6. variant는 `Record`로 고정한다.** ```tsx const TYPE_CLASS: Record = { primary: "fox-button--primary", secondary: "fox-button--secondary", }; ``` 계열을 추가하면 맵 누락이 타입 에러가 된다. **7. `className` prop은 배치용으로만 연다.** 호출부가 넘기는 것은 margin·grid 배치 같은 **위치** 조정이지 디자인 값이 아니다. 모양이 달라져야 하면 모디파이어를 추가한다. **8. 호스트 앱에 의존하지 않는다.** `@fox/` 안에서 `@/lib/...` 같은 앱 경로를 import 하지 않는다. 앱이 주는 값은 prop이나 CSS 변수로 받는다. **9. 만들면 테스트 페이지에 등록한다.** `@fox/dev-test/component-registry.tsx`의 `COMPONENT_EXAMPLES`에 항목 하나를 추가하면 `/dev-test/design`에 나타난다.