@peppone.choi/ui-kit 0.2.0 → 0.2.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 (3) hide show
  1. package/README.ko.md +230 -84
  2. package/README.md +233 -86
  3. package/package.json +27 -17
package/README.ko.md CHANGED
@@ -1,99 +1,148 @@
1
1
  # Peppone UI Kit
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@peppone.choi/ui-kit.svg)](https://www.npmjs.com/package/@peppone.choi/ui-kit)
3
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](#라이선스)
4
5
 
5
- **70개 이상의 컴포넌트**를 제공하는 React 컴포넌트 라이브러리입니다.
6
- [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web)(MIT) 기반이며,
7
- **프레임워크 비종속 코어**와 **선택적 Next.js 어댑터**를 함께 제공합니다 — 설치하면 바로 동작합니다.
6
+ 버튼·입력·다이얼로그·달력·내비게이션 **바로 쓰는 컴포넌트 70개 이상**을 담은
7
+ 친절한 React 컴포넌트 라이브러리입니다.
8
+ [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web) 기반이고,
9
+ **어떤 React 앱에서도** 동작하며, **TypeScript 타입 완비**, **다크 모드 기본 제공**.
8
10
 
9
- > 🇺🇸 English docs: [README.md](./README.md)
11
+ 처음이세요? 아래 **[60초 설정](#60초-설정)**부터 보세요 — 설치하고, CSS 한 줄
12
+ import하고, 컴포넌트 하나 넣으면 끝입니다.
13
+
14
+ > 🇺🇸 English docs: **[README.md](https://github.com/peppone-choi/Peppone-UI-KIT/blob/main/README.md)**
15
+
16
+ ---
17
+
18
+ ## 목차
19
+
20
+ - [왜 Peppone UI Kit인가](#왜-peppone-ui-kit인가)
21
+ - [60초 설정](#60초-설정)
22
+ - [예제](#예제)
23
+ - [다크 모드](#다크-모드)
24
+ - [Next.js와 함께 쓰기](#nextjs와-함께-쓰기)
25
+ - [모든 컴포넌트 둘러보기](#모든-컴포넌트-둘러보기)
26
+ - [문제 해결](#문제-해결)
27
+ - [전체 컴포넌트](#전체-컴포넌트)
28
+ - [기여 / 로컬 개발](#기여--로컬-개발)
29
+ - [라이선스](#라이선스)
10
30
 
11
31
  ---
12
32
 
13
- ## 특징
33
+ ## 왜 Peppone UI Kit인가
14
34
 
15
- - **70개 이상 컴포넌트** — 액션, 콘텐츠, 피드백, 내비게이션, 표현, 선택 카테고리.
16
- - **두 개의 진입점**프레임워크 비종속 코어(`@peppone.choi/ui-kit`)와 Next.js 연동 어댑터(`@peppone.choi/ui-kit/next`).
17
- - **접근성 오버레이** — 다이얼로그/팝오버/툴팁이 [Base UI](https://base-ui.com) 프리미티브 기반(포커스 트랩, 스크롤 락, dismiss, 포털, ARIA).
18
- - **Tailwind CSS v4** 토큰 + CSS 커스텀 프로퍼티 기반 **다크 모드**.
19
- - **TypeScript 완전 지원** — 모든 컴포넌트에 타입 포함.
20
- - **ESM + CJS** 빌드, 트리셰이킹 지원(`sideEffects` 인식), RSC용 `"use client"` 보존.
35
+ - 🧩 **컴포넌트 70개 이상** — 액션, 콘텐츠, 피드백, 내비게이션, 오버레이, 폼.
36
+ - **기본으로 접근성**다이얼로그·팝오버·메뉴·바텀시트가 [Base UI](https://base-ui.com)
37
+ 프리미티브 기반이라 포커스 트랩, 스크롤 락, `Escape`/바깥클릭 닫기, ARIA가 자동.
38
+ - 🎨 **테마 + 다크 모드** — Tailwind CSS v4 토큰을 CSS 변수로. 클래스 하나로 전체
39
+ 다크 전환.
40
+ - 🟦 **TypeScript 우선** 모든 컴포넌트·prop에 타입 제공.
41
+ - ⚛️ **어디서나 동작** — 프레임워크 비종속 코어 + `next/link`를 미리 연결한 선택적
42
+ Next.js 빌드.
43
+ - 📦 **현대적 패키징** — ESM + CJS, 트리셰이킹, RSC용 `"use client"` 보존.
21
44
 
22
45
  ---
23
46
 
24
- ## 설치
47
+ ## 60초 설정
48
+
49
+ **1. 설치**
25
50
 
26
51
  ```bash
27
- pnpm add @peppone.choi/ui-kit
28
- # 또는: npm install @peppone.choi/ui-kit
52
+ npm install @peppone.choi/ui-kit
53
+ # 또는: pnpm add @peppone.choi/ui-kit
29
54
  # 또는: yarn add @peppone.choi/ui-kit
30
55
  ```
31
56
 
32
- ### Peer 의존성
57
+ > `react`/`react-dom`(v19+)은 peer 의존성입니다. **npm 7+ 와 pnpm은 자동 설치**하니
58
+ > 따로 안 해도 됩니다. (Yarn Classic만 `yarn add react react-dom` 필요.) 그 외 킷이
59
+ > 쓰는 패키지는 일반 의존성으로 함께 설치됩니다.
33
60
 
34
- `react`와 `react-dom`(v19 이상)은 의도적으로 **peer** 의존성입니다. 킷이 앱의
35
- 단일 React 인스턴스를 공유해야 하기 때문입니다. React를 일반 의존성으로 넣으면
36
- 사본이 중복돼 "Invalid hook call" 오류가 날 수 있습니다.
61
+ **2. 스타일시트를 번만 import** 진입점에서. 다들 빠뜨리는 단계니 먼저
62
+ 하세요:
37
63
 
38
- 직접 설치할 필요는 보통 없습니다 — **npm 7+ 와 pnpm은 peer 의존성을 자동
39
- 설치**합니다. peer를 자동 설치하지 않는 Yarn Classic뿐이며, 그 경우에만
40
- `yarn add react react-dom`을 실행하세요.
64
+ ```ts
65
+ // Next.js: app/layout.tsx · Vite: src/main.tsx · CRA: src/index.tsx
66
+ import "@peppone.choi/ui-kit/styles.css";
67
+ ```
41
68
 
42
- 킷이 쓰는 것(Base UI, clsx, tailwind-merge, lucide-react 등)은 모두 일반
43
- 의존성이라 자동으로 설치됩니다.
69
+ > ⚠️ 스타일시트 없으면 컴포넌트가 스타일 없이 나옵니다. 깨져 보이면 import부터
70
+ > 확인하세요.
44
71
 
45
- `next`는 **선택적** peer 의존성으로, `@peppone.choi/ui-kit/next`를 import할 때만 끌려옵니다.
72
+ **3. 컴포넌트 사용**
46
73
 
47
- ### 스타일시트 import
74
+ ```tsx
75
+ import { Button } from "@peppone.choi/ui-kit";
48
76
 
49
- 킷은 미리 컴파일된 스타일시트를 함께 배포합니다. 앱 진입점
50
- (`app/layout.tsx`, `main.tsx`, `_app.tsx` 등)에서 **한 번만** import하세요:
77
+ export default function App() {
78
+ return <Button onClick={() => alert("동작!")}>눌러보세요</Button>;
79
+ }
80
+ ```
51
81
 
52
- ```ts
82
+ 이게 설정 전부입니다. 완전한 Next.js 진입점은 이렇게 생겼습니다:
83
+
84
+ ```tsx
85
+ // app/layout.tsx
53
86
  import "@peppone.choi/ui-kit/styles.css";
54
- ```
55
87
 
56
- import가 없으면 컴포넌트가 스타일 없이 렌더링됩니다.
88
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
89
+ return (
90
+ <html lang="ko">
91
+ <body>{children}</body>
92
+ </html>
93
+ );
94
+ }
95
+ ```
57
96
 
58
97
  ---
59
98
 
60
- ## 빠른 시작
99
+ ## 예제
100
+
101
+ ### 카드 + 버튼
61
102
 
62
103
  ```tsx
63
- import { Button, Card, CardHeader, CardTitle, CardContent } from "@peppone.choi/ui-kit";
64
- import "@peppone.choi/ui-kit/styles.css";
104
+ import { Card, CardHeader, CardTitle, CardContent, Button } from "@peppone.choi/ui-kit";
65
105
 
66
- export function Example() {
106
+ export function WelcomeCard() {
67
107
  return (
68
108
  <Card>
69
109
  <CardHeader>
70
- <CardTitle>안녕하세요 Peppone</CardTitle>
110
+ <CardTitle>환영합니다 👋</CardTitle>
71
111
  </CardHeader>
72
- <CardContent>
73
- <Button onClick={() => alert("클릭됨")}>눌러보세요</Button>
112
+ <CardContent className="flex flex-col gap-3">
113
+ <p>몇 초면 시작할 수 있어요.</p>
114
+ <Button>시작하기</Button>
74
115
  </CardContent>
75
116
  </Card>
76
117
  );
77
118
  }
78
119
  ```
79
120
 
80
- ### 다이얼로그 (기본 접근성 내장)
121
+ ### 확인 다이얼로그 (접근성 자동)
81
122
 
82
123
  ```tsx
83
124
  import { useState } from "react";
84
125
  import { Popup, Button } from "@peppone.choi/ui-kit";
85
126
 
86
- export function ConfirmDialog() {
127
+ export function DeleteButton() {
87
128
  const [open, setOpen] = useState(false);
129
+
88
130
  return (
89
131
  <>
90
- <Button onClick={() => setOpen(true)}>열기</Button>
132
+ <Button variant="destructive" onClick={() => setOpen(true)}>
133
+ 삭제
134
+ </Button>
135
+
91
136
  <Popup
92
137
  open={open}
93
138
  onClose={() => setOpen(false)}
94
- title="항목을 삭제할까요?"
139
+ title="항목을 삭제할까요?"
95
140
  description="이 작업은 되돌릴 수 없습니다."
96
- primaryAction={{ label: "삭제", variant: "destructive", onClick: () => setOpen(false) }}
141
+ primaryAction={{
142
+ label: "삭제",
143
+ variant: "destructive",
144
+ onClick: () => setOpen(false),
145
+ }}
97
146
  secondaryAction={{ label: "취소", onClick: () => setOpen(false) }}
98
147
  />
99
148
  </>
@@ -101,55 +150,80 @@ export function ConfirmDialog() {
101
150
  }
102
151
  ```
103
152
 
104
- 다이얼로그가 포커스 트랩, 포커스 복원, 스크롤 락, 바깥 클릭 및 `Escape` 닫기,
105
- ARIA 연결까지 모두 자동 처리합니다.
106
-
107
- ---
108
-
109
- ## 두 개의 진입점
110
-
111
- | import 경로 | 사용 시점 |
112
- |---|---|
113
- | `@peppone.choi/ui-kit` | 모든 React 앱(Vite, CRA, Remix, Astro, Next.js 클라이언트 컴포넌트). 프레임워크 비종속. |
114
- | `@peppone.choi/ui-kit/next` | `next/link` 기반 클라이언트 라우팅을 원하는 Next.js 앱. 코어 전체를 재노출하고 Next 결합 컴포넌트만 덮어씀. |
115
-
116
- ### 코어 (프레임워크 비종속)
153
+ 포커스 트랩, 스크롤 락, `Escape` 처리, ARIA 라벨을 직접 안 짜도 다이얼로그가 다
154
+ 해줍니다.
117
155
 
118
- 코어 `ActionArea`는 링크일 때 네이티브 `<a>`를 렌더링하며, `linkComponent`로
119
- 어떤 라우터든 주입할 수 있습니다:
156
+ ### 라디오 그룹
120
157
 
121
158
  ```tsx
122
- import { ActionArea } from "@peppone.choi/ui-kit";
123
- import { Link } from "react-router-dom";
159
+ import { RadioGroup, RadioGroupItem } from "@peppone.choi/ui-kit";
124
160
 
125
- <ActionArea href="/profile" linkComponent={Link}>프로필</ActionArea>
161
+ export function ShippingOptions() {
162
+ return (
163
+ <RadioGroup defaultValue="standard">
164
+ <RadioGroupItem value="standard" label="일반 (무료)" />
165
+ <RadioGroupItem value="express" label="빠른 배송 (+5,000원)" />
166
+ <RadioGroupItem value="overnight" label="당일 (+15,000원)" />
167
+ </RadioGroup>
168
+ );
169
+ }
126
170
  ```
127
171
 
128
- ### Next.js
172
+ ### 바텀시트 (모바일에 좋음)
129
173
 
130
174
  ```tsx
131
- // 여기 컴포넌트는 next/link로 미리 연결돼 클라이언트 라우팅이 동작합니다.
132
- import { ActionArea, Button, Card } from "@peppone.choi/ui-kit/next";
175
+ import { useState } from "react";
176
+ import { BottomSheet, Button } from "@peppone.choi/ui-kit";
133
177
 
134
- <ActionArea href="/profile">프로필</ActionArea>
178
+ export function FilterSheet() {
179
+ const [open, setOpen] = useState(false);
180
+ return (
181
+ <>
182
+ <Button variant="outline" onClick={() => setOpen(true)}>필터</Button>
183
+ <BottomSheet open={open} onClose={() => setOpen(false)} title="필터">
184
+ <p>여기에 필터 컨트롤을 넣으세요.</p>
185
+ </BottomSheet>
186
+ </>
187
+ );
188
+ }
135
189
  ```
136
190
 
137
191
  ---
138
192
 
139
- ## 스타일링 & 다크 모드
193
+ ## 다크 모드
140
194
 
141
- 킷은 Tailwind CSS v4 토큰을 CSS 커스텀 프로퍼티로 정의합니다. 다크 모드는
142
- 상위 요소(보통 `<html>`)의 `.dark` 클래스로 동작합니다:
195
+ 킷은 색을 CSS 변수에서 읽고, 상위 요소(보통 `<html>`)에 `dark` 클래스가 있으면
196
+ 전환됩니다:
143
197
 
144
198
  ```html
145
199
  <html class="dark">
146
200
  ```
147
201
 
148
- 토글 방식은 자유입니다(next-themes, 클래스 토글, `prefers-color-scheme` 등).
149
- WDS 시맨틱 토큰(`label-normal`, `background-elevated-normal`, `status-positive` 등)은
150
- `var()`로 해석되어 활성 테마를 자동으로 따릅니다.
202
+ 토글 방식은 자유입니다. [`next-themes`](https://github.com/pacocoursey/next-themes) 예:
203
+
204
+ ```tsx
205
+ // app/providers.tsx
206
+ "use client";
207
+ import { ThemeProvider } from "next-themes";
208
+
209
+ export function Providers({ children }: { children: React.ReactNode }) {
210
+ return (
211
+ <ThemeProvider attribute="class" defaultTheme="system">
212
+ {children}
213
+ </ThemeProvider>
214
+ );
215
+ }
216
+ ```
217
+
218
+ 라이브러리 없이 한 줄로:
219
+
220
+ ```tsx
221
+ <button onClick={() => document.documentElement.classList.toggle("dark")}>
222
+ 테마 전환
223
+ </button>
224
+ ```
151
225
 
152
- 프로그래밍 방식 테마 접근(WDS `sx` props, 토큰)이 필요하면 트리를 프로바이더로 감싸세요:
226
+ JS에서 테마 (토큰, WDS `sx` 헬퍼)이 필요하면 킷의 프로바이더로 감싸세요:
153
227
 
154
228
  ```tsx
155
229
  import { ThemeProvider, useTheme } from "@peppone.choi/ui-kit";
@@ -161,9 +235,76 @@ import { ThemeProvider, useTheme } from "@peppone.choi/ui-kit";
161
235
 
162
236
  ---
163
237
 
164
- ## 컴포넌트
238
+ ## Next.js와 함께 쓰기
239
+
240
+ import 경로가 둘 있습니다. 대부분은 첫 번째만 쓰면 됩니다.
241
+
242
+ | import 경로 | 사용 시점 |
243
+ |---|---|
244
+ | `@peppone.choi/ui-kit` | **기본.** 모든 React 앱 — Vite, CRA, Remix, Astro, Next.js. |
245
+ | `@peppone.choi/ui-kit/next` | `next/link` 기반 클라이언트 라우팅을 원하는 Next.js 앱. 코어 전체를 재노출하고 Next 전용 부분만 교체. |
246
+
247
+ ```tsx
248
+ // 순수 React (Next에서도 동작):
249
+ import { Button, ActionArea } from "@peppone.choi/ui-kit";
250
+
251
+ // next/link 라우팅을 원하면 /next에서:
252
+ import { ActionArea } from "@peppone.choi/ui-kit/next";
253
+
254
+ <ActionArea href="/profile">프로필로</ActionArea>;
255
+ ```
256
+
257
+ 다른 라우터(React Router, TanStack Router)를 쓰면 코어 `ActionArea`의
258
+ `linkComponent` prop을 쓰세요:
259
+
260
+ ```tsx
261
+ import { ActionArea } from "@peppone.choi/ui-kit";
262
+ import { Link } from "react-router-dom";
263
+
264
+ <ActionArea href="/profile" linkComponent={Link}>프로필</ActionArea>;
265
+ ```
266
+
267
+ ---
268
+
269
+ ## 모든 컴포넌트 둘러보기
270
+
271
+ props와 라이트/다크 미리보기까지 실제로 보고 싶다면 로컬에서 Storybook을 실행하세요:
272
+
273
+ ```bash
274
+ git clone https://github.com/peppone-choi/Peppone-UI-KIT
275
+ cd Peppone-UI-KIT
276
+ pnpm install
277
+ pnpm storybook # http://localhost:6006 열림
278
+ ```
279
+
280
+ ---
281
+
282
+ ## 문제 해결
283
+
284
+ **컴포넌트에 스타일이 없거나 깨져 보여요.**
285
+ 스타일시트를 빠뜨린 경우가 대부분입니다. 앱 진입점에
286
+ `import "@peppone.choi/ui-kit/styles.css";`를 한 번 추가하세요.
165
287
 
166
- 다음 카테고리에 70개 이상의 컴포넌트가 있습니다:
288
+ **다크 모드가 바뀌어요.**
289
+ 상위 요소(보통 `<html>`)에 실제로 `dark` 클래스가 붙는지 확인하세요. `next-themes`는
290
+ `attribute="class"`로 설정.
291
+
292
+ **"Invalid hook call" / React 사본 2개.**
293
+ 앱에 `react`/`react-dom`이 하나만 있도록 하세요. 중복을 막으려고 `react`를 일부러
294
+ peer 의존성으로 둡니다.
295
+
296
+ **서버 컴포넌트 에러: "use client 필요".**
297
+ 킷 컴포넌트는 클라이언트 컴포넌트입니다(이미 `"use client"` 표시). 본인 클라이언트
298
+ 컴포넌트에서 import하거나 그 안에서 렌더링하세요 — 서버 컴포넌트에서 훅을 직접
299
+ 호출하지 마세요.
300
+
301
+ **Next.js `<Link>` 이동이 전체 새로고침돼요.**
302
+ 컴포넌트를 `@peppone.choi/ui-kit/next`에서 import하거나 `linkComponent`를 넘겨
303
+ `<a>` 대신 `next/link`를 쓰게 하세요.
304
+
305
+ ---
306
+
307
+ ## 전체 컴포넌트
167
308
 
168
309
  | 카테고리 | 컴포넌트 |
169
310
  |---|---|
@@ -171,32 +312,37 @@ import { ThemeProvider, useTheme } from "@peppone.choi/ui-kit";
171
312
  | **콘텐츠** | Accordion, Avatar, AvatarGroup, AvatarButton, Card, CardList, ContentBadge, List, ListCard, ListCell, PlayBadge, SectionHeader, Table, Thumbnail, Typography |
172
313
  | **피드백** | Alert, FallbackView, PushBadge, SectionMessage, Snackbar, Toast, Loading, Skeleton |
173
314
  | **내비게이션** | BottomNavigation, Category, PageCounter, Pagination, PaginationDots, ProgressIndicator, ProgressTracker, ProgressStepIndicator, Tab, TopNavigation |
174
- | **표현** | Autocomplete, BottomSheet, Menu, Popover, Popup, ScrollArea, Tooltip |
315
+ | **오버레이** | Autocomplete, BottomSheet, Menu, Popover, Popup, ScrollArea, Tooltip |
175
316
  | **선택 & 입력** | Checkbox, Radio, RadioGroup, RoundCheckbox, CheckMark, DatePicker, DateRangePicker, DateCalendar, DateRangeCalendar, TimePicker, TimeView, FilterButton, FramedStyle, Label, SearchField, SegmentedControl, Select, SelectMultiple, Slider, Stepper, Switch, TextArea, Input, Form, PickerActionArea |
176
317
  | **레이아웃 & 유틸리티** | Box, FlexBox, Grid, GridItem, Divider |
177
318
 
178
- 모든 컴포넌트는 패키지 루트에서 export되며 타입이 완비돼 있습니다. **Montage WDS
179
- 아이콘 350개**, 아토믹 테마 토큰, Lottie 로딩 애니메이션도 함께 포함됩니다.
319
+ 모두 패키지 루트에서 export되며 타입이 완비돼 있습니다. **Montage WDS 아이콘 350개**,
320
+ 디자인 토큰, Lottie 로딩 애니메이션도 함께 포함됩니다.
180
321
 
181
322
  ---
182
323
 
183
- ## 개발
324
+ ## 기여 / 로컬 개발
184
325
 
185
326
  ```bash
186
327
  pnpm install # 의존성 설치
187
328
  pnpm dev # 쇼케이스 앱 실행 (Next.js)
188
- pnpm build # 라이브러리 빌드 (JS + .d.ts + CSS) → dist/
189
- pnpm test # 단위 테스트 실행 (Vitest)
329
+ pnpm storybook # localhost:6006에서 컴포넌트 둘러보기
330
+ pnpm test # 단위 테스트 (Vitest)
190
331
  pnpm lint # ESLint
191
- pnpm typecheck # tsc --noEmit
332
+ pnpm build # 라이브러리 빌드 (JS + .d.ts + CSS) → dist/
192
333
  ```
193
334
 
194
- 라이브러리 빌드는 `tsup`(ESM + CJS + 타입 선언) Tailwind CLI
195
- (`dist/peppone-ui.css`)를 실행합니다. `tsup.config.ts`, `src/styles.css` 참고.
335
+ 저장소는 [Changesets](https://github.com/changesets/changesets) 씁니다. 릴리스에
336
+ 포함할 변경이면 추가하세요:
337
+
338
+ ```bash
339
+ pnpm changeset
340
+ ```
196
341
 
197
342
  ---
198
343
 
199
344
  ## 라이선스
200
345
 
201
- MIT. 동일하게 MIT 라이선스인
202
- [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web) 기반으로 제작됐습니다.
346
+ MIT — 개인·상업 프로젝트 모두 자유롭게 사용 가능. 동일하게 MIT
347
+ [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web) 기반으로
348
+ 제작됐습니다.
package/README.md CHANGED
@@ -1,101 +1,151 @@
1
1
  # Peppone UI Kit
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@peppone.choi/ui-kit.svg)](https://www.npmjs.com/package/@peppone.choi/ui-kit)
3
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](#license)
4
5
 
5
- A React component library with **70+ components**, built on the
6
- [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web) (MIT).
7
- Ships a **framework-agnostic core** plus an **optional Next.js adapter** — install
8
- it and it works.
6
+ A friendly React component library with **70+ ready-to-use components** buttons,
7
+ inputs, dialogs, calendars, navigation, and more. It's built on the
8
+ [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web), works
9
+ in **any React app**, has **first-class TypeScript types**, and **dark mode** out
10
+ of the box.
9
11
 
10
- > 🇰🇷 한국어 문서는 [README.ko.md](./README.ko.md) 참고하세요.
12
+ New here? Start with **[60-second setup](#60-second-setup)** below — install, import
13
+ one stylesheet, drop in a component. That's it.
14
+
15
+ > 🇰🇷 한국어 문서: **[README.ko.md](https://github.com/peppone-choi/Peppone-UI-KIT/blob/main/README.ko.md)**
16
+
17
+ ---
18
+
19
+ ## Contents
20
+
21
+ - [Why Peppone UI Kit](#why-peppone-ui-kit)
22
+ - [60-second setup](#60-second-setup)
23
+ - [Examples](#examples)
24
+ - [Dark mode](#dark-mode)
25
+ - [Using it with Next.js](#using-it-with-nextjs)
26
+ - [Browse every component](#browse-every-component)
27
+ - [Troubleshooting](#troubleshooting)
28
+ - [All components](#all-components)
29
+ - [Contributing / local development](#contributing--local-development)
30
+ - [License](#license)
11
31
 
12
32
  ---
13
33
 
14
- ## Features
34
+ ## Why Peppone UI Kit
15
35
 
16
- - **70+ components** across actions, content, feedback, navigation, presentation, and selection.
17
- - **Two entry points** — a framework-agnostic core (`@peppone.choi/ui-kit`) and a Next.js-wired adapter (`@peppone.choi/ui-kit/next`).
18
- - **Accessible overlays** — dialogs/popovers/tooltips built on [Base UI](https://base-ui.com) primitives (focus trap, scroll lock, dismiss, portals, ARIA).
19
- - **Tailwind CSS v4** tokens with **dark mode** via CSS custom properties.
20
- - **First-class TypeScript** — every component ships types.
21
- - **ESM + CJS** builds, tree-shakeable (`sideEffects` aware), with `"use client"` preserved for React Server Components.
36
+ - 🧩 **70+ components** actions, content, feedback, navigation, overlays, forms.
37
+ - **Accessible by default** — dialogs, popovers, menus, and the bottom sheet are
38
+ built on [Base UI](https://base-ui.com) primitives, so you get focus trapping,
39
+ scroll locking, `Escape`/outside-click dismissal, and ARIA wiring for free.
40
+ - 🎨 **Theming + dark mode** — Tailwind CSS v4 tokens via CSS variables. Flip one
41
+ class and the whole kit switches to dark.
42
+ - 🟦 **TypeScript first** — every component and prop is typed.
43
+ - ⚛️ **Works anywhere** — a framework-agnostic core plus an optional Next.js build
44
+ that pre-wires `next/link`.
45
+ - 📦 **Modern packaging** — ESM + CJS, tree-shakeable, `"use client"` preserved for
46
+ React Server Components.
22
47
 
23
48
  ---
24
49
 
25
- ## Installation
50
+ ## 60-second setup
51
+
52
+ **1. Install**
26
53
 
27
54
  ```bash
28
- pnpm add @peppone.choi/ui-kit
29
- # or: npm install @peppone.choi/ui-kit
55
+ npm install @peppone.choi/ui-kit
56
+ # or: pnpm add @peppone.choi/ui-kit
30
57
  # or: yarn add @peppone.choi/ui-kit
31
58
  ```
32
59
 
33
- ### Peer dependencies
60
+ > `react` and `react-dom` (v19+) are peer dependencies. **npm 7+ and pnpm install
61
+ > them automatically** — you don't need to do anything. (Only Yarn Classic needs
62
+ > `yarn add react react-dom`.) Everything else the kit needs is bundled as a normal
63
+ > dependency.
34
64
 
35
- `react` and `react-dom` (v19+) are **peer** dependencies on purpose: the kit must
36
- share your app's single React instance. Shipping React as a regular dependency
37
- would risk a duplicate copy and the dreaded "Invalid hook call" error.
65
+ **2. Import the stylesheet once** at your app's entry point. This is the step
66
+ people forget, so do it first:
38
67
 
39
- You normally don't install them by hand — **npm 7+ and pnpm install peer
40
- dependencies automatically**. Only Yarn Classic skips peers; there, run
41
- `yarn add react react-dom`.
68
+ ```ts
69
+ // Next.js: app/layout.tsx · Vite: src/main.tsx · CRA: src/index.tsx
70
+ import "@peppone.choi/ui-kit/styles.css";
71
+ ```
42
72
 
43
- Everything else the kit needs (Base UI, clsx, tailwind-merge, lucide-react, …)
44
- ships as a regular dependency and is installed for you.
73
+ > ⚠️ No stylesheet = unstyled components. If things look broken, check this import.
45
74
 
46
- `next` is an **optional** peer dependency, pulled in only if you import from
47
- `@peppone.choi/ui-kit/next`.
75
+ **3. Use a component**
48
76
 
49
- ### Import the stylesheet
77
+ ```tsx
78
+ import { Button } from "@peppone.choi/ui-kit";
50
79
 
51
- The kit ships a precompiled stylesheet. Import it **once** at your app entry
52
- (e.g. `app/layout.tsx`, `main.tsx`, `_app.tsx`):
80
+ export default function App() {
81
+ return <Button onClick={() => alert("It works!")}>Click me</Button>;
82
+ }
83
+ ```
53
84
 
54
- ```ts
85
+ That's the whole setup. A complete Next.js entry looks like this:
86
+
87
+ ```tsx
88
+ // app/layout.tsx
55
89
  import "@peppone.choi/ui-kit/styles.css";
56
- ```
57
90
 
58
- Without this import, components render unstyled.
91
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
92
+ return (
93
+ <html lang="en">
94
+ <body>{children}</body>
95
+ </html>
96
+ );
97
+ }
98
+ ```
59
99
 
60
100
  ---
61
101
 
62
- ## Quick start
102
+ ## Examples
103
+
104
+ ### Card + Button
63
105
 
64
106
  ```tsx
65
- import { Button, Card, CardHeader, CardTitle, CardContent } from "@peppone.choi/ui-kit";
66
- import "@peppone.choi/ui-kit/styles.css";
107
+ import { Card, CardHeader, CardTitle, CardContent, Button } from "@peppone.choi/ui-kit";
67
108
 
68
- export function Example() {
109
+ export function WelcomeCard() {
69
110
  return (
70
111
  <Card>
71
112
  <CardHeader>
72
- <CardTitle>Hello Peppone</CardTitle>
113
+ <CardTitle>Welcome 👋</CardTitle>
73
114
  </CardHeader>
74
- <CardContent>
75
- <Button onClick={() => alert("clicked")}>Click me</Button>
115
+ <CardContent className="flex flex-col gap-3">
116
+ <p>Get started in seconds.</p>
117
+ <Button>Let's go</Button>
76
118
  </CardContent>
77
119
  </Card>
78
120
  );
79
121
  }
80
122
  ```
81
123
 
82
- ### Dialog (accessible by default)
124
+ ### A confirm dialog (accessible automatically)
83
125
 
84
126
  ```tsx
85
127
  import { useState } from "react";
86
128
  import { Popup, Button } from "@peppone.choi/ui-kit";
87
129
 
88
- export function ConfirmDialog() {
130
+ export function DeleteButton() {
89
131
  const [open, setOpen] = useState(false);
132
+
90
133
  return (
91
134
  <>
92
- <Button onClick={() => setOpen(true)}>Open</Button>
135
+ <Button variant="destructive" onClick={() => setOpen(true)}>
136
+ Delete
137
+ </Button>
138
+
93
139
  <Popup
94
140
  open={open}
95
141
  onClose={() => setOpen(false)}
96
- title="Delete item?"
142
+ title="Delete this item?"
97
143
  description="This action cannot be undone."
98
- primaryAction={{ label: "Delete", variant: "destructive", onClick: () => setOpen(false) }}
144
+ primaryAction={{
145
+ label: "Delete",
146
+ variant: "destructive",
147
+ onClick: () => setOpen(false),
148
+ }}
99
149
  secondaryAction={{ label: "Cancel", onClick: () => setOpen(false) }}
100
150
  />
101
151
  </>
@@ -103,55 +153,80 @@ export function ConfirmDialog() {
103
153
  }
104
154
  ```
105
155
 
106
- The dialog handles focus trapping, focus restore, scroll locking, outside-press
107
- and `Escape` dismissal, and ARIA wiring for you.
108
-
109
- ---
110
-
111
- ## Two entry points
112
-
113
- | Import path | Use when |
114
- |---|---|
115
- | `@peppone.choi/ui-kit` | Any React app (Vite, CRA, Remix, Astro, Next.js client components). Framework-agnostic. |
116
- | `@peppone.choi/ui-kit/next` | Next.js apps that want components pre-wired to `next/link` (client-side navigation). Re-exports the entire core, overriding only the Next-coupled components. |
156
+ You don't wire up focus traps, scroll locking, `Escape` handling, or ARIA labels —
157
+ the dialog does all of that for you.
117
158
 
118
- ### Core (framework-agnostic)
119
-
120
- The core `ActionArea` renders a native `<a>` for links, or accepts a custom
121
- `linkComponent` so it works with any router:
159
+ ### A radio group
122
160
 
123
161
  ```tsx
124
- import { ActionArea } from "@peppone.choi/ui-kit";
125
- import { Link } from "react-router-dom";
162
+ import { RadioGroup, RadioGroupItem } from "@peppone.choi/ui-kit";
126
163
 
127
- <ActionArea href="/profile" linkComponent={Link}>Profile</ActionArea>
164
+ export function ShippingOptions() {
165
+ return (
166
+ <RadioGroup defaultValue="standard">
167
+ <RadioGroupItem value="standard" label="Standard (free)" />
168
+ <RadioGroupItem value="express" label="Express (+$5)" />
169
+ <RadioGroupItem value="overnight" label="Overnight (+$15)" />
170
+ </RadioGroup>
171
+ );
172
+ }
128
173
  ```
129
174
 
130
- ### Next.js
175
+ ### A bottom sheet (great on mobile)
131
176
 
132
177
  ```tsx
133
- // Components here are pre-wired to next/link for client-side navigation.
134
- import { ActionArea, Button, Card } from "@peppone.choi/ui-kit/next";
178
+ import { useState } from "react";
179
+ import { BottomSheet, Button } from "@peppone.choi/ui-kit";
135
180
 
136
- <ActionArea href="/profile">Profile</ActionArea>
181
+ export function FilterSheet() {
182
+ const [open, setOpen] = useState(false);
183
+ return (
184
+ <>
185
+ <Button variant="outline" onClick={() => setOpen(true)}>Filters</Button>
186
+ <BottomSheet open={open} onClose={() => setOpen(false)} title="Filters">
187
+ <p>Your filter controls go here.</p>
188
+ </BottomSheet>
189
+ </>
190
+ );
191
+ }
137
192
  ```
138
193
 
139
194
  ---
140
195
 
141
- ## Styling & dark mode
196
+ ## Dark mode
142
197
 
143
- The kit uses Tailwind CSS v4 tokens defined as CSS custom properties. Dark mode
144
- is driven by a `.dark` class on an ancestor (typically `<html>`):
198
+ The kit reads its colors from CSS variables and switches whenever an ancestor has
199
+ the `dark` class usually on `<html>`:
145
200
 
146
201
  ```html
147
202
  <html class="dark">
148
203
  ```
149
204
 
150
- Toggle it however you like (next-themes, a class toggle, `prefers-color-scheme`).
151
- WDS semantic tokens (`label-normal`, `background-elevated-normal`,
152
- `status-positive`, …) resolve through `var()` so they follow the active theme.
205
+ Use any toggle you like. With [`next-themes`](https://github.com/pacocoursey/next-themes):
206
+
207
+ ```tsx
208
+ // app/providers.tsx
209
+ "use client";
210
+ import { ThemeProvider } from "next-themes";
211
+
212
+ export function Providers({ children }: { children: React.ReactNode }) {
213
+ return (
214
+ <ThemeProvider attribute="class" defaultTheme="system">
215
+ {children}
216
+ </ThemeProvider>
217
+ );
218
+ }
219
+ ```
220
+
221
+ Or a one-liner without any library:
222
+
223
+ ```tsx
224
+ <button onClick={() => document.documentElement.classList.toggle("dark")}>
225
+ Toggle theme
226
+ </button>
227
+ ```
153
228
 
154
- For programmatic theme access (WDS `sx` props, tokens), wrap your tree in the
229
+ Need theme values in JS (tokens, the WDS `sx` helper)? Wrap your tree in the kit's
155
230
  provider:
156
231
 
157
232
  ```tsx
@@ -164,9 +239,77 @@ import { ThemeProvider, useTheme } from "@peppone.choi/ui-kit";
164
239
 
165
240
  ---
166
241
 
167
- ## Components
242
+ ## Using it with Next.js
243
+
244
+ There are two import paths. Most apps only need the first one.
168
245
 
169
- 70+ components across these categories:
246
+ | Import from | When |
247
+ |---|---|
248
+ | `@peppone.choi/ui-kit` | **Default.** Any React app — Vite, CRA, Remix, Astro, or Next.js. |
249
+ | `@peppone.choi/ui-kit/next` | Next.js apps that want components pre-wired to `next/link` for client-side navigation. It re-exports everything from the core and only swaps the Next-specific pieces. |
250
+
251
+ ```tsx
252
+ // Plain React (works in Next too):
253
+ import { Button, ActionArea } from "@peppone.choi/ui-kit";
254
+
255
+ // Want next/link navigation baked in? Import those from /next:
256
+ import { ActionArea } from "@peppone.choi/ui-kit/next";
257
+
258
+ <ActionArea href="/profile">Go to profile</ActionArea>;
259
+ ```
260
+
261
+ Using another router (React Router, TanStack Router)? The core `ActionArea` takes a
262
+ `linkComponent` prop:
263
+
264
+ ```tsx
265
+ import { ActionArea } from "@peppone.choi/ui-kit";
266
+ import { Link } from "react-router-dom";
267
+
268
+ <ActionArea href="/profile" linkComponent={Link}>Profile</ActionArea>;
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Browse every component
274
+
275
+ Want to see everything live, with props and light/dark previews? Run Storybook
276
+ locally:
277
+
278
+ ```bash
279
+ git clone https://github.com/peppone-choi/Peppone-UI-KIT
280
+ cd Peppone-UI-KIT
281
+ pnpm install
282
+ pnpm storybook # opens http://localhost:6006
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Troubleshooting
288
+
289
+ **Components have no styling / look broken.**
290
+ You probably forgot the stylesheet. Add `import "@peppone.choi/ui-kit/styles.css";`
291
+ once at your app entry.
292
+
293
+ **Dark mode isn't switching.**
294
+ Make sure a parent element (usually `<html>`) actually gets the `dark` class. With
295
+ `next-themes`, set `attribute="class"`.
296
+
297
+ **"Invalid hook call" / two copies of React.**
298
+ Make sure there's a single `react` / `react-dom` in your app. `react` is a *peer*
299
+ dependency on purpose so it isn't duplicated.
300
+
301
+ **Server Component error: "needs `use client`".**
302
+ The kit's components are client components (already marked `"use client"`). Import
303
+ them into your own client component, or render them inside one — don't call their
304
+ hooks from a Server Component.
305
+
306
+ **Next.js `<Link>` navigation does a full reload.**
307
+ Import the component from `@peppone.choi/ui-kit/next` (or pass `linkComponent`), so
308
+ it uses `next/link` instead of a plain `<a>`.
309
+
310
+ ---
311
+
312
+ ## All components
170
313
 
171
314
  | Category | Components |
172
315
  |---|---|
@@ -174,32 +317,36 @@ import { ThemeProvider, useTheme } from "@peppone.choi/ui-kit";
174
317
  | **Content** | Accordion, Avatar, AvatarGroup, AvatarButton, Card, CardList, ContentBadge, List, ListCard, ListCell, PlayBadge, SectionHeader, Table, Thumbnail, Typography |
175
318
  | **Feedback** | Alert, FallbackView, PushBadge, SectionMessage, Snackbar, Toast, Loading, Skeleton |
176
319
  | **Navigation** | BottomNavigation, Category, PageCounter, Pagination, PaginationDots, ProgressIndicator, ProgressTracker, ProgressStepIndicator, Tab, TopNavigation |
177
- | **Presentation** | Autocomplete, BottomSheet, Menu, Popover, Popup, ScrollArea, Tooltip |
320
+ | **Overlays** | Autocomplete, BottomSheet, Menu, Popover, Popup, ScrollArea, Tooltip |
178
321
  | **Selection & input** | Checkbox, Radio, RadioGroup, RoundCheckbox, CheckMark, DatePicker, DateRangePicker, DateCalendar, DateRangeCalendar, TimePicker, TimeView, FilterButton, FramedStyle, Label, SearchField, SegmentedControl, Select, SelectMultiple, Slider, Stepper, Switch, TextArea, Input, Form, PickerActionArea |
179
322
  | **Layout & utilities** | Box, FlexBox, Grid, GridItem, Divider |
180
323
 
181
- All components are exported from the package root and fully typed. The kit also
182
- bundles **350 Montage WDS icons**, atomic theme tokens, and a Lottie loading
183
- animation.
324
+ Everything is exported from the package root and fully typed. The kit also bundles
325
+ **350 Montage WDS icons**, design tokens, and a Lottie loading animation.
184
326
 
185
327
  ---
186
328
 
187
- ## Development
329
+ ## Contributing / local development
188
330
 
189
331
  ```bash
190
332
  pnpm install # install dependencies
191
333
  pnpm dev # run the showcase app (Next.js)
192
- pnpm build # build the library (JS + .d.ts + CSS) into dist/
193
- pnpm test # run the unit test suite (Vitest)
334
+ pnpm storybook # browse components at localhost:6006
335
+ pnpm test # run the unit tests (Vitest)
194
336
  pnpm lint # ESLint
195
- pnpm typecheck # tsc --noEmit
337
+ pnpm build # build the library (JS + .d.ts + CSS) into dist/
196
338
  ```
197
339
 
198
- The library build runs `tsup` (ESM + CJS + type declarations) and the Tailwind
199
- CLI (`dist/peppone-ui.css`). See `tsup.config.ts` and `src/styles.css`.
340
+ This repo uses [Changesets](https://github.com/changesets/changesets). If your
341
+ change should ship in a release, add one:
342
+
343
+ ```bash
344
+ pnpm changeset
345
+ ```
200
346
 
201
347
  ---
202
348
 
203
349
  ## License
204
350
 
205
- MIT. Built on the [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web), also MIT.
351
+ MIT free to use in personal and commercial projects. Built on the
352
+ [Wanted Design System (Montage)](https://github.com/wanteddev/montage-web), also MIT.
package/package.json CHANGED
@@ -1,9 +1,20 @@
1
1
  {
2
2
  "name": "@peppone.choi/ui-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Peppone UI Kit — a React component library (framework-agnostic core + optional Next.js adapter) built on the Wanted Design System (Montage).",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/peppone-choi/Peppone-UI-KIT.git"
10
+ },
11
+ "homepage": "https://github.com/peppone-choi/Peppone-UI-KIT#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/peppone-choi/Peppone-UI-KIT/issues"
14
+ },
15
+ "engines": {
16
+ "node": ">=20"
17
+ },
7
18
  "sideEffects": [
8
19
  "**/*.css"
9
20
  ],
@@ -30,21 +41,6 @@
30
41
  "publishConfig": {
31
42
  "access": "public"
32
43
  },
33
- "scripts": {
34
- "dev": "next dev",
35
- "build:app": "next build",
36
- "start": "next start",
37
- "build": "pnpm build:lib && pnpm build:css",
38
- "build:lib": "tsup",
39
- "build:css": "tailwindcss -i ./src/styles.css -o ./dist/peppone-ui.css",
40
- "typecheck": "tsc --noEmit",
41
- "lint": "eslint",
42
- "test": "vitest run",
43
- "test:watch": "vitest",
44
- "storybook": "storybook dev -p 6006",
45
- "build-storybook": "storybook build",
46
- "prepublishOnly": "pnpm build"
47
- },
48
44
  "peerDependencies": {
49
45
  "next": ">=15",
50
46
  "react": ">=19",
@@ -97,5 +93,19 @@
97
93
  "vitest": "^3",
98
94
  "vitest-axe": "^0.1.0",
99
95
  "vitest-canvas-mock": "^1.1.4"
96
+ },
97
+ "scripts": {
98
+ "dev": "next dev",
99
+ "build:app": "next build",
100
+ "start": "next start",
101
+ "build": "pnpm build:lib && pnpm build:css",
102
+ "build:lib": "tsup",
103
+ "build:css": "tailwindcss -i ./src/styles.css -o ./dist/peppone-ui.css",
104
+ "typecheck": "tsc --noEmit",
105
+ "lint": "eslint",
106
+ "test": "vitest run",
107
+ "test:watch": "vitest",
108
+ "storybook": "storybook dev -p 6006",
109
+ "build-storybook": "storybook build"
100
110
  }
101
- }
111
+ }