@spectrabrain/eds 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/AI.md +13 -63
  2. package/AI_CONTEXT.json +26 -95
  3. package/CHANGELOG.md +66 -4
  4. package/CONSUMER_AGENT.md +86 -164
  5. package/FRONTEND_CONVENTIONS.md +252 -0
  6. package/QUICKSTART.md +24 -29
  7. package/README.md +24 -73
  8. package/TAILWIND_V4.md +49 -184
  9. package/TOKEN_CONTEXT.md +63 -36
  10. package/consumer/align/templates/components.json +21 -0
  11. package/consumer/align/templates/env.mock +1 -0
  12. package/consumer/align/templates/eslint.config.js +35 -0
  13. package/consumer/align/templates/package.scripts.json +10 -0
  14. package/consumer/align/templates/prettierignore +2 -0
  15. package/consumer/align/templates/prettierrc.json +7 -0
  16. package/consumer/align/templates/src/app/App.tsx +16 -0
  17. package/consumer/align/templates/src/app/main.tsx +27 -0
  18. package/consumer/align/templates/src/app/providers/ErrorBoundary.tsx +58 -0
  19. package/consumer/align/templates/src/app/providers/ErrorHandler.tsx +29 -0
  20. package/consumer/align/templates/src/app/router/index.tsx +23 -0
  21. package/consumer/align/templates/src/app/styles/index.css +2 -0
  22. package/consumer/align/templates/src/mocks/browser.ts +5 -0
  23. package/consumer/align/templates/src/mocks/handlers.ts +4 -0
  24. package/consumer/align/templates/src/shared/api/index.ts +23 -0
  25. package/consumer/align/templates/src/shared/api/queryClient.ts +24 -0
  26. package/consumer/align/templates/src/shared/lib/eventBus.ts +44 -0
  27. package/consumer/align/templates/src/shared/lib/utils/index.ts +6 -0
  28. package/consumer/align/templates/src/vite-env.d.ts +2 -0
  29. package/consumer/align/templates/tsconfig.json +22 -0
  30. package/consumer/align/templates/vite.config.babel.ts +36 -0
  31. package/consumer/align/templates/vite.config.rolldown.ts +35 -0
  32. package/consumer/align-analysis.mjs +1159 -0
  33. package/consumer/align-plan.mjs +377 -0
  34. package/consumer/align-rules.mjs +473 -0
  35. package/consumer/align.mjs +401 -0
  36. package/consumer/cli.mjs +90 -40
  37. package/consumer/css-root.mjs +24 -4
  38. package/consumer/doctor.mjs +47 -21
  39. package/consumer/init.mjs +39 -25
  40. package/consumer/managed-block.mjs +11 -5
  41. package/consumer/paths.mjs +69 -14
  42. package/consumer/register-scripts.mjs +17 -12
  43. package/consumer/scan-classes.mjs +4 -3
  44. package/consumer/sync.mjs +35 -13
  45. package/consumer/update-agent-context.mjs +63 -7
  46. package/consumer-postinstall.mjs +8 -7
  47. package/dist/components/Button/README.md +66 -95
  48. package/dist/components/Button/spec.json +19 -138
  49. package/dist/components.css +102 -366
  50. package/dist/index.d.mts +20 -18
  51. package/dist/index.d.ts +20 -18
  52. package/dist/tokens.d.mts +3 -0
  53. package/dist/tokens.d.ts +3 -0
  54. package/llms.txt +20 -14
  55. package/package.json +3 -1
@@ -0,0 +1,252 @@
1
+ # EVA 프론트엔드 컨벤션
2
+
3
+ EVA 서비스의 Vite + React 프론트엔드가 따르는 구조·라이브러리·코드 패턴·개발 규칙입니다. `eds align`은 프로토타입을 이 문서에 맞추는 일회성 계획(`.eds/align/PLAN.md`)을 만들고, `eds align --check`가 이 문서의 규칙 중 기계로 판정할 수 있는 부분을 점검합니다. EDS 컴포넌트와 token 규칙은 같은 package root의 `QUICKSTART.md`가 기준입니다.
4
+
5
+ `.eds/align/PLAN.md`를 따르는 중이면 전체를 읽습니다. 그 밖에는 파일을 만들거나 옮길 때 「FSD 계층」, service·hook·store·component·page를 쓸 때 「코드 패턴」의 해당 절, 화면 문구를 쓸 때 「UI 문구」, 의존성을 추가하거나 바꿀 때 「라이브러리 스택」, 완료 전에 「점검」을 읽습니다.
6
+
7
+ ## 적용 범위
8
+
9
+ - 대상은 Vite로 빌드하는 React 단일 페이지 앱의 프론트엔드입니다. Next.js·Remix 같은 framework와 React Native는 대상이 아닙니다.
10
+ - 이 문서는 프론트엔드만 다루며 backend 코드, API 계약(요청 URL·method·header·payload, 응답 해석, 인증 흐름), 배포 설정, backend가 읽는 환경 변수 이름은 정하지 않습니다. 정렬 중 이들을 지키는 규칙은 `.eds/align/PLAN.md` 「보호 경로」에 있습니다.
11
+ - 테스트 도구, 테스트 파일의 위치와 이름, 테스트 설정은 이 문서가 정하지 않습니다. 프로토타입마다 정책이 바뀔 수 있으므로 새로 쓰는 테스트는 저장소의 기존 방식을 따릅니다. `eds align`은 테스트 파일과 도구를 제자리에 두고, 소스를 옮겨 깨진 import 경로만 고치게 합니다.
12
+ - 사용자의 명시적 요청이 이 문서보다 우선합니다. 이 문서에 없는 결정은 가장 가까운 기존 코드를 따릅니다.
13
+
14
+ ## 라이브러리 스택
15
+
16
+ 범주마다 표준 하나를 씁니다. 교체 대상은 프로토타입에서 자주 보이는 예시이며, 전체 목록은 `eds align`이 감지합니다.
17
+
18
+ | 범주 | 표준 | 교체 대상 예시 | 적용 방법 |
19
+ | --- | --- | --- | --- |
20
+ | routing | `react-router-dom` | `wouter`, `@tanstack/react-router` | `app/main.tsx`의 `BrowserRouter`와 `app/router/index.tsx`의 `Routes`·`Route`를 씁니다. page는 `lazy()`로 불러옵니다. |
21
+ | server-state | `@tanstack/react-query` 5 | `swr`, `react-query` 3 | entity의 query·mutation hook에서만 씁니다. |
22
+ | http | `axios` | `ky`, `ofetch`, 직접 호출하는 `fetch` | `shared/api`의 instance 하나를 entity service가 씁니다. |
23
+ | client-state | `zustand` 5 | `redux`, `@reduxjs/toolkit`, `jotai`, `recoil`, `mobx` | `entities/<domain>/stores`에 둡니다. 저장이 필요하면 `persist`와 `partialize`를 씁니다. |
24
+ | form | `useState` 또는 Context + `useReducer` | `react-hook-form`, `formik` | 단순한 form은 `useState`, 여러 단계 form은 feature의 `lib/<Name>FormContext.tsx`에 Context + `useReducer`를 둡니다. |
25
+ | validation | TypeScript 타입과 검증 함수 | `zod`, `yup`, `valibot` | 검증 함수는 화면에 표시할 오류 문구를 돌려줍니다. backend 코드가 import하는 validation 라이브러리는 패키지를 유지하고, 프론트엔드 코드만 검증 함수로 옮깁니다. |
26
+ | styling | Tailwind CSS 4 + EDS token | `styled-components`, `@emotion/*`, `sass`, CSS Modules | 스타일은 `className`으로 쓰고 전역 CSS는 `app/styles/index.css` 하나에 둡니다. |
27
+ | ui-kit | EDS `Button` + shadcn/ui(Radix) | `@mui/material`, `@chakra-ui/react`, `antd`, `@mantine/core` | 버튼은 EDS `Button`, 그 밖의 UI 요소는 `shared/ui`의 shadcn/ui 컴포넌트를 씁니다. |
28
+ | icon | `lucide-react` | `react-icons`, `@heroicons/react`, `@fortawesome/*` | EDS `Button`의 아이콘은 `dist/components/Button/README.md` 「Layout과 아이콘」의 슬롯 규칙을 따릅니다. `lucide-react`는 EDS package root `package.json`의 `dependencies["lucide-react"]`와 같은 버전으로 선언합니다. |
29
+ | toast | `sonner` | `react-toastify`, `react-hot-toast` | `app/App.tsx`에 `<Toaster position="top-right" richColors />`를 한 번 둡니다. |
30
+ | date | `date-fns` | `moment`, `dayjs`, `luxon` | 달력 UI는 `react-day-picker`를 씁니다. |
31
+ | virtual-list | `react-virtuoso` | `react-window`, `react-virtualized` | 긴 목록에만 씁니다. |
32
+ | api-mock | `msw` + `@faker-js/faker` | `miragejs` | `src/mocks`에 domain별 handler를 둡니다. |
33
+ | lint-format | ESLint 9 flat config + Prettier 3 | `@biomejs/biome`, `oxlint`, `.eslintrc*`(legacy 설정) | 설정은 「개발 규칙」을 따릅니다. |
34
+ | build-plugin | `@vitejs/plugin-react` + React Compiler | `@vitejs/plugin-react-swc` | Vite 5–7은 `@vitejs/plugin-react`의 `babel` 옵션에 `babel-plugin-react-compiler`를 넣고, Vite 8 이상은 `@vitejs/plugin-react` 6이 export하는 `reactCompilerPreset()`을 `@rolldown/plugin-babel`의 `presets`로 실행합니다. Tailwind는 `@tailwindcss/vite`로 실행합니다. |
35
+
36
+ - 버전 기준은 React 19입니다.
37
+ - i18n 라이브러리는 당분간 새로 추가하지 않습니다. 저장소가 이미 쓰는 i18n 라이브러리와 문구 작성 규칙은 「UI 문구」를 따릅니다.
38
+ - Supabase·Firebase·tRPC·GraphQL client·인증·결제 SDK처럼 backend 계약을 정하는 client SDK는 교체하지 않습니다. SDK client 생성과 설정은 `shared/api`에 한 번만 두고, entity service만 그 client를 import합니다. 데이터를 SDK로만 주고받고 `fetch()`·axios 호출이 없으면 axios는 쓰지 않습니다.
39
+
40
+ ## FSD 계층
41
+
42
+ `src/`는 Feature-Sliced Design 계층으로 나누고, 소스는 `.ts`·`.tsx`로 씁니다(테스트 파일 제외). import 방향은 「import」를 따릅니다.
43
+
44
+ ```text
45
+ src/
46
+ ├─ app/ main.tsx · App.tsx · router/index.tsx · providers/ · styles/index.css
47
+ ├─ pages/ <domain>/<action>/page.tsx · <name>/page.tsx
48
+ ├─ widgets/ <name>/ 화면 구획(layout, header)
49
+ ├─ features/ <domain>/<feature>/{ui,lib,types} 사용자 행동 · <domain>/{ui,lib,types} domain 안 여러 feature가 공유
50
+ ├─ entities/ <domain>/{components,hooks,services,stores,types,lib} 도메인 데이터
51
+ ├─ shared/ {api,assets,hooks,lib,ui} 도메인을 모르는 코드(api/index.ts · api/queryClient.ts · lib/utils/index.ts)
52
+ ├─ mocks/ <domain>/index.ts · handlers.ts · browser.ts
53
+ ├─ types/ 전역 선언 파일(`*.d.ts`)
54
+ └─ styles/ eds-tokens.css(eds sync가 package root의 src/styles/에 생성합니다. 옮기거나 편집하지 않습니다)
55
+ ```
56
+
57
+ `app/`의 `main.tsx`·`App.tsx`·`router/index.tsx`·`styles/index.css`와 `shared/`의 괄호 안 파일은 `eds align --check`가 필수로 확인합니다.
58
+
59
+ 컴포넌트는 다음 순서로 첫 번째로 해당하는 곳에 둡니다.
60
+
61
+ 1. route 하나를 그리면 `pages/<domain>/<action>/page.tsx`입니다. backend resource에 속하지 않는 화면(home, login, not-found 등)은 `pages/<name>/page.tsx`입니다.
62
+ 2. 두 개 이상의 page가 쓰거나 여러 feature를 묶은 layout·header 같은 화면 구획이면 `widgets/<name>/`입니다.
63
+ 3. 생성·수정·삭제·제출 같은 사용자 행동을 처리하면 `features/<domain>/<feature>/ui/`입니다. 같은 domain의 여러 feature가 함께 쓰면 `features/<domain>/{ui,lib,types}`입니다.
64
+ 4. 한 도메인의 데이터를 보여 주면 `entities/<domain>/components/`입니다.
65
+ 5. 도메인을 모르는 범용 UI면 `shared/ui/`입니다.
66
+
67
+ ### 이름
68
+
69
+ - domain은 backend resource 이름을 소문자 복수형으로 씁니다(예: `accounts`, `device-groups`). backend resource가 없는 client 상태는 `auth`·`ui`처럼 앱 영역 이름을 단수로 씁니다. feature는 kebab-case 동사구입니다(예: `edit-device`).
70
+ - 컴포넌트 파일은 PascalCase(`LoginForm.tsx`), hook은 `useXxx.ts`, service는 `<domain>Service.ts`, store는 `<name>Store.ts`입니다. 타입은 slice의 `types/` segment(`entities/<domain>/types/`, `features/<domain>/<feature>/types/`)에 `api.ts`·`enums.ts`·`store.ts`로 둡니다. `src/types/`에는 전역 선언 파일(`*.d.ts`)만 둡니다.
71
+ - `shared/ui`는 shadcn/ui 규칙대로 kebab-case 파일(`dropdown-menu.tsx`)을 쓰고, cva variant는 `<name>.variants.ts`로 분리합니다.
72
+ - feature 루트와 `entities/<domain>/components`, `shared/ui`에는 `export *` barrel(`index.ts`)을 둡니다. 다른 slice가 barrel로만 import해야 하는지는 `미결`입니다.
73
+
74
+ ### import
75
+
76
+ - `app` → `pages` → `widgets` → `features` → `entities` → `shared` 방향으로만 import합니다.
77
+ - `pages/<domain>`은 다른 domain의 page를, `features/<domain>`은 다른 domain의 feature를 import하지 않습니다. entity끼리는 순환 없이 import할 수 있습니다. `widgets/<name>`끼리 import하면 `eds align --check`가 경고하므로 공통 부분은 `features`·`entities`·`shared`로 내립니다.
78
+ - `shared`는 어떤 계층도 import하지 않습니다. 인증 만료처럼 앱 전체가 반응할 일은 `shared/lib/eventBus`로 알리고 `app/providers`가 처리합니다.
79
+ - slice는 계층 바로 아래 디렉터리(`pages/<domain>`, `widgets/<name>`, `features/<domain>`, `entities/<domain>`)입니다. 다른 slice·계층은 `@/`로 시작하는 경로로, 같은 slice 안은 상대 경로로 import합니다.
80
+
81
+ ## 코드 패턴
82
+
83
+ ### API 타입과 enum
84
+
85
+ 타입 파일은 backend 응답의 필드 이름을 그대로 씁니다. `types/api.ts`는 `type`으로 선언하고 파일 끝에서 `export type { … }`로 한 번에 export합니다. `types/enums.ts`의 enum은 `XxxEnum` 이름으로 선언과 함께 export합니다.
86
+
87
+ ```ts
88
+ // entities/accounts/types/api.ts
89
+ type Account = {
90
+ user_id: string;
91
+ user_name: string;
92
+ };
93
+
94
+ type AccountListResponse = Account[];
95
+
96
+ export type { Account, AccountListResponse };
97
+
98
+ // entities/accounts/types/enums.ts
99
+ export enum AccountTypeEnum {
100
+ ADMIN = "admin",
101
+ USER = "user",
102
+ }
103
+ ```
104
+
105
+ ### service
106
+
107
+ ```ts
108
+ // entities/accounts/services/accountService.ts
109
+ import axiosInstance from "@/shared/api";
110
+ import type { Account, AccountListResponse } from "../types/api";
111
+
112
+ const accountService = {
113
+ getAccounts: async (): Promise<AccountListResponse> => {
114
+ const response = await axiosInstance.get<AccountListResponse>("/api/v1/users/");
115
+ return response.data;
116
+ },
117
+
118
+ getAccount: async (userId: string): Promise<Account> => {
119
+ const response = await axiosInstance.get<Account>(`/api/v1/users/${userId}`);
120
+ return response.data;
121
+ },
122
+
123
+ deleteAccount: async (userId: string): Promise<void> => {
124
+ await axiosInstance.delete(`/api/v1/users/${userId}`);
125
+ },
126
+ };
127
+
128
+ export default accountService;
129
+ ```
130
+
131
+ ### query·mutation hook
132
+
133
+ ```ts
134
+ // entities/accounts/hooks/useAccounts.ts
135
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
136
+ import accountService from "../services/accountService";
137
+
138
+ export const useAccounts = () =>
139
+ useQuery({ queryKey: ["accounts"], queryFn: accountService.getAccounts });
140
+
141
+ export const useDeleteAccount = () => {
142
+ const queryClient = useQueryClient();
143
+ return useMutation({
144
+ mutationFn: (userId: string) => accountService.deleteAccount(userId),
145
+ onSuccess: () => {
146
+ queryClient.invalidateQueries({ queryKey: ["accounts"] });
147
+ },
148
+ });
149
+ };
150
+ ```
151
+
152
+ query 기본값(`staleTime` 30초, `gcTime` 5분, 4xx 재시도 없음, mutation 재시도 없음)은 `shared/api/queryClient.ts`에 한 번 둡니다.
153
+
154
+ ### store
155
+
156
+ ```ts
157
+ // entities/auth/stores/authStore.ts
158
+ import { create } from "zustand";
159
+ import { persist } from "zustand/middleware";
160
+
161
+ interface AuthState {
162
+ isAuthenticated: boolean;
163
+ setIsAuthenticated: (isAuthenticated: boolean) => void;
164
+ }
165
+
166
+ export const useAuthStore = create<AuthState>()(
167
+ persist(
168
+ (set) => ({
169
+ isAuthenticated: false,
170
+ setIsAuthenticated: (isAuthenticated) => set({ isAuthenticated }),
171
+ }),
172
+ {
173
+ name: "auth-storage",
174
+ partialize: (state) => ({ isAuthenticated: state.isAuthenticated }),
175
+ },
176
+ ),
177
+ );
178
+ ```
179
+
180
+ 서버 데이터는 store에 복사하지 않고 query hook으로 읽습니다.
181
+
182
+ ### component와 page
183
+
184
+ - 컴포넌트는 named export 함수이고 props는 `interface XxxProps`입니다. `className`은 `@/shared/lib/utils`의 `cn()`으로 합칩니다.
185
+ - page는 `export default function XxxPage()`이며, widget·feature·entity 컴포넌트를 조합하기만 합니다. `app/router/index.tsx`가 `lazy(() => import("@/pages/<domain>/<action>/page"))`로 불러옵니다.
186
+ - 화면에 보이거나 보조기술이 읽는 문자열은 「UI 문구」를 따릅니다. 주석은 한국어로 써도 됩니다.
187
+ - 되돌리기 어려운 행동은 `shared/ui`의 alert-dialog로 확인받은 뒤 실행합니다.
188
+
189
+ ```tsx
190
+ // features/accounts/delete-account/ui/ConfirmDeleteAccountButton.tsx
191
+ // 계정 삭제 확인 dialog(`shared/ui/alert-dialog`) 안의 확정 버튼입니다. 되돌리기 어려운 행동이라 critical/weak를 씁니다.
192
+ import { Button } from "@spectrabrain/eds";
193
+ import { toast } from "sonner";
194
+ import { useDeleteAccount } from "@/entities/accounts/hooks/useAccounts";
195
+
196
+ interface ConfirmDeleteAccountButtonProps {
197
+ userId: string;
198
+ }
199
+
200
+ export function ConfirmDeleteAccountButton({ userId }: ConfirmDeleteAccountButtonProps) {
201
+ const { mutate, isPending } = useDeleteAccount();
202
+
203
+ return (
204
+ <Button
205
+ size="md"
206
+ role="critical"
207
+ variant="weak"
208
+ type="button"
209
+ loading={isPending}
210
+ onClick={() =>
211
+ mutate(userId, {
212
+ onError: () => toast.error("계정을 삭제하지 못했습니다."),
213
+ })
214
+ }
215
+ >
216
+ 계정 삭제
217
+ </Button>
218
+ );
219
+ }
220
+ ```
221
+
222
+ ### UI 문구
223
+
224
+ - 다국어 리소스(i18n)는 당분간 적용하지 않습니다. 화면에 보이거나 보조기술이 읽는 문자열(텍스트, `aria-label`, `alt`, `title`, `placeholder`)은 한국어로 컴포넌트에 직접 씁니다. 나중에 다국어 리소스로 옮기는 시점과 방식은 `미결`입니다.
225
+ - 나중에 문구를 그대로 옮길 수 있도록 문장은 조각내어 `+`로 잇지 않고 한 문자열로 씁니다. 값이 들어가는 문장은 template literal 하나로 씁니다(예: `` `${count}개를 삭제했습니다` ``).
226
+ - 문구는 컴포넌트와 feature의 UI 코드에 두고, service·store·query hook은 문구 대신 데이터와 오류 값을 돌려줍니다. 검증 함수는 표시할 오류 문구를 돌려줍니다.
227
+ - 저장소가 이미 i18n 라이브러리를 쓰고 있으면 기존 설정·번역 파일·호출은 지우지 않고 그대로 둡니다. 새 문구는 이 절의 첫 항목대로 한국어로 직접 씁니다.
228
+
229
+ ### 오류 처리
230
+
231
+ - `app/router/index.tsx`는 route 전체를 `ErrorBoundary`(`app/providers`)로 감쌉니다.
232
+ - `shared/api`의 axios interceptor는 401·403 응답을 `eventBus.emit("error", error)`로 알리고, `app/providers/ErrorHandler.tsx`가 toast와 session 정리를 처리합니다.
233
+ - 사용자 행동의 실패는 그 행동을 처리한 feature가 `toast.error("…")`로 알립니다.
234
+
235
+ ### API mock
236
+
237
+ - REST API가 있으면 `src/mocks/<domain>/index.ts`가 `createMockXxx()` faker factory와 `<domain>Handlers`(`http.get`·`HttpResponse.json`)를 export하고, `src/mocks/handlers.ts`가 모두 모읍니다.
238
+ - worker(`mockServiceWorker.js`)는 `msw init <Vite root>/public --save`(runner는 저장소 형식)로 Vite root의 `public/`에 만듭니다. `dev:mock` script(`.env.mock`의 `VITE_API_MOCKING=true`)에서만 `app/main.tsx`가 worker를 시작합니다.
239
+
240
+ ## 개발 규칙
241
+
242
+ 설정 값의 기준은 저장소의 설정 파일입니다. 정렬 전이면 `eds align`이 대상 저장소의 경로로 채워 `.eds/align/templates/`에 만든 템플릿이 기준입니다. `eds align --check`가 필수 script(`lint`·`format`·`format:check`·`typecheck`·`build`)와 `.prettierignore` 항목을 점검합니다. 다음은 설정 파일만 봐서는 알 수 없는 규칙입니다.
243
+
244
+ - `.prettierignore`에 `src/styles/eds-tokens.css`(eds sync 생성물)와 backend 경로를 둡니다. 생성물을 포맷하면 `eds doctor`가 `stale:`로 보고합니다. backend 경로는 ESLint `globalIgnores`에도 둡니다.
245
+ - React Compiler lint는 `eslint-plugin-react-hooks` 7 recommended로 대신하며 `eslint-plugin-react-compiler`는 설치하지 않습니다.
246
+ - backend가 dev server나 build를 맡는 package는 `dev`·`build`의 기존 명령을 유지합니다. mock 실행은 `dev:mock`(`vite --mode mock`)입니다.
247
+ - EVA 기본값은 `strict: false`입니다. 저장소가 이미 더 엄격한 설정으로 typecheck를 통과하면 그 설정을 유지합니다.
248
+ - Tailwind는 `@tailwindcss/vite`로 실행합니다(`@tailwindcss/postcss`는 import한 package CSS의 `url()`을 옮기지 않아 EDS font 파일이 번들에 들어가지 않습니다). 기존 `server.proxy`의 API target은 유지합니다.
249
+
250
+ ## 점검
251
+
252
+ - 프론트엔드 코드를 바꾼 작업은 완료 전에 `format:check`·`lint`·`typecheck`·`build` script와 `npx @spectrabrain/eds align --check`가 모두 exit 0이고, `eds:doctor`가 `[EDS] synced with` 줄을 출력해야 합니다. `npm run`·`npx`는 저장소의 패키지 매니저 형식(예: `pnpm exec eds align --check`)으로 바꿔 실행합니다. `--check`의 출력 종류와 exit code는 `CONSUMER_AGENT.md` 「프로토타입 정렬」에 있습니다.
package/QUICKSTART.md CHANGED
@@ -1,47 +1,42 @@
1
1
  # EDS QUICKSTART
2
2
 
3
- `@spectrabrain/eds`를 사용하는 저장소의 AI 에이전트가 UI 작업 전에 읽는 유일한 필수 문서입니다. 설치된 버전의 문서와 TypeScript contract가 최종 기준입니다.
4
-
5
- ## package root
6
-
7
- `require.resolve("@spectrabrain/eds/package.json")`이 가리키는 디렉터리이며 보통 `node_modules/@spectrabrain/eds`입니다. 아래 문서는 모두 이 경로에서 읽습니다. `dist/` 아래 파일은 `@spectrabrain/eds/tokens.json`처럼 export subpath로도 resolve됩니다.
3
+ `@spectrabrain/eds`를 쓰는 저장소의 AI 에이전트가 UI 작업(JSX·CSS·`className`을 만들거나 고치는 작업) 전에 읽는 유일한 필수 문서입니다. 아래 문서는 모두 package root(`require.resolve("@spectrabrain/eds/package.json")`의 디렉터리, 보통 `node_modules/@spectrabrain/eds`)에 있습니다. 기억과 다르면 설치된 버전의 문서와 타입을 따릅니다.
8
4
 
9
5
  ## 규칙
10
6
 
11
- 1. EDS 컴포넌트를 유사한 자체 구현보다 우선 사용합니다. 새 코드와 수정 중인 코드에서는 `Button`을 먼저 검토합니다. 소비처 저장소 규칙이 특정 컴포넌트를 지정하더라도 EDS에 같은 역할의 컴포넌트가 있으면 EDS를 먼저 선택하고, 소비처 규칙이 접근성·보안·제품 요구사항인 경우에만 그것을 우선합니다.
12
- 2. `Button`의 `role`·`variant`는 행동의 의미로 고릅니다.
7
+ 1. 사용자의 명시적 요청을 먼저 따르고, 요청 범위의 코드부터 적용합니다. 같은 역할의 EDS 컴포넌트(현재 `Button`)가 있으면 자체 구현이나 소비처 규칙이 지정한 로컬 컴포넌트보다 EDS를 먼저 선택하고, 소비처 규칙이 접근성·보안·제품 요구를 이유로 명시한 경우에만 그것을 우선합니다.
8
+ 2. `Button`의 `role`(ARIA role이 아닌 시각 prop)과 `variant`는 행동의 의미로 고릅니다.
13
9
 
14
10
  | 행동 | 조합 |
15
11
  | --- | --- |
16
- | 화면의 핵심 행동(저장·생성·확인) | `brand/solid` |
12
+ | 화면의 핵심 행동(생성·저장·관리·확인) | `brand/solid` |
17
13
  | 일반 주요 행동(검색·조회) | `neutral/solid` |
18
- | 취소·닫기·수정 같은 보조 행동 | `neutral/outline` |
19
- | 초기화·갱신·되돌리기처럼 부담이 낮은 반복 행동 | `neutral/weak` |
14
+ | 취소·닫기·수정 같은 보조 작업 | `neutral/outline` |
15
+ | 초기화·갱신처럼 부담이 낮은 반복 행동 | `neutral/weak` |
16
+ | 툴바·헤더·메뉴의 보조 행동 | `neutral/ghost` |
20
17
  | 되돌리기 어려운 삭제·초기화 | `critical/weak` |
18
+ | 페이지·콘텐츠로 이동 | `neutral/link`(`href` 필수) |
21
19
  | 비동기 처리 중 | 선택한 조합에 `loading` |
22
20
 
23
- 3. 색상은 `text-eds-fg-*`, `bg-eds-bg-*`, `border-eds-stroke-*`, typography는 `eds-typography-*` class를 사용합니다. exact token이 없으면 `fg/bg/stroke` 역할, 상태, 행동 의미, `neutral` 순서로 가까운 공개 token을 고릅니다.
24
- 4. raw 색상·typography 값, 임의 CSS variable, 패키지 비공개 경로를 만들거나 복사하지 않습니다.
25
- 5. `className`은 배치·컨테이너 보정에만 사용합니다. 문서에 없는 prop·variant·상태를 `className`이나 inline style로 우회하지 않습니다.
26
- 6. 새 UI 스타일은 Tailwind `className`으로 작성하고 `index.css`에 컴포넌트별 규칙을 추가하지 않습니다.
27
- 7. 작업 전후에 `npm run eds:doctor`를 실행합니다. 실패하면 `npm run sync:eds`를 먼저 실행하고 diff를 검토합니다.
28
- 8. 완료 보고에 변경 파일, 사용한 EDS component·token, typecheck·test·build·doctor 결과를 기록합니다.
21
+ 3. 색상·typography는 공개 token으로 지정합니다. `src/styles/eds-tokens.css`가 있으면 `text-eds-fg-*`, `bg-eds-bg-*`, `border-eds-stroke-*`, `eds-typography-*` class로, 없으면 `var(--eds-color-*)`·`var(--eds-typography-*)` 또는 `cssVar()`로 씁니다.
22
+ 4. raw 색상·typography 값, 공개 token이 아닌 CSS variable(내부 `--eds-button-*` 포함), 패키지 비공개 경로는 쓰지 않습니다.
23
+ 5. EDS 컴포넌트의 `className`은 바깥 배치(margin·정렬·grid 위치)에만 씁니다. 필요한 prop·variant·상태가 문서에 없으면 우회하지 말고 멈춰 사용자에게 확인합니다.
24
+ 6. Tailwind를 쓰는 저장소에서는 EDS 밖 요소의 스타일도 `className`으로 쓰고, `index.css` 같은 전역 CSS에 컴포넌트별 규칙을 추가하지 않습니다.
29
25
 
30
- ## 명령
26
+ ## 완료 기준
31
27
 
32
- | 명령 | 용도 |
33
- | --- | --- |
34
- | `npm run eds:doctor` | 동기화 상태와 `eds-*` class 이름을 읽기 전용으로 점검합니다. exit 1이면 sync가 필요하거나 공개 token이 없는 class가 있습니다. `-- --scan`은 raw 색상 class도 나열합니다. |
35
- | `npm run sync:eds` | Tailwind root CSS import, `src/styles/eds-tokens.css`, `AGENTS.md` managed block을 갱신합니다. |
36
- | `npx eds init` | 위 script가 없을 때 등록하고 첫 sync를 실행합니다. |
28
+ 1. `npm run eds:doctor`가 `[EDS] synced with` 줄을 출력합니다. 실패하면 `scripts:` → `npm run sync:eds` → 나머지 순서로, 각 줄의 조치(`unknown:` 포함)와 `CONSUMER_AGENT.md` 「`eds:doctor` 점검」 표대로 고칩니다.
29
+ 2. 소비처 `typecheck`·`build` script가 exit 0이고, Tailwind 저장소는 `npm run eds:doctor -- --scan`의 `raw:`에 바꾼 파일이 없습니다.
30
+ 3. 완료 보고에 변경 파일, 사용한 EDS component·token, typecheck·build·doctor 결과를 적습니다.
31
+
32
+ ## 명령
37
33
 
38
- `npm run`은 소비처 패키지 매니저의 runner(`pnpm`, `yarn`, `bun run`)로 바꿔 읽습니다.
34
+ `npm run eds:doctor`는 읽기 전용 점검, `npm run sync:eds`는 생성 CSS·managed block 갱신, `npx @spectrabrain/eds init`은 script·hook 등록과 첫 sync입니다. runner는 소비처 형식으로 바꿉니다(`pnpm`·`yarn`·`bun run`, init은 `pnpm exec eds init`·`yarn eds init`·`bunx @spectrabrain/eds init`). `npx`에는 `@spectrabrain/eds`를 적습니다(`eds`는 다른 패키지).
39
35
 
40
36
  ## 더 읽을 문서
41
37
 
42
- - `CONSUMER_AGENT.md`: setup·sync·doctor 절차, 패키지 매니저별 `init`, font 정책이 필요할 때 읽습니다.
43
- - `AI.md`: 공개 범위와 금지 사례를 확인할 때 읽습니다.
44
- - `TOKEN_CONTEXT.md`: token을 직접 고를 때 읽습니다.
45
- - `TAILWIND_V4.md`: Tailwind CSS v4 설정을 다룰 때 읽습니다.
46
- - `CHANGELOG.md`: EDS 버전을 올린 뒤 바뀐 내용을 확인할 때 읽습니다.
47
- - `dist/components/Button/README.md`와 `dist/components/Button/spec.json`: Button을 사용할 때 읽습니다. `spec.json`의 `api.iconNames`가 이름으로 쓸 수 있는 아이콘 목록이며, 목록 밖 아이콘은 `icon`·`startIcon`·`endIcon`에 ReactElement로 전달합니다.
38
+ - `CONSUMER_AGENT.md`: `AGENTS.md`에 `<!-- EDS:BEGIN -->`이 없으면 「첫 setup 절차」, doctor·sync가 exit 2이면 「`eds:doctor` 점검」, EDS 버전을 올렸으면 「버전 업데이트」, 프로토타입 정리를 요청받으면 「프로토타입 정렬」을 읽습니다.
39
+ - `TOKEN_CONTEXT.md`: EDS 컴포넌트 prop 밖에서 색상·typography token을 지정하거나 바꿀 때 읽습니다.
40
+ - `TAILWIND_V4.md`: Tailwind CSS v4 adapter나 root CSS 배선을 설정하거나 고칠 때 읽습니다.
41
+ - `AI.md`: dark theme이나 목록 밖 컴포넌트·Tailwind utility가 필요할 때 읽습니다.
42
+ - `dist/components/Button/README.md`: Button을 쓸 때 읽습니다. 이름으로 쓰는 아이콘 목록은 같은 폴더 `spec.json`의 `api.iconNames`입니다.
package/README.md CHANGED
@@ -6,13 +6,18 @@ EVA 서비스의 일관된 UI/UX를 위한 React 웹 디자인 시스템입니
6
6
 
7
7
  ~~~sh
8
8
  npm install @spectrabrain/eds
9
+ npx @spectrabrain/eds init
9
10
  ~~~
10
11
 
11
- 소비처 AI와 자동화 도구는 설치 후 [QUICKSTART.md](QUICKSTART.md)를 먼저 읽습니다. 4 KB 이하의 유일한 필수 문서이며, [AI_CONTEXT.json](AI_CONTEXT.json)의 조건에 해당할 때만 [CONSUMER_AGENT.md](CONSUMER_AGENT.md) 등 나머지 문서를 읽습니다. npm 10·11에서는 EDS `postinstall`이 소비처 `package.json`에 `sync:eds`·`eds:doctor` script를 등록하고, 의존성의 install script가 실행되지 않는 npm 12·pnpm·yarn berry·bun에서는 `npx @spectrabrain/eds init`이 같은 script를 등록한 뒤 패키지 매니저별 승인 절차를 기록하거나 안내합니다. 설치·업데이트 후 `npm run sync:eds`를 실행하면 Tailwind root CSS의 EDS import, Tailwind token alias, `AGENTS.md` managed block과 `CLAUDE.md`·`GEMINI.md`의 import 줄을 갱신하고, `npm run eds:doctor`가 동기화 상태와 EDS font import를 읽기 전용으로 점검합니다. Tailwind를 사용하지 않는 저장소에서는 sync가 Tailwind 배선을 건너뛰고 `AGENTS.md`만 갱신합니다.
12
+ `init`은 소비처 `package.json`에 `sync:eds`·`eds:doctor` script와 doctor hook을 등록하고 첫 동기화를 실행합니다. EDS를 업데이트한 뒤에는 `npm run sync:eds`로 Tailwind root CSS의 EDS import, Tailwind token alias, `AGENTS.md` managed block을 갱신하고, `npm run eds:doctor`로 동기화 상태를 읽기 전용으로 점검합니다. pnpm·yarn·bun의 명령과 동작은 [CONSUMER_AGENT.md](CONSUMER_AGENT.md) 「첫 setup 절차」에 있습니다. `npx`로 실행할 때는 항상 `@spectrabrain/eds`를 적습니다. npm registry의 `eds`는 다른 패키지입니다.
12
13
 
13
- 최초 설치와 동기화를 한 번에 실행하려면 `npm install @spectrabrain/eds && npm run sync:eds`를 사용합니다.
14
+ 소비처 AI와 자동화 도구는 설치 후 [QUICKSTART.md](QUICKSTART.md)를 먼저 읽습니다. 4 KB 이하의 유일한 필수 문서이며, 나머지 문서는 [AI_CONTEXT.json](AI_CONTEXT.json)의 조건에 해당할 때만 읽습니다.
14
15
 
15
- `@spectrabrain/eds/styles.css`는 Pretendard font, semantic CSS custom property, `html` 기본 font family, Button CSS를 함께 불러옵니다. 개별 파일로 구성할 때는 `@spectrabrain/eds/tokens.css`, `@spectrabrain/eds/components.css`, `@spectrabrain/eds/fonts.css`를 함께 import하고 `@spectrabrain/eds/base.css`는 선택합니다. EDS text style은 `fonts.css`가 선언하는 font family `Pretendard`를 참조하므로, 앱이 Pretendard를 다른 경로로 불러오더라도 EDS `fonts.css`를 생략하지 않습니다.
16
+ 첫 sync 전에는 소비처 `AGENTS.md`에 EDS managed block이 없습니다. 이 기간에 AI가 설치된 계약을 먼저 읽게 하려면 `CLAUDE.md` 또는 `AGENTS.md` 상단에 다음 한 줄을 넣습니다. pnpm·yarn·bun 저장소에서는 `npm run`을 그 runner로 바꿉니다. sync가 block을 넣은 뒤에는 이 줄을 지웁니다.
17
+
18
+ > package.json에 `@spectrabrain/eds`가 있으면 UI 작업 전에 `node_modules/@spectrabrain/eds/QUICKSTART.md`를 먼저 읽고 그 규칙과 완료 기준을 따릅니다.
19
+
20
+ `@spectrabrain/eds/styles.css`는 Pretendard font, semantic CSS custom property, `html` 기본 font family, Button CSS를 함께 불러옵니다. 개별 파일(`@spectrabrain/eds/tokens.css`, `@spectrabrain/eds/components.css`, `@spectrabrain/eds/fonts.css`, 선택 파일 `@spectrabrain/eds/base.css`)의 구성 규칙은 [CONSUMER_AGENT.md](CONSUMER_AGENT.md) 「font 정책」에 있습니다.
16
21
 
17
22
  ## 현재 제공 범위
18
23
 
@@ -22,10 +27,11 @@ npm install @spectrabrain/eds
22
27
  - 자동완성을 지원하는 TypeScript token API와 CSS custom property를 제공합니다.
23
28
  - React `Button` 컴포넌트를 제공합니다. 이름으로 지정하는 lucide 아이콘은 승인된 allowlist로 제한합니다.
24
29
  - 소비처 setup용 `eds` CLI(`npx @spectrabrain/eds init`, `npm run sync:eds`, `npm run eds:doctor`)를 제공합니다.
30
+ - Vite + React 프로토타입을 EVA 프론트엔드 컨벤션에 맞추는 일회성 명령 `npx @spectrabrain/eds align`과 컨벤션 문서를 제공합니다.
25
31
 
26
32
  ## Button 사용
27
33
 
28
- `Button`은 size·시각적 role·role에 종속된 variant·native button type을 명시해 사용합니다. `role="neutral"`, `variant="link"` 조합만 `href`를 사용하며 `<a>`로 렌더링합니다.
34
+ `Button`은 `size`, 시각적 `role`, role에 종속된 `variant`를 명시하고, native button이면 `type`을 함께 지정합니다. `role`은 DOM의 ARIA `role`로 전달되지 않습니다.
29
35
 
30
36
  ~~~tsx
31
37
  import "@spectrabrain/eds/styles.css";
@@ -40,67 +46,11 @@ export function SaveButton() {
40
46
  }
41
47
  ~~~
42
48
 
43
- 아이콘만 사용하는 경우 접근성 이름을 제공하려면 `icon` 또는 `iconName`과 `aria-label`을 함께 지정합니다. `aria-label`은 필수로 강제하지 않습니다. `style`과 임의 스타일 override는 지원하지 않습니다. 전체 contract와 접근성 규칙은 설치된 버전의 `@spectrabrain/eds/components/Button/README.md`와 `@spectrabrain/eds/components/Button/spec.json`(package root의 `dist/components/Button/`)을 확인합니다.
44
-
45
- lucide 아이콘은 승인된 allowlist의 PascalCase 이름을 `iconName`, `startIconName`, `endIconName`으로 지정합니다. `ButtonIconName`은 이 allowlist와 같은 union이며 TypeScript 자동완성을 제공하고, 설치된 `dist/components/Button/spec.json`(`@spectrabrain/eds/components/Button/spec.json`)의 `api.iconNames`에서 같은 목록을 확인합니다. allowlist 밖 아이콘은 `icon`/`startIcon`/`endIcon`에 ReactElement로 전달합니다. 같은 아이콘이 두 화면 이상에서 반복되면 allowlist PR을 엽니다. 같은 슬롯에 custom ReactElement와 이름을 함께 지정하지 않으며, 이름으로 지정한 아이콘은 `lucide-react`의 기본 시각 설정을 사용하고 Button이 아이콘 영역 크기를 조정합니다.
46
-
47
- 처리 중인 버튼은 `loading`을 지정합니다. 라벨 공간을 유지한 2색 progress circle을 표시하고 상호작용을 차단하며, link는 href를 제거합니다. `prefers-reduced-motion: reduce` 환경에서는 애니메이션이 정지합니다.
48
-
49
- ## Button API
50
-
51
- `Button`은 TypeScript 타입으로 허용된 role·variant 조합과 native element branch를 제한합니다. 필수 props를 생략하거나 허용되지 않은 조합을 지정하면 TypeScript 자동완성 단계에서 오류를 확인할 수 있습니다.
52
-
53
- ### 필수 props
54
-
55
- | prop | 타입 | 규칙 |
56
- | --- | --- | --- |
57
- | `size` | `"xs" \| "sm" \| "md"` | 사용 공간과 행동의 중요도에 맞춰 선택합니다. 필수입니다. |
58
- | `role` | `"brand" \| "neutral" \| "critical"` | 액션의 의미를 나타내는 시각적 역할입니다. DOM의 ARIA `role`로 전달되지 않습니다. 필수입니다. |
59
- | `variant` | role별 아래 표 | 같은 role 안에서 시각적 위계를 결정합니다. 필수입니다. |
60
- | `type` | `"button" \| "submit" \| "reset"` | native `<button>` branch에서 필수입니다. `neutral/link` branch에서는 사용하지 않습니다. |
61
- | `href` | `string` | `role="neutral"`, `variant="link"` branch에서 필수입니다. 이 branch는 `<a>`로 렌더링합니다. |
62
-
63
- ### role·variant 조합
64
-
65
- | role | 허용 variant |
66
- | --- | --- |
67
- | `brand` | `solid`, `outline` |
68
- | `neutral` | `solid`, `weak`, `outline`, `ghost`, `link` |
69
- | `critical` | `weak` |
70
-
71
- ### 선택 props와 슬롯
72
-
73
- | prop | 타입·규칙 |
74
- | --- | --- |
75
- | `children` | `ReactNode` label입니다. 렌더링 가능한 내용이 하나 이상 필요하며 `startIcon`·`endIcon`과 조합할 수 있습니다. |
76
- | `icon` | icon-only용 단일 custom `ReactElement`입니다. children이나 다른 아이콘 슬롯과 함께 사용하지 않습니다. |
77
- | `iconName` | icon-only용 `ButtonIconName` lucide 이름입니다. allowlist에 있는 이름만 허용합니다. |
78
- | `startIcon` / `endIcon` | label 앞·뒤에 배치할 단일 custom `ReactElement` 슬롯입니다. |
79
- | `startIconName` / `endIconName` | label 앞·뒤에 배치할 `ButtonIconName` lucide 이름입니다. |
80
- | `aria-label` | 선택적인 accessible name입니다. icon-only의 접근성 이름을 제공할 때 지정하며, text label에서도 필요할 때 사용할 수 있습니다. |
81
- | `disabled` | 실행할 수 없는 상태입니다. native button은 `disabled`, link는 href 제거·`aria-disabled="true"`·`tabIndex={-1}`를 적용합니다. |
82
- | `loading` | 처리 중 상태입니다. label과 아이콘 공간을 유지하고 progress circle을 표시하며 상호작용을 차단합니다. `aria-busy="true"`를 적용합니다. |
83
- | `className` | 배치·컨테이너 보정용으로만 사용합니다. |
84
- | `aria-*` / `data-*` | native 속성으로 전달할 수 있습니다. |
85
- | `ref` | native button은 `HTMLButtonElement`, link는 `HTMLAnchorElement`를 전달합니다. |
86
-
87
- `style`과 임의 스타일 override API는 제공하지 않습니다. lucide 이름과 custom `ReactElement`는 같은 슬롯에 함께 지정하지 않습니다. 이름으로 지정한 lucide 아이콘은 `lucide-react` 기본 시각 설정을 사용하며, Button은 아이콘 영역 크기만 조정합니다.
88
-
89
- 슬롯 조합에 따라 layout은 다음처럼 추론합니다.
90
-
91
- | 조합 | layout |
92
- | --- | --- |
93
- | children만 지정합니다. | `textOnly` |
94
- | children과 `startIcon` 또는 `startIconName`을 지정합니다. | `iconFirst` |
95
- | children과 `endIcon` 또는 `endIconName`을 지정합니다. | `iconLast` |
96
- | children과 앞·뒤 아이콘을 모두 지정합니다. | `iconBoth` |
97
- | children 없이 `icon` 또는 `iconName`만 지정합니다. | `iconOnly` |
98
-
99
- 잘못된 슬롯 조합, 빈 label·icon 또는 제공된 빈 `aria-label`은 모든 환경에서 오류로 처리합니다. Button의 개요와 사용 맥락은 설치된 패키지의 `@spectrabrain/eds/components/Button/README.md`(package root의 `dist/components/Button/README.md`)를 기준으로 합니다.
49
+ 허용 role·variant 조합, `neutral/link`의 `href` 필수 조건, 이름 아이콘 allowlist와 ReactElement 슬롯, 접근성·loading 동작은 설치된 버전의 `@spectrabrain/eds/components/Button/README.md`와 `@spectrabrain/eds/components/Button/spec.json`(package root의 `dist/components/Button/`)이 기준입니다.
100
50
 
101
51
  ## 토큰 사용
102
52
 
103
- CSS를 한 번 import한 뒤 `cssVar()`와 `SemanticTokenName`을 사용합니다. 공개 token 이름은 `-`로 구분하며 복합어 안에서만 lower camelCase를 사용합니다.
53
+ CSS를 한 번 import한 뒤 `cssVar()`와 `SemanticTokenName`을 사용합니다. token 이름은 추측해 조합하지 않고 `SemanticTokenName` 자동완성이나 `@spectrabrain/eds/tokens.json`에 있는 이름을 그대로 씁니다(예: `color-stroke-focusRing`, `typography-body1-fontSize`).
104
54
 
105
55
  ~~~ts
106
56
  import "@spectrabrain/eds/styles.css";
@@ -114,11 +64,11 @@ const color = cssVar(token); // var(--eds-color-fg-brand-contrast)
114
64
 
115
65
  `@spectrabrain/eds/tokens`의 타입은 `exports`의 `types`와 함께 `typesVersions`로도 `dist/tokens.d.ts`에 연결되어 있으므로, `moduleResolution`이 `node`(node10)인 소비처에서도 해석됩니다. `bundler`·`node16`에서는 `exports`가 우선합니다.
116
66
 
117
- 필요한 역할의 token이 없으면 임의 색상값을 추가하지 말고 디자인 시스템 담당자에게 요청합니다.
67
+ 역할에 맞는 exact token이 없을 때 가까운 공개 token을 고르는 순서는 [TOKEN_CONTEXT.md](TOKEN_CONTEXT.md)에 있습니다. 그래도 없으면 임의 색상값을 만들지 않고 디자인 시스템 담당자에게 요청합니다.
118
68
 
119
69
  ## Tailwind CSS v4에서 token 사용
120
70
 
121
- EDS는 Tailwind utility API를 패키지에 포함하지 않습니다. Tailwind CSS v4에서 공개 semantic token을 `className`으로 사용하려면 [Tailwind CSS v4 token 설정 가이드](./TAILWIND_V4.md)를 확인합니다. 소비처의 `npm run sync:eds`가 `tokens.json`에서 `src/styles/eds-tokens.css`를 생성해 `@import "tailwindcss"`가 있는 Tailwind root CSS에 연결하며, `text-eds-fg-*`, `bg-eds-bg-*`, `border-eds-stroke-*`, `eds-typography-*` class를 사용합니다. 생성된 `@theme inline`에는 `--font-sans: var(--eds-typography-body1-fontFamily);`도 들어가므로 Tailwind의 기본 sans font가 Pretendard가 됩니다. root 후보가 둘 이상이면 `EDS_TAILWIND_ROOT`로 한 번 지정하며, sync가 그 경로를 소비처 `package.json`의 `eds.tailwindRoot`에 기록해 이후 doctor·hook이 재사용합니다. root가 없으면 sync는 Tailwind 배선을 건너뜁니다. 새 컴포넌트별 스타일은 `index.css`보다 `className`을 우선 작성합니다.
71
+ EDS는 Tailwind utility API를 패키지에 포함하지 않습니다. Tailwind CSS v4 저장소에서는 `npm run sync:eds`가 `tokens.json`에서 `src/styles/eds-tokens.css`를 생성해 `@import "tailwindcss"`가 있는 Tailwind root CSS에 연결하며, `text-eds-fg-*`, `bg-eds-bg-*`, `border-eds-stroke-*`, `eds-typography-*` class를 사용할 수 있습니다. 생성된 `@theme inline`에는 `--font-sans: var(--eds-typography-body1-fontFamily);`도 들어가므로 Tailwind의 기본 sans font가 Pretendard가 됩니다. 설정 방법은 [Tailwind CSS v4 token 설정 가이드](./TAILWIND_V4.md)를 확인합니다.
122
72
 
123
73
  ## Text style 사용
124
74
 
@@ -134,7 +84,11 @@ EDS는 Tailwind utility API를 패키지에 포함하지 않습니다. Tailwind
134
84
  }
135
85
  ~~~
136
86
 
137
- `styles.css`는 `fonts.css`, `tokens.css`, `base.css`, `components.css`를 순서대로 import하며 Pretendard Variable font에 `font-display: swap`을 사용합니다. `base.css`는 `:where(html)`에 body1 font family를 지정하는 specificity 0 규칙 하나이므로 소비처의 `html`·`body` font-family 선언이 있으면 그 선언이 우선합니다. 개별 파일로 구성할 때는 `tokens.css`, `components.css`, `fonts.css`를 함께 import하고 `base.css`는 선택합니다. EDS text style은 `fonts.css`가 선언하는 family `Pretendard`를 참조하므로 다른 출처의 Pretendard로 `fonts.css`를 대체하지 않습니다. font의 license와 저작권 고지는 패키지의 [NOTICE](./NOTICE)에서 확인합니다.
87
+ `styles.css`는 `fonts.css`, `tokens.css`, `base.css`, `components.css`를 순서대로 import하며 Pretendard Variable font에 `font-display: swap`을 사용합니다. `base.css`와 소비처 `html` font-family 선언의 우선순위는 [CONSUMER_AGENT.md](CONSUMER_AGENT.md) 「font 정책」에 있습니다. font의 license와 저작권 고지는 패키지의 [NOTICE](./NOTICE)에서 확인합니다.
88
+
89
+ ## 프로토타입 정렬
90
+
91
+ AI 바이브코딩으로 만든 Vite + React 프로토타입은 EDS를 설치한 frontend package에서 `npx @spectrabrain/eds align`을 한 번 실행해 [EVA 프론트엔드 컨벤션](./FRONTEND_CONVENTIONS.md)에 맞춥니다. 이 명령은 소스를 바꾸지 않고 `.eds/align/`에 AI 에이전트가 따를 단계(`PLAN.md`)와 이 저장소의 경로로 채운 설정 템플릿을 만듭니다. `npx @spectrabrain/eds align --check`가 컨벤션과 보호 경로를 점검합니다. 동작과 exit code는 [CONSUMER_AGENT.md](./CONSUMER_AGENT.md) 「프로토타입 정렬」에 있습니다.
138
92
 
139
93
  ## 공개 경로
140
94
 
@@ -150,12 +104,13 @@ EDS는 Tailwind utility API를 패키지에 포함하지 않습니다. Tailwind
150
104
  | `@spectrabrain/eds/tokens.json` | 도구 연동용 semantic token manifest입니다. |
151
105
  | `@spectrabrain/eds/QUICKSTART.md` | 소비처 AI의 유일한 필수 읽기 문서입니다. 4 KB 이하입니다. |
152
106
  | `@spectrabrain/eds/CONSUMER_AGENT.md` | 소비처 AI의 setup·sync·doctor 계약과 managed block 템플릿입니다. |
153
- | `@spectrabrain/eds/AI.md` | 소비처 AI의 공개 범위와 금지 사례입니다. |
154
- | `@spectrabrain/eds/CHANGELOG.md` | 버전별 변경 내용입니다. EDS 버전을 올린 뒤 읽습니다. |
155
- | `@spectrabrain/eds/AI_CONTEXT.json` | AI 문서 읽기 순서·우선순위·공개 manifest 경로를 제공합니다. |
107
+ | `@spectrabrain/eds/AI.md` | 소비처 AI의 공개 범위와 사용 경계입니다. |
108
+ | `@spectrabrain/eds/CHANGELOG.md` | 버전별 변경 내용과 소비처 조치입니다. |
109
+ | `@spectrabrain/eds/AI_CONTEXT.json` | AI 문서의 읽기 순서·조건·우선순위입니다. |
156
110
  | `@spectrabrain/eds/TOKEN_CONTEXT.md` | semantic token의 역할과 화면 맥락입니다. |
157
111
  | `@spectrabrain/eds/TAILWIND_V4.md` | Tailwind CSS v4에서 공개 token을 연결하는 설치 가이드입니다. |
158
- | `eds` CLI (`npx @spectrabrain/eds init`) | 소비처 setup CLI입니다. `init`은 script 등록과 첫 동기화, `npm run sync:eds`는 동기화, `npm run eds:doctor`는 동기화 상태와 `eds-*` class 이름의 읽기 전용 점검을 실행합니다. 모듈로 import하지 않습니다. |
112
+ | `@spectrabrain/eds/FRONTEND_CONVENTIONS.md` | EVA 프론트엔드의 구조·라이브러리·코드 패턴·개발 규칙입니다. `eds align`의 기준입니다. |
113
+ | `eds` CLI (`npx @spectrabrain/eds init`) | 소비처 setup CLI입니다. `init`은 script 등록과 첫 동기화, `npm run sync:eds`는 동기화, `npm run eds:doctor`는 동기화 상태·font import·`eds-*` class 이름의 읽기 전용 점검, `npx @spectrabrain/eds align`은 프로토타입 정렬 계획과 점검을 실행합니다. 모듈로 import하지 않습니다. |
159
114
  | `@spectrabrain/eds/components/Button/README.md` | Button 개요·사용 맥락·공개 API 문서입니다. |
160
115
  | `@spectrabrain/eds/components/Button/spec.json` | Button 공개 API와 접근성 계약을 위한 manifest입니다. |
161
116
  | `@spectrabrain/eds/llms.txt` | AI 문서 읽기 순서입니다. |
@@ -163,10 +118,6 @@ EDS는 Tailwind utility API를 패키지에 포함하지 않습니다. Tailwind
163
118
 
164
119
  표에 명시한 경로만 지원합니다. 설치된 패키지 버전의 API를 기준으로 사용합니다.
165
120
 
166
- ## 소비자 가이드
167
-
168
- AI와 자동화 도구를 위한 시작 규칙은 [QUICKSTART.md](QUICKSTART.md), setup·sync·doctor 계약은 [CONSUMER_AGENT.md](CONSUMER_AGENT.md), 공통 공개 범위는 [AI.md](AI.md), token 사용 맥락은 [TOKEN_CONTEXT.md](TOKEN_CONTEXT.md), 문서 읽기 순서는 [llms.txt](llms.txt)에서 확인합니다. React 컴포넌트를 사용하기 전에는 해당 컴포넌트의 공식 API와 사용 규칙을 먼저 확인합니다.
169
-
170
121
  ## 라이선스
171
122
 
172
123
  이 패키지는 현재 `UNLICENSED`입니다. 번들 Pretendard font는 SIL Open Font License 1.1로 제공하며 세부 고지는 [NOTICE](./NOTICE)에서 확인합니다. 그 밖의 사용·재배포 권한은 EVA 디자인 시스템 담당자에게 문의합니다.