zdp-design-system 0.44.0 → 0.45.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/CHANGELOG.md CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Added layer and mobile-safe viewport tokens, then moved overlay z-index and `100vh`/`100vw` sizing to named token usage across Svelte components and static utilities.
6
+ - Strengthened package checks for overlay token usage and overrideable default labels.
7
+ - Documented that repeated mobile keyboard, long option, async option, grouped option, virtualized list, or collision pressure should move Menu, Popover, and Combobox flows to Sheet or a headless spike.
5
8
  - Added `AdSlot` plus static `.zdp-ad-slot` utilities for provider-neutral ad or sponsorship placement reservation while keeping provider scripts, consent, slot ids, `ads.txt`, personalized ads, and automatic content insertion in consuming apps.
6
- - Documented the dependency adoption policy: active ZDP sibling consumers may keep `file:../zdp-design-system`, while standalone consumers, public templates, and external examples should use the npm package from `^0.44.0`.
9
+ - Documented the dependency adoption policy: active ZDP sibling consumers may keep `file:../zdp-design-system`, while standalone consumers, public templates, and external examples should use the npm package from `^0.45.0`.
7
10
  - Added a single-component consumer fixture proof so the root barrel keeps tree-shaking unused component exports out of Button-only bundles.
8
11
 
9
12
  ## 0.43.8
package/README.md CHANGED
@@ -23,6 +23,8 @@ Default component text is English. 제품별 화면은 필요한 모든 user-fac
23
23
 
24
24
  - `type`: body, body small, page title, title, label, caption, control, data에 쓰는 기본 크기와 줄높이
25
25
  - `breakpoint`: mobile, tablet, desktop, wide 기준 폭
26
+ - `layer`: behind, floating, toast, sheet, dialog, skip-link 계층 기준
27
+ - `viewport`: mobile-safe overlay clamp와 safe-area inset 기준
26
28
  - `control`: 버튼, 아이콘 버튼, 입력류가 공유할 높이, radius, border width, hit target, 선택 컨트롤 전용 mark, indicator, switch, scrollbar 크기
27
29
  - `focus`: 키보드 사용자가 현재 위치를 놓치지 않도록 하는 sunlit focus highlight, dark text, dark line
28
30
  - `selection`: 드래그로 선택한 텍스트가 브라우저 기본 파란색 대신 theme surface와 readable text로 보이게 하는 selection 색
@@ -66,7 +68,7 @@ Tailwind Plus와 Tailwind UI 계열은 파생/재배포 리스크 때문에 ZDP
66
68
  - Viewports는 ZDP Mobile, Tablet, Desktop, Wide 프리셋으로 mobile/tablet/desktop 폭을 확인한다.
67
69
  - Accessibility addon은 CI 실패 게이트로 유지한다. 새 story는 a11y 위반을 남긴 채 merge하지 않는다.
68
70
  - Interaction play는 `Tabs`, `Dialog`, `ConfirmAction`처럼 키보드와 상태 전이가 중요한 컴포넌트에 먼저 붙인다.
69
- - Theme / Locale Stress story는 light/dark, ZDP Mobile 폭, 긴 한국어/영어/중국어/힌디어 문장, focus-visible 상태를 한 번에 확인한다.
71
+ - Theme / Locale Stress story는 light/dark, ZDP Mobile 폭, 긴 한국어/영어/중국어/힌디어/베트남어/러시아어/말레이어/태국어 문장, focus-visible 상태를 한 번에 확인한다.
70
72
 
71
73
  ## 패키지 표면
72
74
 
@@ -99,7 +101,7 @@ import 'zdp-design-system/expressive-fonts.css';
99
101
 
100
102
  패키지 export는 `dist/` 산출물을 가리킨다. root runtime entry는 `dist/index.js`, type entry는 `dist/index.d.ts`다. 원천은 `src/lib`, `src/styles`, `tokens/zdp.tokens.json`, `src/lib/share.ts`이고 `bun run package:build`가 소비자용 `dist/` 표면을 다시 만든다. 소비 저장소와 문서 예시는 `zdp-design-system` public export만 쓰고 내부 `src/` 경로를 직접 import하지 않는다.
101
103
 
102
- ZDP monorepo 안의 active sibling 소비처는 `file:../zdp-design-system` 의존성을 유지할 수 있다. 이 방식은 release 전 변경을 같이 검증하기 위한 local workspace 계약이므로 CI에서 sibling checkout과 `bun run package:build`를 먼저 수행해야 한다. sibling checkout을 전제로 하지 않는 standalone consumer, public template, external example은 npm registry의 `zdp-design-system: ^0.44.0`을 기본으로 쓴다.
104
+ ZDP monorepo 안의 active sibling 소비처는 `file:../zdp-design-system` 의존성을 유지할 수 있다. 이 방식은 release 전 변경을 같이 검증하기 위한 local workspace 계약이므로 CI에서 sibling checkout과 `bun run package:build`를 먼저 수행해야 한다. sibling checkout을 전제로 하지 않는 standalone consumer, public template, external example은 npm registry의 `zdp-design-system: ^0.45.0`을 기본으로 쓴다.
103
105
 
104
106
  ## 소비 컴포넌트 계약
105
107
 
@@ -511,8 +513,8 @@ preview/index.html
511
513
  - `styles.css`는 Pretendard Variable dynamic subset을 로드하고, sans/display stack은 `"Pretendard Variable", Pretendard`를 최우선으로 둔다.
512
514
  - `brand-fonts.css`는 선택형 public export이며 `font.family.brand`와 `.zdp-brand-wordmark`가 쓰는 Playwrite AU VIC Guides를 로드한다. 일반 문서 제목, 제품 UI heading, 표, 본문에는 `brand` stack을 쓰지 않는다.
513
515
  - `expressive-fonts.css`는 선택형 public export이며 `font.family.expressionScript`, `font.family.expressionInscription`, `font.family.expressionSketch`, `font.family.expressionEditorial`, `font.family.expressionSans`, `font.family.expressionKeyboard`가 쓰는 표현용 Google Fonts를 로드한다. 기본 앱 UI, 표, 긴 본문, 일반 Tooltip에는 자동 적용하지 않는다.
514
- - `locale-fonts.css`는 선택형 public export이며 Manrope, Noto Sans SC, Noto Sans Devanagari, Noto Sans JP 웹폰트를 로드한다. 모든 소비 앱이 반드시 가져갈 필요는 없다.
515
- - `:lang(en|es|fr|de|pt|id)`는 Manrope/Inter 라틴 스택, `:lang(ko)`는 Pretendard 한국어 스택, `:lang(zh)`는 Noto Sans SC/시스템 중국어 스택, `:lang(hi)`는 Noto Sans Devanagari/시스템 데바나가리 스택, `:lang(ja)`는 Noto Sans JP/시스템 일본어 스택으로 덮어쓴다.
516
+ - `locale-fonts.css`는 선택형 public export이며 Manrope, Noto Sans SC, Noto Sans Devanagari, Noto Sans JP, Noto Sans Thai 웹폰트를 로드한다. 모든 소비 앱이 반드시 가져갈 필요는 없다.
517
+ - `:lang(en|es|fr|vi|ru|id|ms)`는 Manrope/Inter 라틴 스택, `:lang(ko)`는 Pretendard 한국어 스택, `:lang(zh)`는 Noto Sans SC/시스템 중국어 스택, `:lang(hi)`는 Noto Sans Devanagari/시스템 데바나가리 스택, `:lang(ja)`는 Noto Sans JP/시스템 일본어 스택, `:lang(th)`는 Noto Sans Thai/시스템 태국어 스택으로 덮어쓴다.
516
518
  - Flutter, native shell, 문서 생성기는 JSON 토큰의 hex 값을 기본 입력으로 쓰고 필요할 때 OKLCH를 별도 변환한다.
517
519
 
518
520
  ## Flat UI 계약
@@ -476,13 +476,13 @@
476
476
  font-family: var(--zdp-font-family-sans);
477
477
  inline-size: 100%;
478
478
  left: 0;
479
- max-block-size: min(18rem, calc(100vh - var(--zdp-space-8)));
479
+ max-block-size: min(18rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
480
480
  min-width: 0;
481
481
  overflow: auto;
482
482
  padding: var(--zdp-space-1);
483
483
  position: absolute;
484
484
  top: calc(100% + var(--zdp-space-2));
485
- z-index: 40;
485
+ z-index: var(--zdp-layer-floating);
486
486
  }
487
487
 
488
488
  .zdp-combobox__listbox {
@@ -208,7 +208,7 @@
208
208
  position: absolute;
209
209
  top: 0;
210
210
  width: calc(var(--zdp-confirm-action-progress) * 100%);
211
- z-index: -1;
211
+ z-index: var(--zdp-layer-behind);
212
212
  }
213
213
 
214
214
  .zdp-confirm-action__thumb {
@@ -162,13 +162,14 @@
162
162
  display: grid;
163
163
  inset: 0;
164
164
  justify-items: center;
165
- padding: var(--zdp-space-4);
165
+ padding-block: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
166
+ padding-inline: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
166
167
  position: fixed;
167
- z-index: 1000;
168
+ z-index: var(--zdp-layer-dialog);
168
169
  }
169
170
 
170
171
  .zdp-dialog__backdrop {
171
- background: rgb(47 36 24 / 0.42);
172
+ background: var(--zdp-color-backdrop-dialog);
172
173
  border: 0;
173
174
  cursor: pointer;
174
175
  inset: 0;
@@ -177,10 +178,6 @@
177
178
  position: fixed;
178
179
  }
179
180
 
180
- :global([data-zdp-theme="dark"]) .zdp-dialog__backdrop {
181
- background: rgb(10 8 5 / 0.72);
182
- }
183
-
184
181
  .zdp-dialog__panel {
185
182
  background: var(--zdp-color-surface-panel);
186
183
  border: var(--zdp-control-border-width) solid var(--zdp-color-line-strong);
@@ -189,7 +186,7 @@
189
186
  color: var(--zdp-color-ink-normal);
190
187
  display: grid;
191
188
  gap: var(--zdp-space-4);
192
- max-height: min(42rem, calc(100vh - var(--zdp-space-8)));
189
+ max-height: min(42rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
193
190
  min-width: 0;
194
191
  overflow: auto;
195
192
  padding: var(--zdp-space-5);
@@ -292,11 +289,12 @@
292
289
  @media (max-width: 520px) {
293
290
  .zdp-dialog {
294
291
  align-items: end;
295
- padding: var(--zdp-space-3);
292
+ padding-block: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
293
+ padding-inline: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
296
294
  }
297
295
 
298
296
  .zdp-dialog__panel {
299
- max-height: calc(100vh - var(--zdp-space-6));
297
+ max-height: calc(var(--zdp-viewport-block) - var(--zdp-space-6));
300
298
  padding: var(--zdp-space-4);
301
299
  }
302
300
  }
@@ -377,13 +377,13 @@
377
377
  font-family: var(--zdp-font-family-sans);
378
378
  gap: var(--zdp-space-1);
379
379
  inline-size: max-content;
380
- max-block-size: min(24rem, calc(100vh - var(--zdp-space-8)));
381
- max-inline-size: min(18rem, calc(100vw - var(--zdp-space-6)));
380
+ max-block-size: min(24rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
381
+ max-inline-size: min(18rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6)));
382
382
  min-inline-size: 12rem;
383
383
  overflow: auto;
384
384
  padding: var(--zdp-space-1);
385
385
  position: absolute;
386
- z-index: 40;
386
+ z-index: var(--zdp-layer-floating);
387
387
  }
388
388
 
389
389
  .zdp-menu__panel:focus-visible {
@@ -148,13 +148,13 @@
148
148
  font-family: var(--zdp-font-family-sans);
149
149
  gap: var(--zdp-space-3);
150
150
  inline-size: max-content;
151
- max-block-size: min(24rem, calc(100vh - var(--zdp-space-8)));
152
- max-inline-size: min(22rem, calc(100vw - var(--zdp-space-6)));
151
+ max-block-size: min(24rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
152
+ max-inline-size: min(22rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6)));
153
153
  min-inline-size: 12rem;
154
154
  overflow: auto;
155
155
  padding: var(--zdp-space-3);
156
156
  position: absolute;
157
- z-index: 40;
157
+ z-index: var(--zdp-layer-floating);
158
158
  }
159
159
 
160
160
  .zdp-popover__panel:focus-visible {
@@ -136,12 +136,12 @@
136
136
  .zdp-share-dock {
137
137
  color: var(--zdp-color-ink-normal);
138
138
  font-family: var(--zdp-font-family-sans);
139
- z-index: 20;
139
+ z-index: var(--zdp-layer-share-dock);
140
140
  }
141
141
 
142
142
  .zdp-share-dock--side {
143
143
  position: fixed;
144
- right: max(var(--zdp-space-3), calc((100vw - var(--zdp-breakpoint-desktop)) / 2 + var(--zdp-space-4)));
144
+ right: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end), calc((var(--zdp-viewport-inline) - var(--zdp-breakpoint-desktop)) / 2 + var(--zdp-space-4)));
145
145
  top: 40vh;
146
146
  }
147
147
 
@@ -242,9 +242,9 @@
242
242
 
243
243
  @media (max-width: 57.5rem) {
244
244
  .zdp-share-dock--side {
245
- bottom: var(--zdp-space-3);
246
- left: var(--zdp-space-3);
247
- right: var(--zdp-space-3);
245
+ bottom: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
246
+ left: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start));
247
+ right: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
248
248
  top: auto;
249
249
  }
250
250
 
@@ -252,7 +252,7 @@
252
252
  display: flex;
253
253
  flex-wrap: wrap;
254
254
  justify-content: center;
255
- max-inline-size: calc(100vw - var(--zdp-space-6));
255
+ max-inline-size: calc(var(--zdp-viewport-inline) - var(--zdp-space-6));
256
256
  }
257
257
 
258
258
  .zdp-share-dock--rail {
@@ -292,7 +292,7 @@
292
292
  @media (max-width: 42rem) {
293
293
  .zdp-share-dock--side,
294
294
  .zdp-share-dock--bottom {
295
- bottom: var(--zdp-space-2);
295
+ bottom: max(var(--zdp-space-2), var(--zdp-viewport-safe-block-end));
296
296
  }
297
297
 
298
298
  .zdp-share-dock__list {
@@ -156,18 +156,14 @@
156
156
  }
157
157
 
158
158
  .zdp-sheet__backdrop {
159
- background: rgb(47 36 24 / 0.32);
159
+ background: var(--zdp-color-backdrop-sheet);
160
160
  border: 0;
161
161
  cursor: pointer;
162
162
  inset: 0;
163
163
  margin: 0;
164
164
  padding: 0;
165
165
  position: fixed;
166
- z-index: 940;
167
- }
168
-
169
- :global([data-zdp-theme="dark"]) .zdp-sheet__backdrop {
170
- background: rgb(10 8 5 / 0.66);
166
+ z-index: var(--zdp-layer-sheet);
171
167
  }
172
168
 
173
169
  .zdp-sheet {
@@ -178,27 +174,28 @@
178
174
  display: grid;
179
175
  font-family: var(--zdp-font-family-sans);
180
176
  gap: var(--zdp-space-4);
181
- max-block-size: calc(100vh - var(--zdp-space-6));
177
+ max-block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
182
178
  min-width: 0;
183
179
  overflow: auto;
184
- padding: var(--zdp-space-5);
180
+ padding-block: var(--zdp-space-5) max(var(--zdp-space-5), var(--zdp-viewport-safe-block-end));
181
+ padding-inline: max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-end));
185
182
  position: fixed;
186
- z-index: 941;
183
+ z-index: calc(var(--zdp-layer-sheet) + 1);
187
184
  }
188
185
 
189
186
  .zdp-sheet--right,
190
187
  .zdp-sheet--left {
191
- block-size: calc(100vh - var(--zdp-space-6));
188
+ block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
192
189
  border-radius: var(--zdp-control-radius);
193
- inset-block: var(--zdp-space-3);
190
+ inset-block: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
194
191
  }
195
192
 
196
193
  .zdp-sheet--right {
197
- inset-inline-end: var(--zdp-space-3);
194
+ inset-inline-end: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
198
195
  }
199
196
 
200
197
  .zdp-sheet--left {
201
- inset-inline-start: var(--zdp-space-3);
198
+ inset-inline-start: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start));
202
199
  }
203
200
 
204
201
  .zdp-sheet--bottom {
@@ -207,19 +204,19 @@
207
204
  border-radius: var(--zdp-control-radius) var(--zdp-control-radius) 0 0;
208
205
  inset-block-end: 0;
209
206
  inset-inline: 0;
210
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
207
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
211
208
  }
212
209
 
213
210
  .zdp-sheet--sm {
214
- inline-size: min(24rem, calc(100vw - var(--zdp-space-6)));
211
+ inline-size: min(24rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
215
212
  }
216
213
 
217
214
  .zdp-sheet--md {
218
- inline-size: min(30rem, calc(100vw - var(--zdp-space-6)));
215
+ inline-size: min(30rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
219
216
  }
220
217
 
221
218
  .zdp-sheet--lg {
222
- inline-size: min(38rem, calc(100vw - var(--zdp-space-6)));
219
+ inline-size: min(38rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
223
220
  }
224
221
 
225
222
  .zdp-sheet--bottom.zdp-sheet--sm,
@@ -229,7 +226,7 @@
229
226
  }
230
227
 
231
228
  .zdp-sheet--bottom.zdp-sheet--lg {
232
- max-block-size: min(42rem, calc(100vh - var(--zdp-space-6)));
229
+ max-block-size: min(42rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
233
230
  }
234
231
 
235
232
  .zdp-sheet:focus-visible {
@@ -325,8 +322,9 @@
325
322
  inline-size: auto;
326
323
  inset-block: auto 0;
327
324
  inset-inline: 0;
328
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
329
- padding: var(--zdp-space-4);
325
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
326
+ padding-block: var(--zdp-space-4) max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
327
+ padding-inline: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
330
328
  }
331
329
  }
332
330
  </style>
@@ -28,7 +28,7 @@
28
28
  border-color var(--zdp-motion-fast) ease,
29
29
  color var(--zdp-motion-fast) ease,
30
30
  opacity var(--zdp-motion-fast) ease;
31
- z-index: 10000;
31
+ z-index: var(--zdp-layer-skip-link);
32
32
  }
33
33
 
34
34
  .zdp-skip-link:focus-visible {
@@ -79,10 +79,10 @@
79
79
  box-sizing: border-box;
80
80
  display: grid;
81
81
  gap: var(--zdp-space-3);
82
- inline-size: min(28rem, calc(100vw - var(--zdp-space-6)));
82
+ inline-size: min(28rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6)));
83
83
  max-inline-size: 100%;
84
84
  pointer-events: none;
85
- z-index: 50;
85
+ z-index: var(--zdp-layer-toast);
86
86
  }
87
87
 
88
88
  .zdp-status-toast :global(.zdp-toast) {
@@ -102,23 +102,23 @@
102
102
  }
103
103
 
104
104
  .zdp-status-toast--top-start {
105
- left: var(--zdp-space-4);
106
- top: var(--zdp-space-4);
105
+ left: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start));
106
+ top: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-start));
107
107
  }
108
108
 
109
109
  .zdp-status-toast--top-end {
110
- right: var(--zdp-space-4);
111
- top: var(--zdp-space-4);
110
+ right: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
111
+ top: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-start));
112
112
  }
113
113
 
114
114
  .zdp-status-toast--bottom-start {
115
- bottom: var(--zdp-space-4);
116
- left: var(--zdp-space-4);
115
+ bottom: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
116
+ left: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start));
117
117
  }
118
118
 
119
119
  .zdp-status-toast--bottom-end {
120
- bottom: var(--zdp-space-4);
121
- right: var(--zdp-space-4);
120
+ bottom: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
121
+ right: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
122
122
  }
123
123
 
124
124
  @media (max-width: 42rem) {
@@ -126,8 +126,8 @@
126
126
  .zdp-status-toast--top-end,
127
127
  .zdp-status-toast--bottom-start,
128
128
  .zdp-status-toast--bottom-end {
129
- left: var(--zdp-space-3);
130
- right: var(--zdp-space-3);
129
+ left: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start));
130
+ right: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
131
131
  }
132
132
  }
133
133
  </style>
@@ -217,34 +217,31 @@
217
217
  display: grid;
218
218
  font-family: var(--zdp-font-family-sans);
219
219
  gap: var(--zdp-space-4);
220
- max-block-size: calc(100vh - var(--zdp-space-6));
220
+ max-block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
221
221
  overflow: auto;
222
- padding: var(--zdp-space-5);
222
+ padding-block: var(--zdp-space-5) max(var(--zdp-space-5), var(--zdp-viewport-safe-block-end));
223
+ padding-inline: max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-end));
223
224
  position: fixed;
224
- z-index: 901;
225
+ z-index: calc(var(--zdp-layer-term-sheet) + 1);
225
226
  }
226
227
 
227
228
  .zdp-term-sheet__backdrop {
228
- background: rgb(47 36 24 / 0.28);
229
+ background: var(--zdp-color-backdrop-term-sheet);
229
230
  border: 0;
230
231
  cursor: pointer;
231
232
  inset: 0;
232
233
  margin: 0;
233
234
  padding: 0;
234
235
  position: fixed;
235
- z-index: 900;
236
- }
237
-
238
- :global([data-zdp-theme="dark"]) .zdp-term-sheet__backdrop {
239
- background: rgb(10 8 5 / 0.64);
236
+ z-index: var(--zdp-layer-term-sheet);
240
237
  }
241
238
 
242
239
  .zdp-term-sheet--right {
243
- block-size: calc(100vh - var(--zdp-space-6));
240
+ block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
244
241
  border-radius: var(--zdp-control-radius);
245
- inline-size: min(28rem, calc(100vw - var(--zdp-space-6)));
246
- inset-block: var(--zdp-space-3);
247
- inset-inline-end: var(--zdp-space-3);
242
+ inline-size: min(28rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
243
+ inset-block: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
244
+ inset-inline-end: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
248
245
  }
249
246
 
250
247
  .zdp-term-sheet--bottom {
@@ -253,7 +250,7 @@
253
250
  border-radius: var(--zdp-control-radius) var(--zdp-control-radius) 0 0;
254
251
  inset-block-end: 0;
255
252
  inset-inline: 0;
256
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
253
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
257
254
  }
258
255
 
259
256
  .zdp-term-sheet:focus-visible {
@@ -385,8 +382,9 @@
385
382
  inline-size: auto;
386
383
  inset-block: auto 0;
387
384
  inset-inline: 0;
388
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
389
- padding: var(--zdp-space-4);
385
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
386
+ padding-block: var(--zdp-space-4) max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
387
+ padding-inline: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
390
388
  }
391
389
  }
392
390
  </style>
@@ -129,7 +129,7 @@
129
129
  -webkit-user-select: none;
130
130
  user-select: none;
131
131
  white-space: nowrap;
132
- z-index: 30;
132
+ z-index: var(--zdp-layer-floating);
133
133
  }
134
134
 
135
135
  .zdp-tooltip--top {
package/dist/index.ts CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * mf:anchor zdp.design-system.public-barrel
3
+ * purpose: Locate the package public Svelte component and helper export surface.
4
+ * search: public exports, package surface, Svelte components, helper exports, barrel
5
+ * invariant: Consumers import through package exports instead of internal src paths.
6
+ * risk: config
7
+ */
1
8
  export { default as Accordion } from './components/Accordion.svelte';
2
9
  export { default as AdSlot } from './components/AdSlot.svelte';
3
10
  export { default as Avatar } from './components/Avatar.svelte';
@@ -11,6 +11,13 @@ let nextLayerId = 1;
11
11
  const activeLayerIds: number[] = [];
12
12
  let previousBodyOverflow: string | null = null;
13
13
 
14
+ /**
15
+ * mf:anchor zdp.design-system.modal-layer-state
16
+ * purpose: Locate shared modal layer state for dialog, sheet, and term sheet surfaces.
17
+ * search: modal layer, scroll lock, active layer, dialog, sheet, focus trap
18
+ * invariant: Layer activation restores document overflow after the final active layer closes.
19
+ * risk: state
20
+ */
14
21
  export function createZdpModalLayer(): ZdpModalLayerHandle {
15
22
  const layerId = nextLayerId;
16
23
  nextLayerId += 1;
@@ -14,6 +14,8 @@
14
14
  "font",
15
15
  "type",
16
16
  "breakpoint",
17
+ "layer",
18
+ "viewport",
17
19
  "control",
18
20
  "i18n",
19
21
  "shadow",
@@ -76,6 +78,12 @@
76
78
  "breakpoint": {
77
79
  "$ref": "#/$defs/stringMap"
78
80
  },
81
+ "layer": {
82
+ "$ref": "#/$defs/stringMap"
83
+ },
84
+ "viewport": {
85
+ "$ref": "#/$defs/stringMap"
86
+ },
79
87
  "control": {
80
88
  "$ref": "#/$defs/stringMap"
81
89
  },
package/dist/share.ts CHANGED
@@ -45,6 +45,13 @@ export interface ZdpShareDockItem {
45
45
  readonly onclick?: (event: MouseEvent, item: ZdpShareDockItem) => void;
46
46
  }
47
47
 
48
+ /**
49
+ * mf:anchor zdp.design-system.share-icons-source
50
+ * purpose: Locate package-owned share icon shape data used by Svelte and static consumers.
51
+ * search: share icons, Simple Icons, ShareDock, static consumers, platform glyphs
52
+ * invariant: Platform icon shapes stay package-owned while URLs and share actions stay consumer-owned.
53
+ * risk: dependency
54
+ */
48
55
  export const zdpShareIcons: Record<ZdpShareIconName, ZdpShareIconShape> = {
49
56
  copy: {
50
57
  viewBox: '0 0 24 24',
package/dist/shortcuts.js CHANGED
@@ -77,6 +77,13 @@ export const zdpShortcutReservedExamples = [
77
77
  'Backspace'
78
78
  ];
79
79
 
80
+ /**
81
+ * mf:anchor zdp.design-system.shortcut-guard
82
+ * purpose: Locate consumer-facing shortcut guard helpers for text entry, IME, and browser-reserved keys.
83
+ * search: shortcut guard, IME, browser reserved shortcut, text entry, command palette
84
+ * invariant: Visible shortcut hints stay separate from consumer-owned keydown dispatch.
85
+ * risk: state
86
+ */
80
87
  export function isZdpTextEntryTarget(target) {
81
88
  if (!(target instanceof Element)) {
82
89
  return false;
package/dist/shortcuts.ts CHANGED
@@ -105,6 +105,13 @@ export const zdpShortcutReservedExamples = [
105
105
  'Backspace'
106
106
  ] as const;
107
107
 
108
+ /**
109
+ * mf:anchor zdp.design-system.shortcut-guard
110
+ * purpose: Locate consumer-facing shortcut guard helpers for text entry, IME, and browser-reserved keys.
111
+ * search: shortcut guard, IME, browser reserved shortcut, text entry, command palette
112
+ * invariant: Visible shortcut hints stay separate from consumer-owned keydown dispatch.
113
+ * risk: state
114
+ */
108
115
  export function isZdpTextEntryTarget(target: EventTarget | null): boolean {
109
116
  if (!(target instanceof Element)) {
110
117
  return false;
@@ -215,7 +215,7 @@
215
215
  position: absolute;
216
216
  top: 0;
217
217
  width: calc(var(--zdp-confirm-action-progress) * 100%);
218
- z-index: -1;
218
+ z-index: var(--zdp-layer-behind);
219
219
  }
220
220
 
221
221
  .zdp-confirm-action__thumb {
@@ -556,7 +556,7 @@
556
556
  border-color var(--zdp-motion-fast) ease,
557
557
  color var(--zdp-motion-fast) ease,
558
558
  opacity var(--zdp-motion-fast) ease;
559
- z-index: 10000;
559
+ z-index: var(--zdp-layer-skip-link);
560
560
  }
561
561
 
562
562
  .zdp-skip-link:focus-visible {
@@ -1355,7 +1355,7 @@
1355
1355
  top: var(--zdp-tooltip-top, auto);
1356
1356
  transform: var(--zdp-tooltip-transform, none);
1357
1357
  white-space: nowrap;
1358
- z-index: 30;
1358
+ z-index: var(--zdp-layer-floating);
1359
1359
  }
1360
1360
 
1361
1361
  .zdp-tooltip--top {
@@ -1620,12 +1620,12 @@
1620
1620
  display: grid;
1621
1621
  font-family: var(--zdp-font-family-sans);
1622
1622
  inline-size: max-content;
1623
- max-block-size: min(24rem, calc(100vh - var(--zdp-space-8)));
1624
- max-inline-size: min(22rem, calc(100vw - var(--zdp-space-6)));
1623
+ max-block-size: min(24rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
1624
+ max-inline-size: min(22rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6)));
1625
1625
  min-inline-size: 12rem;
1626
1626
  overflow: auto;
1627
1627
  position: absolute;
1628
- z-index: 40;
1628
+ z-index: var(--zdp-layer-floating);
1629
1629
  }
1630
1630
 
1631
1631
  .zdp-popover__panel {
@@ -1635,7 +1635,7 @@
1635
1635
 
1636
1636
  .zdp-menu__panel {
1637
1637
  gap: var(--zdp-space-1);
1638
- max-inline-size: min(18rem, calc(100vw - var(--zdp-space-6)));
1638
+ max-inline-size: min(18rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6)));
1639
1639
  padding: var(--zdp-space-1);
1640
1640
  }
1641
1641
 
@@ -2104,13 +2104,13 @@
2104
2104
  font-family: var(--zdp-font-family-sans);
2105
2105
  inline-size: 100%;
2106
2106
  left: 0;
2107
- max-block-size: min(18rem, calc(100vh - var(--zdp-space-8)));
2107
+ max-block-size: min(18rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
2108
2108
  min-width: 0;
2109
2109
  overflow: auto;
2110
2110
  padding: var(--zdp-space-1);
2111
2111
  position: absolute;
2112
2112
  top: calc(100% + var(--zdp-space-2));
2113
- z-index: 40;
2113
+ z-index: var(--zdp-layer-floating);
2114
2114
  }
2115
2115
 
2116
2116
  .zdp-combobox__listbox {
@@ -3087,10 +3087,10 @@
3087
3087
  box-sizing: border-box;
3088
3088
  display: grid;
3089
3089
  gap: var(--zdp-space-3);
3090
- inline-size: min(28rem, calc(100vw - var(--zdp-space-6)));
3090
+ inline-size: min(28rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6)));
3091
3091
  max-inline-size: 100%;
3092
3092
  pointer-events: none;
3093
- z-index: 50;
3093
+ z-index: var(--zdp-layer-toast);
3094
3094
  }
3095
3095
 
3096
3096
  .zdp-status-toast .zdp-toast {
@@ -3110,23 +3110,23 @@
3110
3110
  }
3111
3111
 
3112
3112
  .zdp-status-toast--top-start {
3113
- left: var(--zdp-space-4);
3114
- top: var(--zdp-space-4);
3113
+ left: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start));
3114
+ top: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-start));
3115
3115
  }
3116
3116
 
3117
3117
  .zdp-status-toast--top-end {
3118
- right: var(--zdp-space-4);
3119
- top: var(--zdp-space-4);
3118
+ right: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
3119
+ top: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-start));
3120
3120
  }
3121
3121
 
3122
3122
  .zdp-status-toast--bottom-start {
3123
- bottom: var(--zdp-space-4);
3124
- left: var(--zdp-space-4);
3123
+ bottom: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
3124
+ left: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start));
3125
3125
  }
3126
3126
 
3127
3127
  .zdp-status-toast--bottom-end {
3128
- bottom: var(--zdp-space-4);
3129
- right: var(--zdp-space-4);
3128
+ bottom: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
3129
+ right: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
3130
3130
  }
3131
3131
 
3132
3132
  @media (max-width: 42rem) {
@@ -3134,8 +3134,8 @@
3134
3134
  .zdp-status-toast--top-end,
3135
3135
  .zdp-status-toast--bottom-start,
3136
3136
  .zdp-status-toast--bottom-end {
3137
- left: var(--zdp-space-3);
3138
- right: var(--zdp-space-3);
3137
+ left: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start));
3138
+ right: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
3139
3139
  }
3140
3140
  }
3141
3141
 
@@ -3952,18 +3952,14 @@
3952
3952
  }
3953
3953
 
3954
3954
  .zdp-term-sheet__backdrop {
3955
- background: rgb(47 36 24 / 0.28);
3955
+ background: var(--zdp-color-backdrop-term-sheet);
3956
3956
  border: 0;
3957
3957
  cursor: pointer;
3958
3958
  inset: 0;
3959
3959
  margin: 0;
3960
3960
  padding: 0;
3961
3961
  position: fixed;
3962
- z-index: 900;
3963
- }
3964
-
3965
- [data-zdp-theme="dark"] .zdp-term-sheet__backdrop {
3966
- background: rgb(10 8 5 / 0.64);
3962
+ z-index: var(--zdp-layer-term-sheet);
3967
3963
  }
3968
3964
 
3969
3965
  .zdp-term-sheet {
@@ -3974,19 +3970,20 @@
3974
3970
  display: grid;
3975
3971
  font-family: var(--zdp-font-family-sans);
3976
3972
  gap: var(--zdp-space-4);
3977
- max-block-size: calc(100vh - var(--zdp-space-6));
3973
+ max-block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
3978
3974
  overflow: auto;
3979
- padding: var(--zdp-space-5);
3975
+ padding-block: var(--zdp-space-5) max(var(--zdp-space-5), var(--zdp-viewport-safe-block-end));
3976
+ padding-inline: max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-end));
3980
3977
  position: fixed;
3981
- z-index: 901;
3978
+ z-index: calc(var(--zdp-layer-term-sheet) + 1);
3982
3979
  }
3983
3980
 
3984
3981
  .zdp-term-sheet--right {
3985
- block-size: calc(100vh - var(--zdp-space-6));
3982
+ block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
3986
3983
  border-radius: var(--zdp-control-radius);
3987
- inline-size: min(28rem, calc(100vw - var(--zdp-space-6)));
3988
- inset-block: var(--zdp-space-3);
3989
- inset-inline-end: var(--zdp-space-3);
3984
+ inline-size: min(28rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
3985
+ inset-block: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
3986
+ inset-inline-end: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
3990
3987
  }
3991
3988
 
3992
3989
  .zdp-term-sheet--bottom {
@@ -3995,7 +3992,7 @@
3995
3992
  border-radius: var(--zdp-control-radius) var(--zdp-control-radius) 0 0;
3996
3993
  inset-block-end: 0;
3997
3994
  inset-inline: 0;
3998
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
3995
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
3999
3996
  }
4000
3997
 
4001
3998
  .zdp-term-sheet:focus-visible {
@@ -4134,8 +4131,9 @@
4134
4131
  inline-size: auto;
4135
4132
  inset-block: auto 0;
4136
4133
  inset-inline: 0;
4137
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
4138
- padding: var(--zdp-space-4);
4134
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
4135
+ padding-block: var(--zdp-space-4) max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
4136
+ padding-inline: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
4139
4137
  }
4140
4138
  }
4141
4139
 
@@ -4144,18 +4142,14 @@
4144
4142
  }
4145
4143
 
4146
4144
  .zdp-sheet__backdrop {
4147
- background: rgb(47 36 24 / 0.32);
4145
+ background: var(--zdp-color-backdrop-sheet);
4148
4146
  border: 0;
4149
4147
  cursor: pointer;
4150
4148
  inset: 0;
4151
4149
  margin: 0;
4152
4150
  padding: 0;
4153
4151
  position: fixed;
4154
- z-index: 940;
4155
- }
4156
-
4157
- [data-zdp-theme="dark"] .zdp-sheet__backdrop {
4158
- background: rgb(10 8 5 / 0.66);
4152
+ z-index: var(--zdp-layer-sheet);
4159
4153
  }
4160
4154
 
4161
4155
  .zdp-sheet {
@@ -4166,27 +4160,28 @@
4166
4160
  display: grid;
4167
4161
  font-family: var(--zdp-font-family-sans);
4168
4162
  gap: var(--zdp-space-4);
4169
- max-block-size: calc(100vh - var(--zdp-space-6));
4163
+ max-block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
4170
4164
  min-width: 0;
4171
4165
  overflow: auto;
4172
- padding: var(--zdp-space-5);
4166
+ padding-block: var(--zdp-space-5) max(var(--zdp-space-5), var(--zdp-viewport-safe-block-end));
4167
+ padding-inline: max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-5), var(--zdp-viewport-safe-inline-end));
4173
4168
  position: fixed;
4174
- z-index: 941;
4169
+ z-index: calc(var(--zdp-layer-sheet) + 1);
4175
4170
  }
4176
4171
 
4177
4172
  .zdp-sheet--right,
4178
4173
  .zdp-sheet--left {
4179
- block-size: calc(100vh - var(--zdp-space-6));
4174
+ block-size: calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end));
4180
4175
  border-radius: var(--zdp-control-radius);
4181
- inset-block: var(--zdp-space-3);
4176
+ inset-block: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
4182
4177
  }
4183
4178
 
4184
4179
  .zdp-sheet--right {
4185
- inset-inline-end: var(--zdp-space-3);
4180
+ inset-inline-end: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
4186
4181
  }
4187
4182
 
4188
4183
  .zdp-sheet--left {
4189
- inset-inline-start: var(--zdp-space-3);
4184
+ inset-inline-start: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start));
4190
4185
  }
4191
4186
 
4192
4187
  .zdp-sheet--bottom {
@@ -4195,19 +4190,19 @@
4195
4190
  border-radius: var(--zdp-control-radius) var(--zdp-control-radius) 0 0;
4196
4191
  inset-block-end: 0;
4197
4192
  inset-inline: 0;
4198
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
4193
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
4199
4194
  }
4200
4195
 
4201
4196
  .zdp-sheet--sm {
4202
- inline-size: min(24rem, calc(100vw - var(--zdp-space-6)));
4197
+ inline-size: min(24rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
4203
4198
  }
4204
4199
 
4205
4200
  .zdp-sheet--md {
4206
- inline-size: min(30rem, calc(100vw - var(--zdp-space-6)));
4201
+ inline-size: min(30rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
4207
4202
  }
4208
4203
 
4209
4204
  .zdp-sheet--lg {
4210
- inline-size: min(38rem, calc(100vw - var(--zdp-space-6)));
4205
+ inline-size: min(38rem, calc(var(--zdp-viewport-inline) - var(--zdp-space-6) - var(--zdp-viewport-safe-inline-start) - var(--zdp-viewport-safe-inline-end)));
4211
4206
  }
4212
4207
 
4213
4208
  .zdp-sheet--bottom.zdp-sheet--sm,
@@ -4217,7 +4212,7 @@
4217
4212
  }
4218
4213
 
4219
4214
  .zdp-sheet--bottom.zdp-sheet--lg {
4220
- max-block-size: min(42rem, calc(100vh - var(--zdp-space-6)));
4215
+ max-block-size: min(42rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
4221
4216
  }
4222
4217
 
4223
4218
  .zdp-sheet:focus-visible {
@@ -4311,7 +4306,7 @@
4311
4306
  inline-size: auto;
4312
4307
  inset-block: auto 0;
4313
4308
  inset-inline: 0;
4314
- max-block-size: min(34rem, calc(100vh - var(--zdp-space-6)));
4309
+ max-block-size: min(34rem, calc(var(--zdp-viewport-block) - var(--zdp-space-6) - var(--zdp-viewport-safe-block-start) - var(--zdp-viewport-safe-block-end)));
4315
4310
  padding: var(--zdp-space-4);
4316
4311
  }
4317
4312
  }
@@ -4322,13 +4317,14 @@
4322
4317
  display: grid;
4323
4318
  inset: 0;
4324
4319
  justify-items: center;
4325
- padding: var(--zdp-space-4);
4320
+ padding-block: max(var(--zdp-space-4), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-block-end));
4321
+ padding-inline: max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-4), var(--zdp-viewport-safe-inline-end));
4326
4322
  position: fixed;
4327
- z-index: 1000;
4323
+ z-index: var(--zdp-layer-dialog);
4328
4324
  }
4329
4325
 
4330
4326
  .zdp-dialog__backdrop {
4331
- background: rgb(47 36 24 / 0.42);
4327
+ background: var(--zdp-color-backdrop-dialog);
4332
4328
  border: 0;
4333
4329
  cursor: pointer;
4334
4330
  inset: 0;
@@ -4337,10 +4333,6 @@
4337
4333
  position: fixed;
4338
4334
  }
4339
4335
 
4340
- [data-zdp-theme="dark"] .zdp-dialog__backdrop {
4341
- background: rgb(10 8 5 / 0.72);
4342
- }
4343
-
4344
4336
  .zdp-dialog__panel {
4345
4337
  background: var(--zdp-color-surface-panel);
4346
4338
  border: var(--zdp-control-border-width) solid var(--zdp-color-line-strong);
@@ -4349,7 +4341,7 @@
4349
4341
  color: var(--zdp-color-ink-normal);
4350
4342
  display: grid;
4351
4343
  gap: var(--zdp-space-4);
4352
- max-height: min(42rem, calc(100vh - var(--zdp-space-8)));
4344
+ max-height: min(42rem, calc(var(--zdp-viewport-block) - var(--zdp-space-8)));
4353
4345
  min-width: 0;
4354
4346
  overflow: auto;
4355
4347
  padding: var(--zdp-space-5);
@@ -4450,11 +4442,12 @@
4450
4442
  @media (max-width: 520px) {
4451
4443
  .zdp-dialog {
4452
4444
  align-items: end;
4453
- padding: var(--zdp-space-3);
4445
+ padding-block: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
4446
+ padding-inline: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start)) max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
4454
4447
  }
4455
4448
 
4456
4449
  .zdp-dialog__panel {
4457
- max-height: calc(100vh - var(--zdp-space-6));
4450
+ max-height: calc(var(--zdp-viewport-block) - var(--zdp-space-6));
4458
4451
  padding: var(--zdp-space-4);
4459
4452
  }
4460
4453
  }
@@ -4501,12 +4494,12 @@
4501
4494
  .zdp-share-dock {
4502
4495
  color: var(--zdp-color-ink-normal);
4503
4496
  font-family: var(--zdp-font-family-sans);
4504
- z-index: 20;
4497
+ z-index: var(--zdp-layer-share-dock);
4505
4498
  }
4506
4499
 
4507
4500
  .zdp-share-dock--side {
4508
4501
  position: fixed;
4509
- right: max(var(--zdp-space-3), calc((100vw - var(--zdp-breakpoint-desktop)) / 2 + var(--zdp-space-4)));
4502
+ right: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end), calc((var(--zdp-viewport-inline) - var(--zdp-breakpoint-desktop)) / 2 + var(--zdp-space-4)));
4510
4503
  top: 40vh;
4511
4504
  }
4512
4505
 
@@ -4603,9 +4596,9 @@
4603
4596
 
4604
4597
  @media (max-width: 57.5rem) {
4605
4598
  .zdp-share-dock--side {
4606
- bottom: var(--zdp-space-3);
4607
- left: var(--zdp-space-3);
4608
- right: var(--zdp-space-3);
4599
+ bottom: max(var(--zdp-space-3), var(--zdp-viewport-safe-block-end));
4600
+ left: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-start));
4601
+ right: max(var(--zdp-space-3), var(--zdp-viewport-safe-inline-end));
4609
4602
  top: auto;
4610
4603
  }
4611
4604
 
@@ -4613,7 +4606,7 @@
4613
4606
  display: flex;
4614
4607
  flex-wrap: wrap;
4615
4608
  justify-content: center;
4616
- max-inline-size: calc(100vw - var(--zdp-space-6));
4609
+ max-inline-size: calc(var(--zdp-viewport-inline) - var(--zdp-space-6));
4617
4610
  }
4618
4611
 
4619
4612
  .zdp-share-dock--rail {
@@ -4675,7 +4668,7 @@
4675
4668
  @media (max-width: 42rem) {
4676
4669
  .zdp-share-dock--side,
4677
4670
  .zdp-share-dock--bottom {
4678
- bottom: var(--zdp-space-2);
4671
+ bottom: max(var(--zdp-space-2), var(--zdp-viewport-safe-block-end));
4679
4672
  }
4680
4673
 
4681
4674
  .zdp-share-dock__list {
@@ -2,3 +2,4 @@
2
2
  @import url("https://cdn.jsdelivr.net/npm/@fontsource-variable/noto-sans-sc@5.2.10/index.css");
3
3
  @import url("https://cdn.jsdelivr.net/npm/@fontsource-variable/noto-sans-devanagari@5.2.8/index.css");
4
4
  @import url("https://cdn.jsdelivr.net/npm/@fontsource-variable/noto-sans-jp@5.2.10/index.css");
5
+ @import url("https://cdn.jsdelivr.net/npm/@fontsource-variable/noto-sans-thai@5.2.8/index.css");
@@ -21,6 +21,9 @@
21
21
  --zdp-color-scrollbar-thumb-hover: #8b6f45;
22
22
  --zdp-color-selection-surface: #e7c97a;
23
23
  --zdp-color-selection-text: #1f160d;
24
+ --zdp-color-backdrop-term-sheet: rgb(47 36 24 / 0.28);
25
+ --zdp-color-backdrop-sheet: rgb(47 36 24 / 0.32);
26
+ --zdp-color-backdrop-dialog: rgb(47 36 24 / 0.42);
24
27
  --zdp-color-accent-primary: #d8c8ac;
25
28
  --zdp-color-accent-primary-strong: #b89a6a;
26
29
  --zdp-color-accent-primary-soft: #f1e4cc;
@@ -48,7 +51,8 @@
48
51
  --zdp-font-family-chinese: "Noto Sans SC Variable", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "Pretendard Variable", Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
49
52
  --zdp-font-family-devanagari: "Noto Sans Devanagari Variable", "Noto Sans Devanagari", "Nirmala UI", "Kohinoor Devanagari", ui-sans-serif, system-ui, sans-serif;
50
53
  --zdp-font-family-japanese: "Noto Sans JP Variable", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", "Meiryo", "Pretendard Variable", Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
51
- --zdp-font-family-multiscript: "Pretendard Variable", Pretendard, "Manrope Variable", Manrope, "Inter Variable", Inter, "Noto Sans SC Variable", "Noto Sans SC", "Noto Sans JP Variable", "Noto Sans JP", "Noto Sans Devanagari Variable", "Noto Sans Devanagari", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "PingFang SC", "Microsoft YaHei", "Hiragino Sans", "Yu Gothic", "Meiryo", "Nirmala UI", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
54
+ --zdp-font-family-thai: "Noto Sans Thai Variable", "Noto Sans Thai", "Leelawadee UI", Tahoma, ui-sans-serif, system-ui, sans-serif;
55
+ --zdp-font-family-multiscript: "Pretendard Variable", Pretendard, "Manrope Variable", Manrope, "Inter Variable", Inter, "Noto Sans SC Variable", "Noto Sans SC", "Noto Sans JP Variable", "Noto Sans JP", "Noto Sans Devanagari Variable", "Noto Sans Devanagari", "Noto Sans Thai Variable", "Noto Sans Thai", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "PingFang SC", "Microsoft YaHei", "Hiragino Sans", "Yu Gothic", "Meiryo", "Nirmala UI", "Leelawadee UI", Tahoma, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
52
56
  --zdp-font-family-serif: Cardo, Georgia, "Times New Roman", ui-serif, serif;
53
57
  --zdp-font-family-display: "Pretendard Variable", Pretendard, "Manrope Variable", Manrope, "Inter Variable", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
54
58
  --zdp-font-family-brand: "Playwrite AU VIC Guides", "Pretendard Variable", Pretendard, "Manrope Variable", Manrope, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
@@ -92,6 +96,20 @@
92
96
  --zdp-breakpoint-tablet: 64rem;
93
97
  --zdp-breakpoint-desktop: 80rem;
94
98
  --zdp-breakpoint-wide: 96rem;
99
+ --zdp-layer-behind: -1;
100
+ --zdp-layer-share-dock: 20;
101
+ --zdp-layer-floating: 40;
102
+ --zdp-layer-toast: 50;
103
+ --zdp-layer-term-sheet: 900;
104
+ --zdp-layer-sheet: 940;
105
+ --zdp-layer-dialog: 1000;
106
+ --zdp-layer-skip-link: 10000;
107
+ --zdp-viewport-block: 100vh;
108
+ --zdp-viewport-inline: 100vw;
109
+ --zdp-viewport-safe-block-start: env(safe-area-inset-top, 0px);
110
+ --zdp-viewport-safe-block-end: env(safe-area-inset-bottom, 0px);
111
+ --zdp-viewport-safe-inline-start: env(safe-area-inset-left, 0px);
112
+ --zdp-viewport-safe-inline-end: env(safe-area-inset-right, 0px);
95
113
  --zdp-control-height-sm: 2.25rem;
96
114
  --zdp-control-height-md: 2.75rem;
97
115
  --zdp-control-icon-sm: 2.25rem;
@@ -124,6 +142,20 @@
124
142
  --zdp-motion-normal: 180ms;
125
143
  }
126
144
 
145
+ @supports (height: 100svh) {
146
+ :root,
147
+ [data-zdp-theme] {
148
+ --zdp-viewport-block: 100svh;
149
+ }
150
+ }
151
+
152
+ @supports (width: 100svw) {
153
+ :root,
154
+ [data-zdp-theme] {
155
+ --zdp-viewport-inline: 100svw;
156
+ }
157
+ }
158
+
127
159
  [data-zdp-theme="dark"] {
128
160
  --zdp-color-ink-strong: #fff8ea;
129
161
  --zdp-color-ink-normal: #f1e4cc;
@@ -144,6 +176,9 @@
144
176
  --zdp-color-scrollbar-thumb-hover: #d8c8ac;
145
177
  --zdp-color-selection-surface: #e7c97a;
146
178
  --zdp-color-selection-text: #1f160d;
179
+ --zdp-color-backdrop-term-sheet: rgb(10 8 5 / 0.64);
180
+ --zdp-color-backdrop-sheet: rgb(10 8 5 / 0.66);
181
+ --zdp-color-backdrop-dialog: rgb(10 8 5 / 0.72);
147
182
  --zdp-color-accent-primary: #d8c8ac;
148
183
  --zdp-color-accent-primary-strong: #e7c97a;
149
184
  --zdp-color-accent-primary-soft: #68553a;
@@ -174,6 +209,9 @@
174
209
  --zdp-color-scrollbar-thumb-hover: oklch(55% 0.075 76);
175
210
  --zdp-color-selection-surface: oklch(84% 0.1 86);
176
211
  --zdp-color-selection-text: oklch(20% 0.035 70);
212
+ --zdp-color-backdrop-term-sheet: oklch(27% 0.04 70 / 0.28);
213
+ --zdp-color-backdrop-sheet: oklch(27% 0.04 70 / 0.32);
214
+ --zdp-color-backdrop-dialog: oklch(27% 0.04 70 / 0.42);
177
215
  --zdp-color-accent-primary: oklch(82% 0.045 82);
178
216
  --zdp-color-accent-primary-strong: oklch(70% 0.08 76);
179
217
  --zdp-color-accent-primary-soft: oklch(91% 0.045 82);
@@ -202,6 +240,9 @@
202
240
  --zdp-color-scrollbar-thumb-hover: oklch(82% 0.045 82);
203
241
  --zdp-color-selection-surface: oklch(84% 0.1 86);
204
242
  --zdp-color-selection-text: oklch(20% 0.035 70);
243
+ --zdp-color-backdrop-term-sheet: oklch(8% 0.01 70 / 0.64);
244
+ --zdp-color-backdrop-sheet: oklch(8% 0.01 70 / 0.66);
245
+ --zdp-color-backdrop-dialog: oklch(8% 0.01 70 / 0.72);
205
246
  --zdp-color-accent-primary: oklch(82% 0.045 82);
206
247
  --zdp-color-accent-primary-strong: oklch(84% 0.1 86);
207
248
  --zdp-color-accent-primary-soft: oklch(43% 0.055 76);
@@ -266,7 +307,11 @@
266
307
 
267
308
  .zdp-surface-reset:lang(en),
268
309
  .zdp-surface-reset:lang(es),
269
- .zdp-surface-reset:lang(fr) {
310
+ .zdp-surface-reset:lang(fr),
311
+ .zdp-surface-reset:lang(vi),
312
+ .zdp-surface-reset:lang(ru),
313
+ .zdp-surface-reset:lang(id),
314
+ .zdp-surface-reset:lang(ms) {
270
315
  --zdp-font-family-sans: var(--zdp-font-family-latin);
271
316
  --zdp-font-family-display: var(--zdp-font-family-latin);
272
317
  hyphens: auto;
@@ -274,7 +319,11 @@
274
319
 
275
320
  .zdp-surface-reset :lang(en),
276
321
  .zdp-surface-reset :lang(es),
277
- .zdp-surface-reset :lang(fr) {
322
+ .zdp-surface-reset :lang(fr),
323
+ .zdp-surface-reset :lang(vi),
324
+ .zdp-surface-reset :lang(ru),
325
+ .zdp-surface-reset :lang(id),
326
+ .zdp-surface-reset :lang(ms) {
278
327
  font-family: var(--zdp-font-family-latin);
279
328
  hyphens: auto;
280
329
  }
@@ -337,6 +386,17 @@
337
386
  word-break: normal;
338
387
  }
339
388
 
389
+ .zdp-surface-reset:lang(th) {
390
+ --zdp-font-family-sans: var(--zdp-font-family-thai);
391
+ --zdp-font-family-display: var(--zdp-font-family-thai);
392
+ word-break: normal;
393
+ }
394
+
395
+ .zdp-surface-reset :lang(th) {
396
+ font-family: var(--zdp-font-family-thai);
397
+ word-break: normal;
398
+ }
399
+
340
400
  .zdp-surface-reset *,
341
401
  .zdp-surface-reset *::before,
342
402
  .zdp-surface-reset *::after {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "../schemas/design-tokens.schema.json",
3
3
  "name": "zdp-core",
4
- "version": "0.6.10",
4
+ "version": "0.7.0",
5
5
  "color": {
6
6
  "ink": {
7
7
  "strong": {
@@ -145,7 +145,8 @@
145
145
  "chinese": "\"Noto Sans SC Variable\", \"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", \"Pretendard Variable\", Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
146
146
  "devanagari": "\"Noto Sans Devanagari Variable\", \"Noto Sans Devanagari\", \"Nirmala UI\", \"Kohinoor Devanagari\", ui-sans-serif, system-ui, sans-serif",
147
147
  "japanese": "\"Noto Sans JP Variable\", \"Noto Sans JP\", \"Hiragino Sans\", \"Yu Gothic\", \"Meiryo\", \"Pretendard Variable\", Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
148
- "multiscript": "\"Pretendard Variable\", Pretendard, \"Manrope Variable\", Manrope, \"Inter Variable\", Inter, \"Noto Sans SC Variable\", \"Noto Sans SC\", \"Noto Sans JP Variable\", \"Noto Sans JP\", \"Noto Sans Devanagari Variable\", \"Noto Sans Devanagari\", \"Apple SD Gothic Neo\", \"Noto Sans KR\", \"Malgun Gothic\", \"PingFang SC\", \"Microsoft YaHei\", \"Hiragino Sans\", \"Yu Gothic\", \"Meiryo\", \"Nirmala UI\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
148
+ "thai": "\"Noto Sans Thai Variable\", \"Noto Sans Thai\", \"Leelawadee UI\", Tahoma, ui-sans-serif, system-ui, sans-serif",
149
+ "multiscript": "\"Pretendard Variable\", Pretendard, \"Manrope Variable\", Manrope, \"Inter Variable\", Inter, \"Noto Sans SC Variable\", \"Noto Sans SC\", \"Noto Sans JP Variable\", \"Noto Sans JP\", \"Noto Sans Devanagari Variable\", \"Noto Sans Devanagari\", \"Noto Sans Thai Variable\", \"Noto Sans Thai\", \"Apple SD Gothic Neo\", \"Noto Sans KR\", \"Malgun Gothic\", \"PingFang SC\", \"Microsoft YaHei\", \"Hiragino Sans\", \"Yu Gothic\", \"Meiryo\", \"Nirmala UI\", \"Leelawadee UI\", Tahoma, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
149
150
  "serif": "Cardo, Georgia, \"Times New Roman\", ui-serif, serif",
150
151
  "display": "\"Pretendard Variable\", Pretendard, \"Manrope Variable\", Manrope, \"Inter Variable\", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
151
152
  "brand": "\"Playwrite AU VIC Guides\", \"Pretendard Variable\", Pretendard, \"Manrope Variable\", Manrope, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
@@ -201,6 +202,24 @@
201
202
  "desktop": "80rem",
202
203
  "wide": "96rem"
203
204
  },
205
+ "layer": {
206
+ "behind": "-1",
207
+ "shareDock": "20",
208
+ "floating": "40",
209
+ "toast": "50",
210
+ "termSheet": "900",
211
+ "sheet": "940",
212
+ "dialog": "1000",
213
+ "skipLink": "10000"
214
+ },
215
+ "viewport": {
216
+ "block": "100vh",
217
+ "inline": "100vw",
218
+ "safeBlockStart": "env(safe-area-inset-top, 0px)",
219
+ "safeBlockEnd": "env(safe-area-inset-bottom, 0px)",
220
+ "safeInlineStart": "env(safe-area-inset-left, 0px)",
221
+ "safeInlineEnd": "env(safe-area-inset-right, 0px)"
222
+ },
204
223
  "control": {
205
224
  "heightSm": "2.25rem",
206
225
  "heightMd": "2.75rem",
package/dist/tokens.js CHANGED
@@ -45,6 +45,7 @@ export const zdpTokenNames = [
45
45
  'font.family.chinese',
46
46
  'font.family.devanagari',
47
47
  'font.family.japanese',
48
+ 'font.family.thai',
48
49
  'font.family.multiscript',
49
50
  'font.family.serif',
50
51
  'font.family.display',
@@ -89,6 +90,20 @@ export const zdpTokenNames = [
89
90
  'breakpoint.tablet',
90
91
  'breakpoint.desktop',
91
92
  'breakpoint.wide',
93
+ 'layer.behind',
94
+ 'layer.shareDock',
95
+ 'layer.floating',
96
+ 'layer.toast',
97
+ 'layer.termSheet',
98
+ 'layer.sheet',
99
+ 'layer.dialog',
100
+ 'layer.skipLink',
101
+ 'viewport.block',
102
+ 'viewport.inline',
103
+ 'viewport.safeBlockStart',
104
+ 'viewport.safeBlockEnd',
105
+ 'viewport.safeInlineStart',
106
+ 'viewport.safeInlineEnd',
92
107
  'control.heightSm',
93
108
  'control.heightMd',
94
109
  'control.iconSm',
package/dist/tokens.ts CHANGED
@@ -45,6 +45,7 @@ export const zdpTokenNames = [
45
45
  'font.family.chinese',
46
46
  'font.family.devanagari',
47
47
  'font.family.japanese',
48
+ 'font.family.thai',
48
49
  'font.family.multiscript',
49
50
  'font.family.serif',
50
51
  'font.family.display',
@@ -89,6 +90,20 @@ export const zdpTokenNames = [
89
90
  'breakpoint.tablet',
90
91
  'breakpoint.desktop',
91
92
  'breakpoint.wide',
93
+ 'layer.behind',
94
+ 'layer.shareDock',
95
+ 'layer.floating',
96
+ 'layer.toast',
97
+ 'layer.termSheet',
98
+ 'layer.sheet',
99
+ 'layer.dialog',
100
+ 'layer.skipLink',
101
+ 'viewport.block',
102
+ 'viewport.inline',
103
+ 'viewport.safeBlockStart',
104
+ 'viewport.safeBlockEnd',
105
+ 'viewport.safeInlineStart',
106
+ 'viewport.safeInlineEnd',
92
107
  'control.heightSm',
93
108
  'control.heightMd',
94
109
  'control.iconSm',
@@ -11,7 +11,7 @@ Default component text is English. 소비 앱은 화면 locale에 맞춰 user-fa
11
11
  - 소비 저장소는 `zdp-design-system`의 public export만 사용하고 내부 `src/` deep import를 만들지 않는다.
12
12
  - package export는 `dist/` 산출물을 통해 소비한다. root runtime entry는 `dist/index.js`, type entry는 `dist/index.d.ts`다. 원천은 `src/lib`, `src/styles`, `tokens/zdp.tokens.json`, `src/lib/share.ts`이고 `dist/`는 release 전 `bun run package:build`로 다시 만든다.
13
13
  - ZDP monorepo 안의 active sibling 소비처는 unpublished local changes와 package surface를 함께 검증하기 위해 `file:../zdp-design-system`을 유지할 수 있다. 이 경우 CI는 sibling `zdp-design-system`을 checkout하고 `bun run package:build`를 먼저 실행해야 한다.
14
- - standalone consumer, public template, external example처럼 sibling checkout을 전제로 하지 않는 표면은 npm registry package를 사용한다. 0.44.0 이상에서는 `zdp-design-system: ^0.44.0`을 기본 semver 범위로 쓰고, 재현 가능한 release proof가 필요한 곳만 exact version을 pin한다.
14
+ - standalone consumer, public template, external example처럼 sibling checkout을 전제로 하지 않는 표면은 npm registry package를 사용한다. 0.45.0 이상에서는 `zdp-design-system: ^0.45.0`을 기본 semver 범위로 쓰고, 재현 가능한 release proof가 필요한 곳만 exact version을 pin한다.
15
15
  - `zdpTokenNames`, `share.js`, `share.d.ts`는 손으로 맞추지 않고 `tokens:generate`, `share-icons:generate` 산출물로 유지한다.
16
16
  - 새 버전은 소비 저장소가 opt-in으로 채택한다. broad adoption 전에는 대표 소비처에서 시각과 build를 확인한다.
17
17
  - keyboard focus, flat UI, framed control, Pretendard-first font stack은 소비처에서 임의로 낮추지 않는다.
@@ -27,7 +27,7 @@ Astro 정적 사이트는 전역 스타일로 기본 CSS를 먼저 불러온다.
27
27
  import 'zdp-design-system/styles.css';
28
28
  ```
29
29
 
30
- 다국어 페이지가 라틴, 중국어, 데바나가리, 일본어 웹폰트를 명시적으로 써야 하면 선택형 locale font entry를 추가한다.
30
+ 다국어 페이지가 라틴, 중국어, 데바나가리, 일본어, 태국어 웹폰트를 명시적으로 써야 하면 선택형 locale font entry를 추가한다.
31
31
 
32
32
  ```ts
33
33
  import 'zdp-design-system/styles.css';
@@ -378,7 +378,7 @@ TextScaleControl은 글자 크기 선택을 위한 radiogroup, 선택 상태, fo
378
378
  Tooltip은 짧은 보조 설명 표면만 제공하며 Escape dismiss를 유지하고, 긴 도움말, validation message, popover, tour, 권한 판단은 소비 앱이 계속 소유한다. 설명이 접근성 이름을 보강해야 할 때만 Tooltip `id`를 넘기고 trigger에 `aria-describedby`를 연결한다.
379
379
  Accordion과 Disclosure는 접힌 안내, 설정 묶음, FAQ형 설명의 trigger/panel/`aria-expanded`/`aria-controls` 구조만 제공하며 실제 FAQ 문구, 설정 값, 항목 노출, 권한, 데이터 fetch 판단은 소비 앱이 계속 소유한다. Accordion은 단일 또는 복수 열린 상태를 관리하지만 항목 visibility, 필터 의미, 서버 상태 판단으로 확장하지 않는다.
380
380
  SegmentedControl은 보기 방식, 밀도, 기간처럼 가까운 단일 선택의 `radiogroup`/`radio`/`aria-checked` 구조만 제공하며 실제 필터 의미, URL state, 정렬, 데이터 로딩, 권한 판단은 소비 앱이 계속 소유한다.
381
- Popover와 Menu는 설정, 더보기, 필터, 계정 메뉴처럼 가까운 트리거에서 짧게 펼치는 표면만 제공하며 항목 노출, 권한, 라우팅, 필터 의미, 계정 상태 판단은 소비 앱이 계속 소유한다. Popover와 Menu는 Escape 닫기, outside click, focus 복귀, menu keyboard movement 같은 기본 상호작용만 유지하고 tour, validation, modal decision, floating collision engine 역할로 확장하지 않는다.
381
+ Popover와 Menu는 설정, 더보기, 필터, 계정 메뉴처럼 가까운 트리거에서 짧게 펼치는 표면만 제공하며 항목 노출, 권한, 라우팅, 필터 의미, 계정 상태 판단은 소비 앱이 계속 소유한다. Popover와 Menu는 Escape 닫기, outside click, focus 복귀, menu keyboard movement 같은 기본 상호작용만 유지하고 tour, validation, modal decision, floating collision engine 역할로 확장하지 않는다. 모바일 keyboard, 긴 옵션, async option, grouped option, virtualized list, collision 반복 요구는 Popover/Menu/Combobox 안에서 계속 키우지 않고 Sheet flow 또는 headless spike로 보낸다.
382
382
  Sheet는 right, left, bottom edge panel로 설정, 필터, 보조 흐름을 여는 modal surface만 제공한다. Escape 닫기, backdrop 닫기, Tab 순환, scroll lock, 이전 focus 복귀, `role="dialog"`, `aria-modal` 구조는 Sheet 표면의 기본 동작으로 유지한다. Drawer는 별도 컴포넌트로 복제하지 않고 Sheet placement/use case로 먼저 다룬다. 저장, 권한, 데이터 fetch, 라우팅 판단은 소비 앱이 계속 소유한다.
383
383
  Sheet root는 `data-zdp-sheet-placement`, `data-zdp-sheet-size`, `data-zdp-sheet-surface="sheet"`를 남겨 QA와 소비 앱이 surface identity를 확인할 수 있게 한다.
384
384
  Toast와 StatusToast는 저장, 동기화, 실패, 경고처럼 짧은 상태 알림 표면과 dismiss/action/live-region 연결만 제공하며 알림 발생 조건, 큐 순서, 자동 닫힘 타이머, 중복 제거, 재시도 정책, 권한, 서버 상태 판단은 소비 앱이 계속 소유한다. Toast와 StatusToast는 페이지 안 피드백이나 modal decision을 대체하지 않고, 오래 읽어야 하는 안내는 Callout 또는 소비 앱의 별도 흐름으로 남긴다.
@@ -419,10 +419,12 @@ Flutter와 native shell은 Svelte 컴포넌트를 직접 소비하지 않는다.
419
419
 
420
420
  - `hex`는 기본 theme fallback이다.
421
421
  - `oklch`는 웹 또는 변환 가능한 렌더러에서 색 의도값으로 사용한다.
422
- - `font.family.korean`, `font.family.latin`, `font.family.chinese`, `font.family.devanagari`, `font.family.japanese`, `font.family.multiscript`는 locale별 fallback 순서의 source of truth다.
422
+ - `font.family.korean`, `font.family.latin`, `font.family.chinese`, `font.family.devanagari`, `font.family.japanese`, `font.family.thai`, `font.family.multiscript`는 locale별 fallback 순서의 source of truth다.
423
423
  - `font.family.brand`는 브랜드 워드마크 전용 source of truth다. 일반 heading의 `font.family.display`와 섞지 않는다.
424
424
  - `font.family.expressionScript`, `font.family.expressionInscription`, `font.family.expressionSketch`, `font.family.expressionEditorial`, `font.family.expressionSans`, `font.family.expressionKeyboard`는 opt-in 표현용 source of truth다. `expressive-fonts.css`를 import한 표면에서만 캠페인, 섹션 제목, 짧은 보조 안내, 키보드 표식에 제한해서 쓴다.
425
425
  - `control.heightMd`, `control.glyphMd`, `control.choiceSize`, `control.choiceIndicatorSize`, `control.switchWidth`, `control.switchHeight`, `control.scrollbarSize`, `control.radius`, `control.borderWidth`, `control.hitTarget`은 native control size, icon glyph size, choice mark, switch track, scrollbar 두께를 맞출 때의 기준이다.
426
+ - `layer.*`는 skip link, floating overlay, toast, sheet, dialog처럼 겹침 순서가 필요한 surface의 기준이고, 소비처는 raw z-index 숫자를 직접 쌓지 않는다.
427
+ - `viewport.*`는 overlay panel clamp, safe-area inset, mobile viewport fallback의 기준이고, 소비처는 modal/sheet/dropdown에 raw `100vh`/`100vw`를 직접 쓰지 않는다.
426
428
  - `color.scrollbar.track`, `color.scrollbar.thumb`, `color.scrollbar.thumbHover`는 overflow 영역의 light/dark scrollbar 색 기준이다.
427
429
  - `color.selection.surface`, `color.selection.text`는 `.zdp-surface-reset` 안의 drag text selection 색 기준이다.
428
430
  - `focus.surface`, `focus.text`, `focus.line`은 keyboard focus 또는 TV/desktop focus affordance의 기준색이다.
@@ -472,7 +474,7 @@ Flutter와 native shell은 Svelte 컴포넌트를 직접 소비하지 않는다.
472
474
  - Table cell, code body, toast message, document body, card data, identity label, 주소, 이메일, ID, 날짜, 가격, 에러 메시지는 계속 선택하고 복사할 수 있는지 확인한다.
473
475
  - Drag interaction은 drag start부터 end/cancel/pointer release까지만 `.zdp-user-select-dragging`을 붙이고, 앱 root나 page/card/list/table container에 상시 `user-select: none`이 남지 않는지 확인한다.
474
476
  - light/dark, keyboard focus, disabled, invalid 상태를 소비처 화면에서 확인한다.
475
- - Theme / Locale Stress story에서 light/dark, ZDP Mobile 폭, 긴 한국어/영어/중국어/힌디어 문장, focus-visible 상태가 함께 무너지지 않는지 확인한다.
477
+ - Theme / Locale Stress story에서 light/dark, ZDP Mobile 폭, 긴 한국어/영어/중국어/힌디어/베트남어/러시아어/말레이어/태국어 문장, focus-visible 상태가 함께 무너지지 않는지 확인한다.
476
478
  - Dialog를 쓰는 화면은 Tab 순환, Escape 닫기, backdrop 닫기, 이전 focus 복귀를 확인한다.
477
479
  - 새 디자인 시스템 버전은 대표 소비처 한 곳에서 build와 시각 QA를 통과한 뒤 확산한다.
478
480
 
@@ -11,6 +11,7 @@
11
11
  - `Dialog`, `Sheet`, `TermSheet`는 ZDP modal layer, focusable helper, scroll lock, focus return을 유지하되 nested modal, inert sibling, portal 요구가 커지면 headless 후보로 다시 평가한다.
12
12
  - `Sheet`는 settings, filter, drawer형 보조 흐름의 공통 modal edge panel이다. Drawer를 별도 primitive로 복제하지 않고 Sheet placement/use case로 먼저 다룬다.
13
13
  - `Menu`와 `Popover`는 가장 높은 위험군이다. 단순 더보기와 짧은 필터 표면은 현재 구현으로 유지하되, typeahead, submenu, collision detection, portal, nested overlay가 필요해지면 Bits UI 내부 의존성 후보로 올린다.
14
+ - 모바일 keyboard, 긴 옵션, async option, grouped option, virtualized list, collision 반복 요구는 `Menu`, `Popover`, `Combobox` 안에서 계속 키우지 않고 `Sheet` flow 또는 headless spike로 보낸다.
14
15
  - `Tooltip`은 짧은 non-interactive label로만 유지한다. 긴 안내, 클릭 가능한 내용, mobile-first 설명은 Popover, Disclosure, Dialog, 소비 앱 flow로 보낸다.
15
16
 
16
17
  ## 감사 매트릭스
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zdp-design-system",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",