@placeholderco/placeholder-ui 2.2.0 → 2.2.2

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/dist/app.css CHANGED
@@ -46,16 +46,6 @@
46
46
  --ui-link-color: var(--ui-tertiary-dark);
47
47
  --ui-link-hover-color: var(--ui-accent-dark);
48
48
 
49
- /* ---- Legacy aliases (deprecated, kept for backwards compatibility) ---- */
50
- --midnight-rgb: var(--ui-primary-rgb);
51
- --midnight-rgbc: var(--ui-primary-rgbc);
52
- --midnight: var(--ui-primary);
53
- --pistachio-rgb: var(--ui-accent-rgb);
54
- --pistachio-rgbc: var(--ui-accent-rgbc);
55
- --pistachio: var(--ui-accent);
56
- --pistachio-dark: var(--ui-accent-dark);
57
- --turquoise: var(--ui-tertiary);
58
- --turquoise-dark: var(--ui-tertiary-dark);
59
49
 
60
50
  /* ---- Surfaces and text (light mode) ---- */
61
51
  --off-white: rgb(221, 221, 221);
@@ -748,7 +748,7 @@
748
748
  line-height: 1.5;
749
749
  }
750
750
 
751
- /* Default variant - use pistachio theme */
751
+ /* Default variant - accent tint */
752
752
  .editor-content :global(.ProseMirror .alert-default) {
753
753
  background: rgba(var(--ui-accent-rgbc), 0.3);
754
754
  border: 1px solid var(--ui-accent);
@@ -72,7 +72,7 @@
72
72
  cursor: not-allowed;
73
73
  }
74
74
 
75
- .toolbar-button :global(.cu-icon) {
75
+ .toolbar-button :global(.pui-icon) {
76
76
  height: 19px;
77
77
  }
78
78
  </style>
@@ -316,22 +316,6 @@
316
316
  }}>{value.label}</Badge
317
317
  >
318
318
  {/if}
319
- <!-- <span class="inline-flex items-center bg-midnight text-white rounded-md px-2">
320
- {value.label}
321
- <button
322
- class="leading-0 ml-1 size-4 rounded hover:bg-neutral-400/25"
323
- {disabled}
324
- onclick={(e) => {
325
- if (disabled) return;
326
-
327
- value.selected = !value.selected;
328
- onSelection(value);
329
- open = false;
330
- e.stopPropagation();
331
- }}
332
-
333
- </button>
334
- </span> -->
335
319
  {/each}
336
320
  </div>
337
321
  {/if}
@@ -29,24 +29,24 @@
29
29
  <span
30
30
  style="--svg-stroke: {stroke}; --svg-fill: {fill}; {sizeStyle}"
31
31
  class:flip
32
- class="cu-icon {classes}"
32
+ class="pui-icon {classes}"
33
33
  >
34
34
  {@html svg}
35
35
  </span>
36
36
 
37
37
  <style>
38
- .cu-icon {
38
+ .pui-icon {
39
39
  display: inline-flex;
40
40
  align-items: center;
41
41
  justify-content: center;
42
42
  }
43
43
 
44
- .cu-icon :global(svg) {
44
+ .pui-icon :global(svg) {
45
45
  width: 100%;
46
46
  height: 100%;
47
47
  }
48
48
 
49
- .cu-icon :global(svg:not(.icons-tabler-filled)) {
49
+ .pui-icon :global(svg:not(.icons-tabler-filled)) {
50
50
  stroke: var(--svg-stroke);
51
51
  fill: var(--svg-fill);
52
52
  }
package/dist/index.d.ts CHANGED
@@ -30,7 +30,7 @@ export { default as ContextMenu } from './ui/ContextMenu.svelte';
30
30
  export type { ContextMenuProps, ContextMenuItem } from './ui/ContextMenu.svelte';
31
31
  export { default as ProgressBar } from './ui/ProgressBar.svelte';
32
32
  export type { ProgressBarProps, ProgressBarSegment } from './ui/ProgressBar.svelte';
33
- export type { ProgressBarVariant, LegacyProgressBarVariant } from './ui/ProgressBarVariant.js';
33
+ export type { ProgressBarVariant } from './ui/ProgressBarVariant.js';
34
34
  export { default as Tabs } from './ui/Tabs.svelte';
35
35
  export type { TabsProps } from './ui/Tabs.svelte';
36
36
  export { default as Table } from './ui/Table.svelte';
@@ -123,7 +123,7 @@ export { default as GitCard } from './cards/GitCard.svelte';
123
123
  export type { GitCardProps } from './cards/GitCard.svelte';
124
124
  export * from './icon/index.js';
125
125
  export type { IconProps } from './icon/Icon.svelte';
126
- export type { ButtonVariant, LegacyButtonVariant } from './ui/ButtonVariant.js';
126
+ export type { ButtonVariant } from './ui/ButtonVariant.js';
127
127
  export type { ButtonSize } from './ui/ButtonSize.js';
128
128
  export type { Hyperlink } from './models/Hyperlink.js';
129
129
  export type { LinkCollectionItem } from './models/LinkCollectionItem.js';
@@ -12,8 +12,7 @@
12
12
  import Logo from '../ui/Logo.svelte';
13
13
  import type { Snippet } from 'svelte';
14
14
 
15
- /** `midnight` is a legacy alias for `primary`. */
16
- export type NavbarVariant = 'default' | 'primary' | 'midnight';
15
+ export type NavbarVariant = 'default' | 'primary';
17
16
 
18
17
  export interface NavbarProps {
19
18
  /** Visual variant. `primary` uses the primary brand colour as the background across all themes */
@@ -67,7 +66,7 @@
67
66
  onDrawerButtonClick = undefined
68
67
  }: NavbarProps = $props();
69
68
 
70
- const isPrimary = $derived(variant === 'primary' || variant === 'midnight');
69
+ const isPrimary = $derived(variant === 'primary');
71
70
  </script>
72
71
 
73
72
  <header class={isPrimary ? 'primary' : ''}>
@@ -254,7 +253,7 @@
254
253
  height: 1.5rem;
255
254
  }
256
255
 
257
- .inner-navbar :global(.cu-icon) {
256
+ .inner-navbar :global(.pui-icon) {
258
257
  display: flex;
259
258
  }
260
259
 
@@ -1,8 +1,7 @@
1
1
  import type { NavbarItem } from '../models/NavbarItem.js';
2
2
  import { type Hyperlink } from '../index.js';
3
3
  import type { Snippet } from 'svelte';
4
- /** `midnight` is a legacy alias for `primary`. */
5
- export type NavbarVariant = 'default' | 'primary' | 'midnight';
4
+ export type NavbarVariant = 'default' | 'primary';
6
5
  export interface NavbarProps {
7
6
  /** Visual variant. `primary` uses the primary brand colour as the background across all themes */
8
7
  variant?: NavbarVariant;
@@ -219,52 +219,44 @@
219
219
  --accordion-open-color: var(--danger-text);
220
220
  }
221
221
 
222
- .variant-accent,
223
- .variant-pistachio {
222
+ .variant-accent {
224
223
  --accordion-open-bg: var(--ui-accent);
225
224
  --accordion-open-color: var(--ui-primary);
226
225
  }
227
226
 
228
- .variant-accent-subtle,
229
- .variant-pistachio-subtle {
227
+ .variant-accent-subtle {
230
228
  --accordion-open-bg: rgba(var(--ui-accent-rgbc), 0.15);
231
229
  --accordion-open-color: var(--text-color);
232
230
  }
233
231
 
234
- .variant-accent-outline,
235
- .variant-pistachio-outline {
232
+ .variant-accent-outline {
236
233
  --accordion-open-bg: transparent;
237
234
  --accordion-open-color: var(--ui-accent);
238
235
  --accordion-open-border-color: var(--ui-accent);
239
236
  }
240
237
 
241
- .variant-primary,
242
- .variant-midnight {
238
+ .variant-primary {
243
239
  --accordion-open-bg: var(--ui-primary);
244
240
  --accordion-open-color: white;
245
241
  }
246
242
 
247
- .variant-primary-subtle,
248
- .variant-midnight-subtle {
243
+ .variant-primary-subtle {
249
244
  --accordion-open-bg: rgba(var(--ui-primary-rgbc), 0.1);
250
245
  --accordion-open-color: var(--text-color);
251
246
  }
252
247
 
253
- .variant-primary-outline,
254
- .variant-midnight-outline {
248
+ .variant-primary-outline {
255
249
  --accordion-open-bg: transparent;
256
250
  --accordion-open-color: var(--ui-primary);
257
251
  --accordion-open-border-color: var(--ui-primary);
258
252
  }
259
253
 
260
- .variant-tertiary,
261
- .variant-turquoise {
254
+ .variant-tertiary {
262
255
  --accordion-open-bg: var(--ui-tertiary);
263
256
  --accordion-open-color: var(--ui-primary);
264
257
  }
265
258
 
266
- .variant-tertiary-subtle,
267
- .variant-turquoise-subtle {
259
+ .variant-tertiary-subtle {
268
260
  --accordion-open-bg: rgba(var(--ui-tertiary-dark-rgbc), 0.25);
269
261
  --accordion-open-color: var(--ui-tertiary-dark);
270
262
  }
@@ -122,8 +122,7 @@
122
122
  color: var(--badge-text-color) !important;
123
123
  }
124
124
 
125
- .accent,
126
- .pistachio {
125
+ .accent {
127
126
  --badge-bg-color: var(--ui-accent);
128
127
  --badge-text-color: var(--ui-primary);
129
128
  --badge-hover-bg-color: var(--ui-tertiary);
@@ -131,13 +130,11 @@
131
130
  --badge-outline-color: var(--ui-tertiary);
132
131
  }
133
132
 
134
- .accent :global(.loader),
135
- .pistachio :global(.loader) {
133
+ .accent :global(.loader) {
136
134
  color: var(--ui-primary);
137
135
  }
138
136
 
139
- .primary,
140
- .midnight {
137
+ .primary {
141
138
  --badge-bg-color: var(--ui-primary);
142
139
  --badge-text-color: #fff;
143
140
  --badge-hover-bg-color: var(--ui-accent-dark);
@@ -145,8 +142,7 @@
145
142
  --badge-outline-color: var(--ui-accent-dark);
146
143
  }
147
144
 
148
- .primary :global(.loader),
149
- .midnight :global(.loader) {
145
+ .primary :global(.loader) {
150
146
  color: var(--ui-accent);
151
147
  }
152
148
 
@@ -213,8 +209,7 @@
213
209
  --badge-outline-color: var(--ui-tertiary);
214
210
  }
215
211
 
216
- .accent-outline,
217
- .pistachio-outline {
212
+ .accent-outline {
218
213
  --badge-text-color: var(--ui-accent);
219
214
  --badge-bg-color: initial;
220
215
  --badge-border: 1px solid var(--ui-accent);
@@ -224,8 +219,7 @@
224
219
  --badge-outline-color: var(--ui-tertiary);
225
220
  }
226
221
 
227
- .primary-outline,
228
- .midnight-outline {
222
+ .primary-outline {
229
223
  --badge-text-color: var(--ui-primary);
230
224
  --badge-bg-color: initial;
231
225
  --badge-border: 1px solid var(--ui-primary);
@@ -235,8 +229,7 @@
235
229
  --badge-outline-color: var(--ui-accent-dark);
236
230
  }
237
231
 
238
- .tertiary,
239
- .turquoise {
232
+ .tertiary {
240
233
  --badge-text-color: black;
241
234
  --badge-bg-color: var(--ui-tertiary);
242
235
  --badge-hover-bg-color: var(--ui-tertiary-dark);
@@ -217,8 +217,7 @@
217
217
  }
218
218
  }
219
219
 
220
- .accent,
221
- .pistachio {
220
+ .accent {
222
221
  --button-text-color: var(--ui-primary);
223
222
  --button-bg-color: var(--ui-accent);
224
223
  --button-hover-bg-color: var(--ui-tertiary);
@@ -226,8 +225,7 @@
226
225
  --button-outline-color: var(--ui-tertiary);
227
226
  }
228
227
 
229
- .accent-outline,
230
- .pistachio-outline {
228
+ .accent-outline {
231
229
  --button-text-color: var(--ui-accent);
232
230
  --button-bg-color: initial;
233
231
  --button-border: 1px solid var(--ui-accent);
@@ -238,8 +236,7 @@
238
236
  --button-outline-color: var(--ui-tertiary);
239
237
  }
240
238
 
241
- .accent-subtle,
242
- .pistachio-subtle {
239
+ .accent-subtle {
243
240
  --button-text-color: var(--ui-accent);
244
241
  --button-bg-color: initial;
245
242
  --button-hover-text-color: var(--ui-primary);
@@ -249,16 +246,14 @@
249
246
  --button-outline-color: var(--ui-tertiary);
250
247
  }
251
248
 
252
- .primary,
253
- .midnight {
249
+ .primary {
254
250
  --button-bg-color: var(--ui-primary);
255
251
  --button-text-color: white;
256
252
  --button-hover-bg-color: var(--ui-accent-dark);
257
253
  --button-active-bg-color: var(--ui-primary-dark);
258
254
  }
259
255
 
260
- .primary-outline,
261
- .midnight-outline {
256
+ .primary-outline {
262
257
  --button-text-color: var(--ui-primary);
263
258
  --button-bg-color: initial;
264
259
  --button-border: 1px solid var(--ui-primary);
@@ -268,8 +263,7 @@
268
263
  --button-active-border: 1px solid var(--ui-primary-dark);
269
264
  }
270
265
 
271
- .primary-subtle,
272
- .midnight-subtle {
266
+ .primary-subtle {
273
267
  --button-text-color: var(--ui-primary);
274
268
  --button-bg-color: initial;
275
269
  --button-hover-text-color: white;
@@ -278,8 +272,7 @@
278
272
  --button-active-bg-color: var(--ui-primary-dark);
279
273
  }
280
274
 
281
- .tertiary,
282
- .turquoise {
275
+ .tertiary {
283
276
  --button-text-color: var(--ui-primary);
284
277
  --button-bg-color: var(--ui-tertiary);
285
278
  --button-hover-bg-color: var(--ui-accent);
@@ -287,8 +280,7 @@
287
280
  --button-outline-color: var(--ui-accent);
288
281
  }
289
282
 
290
- .tertiary-subtle,
291
- .turquoise-subtle {
283
+ .tertiary-subtle {
292
284
  background-color: initial;
293
285
  color: var(--ui-tertiary-dark);
294
286
 
@@ -5,11 +5,5 @@
5
5
  * - `primary` → `--ui-primary` (dark brand colour)
6
6
  * - `accent` → `--ui-accent` (light brand colour)
7
7
  * - `tertiary` → `--ui-tertiary` (supporting colour)
8
- *
9
- * The `midnight`, `pistachio` and `turquoise` names are legacy aliases for
10
- * `primary`, `accent` and `tertiary` respectively. They keep working but new code
11
- * should use the semantic names.
12
8
  */
13
- export type ButtonVariant = 'white' | 'secondary' | 'secondary-subtle' | 'secondary-selected' | 'danger' | 'danger-subtle' | 'auto-subtle' | 'auto-outline' | 'primary' | 'primary-outline' | 'primary-subtle' | 'accent' | 'accent-outline' | 'accent-subtle' | 'tertiary' | 'tertiary-subtle' | 'shaded' | LegacyButtonVariant;
14
- /** @deprecated Use `primary`, `accent` and `tertiary` instead. */
15
- export type LegacyButtonVariant = 'pistachio' | 'pistachio-outline' | 'pistachio-subtle' | 'midnight' | 'midnight-outline' | 'midnight-subtle' | 'turquoise' | 'turquoise-subtle';
9
+ export type ButtonVariant = 'white' | 'secondary' | 'secondary-subtle' | 'secondary-selected' | 'danger' | 'danger-subtle' | 'auto-subtle' | 'auto-outline' | 'primary' | 'primary-outline' | 'primary-subtle' | 'accent' | 'accent-outline' | 'accent-subtle' | 'tertiary' | 'tertiary-subtle' | 'shaded';
@@ -1,3 +1,32 @@
1
+ <script lang="ts" module>
2
+ // One lock shared by every instance so a nested dialog closing does not
3
+ // hand the page its scrollbar back while the parent is still open
4
+ let lockCount = 0;
5
+ let savedOverflow = '';
6
+ let savedPaddingRight = '';
7
+
8
+ function lockBodyScroll() {
9
+ if (lockCount++ > 0) return;
10
+ const body = document.body;
11
+ savedOverflow = body.style.overflow;
12
+ savedPaddingRight = body.style.paddingRight;
13
+ // Hiding the scrollbar widens the page and shifts everything sideways,
14
+ // which reads as a flicker behind the backdrop; pad by its width instead
15
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
16
+ if (scrollbarWidth > 0) {
17
+ const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
18
+ body.style.paddingRight = `${current + scrollbarWidth}px`;
19
+ }
20
+ body.style.overflow = 'hidden';
21
+ }
22
+
23
+ function unlockBodyScroll() {
24
+ if (--lockCount > 0) return;
25
+ document.body.style.overflow = savedOverflow;
26
+ document.body.style.paddingRight = savedPaddingRight;
27
+ }
28
+ </script>
29
+
1
30
  <script lang="ts">
2
31
  import Paper from '../display/Paper.svelte';
3
32
  import { iconX } from '../icon/index.js';
@@ -34,7 +63,7 @@
34
63
  /** @deprecated Scrolling is now the default; use `allowOverflow` to opt out. */
35
64
  preventOverflow?: boolean;
36
65
  /** Size preset for the dialog width */
37
- size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
66
+ size?: 'md' | 'lg' | 'xl' | 'full';
38
67
  /** Additional CSS classes */
39
68
  class?: string;
40
69
  /** Footer content (typically action buttons) */
@@ -110,10 +139,8 @@
110
139
  // Lock body scroll while open; effects only run in the browser, so this is SSR-safe
111
140
  $effect(() => {
112
141
  if (show) {
113
- document.body.style.overflow = 'hidden';
114
- return () => {
115
- document.body.style.overflow = '';
116
- };
142
+ lockBodyScroll();
143
+ return unlockBodyScroll;
117
144
  }
118
145
  });
119
146
 
@@ -142,12 +169,12 @@
142
169
  {#snippet DialogInner()}
143
170
  {#if title}
144
171
  <div class="dialog-title">
145
- <span id={titleId} class="font-bold">{title}</span>
172
+ <span id={titleId} class="dialog-title-text">{title}</span>
146
173
  {#if allowImplicitClose}
147
174
  <ActionIcon
148
175
  onclick={() => (show = false)}
149
176
  variant="secondary-subtle"
150
- class="flex !p-[0.3rem] mr-1 !leading-0"
177
+ class="dialog-close"
151
178
  svg={iconX}
152
179
  size="1rem"
153
180
  ariaLabel="Close dialog"
@@ -182,8 +209,9 @@
182
209
  {#if noPaper}
183
210
  {@render DialogInner()}
184
211
  {:else}
185
- <div class="dialog-paper {overflowClass}">
186
- <Paper containerClass={classes}>
212
+ <div class="dialog-paper">
213
+ <!-- Dialog owns the section padding so header and footer dividers span the card -->
214
+ <Paper containerClass={classes} noPadding noGap>
187
215
  {@render DialogInner()}
188
216
  </Paper>
189
217
  </div>
@@ -192,66 +220,21 @@
192
220
  </dialog>
193
221
 
194
222
  <style>
195
- /* Animate transform, not margin the native dialog centers itself via
196
- `margin: auto`, and a margin animation with `forwards` fill would
197
- permanently override it and pin the dialog to the top of the viewport */
198
- @keyframes fadeIn {
199
- from {
200
- opacity: 0;
201
- transform: translateY(-0.5rem);
202
- }
203
- to {
204
- opacity: 1;
205
- transform: translateY(0);
206
- }
207
- }
208
- @keyframes fadeOut {
209
- from {
210
- opacity: 1;
211
- transform: translateY(0);
212
- }
213
- to {
214
- opacity: 0;
215
- transform: translateY(-0.5rem);
216
- }
217
- }
218
-
219
- @keyframes fadeInFull {
220
- from {
221
- opacity: 0;
222
- }
223
- to {
224
- opacity: 1;
225
- }
226
- }
227
- @keyframes fadeOutFull {
228
- from {
229
- opacity: 1;
230
- }
231
- to {
232
- opacity: 0;
233
- }
234
- }
235
-
236
- /* Keyframes for the backdrop pseudo-element */
237
- @keyframes backdropFadeIn {
238
- from {
239
- background: hsl(0 0% 0% / 0%);
240
- }
241
- to {
242
- background: hsl(0 0% 0% / 65%);
243
- }
244
- }
245
- @keyframes backdropFadeOut {
246
- from {
247
- background: hsl(0 0% 0% / 65%);
248
- }
249
- to {
250
- background: hsl(0 0% 0% / 0%);
251
- }
252
- }
253
-
223
+ /* Open/close is a transition driven by [open] plus @starting-style, not a
224
+ keyframe animation. Keyframes with a `forwards` fill kept a transform
225
+ applied while open, and the backdrop's static colour could paint for a
226
+ frame before its animation took over, showing as a flash on open.
227
+ Transitions have no fill state: the closed values are the base rule, the
228
+ open values live on [open], and @starting-style supplies the from-values
229
+ for the first frame after display switches on */
254
230
  dialog {
231
+ /* Everything that shapes the box lives here, not on [open]: the close
232
+ transition runs after [open] is removed, so rules scoped to it would
233
+ drop out while the dialog is still fading. That includes the browser's
234
+ own :modal positioning, which stops applying the moment close() runs */
235
+ position: fixed;
236
+ inset: 0;
237
+ flex-direction: column;
255
238
  color: var(--text-color);
256
239
  border: none;
257
240
  /* The dialog element itself is focused by showModal(); don't draw a
@@ -260,7 +243,7 @@
260
243
  background-color: transparent;
261
244
  overflow: visible;
262
245
  margin: auto;
263
- max-width: min(calc(100vw - 2rem), calc(100% - 6px));
246
+ max-width: calc(100% - 2rem);
264
247
  /* Cap to the visible viewport with equal margins; the flex chain below
265
248
  (dialog > inner > paper > body, each min-height: 0) passes this limit
266
249
  down so the body shrinks instead of spilling past the bottom edge.
@@ -269,22 +252,56 @@
269
252
  max-height: calc(100vh - 2rem);
270
253
  max-height: calc(100dvh - 2rem);
271
254
 
272
- animation: fadeOut 0.2s forwards;
255
+ opacity: 0;
256
+ /* Animate transform, not margin: the native dialog centres itself via
257
+ `margin: auto` */
258
+ transform: translateY(-0.5rem);
259
+ transition:
260
+ opacity 0.2s ease,
261
+ transform 0.2s ease,
262
+ display 0.2s allow-discrete,
263
+ overlay 0.2s allow-discrete;
264
+ }
265
+
266
+ dialog[open] {
267
+ /* Only when [open] — an unconditional author display would override
268
+ the UA's display: none on closed dialogs and make them visible */
269
+ display: flex;
270
+ opacity: 1;
271
+ /* `none`, not translateY(0): a transform of any value makes the dialog
272
+ the containing block for position: fixed descendants */
273
+ transform: none;
274
+ }
275
+
276
+ @starting-style {
277
+ dialog[open] {
278
+ opacity: 0;
279
+ transform: translateY(-0.5rem);
280
+ }
281
+ }
282
+
283
+ dialog::backdrop {
284
+ background-color: rgb(0 0 0 / 0);
273
285
  transition:
286
+ background-color 0.2s ease,
274
287
  display 0.2s allow-discrete,
275
288
  overlay 0.2s allow-discrete;
276
- &::backdrop {
277
- animation: backdropFadeOut 0.2s forwards;
289
+ }
290
+
291
+ dialog[open]::backdrop {
292
+ background-color: rgb(0 0 0 / 0.65);
293
+ }
294
+
295
+ @starting-style {
296
+ dialog[open]::backdrop {
297
+ background-color: rgb(0 0 0 / 0);
278
298
  }
279
- &[open] {
280
- /* Only when [open] — an unconditional author display would override
281
- the UA's display: none on closed dialogs and make them visible */
282
- display: flex;
283
- flex-direction: column;
284
- animation: fadeIn 0.2s forwards;
285
- &::backdrop {
286
- animation: backdropFadeIn 0.2s forwards;
287
- }
299
+ }
300
+
301
+ @media (prefers-reduced-motion: reduce) {
302
+ dialog,
303
+ dialog::backdrop {
304
+ transition-duration: 0.01ms;
288
305
  }
289
306
  }
290
307
 
@@ -294,36 +311,77 @@
294
311
  min-height: 0;
295
312
  }
296
313
 
297
- /* Paper sits inside the height chain; .paper-body already has an inline
298
- min-height: 0, so only .paper needs the shrink link */
299
- .dialog :global(.paper) {
314
+ /* Paper sits inside the height chain. Without a gap its body is a plain
315
+ block, so make it a shrinkable flex column here; the sections below rely
316
+ on that to give the body the leftover height */
317
+ .dialog :global(.paper),
318
+ .dialog :global(.paper-body) {
319
+ display: flex;
320
+ flex-direction: column;
300
321
  min-height: 0;
301
322
  }
302
323
 
324
+ /* Section padding: 1rem inset, 0.75rem vertical for the header and footer,
325
+ 1rem for the body. Dividers run edge to edge */
303
326
  .dialog-title {
304
327
  display: flex;
328
+ align-items: center;
305
329
  justify-content: space-between;
306
- margin-bottom: 0.5rem;
307
- padding-bottom: 0.25rem;
330
+ gap: 0.75rem;
331
+ padding: 0.75rem 1rem;
308
332
  border-bottom: 1px solid var(--border-color);
333
+ flex-shrink: 0;
334
+ }
335
+
336
+ .dialog-title-text {
337
+ font-size: 1.0625rem;
309
338
  font-weight: 600;
339
+ line-height: 1.4;
340
+ min-width: 0;
341
+ overflow-wrap: anywhere;
342
+ }
343
+
344
+ /* Keep the close button from setting the header height: its hit area
345
+ overhangs the text line and the right inset instead */
346
+ .dialog-title :global(button.dialog-close) {
310
347
  flex-shrink: 0;
348
+ padding: 0.25rem;
349
+ margin: -0.25rem -0.5rem -0.25rem 0;
350
+ line-height: 0;
311
351
  }
312
352
 
353
+ /* The only scroll container. Padding lives inside it so focus rings and
354
+ negative-margin rows are not clipped, and the scrollbar hugs the edge */
313
355
  .dialog-body {
314
356
  display: flex;
315
357
  flex-direction: column;
316
358
  min-width: 0;
317
359
  min-height: 0;
318
360
  flex-shrink: 1;
361
+ padding: 1rem;
319
362
  }
320
363
 
364
+ /* The body padding is the spacing; don't let the first and last elements
365
+ (or a wrapper's first and last) add their own margins on top of it */
366
+ .dialog-body > :global(:first-child),
367
+ .dialog-body > :global(:first-child > :first-child) {
368
+ margin-top: 0;
369
+ }
370
+
371
+ .dialog-body > :global(:last-child),
372
+ .dialog-body > :global(:last-child > :last-child) {
373
+ margin-bottom: 0;
374
+ }
375
+
376
+ /* Actions align right. A secondary action can sit on the left with
377
+ `margin-right: auto` */
321
378
  .dialog-footer {
322
379
  display: flex;
323
- justify-content: space-between;
324
- padding-top: 0.5rem;
325
- border-top: 1px solid var(--border-color);
380
+ align-items: center;
381
+ justify-content: flex-end;
326
382
  gap: 0.5rem;
383
+ padding: 0.75rem 1rem;
384
+ border-top: 1px solid var(--border-color);
327
385
  flex-shrink: 0;
328
386
  }
329
387
 
@@ -335,31 +393,16 @@
335
393
  overflow-y: auto;
336
394
  }
337
395
 
338
- .dialog-no-paper {
339
- padding: 1rem;
340
- }
341
-
342
396
  .dialog-paper,
343
397
  .dialog-no-paper {
344
398
  display: flex;
345
399
  flex-direction: column;
346
- box-sizing: border-box;
347
- border-radius: 0.25rem;
348
- background: #f1f3f5;
349
400
  min-height: 0;
350
401
  }
351
402
 
352
- :global(.dark .dialog-paper),
353
- :global(.dark .dialog-no-paper) {
354
- background-color: rgb(33, 34, 38);
355
- }
356
-
357
- dialog::backdrop {
358
- background-color: #0008;
359
- }
360
-
361
- .dialog.sm {
362
- width: 20rem;
403
+ .dialog-no-paper {
404
+ border-radius: 0.25rem;
405
+ background-color: var(--paper-body-bg);
363
406
  }
364
407
 
365
408
  .dialog.md {
@@ -379,11 +422,9 @@
379
422
  max-width: 100%;
380
423
  max-height: 100%;
381
424
  margin-top: auto;
382
- animation: fadeOutFull 0.2s forwards;
383
-
384
- &[open] {
385
- animation: fadeInFull 0.2s forwards;
386
- }
425
+ /* Full-screen fades in place; more specific than the @starting-style
426
+ rule above, so it wins for both the starting and the open state */
427
+ transform: none;
387
428
  }
388
429
 
389
430
  .dialog.full,
@@ -27,7 +27,7 @@ export interface DialogProps {
27
27
  /** @deprecated Scrolling is now the default; use `allowOverflow` to opt out. */
28
28
  preventOverflow?: boolean;
29
29
  /** Size preset for the dialog width */
30
- size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
30
+ size?: 'md' | 'lg' | 'xl' | 'full';
31
31
  /** Additional CSS classes */
32
32
  class?: string;
33
33
  /** Footer content (typically action buttons) */
@@ -132,20 +132,17 @@
132
132
  }
133
133
 
134
134
  /* Variant colors */
135
- .primary,
136
- .midnight {
135
+ .primary {
137
136
  background-color: var(--ui-primary);
138
137
  color: #fff;
139
138
  }
140
139
 
141
- .accent,
142
- .pistachio {
140
+ .accent {
143
141
  background-color: var(--ui-accent);
144
142
  color: var(--ui-primary);
145
143
  }
146
144
 
147
- .tertiary,
148
- .turquoise {
145
+ .tertiary {
149
146
  background-color: var(--ui-tertiary);
150
147
  color: var(--ui-primary);
151
148
  }
@@ -1,7 +1,2 @@
1
- /**
2
- * Colour variants for ProgressBar. `midnight`, `pistachio` and `turquoise` are
3
- * legacy aliases for `primary`, `accent` and `tertiary`.
4
- */
5
- export type ProgressBarVariant = 'primary' | 'accent' | 'tertiary' | 'danger' | 'secondary' | LegacyProgressBarVariant;
6
- /** @deprecated Use `primary`, `accent` and `tertiary` instead. */
7
- export type LegacyProgressBarVariant = 'midnight' | 'pistachio' | 'turquoise';
1
+ /** Colour variants for ProgressBar, named after the `--ui-*` tokens. */
2
+ export type ProgressBarVariant = 'primary' | 'accent' | 'tertiary' | 'danger' | 'secondary';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@placeholderco/placeholder-ui",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "A modern, customizable Svelte 5 UI component library with comprehensive theming support. Re-brand it with a single config object.",
5
5
  "license": "MIT",
6
6
  "author": "Placeholderco",