kwon-ui 0.0.0-stage → 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/CATALOG.md ADDED
@@ -0,0 +1,322 @@
1
+ # kwon-ui 카탈로그
2
+
3
+ 버전 0.2.0. 이 파일은 `npm run build` 가 `src/catalog/manifest.tsx` 에서 만든다. 직접 고치지 않는다.
4
+
5
+ 화면을 만들기 전에 이 목록에서 고른다. 여기 없는 조각이 필요하면 페이지에서 새로 만들지 않고, kwon-ui 에 추가를 제안한다.
6
+
7
+ ## 쓰는 법
8
+
9
+ ```tsx
10
+ import "kwon-ui/kwon-ui.css"; // 앱의 레이아웃에서 한 번
11
+ import { Page, PageHead, Section, SectionHead, Rows, Row, Pill, TextLink } from "kwon-ui";
12
+ ```
13
+
14
+ - 페이지 맨 바깥에 `.night` 가 있어야 한다(`<Page>` 가 붙인다).
15
+ - 색, 글자 크기, 모서리, 폭은 토큰(`var(--n-…)`)으로만 쓴다. 값을 직접 쓰지 않는다.
16
+ - 링크는 `as` 로 프레임워크의 링크 컴포넌트를 넘긴다: `<Pill as={Link} href="/contact">`.
17
+ - 조각에는 바깥 여백(margin)이 없다. 조각 사이 간격은 앱에서 준다.
18
+ - 제목은 `"**진한 낱말** 나머지"` 꼴의 한 줄로 쓴다. 진한 글자는 핵심 낱말 한두 개에만 준다.
19
+
20
+ ## 한눈에
21
+
22
+ | 조각 | 컴포넌트 | 클래스 | 언제 |
23
+ |---|---|---|---|
24
+ | 첫 화면 제목 | `<Display>` | `.n-display` | 첫 화면의 제목. 한 페이지에 한 번만 씁니다. |
25
+ | 구간 제목 | `<Title>` | `.n-title` | 구간과 페이지의 제목. 한 문장 한 줄로 쓰고, ** 로 감싼 핵심 낱말 한두 개만 진하게 둡니다. |
26
+ | 본문 | `<Body>` | `.n-body` | 읽는 문단. 한 구간에 한두 문단까지 둡니다. 목록에서 줄마다 먼저 읽혀야 하는 낱말(기관 이름)은 line 에 ** 로 감싸 진하게 둡니다. 작은 글 안에서도 같은 hi 를 씁니다. |
27
+ | 작은 제목 | `<CardTitle>` | `.n-card-title` | 목록 줄이나 카드 안의 제목. 본문과 같은 크기이고 진한 글자입니다. |
28
+ | 작은 글 | `<Small>` | `.n-small` | 캡션, 기준, 라벨. 숫자 폭을 맞춰야 하면 num 을 줍니다. |
29
+ | 페이지와 페이지 머리 | `<Page>` `<PageHead>` | `.n-page` | 하위 페이지의 맨 바깥과 머리. 제목 한 줄, 문단 하나, 버튼 줄로 시작합니다. |
30
+ | 구간 | `<Section>` | `.n-sect` | 페이지를 이루는 구간 하나. 위아래 여백(넓은 화면 128/96px)과 내용 폭을 줍니다. rule 은 앞 구간과의 사이에 가는 선을 내용 폭만큼 긋습니다. slim 은 문장 하나와 버튼 하나뿐인 짧은 구간의 여백(넓은 화면 80px)입니다. |
31
+ | 구간 머리 | `<SectionHead>` | `.n-head` | 구간의 시작. 왼쪽에 한 줄 제목, 오른쪽에 본문. rule 을 주면 가는 선 아래에서 시작합니다. |
32
+ | 가는 선 | (클래스만) | `.n-rule` | 내용 위에 가는 선 하나를 긋습니다. 구간 머리의 선은 SectionHead 의 rule 로 긋습니다. |
33
+ | 줄 목록 | `<Rows>` `<Row>` | `.n-rows` | 항목이 여럿일 때. 왼쪽 200px 에 작은 라벨, 오른쪽에 본문 한 칸. 라벨과 본문은 첫 줄의 글자 밑선이 맞습니다. |
34
+ | 검은 판 | `<Panel>` | `.n-panel` | 앞뒤 구간과 구분해야 하는 구간 하나(후기, 수강료처럼). 한 페이지에 두세 번까지. |
35
+ | 그림 틀 | `<Media>` | `.n-media` | 그림과 영상을 담는 틀. 기본 4:3, wide 는 16:9. 안의 img, video 가 틀을 채웁니다. |
36
+ | 사진 판 | `<PhotoPanel>` | `.n-photo` | 어둡게 누른 사진 위 왼쪽 아래에 제목 한 줄과 문단 하나를 둡니다. 강의 현장처럼 사진이 내용인 구간에 씁니다. media 에 img 나 video 를 넘기면 판을 채웁니다. |
37
+ | 연도별 이력 | (클래스만) | `.n-timeline` | 해마다 쌓인 이력. 왼쪽에 연도, 오른쪽에 그 해의 줄들. 많으면 앞의 몇 줄만 보이고 '외 N건'으로 접습니다. |
38
+ | 문의 띠 | `<CtaBand>` | `.n-cta-band` | 페이지 끝의 마지막 행동. 왼쪽에 한 줄 문장, 오른쪽에 버튼 줄. 한 페이지에 하나. |
39
+ | 알약 버튼 | `<Pill>` | `.n-pill` | 한 화면의 주 행동 하나. 높이 48px. ghost 는 어두운 면(둘째 행동이 꼭 버튼이어야 할 때). |
40
+ | 글자 링크 | `<TextLink>` | `.n-link` | 버튼 옆의 둘째 행동, 본문 아래의 '더 보기'. arrow 를 주면 끝에 화살표가 붙습니다. |
41
+ | 버튼 줄 | `<Actions>` | `.n-actions` | 버튼과 글자 링크를 나란히 놓는 줄. 버튼 하나와 링크 하나까지. |
42
+
43
+ ## 글자
44
+
45
+ 읽는 글자 크기는 셋입니다. 제목, 본문, 작은 글. 위계는 굵기 대신 진한 글자와 회색 두 톤으로 만듭니다.
46
+
47
+ ### 첫 화면 제목
48
+
49
+ `<Display>` · `.n-display`
50
+
51
+ 첫 화면의 제목. 한 페이지에 한 번만 씁니다. 구간 제목에는 쓰지 않습니다(Title).
52
+
53
+ ```tsx
54
+ <Display line="**기술과 사람** 사이를 공부합니다" />
55
+ ```
56
+
57
+ ### 구간 제목
58
+
59
+ `<Title>` · `.n-title` `.lo-inline`
60
+
61
+ 구간과 페이지의 제목. 한 문장 한 줄로 쓰고, ** 로 감싼 핵심 낱말 한두 개만 진하게 둡니다. 제목과 부제를 두 줄로 쌓지 않습니다. 숫자는 숫자가 요점일 때만 진하게 둡니다.
62
+
63
+ ```tsx
64
+ <Title line="**문헌 조사**부터 **논문 작성**까지 다룹니다" label="교육 내용" />
65
+ ```
66
+
67
+ ### 본문
68
+
69
+ `<Body>` · `.n-body` `.hi`
70
+
71
+ 읽는 문단. 한 구간에 한두 문단까지 둡니다. 목록에서 줄마다 먼저 읽혀야 하는 낱말(기관 이름)은 line 에 ** 로 감싸 진하게 둡니다. 작은 글 안에서도 같은 hi 를 씁니다. 문단마다 강조를 넣는 데 쓰지 않습니다. 굵기는 바꾸지 않고 색만 올립니다.
72
+
73
+ ```tsx
74
+ <Body>현재 업무와 자료를 함께 살펴보고 먼저 만들 것을 정합니다.</Body>
75
+ <Body as="li" line="**○○전자** 임원 AI 워크숍" />
76
+ ```
77
+
78
+ ### 작은 제목
79
+
80
+ `<CardTitle>` · `.n-card-title`
81
+
82
+ 목록 줄이나 카드 안의 제목. 본문과 같은 크기이고 진한 글자입니다.
83
+
84
+ ```tsx
85
+ <CardTitle>임원 AI 워크숍</CardTitle>
86
+ ```
87
+
88
+ ### 작은 글
89
+
90
+ `<Small>` · `.n-small` `.n-num`
91
+
92
+ 캡션, 기준, 라벨. 숫자 폭을 맞춰야 하면 num 을 줍니다. 본문을 작은 글로 줄여 넣지 않습니다. 길면 빼거나 접습니다.
93
+
94
+ ```tsx
95
+ <Small>홈페이지로 접수된 문의 기준입니다.</Small>
96
+ <Small num>2022 ~ 2026</Small>
97
+ ```
98
+
99
+ ## 뼈대
100
+
101
+ 글은 한 줄기로 흐릅니다. 왼쪽에 제목이나 작은 라벨, 오른쪽 한 칸에 본문을 둡니다.
102
+
103
+ ### 페이지와 페이지 머리
104
+
105
+ `<Page>`, `<PageHead>` · `.night` `.n-page` `.n-page-head` `.n-wrap`
106
+
107
+ 하위 페이지의 맨 바깥과 머리. 제목 한 줄, 문단 하나, 버튼 줄로 시작합니다.
108
+
109
+ ```tsx
110
+ <Page>
111
+ <PageHead
112
+ title="교육 과정"
113
+ lead="직무와 수준에 맞춰 고르실 수 있습니다."
114
+ actions={<Pill as={Link} href="/contact">교육 문의하기</Pill>}
115
+ />
116
+ …
117
+ </Page>
118
+ ```
119
+
120
+ ### 구간
121
+
122
+ `<Section>` · `.n-sect` `.n-wrap` `.ruled` `.slim`
123
+
124
+ 페이지를 이루는 구간 하나. 위아래 여백(넓은 화면 128/96px)과 내용 폭을 줍니다. rule 은 앞 구간과의 사이에 가는 선을 내용 폭만큼 긋습니다. slim 은 문장 하나와 버튼 하나뿐인 짧은 구간의 여백(넓은 화면 80px)입니다. 여백을 구간마다 따로 정하지 않습니다. 구간 머리에 선이 있으면(SectionHead 의 rule) 구간에는 긋지 않습니다.
125
+
126
+ ```tsx
127
+ <Section id="courses" rule>
128
+ <SectionHead title="**직무별** 교육 과정" />
129
+ …
130
+ </Section>
131
+ <Section id="team" slim rule>
132
+ …
133
+ </Section>
134
+ ```
135
+
136
+ ### 구간 머리
137
+
138
+ `<SectionHead>` · `.n-head` `.ruled` `.n-title` `.n-body`
139
+
140
+ 구간의 시작. 왼쪽에 한 줄 제목, 오른쪽에 본문. rule 을 주면 가는 선 아래에서 시작합니다. 본문에는 진한 강조를 두지 않습니다. 제목 칸에는 한 줄에 15자 안팎이 들어갑니다.
141
+
142
+ ```tsx
143
+ <SectionHead rule title="**연구실 상황**에 맞춰 범위를 정합니다" label="진행 방식">
144
+ <Body>연구 분야와 일정을 알려 주시면 범위와 시간을 함께 정합니다.</Body>
145
+ </SectionHead>
146
+ ```
147
+
148
+ ### 가는 선
149
+
150
+ `.n-rule`
151
+
152
+ 내용 위에 가는 선 하나를 긋습니다. 구간 머리의 선은 SectionHead 의 rule 로 긋습니다. 상자 테두리로 쓰지 않습니다. 구분은 선 하나와 여백으로 합니다.
153
+
154
+ ```tsx
155
+ <div className="n-rule">…</div>
156
+ ```
157
+
158
+ ### 줄 목록
159
+
160
+ `<Rows>`, `<Row>` · `.n-rows` `.n-row` `.n-row-label` `.wide-label`
161
+
162
+ 항목이 여럿일 때. 왼쪽 200px 에 작은 라벨, 오른쪽에 본문 한 칸. 라벨과 본문은 첫 줄의 글자 밑선이 맞습니다. 한 줄을 번호, 이름, 설명 세 칸으로 나누지 않습니다.
163
+
164
+ ```tsx
165
+ <Rows as="ol">
166
+ <Row as="li" label="업무 파악">현재 업무와 자료를 함께 살펴보고 먼저 만들 것을 정합니다.</Row>
167
+ <Row as="li" label="유연한 협업">필요한 만큼 만들고, 쓰면서 범위를 조정합니다.</Row>
168
+ </Rows>
169
+ ```
170
+
171
+ ### 검은 판
172
+
173
+ `<Panel>` · `.n-panel` `.n-wrap`
174
+
175
+ 앞뒤 구간과 구분해야 하는 구간 하나(후기, 수강료처럼). 한 페이지에 두세 번까지. 모든 구간을 판으로 감싸지 않습니다.
176
+
177
+ ```tsx
178
+ <Panel id="price">
179
+ <SectionHead title="한 번 결제로 **1년간 수강**합니다" />
180
+ …
181
+ </Panel>
182
+ ```
183
+
184
+ ### 그림 틀
185
+
186
+ `<Media>` · `.n-media` `.wide`
187
+
188
+ 그림과 영상을 담는 틀. 기본 4:3, wide 는 16:9. 안의 img, video 가 틀을 채웁니다.
189
+
190
+ ```tsx
191
+ <Media wide>
192
+ <video src="/lab.mp4" muted loop playsInline />
193
+ </Media>
194
+ ```
195
+
196
+ ### 사진 판
197
+
198
+ `<PhotoPanel>` · `.n-photo` `.n-photo-in`
199
+
200
+ 어둡게 누른 사진 위 왼쪽 아래에 제목 한 줄과 문단 하나를 둡니다. 강의 현장처럼 사진이 내용인 구간에 씁니다. media 에 img 나 video 를 넘기면 판을 채웁니다. 한 페이지에 하나까지 둡니다. 글이 길면 사진 판에 넣지 않습니다.
201
+
202
+ ```tsx
203
+ <PhotoPanel media={<Image src="/hall.jpg" alt="강의 현장" fill sizes="100vw" />}>
204
+ <Title line="**강의 현장**에서 다듬은 과정입니다" label="강사" />
205
+ <Body>실습에 쓰는 자료와 순서는 수업을 거치며 고쳐 왔습니다.</Body>
206
+ </PhotoPanel>
207
+ ```
208
+
209
+ ### 연도별 이력
210
+
211
+ `.n-timeline` `.n-tl-year` `.n-tl-more` `.chev`
212
+
213
+ 해마다 쌓인 이력. 왼쪽에 연도, 오른쪽에 그 해의 줄들. 많으면 앞의 몇 줄만 보이고 '외 N건'으로 접습니다.
214
+
215
+ ```tsx
216
+ <div className="n-timeline">
217
+ <div className="n-tl-year">
218
+ <p className="n-card-title n-num">2026</p>
219
+ <ul>
220
+ <li className="n-body">임원 AI 워크숍</li>
221
+ <li className="n-tl-more">
222
+ <details>
223
+ <summary className="n-small">외 12건 <span className="chev" aria-hidden>→</span></summary>
224
+ …
225
+ </details>
226
+ </li>
227
+ </ul>
228
+ </div>
229
+ </div>
230
+ ```
231
+
232
+ ### 문의 띠
233
+
234
+ `<CtaBand>` · `.n-cta-band` `.n-actions`
235
+
236
+ 페이지 끝의 마지막 행동. 왼쪽에 한 줄 문장, 오른쪽에 버튼 줄. 한 페이지에 하나.
237
+
238
+ ```tsx
239
+ <CtaBand
240
+ title="**시간표**와 **견적서**를 바로 받아 보세요"
241
+ label="교육 문의"
242
+ actions={<>
243
+ <Pill as={Link} href="/contact">교육 문의하기</Pill>
244
+ <TextLink as={Link} href="/courses" arrow>교육 과정 보기</TextLink>
245
+ </>}
246
+ />
247
+ ```
248
+
249
+ ## 조작부
250
+
251
+ 버튼은 하나, 링크도 하나. 한 화면의 주 행동은 알약 버튼 하나입니다. 둘째 행동은 글자 링크로 둡니다.
252
+
253
+ ### 알약 버튼
254
+
255
+ `<Pill>` · `.n-pill` `.ghost`
256
+
257
+ 한 화면의 주 행동 하나. 높이 48px. ghost 는 어두운 면(둘째 행동이 꼭 버튼이어야 할 때). 흰 버튼 둘을 나란히 두지 않습니다.
258
+
259
+ ```tsx
260
+ <Pill as={Link} href="/contact">교육 문의하기</Pill>
261
+ <Pill as="button" type="button" ghost onClick={open}>진단 시작</Pill>
262
+ ```
263
+
264
+ ### 글자 링크
265
+
266
+ `<TextLink>` · `.n-link`
267
+
268
+ 버튼 옆의 둘째 행동, 본문 아래의 '더 보기'. arrow 를 주면 끝에 화살표가 붙습니다.
269
+
270
+ ```tsx
271
+ <TextLink as={Link} href="/about" arrow>회사소개 보기</TextLink>
272
+ ```
273
+
274
+ ### 버튼 줄
275
+
276
+ `<Actions>` · `.n-actions`
277
+
278
+ 버튼과 글자 링크를 나란히 놓는 줄. 버튼 하나와 링크 하나까지.
279
+
280
+ ```tsx
281
+ <Actions>
282
+ <Pill as={Link} href="/signup">얼리버드로 신청하기</Pill>
283
+ <TextLink as={Link} href="#check" arrow>2분 무료 진단</TextLink>
284
+ </Actions>
285
+ ```
286
+
287
+ ## 토큰
288
+
289
+ 값은 `src/css/tokens.css` 에만 있다. `.night` 안에서 쓴다.
290
+
291
+ | 이름 | 값 |
292
+ |---|---|
293
+ | `--n-bg` | `#000000` |
294
+ | `--n-panel` | `#0d0d0d` |
295
+ | `--n-card` | `#171717` |
296
+ | `--n-card-2` | `#202020` |
297
+ | `--n-line` | `rgba(255, 255, 255, 0.1)` |
298
+ | `--n-hi` | `#ffffff` |
299
+ | `--n-soft` | `rgba(255, 255, 255, 0.78)` |
300
+ | `--n-mid` | `rgba(255, 255, 255, 0.5)` |
301
+ | `--n-dim` | `rgba(255, 255, 255, 0.32)` |
302
+ | `--n-btn` | `#ffffff` |
303
+ | `--n-btn-ink` | `#000000` |
304
+ | `--n-btn-hover` | `#e9e9e9` |
305
+ | `--n-ghost` | `rgba(255, 255, 255, 0.08)` |
306
+ | `--n-ghost-hover` | `rgba(255, 255, 255, 0.16)` |
307
+ | `--n-focus` | `rgba(255, 255, 255, 0.55)` |
308
+ | `--n-shade` | `rgba(0, 0, 0, 0.82)` |
309
+ | `--n-shade-soft` | `rgba(0, 0, 0, 0.1)` |
310
+ | `--n-on-photo` | `rgba(255, 255, 255, 0.62)` |
311
+ | `--n-fs-display` | `clamp(34px, 3.8vw, 50px)` |
312
+ | `--n-fs-title` | `clamp(26px, 2.4vw, 32px)` |
313
+ | `--n-fs-body` | `18px` |
314
+ | `--n-fs-small` | `14px` |
315
+ | `--n-fs-control` | `15px` |
316
+ | `--n-radius` | `32px` |
317
+ | `--n-radius-md` | `20px` |
318
+ | `--n-radius-pill` | `999px` |
319
+ | `--n-wrap` | `1064px` |
320
+ | `--n-gutter` | `24px` |
321
+ | `--n-edge` | `clamp(20px, 3.2vw, 48px)` |
322
+ | `--n-font` | `"Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif` |
package/CHANGELOG.md ADDED
@@ -0,0 +1,61 @@
1
+ # 바뀐 것
2
+
3
+ ## 0.2.0 (2026-11-16)
4
+
5
+ 스킬캠퍼스에 넣으면서 앱이 덧붙여 쓰던 여섯 가지를 올렸다. 없어진 이름은 없다.
6
+
7
+ 더한 것
8
+
9
+ - 선으로 시작하는 구간: `.n-sect.ruled`, `<Section rule>`. 앞 구간과의 사이에 가는 선을 내용 폭만큼 긋는다.
10
+ - 짧은 구간: `.n-sect.slim`, `<Section slim>`. 문장 하나와 버튼 하나뿐인 줄. 위아래 64px, 넓은 화면 80px.
11
+ - 본문 안의 진한 낱말: `.hi`, `hi()`, `<Body line>`. `n-body`와 `n-small` 안에서 색만 올린다. 굵기는 그대로다.
12
+ - 사진 판: `.n-photo`, `.n-photo-in`, `<PhotoPanel>`. 어둡게 누른 사진 위 왼쪽 아래에 글을 둔다.
13
+ - 토큰 셋(모두 30개): `--n-shade`, `--n-shade-soft`(사진 위의 그늘), `--n-on-photo`(사진 위에서 받쳐 주는 말).
14
+
15
+ 바뀐 값
16
+
17
+ - 줄 목록 `.n-row`: 라벨과 본문의 첫 줄 밑선을 맞춘다(`align-items: baseline`). 본문 칸은 `minmax(0, 1fr)`이라 긴 낱말이 칸을 밀지 않는다.
18
+ - 글자 링크 `.n-link`: 줄 높이를 1.6으로 정했다. 앱의 바탕 줄 높이를 물려받아 앱마다 높이가 달랐다(홈페이지 24px, 스킬캠퍼스 24.75px).
19
+
20
+ 근거
21
+
22
+ - 구간 선: 스킬캠퍼스 세 페이지의 구간 17곳. Projects 랜딩에도 같은 자리에 선이 있다(그쪽은 선이 바깥 여백까지 닿는다).
23
+ - 짧은 구간: 스킬캠퍼스 세 페이지의 구간 5곳.
24
+ - 줄 목록: 스킬캠퍼스와 Projects 랜딩이 둘 다 밑선 맞춤과 `minmax(0, 1fr)`을 덧붙여 쓰고 있었다.
25
+ - 진한 낱말: 연도별 이력의 기관 이름. 홈페이지는 유틸리티(`text-white`)로, 스킬캠퍼스는 `b`에 규칙을 덧붙여 쓰고 있었다.
26
+ - 사진 판: 홈페이지의 교육 문의 판(`.n-closing`)과 스킬캠퍼스의 강사 판. 그늘, 사진을 누르는 정도, 안쪽 여백, 넓은 화면 높이(560px)는 두 곳이 같다. 두 곳이 다른 값은 스킬캠퍼스 쪽을 따랐다: 좁은 화면 높이 480px, 제목과 문단 사이 16px, 사진 위 회색 글자 62%.
27
+
28
+ 확인
29
+
30
+ - 스킬캠퍼스 공개 네 페이지를 0.1.0과 0.2.0에서 요소별로 대조했다(화면 폭 390·768·1024·1440). 앱의 덧붙임을 지우고도 세 페이지는 차이가 없고, 한 페이지에서 글자 링크 두 곳의 높이만 1px 줄었다. 사진 판은 픽셀이 같다.
31
+ - 홈페이지 원본 CSS와 계산된 스타일을 대조했다. 넣을 때 달라지는 값은 `claude/ADOPTING.md`의 '넣을 때 알아 둘 차이'에 적었다.
32
+
33
+ ## 0.1.0 (2026-11-16)
34
+
35
+ 첫 판. kwonandcompany.com 밤 테마에서 여러 화면이 같이 쓰는 핵심만 값 그대로 옮겼다.
36
+
37
+ - 토큰 27개: 바탕과 면, 글자 네 톤, 버튼, 글자 크기(display, title, body, small, control), 모서리, 폭과 바깥 여백.
38
+ - 글자: `n-display`, `n-title`(두 톤 `lo`, `lo-inline`), `n-body`, `n-card-title`, `n-small`, `n-num`.
39
+ - 뼈대: `n-wrap`, `n-page`, `n-page-head`, `n-sect`, `n-rule`, `n-head`, `n-rows`, `n-row`, `n-row-label`, `n-panel`, `n-media`, `n-cta-band`, `n-timeline`, `n-tl-year`, `n-tl-more`.
40
+ - 조작부: `n-pill`(`ghost`), `n-link`.
41
+ - 컴포넌트 17개: `Display`, `Title`, `Body`, `CardTitle`, `Small`, `Page`, `PageHead`, `Section`, `SectionHead`, `Rows`, `Row`, `Panel`, `Media`, `CtaBand`, `Pill`, `TextLink`, `Actions`. 그리고 `tone()`.
42
+ - 검사기 `kwon-ui check`(기준선 방식), Claude Code 훅 `claude/hook.mjs`.
43
+
44
+ 근거와 확인
45
+
46
+ - 범위는 사용 수로 정했다. 홈페이지에서 `n-` 조각을 쓰는 TSX 45개 파일 가운데 `n-small`은 25개, `n-wrap` 24개, `n-body` 22개, `n-title` 21개, `n-head`와 `n-link` 18개, `n-pill` 15개 파일이 쓴다. 여러 화면이 쓰는 이름과, 다른 제품(스킬캠퍼스, Projects 랜딩)에도 있는 이름을 먼저 담았다.
47
+ - 같은 마크업에 홈페이지 원본 CSS와 이 패키지의 CSS를 각각 입혀 계산된 스타일을 대조했다(요소 38개, 속성 47개, 화면 폭 390·768·1024·1440). 다른 것은 줄바꿈 규칙 둘뿐이다: `word-break: keep-all`과 `text-wrap: balance`·`pretty`. 홈페이지는 같은 규칙을 전역 CSS에서 이미 준다.
48
+
49
+ 홈페이지에 없던 이름(되풀이되던 조합에 이름을 붙였다. 값은 같다)
50
+
51
+ - `.n-head.ruled`: `n-head n-rule pt-12 md:pt-16`을 28곳에서 손으로 쓰던 것.
52
+ - `.n-actions`: 버튼과 글자 링크를 나란히 놓던 `flex flex-wrap items-center gap-x-8 gap-y-4`(18곳).
53
+ - `Rows`, `Row`: 네 페이지가 저마다 따로 정의해 쓰던 줄 목록 컴포넌트.
54
+ - `.n-sr`: 두 톤 제목 앞의 화면 낭독용 구간 이름(Tailwind `sr-only` 대신).
55
+ - `.n-page-head > .n-head`의 아래 여백 0, `.n-cta-band .n-title + .n-body`의 위 여백과 최대 폭: 인라인 스타일과 유틸리티로 쓰던 값.
56
+ - 글자 크기, 버튼 색, 모서리, 내용 폭 토큰: CSS에 직접 적혀 있던 값.
57
+
58
+ 아직 담지 않은 것
59
+
60
+ - 떠 있는 메뉴(세 제품에 다 있지만 제품마다 링크와 동작이 다르다), 첫 화면, 탭, 읽는 페이지(흰 바탕) 조각.
61
+ - 한 제품에서만 쓰는 조각: 그 앱에 둔다.
package/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ Copyright (c) 2026 Kwon and Company Co., Ltd. All rights reserved.
2
+
3
+ 이 저장소의 코드는 권앤컴퍼니 제품에 쓰려고 만든 것입니다. 읽을 수 있게 공개해 두었지만,
4
+ 복제, 수정, 배포를 포함한 사용을 허락하는 라이선스는 부여하지 않습니다.
package/README.md CHANGED
@@ -1,3 +1,72 @@
1
- # Temporary Holding Version
1
+ # kwon-ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 권앤컴퍼니의 세 제품(kwonandcompany.com, 스킬캠퍼스, Projects)이 같이 쓰는 디자인 시스템입니다. 화면의 값과 조각을 한 곳에 두고, 앱은 발행된 버전을 받아 씁니다.
4
+
5
+ ```bash
6
+ npm install kwon-ui --save-exact
7
+ ```
8
+
9
+ ## 들어 있는 것
10
+
11
+ - **토큰과 CSS** (`kwon-ui/kwon-ui.css`): 색, 글자 크기 셋, 모서리, 폭. 글자(`n-title`, `n-body`, `n-small`), 뼈대(`n-sect`, `n-head`, `n-rows`, `n-panel`, `n-photo`), 조작부(`n-pill`, `n-link`).
12
+ - **컴포넌트** (`kwon-ui`): `Page`, `PageHead`, `Section`, `SectionHead`, `Rows`, `Row`, `Panel`, `Media`, `PhotoPanel`, `CtaBand`, `Pill`, `TextLink`, `Actions`, `Title`, `Body`, `Small` 등. 서버에서 그려지는 평범한 마크업입니다.
13
+ - **목록** ([CATALOG.md](CATALOG.md)): 조각마다 언제 쓰는지와 사용 예. 화면을 만들기 전에 먼저 읽습니다.
14
+ - **검사기** (`kwon-ui check`): 앱 코드에서 색·글자 값을 직접 쓴 곳, 시스템 조각을 다시 정의한 곳을 찾습니다. 이미 있는 위반은 기준선에 적어 두고 늘어날 때만 실패합니다.
15
+
16
+ ## 쓰는 법
17
+
18
+ ```tsx
19
+ // 레이아웃에서 한 번
20
+ import "kwon-ui/kwon-ui.css";
21
+
22
+ // 페이지
23
+ import Link from "next/link";
24
+ import { Page, PageHead, Section, SectionHead, Rows, Row, Pill } from "kwon-ui";
25
+
26
+ export default function CoursesPage() {
27
+ return (
28
+ <Page>
29
+ <PageHead
30
+ title="교육 과정"
31
+ lead="직무와 수준에 맞춰 고르실 수 있습니다."
32
+ actions={<Pill as={Link} href="/contact">교육 문의하기</Pill>}
33
+ />
34
+ <Section>
35
+ <SectionHead rule title="**직무별** 교육 과정" />
36
+ <Rows>
37
+ <Row label="임원">조직의 AI 도입 방향을 정합니다.</Row>
38
+ <Row label="실무자">반복 업무를 AI에게 맡기는 방법을 익힙니다.</Row>
39
+ </Rows>
40
+ </Section>
41
+ </Page>
42
+ );
43
+ }
44
+ ```
45
+
46
+ 글꼴(Pretendard)은 앱이 불러옵니다. 패키지에는 글꼴 파일이 없습니다.
47
+
48
+ ## 검사
49
+
50
+ ```bash
51
+ npx kwon-ui check # 기준선보다 위반이 늘면 실패
52
+ npx kwon-ui check --report # 규칙별 수만 보기
53
+ npx kwon-ui baseline # 지금의 위반 수를 기준선으로 적기
54
+ ```
55
+
56
+ 앱에 넣는 순서는 [claude/ADOPTING.md](claude/ADOPTING.md)에 있습니다.
57
+
58
+ ## 카탈로그 보기
59
+
60
+ ```bash
61
+ npm install
62
+ npm run build
63
+ open site/index.html
64
+ ```
65
+
66
+ ## 고치기
67
+
68
+ 조각을 더하거나 바꾸는 규칙은 [CLAUDE.md](CLAUDE.md)에 있습니다. 요점은 세 가지입니다. 새로 디자인하지 않고 운영 중인 화면에서 뽑아냅니다. 두 곳 이상에서 쓰는 것만 올립니다. 값은 `src/css/tokens.css`에만 둡니다.
69
+
70
+ ## 사용 범위
71
+
72
+ © 2026 Kwon and Company Co., Ltd. 권앤컴퍼니 제품에 쓰려고 만든 것이며, 코드를 공개해 두었을 뿐 다른 곳에서의 사용을 허락하는 라이선스는 붙이지 않았습니다.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // kwon-ui 명령.
3
+ // kwon-ui check [파일…] [--report] [--json] 검사한다. 기준선보다 위반이 늘면 종료 코드 1
4
+ // kwon-ui baseline 지금의 위반 수를 기준선으로 적는다
5
+ // kwon-ui classes 시스템 조각과 토큰 이름을 찍는다
6
+ import { loadSystem, RULES, run, WARNINGS, writeBaseline } from "../check/index.mjs";
7
+
8
+ const [cmd, ...rest] = process.argv.slice(2);
9
+ const flags = new Set(rest.filter((a) => a.startsWith("--")));
10
+ const files = rest.filter((a) => !a.startsWith("--"));
11
+
12
+ function total(counts) {
13
+ const byRule = {};
14
+ for (const rules of Object.values(counts)) for (const [r, n] of Object.entries(rules)) byRule[r] = (byRule[r] ?? 0) + n;
15
+ return byRule;
16
+ }
17
+
18
+ try {
19
+ if (cmd === "check") {
20
+ const r = run({ files });
21
+ if (flags.has("--json")) {
22
+ console.log(JSON.stringify({ over: r.over, counts: r.counts, violations: r.violations }, null, 2));
23
+ process.exit(r.over.length && !flags.has("--report") ? 1 : 0);
24
+ }
25
+ const byRule = total(r.counts);
26
+ const sum = Object.values(byRule).reduce((a, b) => a + b, 0);
27
+ const warn = r.violations.filter((v) => v.rule in WARNINGS).length;
28
+ if (flags.has("--report")) {
29
+ console.log(`kwon-ui ${r.system.version} 검사 보고: 파일 ${r.targets.length}개, 위반 ${sum}건, 경고 ${warn}건`);
30
+ for (const [rule, n] of Object.entries(byRule).sort((a, b) => b[1] - a[1])) console.log(` ${String(n).padStart(5)} ${rule}`);
31
+ process.exit(0);
32
+ }
33
+ if (!r.over.length) {
34
+ const note = r.baseline ? ` (기준선에 남은 위반 ${sum}건)` : "";
35
+ console.log(`kwon-ui check: 통과${note}`);
36
+ process.exit(0);
37
+ }
38
+ console.error(`kwon-ui check: 디자인 시스템을 벗어난 곳이 ${r.baseline ? "기준선보다 늘었습니다" : "있습니다"}.\n`);
39
+ for (const o of r.over) {
40
+ console.error(`${o.file} [${o.rule}] ${o.base ? `${o.base}건 → ${o.count}건` : `${o.count}건`}`);
41
+ console.error(` ${RULES[o.rule]}`);
42
+ for (const v of r.violations.filter((x) => x.file === o.file && x.rule === o.rule).slice(0, 8)) console.error(` ${String(v.line).padStart(4)}: ${v.text}`);
43
+ console.error("");
44
+ }
45
+ console.error("조각과 토큰 목록은 node_modules/kwon-ui/CATALOG.md 에 있습니다. 필요한 조각이 없으면 페이지에서 만들지 말고 kwon-ui 에 추가를 제안합니다.");
46
+ process.exit(1);
47
+ } else if (cmd === "baseline") {
48
+ const b = writeBaseline();
49
+ console.log(`기준선을 ${b.path} 에 적었습니다: 파일 ${b.files}개, 위반 ${b.total}건. 이 수는 줄어들기만 해야 합니다.`);
50
+ } else if (cmd === "classes") {
51
+ const s = loadSystem();
52
+ console.log(`kwon-ui ${s.version}\n조각: ${[...s.classes].join(", ")}\n변형: ${[...s.modifiers].join(", ")}\n토큰: ${[...s.tokens].join(", ")}`);
53
+ } else {
54
+ console.log("사용: kwon-ui check [파일…] [--report] [--json] | kwon-ui baseline | kwon-ui classes");
55
+ process.exit(cmd ? 2 : 0);
56
+ }
57
+ } catch (e) {
58
+ console.error(`kwon-ui: ${e.message}`);
59
+ process.exit(2);
60
+ }