@moyeorak/design-system 0.1.3 → 0.3.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/README.md CHANGED
@@ -1,13 +1,27 @@
1
1
  # @moyeorak/design-system
2
2
 
3
- moyeorak 프로젝트를 위한 React 컴포넌트 디자인 시스템.
3
+ ## 프로젝트 소개
4
+
5
+ 실 서비스인 모여락을 먼저 개발한 뒤 시작된 프로젝트로, 함께 작업한 디자이너와 함께 디자인 토큰과 컴포넌트를 체계적으로 문서화해 디자이너-개발자 간 커뮤니케이션 비용을 줄이고 서비스 전반의 디자인 일관성을 보장하고자 별도 프로젝트로 시작했습니다.
6
+
7
+ ### 기술적 의사결정
8
+
9
+ - **Panda CSS** — 빌드타임에 정적 CSS를 추출하는 zero-runtime CSS-in-JS로, 토큰을 타입 안전하게 참조할 수 있어요.
10
+ - **Radix UI** — 복잡한 동작이나 합성이 필요한 곳에 선택적으로 헤드리스 프리미티브를 적용해요. 합성에는 `Slot`(Button), 선택 상태에는 `Toggle`(Chip), 일시적 알림에는 `Toast`를 써요. 단순 표시용 컴포넌트는 Panda CSS만으로 구현해요.
11
+ - **Tailwind 동시 지원** — 소비 프로젝트가 반드시 Panda CSS를 쓰지 않아도 동일한 디자인 토큰을 쓸 수 있도록 별도의 `tailwind-plugin` 진입점을 제공해요.
12
+
13
+ ### 컴포넌트 / 토큰 문서
14
+
15
+ 전체 컴포넌트 목록, 디자인 토큰, 사용 예제는 Storybook에서 확인할 수 있어요.
16
+
17
+ → [Storybook 바로가기](https://main--69cf7bceba8267c1b5d86ba6.chromatic.com/?path=/docs/foundations-colors--docs)
4
18
 
5
19
  ## 기술 스택
6
20
 
7
21
  | 역할 | 기술 |
8
22
  |------|------|
9
23
  | 프레임워크 | React 19 + TypeScript |
10
- | 스타일링 | Panda CSS |
24
+ | 스타일링 | Panda CSS (+ Tailwind CSS 플러그인 지원) |
11
25
  | 헤드리스 UI | Radix UI |
12
26
  | 문서화 | Storybook 8 |
13
27
  | 빌드 | Vite 6 |
@@ -26,20 +40,19 @@ bun install
26
40
  | `bun run build` | 라이브러리 빌드 (`panda codegen` 포함) |
27
41
  | `bun run dev` | watch 모드로 빌드 |
28
42
  | `bun run storybook` | Storybook 개발 서버 실행 (포트 6006) |
29
- | `bun run build-storybook` | Storybook 정적 빌드 |
30
- | `bun run prepare` | Panda CSS 코드 생성 |
31
- | `bun run typecheck` | TypeScript 타입 검사 |
32
43
  | `bun run test` | 테스트 실행 |
33
- | `bun run test:watch` | watch 모드로 테스트 실행 |
44
+
45
+ 그 외 스크립트(`lint`, `format`, `chromatic` 등)는 `package.json`을 참고하세요.
34
46
 
35
47
  ## 프로젝트 구조
36
48
 
37
49
  ```
38
50
  moyeorakui/
39
51
  ├── src/
40
- │ ├── components/ # 컴포넌트 (앞으로 추가될 위치)
52
+ │ ├── components/ # 컴포넌트 (안정화된 것만 src/index.ts에서 export)
41
53
  │ ├── lib/
42
54
  │ │ └── utils.ts # cx 유틸리티 (styled-system/css에서 re-export)
55
+ │ ├── tailwind-plugin.ts # Tailwind CSS용 토큰 플러그인
43
56
  │ ├── styles.css # Panda CSS 레이어 선언
44
57
  │ ├── env.d.ts # CSS 파일 타입 선언
45
58
  │ ├── styles.css.d.ts # styles.css companion 타입
@@ -52,158 +65,75 @@ moyeorakui/
52
65
 
53
66
  ## 디자인 토큰
54
67
 
55
- 토큰은 `panda.config.ts` 한 곳에서 관리하며 두 레이어로 구성돼요.
68
+ 토큰은 `panda.config.ts` 한 곳에서 관리하며, 색상과 텍스트 스타일 모두 원시값 → 의미 기반 값 2단계 구조를 따라요.
56
69
 
57
- ### Atomic 토큰 (원시값)
70
+ - **색상** — semantic 토큰은 `요소.의도.강조단계.상태` 구조예요. → [색상 토큰 보기](https://main--69cf7bceba8267c1b5d86ba6.chromatic.com/?path=/docs/foundations-colors--docs)
71
+ - **텍스트 스타일** — Heading / Title / Body / Caption 4개 그룹으로 구성돼요. → [텍스트 스타일 보기](https://main--69cf7bceba8267c1b5d86ba6.chromatic.com/?path=/docs/foundations-typography--docs)
72
+
73
+ ## 파일 구조 컨벤션
58
74
 
59
75
  ```
60
- colors.neutral.{0,50,100-900} 중립 색상 팔레트
61
- colors.neutralOpacity.{100-900} 투명도 기반 중립 색상 (rgba)
62
- colors.blue.{100-900} 브랜드 블루 팔레트
63
- colors.red #f0405c
64
- colors.green #00cf95
65
-
66
- fontSizes.{xs|sm|md|lg|xl|2xl}
67
- fontWeights.{regular|medium|semibold|bold}
68
- letterSpacings.{tight|tighter}
69
- radii.{2xs|xs|sm|md|lg|xl|full}
70
- shadows.{subtle|default|strong|stronger}
76
+ src/components/ComponentName/
77
+ ├── index.ts # named export
78
+ ├── ComponentName.tsx # 구현
79
+ ├── ComponentName.stories.tsx # Storybook 스토리
80
+ └── ComponentName.mdx # Storybook 문서 (anatomy/usage 이미지 포함, 선택)
71
81
  ```
72
82
 
73
- ### Semantic 토큰 (의미 기반)
83
+ ## Tailwind 지원 (v4)
74
84
 
75
- 토큰 경로는 `요소.의도.강조단계` 구조를 따르며, 상태가 있는 경우 `강조단계-상태` 형식으로 표기해요.
85
+ Panda CSS를 쓰지 않는 소비 프로젝트를 위해 동일한 디자인 토큰을 Tailwind CSS 유틸리티로 노출하는 플러그인을 별도로 제공해요. Tailwind v4는 CSS 안에서 직접 설정하는 방식이라 `tailwind.config.ts` 없이 진입 CSS에서 바로 로드해요.
76
86
 
87
+ ```css
88
+ /* 앱의 전역 CSS 진입점 */
89
+ @import "@moyeorak/design-system/layers.css";
90
+ @import "tailwindcss";
91
+ @plugin "@moyeorak/design-system/tailwind-plugin";
92
+ @import "@moyeorak/design-system/styles.css";
77
93
  ```
78
- fg.neutral.{faintest|subtlest|subtler|subtle|default|strong|default-disabled}
79
- fg.inverted.{default}
80
- fg.brand.{subtlest|default}
81
- fg.positive.{default}
82
- fg.negative.{default}
83
94
 
84
- stroke.neutral.{subtler|subtle|default|strong}
85
- stroke.brand.{subtle|default}
86
- stroke.negative.{default}
95
+ 플러그인은 semantic 색상 토큰을 `bg-fill-brand-default`, `text-fg-neutral-default`처럼 CSS 변수 기반 Tailwind 컬러로, 텍스트 스타일을 `.textStyle_body1` 같은 유틸리티 클래스로 노출해요. CSS 변수 자체는 `@moyeorak/design-system/styles.css`에서 가져오므로, 이 CSS는 Tailwind 사용 여부와 관계없이 항상 import해야 해요.
87
96
 
88
- fill.neutral.{default|strong|stronger|strongest}
89
- fill.inverted.{default|default-pressed|default-disabled}
90
- fill.brand.{subtlest|subtler|subtle|subtle-pressed|default|default-pressed}
97
+ ### Cascade layer 우선순위 고정
91
98
 
92
- bg.neutral.{default|strong}
93
- bg.dim.{default}
99
+ Tailwind v4는 네이티브 CSS cascade layer(`@layer theme, base, components, utilities`)를 쓰는데, 레이어 우선순위는 import 순서가 아니라 "레이어 이름이 처음 선언된 순서"로 전역 고정돼요. 이 패키지의 컴포넌트 CSS도 자체 `moyeorak-*` 레이어를 쓰기 때문에, 위 예제처럼 `layers.css`를 다른 `@import`보다 먼저 로드해 순서를 미리 고정해야 해요. 그러지 않으면 디자인 시스템 컴포넌트 기본 스타일이 Tailwind preflight에 눌리거나, 반대로 컴포넌트에 붙인 Tailwind 유틸 클래스 오버라이드가 안 먹히는 문제가 import 순서에 따라 무작위로 발생해요.
94
100
 
95
- shadow.{subtle|default|strong|stronger}
96
- ```
101
+ ## 다크 모드
97
102
 
98
- ### 텍스트 스타일
103
+ 현재 semantic 토큰에 `_dark` 값이 정의되지 않아 다크 모드를 지원하지 않아요.
99
104
 
100
- ```
101
- head1 28px semibold
102
- head2 24px bold
103
- head3 24px semibold
104
- title1 20px semibold
105
- title2 20px regular
106
- title3 16px semibold
107
- body{1-2} 16px medium / regular
108
- body{3-5} 14px semibold / medium / regular
109
- caption{1-3} 12px semibold / medium / regular
110
- ```
111
-
112
- ## 컴포넌트 작성 가이드
113
-
114
- Radix UI 프리미티브 위에 Panda CSS `cva`로 스타일을 입히는 패턴을 따라요.
115
-
116
- ```tsx
117
- // src/components/Button/Button.tsx
118
- import { Slot } from "@radix-ui/react-slot"
119
- import { cva, type VariantProps } from "../../../styled-system/css"
120
- import { cx } from "../../lib/utils"
121
-
122
- const buttonStyles = cva({
123
- base: {
124
- display: "inline-flex",
125
- alignItems: "center",
126
- cursor: "pointer",
127
- borderRadius: "md",
128
- fontWeight: "semibold",
129
- _disabled: { opacity: 0.5, pointerEvents: "none" },
130
- },
131
- variants: {
132
- variant: {
133
- primary: { bg: "fill.brand.default", color: "white" },
134
- secondary: { bg: "fill.neutral.default", color: "fg.neutral.default" },
135
- },
136
- size: {
137
- sm: { h: "32px", px: "12px", textStyle: "body3" },
138
- md: { h: "40px", px: "16px", textStyle: "body2" },
139
- lg: { h: "48px", px: "24px", textStyle: "body1" },
140
- },
141
- },
142
- defaultVariants: { variant: "primary", size: "md" },
143
- })
144
-
145
- export interface ButtonProps
146
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
147
- VariantProps<typeof buttonStyles> {
148
- asChild?: boolean
149
- }
105
+ ## 라이브러리 사용 (소비자)
150
106
 
151
- export function Button({ variant, size, asChild = false, className, ...props }: ButtonProps) {
152
- const Comp = asChild ? Slot : "button"
153
- return <Comp className={cx(buttonStyles({ variant, size }), className)} {...props} />
154
- }
107
+ ```bash
108
+ npm install @moyeorak/design-system
155
109
  ```
156
110
 
157
- ### 파일 구조 컨벤션
111
+ CSS는 앱의 전역 CSS 진입점에서 한 번만 가져오면 돼요. Tailwind 없이 쓴다면:
158
112
 
159
- ```
160
- src/components/ComponentName/
161
- ├── index.ts # named export
162
- ├── ComponentName.tsx # 구현
163
- └── ComponentName.stories.tsx # Storybook 스토리
113
+ ```css
114
+ /* 앱의 전역 CSS 진입점 */
115
+ @import "@moyeorak/design-system/styles.css";
164
116
  ```
165
117
 
166
- ## 다크 모드
118
+ Tailwind v4를 함께 쓴다면 위 [Tailwind 지원 (v4)](#tailwind-지원-v4) 섹션의 예제를 참고해주세요.
167
119
 
168
- 현재 semantic 토큰에 `_dark` 값이 정의되지 않아 다크 모드를 지원하지 않아요. 추후 각 semantic 토큰에 `_dark` 값을 추가하면 `.dark` 클래스 기반으로 동작해요.
120
+ 토큰은 `styles.css`의 `:root`에 CSS 변수로 노출돼요. 어디서든 `var(--colors-fill-brand-default)`처럼 직접 참조하면 돼요.
169
121
 
170
- ```ts
171
- // 다크 모드 추가 시 panda.config.ts 토큰 형태
172
- fg: {
173
- neutral: {
174
- default: { value: { base: "{colors.neutral.800}", _dark: "{colors.neutral.100}" } },
175
- }
122
+ ```css
123
+ .myButton {
124
+ background: var(--colors-fill-brand-default);
176
125
  }
177
126
  ```
178
127
 
179
- ## 라이브러리 사용 (소비자)
180
-
181
- ```bash
182
- npm install @moyeorak/design-system
183
- ```
184
-
185
- ```tsx
186
- // CSS import 필수
187
- import "@moyeorak/design-system/dist/style.css"
128
+ 텍스트 스타일(Heading/Title/Body/Caption)은 CSS 변수 조합이 아니라 완성된 클래스로 제공돼요. `textStyle_{이름}` 클래스를 그대로 붙이면 돼요.
188
129
 
189
- import { Button } from "@moyeorak/design-system"
130
+ ```html
131
+ <p class="textStyle_body1">본문 텍스트</p>
190
132
  ```
191
133
 
192
134
  ### 폰트 (Pretendard)
193
135
 
194
- 라이브러리는 폰트 파일을 번들링하지 않고 `font-family: 'Pretendard Variable', Pretendard, sans-serif`로 이름만 참조해요. Pretendard를 직접 적용하려면 소비 앱에서 폰트를 로드해야 해요.
195
-
196
- ```tsx
197
- // Next.js의 경우 next/font/local 사용 권장
198
- import localFont from "next/font/local"
199
-
200
- const pretendard = localFont({
201
- src: "../fonts/PretendardVariable.woff2",
202
- variable: "--font-pretendard",
203
- })
204
- ```
205
-
206
- 폰트를 로드하지 않으면 시스템 sans-serif 폰트로 자동 폴백돼요.
136
+ 라이브러리는 폰트 파일을 번들링하지 않고 `font-family: 'Pretendard Variable', Pretendard, sans-serif`로 이름만 참조해요. Pretendard를 적용하려면 소비 앱에서 직접 로드해야 하고(예: Next.js는 `next/font/local`), 로드하지 않으면 시스템 sans-serif로 자동 폴백돼요.
207
137
 
208
138
  ## Panda CSS 코드 생성
209
139
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { FC } from 'react';
1
2
  import { JSX } from 'react/jsx-runtime';
3
+ import * as RadixToast from '@radix-ui/react-toast';
2
4
 
3
5
  declare type AdvancedPseudos =
4
6
  | ":-moz-any()"
@@ -172,9 +174,14 @@ p: "16px";
172
174
  };
173
175
  }>;
174
176
 
175
- export declare function Chip({ variant, shape, className, children, ...props }: ChipProps): JSX.Element;
177
+ export declare function Chip({ variant, shape, className, children, pressed, defaultPressed, onPressedChange, ...props }: ChipProps): JSX.Element;
176
178
 
177
- export declare type ChipProps = React.ButtonHTMLAttributes<HTMLButtonElement> & RecipeVariantProps<typeof chipStyles>;
179
+ export declare type ChipProps = React.ButtonHTMLAttributes<HTMLButtonElement> & RecipeVariantProps<typeof chipStyles> & {
180
+ /** 칩의 선택 상태예요. 전달하면 controlled로 동작하며, `aria-pressed`에 반영돼요. */
181
+ pressed?: boolean;
182
+ defaultPressed?: boolean;
183
+ onPressedChange?: (pressed: boolean) => void;
184
+ };
178
185
 
179
186
  declare const chipStyles: RecipeRuntimeFn< {
180
187
  variant: {
@@ -17870,10 +17877,23 @@ export declare type SystemStyleObject = Omit<Nested<SystemProperties & CssVarPro
17870
17877
 
17871
17878
  export declare function Toast(props: ToastProps): JSX.Element;
17872
17879
 
17873
- export declare type ToastProps = NeutralToastProps | NegativeToastProps | PositiveToastProps;
17880
+ export declare type ToastProps = (NeutralToastProps | NegativeToastProps | PositiveToastProps) & {
17881
+ /** 토스트가 열려 있는지 여부예요. 전달하면 controlled로 동작해요. */
17882
+ open?: boolean;
17883
+ defaultOpen?: boolean;
17884
+ onOpenChange?: (open: boolean) => void;
17885
+ /** 자동으로 닫히기까지의 시간(ms)이에요. */
17886
+ duration?: number;
17887
+ };
17888
+
17889
+ export declare const ToastProvider: FC<RadixToast.ToastProviderProps>;
17874
17890
 
17875
17891
  export declare type ToastVariant = "neutral" | "negative" | "positive";
17876
17892
 
17893
+ export declare function ToastViewport(props: ToastViewportProps): JSX.Element;
17894
+
17895
+ export declare type ToastViewportProps = RadixToast.ToastViewportProps;
17896
+
17877
17897
  declare type Token = "colors.gray.0" | "colors.gray.50" | "colors.gray.100" | "colors.gray.200" | "colors.gray.300" | "colors.gray.400" | "colors.gray.500" | "colors.gray.600" | "colors.gray.700" | "colors.gray.800" | "colors.gray.900" | "colors.blue.100" | "colors.blue.200" | "colors.blue.300" | "colors.blue.400" | "colors.blue.500" | "colors.blue.600" | "colors.blue.700" | "colors.blue.800" | "colors.blue.900" | "colors.grayOpacity.100" | "colors.grayOpacity.200" | "colors.grayOpacity.300" | "colors.grayOpacity.400" | "colors.grayOpacity.500" | "colors.grayOpacity.600" | "colors.grayOpacity.700" | "colors.grayOpacity.800" | "colors.grayOpacity.900" | "colors.red" | "colors.green" | "fonts.sans" | "fontSizes.xs" | "fontSizes.sm" | "fontSizes.md" | "fontSizes.lg" | "fontSizes.xl" | "fontSizes.2xl" | "fontWeights.regular" | "fontWeights.medium" | "fontWeights.semibold" | "fontWeights.bold" | "letterSpacings.tight" | "letterSpacings.tighter" | "radii.2xs" | "radii.xs" | "radii.sm" | "radii.md" | "radii.lg" | "radii.xl" | "radii.full" | "shadows.strong" | "breakpoints.sm" | "breakpoints.md" | "breakpoints.lg" | "breakpoints.xl" | "breakpoints.2xl" | "sizes.breakpoint-sm" | "sizes.breakpoint-md" | "sizes.breakpoint-lg" | "sizes.breakpoint-xl" | "sizes.breakpoint-2xl" | "colors.fill.neutral.default" | "colors.fill.neutral.default-pressed" | "colors.fill.neutral.default-disabled" | "colors.fill.neutral.subtle" | "colors.fill.neutral.subtler" | "colors.fill.neutral.subtlest" | "colors.fill.neutral.faintest" | "colors.fill.brand.default" | "colors.fill.brand.default-pressed" | "colors.fill.brand.subtle" | "colors.fill.brand.subtle-pressed" | "colors.fill.brand.subtler" | "colors.fill.brand.subtlest" | "colors.bg.neutral.strongest" | "colors.bg.neutral.strong" | "colors.bg.neutral.default" | "colors.overlay.neutral.stronger" | "colors.overlay.neutral.strong" | "colors.overlay.neutral.default" | "colors.overlay.neutral.subtle" | "colors.overlay.neutral.subtler" | "colors.stroke.neutral.strong" | "colors.stroke.neutral.default" | "colors.stroke.neutral.subtle" | "colors.stroke.neutral.subtler" | "colors.stroke.brand.default" | "colors.stroke.brand.subtle" | "colors.stroke.negative.default" | "colors.shadow.neutral.stronger" | "colors.shadow.neutral.strong" | "colors.shadow.neutral.default" | "colors.shadow.neutral.subtle" | "colors.fg.neutral.stronger" | "colors.fg.neutral.strong" | "colors.fg.neutral.default" | "colors.fg.neutral.subtle" | "colors.fg.neutral.subtler" | "colors.fg.neutral.subtlest" | "colors.fg.neutral.faintest" | "colors.fg.brand.default" | "colors.fg.brand.subtle" | "colors.fg.brand.subtlest" | "colors.fg.positive.default" | "colors.fg.negative.default" | "colors.colorPalette.0" | "colors.colorPalette.50" | "colors.colorPalette.100" | "colors.colorPalette.200" | "colors.colorPalette.300" | "colors.colorPalette.400" | "colors.colorPalette.500" | "colors.colorPalette.600" | "colors.colorPalette.700" | "colors.colorPalette.800" | "colors.colorPalette.900" | "colors.colorPalette" | "colors.colorPalette.neutral.default" | "colors.colorPalette.default" | "colors.colorPalette.neutral.default-pressed" | "colors.colorPalette.default-pressed" | "colors.colorPalette.neutral.default-disabled" | "colors.colorPalette.default-disabled" | "colors.colorPalette.neutral.subtle" | "colors.colorPalette.subtle" | "colors.colorPalette.neutral.subtler" | "colors.colorPalette.subtler" | "colors.colorPalette.neutral.subtlest" | "colors.colorPalette.subtlest" | "colors.colorPalette.neutral.faintest" | "colors.colorPalette.faintest" | "colors.colorPalette.brand.default" | "colors.colorPalette.brand.default-pressed" | "colors.colorPalette.brand.subtle" | "colors.colorPalette.brand.subtle-pressed" | "colors.colorPalette.subtle-pressed" | "colors.colorPalette.brand.subtler" | "colors.colorPalette.brand.subtlest" | "colors.colorPalette.neutral.strongest" | "colors.colorPalette.strongest" | "colors.colorPalette.neutral.strong" | "colors.colorPalette.strong" | "colors.colorPalette.neutral.stronger" | "colors.colorPalette.stronger" | "colors.colorPalette.negative.default" | "colors.colorPalette.positive.default"
17878
17898
 
17879
17899
  declare type Tokens = {
@@ -19918,6 +19938,6 @@ declare interface VendorShorthandProperties<TLength = (string & {}) | 0, TTime =
19918
19938
 
19919
19939
  declare type VendorShorthandPropertiesFallback<TLength = (string & {}) | 0, TTime = string & {}> = Fallback<VendorShorthandProperties<TLength, TTime>>;
19920
19940
 
19921
- export declare const version = "0.1.3";
19941
+ export declare const version = "0.3.0";
19922
19942
 
19923
19943
  export { }