@pylerai/design-system 1.2.3 → 2.0.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.
Files changed (50) hide show
  1. package/README.md +169 -316
  2. package/dist/components/ui/badge.d.ts +2 -1
  3. package/dist/components/ui/badge.js +49 -0
  4. package/dist/components/ui/body.js +52 -0
  5. package/dist/components/ui/button.d.ts +2 -1
  6. package/dist/components/ui/button.js +172 -0
  7. package/dist/components/ui/calendar.js +275 -0
  8. package/dist/components/ui/checkbox.d.ts +7 -0
  9. package/dist/components/ui/checkbox.js +45 -0
  10. package/dist/components/ui/chip.d.ts +4 -3
  11. package/dist/components/ui/chip.js +66 -0
  12. package/dist/components/ui/dropdown.d.ts +8 -2
  13. package/dist/components/ui/dropdown.js +256 -0
  14. package/dist/components/ui/headline.js +50 -0
  15. package/dist/components/ui/input.d.ts +3 -2
  16. package/dist/components/ui/input.js +129 -0
  17. package/dist/components/ui/label.js +43 -0
  18. package/dist/components/ui/loading-bar.d.ts +2 -1
  19. package/dist/components/ui/loading-bar.js +64 -0
  20. package/dist/components/ui/modal.d.ts +3 -2
  21. package/dist/components/ui/modal.js +170 -0
  22. package/dist/components/ui/pagination.d.ts +2 -0
  23. package/dist/components/ui/pagination.js +106 -0
  24. package/dist/components/ui/radio.d.ts +7 -0
  25. package/dist/components/ui/radio.js +42 -0
  26. package/dist/components/ui/search-field.d.ts +3 -2
  27. package/dist/components/ui/search-field.js +98 -0
  28. package/dist/components/ui/table.js +206 -0
  29. package/dist/components/ui/tabs.js +149 -0
  30. package/dist/components/ui/tag.d.ts +2 -1
  31. package/dist/components/ui/tag.js +31 -0
  32. package/dist/components/ui/tips.d.ts +2 -1
  33. package/dist/components/ui/tips.js +25 -0
  34. package/dist/components/ui/toast.js +70 -0
  35. package/dist/components/ui/toggle.d.ts +1 -2
  36. package/dist/components/ui/toggle.js +41 -0
  37. package/dist/components/ui/use-modal.js +94 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +75 -5769
  40. package/dist/lib/icons.js +225 -0
  41. package/dist/lib/size.d.ts +8 -0
  42. package/dist/lib/use-dialog-a11y.d.ts +2 -0
  43. package/dist/lib/use-dialog-a11y.js +50 -0
  44. package/dist/lib/utils.js +16 -0
  45. package/dist/style.css +1 -1
  46. package/dist/tokens/pyler-light.js +134 -0
  47. package/dist/tokens/typography.js +84 -0
  48. package/package.json +14 -1
  49. package/pyler-tailwind-colors.cjs +4 -0
  50. package/dist/index.css.d.ts +0 -0
package/README.md CHANGED
@@ -1,120 +1,51 @@
1
1
  # @pylerai/design-system
2
2
 
3
- Pyler Frontend를 위한 디자인 시스템 패키지 (Tailwind CSS + shadcn/ui 기반)
3
+ Pyler Frontend를 위한 디자인 시스템 패키지 (React + TypeScript + Tailwind CSS)
4
4
 
5
- ## 📦 패키지 정보
6
-
7
- - **패키지명**: `@pylerai/design-system`
8
- - **버전**: ![npm version](https://img.shields.io/npm/v/@pylerai/design-system)
9
- - **라이선스**: ISC
10
- - **레지스트리**: npm (registry.npmjs.org, public)
11
- - **기술 스택**: React + TypeScript + Tailwind CSS + shadcn/ui + Storybook + Vitest
5
+ 📖 **[Storybook](https://pylerai.github.io/design-system/)** — 모든 컴포넌트의 렌더링 결과와 전체 prop 문서는 여기서 확인하세요.
12
6
 
13
- ## 🚀 설치 및 사용
7
+ > 이 README는 Storybook이 알려줄 수 없는 것(설치·Tailwind 연동·토큰 사용법·릴리스 절차)만 다룹니다.
8
+ > 컴포넌트별 prop 목록은 Storybook autodocs가 소스에서 자동 생성하므로 여기에 중복해 적지 않습니다 —
9
+ > 같은 내용을 두 곳에 두면 한쪽이 반드시 낡습니다.
14
10
 
15
- ### 다른 프로젝트에서 사용하기
11
+ ## 📦 패키지 정보
16
12
 
17
- 다른 레포지토리에서 이 디자인 시스템을 사용하려면 다음 단계를 따라하세요:
13
+ - **패키지명**: `@pylerai/design-system` ![npm version](https://img.shields.io/npm/v/@pylerai/design-system)
14
+ - **레지스트리**: public npm (`registry.npmjs.org`) — 별도 인증 불필요
15
+ - **라이선스**: ISC
16
+ - **peer 의존성**: React 18 또는 19
18
17
 
19
- #### 1. 패키지 설치
18
+ > [CHANGELOG](./CHANGELOG.md) · [CONTRIBUTING](./CONTRIBUTING.md)
20
19
 
21
- public npm 레지스트리에 배포되어 있으므로 별도 인증 없이 바로 설치할 수 있습니다.
20
+ ## 🚀 설치
22
21
 
23
22
  ```bash
24
23
  npm install @pylerai/design-system
25
24
  ```
26
25
 
27
- > [CHANGELOG](./CHANGELOG.md) · [CONTRIBUTING](./CONTRIBUTING.md)
26
+ ### 1. 스타일 로드
28
27
 
29
- #### 2. Tailwind CSS 설정
28
+ 앱 진입점에서 한 번만 로드합니다.
30
29
 
31
- 사용할 프로젝트의 `tailwind.config.js` / `tailwind.config.cjs` 파일에 다음을 추가하세요:
32
-
33
- ```js
34
- module.exports = {
35
- content: [
36
- "./src/**/*.{js,jsx,ts,tsx}",
37
- "./node_modules/@pylerai/design-system/**/*.{js,ts,jsx,tsx}", // 추가
38
- ],
39
- // ...
40
- };
30
+ ```typescript
31
+ import "@pylerai/design-system/style.css";
41
32
  ```
42
33
 
43
- 그리고 CSS 파일에 다음을 추가하세요:
34
+ CSS 파일에서 `@import` 해도 동일합니다.
44
35
 
45
36
  ```css
46
37
  @tailwind base;
47
38
  @tailwind components;
48
39
  @tailwind utilities;
49
40
 
50
- /* 디자인 시스템 스타일 (컴포넌트 + Pyler 컬러 변수 등) */
51
41
  @import "@pylerai/design-system/style.css";
52
42
  ```
53
43
 
54
- 앱 진입 TS/JS에서 한 번 로드하는 방식도 동일합니다.
55
-
56
- ```typescript
57
- import "@pylerai/design-system/style.css";
58
- ```
59
-
60
- > 구버전 번들러에서 서브패스가 동작하지 않으면 `@import "@pylerai/design-system/dist/style.css";` 또는 `import "@pylerai/design-system/dist/style.css";` 를 사용하세요.
61
-
62
- #### 3. 사용하기
63
-
64
- ```typescript
65
- import { Button } from "@pylerai/design-system";
66
-
67
- function App() {
68
- return (
69
- <div className="p-4">
70
- <Button>클릭하세요</Button>
71
- <Button variant="outline">외곽선</Button>
72
- <Button variant="destructive">삭제</Button>
73
- </div>
74
- );
75
- }
76
- ```
77
-
78
- ### 컬러 토큰 (Pyler Light)
79
-
80
- Figma Light 모드와 동일한 HEX를 **TypeScript 상수**, **CSS 변수 (`--ds-*`)**, **Tailwind `ds` 팔레트**로 제공합니다. 앱 코드에 HEX를 직접 쓰지 말고 패키지 토큰을 참조하세요.
81
-
82
- #### TypeScript에서 HEX·경로 사용
83
-
84
- ```typescript
85
- import {
86
- PYLER_LIGHT_COLOR_TOKENS,
87
- flatPylerLightTokens,
88
- getDsCssVar,
89
- } from "@pylerai/design-system";
90
-
91
- // 중첩 객체
92
- const strong = PYLER_LIGHT_COLOR_TOKENS.text.strong.hex; // "#333333"
93
-
94
- // 순회·문서화용 플랫 리스트
95
- flatPylerLightTokens.forEach(({ path, hex, cssVar }) => {
96
- console.log(path, hex, cssVar);
97
- });
98
-
99
- // CSS 변수 이름
100
- getDsCssVar("text.strong"); // => "--ds-text-strong"
101
- ```
102
-
103
- #### CSS 변수
104
-
105
- `style.css`를 로드한 뒤 임의 요소에 적용합니다.
106
-
107
- ```css
108
- .my-title {
109
- color: var(--ds-text-strong);
110
- border-color: var(--ds-line-strong);
111
- background-color: var(--ds-surface-normal);
112
- }
113
- ```
44
+ > 구버전 번들러에서 서브패스가 동작하지 않으면 `@pylerai/design-system/dist/style.css`를 사용하세요.
114
45
 
115
- #### Tailwind에서 `ds` 색상
46
+ ### 2. Tailwind 연동
116
47
 
117
- 1. **프리셋 사용 (권장)** — `theme.extend.colors.ds`가 자동으로 합쳐집니다.
48
+ 프리셋을 사용하면 `ds` 컬러 팔레트·타이포그래피 스케일·radius·shadow가 한 번에 합쳐집니다.
118
49
 
119
50
  ```javascript
120
51
  // tailwind.config.cjs
@@ -126,303 +57,225 @@ module.exports = {
126
57
  ],
127
58
  theme: {
128
59
  extend: {
129
- /* 기존 앱 전용 확장 */
60
+ /* 앱 전용 확장 */
130
61
  },
131
62
  },
132
63
  };
133
64
  ```
134
65
 
135
- 2. **색상 맵만 병합** — 이미 `extend.colors`를 쓰는 경우:
66
+ 이미 `extend.colors`를 쓰고 있어 색상 맵만 필요하다면:
136
67
 
137
68
  ```javascript
138
69
  const { dsColors } = require("@pylerai/design-system/pyler-tailwind-colors");
139
70
 
140
71
  module.exports = {
141
- theme: {
142
- extend: {
143
- colors: {
144
- ds: dsColors,
145
- },
146
- },
147
- },
72
+ theme: { extend: { colors: { ds: dsColors } } },
148
73
  };
149
74
  ```
150
75
 
151
- 유틸 클래스 예: `bg-ds-primary-blue-normal`, `text-ds-text-strong`, `border-ds-line-normal`.
76
+ 클래스명을 `bg-ds-${color}`처럼 **동적으로 조합**하면 Tailwind의 content 스캔에 잡히지 않습니다. 그런 경우 앱 config에 safelist를 두세요.
152
77
 
153
- 동적으로 `bg-ds-*`만 조합하는 경우 Tailwind가 클래스를 빼버릴 수 있으므로, 소비 앱 `tailwind.config`에 `safelist: [{ pattern: /^bg-ds-/ }]`를 두는 것을 권장합니다.
78
+ ```javascript
79
+ safelist: [{ pattern: /^bg-ds-/ }],
80
+ ```
154
81
 
155
- 스토리북 **Design Tokens → Color Tokens**에서 전체 스와치와 HEX를 확인할 수 있습니다.
82
+ ### 3. 사용
156
83
 
157
- ## 🎨 컴포넌트
84
+ ```tsx
85
+ import { Button, InputField, Toaster, toast } from "@pylerai/design-system";
158
86
 
159
- ### Typography
87
+ function App() {
88
+ return (
89
+ <div className="flex flex-col gap-4 p-4">
90
+ <InputField label="이메일" placeholder="you@pyler.tech" require />
91
+ <Button variant="filled" color="blue" size="lg" onClick={() => toast.success("저장했습니다")}>
92
+ 저장
93
+ </Button>
94
+ <Toaster />
95
+ </div>
96
+ );
97
+ }
98
+ ```
160
99
 
161
- 일관된 타이포그래피를 위한 컴포넌트들입니다.
100
+ ## 🧩 컴포넌트
162
101
 
163
- #### Headline
102
+ 전체 목록입니다. **각 컴포넌트의 prop과 실제 렌더링은 [Storybook](https://pylerai.github.io/design-system/)에서 확인하세요.**
164
103
 
165
- 페이지와 섹션의 제목을 위한 컴포넌트입니다.
104
+ | 카테고리 | export |
105
+ |---|---|
106
+ | Action | `Button`, `ButtonIndicator`, `buttonVariants` |
107
+ | Form | `InputField`, `TextField`, `SearchField`, `Dropdown`, `Checkbox`, `Radio`, `Toggle`, `Calendar` |
108
+ | Data display | `Table`, `TableRoot`, `TableHead`, `TableBody`, `TableRow`, `TableHeaderCell`, `TableCell`, `Pagination` |
109
+ | Status | `Badge`, `Tag`, `Chip`, `Tips`, `LoadingBar` |
110
+ | Feedback | `Toaster`, `toast`, `snackbar`, `SnackbarContent` |
111
+ | Overlay | `InformationModal`, `ActionModal`, `ContentModal`, `ModalProvider`, `useModal` |
112
+ | Navigation | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` |
113
+ | Typography | `Headline`, `Body`, `Label` (+ `headlineVariants`, `bodyVariants`, `labelVariants`) |
114
+ | Types | `DsSize` (전역 사이즈 스케일) |
115
+ | Tokens | `PYLER_LIGHT_COLOR_TOKENS`, `flatPylerLightTokens`, `PYLER_LIGHT_GROUP_ORDER`, `getDsCssVar`, `formatPylerPathForDisplay`, `PYLER_TYPOGRAPHY_STYLE` |
166
116
 
167
- ```typescript
168
- import { Headline } from '@pylerai/design-system';
169
-
170
- // 기본 사용
171
- <Headline>페이지 제목</Headline>
172
-
173
- // Variant
174
- <Headline variant="h1-20">페이지/섹션 최상위 제목</Headline>
175
- <Headline variant="h1-20-m">보조 헤드라인</Headline>
176
- <Headline variant="h1-20-r">큰 본문/설명</Headline>
177
- <Headline variant="h2-18">섹션 제목</Headline>
178
- <Headline variant="h2-18-sb">섹션 보조 제목</Headline>
179
- <Headline variant="h2-18-r">크고 차분한 본문</Headline>
180
-
181
- // HTML 태그 지정
182
- <Headline as="h1">H1 태그로 렌더링</Headline>
183
- <Headline as="h2">H2 태그로 렌더링</Headline>
184
- ```
117
+ ### 알아두면 좋은 것
185
118
 
186
- #### Body
119
+ - **`size`는 전 컴포넌트가 `xs | sm | md | lg | xl` 스케일(`DsSize`)을 씁니다.** 컴포넌트마다 지원 범위는 다릅니다 — `Button`/`LoadingBar` `sm~xl`, `InputField`/`SearchField`/`Dropdown`/`ContentModal` `sm~lg`, `Badge`/`Chip`/`Tips` `sm~md`, `Tag` `xs~md`.
120
+ - **`Checkbox`·`Radio`는 라벨 텍스트를 children으로 받습니다** (`<Checkbox>동의</Checkbox>`). 밖에서 `<label>`로 감싸면 label이 중첩됩니다.
121
+ - **`Checkbox`·`Radio`·`Toggle`의 `onChange`는 네이티브 이벤트가 아니라 값을 직접 넘깁니다** (`(checked: boolean) => void`, Radio는 `(value: string) => void`). `react-hook-form`의 `register()` 스프레드와는 그대로 맞물리지 않으니 `Controller`를 쓰세요.
122
+ - **`Checkbox`·`Radio`·`Toggle`·`Dropdown`은 제어 컴포넌트 전용**입니다(`defaultChecked`/`defaultValue` 미지원). `Tabs`만 제어·비제어를 모두 지원합니다.
123
+ - **아이콘은 아직 export되지 않습니다.** Storybook `Foundation/Icon`에서 목록은 볼 수 있지만 패키지에서 import할 수는 없습니다. `Button`의 `leftIcon`/`rightIcon`에는 앱 쪽 아이콘을 넘겨야 합니다.
187
124
 
188
- 본문 텍스트를 위한 컴포넌트입니다.
125
+ ## 🎨 디자인 토큰
189
126
 
190
- ```typescript
191
- import { Body } from '@pylerai/design-system';
192
-
193
- // 기본 사용
194
- <Body>본문 텍스트입니다.</Body>
195
-
196
- // Variant
197
- <Body variant="b1-16">강조 본문</Body>
198
- <Body variant="b1-16-m">일반 본문(기본)</Body>
199
- <Body variant="b1-16-r">일반 본문(라이트)</Body>
200
- <Body variant="b2-14">작은 강조 본문</Body>
201
- <Body variant="b2-14-m">작은 본문/캡션</Body>
202
- <Body variant="b2-14-r">캡션/헬퍼텍스트</Body>
203
- <Body variant="b3-13-m">세부설명/테이블 셀</Body>
204
- <Body variant="b3-13-r">보조설명</Body>
205
- <Body variant="b4-12-m">메타/상태</Body>
206
- <Body variant="b4-12-r">아주 작은 본문</Body>
207
-
208
- // HTML 태그 지정
209
- <Body as="p">P 태그로 렌더링</Body>
210
- <Body as="div">DIV 태그로 렌더링</Body>
211
- ```
127
+ Figma Light 모드와 동일한 값을 **CSS 변수(`--ds-*`)**, **Tailwind `ds` 팔레트**, **TypeScript 상수** 세 가지 형태로 제공합니다. 앱 코드에 HEX를 직접 쓰지 말고 토큰을 참조하세요.
212
128
 
213
- #### Label
129
+ > 세 형태의 값이 어긋나지 않도록 `npm run check:tokens`가 CI에서 정합성을 검사합니다.
214
130
 
215
- 폼 요소나 작은 텍스트 라벨을 위한 컴포넌트입니다.
131
+ ### 컬러
216
132
 
217
- ```typescript
218
- import { Label } from '@pylerai/design-system';
133
+ Tailwind 유틸리티가 가장 일반적입니다.
134
+
135
+ ```tsx
136
+ <div className="bg-ds-surface-normal border border-ds-line-normal text-ds-text-strong" />
137
+ ```
138
+
139
+ CSS 변수로도 쓸 수 있습니다.
219
140
 
220
- // 기본 사용
221
- <Label>라벨 텍스트</Label>
141
+ ```css
142
+ .my-title {
143
+ color: var(--ds-text-strong);
144
+ background-color: var(--ds-surface-normal);
145
+ }
146
+ ```
222
147
 
223
- // Variant
224
- <Label variant="l1-10-sb">라벨/태그</Label>
225
- <Label variant="l1-10-r">라벨(비강조)</Label>
148
+ TypeScript에서 HEX 값이나 변수명이 필요하면:
226
149
 
227
- // 폼과 함께 사용
228
- <Label htmlFor="email">이메일 주소</Label>
229
- <input id="email" type="email" />
150
+ ```typescript
151
+ import { PYLER_LIGHT_COLOR_TOKENS, flatPylerLightTokens, getDsCssVar } from "@pylerai/design-system";
152
+
153
+ PYLER_LIGHT_COLOR_TOKENS.text.strong.hex; // "#333333"
154
+ getDsCssVar("text.strong"); // "--ds-text-strong"
155
+ flatPylerLightTokens.forEach(({ path, hex, cssVar }) => { /* 문서화·순회용 */ });
230
156
  ```
231
157
 
232
- ### Button
158
+ 토큰 그룹: `primitive` · `main` · `background` · `text` · `line` · `surface` · `primary`(blue/red/green × normal/hovered/disabled/muted) · `category` · `icon`.
159
+ 전체 스와치와 HEX는 Storybook **Foundation → Color**에서 확인하세요.
160
+
161
+ ### 타이포그래피
162
+
163
+ | 스케일 | 크기 | 굵기 | Tailwind | 순수 CSS 클래스 |
164
+ |---|---|---|---|---|
165
+ | Title 1 SB | 24px | 600 | `text-title-1-sb` | `.ds-type-title-1-sb` |
166
+ | Title 1 M | 24px | 500 | `text-title-1-m` | `.ds-type-title-1-m` |
167
+ | Title 2 SB | 20px | 600 | `text-title-2-sb` | `.ds-type-title-2-sb` |
168
+ | Body 1 M | 16px | 500 | `text-body-1-m` | `.ds-type-body-1-m` |
169
+ | Body 1 R | 16px | 400 | `text-body-1-r` | `.ds-type-body-1-r` |
170
+ | Body 2 SB | 14px | 600 | `text-body-2-sb` | `.ds-type-body-2-sb` |
171
+ | Body 2 M | 14px | 500 | `text-body-2-m` | `.ds-type-body-2-m` |
172
+ | Body 2 R | 14px | 400 | `text-body-2-r` | `.ds-type-body-2-r` |
173
+ | Body 3 M | 13px | 500 | `text-body-3-m` | `.ds-type-body-3-m` |
174
+ | Body 3 R | 13px | 400 | `text-body-3-r` | `.ds-type-body-3-r` |
175
+ | Body 4 M | 12px | 500 | `text-body-4-m` | `.ds-type-body-4-m` |
176
+ | Body 4 R | 12px | 400 | `text-body-4-r` | `.ds-type-body-4-r` |
177
+ | Label 1 R | 11px | 400 | `text-label-1-r` | `.ds-type-label-1-r` |
178
+
179
+ 폰트는 Pretendard JP(`--ds-font-sans`)를 사용합니다. 앱에서 웹폰트를 별도로 로드해야 합니다.
180
+
181
+ 컴포넌트로 쓸 수도 있습니다. `as`로 태그를, `asChild`로 자식 요소에 스타일만 입힐 수 있습니다.
182
+
183
+ ```tsx
184
+ <Headline variant="title-1-sb" as="h1">페이지 제목</Headline>
185
+ <Body variant="body-2-r">본문</Body>
186
+ <Label variant="label-1-r" htmlFor="email">이메일</Label>
187
+
188
+ <Headline asChild variant="title-2-sb">
189
+ <a href="/docs">래퍼 없이 링크에 타이틀 스타일만</a>
190
+ </Headline>
191
+ ```
233
192
 
234
- 다양한 variant와 size를 지원하는 버튼 컴포넌트입니다.
193
+ Chakra UI·Emotion 등 CSS-in-JS에서는 `PYLER_TYPOGRAPHY_STYLE`을 `textStyle`/`sx`에 그대로 넘기면 됩니다.
235
194
 
236
195
  ```typescript
237
- import { Button } from '@pylerai/design-system';
238
-
239
- // 기본 사용
240
- <Button>클릭하세요</Button>
241
-
242
- // Variant
243
- <Button variant="default">기본</Button>
244
- <Button variant="destructive">삭제</Button>
245
- <Button variant="outline">외곽선</Button>
246
- <Button variant="secondary">두 번째</Button>
247
- <Button variant="ghost">고스트</Button>
248
- <Button variant="link">링크</Button>
249
-
250
- // Size
251
- <Button size="sm">작은 버튼</Button>
252
- <Button size="default">기본</Button>
253
- <Button size="lg">큰 버튼</Button>
254
- <Button size="icon">🔍</Button>
255
-
256
- // 조합
257
- <Button variant="outline" size="lg">외곽선 큰 버튼</Button>
258
- ```
196
+ import { PYLER_TYPOGRAPHY_STYLE } from "@pylerai/design-system";
259
197
 
260
- ## 📏 타이포그래피 스케일
198
+ <Text sx={PYLER_TYPOGRAPHY_STYLE.body2R}>본문</Text>
199
+ ```
261
200
 
262
- ### 디자인 토큰
201
+ ### Radius · Shadow · Dim
263
202
 
264
- | Token | Size(px) | Weight | Tailwind class | 용도 |
265
- |-------|----------|--------|----------------|------|
266
- | H1-20B | 20 | 700 | text-h1-20 | 페이지/섹션 최상위 제목 |
267
- | H1-20M | 20 | 500 | text-h1-20-m | 보조 헤드라인 |
268
- | H1-20R | 20 | 400 | text-h1-20-r | 큰 본문/설명 |
269
- | H2-18B | 18 | 700 | text-h2-18 | 섹션 제목 |
270
- | H2-18SB | 18 | 600 | text-h2-18-sb | 섹션 보조 제목 |
271
- | H2-18R | 18 | 400 | text-h2-18-r | 크고 차분한 본문 |
272
- | B1-16B | 16 | 700 | text-b1-16 | 강조 본문 |
273
- | B1-16M | 16 | 500 | text-b1-16-m | 일반 본문(기본) |
274
- | B1-16R | 16 | 400 | text-b1-16-r | 일반 본문(라이트) |
275
- | B2-14B | 14 | 700 | text-b2-14 | 작은 강조 본문 |
276
- | B2-14M | 14 | 500 | text-b2-14-m | 작은 본문/캡션 |
277
- | B2-14R | 14 | 400 | text-b2-14-r | 캡션/헬퍼텍스트 |
278
- | B3-13M | 13 | 500 | text-b3-13-m | 세부설명/테이블 셀 |
279
- | B3-13R | 13 | 400 | text-b3-13-r | 보조설명 |
280
- | B4-12M | 12 | 500 | text-b4-12-m | 메타/상태 |
281
- | B4-12R | 12 | 400 | text-b4-12-r | 아주 작은 본문 |
282
- | L1-10SB | 10 | 600 | text-l1-10-sb | 라벨/태그 |
283
- | L1-10R | 10 | 400 | text-l1-10-r | 라벨(비강조) |
203
+ ```tsx
204
+ <div className="rounded-ds-md shadow-ds-default" /> // radius: xs(2) ss(4) md(8) lg(12)
205
+ // shadow: ds-default, ds-modal
206
+ ```
284
207
 
285
- ### 사용 가이드라인
208
+ Dim은 `var(--ds-dim-default)`로 제공됩니다. Storybook **Foundation → Radius / Shadow / Dim** 참고.
286
209
 
287
- #### ✅ 권장사항
210
+ ## ♿ 접근성
288
211
 
289
- - **계층 구조 유지**: H1 → H2 → H3 순서로 헤딩을 사용하고, 각 레벨의 용도를 일관되게 유지하세요.
290
- - **충분한 대비**: 텍스트와 배경 간 충분한 색상 대비를 확보하여 가독성을 보장하세요.
291
- - **적절한 라인 높이**: 본문 텍스트는 1.4~1.6의 라인 높이를 사용하여 가독성을 높이세요.
212
+ `@storybook/addon-a11y`(axe)가 모든 스토리에서 실행되며 **위반이 있으면 CI가 실패**합니다(`npm test`).
292
213
 
293
- #### ❌ 주의사항
214
+ 키보드 조작 요약:
294
215
 
295
- - **과도한 변형 사용**: 너무 많은 폰트 크기와 굵기를 혼용하면 일관성이 떨어집니다.
296
- - **너무 작은 텍스트**: 10px 이하의 텍스트는 가독성과 접근성 문제를 일으킬 수 있습니다.
297
- - **의미 없는 굵기 변경**: 단순히 강조를 위해 굵기를 변경하지 말고, 의미 있는 계층 구조를 사용하세요.
216
+ | 컴포넌트 | 키보드 |
217
+ |---|---|
218
+ | `Modal` | 포커스 트랩, 열 때 포커스 진입 / 닫을 때 복원, `Esc` 닫기, 스크롤 잠금. 모달이 쌓이면 `Esc`는 최상단만 닫습니다 |
219
+ | `Dropdown` | `↑`/`↓`/`Home`/`End` 이동, `Enter`/`Space` 선택, `Esc` 닫기 |
220
+ | `Tabs` | `←`/`→`/`Home`/`End` 이동(선택 동시 적용), `Tab`은 탭 목록을 통과 |
221
+ | `Table` | 정렬 헤더에 포커스 이동 후 `Enter`/`Space`로 정렬, `aria-sort` 노출 |
298
222
 
299
- ### 접근성
223
+ 라벨이 필요한 컴포넌트:
300
224
 
301
- 모든 타이포그래피 컴포넌트는 WCAG 2.1 AA 기준을 준수합니다:
225
+ - `Checkbox`·`Radio`는 자체적으로 `<label>`을 렌더합니다. **밖에서 `<label>`로 감싸지 마세요** — label이 중첩되어 연결이 깨집니다. 텍스트는 children으로 넘기고(`<Checkbox>동의</Checkbox>`), 텍스트가 없으면 `aria-label`을 지정하세요.
226
+ - `Toggle`, 아이콘 전용 `Button`, 라벨 없는 `Dropdown`/`SearchField`에는 `aria-label`이 필요합니다.
302
227
 
303
- - **색상 대비**: 최소 4.5:1의 대비율 유지
304
- - **헤딩 계층**: 올바른 HTML 헤딩 태그 사용
305
- - **스크린 리더**: 의미 있는 텍스트와 라벨 제공
306
- - **키보드 네비게이션**: 포커스 표시 및 키보드 접근성 지원
228
+ > ⚠️ **컬러 대비는 아직 WCAG AA를 충족하지 못합니다.** `--ds-text-normal`(#969696)은 흰 배경에서 2.95:1, `--ds-text-disabled`(#cccccc)는 1.6:1, 흰 글자 + `--ds-primary-blue-normal`(#2c80f5)은 3.8:1로 기준(4.5:1)에 미달합니다. 토큰을 바꾸면 제품 전체 색이 바뀌므로 디자이너 결정이 필요한 사안이며, 그때까지 axe의 `color-contrast` 규칙만 꺼 둔 상태입니다(`.storybook/preview.ts`).
307
229
 
308
230
  ## 📁 프로젝트 구조
309
231
 
310
232
  ```
311
233
  design-system/
312
- ├── .storybook/
313
- │ ├── main.cjs # Storybook 메인 설정 (CJS — Vite/Storybook 호환)
314
- │ ├── preview.ts # Storybook 미리보기 설정
315
- │ └── vitest.setup.ts # Vitest 테스트 설정
316
- ├── .github/
317
- │ └── workflows/
318
- │ └── release-package.yml # GitHub Actions 릴리스 워크플로우
234
+ ├── .storybook/ # Storybook 설정 (main/preview/vitest.setup)
235
+ ├── .github/workflows/
236
+ │ ├── pr-checks.yml # PR 검증 (tokens → lint → tsc → test → build)
237
+ │ └── deploy-github-pages.yml # Storybook 배포
238
+ ├── scripts/
239
+ │ └── check-tokens.cjs # 토큰 정합성 검사
319
240
  ├── src/
320
- │ ├── components/
321
- │ │ ├── ui/
322
- │ │ │ ├── button.tsx # Button 컴포넌트 구현
323
- │ │ │ └── button.stories.tsx # Button Storybook 스토리
324
- │ │ └── Button.tsx # Button export
325
- │ ├── stories/ # Storybook 데모 컴포넌트
326
- │ │ ├── assets/ # 데모용 이미지/아이콘
327
- │ │ ├── Button.tsx # 데모 Button
328
- │ │ ├── Button.stories.ts # Button 스토리
329
- │ │ ├── Header.tsx # 데모 Header
330
- │ │ ├── Header.stories.ts # Header 스토리
331
- │ │ ├── Page.tsx # 데모 Page
332
- │ │ ├── Page.stories.ts # Page 스토리
333
- │ │ └── *.css # 데모 스타일
241
+ │ ├── components/ui/ # 컴포넌트 + *.stories.tsx (스토리가 곧 테스트)
334
242
  │ ├── lib/
335
- │ │ └── utils.ts # 유틸리티 함수 (cn, clsx)
336
- │ ├── index.css # Tailwind CSS 스타일
337
- │ ├── index.css.ts # CSS 타입 정의
243
+ │ │ ├── icons.tsx # 내부 아이콘 (아직 미export)
244
+ │ │ └── utils.ts # cn() — clsx + tailwind-merge
245
+ │ ├── tokens/
246
+ │ │ ├── pyler-light.ts # 컬러 토큰 (TS)
247
+ │ │ └── typography.ts # 타이포 토큰 (CSS-in-JS용)
248
+ │ ├── index.css # --ds-* CSS 변수 + 타이포 유틸리티 (실제 값)
338
249
  │ └── index.tsx # 패키지 진입점
339
- ├── components.json # shadcn/ui 설정
340
- ├── tailwind.config.cjs # Tailwind CSS 설정
341
- ├── postcss.config.js # PostCSS 설정
342
- ├── tsconfig.json # TypeScript 설정
343
- ├── tsconfig.node.json # TypeScript Node 설정
344
- ├── vite.config.ts # Vite 설정
345
- ├── vitest.shims.d.ts # Vitest 타입 정의
346
- └── package.json # 패키지 정보
250
+ ├── pyler-tailwind-colors.cjs # Tailwind ds 컬러 맵
251
+ ├── pyler-typography.cjs # Tailwind 타이포 스케일
252
+ ├── tailwind-preset.cjs # 소비 앱용 프리셋
253
+ └── eslint.config.js
347
254
  ```
348
255
 
349
- ### 주요 디렉토리 설명
350
-
351
- - **`src/components/`**: 실제 디자인 시스템 컴포넌트들이 위치
352
-
353
- - `ui/`: shadcn/ui 기반 컴포넌트 구현
354
- - 컴포넌트 export 파일들
355
-
356
- - **`src/stories/`**: Storybook 데모 및 예제 컴포넌트들
357
-
358
- - 실제 패키지에는 포함되지 않으며 개발/테스트 용도
359
- - Button, Header, Page 등의 데모 컴포넌트
360
-
361
- - **`src/lib/`**: 공통 유틸리티 함수
362
-
363
- - `utils.ts`: class-variance-authority, clsx, tailwind-merge를 사용한 `cn` 함수
364
-
365
- - **`.storybook/`**: Storybook 설정 파일
366
- - Vitest 통합
367
- - addons: a11y, docs, onboarding, vitest
368
-
369
256
  ## 🔧 개발
370
257
 
371
- ### 로컬 개발
372
-
373
258
  ```bash
374
- # 의존성 설치
375
259
  npm install
376
-
377
- # 개발 서버 실행 (테스트용)
378
- npm run dev
379
-
380
- # 빌드
260
+ npm run storybook # http://localhost:6006
261
+ npm test # 스토리의 play 함수를 vitest로 실행
262
+ npm run lint
263
+ npm run check:tokens
381
264
  npm run build
382
-
383
- # 테스트 실행
384
- npm test
385
265
  ```
386
266
 
387
- ### Storybook 개발
388
-
389
- 컴포넌트를 개발하고 테스트하기 위해 Storybook을 사용할 수 있습니다.
267
+ 이 저장소는 **별도 테스트 파일을 두지 않고 Storybook 스토리의 `play` 함수를 테스트로 실행**합니다(`@storybook/addon-vitest`). 스토리를 추가하면 그 자체로 렌더 스모크 테스트가 되고, 핵심 상호작용에는 `play`를 작성합니다.
390
268
 
391
- ```bash
392
- # Storybook 개발 서버 실행
393
- npm run storybook
394
-
395
- # Storybook 빌드
396
- npm run build-storybook
397
- ```
398
-
399
- Storybook은 `http://localhost:6006`에서 실행됩니다.
400
-
401
- 컴포넌트 스토리는 `src/components/**/*.stories.tsx` 경로에 생성됩니다.
269
+ 새 컴포넌트를 추가하는 순서와 PR 절차는 [CONTRIBUTING](./CONTRIBUTING.md)을 참고하세요.
402
270
 
403
271
  ## 📤 배포
404
272
 
405
- 이 프로젝트는 `npm run publish:npm` 실행을 통해 public npm 레지스트리(`registry.npmjs.org`)로 배포됩니다.
406
-
407
- ### 배포 프로세스
408
-
409
- 1. **버전 업데이트**: `package.json`의 `version`을 올립니다.
410
- 2. **배포 실행**:
273
+ `main`은 브랜치 보호로 직접 push할 수 없습니다. 버전 릴리스 절차(버전 범프 → CHANGELOG → 태그 → `npm run publish:npm`)는 [CONTRIBUTING](./CONTRIBUTING.md)에 정리돼 있습니다.
411
274
 
412
- ```bash
413
- npm run publish:npm
414
- ```
415
-
416
- 이 스크립트는 `npm run build`로 패키지를 빌드한 뒤, `npm publish --access public`으로 `@pylerai/design-system`을 public 스코프로 퍼블리시합니다.
417
-
418
- > ⚠️ `.github/workflows/release-package.yml`은 GitHub Release가 생성될 때 트리거되어 GitHub Packages(`npm.pkg.github.com`)로도 별도 퍼블리시를 시도하는 워크플로우입니다. 현재 실제 배포는 위 수동 `npm run publish:npm` 경로로만 이루어지고 있어 이 워크플로우와 실제 배포 방식이 어긋나 있습니다. GitHub Release를 생성하면 의도치 않게 GitHub Packages 쪽에도 배포가 실행될 수 있으니 주의하세요 (워크플로우 자체 정리는 별도 후속 작업 — [CONTRIBUTING](./CONTRIBUTING.md) 참고).
275
+ > ⚠️ `.github/workflows/release-package.yml`은 GitHub Release 생성 시 GitHub Packages로 배포하도록 되어 있으나, 실제 배포는 public npm으로 수동 실행하는 방식입니다. 한 번도 실행된 적 없는 워크플로이며 정리 대상입니다. 자세한 경위는 [CONTRIBUTING](./CONTRIBUTING.md#알려진-이슈-release-packageyml-워크플로-범위-외) 참고.
419
276
 
420
277
  ## 🔗 링크
421
278
 
279
+ - **Storybook**: https://pylerai.github.io/design-system/
422
280
  - **Repository**: https://github.com/pylerAI/design-system
423
281
  - **Issues**: https://github.com/pylerAI/design-system/issues
424
- - **Homepage**: https://github.com/pylerAI/design-system#readme
425
-
426
- ## 📄 라이선스
427
-
428
- ISC
@@ -1,6 +1,7 @@
1
+ import { DsSize } from '../../lib/size';
1
2
  import * as React from "react";
2
3
  export type BadgeColor = "normal" | "neutral" | "negative" | "positive";
3
- export type BadgeSize = "medium" | "small";
4
+ export type BadgeSize = Extract<DsSize, "md" | "sm">;
4
5
  export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
5
6
  color?: BadgeColor;
6
7
  size?: BadgeSize;
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx as x } from "react/jsx-runtime";
3
+ import { cn as i } from "../../lib/utils.js";
4
+ import * as l from "react";
5
+ const b = {
6
+ normal: {
7
+ border: "border-ds-primary-blue-normal",
8
+ text: "text-ds-primary-blue-normal"
9
+ },
10
+ neutral: {
11
+ border: "border-ds-line-strong",
12
+ text: "text-ds-text-strong"
13
+ },
14
+ negative: {
15
+ border: "border-ds-primary-red-normal",
16
+ text: "text-ds-primary-red-normal"
17
+ },
18
+ positive: {
19
+ border: "border-ds-primary-green-normal",
20
+ text: "text-ds-primary-green-normal"
21
+ }
22
+ }, c = {
23
+ md: { padding: "px-2 pt-[5px] pb-[3px]", text: "text-body-3-m" },
24
+ sm: { padding: "px-1 pt-[3px] pb-[1px]", text: "text-label-1-r" }
25
+ }, g = l.forwardRef(
26
+ ({ color: r = "normal", size: e = "md", className: t, children: o, ...d }, a) => {
27
+ const { border: s, text: n } = b[r], { padding: p, text: m } = c[e];
28
+ return /* @__PURE__ */ x(
29
+ "span",
30
+ {
31
+ ref: a,
32
+ className: i(
33
+ "inline-flex items-center justify-center border rounded-[4px] bg-ds-background-default whitespace-nowrap",
34
+ s,
35
+ p,
36
+ n,
37
+ m,
38
+ t
39
+ ),
40
+ ...d,
41
+ children: o
42
+ }
43
+ );
44
+ }
45
+ );
46
+ g.displayName = "Badge";
47
+ export {
48
+ g as Badge
49
+ };