srcdev-nuxt-components 9.3.2 → 9.3.3

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.
@@ -14,9 +14,9 @@ open/close state management.
14
14
  |---|---|---|---|
15
15
  | `dataDialogId` | `string` | — | **Required.** Unique identifier rendered as `data-dialog-id` attribute. |
16
16
  | `variant` | `'dialog' \| 'modal' \| 'confirm' \| 'alert' \| 'fullscreen'` | `'dialog'` | Controls panel sizing and ARIA role. |
17
- | `theme` | `SemanticTheme` | `undefined` | Optional header accent — `data-theme` on `.header` only; adds a coloured bottom border and tints the close icon. |
17
+ | `theme` | `SemanticTheme` | `undefined` | Optional header accent — `data-theme` on `.display-dialog-header` only; adds a coloured bottom border and tints the close icon. |
18
18
  | `v-model` | `boolean` | — | Controls open/closed state. |
19
- | `allowContentScroll` | `boolean` | `false` | Enables independent scroll on `.dialog-content`. |
19
+ | `allowContentScroll` | `boolean` | `false` | Enables independent scroll on `.display-dialog-content`. |
20
20
  | `lockViewport` | `boolean` | `true` | Adds/removes `lock` class on `<body>` on mount/close. |
21
21
  | `justifyDialog` | `'start' \| 'center' \| 'end'` | `'center'` | Horizontal position of panel within overlay. |
22
22
  | `alignDialog` | `'start' \| 'center' \| 'end'` | `'center'` | Vertical position of panel within overlay. |
@@ -27,12 +27,12 @@ open/close state management.
27
27
 
28
28
  | Slot | Renders in |
29
29
  |---|---|
30
- | `#dialogTitle` | `.col-left` in the header (hidden when slot is empty) |
31
- | `#dialogContent` | `.dialog-content` (hidden when slot is empty) |
32
- | `#actionButtonLeft` | Left side of `.footer` |
33
- | `#actionButtonRight` | Right side of `.footer` |
30
+ | `#dialogTitle` | `.display-dialog-col-left` in the header (hidden when slot is empty) |
31
+ | `#dialogContent` | `.display-dialog-content` (hidden when slot is empty) |
32
+ | `#actionButtonLeft` | Left side of `.display-dialog-footer` |
33
+ | `#actionButtonRight` | Right side of `.display-dialog-footer` |
34
34
 
35
- `.footer` is omitted entirely when neither action button slot is provided.
35
+ `.display-dialog-footer` is omitted entirely when neither action button slot is provided.
36
36
 
37
37
  ## Variants
38
38
 
@@ -1,5 +1,17 @@
1
1
  # DisplayDialog — Consumer Styling Guide
2
2
 
3
+ ## Structural class names (2026-08-22)
4
+
5
+ `DisplayDialog` renders `<dialog>` inline in the component tree, not via `<Teleport>` — so it's a
6
+ genuine descendant of wherever it's used in the page, and any ancestor's CSS selectors reach it.
7
+ Before 2026-08-22 its internal sections used bare generic class names (`.inner`, `.header`,
8
+ `.footer`, `.col-left`, `.col-right`, `.dialog-content`) — a consumer app with its own `.header`/
9
+ `.footer` layout classes (extremely common names) had its unlayered site CSS silently override the
10
+ dialog's own footer layout (button alignment, padding) purely by class-name collision, since
11
+ unlayered CSS always beats `@layer components` regardless of specificity. All six renamed to
12
+ `.display-dialog-*` prefixed variants (`.display-dialog-inner`, `.display-dialog-header`, etc.) —
13
+ update any consumer overrides written against the old names.
14
+
3
15
  ## Public token API
4
16
 
5
17
  All `--display-dialog-*` tokens are the stable override surface. Because dialogs are site-wide UI
@@ -75,11 +87,11 @@ on `:root`. This applies to every `DisplayDialog` across the site.
75
87
 
76
88
  The `fullscreen` variant bypasses `--_inner-border-radius`, `--_inner-border`, and
77
89
  `--_inner-outline` directly (setting them to `0`/`none`). To restyle the fullscreen panel,
78
- target the private tokens on `.inner.fullscreen`:
90
+ target the private tokens on `.display-dialog-inner.fullscreen`:
79
91
 
80
92
  ```css
81
93
  .display-dialog {
82
- .inner {
94
+ .display-dialog-inner {
83
95
  &.confirm {
84
96
  /* e.g. constrain confirm panel width further */
85
97
  max-width: 40rem;
@@ -101,19 +113,19 @@ target the private tokens on `.inner.fullscreen`:
101
113
 
102
114
  ## Section targeting
103
115
 
104
- Target `.header`, `.dialog-content`, and `.footer` directly to adjust layout within the panel:
116
+ Target `.display-dialog-header`, `.display-dialog-content`, and `.display-dialog-footer` directly to adjust layout within the panel:
105
117
 
106
118
  ```css
107
119
  .display-dialog {
108
- .header {
120
+ .display-dialog-header {
109
121
  border-bottom: 0.1rem solid var(--brand-border);
110
122
  }
111
123
 
112
- .dialog-content {
124
+ .display-dialog-content {
113
125
  /* content area uses --display-dialog-content-padding */
114
126
  }
115
127
 
116
- .footer {
128
+ .display-dialog-footer {
117
129
  border-top: 0.1rem solid var(--brand-border);
118
130
  justify-content: space-between; /* override default flex-end */
119
131
  }
@@ -134,7 +146,7 @@ you can scope overrides to a specific page without affecting the rest of the sit
134
146
  --display-dialog-inner-border-radius: 0;
135
147
  --display-dialog-backdrop-background: rgba(0, 0, 0, 0.8);
136
148
 
137
- .footer {
149
+ .display-dialog-footer {
138
150
  justify-content: stretch;
139
151
  }
140
152
  }
@@ -156,7 +168,7 @@ needs a different look, pass a modifier class and target it alongside `.display-
156
168
  .display-dialog.danger-dialog {
157
169
  --display-dialog-backdrop-background: rgba(180, 0, 0, 0.4);
158
170
 
159
- .inner {
171
+ .display-dialog-inner {
160
172
  --display-dialog-inner-border: 0.2rem solid var(--color-danger);
161
173
  }
162
174
  }
@@ -16,13 +16,13 @@
16
16
  :click-outside-deactivates="!isAlert"
17
17
  @deactivate="closeDialog()"
18
18
  >
19
- <div class="inner" :class="[resolved.variant]">
20
- <div class="header" :data-theme="resolved.theme">
21
- <div v-if="hasTitle" :id="dialogTitleId" class="col-left">
19
+ <div class="display-dialog-inner" :class="[resolved.variant]">
20
+ <div class="display-dialog-header" :data-theme="resolved.theme">
21
+ <div v-if="hasTitle" :id="dialogTitleId" class="display-dialog-col-left">
22
22
  <slot name="dialogTitle"></slot>
23
23
  </div>
24
24
 
25
- <div class="col-right">
25
+ <div class="display-dialog-col-right">
26
26
  <button
27
27
  data-test-id="display-dialog-header-close"
28
28
  class="display-dialog-close"
@@ -35,13 +35,13 @@
35
35
  </div>
36
36
  <div
37
37
  v-if="hasContent"
38
- class="dialog-content"
38
+ class="display-dialog-content"
39
39
  :class="[{ 'allow-content-scroll': resolved.allowContentScroll }]"
40
40
  :tabindex="resolved.allowContentScroll ? 0 : undefined"
41
41
  >
42
42
  <slot name="dialogContent"></slot>
43
43
  </div>
44
- <div v-if="hasFooter" class="footer">
44
+ <div v-if="hasFooter" class="display-dialog-footer">
45
45
  <slot name="actionButtonLeft"></slot>
46
46
  <slot name="actionButtonRight"></slot>
47
47
  </div>
@@ -216,7 +216,7 @@ onUnmounted(() => {
216
216
  align-items: flex-end;
217
217
  }
218
218
 
219
- .inner {
219
+ .display-dialog-inner {
220
220
  display: grid;
221
221
  grid-template-rows: auto 1fr auto;
222
222
  border-radius: var(--_inner-border-radius);
@@ -232,7 +232,11 @@ onUnmounted(() => {
232
232
  }
233
233
 
234
234
  &.confirm {
235
- width: initial;
235
+ /* Matches .alert's cap. Plain `width: initial` (== auto) makes this a shrink-to-fit flex
236
+ item with no wrapping constraint, so a long confirm sentence renders as one unbroken
237
+ line and drags the whole dialog (and its footer buttons) out to that width instead of
238
+ wrapping normally — visible once a real confirm message runs more than a few words. */
239
+ width: min(90%, 48rem);
236
240
  }
237
241
 
238
242
  &.dialog {
@@ -252,7 +256,7 @@ onUnmounted(() => {
252
256
  width: initial;
253
257
  }
254
258
 
255
- .header {
259
+ .display-dialog-header {
256
260
  display: flex;
257
261
  align-items: center;
258
262
  padding: var(--_header-padding);
@@ -265,11 +269,11 @@ onUnmounted(() => {
265
269
  }
266
270
  }
267
271
 
268
- .col-left {
272
+ .display-dialog-col-left {
269
273
  flex: 1;
270
274
  }
271
275
 
272
- .col-right {
276
+ .display-dialog-col-right {
273
277
  margin-inline-start: auto;
274
278
 
275
279
  .display-dialog-close {
@@ -303,7 +307,7 @@ onUnmounted(() => {
303
307
  }
304
308
  }
305
309
 
306
- .dialog-content {
310
+ .display-dialog-content {
307
311
  overflow: hidden;
308
312
  padding: var(--_content-padding);
309
313
 
@@ -315,7 +319,7 @@ onUnmounted(() => {
315
319
  }
316
320
  }
317
321
 
318
- .footer {
322
+ .display-dialog-footer {
319
323
  display: flex;
320
324
  gap: var(--_footer-gap);
321
325
  justify-content: flex-end;
@@ -116,81 +116,81 @@ describe("DisplayDialog", () => {
116
116
  expect(wrapper.attributes("role")).toBe("alertdialog");
117
117
  });
118
118
 
119
- // ─── Variant class on .inner ──────────────────────────────────────────────
119
+ // ─── Variant class on .display-dialog-inner ──────────────────────────────────────────────
120
120
 
121
- it("applies 'dialog' class to .inner by default", async () => {
121
+ it("applies 'dialog' class to .display-dialog-inner by default", async () => {
122
122
  const wrapper = await mountSuspended(DisplayDialog, {
123
123
  props: { dataDialogId: "test" },
124
124
  });
125
- expect(wrapper.find(".inner").classes()).toContain("dialog");
125
+ expect(wrapper.find(".display-dialog-inner").classes()).toContain("dialog");
126
126
  });
127
127
 
128
128
  it.each(["dialog", "modal", "confirm", "alert", "fullscreen"] as const)(
129
- "applies '%s' class to .inner",
129
+ "applies '%s' class to .display-dialog-inner",
130
130
  async (variant) => {
131
131
  const wrapper = await mountSuspended(DisplayDialog, {
132
132
  props: { dataDialogId: "test", variant },
133
133
  });
134
- expect(wrapper.find(".inner").classes()).toContain(variant);
134
+ expect(wrapper.find(".display-dialog-inner").classes()).toContain(variant);
135
135
  }
136
136
  );
137
137
 
138
138
  // ─── Slots ────────────────────────────────────────────────────────────────
139
139
 
140
- it("renders dialogTitle slot in .col-left", async () => {
140
+ it("renders dialogTitle slot in .display-dialog-col-left", async () => {
141
141
  const wrapper = await mountSuspended(DisplayDialog, {
142
142
  props: { dataDialogId: "test" },
143
143
  slots: { dialogTitle: "<p>My Title</p>" },
144
144
  });
145
- expect(wrapper.find(".col-left").text()).toBe("My Title");
145
+ expect(wrapper.find(".display-dialog-col-left").text()).toBe("My Title");
146
146
  });
147
147
 
148
- it("omits .col-left when dialogTitle slot is not provided", async () => {
148
+ it("omits .display-dialog-col-left when dialogTitle slot is not provided", async () => {
149
149
  const wrapper = await mountSuspended(DisplayDialog, {
150
150
  props: { dataDialogId: "test" },
151
151
  });
152
- expect(wrapper.find(".col-left").exists()).toBe(false);
152
+ expect(wrapper.find(".display-dialog-col-left").exists()).toBe(false);
153
153
  });
154
154
 
155
- it("renders dialogContent slot in .dialog-content", async () => {
155
+ it("renders dialogContent slot in .display-dialog-content", async () => {
156
156
  const wrapper = await mountSuspended(DisplayDialog, {
157
157
  props: { dataDialogId: "test" },
158
158
  slots: { dialogContent: "<p>Content</p>" },
159
159
  });
160
- expect(wrapper.find(".dialog-content").text()).toBe("Content");
160
+ expect(wrapper.find(".display-dialog-content").text()).toBe("Content");
161
161
  });
162
162
 
163
- it("omits .dialog-content when dialogContent slot is not provided", async () => {
163
+ it("omits .display-dialog-content when dialogContent slot is not provided", async () => {
164
164
  const wrapper = await mountSuspended(DisplayDialog, {
165
165
  props: { dataDialogId: "test" },
166
166
  });
167
- expect(wrapper.find(".dialog-content").exists()).toBe(false);
167
+ expect(wrapper.find(".display-dialog-content").exists()).toBe(false);
168
168
  });
169
169
 
170
- it("renders .footer when actionButtonLeft slot is provided", async () => {
170
+ it("renders .display-dialog-footer when actionButtonLeft slot is provided", async () => {
171
171
  const wrapper = await mountSuspended(DisplayDialog, {
172
172
  props: { dataDialogId: "test" },
173
173
  slots: { actionButtonLeft: "<button>Cancel</button>" },
174
174
  });
175
- expect(wrapper.find(".footer").exists()).toBe(true);
175
+ expect(wrapper.find(".display-dialog-footer").exists()).toBe(true);
176
176
  });
177
177
 
178
- it("renders .footer when actionButtonRight slot is provided", async () => {
178
+ it("renders .display-dialog-footer when actionButtonRight slot is provided", async () => {
179
179
  const wrapper = await mountSuspended(DisplayDialog, {
180
180
  props: { dataDialogId: "test" },
181
181
  slots: { actionButtonRight: "<button>Confirm</button>" },
182
182
  });
183
- expect(wrapper.find(".footer").exists()).toBe(true);
183
+ expect(wrapper.find(".display-dialog-footer").exists()).toBe(true);
184
184
  });
185
185
 
186
- it("omits .footer when neither action button slot is provided", async () => {
186
+ it("omits .display-dialog-footer when neither action button slot is provided", async () => {
187
187
  const wrapper = await mountSuspended(DisplayDialog, {
188
188
  props: { dataDialogId: "test" },
189
189
  });
190
- expect(wrapper.find(".footer").exists()).toBe(false);
190
+ expect(wrapper.find(".display-dialog-footer").exists()).toBe(false);
191
191
  });
192
192
 
193
- it("renders both action button slots inside .footer", async () => {
193
+ it("renders both action button slots inside .display-dialog-footer", async () => {
194
194
  const wrapper = await mountSuspended(DisplayDialog, {
195
195
  props: { dataDialogId: "test" },
196
196
  slots: {
@@ -198,8 +198,8 @@ describe("DisplayDialog", () => {
198
198
  actionButtonRight: "<button>Confirm</button>",
199
199
  },
200
200
  });
201
- expect(wrapper.find(".footer").text()).toContain("Cancel");
202
- expect(wrapper.find(".footer").text()).toContain("Confirm");
201
+ expect(wrapper.find(".display-dialog-footer").text()).toContain("Cancel");
202
+ expect(wrapper.find(".display-dialog-footer").text()).toContain("Confirm");
203
203
  });
204
204
 
205
205
  // ─── allowContentScroll ───────────────────────────────────────────────────
@@ -209,7 +209,7 @@ describe("DisplayDialog", () => {
209
209
  props: { dataDialogId: "test" },
210
210
  slots: { dialogContent: "<p>Content</p>" },
211
211
  });
212
- expect(wrapper.find(".dialog-content").classes()).not.toContain("allow-content-scroll");
212
+ expect(wrapper.find(".display-dialog-content").classes()).not.toContain("allow-content-scroll");
213
213
  });
214
214
 
215
215
  it("applies allow-content-scroll class when allowContentScroll=true", async () => {
@@ -217,7 +217,7 @@ describe("DisplayDialog", () => {
217
217
  props: { dataDialogId: "test", allowContentScroll: true },
218
218
  slots: { dialogContent: "<p>Content</p>" },
219
219
  });
220
- expect(wrapper.find(".dialog-content").classes()).toContain("allow-content-scroll");
220
+ expect(wrapper.find(".display-dialog-content").classes()).toContain("allow-content-scroll");
221
221
  });
222
222
 
223
223
  // ─── Close button ─────────────────────────────────────────────────────────
@@ -312,12 +312,12 @@ describe("DisplayDialog", () => {
312
312
  props: { dataDialogId: "test" },
313
313
  slots: { dialogContent: "<p>Content</p>" },
314
314
  });
315
- expect(wrapper.find(".inner").classes()).toContain("dialog");
315
+ expect(wrapper.find(".display-dialog-inner").classes()).toContain("dialog");
316
316
  expect(wrapper.attributes("align-dialog")).toBe("center");
317
317
  expect(wrapper.attributes("justify-dialog")).toBe("center");
318
318
  expect(document.body.classList.contains("lock")).toBe(true);
319
- expect(wrapper.find(".dialog-content").classes()).not.toContain("allow-content-scroll");
320
- expect(wrapper.find(".header").attributes("data-theme")).toBeUndefined();
319
+ expect(wrapper.find(".display-dialog-content").classes()).not.toContain("allow-content-scroll");
320
+ expect(wrapper.find(".display-dialog-header").attributes("data-theme")).toBeUndefined();
321
321
  expect(iconName(wrapper)).toContain("bitcoin-icons:cross-filled");
322
322
  });
323
323
 
@@ -340,12 +340,12 @@ describe("DisplayDialog", () => {
340
340
  props: { dataDialogId: "test" },
341
341
  slots: { dialogContent: "<p>Content</p>" },
342
342
  });
343
- expect(wrapper.find(".inner").classes()).toContain("modal");
343
+ expect(wrapper.find(".display-dialog-inner").classes()).toContain("modal");
344
344
  expect(wrapper.attributes("align-dialog")).toBe("end");
345
345
  expect(wrapper.attributes("justify-dialog")).toBe("start");
346
346
  expect(document.body.classList.contains("lock")).toBe(false);
347
- expect(wrapper.find(".dialog-content").classes()).toContain("allow-content-scroll");
348
- expect(wrapper.find(".header").attributes("data-theme")).toBe("info");
347
+ expect(wrapper.find(".display-dialog-content").classes()).toContain("allow-content-scroll");
348
+ expect(wrapper.find(".display-dialog-header").attributes("data-theme")).toBe("info");
349
349
  expect(iconName(wrapper)).toContain("heroicons:x-mark");
350
350
  });
351
351
 
@@ -377,12 +377,12 @@ describe("DisplayDialog", () => {
377
377
  },
378
378
  slots: { dialogContent: "<p>Content</p>" },
379
379
  });
380
- expect(wrapper.find(".inner").classes()).toContain("fullscreen");
380
+ expect(wrapper.find(".display-dialog-inner").classes()).toContain("fullscreen");
381
381
  expect(wrapper.attributes("align-dialog")).toBe("start");
382
382
  expect(wrapper.attributes("justify-dialog")).toBe("end");
383
383
  expect(document.body.classList.contains("lock")).toBe(true);
384
- expect(wrapper.find(".dialog-content").classes()).not.toContain("allow-content-scroll");
385
- expect(wrapper.find(".header").attributes("data-theme")).toBe("error");
384
+ expect(wrapper.find(".display-dialog-content").classes()).not.toContain("allow-content-scroll");
385
+ expect(wrapper.find(".display-dialog-header").attributes("data-theme")).toBe("error");
386
386
  expect(iconName(wrapper)).toContain("material-symbols:close");
387
387
  });
388
388
 
@@ -2,17 +2,17 @@
2
2
 
3
3
  exports[`DisplayDialog > renders correct HTML structure with all slots 1`] = `
4
4
  "<dialog class="display-dialog" aria-modal="true" aria-labelledby="v-0-0" align-dialog="center" justify-dialog="center" open="" data-dialog-id="test">
5
- <div class="inner dialog">
6
- <div class="header">
7
- <div id="v-0-0" class="col-left">
5
+ <div class="display-dialog-inner dialog">
6
+ <div class="display-dialog-header">
7
+ <div id="v-0-0" class="display-dialog-col-left">
8
8
  <p>Title</p>
9
9
  </div>
10
- <div class="col-right"><button data-test-id="display-dialog-header-close" class="display-dialog-close"><span class="iconify undefinedbitcoin-icons:cross-filled icon"></span><span class="sr-only">Close</span></button></div>
10
+ <div class="display-dialog-col-right"><button data-test-id="display-dialog-header-close" class="display-dialog-close"><span class="iconify undefinedbitcoin-icons:cross-filled icon"></span><span class="sr-only">Close</span></button></div>
11
11
  </div>
12
- <div class="dialog-content">
12
+ <div class="display-dialog-content">
13
13
  <p>Content</p>
14
14
  </div>
15
- <div class="footer"><button>Cancel</button><button>Confirm</button></div>
15
+ <div class="display-dialog-footer"><button>Cancel</button><button>Confirm</button></div>
16
16
  </div>
17
17
  </dialog>"
18
18
  `;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.3.2",
4
+ "version": "9.3.3",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",