@spectrabrain/eds 0.1.1 → 0.2.0

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.
package/AI.md CHANGED
@@ -4,14 +4,14 @@
4
4
 
5
5
  ## 공개 범위
6
6
 
7
- - light theme, `AI_CONTEXT.json`의 `components`에 있는 컴포넌트(현재 `Button`), 공개 semantic color·typography token과 token runtime API(`@spectrabrain/eds/tokens`)를 제공합니다.
7
+ - light theme, `AI_CONTEXT.json`의 `components`에 있는 컴포넌트(현재 `Button`), 공개 semantic color·typography·spacing·radius token과 token runtime API(`@spectrabrain/eds/tokens`)를 제공합니다.
8
8
  - 패키지는 Tailwind preset·plugin·utility를 export하지 않습니다. Tailwind class는 소비처의 `eds sync`가 `src/styles/eds-tokens.css`에 생성한 것만 씁니다.
9
- - dark theme, 목록 밖 컴포넌트, 공개 spacing·radius token은 없습니다. 요청이 이를 요구하면 만들지 말고 사용자에게 확인합니다. spacing·radius는 소비처의 기존 scale을 씁니다.
9
+ - dark theme과 목록 밖 컴포넌트는 지원 범위 밖입니다. EDS component가 없거나 필요한 prop·variant·state·slot contract가 맞지 않으면 소비처에서 로컬 UI를 구현하고, 공개 token으로 색상·typography·spacing·radius를 지정합니다. 로컬 UI를 나중에 EDS로 옮긴다는 별도 기록은 남기지 않습니다.
10
10
  - CSS 진입점 구성은 `CONSUMER_AGENT.md` 「font 정책」을 따릅니다.
11
11
 
12
12
  ## 사용 경계
13
13
 
14
- - 문서·`spec.json`·`tokens.json`에 있는 API·prop·variant·token·export 경로만 씁니다. 없거나 스타일 우회가 필요하면 `QUICKSTART.md` 규칙 4·5를 따릅니다.
14
+ - 문서·`spec.json`·`tokens.json`에 있는 API·prop·variant·token·export 경로만 씁니다. component 존재 여부는 `AI_CONTEXT.json.components` → component README → `spec.json` → TypeScript contract 순으로 확인하며, contract가 맞지 않으면 `QUICKSTART.md` 규칙 3·4로 분기합니다.
15
15
  - EDS 컴포넌트의 native element·accessible name·keyboard 동작은 그대로 둡니다. icon-only Button에는 비어 있지 않은 `aria-label`(또는 `aria-labelledby`)을 지정합니다.
16
16
  - 이름으로 지정하는 아이콘은 allowlist(`spec.json`의 `api.iconNames`)만 받고, 목록 밖 아이콘은 ReactElement 슬롯으로 전달합니다. 버전 선언과 보고 방법은 `dist/components/Button/README.md` 「Layout과 아이콘」에 있습니다.
17
17
 
package/AI_CONTEXT.json CHANGED
@@ -32,7 +32,7 @@
32
32
  "specifier": "@spectrabrain/eds/TOKEN_CONTEXT.md",
33
33
  "kind": "token-selection",
34
34
  "required": false,
35
- "when": "A color or typography token is written or changed outside EDS component props, including CSS variables or cssVar() without Tailwind"
35
+ "when": "A color, typography, spacing or radius token is written or changed outside EDS component props, including CSS variables or cssVar() without Tailwind"
36
36
  },
37
37
  {
38
38
  "file": "TAILWIND_V4.md",
@@ -53,7 +53,7 @@
53
53
  "specifier": "@spectrabrain/eds/tokens.json",
54
54
  "kind": "token-manifest",
55
55
  "required": false,
56
- "when": "A tool or script needs the machine-readable token manifest, or an agent checks a CSS variable name for var() without Tailwind; class names come from src/styles/eds-tokens.css"
56
+ "when": "A tool or script needs the machine-readable token manifest and public name-to-CSS-variable mapping, or an agent checks a CSS variable name for var() without Tailwind; class names come from src/styles/eds-tokens.css"
57
57
  },
58
58
  {
59
59
  "file": "dist/components/{name}/README.md",
package/CHANGELOG.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @spectrabrain/eds
2
2
 
3
- ## 0.1.1
3
+ ## 0.2.0
4
4
 
5
- ### Patch Changes
5
+ ### Minor Changes
6
6
 
7
7
  - 190c1d1: 소비처 조치: 업데이트 뒤 sync가 `note:` 줄을 출력하면 그 안내대로 지시 파일을 만들거나 루트 지시 파일에 안내 문장을 넣습니다.
8
8
 
@@ -48,6 +48,10 @@
48
48
 
49
49
  `eds align`과 `eds align --check`, 새 공개 문서 `FRONTEND_CONVENTIONS.md`를 추가했습니다. Vite + React 프로토타입의 frontend package에서 `npx @spectrabrain/eds align`을 실행하면 소스를 바꾸지 않고 `.eds/align/`에 AI 에이전트가 따를 8단계 계획(`PLAN.md`), 진행·기준선·결정을 적는 `NOTES.md`, 이 저장소의 경로로 채운 설정·소스 템플릿을 만듭니다. `--check`는 컨벤션(FSD 구조, import 방향, 의존성, 설정, script)과, 계획이 있으면 기준 commit 이후 보호 경로(backend·배포 설정·frontend package 밖)의 변경을 점검합니다. UI 문구는 당분간 다국어 리소스 없이 한국어로 컴포넌트에 직접 쓰고, 나중에 리소스로 옮길 수 있도록 문장을 한 문자열로 씁니다(「UI 문구」). 사용법·보호 경로·exit code는 `CONSUMER_AGENT.md` 「프로토타입 정렬」, 규칙은 `FRONTEND_CONVENTIONS.md`에 있습니다.
50
50
 
51
+ - d6f7889: 소비처 조치: 업데이트 뒤 `npm run sync:eds`를 실행해 spacing·radius Tailwind alias를 생성하고, UI 작업에서는 `TOKEN_CONTEXT.md`의 public token 이름과 `cssVar()` 사용법을 따릅니다.
52
+
53
+ 공개 semantic token에 현재 spacing·radius scale 전체를 추가했습니다. `spacing-0.5`처럼 소수점이 있는 public 이름은 TypeScript 자동완성과 `tokens.json`의 CSS variable mapping을 유지하며, CSS reference에서만 점을 escape합니다. Tailwind에서는 `p-eds-spacing-0-5`, `rounded-eds-radius-1` 같은 CSS-safe alias를 생성합니다. EDS component가 없거나 contract가 맞지 않는 화면은 소비처 로컬 UI로 구현하고 공개 token을 사용하도록 AI 문서와 검사를 맞췄습니다.
54
+
51
55
  - ec3d964: 소비처 조치: Tailwind CSS v3 저장소(CSS가 `@tailwind` 지시어를 씀)에서 `EDS_TAILWIND_ROOT`나 `package.json` `eds.tailwindRoot`가 v3 CSS 파일을 가리키면 이제 `eds sync`·`eds doctor`가 exit 2로 멈춥니다. 그 설정을 지우고, 앱 진입점에서 `@spectrabrain/eds/styles.css`를 import한 뒤 `var(--eds-*)`·`cssVar()`로 token을 씁니다. Tailwind CSS v4로 올리는 것은 사용자에게 확인한 뒤 진행합니다.
52
56
 
53
57
  Tailwind root가 없을 때 sync·doctor는 v3 저장소에 `Tailwind CSS v3 detected` note를, 그 밖의 저장소에 `@import "tailwindcss";`를 추가한 뒤 sync를 다시 실행하라는 note를 출력합니다. `TAILWIND_V4.md`는 설정 순서, 관찰할 수 있는 조건으로 고르는 adapter 표(Vite에서는 `@tailwindcss/vite`만), 기존 설정에 합치는 예시, `src/styles/eds-tokens.css`를 commit하는 이유, Tailwind 저장소 전용 완료 기준만 담는 설정 문서로 바뀝니다. class 사용법은 `TOKEN_CONTEXT.md`에만 둡니다. `CONSUMER_AGENT.md`는 yarn berry에서 doctor hook 대신 `typecheck`·`build` 앞의 `eds doctor`가 같은 역할을 한다고 적습니다.
@@ -58,7 +62,7 @@
58
62
 
59
63
  - 82a251f: 소비처 조치: 업데이트 뒤 `eds doctor`가 대소문자를 틀린 token class(예: `outline-eds-stroke-FocusRing`)를 `unknown:`으로 새로 보고하면 token 이름의 대소문자대로 고칩니다. doctor가 이제 대소문자가 섞인 class(예: `outline-eds-stroke-focusRing`)도 검사하기 때문입니다.
60
64
 
61
- `TOKEN_CONTEXT.md`는 역할별 실제 token 이름표(의미 색, `neutral`, `layer`·`transparent`·`disabled`·`placeholder`·`focus`·`focusRing`)와 hover가 bg에만 있다는 점, `ring-`·`outline-`·`divide-`·`fill-` class 패턴, exact token이 없을 때 조건을 하나씩 완화하는 순서, 의미별 용도(`critical` 오류·위험 등)와 Button과 같은 배경·글자 조합을 담습니다. 확인되지 않은 조합(`-contrast` fg의 용도, `layer`와 `neutral` 배경의 구분, divider 단계)은 `미결`로 표시합니다. 예시는 surface 배경 class를 빼고 divider를 `<hr className="border-t …">`로 바꾸며, 기존 scale로 표현할 수 없는 spacing·radius는 멈추고 사용자에게 확인하도록 안내합니다.
65
+ `TOKEN_CONTEXT.md`는 역할별 실제 token 이름표(의미 색, `neutral`, `layer`·`transparent`·`disabled`·`placeholder`·`focus`·`focusRing`)와 hover가 bg에만 있다는 점, `ring-`·`outline-`·`divide-`·`fill-` class 패턴, exact token이 없을 때 조건을 하나씩 완화하는 순서, 의미별 용도(`critical` 오류·위험 등)와 Button과 같은 배경·글자 조합을 담습니다. 확인되지 않은 조합(`-contrast` fg의 용도, `layer`와 `neutral` 배경의 구분, divider 단계)은 `미결`로 표시합니다. 예시는 surface 배경 class를 빼고 divider를 `<hr className="border-t …">`로 바꾸며, 공개 spacing·radius scale과 dotted public name·CSS-safe Tailwind alias·`cssVar()` 사용법을 함께 안내합니다.
62
66
 
63
67
  ## 0.1.0
64
68
 
package/CONSUMER_AGENT.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  - JSX·CSS·`className`을 만들거나 고치는 작업(UI 작업) 전에 EDS package root(`require.resolve("@spectrabrain/eds/package.json")`의 디렉터리, 보통 `node_modules/@spectrabrain/eds`)의 `QUICKSTART.md`를 읽습니다. Button 선택표, 명령, doctor 실패 시 조치가 있습니다.
14
14
  - 사용자의 명시적 요청이 이 block보다 우선하며, 요청 범위의 코드부터 적용합니다. 같은 역할의 EDS 컴포넌트(현재 `Button`)가 있으면 자체 구현이나 소비처 규칙이 지정한 로컬 컴포넌트보다 EDS를 먼저 선택하고, 소비처 규칙이 접근성·보안·제품 요구를 이유로 명시한 경우에만 그것을 우선합니다.
15
- - 색상·typography는 raw 값 대신 공개 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()`를 씁니다. spacing·radius는 소비처의 기존 scale을 씁니다.
15
+ - `AI_CONTEXT.json.components` → component README → `spec.json` → TypeScript contract 순으로 존재 여부를 확인합니다. 필요한 contract가 맞으면 EDS component를 쓰고, 목록에 없거나 맞지 않으면 소비처 로컬 UI를 구현합니다. 색상·typography·spacing·radius는 raw 값 대신 공개 token으로 지정하며, Tailwind에서는 `text-eds-fg-*`·`bg-eds-bg-*`·`border-eds-stroke-*`·`eds-typography-*`·`p-eds-spacing-0-5`·`rounded-eds-radius-1`, 그 밖에서는 `var(--eds-color-*)`·`cssVar("spacing-0.5")` 같은 API를 씁니다.
16
16
  - UI 작업을 끝내기 전에 이 `AGENTS.md`가 있는 디렉터리에서 `npm run eds:doctor`를 실행해 `[EDS] synced with` 줄을 확인합니다. 이 줄이 없으면 `QUICKSTART.md` 「완료 기준」의 순서로 고칩니다.
17
17
  - UI 작업의 완료 보고에 변경 파일, 사용한 EDS component·token, typecheck·build·doctor 결과를 적습니다.
18
18
  - 이 block은 `npm run sync:eds`가 다시 씁니다. 소비처 규칙은 block 밖에 적습니다.
@@ -72,7 +72,7 @@ setup은 `npm run eds:doctor`가 `[EDS] synced with` 줄을 출력하면 완료
72
72
 
73
73
  sync는 모든 변경을 먼저 계산하고, 계산 중 하나라도 실패하면 어떤 파일도 바꾸지 않습니다. 쓰는 내용은 다음과 같습니다.
74
74
 
75
- 1. `src/styles/eds-tokens.css`를 생성하고, Tailwind root의 마지막 Tailwind import 바로 다음 줄에 `@import "@spectrabrain/eds/styles.css";`와 생성 CSS의 상대 경로 `@import`를 추가합니다. 이미 있는 import는 다시 추가하지 않습니다. 생성 CSS의 `@theme inline`에는 공개 color alias와 `--font-sans: var(--eds-typography-body1-fontFamily);`가 들어갑니다. root CSS 예시는 `TAILWIND_V4.md`에 있습니다.
75
+ 1. `src/styles/eds-tokens.css`를 생성하고, Tailwind root의 마지막 Tailwind import 바로 다음 줄에 `@import "@spectrabrain/eds/styles.css";`와 생성 CSS의 상대 경로 `@import`를 추가합니다. 이미 있는 import는 다시 추가하지 않습니다. 생성 CSS의 `@theme inline`에는 공개 color·spacing·radius alias와 `--font-sans: var(--eds-typography-body1-fontFamily);`가 들어갑니다. root CSS 예시는 `TAILWIND_V4.md`에 있습니다.
76
76
  2. `src/` 아래 JS·TS 파일에서 `eds-tokens.css`를 import하는 줄을 제거합니다. Tailwind는 JS에서 import한 CSS를 compile하지 않기 때문입니다.
77
77
  3. `AGENTS.md` managed block을 넣거나 갱신합니다.
78
78
  4. `CLAUDE.md`가 있으면 `@AGENTS.md`, `CLAUDE.md` 없이 `.claude/CLAUDE.md`가 있으면 `@../AGENTS.md`, `GEMINI.md`가 있으면 `@./AGENTS.md` import를 `<!-- EDS:CLAUDE:BEGIN/END -->`·`<!-- EDS:GEMINI:BEGIN/END -->` marker로 감싸 파일 끝에 한 번 추가합니다. sync는 이 marker로 import를 찾으므로 marker와 import를 함께 유지합니다. 파일이 없으면 만들지 않고, `AGENTS.md`와 같은 파일(symlink)이면 추가하지 않습니다.
@@ -108,7 +108,7 @@ EDS text style은 font family `Pretendard`를 참조하며, 이 family는 `@spec
108
108
  | `missing:`·`stale:`·`legacy:`(생성 CSS, root CSS import, `AGENTS.md` block, `CLAUDE.md`·`GEMINI.md` import) | `npm run sync:eds`로 고칩니다(`sync:eds`가 등록되지 않았으면 `npx @spectrabrain/eds sync`). |
109
109
  | `missing:` EDS font import(Tailwind root가 없는 저장소) | 앱 진입점에서 `@spectrabrain/eds/styles.css`를 import합니다. 이 경우 sync는 CSS를 고치지 않습니다. |
110
110
  | `scripts:` | 등록되지 않았으면 「첫 setup 절차」의 `init`으로 등록합니다(init이 sync까지 실행합니다). 같은 이름의 다른 script이면 init이 덮어쓰지 않으므로, 그 script가 `eds sync`·`eds doctor`를 실행하도록 고치거나 이름을 바꾼 뒤 init을 실행합니다. |
111
- | `unknown: <파일:줄> <class>` | 공개 token이 없는 `eds-*` class입니다. Tailwind는 이런 class를 조용히 버리므로 `src/styles/eds-tokens.css`에 있는 class로 고칩니다. class 이름은 token `color-<이름>`이 `<utility>-eds-<이름>`, `typography-<style>-*`가 `eds-typography-<style>`이 되는 규칙으로 만들어집니다. |
111
+ | `unknown: <파일:줄> <class>` | 공개 token이 없는 `eds-*` class입니다. Tailwind는 이런 class를 조용히 버리므로 `src/styles/eds-tokens.css`에 있는 class로 고칩니다. class 이름은 color token이 `<utility>-eds-<이름>`, `typography-<style>-*`가 `eds-typography-<style>`, spacing·radius가 `p-eds-spacing-0-5`·`rounded-eds-radius-1`처럼 되는 규칙으로 만들어집니다. |
112
112
  | `invalid:` | `AGENTS.md`의 EDS marker가 중복되거나 한쪽만 있습니다. sync는 이 상태를 고치지 않습니다. 출력에 적힌 줄에서 BEGIN 한 줄과 그 뒤의 END 한 줄만 남기고 나머지 marker 줄을 지운 뒤 sync를 다시 실행합니다. |
113
113
 
114
114
  `npm run eds:doctor -- --warn`과 `EDS_DOCTOR=warn`은 같은 출력에 warn mode 경고를 더하고 exit 0으로 끝나며, `EDS_DOCTOR=off`는 출력 없이 exit 0으로 끝납니다. 이 설정은 hook을 잠시 풀어야 할 때 사용자가 쓰는 것이며, 이 상태의 exit 0은 완료가 아닙니다.
@@ -147,6 +147,6 @@ EDS 버전을 올렸으면 다음 순서로 진행합니다.
147
147
  1. sync 전에 `AGENTS.md`의 `<!-- EDS:VERSION x.y.z -->` 줄에서 이전 버전을 확인합니다. 이미 sync했다면 `git diff AGENTS.md`의 삭제된 버전 줄에서 확인합니다.
148
148
  2. package root의 `CHANGELOG.md`(`@spectrabrain/eds/CHANGELOG.md`)에서 이전 버전보다 높은 절만 읽고, 각 항목이 요구하는 소비처 조치를 모두 적용합니다. 새 항목은 `소비처 조치:`로 시작합니다.
149
149
  3. `npm run sync:eds`로 Tailwind root의 import, 공개 token alias, managed block과 버전 줄을 갱신하고, `npm run eds:doctor`가 `[EDS] synced with` 줄을 출력하는지 확인합니다.
150
- 4. 제거되거나 이름이 바뀐 token은 `CHANGELOG.md`에서 이름을 확인하고, Tailwind class는 doctor의 `unknown:` 항목에서, `cssVar()` 호출은 typecheck 오류에서, CSS·inline style의 `var(--eds-<이름>)`은 저장소 검색으로 찾아 고칩니다. 새로 쓸 수 있는 class는 `src/styles/eds-tokens.css`의 diff에서 확인합니다.
150
+ 4. 제거되거나 이름이 바뀐 token은 `CHANGELOG.md`에서 이름을 확인하고, Tailwind class는 doctor의 `unknown:` 항목에서, `cssVar()` 호출은 typecheck 오류에서, CSS·inline style의 `var(--eds-<이름>)`은 `tokens.json`의 `cssVariable` mapping과 대조해 고칩니다. dotted public name은 CSS reference에서만 점을 escape합니다. 새로 쓸 수 있는 class는 `src/styles/eds-tokens.css`의 diff에서 확인합니다.
151
151
 
152
152
  생성 CSS의 header나 `AGENTS.md`의 버전 줄이 설치된 버전과 다르면 doctor가 `stale:`로 보고하므로, sync를 빠뜨린 업데이트는 `pretypecheck`·`prebuild` hook(yarn berry는 `typecheck`·`build` 앞의 `eds doctor`)에서 드러납니다. 작업 보고에는 올린 EDS 버전, 적용한 소비처 조치, 바뀐 파일, 새로 쓸 수 있는 class, 제거되거나 이름이 바뀐 공개 token, 검증 결과를 남깁니다.
package/QUICKSTART.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # EDS QUICKSTART
2
2
 
3
- `@spectrabrain/eds`를 쓰는 저장소의 AI 에이전트가 UI 작업(JSX·CSS·`className`을 만들거나 고치는 작업) 전에 읽는 유일한 필수 문서입니다. 아래 문서는 모두 package root(`require.resolve("@spectrabrain/eds/package.json")`의 디렉터리, 보통 `node_modules/@spectrabrain/eds`)에 있습니다. 기억과 다르면 설치된 버전의 문서와 타입을 따릅니다.
3
+ `@spectrabrain/eds` 소비처 AI가 UI 작업 전에 읽는 유일한 필수 문서입니다. 문서는 package root(`require.resolve("@spectrabrain/eds/package.json")`, 보통 `node_modules/@spectrabrain/eds`)에 있으며 설치 버전의 문서·타입을 따릅니다.
4
4
 
5
5
  ## 규칙
6
6
 
@@ -18,10 +18,10 @@
18
18
  | 페이지·콘텐츠로 이동 | `neutral/link`(`href` 필수) |
19
19
  | 비동기 처리 중 | 선택한 조합에 `loading` |
20
20
 
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에 컴포넌트별 규칙을 추가하지 않습니다.
21
+ 3. 화면 요구를 `AI_CONTEXT.json.components` → component `README.md` → `spec.json` → TypeScript contract 순으로 대조합니다. 필요한 prop·variant·state·slot까지 모두 맞으면 EDS component로 판정하고, 목록에 없거나 contract가 맞지 않으면 소비처 로컬 UI로 분기합니다.
22
+ 4. 색상·typography·spacing·radius는 공개 token으로 지정합니다. Tailwind는 `text-eds-fg-*`, `bg-eds-bg-*`, `border-eds-stroke-*`, `p-eds-spacing-0-5`, `rounded-eds-radius-1`, 그 밖은 `var(--eds-color-*)`·`var(--eds-typography-*)` 또는 `cssVar("spacing-0.5")`처럼 씁니다.
23
+ 5. raw 색상·typography 값, 공개 token이 아닌 CSS variable(내부 `--eds-button-*` 포함), 패키지 비공개 경로는 쓰지 않습니다. EDS component의 `className`은 바깥 배치에만 씁니다.
24
+ 6. Tailwind를 쓰는 저장소에서는 EDS 밖 요소도 `className`으로 쓰고, `index.css` 같은 전역 CSS에 컴포넌트별 규칙을 추가하지 않습니다.
25
25
 
26
26
  ## 완료 기준
27
27
 
@@ -31,12 +31,12 @@
31
31
 
32
32
  ## 명령
33
33
 
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`는 다른 패키지).
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`).
35
35
 
36
36
  ## 더 읽을 문서
37
37
 
38
38
  - `CONSUMER_AGENT.md`: `AGENTS.md`에 `<!-- EDS:BEGIN -->`이 없으면 「첫 setup 절차」, doctor·sync가 exit 2이면 「`eds:doctor` 점검」, EDS 버전을 올렸으면 「버전 업데이트」, 프로토타입 정리를 요청받으면 「프로토타입 정렬」을 읽습니다.
39
- - `TOKEN_CONTEXT.md`: EDS 컴포넌트 prop 밖에서 색상·typography token을 지정하거나 바꿀 때 읽습니다.
39
+ - `TOKEN_CONTEXT.md`: EDS 컴포넌트 prop 밖에서 color·typography·spacing·radius token을 지정하거나 바꿀 때 읽습니다.
40
40
  - `TAILWIND_V4.md`: Tailwind CSS v4 adapter나 root CSS 배선을 설정하거나 고칠 때 읽습니다.
41
41
  - `AI.md`: dark theme이나 목록 밖 컴포넌트·Tailwind utility가 필요할 때 읽습니다.
42
42
  - `dist/components/Button/README.md`: Button을 쓸 때 읽습니다. 이름으로 쓰는 아이콘 목록은 같은 폴더 `spec.json`의 `api.iconNames`입니다.
package/README.md CHANGED
@@ -22,6 +22,7 @@ npx @spectrabrain/eds init
22
22
  ## 현재 제공 범위
23
23
 
24
24
  - foreground, background, stroke 역할의 semantic color token을 제공합니다.
25
+ - 소비처 로컬 UI에서도 사용할 수 있는 spacing·radius semantic scale을 제공합니다. public 이름의 소수점은 유지하며 Tailwind에서는 CSS-safe alias로 연결합니다.
25
26
  - Display, Heading, Title, Body, Caption semantic text style을 제공합니다. 각 style은 font family, weight, size, line height, letter spacing token으로 구성합니다.
26
27
  - `styles.css`에서 사용할 수 있는 Pretendard Variable v1.3.9 webfont를 제공합니다.
27
28
  - 자동완성을 지원하는 TypeScript token API와 CSS custom property를 제공합니다.
@@ -62,13 +63,17 @@ const color = cssVar(token); // var(--eds-color-fg-brand-contrast)
62
63
 
63
64
  `SemanticTokenName`을 사용하면 승인된 token 이름을 TypeScript 자동완성으로 선택할 수 있습니다. `var(--eds-...)`의 직접 자동완성은 에디터의 CSS import 색인 방식에 따라 달라질 수 있습니다.
64
65
 
66
+ `spacing-0.5`처럼 소수점이 있는 public 이름은 `cssVar("spacing-0.5")`로 전달합니다. 반환되는 CSS reference는 `var(--eds-spacing-0\.5)`처럼 점을 escape합니다. Tailwind에서는 `p-eds-spacing-0-5`, `rounded-eds-radius-1` 같은 CSS-safe alias를 사용합니다.
67
+
65
68
  `@spectrabrain/eds/tokens`의 타입은 `exports`의 `types`와 함께 `typesVersions`로도 `dist/tokens.d.ts`에 연결되어 있으므로, `moduleResolution`이 `node`(node10)인 소비처에서도 해석됩니다. `bundler`·`node16`에서는 `exports`가 우선합니다.
66
69
 
67
70
  역할에 맞는 exact token이 없을 때 가까운 공개 token을 고르는 순서는 [TOKEN_CONTEXT.md](TOKEN_CONTEXT.md)에 있습니다. 그래도 없으면 임의 색상값을 만들지 않고 디자인 시스템 담당자에게 요청합니다.
68
71
 
72
+ 화면 요구에 맞는 EDS component가 `AI_CONTEXT.json` 목록에 없거나 `README.md`·`spec.json`·TypeScript contract가 요구사항과 맞지 않으면 소비처에서 로컬 UI를 구현하고 공개 token을 사용합니다.
73
+
69
74
  ## Tailwind CSS v4에서 token 사용
70
75
 
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)를 확인합니다.
76
+ 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-*`, `p-eds-spacing-0-5`, `rounded-eds-radius-1` class를 사용할 수 있습니다. 생성된 `@theme inline`에는 `--font-sans: var(--eds-typography-body1-fontFamily);`도 들어가므로 Tailwind의 기본 sans font가 Pretendard가 됩니다. 설정 방법은 [Tailwind CSS v4 token 설정 가이드](./TAILWIND_V4.md)를 확인합니다.
72
77
 
73
78
  ## Text style 사용
74
79
 
package/TAILWIND_V4.md CHANGED
@@ -64,6 +64,8 @@ sync는 설치된 package의 공개 `@spectrabrain/eds/tokens.json`에서 token
64
64
  /* Generated from @spectrabrain/eds@<version>. Do not edit manually. */
65
65
  @theme inline {
66
66
  --color-eds-fg-brand: var(--eds-color-fg-brand);
67
+ --spacing-eds-spacing-0-5: var(--eds-spacing-0\.5);
68
+ --radius-eds-radius-1: var(--eds-radius-1);
67
69
  --font-sans: var(--eds-typography-body1-fontFamily);
68
70
  }
69
71
 
@@ -77,6 +79,7 @@ sync는 설치된 package의 공개 `@spectrabrain/eds/tokens.json`에서 token
77
79
  ```
78
80
 
79
81
  - `--font-sans`는 `@theme inline`의 마지막 줄이며 Tailwind의 기본 sans font를 EDS body1 font family로 바꿉니다.
82
+ - 공개 spacing·radius token은 Tailwind CSS-safe alias로 연결합니다. `spacing-0.5`는 `p-eds-spacing-0-5`, `radius-1`은 `rounded-eds-radius-1`처럼 사용합니다. alias의 점은 `-`로 바뀌며, 생성 CSS의 `var(--eds-spacing-0\.5)`에서만 점을 escape합니다.
80
83
  - `@theme`의 EDS alias는 sync가 생성한 `var(--eds-*)` 참조만 두고, EDS를 업데이트할 때마다 `npm run sync:eds`로 다시 생성합니다.
81
84
  - `eds-tokens.css`는 `@theme`·`@utility`를 담으므로 Tailwind root CSS에서만 import합니다. `@spectrabrain/eds/styles.css`와 `eds-tokens.css`는 각각 한 곳에서만 import합니다.
82
85
  - `src/styles/eds-tokens.css`는 생성물이지만 저장소에 commit합니다. install은 이 파일을 만들지 않으므로, 파일이 없으면 Tailwind root의 `@import`가 깨지고, `pretypecheck`·`prebuild` hook(yarn berry는 `typecheck`·`build` 앞의 `eds doctor`)과 `--print-ci` CI job의 `eds doctor`가 `missing:`으로 실패합니다.
package/TOKEN_CONTEXT.md CHANGED
@@ -90,9 +90,25 @@ import { cssVar } from "@spectrabrain/eds/tokens";
90
90
 
91
91
  ## Spacing과 radius
92
92
 
93
- 현재 공개 semantic token에는 소비처용 spacing·radius alias가 없습니다. 소비처의 기존 scale(Tailwind를 쓰는 저장소는 Tailwind 기본 scale)을 씁니다. 기존 scale로 표현할 수 없으면 임의 값을 만들기 전에 멈추고 사용자에게 확인합니다.
93
+ 소비처 로컬 UI와 EDS component 밖의 배치에는 공개 spacing·radius token을 씁니다. 현재 scale 전체를 공개하며, 이름의 소수점은 유지합니다.
94
+
95
+ - spacing: `spacing-none`, `spacing-0.5`, `spacing-1`, `spacing-1.5`, `spacing-2`, `spacing-2.5`, `spacing-3`, `spacing-3.5`, `spacing-4`, `spacing-4.5`, `spacing-5`, `spacing-6`, `spacing-7`, `spacing-7.5`, `spacing-8`, `spacing-9`, `spacing-10`, `spacing-11`, `spacing-12`
96
+ - radius: `radius-none`, `radius-1`, `radius-2`, `radius-3`, `radius-4`, `radius-5`, `radius-6`, `radius-7`, `radius-8`, `radius-9`, `radius-10`
97
+
98
+ Tailwind에서는 CSS-safe alias를 사용합니다. `spacing-0.5`는 `p-eds-spacing-0-5`, `radius-1`은 `rounded-eds-radius-1`처럼 씁니다. `0.5`·`1.5`·`2.5`·`3.5`·`4.5`·`7.5`의 점은 alias에서 `-`로 바뀝니다.
99
+
100
+ Tailwind가 없으면 `@spectrabrain/eds/tokens`의 `SemanticTokenName` 자동완성과 `cssVar()`를 사용합니다. public 이름은 `spacing-0.5`로 전달하고, CSS reference에서만 점이 escape됩니다.
101
+
102
+ ```tsx
103
+ import { cssVar } from "@spectrabrain/eds/tokens";
104
+
105
+ <div style={{ padding: cssVar("spacing-0.5"), borderRadius: cssVar("radius-1") }}>…</div>;
106
+ // var(--eds-spacing-0\.5), var(--eds-radius-1)
107
+ ```
108
+
109
+ 직접 CSS를 쓸 때도 `tokens.json`의 `tokens.<name>.$extensions.eds.cssVariable` mapping을 따릅니다. CSS custom property 선언과 `var()` reference에서만 `.`을 escape하며, public token 이름은 바꾸지 않습니다.
94
110
 
95
111
  ## 확인 목록
96
112
 
97
- - 새로 쓴 `var(--eds-*)` 이름이 모두 `tokens.json` key 앞에 `--eds-`를 붙인 이름입니다. doctor는 Tailwind class만, typecheck는 `cssVar()` 인자만 검사하므로 CSS·inline style의 변수는 직접 대조합니다.
113
+ - 새로 쓴 CSS variable이 `tokens.json`의 `cssVariable` mapping과 일치하는지 확인합니다. dotted public name은 `var(--eds-spacing-0\.5)`처럼 CSS reference에서만 escape합니다. doctor는 Tailwind class를, typecheck는 `cssVar()` 인자를 검사합니다.
98
114
  - typography는 preset 하나(`eds-typography-*` class 또는 같은 preset의 변수 다섯 개)로 지정합니다.
@@ -10,9 +10,41 @@ const propertyNames = [
10
10
  ];
11
11
 
12
12
  export const fontSansStyleName = "body1";
13
+
14
+ function safeTailwindAlias(name) {
15
+ return "eds-" + name.replace(/\./g, "-");
16
+ }
17
+
18
+ function cssVariableForToken(manifest, name) {
19
+ const cssVariable = manifest.tokens?.[name]?.$extensions?.eds?.cssVariable;
20
+ if (typeof cssVariable === "string") {
21
+ return cssVariable;
22
+ }
23
+
24
+ return "--eds-" + name.replace(/\./g, "\\.");
25
+ }
26
+
27
+ function collectDimensionAliases(manifest, prefix) {
28
+ const aliases = new Map();
29
+
30
+ for (const name of Object.keys(manifest.tokens ?? {}).filter((entry) => entry.startsWith(prefix))) {
31
+ const alias = safeTailwindAlias(name);
32
+ const existing = aliases.get(alias);
33
+ if (existing !== undefined && existing !== name) {
34
+ throw new Error(`Tailwind alias collision: ${existing} and ${name} both map to ${alias}.`);
35
+ }
36
+
37
+ aliases.set(alias, name);
38
+ }
39
+
40
+ return [...aliases.entries()].sort(([left], [right]) => left.localeCompare(right));
41
+ }
42
+
13
43
  export function renderTailwindCss(manifest, version) {
14
44
  const tokens = Object.keys(manifest.tokens ?? {});
15
45
  const colorNames = tokens.filter((name) => name.startsWith("color-"));
46
+ const spacingAliases = collectDimensionAliases(manifest, "spacing-");
47
+ const radiusAliases = collectDimensionAliases(manifest, "radius-");
16
48
  const typography = new Map();
17
49
 
18
50
  for (const name of tokens.filter((entry) => entry.startsWith("typography-"))) {
@@ -40,7 +72,13 @@ export function renderTailwindCss(manifest, version) {
40
72
  }
41
73
 
42
74
  const colorAliases = colorNames
43
- .map((name) => ` --color-eds-${name.slice("color-".length)}: var(--eds-${name});`)
75
+ .map((name) => ` --color-eds-${name.slice("color-".length)}: var(${cssVariableForToken(manifest, name)});`)
76
+ .join("\n");
77
+ const spacingThemeAliases = spacingAliases
78
+ .map(([alias, name]) => ` --spacing-${alias}: var(${cssVariableForToken(manifest, name)});`)
79
+ .join("\n");
80
+ const radiusThemeAliases = radiusAliases
81
+ .map(([alias, name]) => ` --radius-${alias}: var(${cssVariableForToken(manifest, name)});`)
44
82
  .join("\n");
45
83
  // Last line of the @theme block: Tailwind's default font family becomes the EDS body font.
46
84
  const fontSans = ` --font-sans: var(--eds-typography-${fontSansStyleName}-fontFamily);`;
@@ -58,6 +96,8 @@ export function renderTailwindCss(manifest, version) {
58
96
  `/* Generated from @spectrabrain/eds@${version}. Do not edit manually. */`,
59
97
  "@theme inline {",
60
98
  colorAliases,
99
+ spacingThemeAliases,
100
+ radiusThemeAliases,
61
101
  fontSans,
62
102
  "}",
63
103
  "",
@@ -10,6 +10,9 @@ const opacity = "(?:/[0-9]{1,3})?";
10
10
 
11
11
  const edsColorClass = new RegExp(boundaryBefore + variantPrefix + colorUtility + "-eds-([A-Za-z0-9-]+)" + opacity + boundaryAfter, "g");
12
12
  const edsTypographyClass = new RegExp(boundaryBefore + variantPrefix + "eds-typography-([A-Za-z0-9-]+)" + boundaryAfter, "g");
13
+ const dimensionUtility = "(?:p|px|py|ps|pe|pt|pr|pb|pl|m|mx|my|ms|me|mt|mr|mb|ml|gap|gap-x|gap-y|space-x|space-y|inset|inset-x|inset-y|start|end|top|right|bottom|left|w|min-w|max-w|h|min-h|max-h|size|basis|scroll-m|scroll-p)";
14
+ const edsSpacingClass = new RegExp(boundaryBefore + variantPrefix + dimensionUtility + "-eds-(spacing-[A-Za-z0-9-]+)" + boundaryAfter, "g");
15
+ const edsRadiusClass = new RegExp(boundaryBefore + variantPrefix + "rounded(?:-(?:s|e|t|r|b|l|ss|se|ee|es|tl|tr|br|bl))?-eds-(radius-[A-Za-z0-9-]+)" + boundaryAfter, "g");
13
16
  const rawColorClass = new RegExp(
14
17
  boundaryBefore + variantPrefix + colorUtility +
15
18
  "-(?:(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-[0-9]{2,3}|white|black|\\[(?:#|rgba?\\(|hsla?\\(|oklch\\(|oklab\\(|color\\()[^\\]]*\\])" +
@@ -21,6 +24,7 @@ const rawColorClass = new RegExp(
21
24
  export function publicClassNames(manifest) {
22
25
  const colors = new Set();
23
26
  const typography = new Set();
27
+ const dimensions = new Set();
24
28
  for (const name of Object.keys(manifest.tokens ?? {})) {
25
29
  if (name.startsWith("color-")) {
26
30
  colors.add("eds-" + name.slice("color-".length));
@@ -29,16 +33,20 @@ export function publicClassNames(manifest) {
29
33
  const match = name.match(/^typography-(.+)-(?:fontFamily|fontWeight|fontSize|lineHeight|letterSpacing)$/);
30
34
  if (match) {
31
35
  typography.add(match[1]);
36
+ continue;
37
+ }
38
+ if (name.startsWith("spacing-") || name.startsWith("radius-")) {
39
+ dimensions.add("eds-" + name.replace(/\./g, "-"));
32
40
  }
33
41
  }
34
- return { colors, typography };
42
+ return { colors, typography, dimensions };
35
43
  }
36
44
 
37
45
  // files: [{ path, contents }] as readSources() lists them (the generated eds-tokens.css is not among them).
38
46
  // unknown: eds-* classes that no public token backs (always a mistake, Tailwind silently drops them).
39
47
  // raw: Tailwind palette, white/black and arbitrary color classes that bypass the EDS tokens (informational).
40
48
  export function scanClassNames({ consumerRoot, manifest, files }) {
41
- const { colors, typography } = publicClassNames(manifest);
49
+ const { colors, typography, dimensions } = publicClassNames(manifest);
42
50
  const unknown = [];
43
51
  const raw = [];
44
52
  for (const file of files) {
@@ -62,6 +70,16 @@ export function scanClassNames({ consumerRoot, manifest, files }) {
62
70
  unknown.push({ location, className: match[0] });
63
71
  }
64
72
  }
73
+ for (const match of line.matchAll(edsSpacingClass)) {
74
+ if (match[1] !== undefined && !dimensions.has("eds-" + match[1])) {
75
+ unknown.push({ location, className: match[0] });
76
+ }
77
+ }
78
+ for (const match of line.matchAll(edsRadiusClass)) {
79
+ if (match[1] !== undefined && !dimensions.has("eds-" + match[1])) {
80
+ unknown.push({ location, className: match[0] });
81
+ }
82
+ }
65
83
  for (const match of line.matchAll(rawColorClass)) {
66
84
  raw.push({ location, className: match[0] });
67
85
  }
@@ -128,6 +128,6 @@ Layout은 prop으로 지정하지 않고 슬롯 조합에서 추론합니다.
128
128
 
129
129
  ## 문서와 검증
130
130
 
131
- Button의 사용 기준은 이 문서이며, 같은 폴더의 `spec.json`은 같은 계약을 도구가 읽는 형식으로 담습니다(이름으로 쓰는 아이콘 목록은 `api.iconNames`). 필요한 prop·variant·상태가 없으면 우회하지 말고 멈춰 사용자에게 확인합니다.
131
+ Button의 사용 기준은 이 문서이며, 같은 폴더의 `spec.json`은 같은 계약을 도구가 읽는 형식으로 담습니다(이름으로 쓰는 아이콘 목록은 `api.iconNames`). 필요한 prop·variant·상태가 없으면 Button을 임의로 확장하지 않고 소비처 로컬 UI 정책(`QUICKSTART.md`)으로 분기합니다.
132
132
 
133
133
  기존 코드를 한 번에 교체하지 않고 요청 범위에서 새로 쓰거나 고치는 코드부터 EDS `Button`을 적용합니다. 저장소에 있던 로컬 Button wrapper는 EDS `Button` contract로 표현할 수 없는 기능이 있을 때만 유지합니다.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "specVersion": 1,
3
- "packageVersion": "0.1.1",
3
+ "packageVersion": "0.2.0",
4
4
  "name": "Button",
5
5
  "purpose": "사용자가 즉시 실행할 수 있는 행동과 탐색을 표현하는 인터랙티브 요소입니다.",
6
6
  "usageContexts": [