@highpixel-co/palda-design-system 0.4.1 → 0.6.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 +21 -2
- package/dist/{chunk-GPCPGABI.js → chunk-6JZPK2W5.js} +10 -2
- package/dist/{chunk-Q4UXASZH.js → chunk-QW2SD3XV.js} +13 -5
- package/dist/guide/LICENSE +21 -0
- package/dist/guide/NOTICE +29 -0
- package/dist/guide/README.md +57 -0
- package/dist/guide/catalog/components.yml +272 -0
- package/dist/guide/catalog/drafts.yml +333 -0
- package/dist/guide/catalog/icons.yml +309 -0
- package/dist/guide/catalog/patterns.yml +132 -0
- package/dist/guide/catalog/tokens.yml +14 -0
- package/dist/guide/docs/ACCESSIBILITY.md +36 -0
- package/dist/guide/docs/AI_UI_DESIGNER_HANDOFF.md +258 -0
- package/dist/guide/docs/COMPONENT_POLICY.md +105 -0
- package/dist/guide/docs/CONSUMER_GUIDE.md +131 -0
- package/dist/guide/docs/CONTENT.md +148 -0
- package/dist/guide/docs/DESIGN_GRAMMAR.md +480 -0
- package/dist/guide/docs/DESIGN_PRINCIPLES.md +263 -0
- package/dist/guide/docs/FIGMA_ALIGNMENT_DELTA.md +141 -0
- package/dist/guide/docs/FIGMA_NAME_MAPPING.md +84 -0
- package/dist/guide/docs/FIGMA_WORKFLOW.md +33 -0
- package/dist/guide/docs/ICON_POLICY.md +175 -0
- package/dist/guide/docs/LAYOUT.md +221 -0
- package/dist/guide/docs/PATTERN_POLICY.md +13 -0
- package/dist/guide/docs/TOKEN_POLICY.md +266 -0
- package/dist/guide/icons/manifest.json +572 -0
- package/dist/harness/check.mjs +330 -0
- package/dist/harness/cli.mjs +88 -0
- package/dist/harness/metadata.json +1325 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/patterns.d.ts +15 -2
- package/dist/patterns.js +2 -2
- package/dist/scripts/check-examples.mjs +406 -0
- package/dist/styles.css +11 -0
- package/dist/ui.d.ts +6 -1
- package/dist/ui.js +1 -1
- package/package.json +13 -2
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
app-shell:
|
|
2
|
+
status: draft
|
|
3
|
+
source: patterns/src/AppShell/AppShell.tsx
|
|
4
|
+
story: patterns/src/AppShell/AppShell.stories.tsx
|
|
5
|
+
figma: TODO
|
|
6
|
+
use_for:
|
|
7
|
+
- 모든 화면의 틀. 사이드바(전체 높이) + 콘텐츠 3단(topRow / statusBar / body)
|
|
8
|
+
- 페이지 여백·폭·배경 위계를 레이아웃 토큰으로 고정
|
|
9
|
+
note: >-
|
|
10
|
+
여백은 0008의 --layout-* 토큰을 쓴다. 상단바 높이 98px은 실측값이고 대응 토큰이 없다.
|
|
11
|
+
상태를 갖지 않는다 — 규칙은 docs/DESIGN_GRAMMAR.md의 §상태는 누가 갖나에 있다.
|
|
12
|
+
breakpoint는 0010의 desktop 1440 / mobile 768을 쓴다. 사이드바는 768 아래에서 가로 줄이 된다.
|
|
13
|
+
섹션 사이 여백은 0011에 따라 --space-8(32)이다. 0012에 따라 전역 상단 바가 없고 상단 행은
|
|
14
|
+
콘텐츠 안(topRow)에 있다. 페이지 제목은 바디 안에 둔다. 사이드바 자체 스크롤은 미정.
|
|
15
|
+
0014에 따라 각 단의 배경은 풀블리드이고 폭 제한은 안쪽 밴드(--layout-content-width)가 갖는다.
|
|
16
|
+
배경 위계는 0013으로 뒤집혔다 — 캔버스가 흰색, 사이드바가 회색이다.
|
|
17
|
+
|
|
18
|
+
card:
|
|
19
|
+
status: draft
|
|
20
|
+
source: patterns/src/Card/Card.tsx
|
|
21
|
+
story: patterns/src/Card/Card.stories.tsx
|
|
22
|
+
figma: TODO
|
|
23
|
+
use_for:
|
|
24
|
+
- 고르는 카드 — 카드 자신이 선택 대상인 개별 객체 (템플릿 카드, 옵션 카드).
|
|
25
|
+
onClick·selected를 항상 짝으로 쓴다. button으로 그려지고 aria-pressed가 붙는다
|
|
26
|
+
- 담는 카드 — 카드는 면이고 누를 것은 안에 둔다. onClick을 주지 않으며 section으로 그려진다
|
|
27
|
+
- 페이지 바디 최상위에 놓이는 요약 블록 (익스텐션 연결 상태 등). 고르지도 열리지도 떠 있지도
|
|
28
|
+
않아도 Card와 나란히 서는 층이면 면이다
|
|
29
|
+
avoid_for:
|
|
30
|
+
- 눌러서 무언가를 여는 카드. onClick은 "누를 수 있다"가 아니라 "고를 수 있다"는 뜻이라
|
|
31
|
+
selected 없이 혼자 쓰지 않는다. 여는 것은 카드 안의 Button이 한다 —
|
|
32
|
+
시안이 "카드 전체 클릭"을 요구해도 담는 카드로 두고 안에 버튼을 놓는다
|
|
33
|
+
- 섹션 묶기 (FormSection을 쓴다)
|
|
34
|
+
- 정보 나열과 통계 (ListRow를 디바이더로 쓴다)
|
|
35
|
+
- 섹션 안에서 값을 강조해 담는 요약 행 (ListRow의 tone="filled"를 쓴다 — 흰 카드가 아니라
|
|
36
|
+
회색 필이다). 같은 내용이라도 페이지 최상위면 Card다
|
|
37
|
+
tone:
|
|
38
|
+
plain: 흰 면(surface/base) + 보더. 기본값이다
|
|
39
|
+
filled: 옅은 회색 필(surface/raised) + 보더. 한 화면에서 앞으로 끌어낼 카드 하나에만 쓴다
|
|
40
|
+
note: >-
|
|
41
|
+
0011에 따라 그림자를 갖지 않는다. shadow/100은 실제로 떠 있는 표면에만 준다. 선택은 채움이
|
|
42
|
+
아니라 accent 보더다. 카드 안에 또 보더 박스를 넣지 않는다(최대 1 depth).
|
|
43
|
+
tone은 채움만 가르고 보더·라운드·여백은 둘이 같다 — 흰 캔버스 위에서는 보더가 유일한
|
|
44
|
+
경계라 plain에서도 빼지 않는다. ListRow의 tone과 같은 축이고 같은 뜻이다. 2026-08-14에
|
|
45
|
+
기본값을 filled에서 plain으로 뒤집었다. 카드가 여럿 선 화면이 무거워서이고, 강조는
|
|
46
|
+
기본값이 아니라 골라서 올리는 것으로 정했다.
|
|
47
|
+
상태를 갖지 않는다 — 규칙은 docs/DESIGN_GRAMMAR.md의 §상태는 누가 갖나에 있다.
|
|
48
|
+
어느 자리에 Card 대신 무엇을 쓰는지는 같은 문서의 §어느 pattern을 고르나에 있다.
|
|
49
|
+
|
|
50
|
+
list-row:
|
|
51
|
+
status: draft
|
|
52
|
+
source: patterns/src/ListRow/ListRow.tsx
|
|
53
|
+
story: patterns/src/ListRow/ListRow.stories.tsx
|
|
54
|
+
figma: TODO
|
|
55
|
+
use_for:
|
|
56
|
+
- 목록의 한 줄. 제목·설명·좌우 슬롯
|
|
57
|
+
- 정보 나열(상태/남은 일수/기한, 잔여 건수)
|
|
58
|
+
- 섹션 안에서 tone="filled"로 값을 강조해 담는 요약 행 (옅은 회색 필, 보더 없음).
|
|
59
|
+
페이지 바디 최상위 블록은 Card다 — 안쪽 여백은 둘 다 16으로 같고 라운드가 갈린다
|
|
60
|
+
- onClick을 주면 누를 수 있는 행(선택 상태 포함)
|
|
61
|
+
- 그 목록이 화면의 본문이면 emphasis="strong"으로 행을 한 칸 올린다
|
|
62
|
+
emphasis:
|
|
63
|
+
default: 제목 body2(14) + 설명 label(12). 곁·요약·참조 나열이다. 기본값이다
|
|
64
|
+
strong: 제목 body1(16) + 설명 body2(14). 선언서의 본문 문장이 가리키는 목록 하나에만 쓴다
|
|
65
|
+
avoid_for:
|
|
66
|
+
- 진행률·CTA·취소를 함께 든 작업 행. leading이 16px 아이콘 고정이라 썸네일이 들어가지 않고,
|
|
67
|
+
세로 여백과 가운데 정렬이 제목·설명 두 줄 기준이라 여러 단 내용이 붙어 보인다.
|
|
68
|
+
2026-08-13 결정 — 이 모양은 여러 화면에 반복되지 않아 pattern으로 올리지 않고 기능 지역
|
|
69
|
+
컴포넌트로 둔다 (docs/PATTERN_POLICY.md). ListRow를 넓혀 담지도 않는다
|
|
70
|
+
note: >-
|
|
71
|
+
0011에 따라 행을 박스로 감싸지 않는다. 구분은 행 사이 디바이더뿐이고 마지막 행에는 없다.
|
|
72
|
+
선택은 보더가 아니라 색이다. onClick이 있으면 chevron이 기본으로 붙고, 오른쪽에 컨트롤이 오면
|
|
73
|
+
chevron={false}로 끈다. 목록 위에서 갈래를 가르는 것은 Tab이다(0023).
|
|
74
|
+
목록 전체의 empty/error는 EmptyState가 맡는다(docs/DESIGN_GRAMMAR.md의 §상태는 누가 갖나).
|
|
75
|
+
뼈대 높이는 텍스트 스타일의 font-size를 따르고 emphasis를 따라 같이 올라간다.
|
|
76
|
+
emphasis는 크기를 고르는 자리가 아니라 이 목록이 화면의 본문인지를 밝히는 자리다 — 판정은
|
|
77
|
+
"선언서의 본문 문장이 가리키는 목록인가" 하나이고 한 화면에 strong은 하나다(0034).
|
|
78
|
+
제목만 올리지 않는다. 16 → 12로 두 칸을 건너뛰면 설명이 각주가 된다.
|
|
79
|
+
tone="filled"와 겹쳐 쓰지 않는다 — 곁이 본문 크기를 갖는 일이다(0032).
|
|
80
|
+
|
|
81
|
+
form-section:
|
|
82
|
+
status: draft
|
|
83
|
+
source: patterns/src/FormSection/FormSection.tsx
|
|
84
|
+
story: patterns/src/FormSection/FormSection.stories.tsx
|
|
85
|
+
figma: TODO
|
|
86
|
+
use_for:
|
|
87
|
+
- 설정·정보 섹션을 제목 아래로 묶기 — 0011 이후 섹션의 기본 단위다
|
|
88
|
+
- 입력 필드를 제목 아래로 묶는 폼의 한 덩어리
|
|
89
|
+
- 섹션 단위 오류 표시
|
|
90
|
+
note: >-
|
|
91
|
+
0011에 따라 보더와 배경을 갖지 않는다. 섹션을 Card로 감싸지 않고 이것을 쓴다. 섹션 사이 여백은
|
|
92
|
+
AppShell이 --space-8(32)로 준다. 라벨↔컨트롤 간격은 TextField가 갖고 있어 여기서 다시 주지
|
|
93
|
+
않는다. 섹션 오류는 role="alert"이고, 필드 하나의 오류는 TextField의 error를 쓴다.
|
|
94
|
+
폼이 아닌 섹션에도 쓰이므로 이름이 좁다 — 개명 여부 확정 대기.
|
|
95
|
+
|
|
96
|
+
live-preview:
|
|
97
|
+
status: draft
|
|
98
|
+
source: patterns/src/LivePreview/LivePreview.tsx
|
|
99
|
+
story: patterns/src/LivePreview/LivePreview.stories.tsx
|
|
100
|
+
figma: TODO
|
|
101
|
+
use_for:
|
|
102
|
+
- 폼 옆에서 입력 결과를 그대로 비추는 2단 구성의 오른쪽 열
|
|
103
|
+
note: >-
|
|
104
|
+
ready/loading/empty/error 네 상태를 모두 갖는다. 폭 319px은 2단 구성 실측값이고 대응 토큰이 없다.
|
|
105
|
+
뼈대 높이 96px도 마찬가지다. 1440 아래에서 2단이 1단이 되며 sticky와 고정폭을 푼다(0010).
|
|
106
|
+
|
|
107
|
+
bottom-bar:
|
|
108
|
+
status: draft
|
|
109
|
+
source: patterns/src/BottomBar/BottomBar.tsx
|
|
110
|
+
story: patterns/src/BottomBar/BottomBar.stories.tsx
|
|
111
|
+
figma: TODO
|
|
112
|
+
use_for:
|
|
113
|
+
- 화면 아래에 붙어 페이지 전체의 액션을 담는 바
|
|
114
|
+
- 저장되지 않은 변경 같은 상태 문구 노출
|
|
115
|
+
note: >-
|
|
116
|
+
AppShell의 footer에 넣는다. 0014에 따라 배경은 풀블리드이고 안쪽 밴드만 --layout-content-width로
|
|
117
|
+
묶인다. 좌우 여백은 상단 행과 같은 --layout-inset이다. 진행 중 표시는 자체 상태가 아니라 안에
|
|
118
|
+
놓인 Button의 disabled로 낸다. 시안 없이 만든 초안이다.
|
|
119
|
+
|
|
120
|
+
empty-state:
|
|
121
|
+
status: draft
|
|
122
|
+
source: patterns/src/EmptyState/EmptyState.tsx
|
|
123
|
+
story: patterns/src/EmptyState/EmptyState.stories.tsx
|
|
124
|
+
figma: TODO
|
|
125
|
+
use_for:
|
|
126
|
+
- 목록이나 검색 결과가 비어 있을 때 안내
|
|
127
|
+
- 다음에 할 일을 액션 하나로 제시
|
|
128
|
+
note: 시안 없이 만든 초안. 액션은 `actionLabel`을 넘길 때만 Button으로 그린다. 폭 480px은 확정 전이다.
|
|
129
|
+
|
|
130
|
+
data-table:
|
|
131
|
+
status: planned
|
|
132
|
+
figma: TODO
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
layout-popup-width:
|
|
2
|
+
status: draft
|
|
3
|
+
token: --palda-layout-popup-width
|
|
4
|
+
source: tokens/layout.tokens.json
|
|
5
|
+
story: tokens/DraftTokenCatalog.stories.tsx
|
|
6
|
+
use_for:
|
|
7
|
+
- 브라우저 확장 팝업처럼 페이지 shell 밖에서 열리는 좁은 독립 표면
|
|
8
|
+
avoid_for:
|
|
9
|
+
- 앱 사이드바
|
|
10
|
+
- 일반 페이지 콘텐츠 열
|
|
11
|
+
- Modal처럼 자체 폭 계약이 있는 컴포넌트
|
|
12
|
+
note: 기존 익스텐션 팝업의 310px content와 좌우 20px inset을 합한 350px 외곽 폭을 보존한 Draft다.
|
|
13
|
+
consumers:
|
|
14
|
+
- 브라우저 확장 팝업 (별도 저장소)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Accessibility
|
|
2
|
+
|
|
3
|
+
**모든 컴포넌트와 화면이 예외 없이 지켜야 하는 접근성 최소선이다.**
|
|
4
|
+
|
|
5
|
+
- 텍스트와 UI의 색상 대비를 확인한다. 수치는 아래 §대비가 갖는다.
|
|
6
|
+
- 모든 조작 요소에 visible focus를 제공한다.
|
|
7
|
+
- icon-only button에는 접근 가능한 이름을 제공한다.
|
|
8
|
+
- 색상만으로 상태를 전달하지 않는다.
|
|
9
|
+
- Form error는 관련 입력과 연결한다.
|
|
10
|
+
- motion 감소 설정을 존중한다.
|
|
11
|
+
|
|
12
|
+
## 대비
|
|
13
|
+
|
|
14
|
+
**읽어야 하는 글자는 아래를 넘는다.** 넘지 못하면 시각 표현이 아니라 위반이다 —
|
|
15
|
+
[`DESIGN_PRINCIPLES.md`](DESIGN_PRINCIPLES.md) §7의 "글자가 배경에 묻힌다 → 읽히는 쪽이 이긴다"에
|
|
16
|
+
수치를 준 것이다.
|
|
17
|
+
|
|
18
|
+
| 무엇 | 하한 |
|
|
19
|
+
| ------------------------------------------------- | ------- |
|
|
20
|
+
| 본문 글자 (18.66px 미만, 또는 24px 미만 non-bold) | `4.5:1` |
|
|
21
|
+
| 큰 글자 (18.66px 이상 bold, 또는 24px 이상) | `3:1` |
|
|
22
|
+
| 조작 요소의 경계와 상태 표시 | `3:1` |
|
|
23
|
+
|
|
24
|
+
흰 배경(`surface/base`) 위 실측이다.
|
|
25
|
+
|
|
26
|
+
| 토큰 | 값 | 대비 |
|
|
27
|
+
| ------------------ | ---------------------- | ------------ |
|
|
28
|
+
| `text/primary` | `gray-900` `#323236` | `12.76:1` |
|
|
29
|
+
| `text/accent` | `purple-600` `#4345e5` | `6.52:1` |
|
|
30
|
+
| `text/primary-sub` | `gray-500` `#86868d` | **`3.62:1`** |
|
|
31
|
+
|
|
32
|
+
**`text/primary-sub`는 14px 본문에서 하한을 넘지 못한다.** 24px 이상이거나 18.66px 이상 bold인
|
|
33
|
+
자리에만 쓴다. 그보다 작은 글자에 쓰면 읽히지 않는다.
|
|
34
|
+
|
|
35
|
+
**아직 정리되지 않은 자리가 있다.** `ListRow`의 `description`이 `label`(12px)에
|
|
36
|
+
`text/primary-sub`라 같은 미달이다. 토큰 값을 올릴지 그 자리만 바꿀지는 결정 대기다.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# AI 기반 UI 생성을 위한 Palda 디자인 시스템 개선 요청
|
|
2
|
+
|
|
3
|
+
## 문서 목적
|
|
4
|
+
|
|
5
|
+
Palda가 목표로 하는 것은 단순히 컴포넌트를 많이 보유하는 것이 아니다.
|
|
6
|
+
|
|
7
|
+
> AI가 Palda 디자인 시스템과 검증 체계를 이용해 일관되고 좋은 UI를 지속적으로 생성하도록 한다.
|
|
8
|
+
|
|
9
|
+
현재 디자인 시스템은 토큰·컴포넌트·패턴·코드 검사 기반이 잘 마련되어 있다. AI가 기존 규칙을
|
|
10
|
+
따라 화면을 조립하고 임의의 색·간격·variant를 만들지 않도록 통제하는 데도 적합하다.
|
|
11
|
+
|
|
12
|
+
다만 AI가 만든 화면이 **기술적으로 일관된 화면**을 넘어 **Palda다운 좋은 화면**인지 판단하려면
|
|
13
|
+
디자인 원칙, 정보 위계, 모바일 기준과 승인 사례가 더 필요하다. 이 문서는 그중 디자이너의 판단과
|
|
14
|
+
산출물이 필요한 항목을 정리한다.
|
|
15
|
+
|
|
16
|
+
## 현재 상태 요약
|
|
17
|
+
|
|
18
|
+
### 이미 잘 갖춰진 것
|
|
19
|
+
|
|
20
|
+
- 색상·간격·타이포그래피·radius·motion·layout 토큰
|
|
21
|
+
- Button·TextField·Badge·Modal 등 기본 UI 컴포넌트
|
|
22
|
+
- AppShell·FormSection·ListRow·Card·EmptyState 등 화면 패턴
|
|
23
|
+
- 각 자산의 `use_for`·`avoid_for`를 기록한 catalog
|
|
24
|
+
- 없는 토큰·아이콘·variant를 AI가 임의로 만들지 못하게 하는 규칙
|
|
25
|
+
- 테스트·Storybook·빌드·생성 파일 동기화를 확인하는 자동 검사
|
|
26
|
+
- Card 남용, pattern 선택과 상태 소유권을 설명하는 디자인 문법
|
|
27
|
+
- Palda 고유 디자인 원칙 — 사용자·인상 3개·핵심 원칙 3개·지양할 디자인
|
|
28
|
+
([`DESIGN_PRINCIPLES.md`](DESIGN_PRINCIPLES.md))
|
|
29
|
+
- 정보 위계와 CTA 배치 — 타이포 위계, 주 액션의 자리와 개수, 화면이 본문을 선언하는 규칙
|
|
30
|
+
|
|
31
|
+
### 현재 부족한 것
|
|
32
|
+
|
|
33
|
+
- 실제 승인 화면과 안티패턴 사례
|
|
34
|
+
- 고정 용어와 날짜·금액·수량 표기 (말투와 버튼 동사는 확정됐다)
|
|
35
|
+
- 모바일 전용 정보 구조와 동작
|
|
36
|
+
- 일부 제품 필수 UI의 디자인 결정
|
|
37
|
+
- AI가 만든 화면을 승인 사례와 비교하는 시각 검증 기준
|
|
38
|
+
|
|
39
|
+
## 가장 먼저 필요한 디자인 결정
|
|
40
|
+
|
|
41
|
+
### 1. Palda 디자인 원칙 — 확정됨
|
|
42
|
+
|
|
43
|
+
[`DESIGN_PRINCIPLES.md`](DESIGN_PRINCIPLES.md)가 받았다. 사용자(스마트스토어 1인 자영업자),
|
|
44
|
+
인상 3개(정돈·편안함·생기), 핵심 원칙 3개, 지양할 디자인(§6), 규칙끼리 부딪힐 때의 순서(§7)가
|
|
45
|
+
들어 있고, 강한 강조는 한 화면에 하나다(§4).
|
|
46
|
+
|
|
47
|
+
남은 것은 §아직 확정되지 않은 것의 3건이다 — 표기(날짜·금액·수량), 로고·일러스트 사용 규칙,
|
|
48
|
+
색 이름.
|
|
49
|
+
|
|
50
|
+
### 2. 정보 위계와 Primary action 배치 — 확정됨
|
|
51
|
+
|
|
52
|
+
**타이포 위계**는 [`TOKEN_POLICY.md`](TOKEN_POLICY.md) §타이포가 갖는다 — 페이지 `headline`(24)
|
|
53
|
+
→ 섹션·카드 `body1`(16) → 행 `body2`(14) → 보조 `label`(12). 화면이 고르는 것은 페이지 제목과
|
|
54
|
+
**본문 목록 여부** 둘이고 나머지는 pattern이 이미 갖고 있다. 선언서의 본문 문장이 가리키는
|
|
55
|
+
목록은 `ListRow`의 `emphasis="strong"`으로 제목 `body1`(16) + 설명 `body2`(14)가 된다
|
|
56
|
+
([`decisions/0034`](https://github.com/highpixel-co/palda-design-system/blob/ad23980481cd81dcf09a1044504d2295f0e4a0f3/docs/decisions/0034-the-body-list-lifts-one-step.md)).
|
|
57
|
+
|
|
58
|
+
**주 액션의 자리**는 [`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md) §주 액션을 어디 두나가 갖는다 —
|
|
59
|
+
페이지 헤더 우측과 `BottomBar` 둘 다 맞는 자리이고, 읽다가 가끔 누르는 화면은 위, 채워 넣고
|
|
60
|
+
마지막에 확정하는 화면은 아래다. **시스템이 고르지 않고 화면이 고른 뒤 선언서에 밝힌다.**
|
|
61
|
+
|
|
62
|
+
**개수는 하나다.** 자리도 하나이고 위아래에 같은 액션을 두 번 두지 않는다. 화면 선언서는
|
|
63
|
+
`주 액션` 한 줄을 반드시 갖고 값이 두 자리 중 하나여야 해서, 자리를 빠뜨리거나 둘로 적으면
|
|
64
|
+
`pnpm verify`의 `check:examples`가 막는다. 마크업에 실제로 몇 개가 그려졌는지는 세지 않는다.
|
|
65
|
+
|
|
66
|
+
**완료 기준을 채웠다.** 화면 목적과 액션을 주면 제목 계층과 CTA 위치가 추측 없이 결정된다.
|
|
67
|
+
|
|
68
|
+
### 3. 승인 화면
|
|
69
|
+
|
|
70
|
+
AI에게 가장 효과적인 기준은 긴 설명보다 실제 정답 화면이다. 처음부터 모든 화면을 준비할 필요는
|
|
71
|
+
없으며, 다음 5종의 대표 화면을 우선 승인한다.
|
|
72
|
+
|
|
73
|
+
1. 목록 화면
|
|
74
|
+
2. 설정·폼 화면
|
|
75
|
+
3. 상세·요약 화면
|
|
76
|
+
4. 검색 결과 없음 또는 Empty State 화면
|
|
77
|
+
5. 모바일 대표 화면
|
|
78
|
+
|
|
79
|
+
각 승인 화면에는 이미지나 Figma 링크만 두지 않고 다음 판단 근거를 짧게 적는다.
|
|
80
|
+
|
|
81
|
+
- 사용자가 이 화면에서 달성하려는 목적
|
|
82
|
+
- 가장 중요한 정보와 Primary action
|
|
83
|
+
- 사용한 pattern과 선택 이유
|
|
84
|
+
- Card로 감싼 영역과 감싸지 않은 영역의 이유
|
|
85
|
+
- 데스크톱과 모바일에서 달라지는 부분
|
|
86
|
+
- loading·empty·error 처리 방식
|
|
87
|
+
|
|
88
|
+
산출물은 [`../examples/approved/`](https://github.com/highpixel-co/palda-design-system/tree/ad23980481cd81dcf09a1044504d2295f0e4a0f3/examples/approved)에 보관한다.
|
|
89
|
+
|
|
90
|
+
**완료 기준:** AI가 새 화면을 만들 때 가장 가까운 승인 화면을 골라 구조와 판단 기준을 재사용할
|
|
91
|
+
수 있다.
|
|
92
|
+
|
|
93
|
+
### 4. 안티패턴
|
|
94
|
+
|
|
95
|
+
AI가 자주 만들 수 있는 잘못된 화면도 시각적으로 남긴다.
|
|
96
|
+
|
|
97
|
+
우선 권장 사례:
|
|
98
|
+
|
|
99
|
+
- 모든 섹션을 Card로 감싼 화면
|
|
100
|
+
- 강하게 강조된 버튼이 여러 개인 화면
|
|
101
|
+
- 읽기 전용 Badge와 누르는 Chip을 혼동한 화면
|
|
102
|
+
- loading·empty·error 상태가 없는 화면
|
|
103
|
+
- 모바일에서 버튼이나 긴 한글이 잘리는 화면
|
|
104
|
+
|
|
105
|
+
각 사례에는 문제가 되는 이유와 올바른 대안을 함께 기록한다. 산출물은
|
|
106
|
+
[`../examples/anti-patterns/`](https://github.com/highpixel-co/palda-design-system/tree/ad23980481cd81dcf09a1044504d2295f0e4a0f3/examples/anti-patterns)에 보관한다.
|
|
107
|
+
|
|
108
|
+
**완료 기준:** AI가 동일한 실수를 반복하지 않고, 잘못된 구현을 구체적인 사례와 비교해 수정할 수
|
|
109
|
+
있다.
|
|
110
|
+
|
|
111
|
+
### 5. 콘텐츠 원칙
|
|
112
|
+
|
|
113
|
+
[`CONTENT.md`](CONTENT.md)가 말투와 존댓말 수준, 버튼·메뉴 동사, 오류·성공·Empty State 문구
|
|
114
|
+
구조를 이미 갖고 있다. 남은 것은 같은 문서 §아직 확정되지 않은 것이다.
|
|
115
|
+
|
|
116
|
+
- 메뉴 라벨
|
|
117
|
+
- 같은 액션에 사용하는 고정 명칭 (후보 표는 있고 확정 전이다)
|
|
118
|
+
- 날짜·금액·수량 표기
|
|
119
|
+
- 대화상자 버튼의 예외 (`취소`·`확인`에 "~기"를 붙일지)
|
|
120
|
+
|
|
121
|
+
**완료 기준:** 같은 기능의 버튼과 상태 문구가 화면마다 다른 표현으로 생성되지 않는다.
|
|
122
|
+
|
|
123
|
+
### 6. 모바일 기준
|
|
124
|
+
|
|
125
|
+
현재 breakpoint는 있지만 모바일 전용 시안과 정보 우선순위는 없다. 최소한 다음 화면 요소의 모바일
|
|
126
|
+
동작을 먼저 확정한다.
|
|
127
|
+
|
|
128
|
+
- AppShell과 내비게이션
|
|
129
|
+
- Form과 TextField
|
|
130
|
+
- List와 Tab
|
|
131
|
+
- Modal
|
|
132
|
+
- BottomBar
|
|
133
|
+
|
|
134
|
+
함께 결정할 항목:
|
|
135
|
+
|
|
136
|
+
- 모바일에서 숨기거나 축약하는 정보
|
|
137
|
+
- 버튼과 조작 요소의 최소 터치 영역
|
|
138
|
+
- 긴 한글과 확대 글꼴 대응
|
|
139
|
+
- Modal과 Table의 모바일 전환 방식
|
|
140
|
+
- BottomBar가 콘텐츠를 가리지 않는 방식
|
|
141
|
+
|
|
142
|
+
**완료 기준:** 데스크톱 화면을 단순히 좁히는 것이 아니라 모바일에서의 정보 순서와 조작 방식이
|
|
143
|
+
정의되어 있다.
|
|
144
|
+
|
|
145
|
+
## 실제 제품 적용에서 확인된 디자인 시스템 갭
|
|
146
|
+
|
|
147
|
+
아래 항목은 추측이 아니라 기존 Palda 화면을 디자인 시스템으로 전환하는 과정에서 확인된 문제다.
|
|
148
|
+
|
|
149
|
+
| 우선순위 | 항목 | 필요한 결정 |
|
|
150
|
+
| -------- | ------------ | --------------------------------------------------------------------- |
|
|
151
|
+
| 높음 | 위험 액션 | 삭제·탈퇴·해제에 사용하는 `danger` Button과 Modal 액션 구성 |
|
|
152
|
+
| 높음 | 인라인 Alert | 정보·경고·오류·성공의 허용 범위와 Toast와의 역할 구분 |
|
|
153
|
+
| 높음 | 로딩 | Spinner와 Skeleton의 사용 조건, Button 처리 중 표현 |
|
|
154
|
+
| 높음 | Select | 단일 선택 UI, 메뉴 패널, 키보드·모바일 동작 |
|
|
155
|
+
| 중간 | TextField | 한 줄 input과 여러 줄 textarea를 `size`로 구분하는 현재 API 유지 여부 |
|
|
156
|
+
| 중간 | Button 크기 | 현재 `sm` 24px의 사용 범위와 최소 터치 영역 충족 방법 |
|
|
157
|
+
| 중간 | Badge | 제품에서 필요한 danger·outline 성격을 Badge에 둘지 별도 방식으로 풀지 |
|
|
158
|
+
| 중간 | ListRow | 긴 trailing 문구의 줄바꿈과 클릭 행의 선택·이동 의미 구분 |
|
|
159
|
+
| 높음 | 접근성 | Tab·NavigationButton·Checkbox의 알려진 낮은 색상 대비 해결 |
|
|
160
|
+
|
|
161
|
+
새 variant나 컴포넌트를 먼저 만들기보다 각 항목의 **사용 상황·금지 상황·상태·모바일 동작**을 먼저
|
|
162
|
+
정의한다.
|
|
163
|
+
|
|
164
|
+
## AI 화면 생성에 필요한 최소 체크리스트
|
|
165
|
+
|
|
166
|
+
다음 절차는 디자인 결정이 반영된 후 AI 작업 규칙으로 사용한다.
|
|
167
|
+
|
|
168
|
+
1. 화면의 사용자 목적과 Primary action을 한 줄로 정의한다.
|
|
169
|
+
2. 가장 가까운 승인 화면을 선택한다.
|
|
170
|
+
3. AppShell과 catalog에서 적합한 pattern을 고른다.
|
|
171
|
+
4. `stable` 자산을 우선 사용한다.
|
|
172
|
+
5. 없는 토큰·variant·아이콘은 임의로 만들지 않고 결정 요청으로 남긴다.
|
|
173
|
+
6. loading·empty·error 상태를 확인한다.
|
|
174
|
+
7. 데스크톱과 모바일에서 실제 렌더를 확인한다.
|
|
175
|
+
8. 긴 한글, 키보드 조작과 접근성을 확인한다.
|
|
176
|
+
9. 승인 화면·안티패턴과 비교한다.
|
|
177
|
+
10. 코드 검증과 Storybook 검토를 통과한다.
|
|
178
|
+
|
|
179
|
+
Catalog 상태에 따른 AI 사용 원칙도 함께 적용한다.
|
|
180
|
+
|
|
181
|
+
| 상태 | AI 사용 원칙 |
|
|
182
|
+
| ------------ | ------------------------------------ |
|
|
183
|
+
| `stable` | 기본적으로 사용 가능 |
|
|
184
|
+
| `draft` | 명시적 허용 또는 디자인 검토 후 사용 |
|
|
185
|
+
| `planned` | 구현에 사용하지 않음 |
|
|
186
|
+
| `deprecated` | 신규 화면에 사용하지 않음 |
|
|
187
|
+
|
|
188
|
+
## 디자이너와 개발 영역의 구분
|
|
189
|
+
|
|
190
|
+
### 디자이너 중심
|
|
191
|
+
|
|
192
|
+
- 디자인 원칙
|
|
193
|
+
- 정보 위계와 CTA 배치
|
|
194
|
+
- 승인 화면과 안티패턴
|
|
195
|
+
- 콘텐츠 원칙
|
|
196
|
+
- 모바일 동작
|
|
197
|
+
- danger·Alert·로딩·Select 등의 시각·사용 규칙
|
|
198
|
+
- 접근성 대비를 만족하는 색상 결정
|
|
199
|
+
|
|
200
|
+
### 디자이너와 개발자 공동
|
|
201
|
+
|
|
202
|
+
- 컴포넌트 property와 상태 배치
|
|
203
|
+
- 반응형 동작의 구현 가능성 확인
|
|
204
|
+
- Figma와 Storybook의 실제 렌더 비교
|
|
205
|
+
- draft에서 stable로 전환하는 승인 기준
|
|
206
|
+
- AI가 생성한 화면의 초기 리뷰
|
|
207
|
+
|
|
208
|
+
### 개발자 중심
|
|
209
|
+
|
|
210
|
+
- 코드 규칙과 자동 검사
|
|
211
|
+
- 데스크톱·모바일 스크린샷 생성
|
|
212
|
+
- 접근성·콘솔 오류·상호작용 자동 검사
|
|
213
|
+
- 보고서 자동화
|
|
214
|
+
- 검증 하네스의 Git·CI 운영과 실행 안전성
|
|
215
|
+
|
|
216
|
+
디자이너가 하네스 내부 구현을 설계할 필요는 없다. 디자이너에게 필요한 것은 하네스가 판정할 수 있는
|
|
217
|
+
명확한 디자인 기준과 승인 사례다.
|
|
218
|
+
|
|
219
|
+
## 권장 진행 순서
|
|
220
|
+
|
|
221
|
+
### 1단계 — AI가 판단할 기준 마련
|
|
222
|
+
|
|
223
|
+
1. ~~디자인 원칙 확정~~ — 완료
|
|
224
|
+
2. ~~정보 위계와 CTA 규칙 확정~~ — 완료
|
|
225
|
+
3. 승인 화면 3~5개 제작
|
|
226
|
+
4. 안티패턴 3~5개 제작
|
|
227
|
+
5. 콘텐츠 원칙 확정
|
|
228
|
+
|
|
229
|
+
### 2단계 — 제품 적용을 막는 DS 갭 해결
|
|
230
|
+
|
|
231
|
+
1. danger Button과 Modal 액션
|
|
232
|
+
2. 인라인 Alert
|
|
233
|
+
3. 로딩 표현
|
|
234
|
+
4. Select
|
|
235
|
+
5. 모바일과 접근성 문제
|
|
236
|
+
|
|
237
|
+
### 3단계 — AI 생성 결과 검증
|
|
238
|
+
|
|
239
|
+
1. 1440px·768px·375px 렌더 확인
|
|
240
|
+
2. 접근성·긴 문구·상태 확인
|
|
241
|
+
3. 승인 화면과 비교
|
|
242
|
+
4. 사람이 승인한 결과를 새 승인 사례로 축적
|
|
243
|
+
|
|
244
|
+
## 디자이너에게 요청하는 최소 산출물
|
|
245
|
+
|
|
246
|
+
첫 단계에서는 전체 디자인 시스템을 완성할 필요가 없다. 아래 산출물이 있으면 AI 기반 UI 생성
|
|
247
|
+
사이클을 시작할 수 있다.
|
|
248
|
+
|
|
249
|
+
- ~~Palda 디자인 원칙 3개~~ — 완료
|
|
250
|
+
- ~~정보 위계와 CTA 배치 규칙~~ — 완료
|
|
251
|
+
- 승인 화면 3개 이상
|
|
252
|
+
- 안티패턴 3개 이상
|
|
253
|
+
- 대표 모바일 화면 1개 이상
|
|
254
|
+
- danger·Alert·로딩의 사용 기준
|
|
255
|
+
- 현재 알려진 색상 대비 문제에 대한 수정안
|
|
256
|
+
|
|
257
|
+
이 산출물을 코드의 토큰·컴포넌트·Storybook과 연결하고, AI가 생성한 새 화면을 승인 사례와 비교해
|
|
258
|
+
검증하는 것은 개발 영역에서 진행한다.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Component Policy
|
|
2
|
+
|
|
3
|
+
**컴포넌트 하나를 만들 때 무엇을 먼저 정하고, variant·size·story를 어떻게 고정하는지의 규칙이다.**
|
|
4
|
+
|
|
5
|
+
각 컴포넌트에는 다음을 정의한다.
|
|
6
|
+
|
|
7
|
+
- 사용 목적과 사용하지 말아야 할 상황
|
|
8
|
+
- Anatomy, variant, size
|
|
9
|
+
- Default, hover, focus, disabled, error 상태
|
|
10
|
+
- 긴 문구와 모바일 동작
|
|
11
|
+
- 접근성 요구사항
|
|
12
|
+
- Do/Don't 예시
|
|
13
|
+
|
|
14
|
+
컴포넌트마다의 용도와 쓰지 말아야 할 상황은 [`catalog/components.yml`](../catalog/components.yml)에
|
|
15
|
+
적는다. prop 이름과 기본값은 타입이 원본이므로 문서로 옮겨 적지 않는다.
|
|
16
|
+
|
|
17
|
+
**새 컴포넌트는 코드보다 먼저 property 목록과 상태 배치표를 낸다.** 상태는
|
|
18
|
+
[`decisions/0004`](https://github.com/highpixel-co/palda-design-system/blob/ad23980481cd81dcf09a1044504d2295f0e4a0f3/docs/decisions/0004-component-state-naming.md)의 다섯 종류(상호작용 · 네이티브 ·
|
|
19
|
+
값에서 파생 · 컴포넌트 소유 · 부모가 정함)에 배치하고, **prop으로 빼도 되는 것은 "부모가 정함"
|
|
20
|
+
하나뿐이다.** `hovered`·`focused`·`filled` 같은 prop은 만들지 않는다. pattern에서 어느 것이
|
|
21
|
+
상태를 소유하는지는 [`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md)의 §상태는 누가 갖나에 있다.
|
|
22
|
+
|
|
23
|
+
## 시안은 HTML로 먼저 확정한다
|
|
24
|
+
|
|
25
|
+
**property 목록이 확정된 다음, 코드보다 먼저 HTML 시안을 낸다.** `components/src/`에 파일을
|
|
26
|
+
만들거나 story를 쓰는 것은 사용자가 그 시안을 보고 확정한 뒤다. 시안 없이 곧장 Storybook으로
|
|
27
|
+
가지 않는다.
|
|
28
|
+
|
|
29
|
+
- 시안은 정적 HTML 한 장이다. `tokens/generated/tokens.css`를 그대로 불러 쓰고, 값을 눈으로
|
|
30
|
+
맞추지 않는다. 시안에서 쓴 토큰이 그대로 구현으로 넘어가야 한다.
|
|
31
|
+
- variant · size · 상태(default, hover, focus, disabled, error)를 한 화면에 늘어놓아 무엇을
|
|
32
|
+
확정하는지가 보이게 한다. 확정 대상은 story가 아니라 이 화면이다.
|
|
33
|
+
- 아이콘도 같다. `icons/svg/`에 넣기 전에 HTML로 기존 아이콘 옆에 놓고 시각 무게를 대조한다
|
|
34
|
+
([`ICON_POLICY.md`](ICON_POLICY.md) §고를 때의 contact-sheet 승인이 이 단계다).
|
|
35
|
+
- 시안 파일은 `preview/` 아래 untracked로 두고 커밋하지 않는다. 확인용 코드는 저장소에 남지
|
|
36
|
+
않는다.
|
|
37
|
+
- **화면 시안은 선언서와 소속 표시를 갖는다.** 파일 맨 위 주석에 `본문`·`주 액션`·`곁` 표를 두고,
|
|
38
|
+
셸 바디 밴드의 직계 자식마다 `data-screen-role="헤더|본문|곁"`을 단다. 화면을 그리기 전에 무엇이
|
|
39
|
+
이 화면 것인지 정하기 위한 것이고, 곁의 규칙은
|
|
40
|
+
[`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md) §곁을 어디 두나에 있다. **`pnpm verify`의
|
|
41
|
+
`check:examples`가 막는다** — 표시가 없거나, 곁의 개수가 선언서와 다르거나, 곁이 본문 pattern·강조
|
|
42
|
+
채움을 쓰거나, 곁 안에 블록 요소가 있거나, 페이지 헤더가 첫 블록이 아니거나, 본문 블록 사이에
|
|
43
|
+
다른 것이 끼면 실패한다. 곁이 본문보다 앞에 오는 것은 실패가 아니다
|
|
44
|
+
([`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md) §자리는 시스템이 정하지 않는다). 컴포넌트 시안은 셸이 없어 이 검사를 받지 않는다.
|
|
45
|
+
- **렌더해서 흐리게 본다.** 시안을 실제로 그린 뒤, 스크린샷을 흐리게 봤을 때 가장 먼저 보이는
|
|
46
|
+
것이 본문이어야 한다. 상태 배너·경고처럼 면적이 큰 것이 본문보다 먼저 보이면 그것을 뺀다
|
|
47
|
+
([`decisions/0024`](https://github.com/highpixel-co/palda-design-system/blob/ad23980481cd81dcf09a1044504d2295f0e4a0f3/docs/decisions/0024-state-belongs-to-the-nearest-owner.md)). 검사가 읽지
|
|
48
|
+
못하는 자리라 이 확인은 사람이 한다.
|
|
49
|
+
- **시안은 자기 클래스만 정의한다.** 시스템 클래스(`.palda-*`)를 셀렉터에 쓰지 않는다 — 붙이는
|
|
50
|
+
것은 마크업이 하고, 늘리거나 자리를 잡는 것은 감싸는 자리가 한다. pattern은 `className`을 받지
|
|
51
|
+
않아서(컴포넌트 18개는 전부 받고 pattern 8개는 전부 안 받는다), 밖에서 잡은 스타일은 구현으로
|
|
52
|
+
넘어가지 못한다. **`pnpm verify`의 `check:css`가 막는다** — 시안의 인라인 `<style>`은 컴포넌트
|
|
53
|
+
CSS와 같은 규칙(`:focus-visible`, 보더 1px, 정의된 토큰만)도 함께 받는다.
|
|
54
|
+
|
|
55
|
+
확정 전에 구현을 시작하지 않는 이유는 property를 먼저 내는 것과 같다 — 코드가 다 써진 뒤에
|
|
56
|
+
논의가 시작되면 되돌리기가 비싸다.
|
|
57
|
+
|
|
58
|
+
## Variant가 뜻하는 것
|
|
59
|
+
|
|
60
|
+
색이 뜻하는 것은 시스템 전체가 같다. 컴포넌트마다 다시 정하지 않는다.
|
|
61
|
+
|
|
62
|
+
| variant | 색 | 뜻 |
|
|
63
|
+
| ----------- | --------- | ---------------- |
|
|
64
|
+
| `primary` | 검정·회색 | 중립. 기본값이다 |
|
|
65
|
+
| `accent` | 보라 | 브랜드 강조 |
|
|
66
|
+
| `secondary` | 주황 | 보조 강조 |
|
|
67
|
+
| `danger` | 빨강 | 위험과 오류 |
|
|
68
|
+
| `outline` | 테두리만 | 저강조 |
|
|
69
|
+
| `subtle` | 채움 없음 | 최소 강조 |
|
|
70
|
+
|
|
71
|
+
**한 그룹에 채움이 강한 액션은 하나만 둔다.** 나머지는 `outline`이나 `subtle`로 내린다. 강조가
|
|
72
|
+
여러 개면 무엇을 눌러야 하는지가 사라진다.
|
|
73
|
+
|
|
74
|
+
컴포넌트마다 이 색을 어떤 상황에 쓰는지가 갈리면 `catalog/components.yml`의 `variant`에 적는다.
|
|
75
|
+
Button의 `outline`이 "취소·뒤로"인 것처럼, 색 이상의 뜻이 붙을 때만 적는다.
|
|
76
|
+
|
|
77
|
+
## Size
|
|
78
|
+
|
|
79
|
+
`size`가 있는 컴포넌트의 기본값은 `md`다. **TextField만 `sm`이 기본이다** — 한 줄 입력이 더
|
|
80
|
+
흔해서다.
|
|
81
|
+
|
|
82
|
+
`sm`은 좁은 영역과 조밀한 화면에 쓴다. 크기를 강조 수단으로 쓰지 않는다.
|
|
83
|
+
|
|
84
|
+
## 구현 규칙
|
|
85
|
+
|
|
86
|
+
**Variant는 시안에 있는 것만 만든다.** 대응하는 token이 존재한다는 이유로 시안에 없는 variant를 추가하지 않는다. 이름은 시스템 전체에서 `outline`, `subtle`을 쓴다. `outlined`, `ghost`는 쓰지 않는다.
|
|
87
|
+
|
|
88
|
+
**Disabled는 variant마다 다르게 처리한다.** `opacity`로 일괄 처리하지 않고, variant별로 배경과 텍스트 token을 각각 지정한다. 채움이 있는 variant는 `{family}/subtle`로 바뀌고, 아웃라인과 고스트는 텍스트만 `text/disabled`로 바뀐다.
|
|
89
|
+
|
|
90
|
+
**로딩은 별도 상태로 두지 않는다.** `loading` prop을 만들지 않고 `disabled`로 표현한다. 스피너나 라벨 교체 같은 동작을 임의로 넣지 않는다.
|
|
91
|
+
|
|
92
|
+
**타이포그래피는 텍스트 스타일 클래스로 적용한다.** `palda-text-*` 클래스를 붙이고, `font-size`·`font-weight`·`line-height`를 컴포넌트 CSS에서 따로 조립하지 않는다. Figma에서 텍스트 스타일이 바뀌면 컴포넌트가 따라가야 한다.
|
|
93
|
+
|
|
94
|
+
## Story 구성
|
|
95
|
+
|
|
96
|
+
**Story는 시안 variant와 Matrix 하나로 끝낸다.** 만들어도 되는 story는 다음 두 종류뿐이다.
|
|
97
|
+
|
|
98
|
+
1. 시안에 정의된 variant별 story
|
|
99
|
+
2. 전체 조합을 한눈에 보는 Matrix story 하나
|
|
100
|
+
|
|
101
|
+
**그 외에는 어떤 story도 임의로 추가하지 않는다.** 사용 예시, 그룹 조합, 폼 안에서의 배치, 긴 문구나 예외 케이스처럼 "있으면 도움이 될 것 같은" story를 판단해서 넣지 않는다. 필요해 보이면 만들지 말고 먼저 확인받는다.
|
|
102
|
+
|
|
103
|
+
**나누는 축은 variant 하나뿐이다.** `size`, 상태(`disabled`, `checked`), 라벨·아이콘 유무는 story로 만들지 않고 `argTypes`의 Controls로 내린다. 그래서 **story 수 = 시안 variant 개수 + 1(Matrix)** 이고, 이 수를 넘기면 축을 잘못 잡은 것이다.
|
|
104
|
+
|
|
105
|
+
이름은 variant가 있으면 variant 이름(`Primary`, `Outline`), 없으면 `Default` 하나를 둔다. `Medium`/`Small`처럼 size로 짓지 않는다. 전체 조합 story는 항상 `Matrix`다.
|