@highpixel-co/palda-design-system 0.4.1 → 0.5.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/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 +124 -0
- package/dist/guide/catalog/tokens.yml +14 -0
- package/dist/guide/docs/ACCESSIBILITY.md +10 -0
- package/dist/guide/docs/AI_UI_DESIGNER_HANDOFF.md +256 -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 +378 -0
- package/dist/guide/docs/DESIGN_PRINCIPLES.md +254 -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 +255 -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 +1323 -0
- package/dist/scripts/check-examples.mjs +444 -0
- package/package.json +13 -2
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Icon Policy
|
|
2
|
+
|
|
3
|
+
**아이콘을 고르는 기준과, 없을 때 멈추는 법, 새로 그릴 때 지키는 형태 규격이다.**
|
|
4
|
+
|
|
5
|
+
- 형식: SVG
|
|
6
|
+
- 기준 캔버스: `24 × 24`
|
|
7
|
+
- live area: `20 × 20` (사방 여백 `2` 이상)
|
|
8
|
+
- 렌더 사이즈: `4` 단위로 자유롭게 키우고 줄인다 (`16`, `20`, `24`, `28` …)
|
|
9
|
+
- 색상: `currentColor`
|
|
10
|
+
- 스타일: outline, round cap, round join
|
|
11
|
+
- stroke 굵기: `1.5`
|
|
12
|
+
- stroke 위치: inside
|
|
13
|
+
- 코너 radius: `2`
|
|
14
|
+
- SVG 원본은 `icons/svg/`에 저장한다.
|
|
15
|
+
- 모든 아이콘은 이름, 의미와 금지 용도를 `icons/manifest.json`에 기록한다.
|
|
16
|
+
- `catalog/icons.yml`은 manifest에서 생성한다(`pnpm icons:catalog`). 항목을 손으로 추가하지 않고,
|
|
17
|
+
`status`와 `figma`만 고친다. 생성 결과가 커밋과 다르면 `pnpm check:generated`가 실패한다.
|
|
18
|
+
- 신규 아이콘은 만들기 전에 아래 §고를 때를 따르고, `contact-sheet`에서 기존 아이콘과 시각적
|
|
19
|
+
무게를 비교해 승인한다.
|
|
20
|
+
|
|
21
|
+
## 고를 때
|
|
22
|
+
|
|
23
|
+
**필요한 아이콘이 없으면 만들지 말고 먼저 확인받는다.** token과 같은 규칙이다
|
|
24
|
+
([`TOKEN_POLICY.md`](TOKEN_POLICY.md)). 아이콘의 모양과 뜻은 `contact-sheet` 승인으로 확정된다.
|
|
25
|
+
|
|
26
|
+
**아이콘의 원본은 `icons/svg/`다.** Figma는 미러이고, 어긋나면 SVG가 옳다
|
|
27
|
+
([`0016`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0016-asset-ssot-boundary.md)). `catalog/icons.yml`의 `figma`가 아직 전부
|
|
28
|
+
`TODO`인 것은 미러가 안 만들어졌다는 뜻이지, 저장소가 앞서 있다는 신호가 아니다.
|
|
29
|
+
|
|
30
|
+
자리에 맞는 아이콘을 찾을 때는 셋 중 하나를 고른다. 가운데를 비워 두면 애매한 것이 전부
|
|
31
|
+
"만든다"로 흘러간다.
|
|
32
|
+
|
|
33
|
+
| 상황 | 한다 |
|
|
34
|
+
| ------------------------------------ | ------------------------------------------------------- |
|
|
35
|
+
| 뜻이 맞는 아이콘이 있다 | 쓴다 |
|
|
36
|
+
| 딱 맞진 않아도 뜻이 통하는 것이 있다 | 그것으로 갈음하고, 어느 자리에 무엇이 아쉬웠는지 남긴다 |
|
|
37
|
+
| 없다 | 만들지 않고 멈춘다 |
|
|
38
|
+
|
|
39
|
+
맞는지는 `icons/manifest.json`의 `description`과 `avoidFor`로 판단한다. `avoidFor`에 걸리면
|
|
40
|
+
비슷해 보여도 쓰지 않는다. 그 목록이 아이콘 사이의 경계선이라, 어기면 세트의 의미 구분이 흐려진다.
|
|
41
|
+
|
|
42
|
+
없어서 멈출 때는 `blank`를 놓거나 아이콘 없이 둔다. `blank`가 그러라고 있는 placeholder다.
|
|
43
|
+
|
|
44
|
+
### 없을 때 내는 것
|
|
45
|
+
|
|
46
|
+
그림이 아니라 **manifest 항목 초안**을 낸다. 이름, 뜻, 금지 용도 셋이다.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
"warning": {
|
|
50
|
+
"description": "되돌릴 수 있는 주의 상황을 알릴 때 사용",
|
|
51
|
+
"category": "status",
|
|
52
|
+
"avoidFor": ["오류", "정보 안내", "차단된 동작"],
|
|
53
|
+
"origin": { "set": "Palda" }
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`origin`은 어디서 왔는지다. 직접 그리면 `{ "set": "Palda" }`, 다른 세트에서 가져오면
|
|
58
|
+
아래 §출처를 따른다.
|
|
59
|
+
|
|
60
|
+
이름과 뜻이 확정된 뒤에 그린다. 그리고 나서도 `icons/svg/`에 바로 넣지 않고, **HTML로 기존
|
|
61
|
+
아이콘 옆에 놓아 보여주고 확정받는다** ([`COMPONENT_POLICY.md`](COMPONENT_POLICY.md) §시안은
|
|
62
|
+
HTML로 먼저 확정한다). 그림을 먼저 그리면 무엇을 그렸는지가 무슨 뜻인지를 정해 버린다.
|
|
63
|
+
[`AGENTS.md`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/AGENTS.md) §항상의 "지어내지 않고, 허용 상태와 사용 조건을 먼저 정의해 확인받는다"를
|
|
64
|
+
아이콘에 적용한 것이다.
|
|
65
|
+
|
|
66
|
+
### 새 아이콘이 들어왔을 때
|
|
67
|
+
|
|
68
|
+
아이콘이 추가되면 **그 아이콘을 기다리던 자리를 다시 본다.** 어디인지는 위 "갈음하고 남긴다"의
|
|
69
|
+
기록과, 아이콘이 없어서 열어 둔 prop에 남아 있다.
|
|
70
|
+
|
|
71
|
+
2026-08-03에 `chevron-down`이 들어왔지만 DropdownButton은 그대로 `trailingIcon` prop으로 chevron을
|
|
72
|
+
받고 있었다. 컴포넌트 주석에 "chevron-down 아이콘이 들어오면 이 prop을 없애고 컴포넌트가 직접
|
|
73
|
+
그린다"고 적어 두었는데도 아무도 그 주석과 맞춰보지 않았고, 넘기는 것을 잊으면 chevron이 조용히
|
|
74
|
+
사라지는 상태로 사흘을 보냈다. 새 아이콘은 쓸 자리를 만들 때가 아니라 **들어온 때에** 한 번 훑는다.
|
|
75
|
+
|
|
76
|
+
## 출처
|
|
77
|
+
|
|
78
|
+
**아이콘마다 어디서 왔는지를 manifest에 적는다.** 세트마다 라이선스가 다르고, 들어온 때에
|
|
79
|
+
안 적으면 나중에 되짚을 수 없다. 근거는 [`decisions/0027`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0027-icons-record-where-they-came-from.md)이다.
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
"copy": {
|
|
83
|
+
"origin": { "set": "Lucide", "license": "ISC", "url": "https://lucide.dev" }
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
직접 그린 것은 `{ "set": "Palda" }` 하나면 된다. 외부 세트는 `license`와 `url`도 함께 적는다.
|
|
88
|
+
|
|
89
|
+
`icons/NOTICE`는 이 값에서 생성된다. 직접 고치지 않고 manifest를 고친 뒤 `pnpm generate`를
|
|
90
|
+
돌린다. **처음 보는 세트를 적으면 `scripts/generate-icon-notice.mjs`가 멈춘다** — 그 세트의
|
|
91
|
+
라이선스 전문을 `LICENSE_TEXTS`에 먼저 넣어야 한다. 출처만 적고 라이선스를 안 챙기는 것을
|
|
92
|
+
막는 자리다.
|
|
93
|
+
|
|
94
|
+
`pnpm verify`가 `origin` 없는 아이콘과, 생성된 `icons/NOTICE`가 manifest와 어긋나는 것을
|
|
95
|
+
함께 막는다.
|
|
96
|
+
|
|
97
|
+
## 색상
|
|
98
|
+
|
|
99
|
+
코드에서 아이콘은 `stroke="currentColor"`만 쓰고 색을 직접 지정하지 않는다. 기본값은 부모에서
|
|
100
|
+
상속된 `color/primary/default`이고, 아이콘을 쓰는 컴포넌트가 `color`로 덮어써서 상황에 맞는
|
|
101
|
+
색을 준다. 아이콘 쪽에 색 토큰을 박으면 이 덮어쓰기가 막히므로 하지 않는다.
|
|
102
|
+
|
|
103
|
+
## Figma 변수
|
|
104
|
+
|
|
105
|
+
아이콘은 변수 중 **컬러 변수만** 쓴다. 기본 색은 `color/primary/default`에 바인딩하고,
|
|
106
|
+
hex 리터럴로 두지 않는다.
|
|
107
|
+
|
|
108
|
+
나머지는 변수로 지정하지 않는다. stroke 굵기, 사이즈, radius, 여백은 이 문서의 값을 리터럴로
|
|
109
|
+
넣는다. 아이콘은 도형 자체가 의미를 만들어서 값이 아이콘마다 달라지고, 변수로 묶으면 그 자유도가
|
|
110
|
+
사라진다.
|
|
111
|
+
|
|
112
|
+
## 이름
|
|
113
|
+
|
|
114
|
+
파일 이름은 소문자 kebab-case다 (`bell`, `arrow-right`). 컴포넌트 이름은 여기서 파생되므로
|
|
115
|
+
(`arrow-right` → `ArrowRightIcon`) 파일 이름을 바꾸면 코드의 import도 함께 바뀐다.
|
|
116
|
+
|
|
117
|
+
같은 뜻에는 항상 같은 단어를 써서 검색이 되게 한다.
|
|
118
|
+
|
|
119
|
+
- 방향: `-up` · `-down` · `-left` · `-right`
|
|
120
|
+
- 액션: `-plus` · `-minus` · `-check` · `-x`
|
|
121
|
+
- 형태 변형: 하이픈 두 개로 접미사를 붙인다 (`bell--filled`)
|
|
122
|
+
|
|
123
|
+
하이픈 하나로 쓴 이름과 컴포넌트 이름이 겹치는 조합은 만들지 않는다. `bell-filled`와
|
|
124
|
+
`bell--filled`는 둘 다 `BellFilledIcon`이 되어 충돌한다.
|
|
125
|
+
|
|
126
|
+
접미사는 **형태가 다른 변형에만** 쓴다. 색만 다른 상태(`disabled`, `hover`)는 아이콘을 따로
|
|
127
|
+
만들지 않는다. 색은 위 §색상대로 아이콘을 쓰는 컴포넌트가 정한다.
|
|
128
|
+
|
|
129
|
+
## 사이즈
|
|
130
|
+
|
|
131
|
+
아이콘은 부모의 `font-size`를 그대로 따라가지 않는다. 그 값에서 가장 가까운 `4`의 배수로
|
|
132
|
+
반올림한 크기로 렌더한다. `font-size`가 `14`면 `16`, `18`이면 `20`, `22`면 `24`다.
|
|
133
|
+
글자 크기가 어떻게 정해지든 아이콘은 항상 `4` 단위 그리드에 맞는다.
|
|
134
|
+
|
|
135
|
+
구현은 생성된 컴포넌트의 `style`에 들어가는 `round(1em, 4px)`다. 그래서 `width`/`height`
|
|
136
|
+
prop으로는 크기가 바뀌지 않는다. 크기는 부모의 `font-size`로 주고, 반올림을 벗어나야 하는
|
|
137
|
+
예외 상황에서만 `style`로 덮어쓴다.
|
|
138
|
+
|
|
139
|
+
## 여백
|
|
140
|
+
|
|
141
|
+
`24 × 24` 캔버스에서 아이콘이 쓰는 영역은 가운데 `20 × 20`이다. 사방으로 `2`를 비운다.
|
|
142
|
+
|
|
143
|
+
이 여백은 path 좌표가 아니라 **stroke 바깥** 기준으로 잰다. stroke `1.5`는 path 바깥으로
|
|
144
|
+
`0.75`씩 번지므로, 도형 좌표는 `2.75 ~ 21.25` 안에 들어와야 한다. `blank.svg`의
|
|
145
|
+
`rect x="2.75" width="18.5"`가 정확히 이 값이다.
|
|
146
|
+
|
|
147
|
+
`2`는 넘지 말아야 할 상한선이고, 모든 아이콘이 맞닿아야 할 기준선이 아니다. live area 안에서
|
|
148
|
+
얼마를 쓸지는 형태마다 다르게 잡는다. 같은 `20`이라도 원은 정사각보다 작아 보이고, 가로로
|
|
149
|
+
뻗는 형태는 세로 여백이 더 남는다. `search.svg`가 왼쪽 `3.75` / 오른쪽 `3.25`로 비대칭인 것은
|
|
150
|
+
돋보기 손잡이가 오른쪽 아래로 뻗는 것을 상쇄한 광학 보정이고, 이런 보정은 그대로 둔다.
|
|
151
|
+
|
|
152
|
+
## 형태
|
|
153
|
+
|
|
154
|
+
- 코너 radius는 `2`로 고정한다. 아이콘마다 다른 값을 쓰지 않는다.
|
|
155
|
+
- 획 굵기는 한 아이콘 안에서 섞지 않는다. 세트 전체가 `1.5` 하나다.
|
|
156
|
+
- 같은 형태는 최소한의 앵커포인트로 그린다. 곡선에 노드를 과하게 두지 않는다.
|
|
157
|
+
- 정렬은 수학적 중앙이 아니라 시각 무게중심에 맞춘다.
|
|
158
|
+
- 라인과 필을 한 화면에서 섞지 않는다. 세트는 outline 전용이고, 필이 필요하면 별도 변형으로
|
|
159
|
+
페어를 만들어 정한다. 지금은 필 변형이 없다.
|
|
160
|
+
- `16`px에서 형태가 뭉개지면 그 크기 전용 옵티컬 버전을 만든다.
|
|
161
|
+
|
|
162
|
+
안쪽 여백은 아이콘마다 다를 수 있다. 의미가 더 잘 드러나는 쪽을 택하고, 판단은 `contact-sheet`에서
|
|
163
|
+
기존 아이콘과 톤·밀도·여백을 눈으로 대조해서 한다.
|
|
164
|
+
|
|
165
|
+
## 그리드와 픽셀 스냅
|
|
166
|
+
|
|
167
|
+
**그릴 때는** 주요 좌표를 정수나 하프픽셀(`.5`)에 맞춘다. `1.5` stroke는 그리드에 정렬되지
|
|
168
|
+
않으면 흐릿해진다.
|
|
169
|
+
|
|
170
|
+
**내보낸 뒤에는 손대지 않는다.** Figma의 inside stroke는 SVG에 그대로 표현되지 않아서, 내보낸
|
|
171
|
+
좌표가 stroke 굵기의 절반만큼 안쪽으로 들어온다. `1.5` 기준으로 `0.75`다. 그래서 export에는
|
|
172
|
+
`2.75`나 `1.25`처럼 `0.25` 단위 값이 남고, 이건 정상이다. 이 값을 정수나 하프픽셀로 되돌리면
|
|
173
|
+
stroke가 오히려 픽셀 그리드에서 어긋난다. 스냅은 그리는 단계에서 하고, 내보낸 값은 그대로 둔다.
|
|
174
|
+
|
|
175
|
+
> 크기별 보정 값은 디자이너가 확정한다.
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Layout
|
|
2
|
+
|
|
3
|
+
**페이지를 조립하는 여백·폭 스케일과, 모든 화면이 공유하는 셸 구조다.**
|
|
4
|
+
|
|
5
|
+
컴포넌트 안쪽을 재는 `--space-*`와 다른 스케일이고, 섞어 쓰지
|
|
6
|
+
않는다. 계층을 나눈 이유는
|
|
7
|
+
[`decisions/0008`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0008-layout-scale-separate-from-component-scale.md)에 있다.
|
|
8
|
+
|
|
9
|
+
값은 Figma 3화면에서 같은 값이 반복되는 것을 확인한 실측이다. 지어낸 값이 없다. 실측이 아니라
|
|
10
|
+
고른 값은 그 자리에 그렇게 적는다 — 지금은 §카드 그리드의 3열 하나다.
|
|
11
|
+
|
|
12
|
+
## 셸 (canonical)
|
|
13
|
+
|
|
14
|
+
**모든 화면이 이 구조를 따른다. 화면별로 재정의하지 않는다.** 기준 화면은 Figma PALDA node
|
|
15
|
+
`586-10288`이고, 수치와 값은 코드 토큰이 SSOT다. 근거는
|
|
16
|
+
[`decisions/0012`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0012-canonical-app-shell.md)에 있다.
|
|
17
|
+
|
|
18
|
+
**사이드바(전체 높이) + 콘텐츠.** 사이드바 위를 덮는 전역 상단 바는 없다. 페이지 제목은
|
|
19
|
+
상단 바가 아니라 **바디 안**에 둔다. 스펙 확인용 토글 같은 Figma 전용 장치는 제품 UI로 만들지
|
|
20
|
+
않는다.
|
|
21
|
+
|
|
22
|
+
### 콘텐츠 세로 순서
|
|
23
|
+
|
|
24
|
+
| 단계 | 자리 | 무엇이 오나 | 좌우 여백 |
|
|
25
|
+
| ---- | --------------------- | ------------------------------------------------------ | ---------------------------- |
|
|
26
|
+
| 1 | 인-콘텐츠 상단 행 | 컨텍스트 스위처(스토어) + 계정 | `--palda-layout-inset` |
|
|
27
|
+
| 2 | 상태·활성화 바 (선택) | 화면 전역 상태 토글. `accent/subtle` 틴트, 콘텐츠 풀폭 | `--palda-layout-inset` |
|
|
28
|
+
| 3 | 바디 | 페이지 헤더(제목 + 설명 + 우측 상단 액션) → 본문 | `--palda-layout-page-margin` |
|
|
29
|
+
|
|
30
|
+
**2단이 `(선택)`인 조건은 하나다 — 화면 전역을 켜고 끄는 것이 있을 때만 깔린다.** 없으면 깔지
|
|
31
|
+
않는다. 다른 기능의 요약이나 입구(곁)는 이 바를 켜는 이유가 되지 못하고, 이미 깔린 바의 배치를
|
|
32
|
+
바꾸지도 못한다 — `band--inset`은 `space-between`이라 자리가 양끝 둘뿐이고 전역 토글이 그 둘을
|
|
33
|
+
쓴다. 근거는 [`decisions/0022`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0022-screens-declare-their-body.md)에 있다.
|
|
34
|
+
|
|
35
|
+
바디 상단 여백은 `space-8`(32)이고 **바디의 직계 자식 사이는 `space-gap-xl`(24)**이다. 자식은
|
|
36
|
+
섹션이 아니라 블록이라 24다 — 근거는 [`decisions/0021`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0021-body-children-are-blocks.md)에
|
|
37
|
+
있다. `AppShell`이 이 세 단을 `topRow` · `statusBar` · `children` 슬롯으로 갖고 있다.
|
|
38
|
+
|
|
39
|
+
### 배경 위계
|
|
40
|
+
|
|
41
|
+
**읽는 면이 가장 밝다.** 페이지 캔버스가 흰색이고, 틀과 조작하는 것이 회색으로 물러난다. 근거는
|
|
42
|
+
[`decisions/0013`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0013-surface-base-is-white.md)에 있다.
|
|
43
|
+
|
|
44
|
+
| 자리 | 배경 | 색 |
|
|
45
|
+
| ------------------------------------ | ---------------- | --------- |
|
|
46
|
+
| 페이지 캔버스, 바디, 모달 카드, 카드 | `surface/base` | 흰색 |
|
|
47
|
+
| 사이드바, 드롭다운·프리뷰, 입력 | `surface/raised` | `#F5F5F7` |
|
|
48
|
+
| 앞으로 끌어낸 카드 하나 | `surface/raised` | `#F5F5F7` |
|
|
49
|
+
| 요약 행 | `surface/raised` | `#F5F5F7` |
|
|
50
|
+
| 상태·활성화 바 | `accent/subtle` | 보라 틴트 |
|
|
51
|
+
|
|
52
|
+
바디에 배경을 따로 깔지 않는다. 캔버스가 그대로 비친다. 요약 행은 옅은 회색 필이고 보더를 주지
|
|
53
|
+
않는다(`ListRow`의 `tone="filled"`).
|
|
54
|
+
|
|
55
|
+
**채운 면의 회색은 한 종류다.** 앞으로 끌어낸 카드와 요약 행이 같은 `surface/raised`이고, 둘은
|
|
56
|
+
색이 아니라 층으로 갈린다 — 여백과 라운드가 다르다
|
|
57
|
+
([`decisions/0018`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0018-summary-row-shares-the-filled-surface.md)).
|
|
58
|
+
`surface/sunken`은 화면 배경에서 빠지고 컨트롤 안쪽(스위치 트랙·`ProgressBar` 트랙·프리뷰 바닥)에만
|
|
59
|
+
남는다.
|
|
60
|
+
|
|
61
|
+
**카드도 기본은 흰색이다.** 회색 면이 여러 장 반복되면 전부 똑같이 무거워져 어느 것도 앞에
|
|
62
|
+
서지 못한다. 강조는 한 화면에서 하나를 `Card`의 `tone="filled"`로 올려서 낸다. 조건은
|
|
63
|
+
[`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md) §쓰는 경우에 있다.
|
|
64
|
+
|
|
65
|
+
**이름과 값이 어긋나 있다.** `raised`(떠 있는 것)가 캔버스보다 어둡다. 0013에서 값만 맞바꾸고
|
|
66
|
+
이름은 두었기 때문이고, 이름을 짐작해서 쓰지 말고 이 표를 본다.
|
|
67
|
+
|
|
68
|
+
### 폭
|
|
69
|
+
|
|
70
|
+
사이드바는 `--palda-layout-sidebar-width`(220)이고 화면 왼쪽 끝에 붙는다. **콘텐츠 열은 남은 폭을 다
|
|
71
|
+
쓰고, 폭 제한은 각 단의 안쪽 밴드가 갖는다.** 밴드는 `--palda-layout-content-width`(1220)를 넘지 않고
|
|
72
|
+
가운데로 모인다.
|
|
73
|
+
|
|
74
|
+
배경은 밴드가 아니라 바깥 단이 깔기 때문에 **화면 끝까지 간다(풀블리드).** 열에 폭을 걸면 넓은
|
|
75
|
+
화면에서 상태 바 틴트와 하단 바가 중간에서 끊긴다. 근거는
|
|
76
|
+
[`decisions/0014`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0014-full-bleed-bands.md)에 있다.
|
|
77
|
+
|
|
78
|
+
좌우 여백도 밴드가 갖는다 — 상단 행·상태 바·하단 바는 `--palda-layout-inset`, 바디는
|
|
79
|
+
`--palda-layout-page-margin`이다. `box-sizing: border-box`라 `1440`에서의 계산은 그대로다.
|
|
80
|
+
|
|
81
|
+
사이드바 안쪽 여백이 `--palda-space-4`(16)이므로 nav 항목 폭은 `220 − 16 × 2 = 188`로 저절로 나온다.
|
|
82
|
+
|
|
83
|
+
### 카드 그리드
|
|
84
|
+
|
|
85
|
+
**고를 수 있는 객체를 늘어놓는 자리는 3열이고, 열 사이는 `--palda-space-gap-xl`(24)이다.**
|
|
86
|
+
`gap-xl`이 §토큰 표에서 "블록 ↔ 블록, 컬럼 ↔ 컬럼"의 값이라 열 사이도 같은 값을 쓴다. 카드 폭은
|
|
87
|
+
담는 자리에서 나온다 — 바디 폭 964에서는 305, 2단의 폼 열(621)에서는 191이다.
|
|
88
|
+
|
|
89
|
+
**열 수를 내용 폭으로 정하지 않는다.** `auto-fill`과 최소 카드 폭은 중간 구간(`768`~`1439`)이
|
|
90
|
+
디자인된 뒤에 정할 수 있고, 지금 정하면 근거 없는 값이 하나 더 는다. 3열은 2026-09-02에 고른
|
|
91
|
+
값이고 실측이 아니다 — 시안이 4열을 요구하면 그때 다시 본다.
|
|
92
|
+
|
|
93
|
+
이 자리인지는 [`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md) §절대 규칙의 "벤토·카드 그리드는 개별 객체
|
|
94
|
+
모음에만"이 가른다. 설정 폼과 정보 패널은 그리드로 만들지 않는다.
|
|
95
|
+
|
|
96
|
+
## 토큰
|
|
97
|
+
|
|
98
|
+
| token | 값 | 어디에 |
|
|
99
|
+
| ------------------------------ | ------ | ------------------------- |
|
|
100
|
+
| `--palda-layout-page-margin` | 128px | 바디 좌우 여백 |
|
|
101
|
+
| `--palda-layout-inset` | 48px | 상단 행·상태 바 좌우 여백 |
|
|
102
|
+
| `--palda-layout-sidebar-width` | 220px | 사이드바 폭 |
|
|
103
|
+
| `--palda-layout-content-width` | 1220px | 콘텐츠 최대 폭 |
|
|
104
|
+
| `--palda-space-gap-xl` | 24px | 블록 ↔ 블록, 컬럼 ↔ 컬럼 |
|
|
105
|
+
|
|
106
|
+
`--palda-space-gap-xl`은 layout이 아니라 gap 스케일에 있다. 성격이 "요소 사이"로 컴포넌트 gap과 같고
|
|
107
|
+
값도 이미 primitive에 있어서다.
|
|
108
|
+
|
|
109
|
+
`--palda-layout-popup-width`(350px)는 브라우저 확장 팝업의 기존 외곽 폭을 보존한 Draft다. Stable
|
|
110
|
+
폭 표에 섞지 않으며 사용 조건과 검토 질문은 [`reviews/draft-tokens.md`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/reviews/draft-tokens.md)가
|
|
111
|
+
정본이다.
|
|
112
|
+
|
|
113
|
+
## 기존 토큰으로 덮이는 값
|
|
114
|
+
|
|
115
|
+
실측값 중 아래는 새로 만들지 않고 있는 것을 쓴다. 값이 같다고 새 이름을 만들면 같은 눈금이 두 벌이
|
|
116
|
+
된다.
|
|
117
|
+
|
|
118
|
+
| 자리 | 값 | token |
|
|
119
|
+
| ------------------- | ---- | ---------------------- |
|
|
120
|
+
| 본문 상단 여백 | 32px | `--palda-space-8` |
|
|
121
|
+
| 바디 직계 자식 사이 | 24px | `--palda-space-gap-xl` |
|
|
122
|
+
| 섹션 ↔ 섹션 | 32px | `--palda-space-8` |
|
|
123
|
+
| 사이드바 내부 여백 | 16px | `--palda-space-4` |
|
|
124
|
+
| nav 항목 간격 | 4px | `--palda-space-1` |
|
|
125
|
+
| 라벨 ↔ 컨트롤 | 12px | `--palda-space-gap-md` |
|
|
126
|
+
|
|
127
|
+
**섹션 사이 32는 블록 사이 24(`--palda-space-gap-xl`)와 다른 값이다.** 박스를 쓰지 않는 것이 기본값이라
|
|
128
|
+
(0011) 그룹을 가르는 것이 여백뿐이고, 그래서 섹션 경계가 블록 경계보다 넓다. `--palda-space-8`을
|
|
129
|
+
primitive로 직접 쓰는 자리이며 대응하는 semantic이 없다.
|
|
130
|
+
|
|
131
|
+
**다만 이 32는 바디의 직계 자식에는 적용되지 않는다.** 그 자리는 블록이라 24이고, `AppShell`이
|
|
132
|
+
`gap`으로 준다([`decisions/0021`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0021-body-children-are-blocks.md)의 §대가).
|
|
133
|
+
|
|
134
|
+
**그래서 섹션을 바디에 바로 쌓지 않는다.** 섹션이 여럿이면 묶음 하나를 블록으로 세우고, 32는 그
|
|
135
|
+
묶음 안에서 준다 — 바디가 보는 것은 블록 하나이고 24가 그대로 맞는다. 시안 세 장이 전부
|
|
136
|
+
이렇게 쌓고 있어 2026-09-02에 규칙으로 적었다. `AppShell`은 24 하나만 알면 된다.
|
|
137
|
+
|
|
138
|
+
묶음은 지금 화면이 자기 클래스로 만든다. 같은 세로 스택이 반복되는 것이 확인되면 그때 pattern으로
|
|
139
|
+
올린다 — 값이 `gap: 32` 하나뿐이라 아직 승격할 만큼이 아니다.
|
|
140
|
+
|
|
141
|
+
## 실측 치수
|
|
142
|
+
|
|
143
|
+
토큰으로 만들지 않은 값도 함께 적는다. 화면을 조립할 때 대조하는 기준이고, 반복이 확인되면 그때
|
|
144
|
+
토큰으로 올린다. **아직 토큰이 아니므로 CSS에 리터럴로 쓰지 않는다.**
|
|
145
|
+
|
|
146
|
+
| 자리 | 값 |
|
|
147
|
+
| ------------------- | --------------------------------- |
|
|
148
|
+
| 바디 폭 | 964px (1220 − 128 × 2) |
|
|
149
|
+
| 사이드바 nav 항목 | 188 × 46px |
|
|
150
|
+
| 상단 행 높이 | 98px |
|
|
151
|
+
| 상태·활성화 바 높이 | 66px |
|
|
152
|
+
| 2단 구성 | 폼 620px + 갭 24px + 프리뷰 319px |
|
|
153
|
+
| 토스트 | 폭 400px, 하단 중앙 |
|
|
154
|
+
|
|
155
|
+
nav 항목 폭 188과 바디 폭 964는 계산으로 떨어져서 토큰이 필요 없다. 앞은 사이드바 폭에서
|
|
156
|
+
안쪽 여백을 뺀 값이고, 뒤는 콘텐츠 폭에서 바디 여백을 뺀 값이다.
|
|
157
|
+
|
|
158
|
+
본문 폭 964는 `1220 − 128 × 2`로 떨어지고, 2단 구성 `620 + 24 + 319 = 963`은 964와 1px 차이다.
|
|
159
|
+
실측 반올림으로 보이며, 폼과 프리뷰 중 어느 쪽이 남는 1px을 갖는지는 확정되지 않았다. 지금은 폼을
|
|
160
|
+
`1fr`로 두고 프리뷰를 고정폭으로 잡아 이 차이를 폼이 흡수한다.
|
|
161
|
+
|
|
162
|
+
**2단 구성은 시안 `597:12306`에서 다시 확인했다** — 폼 620, 갭 24, 프리뷰 319.42로 위 표와 같다.
|
|
163
|
+
`LivePreview`가 이미 `width: 319px`이라 이 자리는 코드가 시안과 맞다.
|
|
164
|
+
|
|
165
|
+
## 4배수 그리드
|
|
166
|
+
|
|
167
|
+
레이아웃 여백은 `--space-*`와 같은 4배수 그리드 위에 있다. `48 = 4 × 12`, `128 = 4 × 32`이고
|
|
168
|
+
primitive index는 `px / 4`라는 기존 규칙 그대로다(`--palda-space-12`, `--palda-space-32`).
|
|
169
|
+
|
|
170
|
+
`--palda-layout-sidebar-width`의 220도 4의 배수(4 × 55)지만 여백 그리드의 한 칸이 아니라 "nav 라벨이
|
|
171
|
+
안 접히는 폭"에서 나온 값이다. 그래서 primitive를 거치지 않고 semantic에 값을 직접 둔다. 폭은
|
|
172
|
+
여백과 성격이 다르다.
|
|
173
|
+
|
|
174
|
+
## 고를 때
|
|
175
|
+
|
|
176
|
+
- 페이지 좌우 끝에서 본문을 밀어내는 여백이면 `--palda-layout-page-margin`
|
|
177
|
+
- 상단바나 섹션 헤더처럼 **가로로 꽉 찬 바** 안쪽 여백이면 `--palda-layout-inset`
|
|
178
|
+
- 컴포넌트 안쪽 여백이면 layout이 아니라 `--space-inset-*`
|
|
179
|
+
- 섹션 사이면 `--palda-space-8`, 블록 사이면 `--palda-space-gap-xl`, 그보다 가까운 묶음이면 `gap`의 아래 단계
|
|
180
|
+
|
|
181
|
+
컴포넌트 CSS에서 `--layout-*`을 쓰지 않는다. 컴포넌트는 자기가 놓일 페이지의 여백을 모른다.
|
|
182
|
+
레이아웃 여백은 pattern(`patterns/src/AppShell` 등)과 페이지가 준다.
|
|
183
|
+
|
|
184
|
+
## 무엇을 박스로 감싸나
|
|
185
|
+
|
|
186
|
+
**기본값은 감싸지 않는 것이다.** 섹션은 제목과 여백으로 가르고, 목록은 디바이더로 가른다. 보더와
|
|
187
|
+
카드는 고르거나, 열리거나, 떠 있는 것에만 쓴다. 조건과 pattern별 적용은
|
|
188
|
+
[`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md), 근거는
|
|
189
|
+
[`decisions/0011`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0011-no-over-carding.md)에 있다.
|
|
190
|
+
|
|
191
|
+
## 좁아질 때
|
|
192
|
+
|
|
193
|
+
breakpoint는 `desktop 1440` · `mobile 768` 둘이다([`decisions/0010`](https://github.com/highpixel-co/palda-design-system/blob/0504d8eaa033955d7a70414616ee999e447a0ced/docs/decisions/0010-breakpoints.md)).
|
|
194
|
+
위 표의 값은 `1440` 이상에서만 그대로 들어간다.
|
|
195
|
+
|
|
196
|
+
| 구간 | 본문 좌우 | 상단바·하단바 좌우 | 사이드바 | 2단 |
|
|
197
|
+
| -------------- | ---------------------------------- | ----------------------------- | --------- | --- |
|
|
198
|
+
| `≥ 1440` | `--palda-layout-page-margin` (128) | `--palda-layout-inset` (48) | 세로 기둥 | 2단 |
|
|
199
|
+
| `768` ~ `1439` | `--palda-space-inset-lg` (24) | `--palda-space-inset-lg` (24) | 세로 기둥 | 1단 |
|
|
200
|
+
| `< 768` | `--palda-space-inset-md` (16) | `--palda-space-inset-md` (16) | 가로 줄 | 1단 |
|
|
201
|
+
|
|
202
|
+
좁은 구간에서 새 값을 만들지 않고 이미 있는 `--space-inset-*`로 접는다.
|
|
203
|
+
|
|
204
|
+
**`@media`는 CSS 변수를 읽지 못한다.** 미디어 쿼리에는 숫자를 그대로 적고 어느 토큰인지 주석을
|
|
205
|
+
남긴다. 토큰을 바꾸면 미디어 쿼리도 함께 고쳐야 하고, 이 어긋남을 잡는 검사는 없다.
|
|
206
|
+
|
|
207
|
+
## 아직 정하지 않은 것
|
|
208
|
+
|
|
209
|
+
이 절은 디자이너가 확정한다. 확정 전까지 값을 지어내지 않는다.
|
|
210
|
+
|
|
211
|
+
- container 폭
|
|
212
|
+
- mobile에서 숨기거나 축약할 정보
|
|
213
|
+
- Table, Modal, Navigation의 mobile 전환 방식
|
|
214
|
+
- 최소 터치 영역
|
|
215
|
+
- 긴 한글 문구와 확대 글꼴 대응
|
|
216
|
+
- `768`의 근거와 가운데 구간(`768`~`1439`) 구성
|
|
217
|
+
|
|
218
|
+
**mobile 시안이 없다.** `768`은 실측이 아니라 고른 값이고, `768`~`1439` 구간은 데스크톱을 접은
|
|
219
|
+
모습일 뿐 디자인된 적이 없다. 시안이 나오면 0010부터 다시 본다.
|
|
220
|
+
|
|
221
|
+
**토큰이 아닌 실측값이 남아 있다.** 위 §실측 치수의 값들은 반복이 확인되면 그때 토큰으로 올린다.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Pattern Policy
|
|
2
|
+
|
|
3
|
+
**어떤 조합을 pattern으로 승격하고, 승격한 pattern이 무엇까지 정의해야 하는지의 규칙이다.**
|
|
4
|
+
|
|
5
|
+
Pattern은 여러 컴포넌트를 조합해 반복 사용하는 화면 구조다.
|
|
6
|
+
|
|
7
|
+
- 반복 사용되는 과업인지 확인한다.
|
|
8
|
+
- Desktop과 mobile 구성을 함께 정의한다.
|
|
9
|
+
- Loading, empty, error 상태를 포함한다. **다만 틀 역할의 pattern은 제외한다** — `AppShell`과
|
|
10
|
+
`Card`는 상태를 갖지 않고 `children`에 맡긴다. 어느 pattern이 무엇을 갖는지는
|
|
11
|
+
[`DESIGN_GRAMMAR.md`](DESIGN_GRAMMAR.md)의 §상태는 누가 갖나에 있다.
|
|
12
|
+
- 어느 자리에 어느 pattern을 쓰는지는 같은 문서의 §어느 pattern을 고르나를 따른다.
|
|
13
|
+
- 특정 기능에만 필요한 조합은 공통 pattern으로 승격하지 않는다.
|