zdp-design-system 0.43.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +449 -0
  2. package/LICENSE +21 -0
  3. package/README.md +568 -0
  4. package/THIRD_PARTY_NOTICES.md +34 -0
  5. package/dist/code.ts +2 -0
  6. package/dist/combobox.ts +9 -0
  7. package/dist/command.ts +1 -0
  8. package/dist/components/Accordion.svelte +97 -0
  9. package/dist/components/Avatar.svelte +90 -0
  10. package/dist/components/Badge.svelte +61 -0
  11. package/dist/components/Breadcrumb.svelte +97 -0
  12. package/dist/components/Button.svelte +163 -0
  13. package/dist/components/Callout.svelte +81 -0
  14. package/dist/components/Card.svelte +151 -0
  15. package/dist/components/CardHeader.svelte +58 -0
  16. package/dist/components/Checkbox.svelte +135 -0
  17. package/dist/components/CodeBlock.svelte +247 -0
  18. package/dist/components/Combobox.svelte +552 -0
  19. package/dist/components/CommandField.svelte +230 -0
  20. package/dist/components/ConfirmAction.svelte +307 -0
  21. package/dist/components/Container.svelte +63 -0
  22. package/dist/components/Dialog.svelte +303 -0
  23. package/dist/components/Disclosure.svelte +176 -0
  24. package/dist/components/Divider.svelte +41 -0
  25. package/dist/components/EmptyState.svelte +79 -0
  26. package/dist/components/ErrorText.svelte +18 -0
  27. package/dist/components/Field.svelte +38 -0
  28. package/dist/components/Grid.svelte +76 -0
  29. package/dist/components/HelpText.svelte +17 -0
  30. package/dist/components/Icon.svelte +45 -0
  31. package/dist/components/IconButton.svelte +162 -0
  32. package/dist/components/IdentityChip.svelte +130 -0
  33. package/dist/components/Inline.svelte +85 -0
  34. package/dist/components/InlineCode.svelte +27 -0
  35. package/dist/components/Input.svelte +109 -0
  36. package/dist/components/Kbd.svelte +63 -0
  37. package/dist/components/KeyValue.svelte +73 -0
  38. package/dist/components/Label.svelte +43 -0
  39. package/dist/components/Link.svelte +70 -0
  40. package/dist/components/LocaleSwitcher.svelte +209 -0
  41. package/dist/components/Menu.svelte +491 -0
  42. package/dist/components/Page.svelte +36 -0
  43. package/dist/components/PageHeader.svelte +93 -0
  44. package/dist/components/Pagination.svelte +297 -0
  45. package/dist/components/Popover.svelte +208 -0
  46. package/dist/components/Progress.svelte +111 -0
  47. package/dist/components/Radio.svelte +132 -0
  48. package/dist/components/Section.svelte +52 -0
  49. package/dist/components/SegmentedControl.svelte +190 -0
  50. package/dist/components/Select.svelte +88 -0
  51. package/dist/components/ShareDock.svelte +304 -0
  52. package/dist/components/Sheet.svelte +332 -0
  53. package/dist/components/ShortcutHint.svelte +52 -0
  54. package/dist/components/Skeleton.svelte +82 -0
  55. package/dist/components/SkipLink.svelte +40 -0
  56. package/dist/components/SortHeader.svelte +138 -0
  57. package/dist/components/Spinner.svelte +82 -0
  58. package/dist/components/Stack.svelte +62 -0
  59. package/dist/components/StatusToast.svelte +133 -0
  60. package/dist/components/Surface.svelte +53 -0
  61. package/dist/components/Switch.svelte +152 -0
  62. package/dist/components/Table.svelte +94 -0
  63. package/dist/components/TableToolbar.svelte +195 -0
  64. package/dist/components/Tabs.svelte +205 -0
  65. package/dist/components/TermSheet.svelte +392 -0
  66. package/dist/components/TermTrigger.svelte +70 -0
  67. package/dist/components/TextScaleControl.svelte +219 -0
  68. package/dist/components/Textarea.svelte +106 -0
  69. package/dist/components/ThemeToggle.svelte +148 -0
  70. package/dist/components/Toast.svelte +180 -0
  71. package/dist/components/Toolbar.svelte +83 -0
  72. package/dist/components/Tooltip.svelte +199 -0
  73. package/dist/components/VisuallyHidden.svelte +18 -0
  74. package/dist/disclosure.ts +11 -0
  75. package/dist/focusable.ts +36 -0
  76. package/dist/identity.ts +5 -0
  77. package/dist/index.d.ts +106 -0
  78. package/dist/index.js +76 -0
  79. package/dist/index.ts +106 -0
  80. package/dist/menu.ts +12 -0
  81. package/dist/modal-layer.ts +108 -0
  82. package/dist/pagination.ts +10 -0
  83. package/dist/preferences.js +14 -0
  84. package/dist/preferences.ts +36 -0
  85. package/dist/progress.ts +4 -0
  86. package/dist/schemas/design-tokens.schema.json +119 -0
  87. package/dist/segmented.ts +8 -0
  88. package/dist/share.d.ts +48 -0
  89. package/dist/share.js +115 -0
  90. package/dist/share.ts +99 -0
  91. package/dist/sheet.ts +3 -0
  92. package/dist/shortcuts.js +125 -0
  93. package/dist/shortcuts.ts +153 -0
  94. package/dist/styles/brand-fonts.css +10 -0
  95. package/dist/styles/components.css +4686 -0
  96. package/dist/styles/expressive-fonts.css +2 -0
  97. package/dist/styles/index.css +2 -0
  98. package/dist/styles/locale-fonts.css +4 -0
  99. package/dist/styles/tokens.css +413 -0
  100. package/dist/table-tools.ts +10 -0
  101. package/dist/term.ts +16 -0
  102. package/dist/theme.ts +2 -0
  103. package/dist/toast.ts +14 -0
  104. package/dist/tokens/zdp.tokens.json +241 -0
  105. package/dist/tokens.js +122 -0
  106. package/dist/tokens.ts +123 -0
  107. package/docs/CONSUMER_CONTRACT.md +482 -0
  108. package/docs/EXTERNAL_UI_ADOPTION.md +141 -0
  109. package/docs/INTERACTIVE_PRIMITIVE_AUDIT.md +127 -0
  110. package/package.json +78 -0
@@ -0,0 +1,491 @@
1
+ <script lang="ts" context="module">
2
+ let nextMenuInstanceId = 0;
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import { tick } from 'svelte';
7
+ import type { ZdpMenuItem } from '../menu';
8
+
9
+ export let items: readonly ZdpMenuItem[] = [];
10
+ export let open = false;
11
+ export let triggerLabel = 'Menu';
12
+ export let idPrefix: string | null = null;
13
+ export let placement: 'top' | 'right' | 'bottom' | 'left' = 'bottom';
14
+ export let align: 'start' | 'center' | 'end' = 'end';
15
+ export let onOpenChange: ((open: boolean) => void) | null = null;
16
+ export let onSelect: ((event: MouseEvent, item: ZdpMenuItem) => void) | null = null;
17
+
18
+ const fallbackIdPrefix = `zdp-menu-${++nextMenuInstanceId}`;
19
+
20
+ let rootElement: HTMLElement | null = null;
21
+ let triggerElement: HTMLButtonElement | null = null;
22
+ let panelElement: HTMLElement | null = null;
23
+ let previousFocusElement: HTMLElement | null = null;
24
+ let knownOpenState = false;
25
+ let restoreFocusAfterClose = false;
26
+ let focusIntent: 'first' | 'last' | 'current' = 'first';
27
+ let activeItemId = '';
28
+
29
+ $: enabledItems = items.filter((item) => !item.disabled);
30
+ $: resolvedIdPrefix = toDomId(idPrefix ?? fallbackIdPrefix);
31
+ $: triggerId = `${resolvedIdPrefix}-trigger`;
32
+ $: panelId = `${resolvedIdPrefix}-panel`;
33
+ $: activeItemId = resolveActiveItemId(activeItemId, enabledItems);
34
+
35
+ $: if (open !== knownOpenState) {
36
+ knownOpenState = open;
37
+
38
+ if (open) {
39
+ void handleMenuOpened();
40
+ } else if (restoreFocusAfterClose) {
41
+ restorePreviousFocus();
42
+ restoreFocusAfterClose = false;
43
+ }
44
+ }
45
+
46
+ function setOpen(nextOpen: boolean): void {
47
+ if (open === nextOpen) {
48
+ return;
49
+ }
50
+
51
+ open = nextOpen;
52
+ onOpenChange?.(nextOpen);
53
+ }
54
+
55
+ function openMenu(nextFocusIntent: 'first' | 'last' | 'current' = 'first'): void {
56
+ focusIntent = nextFocusIntent;
57
+ setOpen(true);
58
+ }
59
+
60
+ function closeMenu(restoreFocus = true): void {
61
+ restoreFocusAfterClose = restoreFocus;
62
+ setOpen(false);
63
+ }
64
+
65
+ function handleTriggerClick(): void {
66
+ if (open) {
67
+ closeMenu(false);
68
+ return;
69
+ }
70
+
71
+ openMenu('first');
72
+ }
73
+
74
+ function handleTriggerKeydown(event: KeyboardEvent): void {
75
+ if (event.key === 'ArrowDown') {
76
+ event.preventDefault();
77
+ openMenu('first');
78
+ return;
79
+ }
80
+
81
+ if (event.key === 'ArrowUp') {
82
+ event.preventDefault();
83
+ openMenu('last');
84
+ }
85
+ }
86
+
87
+ async function handleMenuOpened(): Promise<void> {
88
+ if (typeof document === 'undefined') {
89
+ return;
90
+ }
91
+
92
+ const activeElement = document.activeElement;
93
+ previousFocusElement = activeElement instanceof HTMLElement ? activeElement : triggerElement;
94
+
95
+ if (focusIntent === 'first') {
96
+ activeItemId = enabledItems[0]?.id ?? '';
97
+ } else if (focusIntent === 'last') {
98
+ activeItemId = enabledItems[enabledItems.length - 1]?.id ?? '';
99
+ }
100
+
101
+ await tick();
102
+
103
+ focusActiveItem();
104
+ }
105
+
106
+ function restorePreviousFocus(): void {
107
+ if (typeof document === 'undefined') {
108
+ return;
109
+ }
110
+
111
+ const focusTarget = previousFocusElement ?? triggerElement;
112
+
113
+ if (focusTarget !== null && document.contains(focusTarget)) {
114
+ focusTarget.focus();
115
+ }
116
+
117
+ previousFocusElement = null;
118
+ }
119
+
120
+ function handleDocumentClick(event: MouseEvent): void {
121
+ if (!open || rootElement?.contains(event.target as Node)) {
122
+ return;
123
+ }
124
+
125
+ closeMenu(false);
126
+ }
127
+
128
+ function handlePanelKeydown(event: KeyboardEvent): void {
129
+ if (event.key === 'Escape') {
130
+ event.preventDefault();
131
+ closeMenu();
132
+ return;
133
+ }
134
+
135
+ if (event.key === 'Tab') {
136
+ closeMenu(false);
137
+ return;
138
+ }
139
+
140
+ if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
141
+ return;
142
+ }
143
+
144
+ event.preventDefault();
145
+ moveActiveItem(event.key);
146
+ }
147
+
148
+ function handleItemClick(event: MouseEvent, item: ZdpMenuItem): void {
149
+ if (item.disabled) {
150
+ event.preventDefault();
151
+ return;
152
+ }
153
+
154
+ item.onclick?.(event, item);
155
+ onSelect?.(event, item);
156
+ closeMenu(!item.href);
157
+ }
158
+
159
+ function moveActiveItem(key: string): void {
160
+ if (enabledItems.length === 0) {
161
+ return;
162
+ }
163
+
164
+ const currentIndex = Math.max(
165
+ 0,
166
+ enabledItems.findIndex((item) => item.id === activeItemId)
167
+ );
168
+ const nextIndex = getNextIndex(key, currentIndex, enabledItems.length);
169
+ activeItemId = enabledItems[nextIndex]?.id ?? activeItemId;
170
+ focusActiveItem();
171
+ }
172
+
173
+ function getNextIndex(key: string, currentIndex: number, length: number): number {
174
+ if (key === 'Home') {
175
+ return 0;
176
+ }
177
+
178
+ if (key === 'End') {
179
+ return length - 1;
180
+ }
181
+
182
+ if (key === 'ArrowUp') {
183
+ return (currentIndex - 1 + length) % length;
184
+ }
185
+
186
+ return (currentIndex + 1) % length;
187
+ }
188
+
189
+ function focusActiveItem(): void {
190
+ const nextItem = Array.from(panelElement?.querySelectorAll<HTMLElement>('[role="menuitem"]') ?? []).find(
191
+ (element) => element.dataset.menuItemId === activeItemId
192
+ );
193
+
194
+ nextItem?.focus();
195
+ }
196
+
197
+ function resolveActiveItemId(currentId: string, availableItems: readonly ZdpMenuItem[]): string {
198
+ if (availableItems.some((item) => item.id === currentId)) {
199
+ return currentId;
200
+ }
201
+
202
+ return availableItems[0]?.id ?? '';
203
+ }
204
+
205
+ function resolvedRel(item: ZdpMenuItem): string | undefined {
206
+ if (item.rel) {
207
+ return item.rel;
208
+ }
209
+
210
+ return item.target === '_blank' ? 'noopener noreferrer' : undefined;
211
+ }
212
+
213
+ function toDomId(id: string): string {
214
+ return id.trim().replace(/[^a-zA-Z0-9_-]+/g, '-') || 'menu';
215
+ }
216
+ </script>
217
+
218
+ <svelte:document onclick={handleDocumentClick} />
219
+
220
+ <span
221
+ class={`zdp-menu zdp-menu--${placement} zdp-menu--align-${align}`}
222
+ data-open={open ? 'true' : 'false'}
223
+ bind:this={rootElement}
224
+ >
225
+ <button
226
+ class="zdp-menu__trigger"
227
+ id={triggerId}
228
+ type="button"
229
+ aria-label={triggerLabel}
230
+ aria-haspopup="menu"
231
+ aria-expanded={open}
232
+ aria-controls={open ? panelId : undefined}
233
+ bind:this={triggerElement}
234
+ onclick={handleTriggerClick}
235
+ onkeydown={handleTriggerKeydown}
236
+ >
237
+ <span class="zdp-menu__trigger-label">
238
+ <slot name="trigger">{triggerLabel}</slot>
239
+ </span>
240
+ <span class="zdp-menu__trigger-mark" aria-hidden="true"></span>
241
+ </button>
242
+
243
+ {#if open}
244
+ <div
245
+ class="zdp-menu__panel"
246
+ id={panelId}
247
+ role="menu"
248
+ aria-labelledby={triggerId}
249
+ tabindex="-1"
250
+ bind:this={panelElement}
251
+ onkeydown={handlePanelKeydown}
252
+ >
253
+ {#each items as item (item.id)}
254
+ {#if item.separatorBefore}
255
+ <span class="zdp-menu__separator" role="separator" aria-hidden="true"></span>
256
+ {/if}
257
+
258
+ {#if item.href}
259
+ <a
260
+ class={`zdp-menu__item ${item.tone === 'danger' ? 'zdp-menu__item--danger' : ''}`}
261
+ href={item.disabled ? undefined : item.href}
262
+ target={item.target ?? undefined}
263
+ rel={resolvedRel(item)}
264
+ role="menuitem"
265
+ aria-label={item.ariaLabel ?? undefined}
266
+ aria-disabled={item.disabled ? 'true' : undefined}
267
+ tabindex={item.id === activeItemId && !item.disabled ? 0 : -1}
268
+ data-menu-item-id={item.id}
269
+ onclick={(event) => handleItemClick(event, item)}
270
+ onmouseenter={() => {
271
+ if (!item.disabled) activeItemId = item.id;
272
+ }}
273
+ >
274
+ {item.label}
275
+ </a>
276
+ {:else}
277
+ <button
278
+ class={`zdp-menu__item ${item.tone === 'danger' ? 'zdp-menu__item--danger' : ''}`}
279
+ type="button"
280
+ role="menuitem"
281
+ aria-label={item.ariaLabel ?? undefined}
282
+ disabled={item.disabled}
283
+ tabindex={item.id === activeItemId && !item.disabled ? 0 : -1}
284
+ data-menu-item-id={item.id}
285
+ onclick={(event) => handleItemClick(event, item)}
286
+ onmouseenter={() => {
287
+ if (!item.disabled) activeItemId = item.id;
288
+ }}
289
+ >
290
+ {item.label}
291
+ </button>
292
+ {/if}
293
+ {/each}
294
+ </div>
295
+ {/if}
296
+ </span>
297
+
298
+ <style>
299
+ .zdp-menu {
300
+ align-self: start;
301
+ display: inline-flex;
302
+ justify-self: start;
303
+ min-width: 0;
304
+ position: relative;
305
+ vertical-align: middle;
306
+ }
307
+
308
+ .zdp-menu__trigger {
309
+ align-items: center;
310
+ background: var(--zdp-color-surface-panel);
311
+ border: var(--zdp-control-border-width) solid var(--zdp-color-line-subtle);
312
+ border-radius: var(--zdp-control-radius);
313
+ color: var(--zdp-color-ink-normal);
314
+ cursor: pointer;
315
+ display: inline-flex;
316
+ font-family: var(--zdp-font-family-sans);
317
+ font-size: var(--zdp-type-control-size);
318
+ font-weight: var(--zdp-font-weight-medium);
319
+ gap: var(--zdp-space-2);
320
+ justify-content: center;
321
+ line-height: var(--zdp-type-control-line-height);
322
+ min-height: var(--zdp-control-height-md);
323
+ min-inline-size: 12rem;
324
+ min-width: 12rem;
325
+ padding: 0 var(--zdp-space-3);
326
+ transition:
327
+ background-color var(--zdp-motion-fast) ease,
328
+ border-color var(--zdp-motion-fast) ease,
329
+ color var(--zdp-motion-fast) ease;
330
+ -webkit-user-select: none;
331
+ user-select: none;
332
+ }
333
+
334
+ .zdp-menu__trigger:hover {
335
+ background: var(--zdp-color-surface-raised);
336
+ border-color: var(--zdp-color-line-strong);
337
+ color: var(--zdp-color-ink-strong);
338
+ }
339
+
340
+ .zdp-menu__trigger:focus-visible {
341
+ border-color: var(--zdp-color-focus-line);
342
+ outline: var(--zdp-control-focus-outline-width) solid var(--zdp-color-focus-surface);
343
+ outline-offset: var(--zdp-control-focus-outline-offset);
344
+ }
345
+
346
+ .zdp-menu__trigger-label {
347
+ min-width: 0;
348
+ overflow-wrap: var(--zdp-i18n-overflow-wrap);
349
+ }
350
+
351
+ .zdp-menu__trigger-mark {
352
+ align-items: center;
353
+ display: inline-flex;
354
+ flex: 0 0 auto;
355
+ justify-content: center;
356
+ line-height: 1;
357
+ -webkit-user-select: none;
358
+ user-select: none;
359
+ }
360
+
361
+ .zdp-menu__trigger-mark::before {
362
+ border-left: 4px solid transparent;
363
+ border-right: 4px solid transparent;
364
+ border-top: 4px solid currentColor;
365
+ content: '';
366
+ display: inline-block;
367
+ height: 0;
368
+ width: 0;
369
+ }
370
+
371
+ .zdp-menu__panel {
372
+ background: var(--zdp-color-surface-panel);
373
+ border: var(--zdp-control-border-width) solid var(--zdp-color-line-strong);
374
+ border-radius: var(--zdp-control-radius);
375
+ color: var(--zdp-color-ink-normal);
376
+ display: grid;
377
+ font-family: var(--zdp-font-family-sans);
378
+ gap: var(--zdp-space-1);
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)));
382
+ min-inline-size: 12rem;
383
+ overflow: auto;
384
+ padding: var(--zdp-space-1);
385
+ position: absolute;
386
+ z-index: 40;
387
+ }
388
+
389
+ .zdp-menu__panel:focus-visible {
390
+ border-color: var(--zdp-color-focus-line);
391
+ outline: var(--zdp-control-focus-outline-width) solid var(--zdp-color-focus-surface);
392
+ outline-offset: var(--zdp-control-focus-outline-offset);
393
+ }
394
+
395
+ .zdp-menu__item {
396
+ align-items: center;
397
+ background: transparent;
398
+ border: var(--zdp-control-border-width) solid transparent;
399
+ border-radius: var(--zdp-control-radius);
400
+ color: var(--zdp-color-ink-normal);
401
+ cursor: pointer;
402
+ display: flex;
403
+ font-family: var(--zdp-font-family-sans);
404
+ font-size: var(--zdp-type-control-size);
405
+ gap: var(--zdp-space-2);
406
+ line-height: var(--zdp-type-control-line-height);
407
+ min-height: var(--zdp-control-height-sm);
408
+ min-width: 0;
409
+ padding: var(--zdp-space-1) var(--zdp-space-2);
410
+ text-align: left;
411
+ text-decoration-line: none;
412
+ transition:
413
+ background-color var(--zdp-motion-fast) ease,
414
+ border-color var(--zdp-motion-fast) ease,
415
+ color var(--zdp-motion-fast) ease;
416
+ -webkit-user-select: none;
417
+ user-select: none;
418
+ }
419
+
420
+ .zdp-menu__item:hover:not(:disabled):not([aria-disabled="true"]),
421
+ .zdp-menu__item:focus-visible {
422
+ background: var(--zdp-color-surface-raised);
423
+ border-color: var(--zdp-color-line-strong);
424
+ color: var(--zdp-color-ink-strong);
425
+ outline: 0;
426
+ }
427
+
428
+ .zdp-menu__item--danger {
429
+ color: var(--zdp-color-accent-danger);
430
+ }
431
+
432
+ .zdp-menu__item:disabled,
433
+ .zdp-menu__item[aria-disabled="true"] {
434
+ cursor: not-allowed;
435
+ opacity: 0.56;
436
+ }
437
+
438
+ .zdp-menu__separator {
439
+ border-block-start: 1px solid var(--zdp-color-line-subtle);
440
+ display: block;
441
+ margin: var(--zdp-space-1) 0;
442
+ }
443
+
444
+ .zdp-menu--top .zdp-menu__panel {
445
+ bottom: calc(100% + var(--zdp-space-2));
446
+ }
447
+
448
+ .zdp-menu--right .zdp-menu__panel {
449
+ left: calc(100% + var(--zdp-space-2));
450
+ top: 0;
451
+ }
452
+
453
+ .zdp-menu--bottom .zdp-menu__panel {
454
+ top: calc(100% + var(--zdp-space-2));
455
+ }
456
+
457
+ .zdp-menu--left .zdp-menu__panel {
458
+ right: calc(100% + var(--zdp-space-2));
459
+ top: 0;
460
+ }
461
+
462
+ .zdp-menu--align-start .zdp-menu__panel {
463
+ left: 0;
464
+ }
465
+
466
+ .zdp-menu--align-center .zdp-menu__panel {
467
+ left: 50%;
468
+ translate: -50% 0;
469
+ }
470
+
471
+ .zdp-menu--align-end .zdp-menu__panel {
472
+ right: 0;
473
+ }
474
+
475
+ .zdp-menu--right.zdp-menu--align-end .zdp-menu__panel,
476
+ .zdp-menu--left.zdp-menu--align-end .zdp-menu__panel {
477
+ bottom: 0;
478
+ top: auto;
479
+ }
480
+
481
+ .zdp-menu--right.zdp-menu--align-center .zdp-menu__panel,
482
+ .zdp-menu--left.zdp-menu--align-center .zdp-menu__panel {
483
+ top: 50%;
484
+ translate: 0 -50%;
485
+ }
486
+
487
+ .zdp-menu--bottom .zdp-menu__panel,
488
+ .zdp-menu--top .zdp-menu__panel {
489
+ inline-size: 100%;
490
+ }
491
+ </style>
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ export let as: 'main' | 'div' = 'main';
3
+ export let tone: 'canvas' | 'panel' = 'canvas';
4
+ export let id: string | null = null;
5
+ export let labelledBy: string | null = null;
6
+
7
+ $: labelledRegionRole = as === 'div' && labelledBy ? 'region' : undefined;
8
+ </script>
9
+
10
+ <svelte:element
11
+ this={as}
12
+ class={`zdp-page zdp-page--${tone} zdp-surface-reset`}
13
+ {id}
14
+ role={labelledRegionRole}
15
+ aria-labelledby={labelledBy ?? undefined}
16
+ >
17
+ <slot />
18
+ </svelte:element>
19
+
20
+ <style>
21
+ .zdp-page {
22
+ background: var(--zdp-color-surface-canvas);
23
+ color: var(--zdp-color-ink-normal);
24
+ display: grid;
25
+ min-block-size: 100%;
26
+ min-width: 0;
27
+ }
28
+
29
+ .zdp-page--canvas {
30
+ background: var(--zdp-color-surface-canvas);
31
+ }
32
+
33
+ .zdp-page--panel {
34
+ background: var(--zdp-color-surface-panel);
35
+ }
36
+ </style>
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ export let as: 'header' | 'div' = 'header';
3
+ export let align: 'start' | 'center' = 'start';
4
+ export let id: string | null = null;
5
+ export let labelledBy: string | null = null;
6
+
7
+ $: labelledHeaderRole = labelledBy ? 'group' : undefined;
8
+ </script>
9
+
10
+ <svelte:element
11
+ this={as}
12
+ class={`zdp-page-header zdp-page-header--align-${align}`}
13
+ {id}
14
+ role={labelledHeaderRole}
15
+ aria-labelledby={labelledBy ?? undefined}
16
+ >
17
+ <div class="zdp-page-header__body">
18
+ <slot name="eyebrow" />
19
+ <div class="zdp-page-header__title">
20
+ <slot />
21
+ </div>
22
+ <div class="zdp-page-header__summary">
23
+ <slot name="summary" />
24
+ </div>
25
+ </div>
26
+ <div class="zdp-page-header__actions">
27
+ <slot name="actions" />
28
+ </div>
29
+ </svelte:element>
30
+
31
+ <style>
32
+ .zdp-page-header {
33
+ align-items: start;
34
+ display: grid;
35
+ gap: var(--zdp-space-4);
36
+ grid-template-columns: minmax(0, 1fr) auto;
37
+ min-width: 0;
38
+ }
39
+
40
+ .zdp-page-header--align-center {
41
+ align-items: center;
42
+ }
43
+
44
+ .zdp-page-header__body {
45
+ display: grid;
46
+ gap: var(--zdp-space-2);
47
+ min-width: 0;
48
+ }
49
+
50
+ .zdp-page-header__title {
51
+ color: var(--zdp-color-ink-strong);
52
+ font-family: var(--zdp-font-family-display);
53
+ line-height: var(--zdp-type-title-line-height);
54
+ min-width: 0;
55
+ }
56
+
57
+ .zdp-page-header__title :global(:where(h1, h2, h3)) {
58
+ color: inherit;
59
+ font: inherit;
60
+ margin: 0;
61
+ }
62
+
63
+ .zdp-page-header__summary {
64
+ color: var(--zdp-color-ink-muted);
65
+ font-size: var(--zdp-type-body-size);
66
+ line-height: var(--zdp-type-body-line-height);
67
+ max-inline-size: 42rem;
68
+ min-width: 0;
69
+ }
70
+
71
+ .zdp-page-header__summary :global(:where(p)) {
72
+ margin: 0;
73
+ }
74
+
75
+ .zdp-page-header__actions {
76
+ align-items: center;
77
+ display: flex;
78
+ flex-wrap: wrap;
79
+ gap: var(--zdp-space-2);
80
+ justify-content: flex-end;
81
+ min-width: 0;
82
+ }
83
+
84
+ @media (max-width: 48rem) {
85
+ .zdp-page-header {
86
+ grid-template-columns: 1fr;
87
+ }
88
+
89
+ .zdp-page-header__actions {
90
+ justify-content: flex-start;
91
+ }
92
+ }
93
+ </style>