@inglorious/ui 8.5.0 → 9.0.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.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/src/controls/button/button.stories.js +22 -0
  3. package/src/controls/button/template.js +39 -6
  4. package/src/controls/combobox/style.css +14 -7
  5. package/src/controls/combobox/template.js +12 -2
  6. package/src/controls/file-upload/style.css +4 -0
  7. package/src/controls/rating/style.css +26 -4
  8. package/src/controls/rating/template.js +29 -12
  9. package/src/controls/switch/template.js +3 -1
  10. package/src/data-display/tooltip/style.css +22 -14
  11. package/src/examples/dashboard/primitive-content.js +6 -6
  12. package/src/examples/dashboard/style.css +11 -5
  13. package/src/feedback/dialog/dialog.stories.js +9 -0
  14. package/src/feedback/dialog/style.css +13 -12
  15. package/src/feedback/dialog/template.js +63 -18
  16. package/src/feedback/progress/style.css +47 -23
  17. package/src/feedback/progress/template.js +18 -11
  18. package/src/feedback/snackbar/style.css +13 -0
  19. package/src/feedback/snackbar/template.js +7 -0
  20. package/src/navigation/drawer/drawer.stories.js +3 -7
  21. package/src/navigation/drawer/style.css +59 -18
  22. package/src/navigation/drawer/template.js +137 -80
  23. package/src/navigation/menu/menu.stories.js +15 -0
  24. package/src/navigation/menu/style.css +19 -0
  25. package/src/navigation/menu/template.js +59 -6
  26. package/src/navigation/speed-dial/style.css +32 -5
  27. package/src/navigation/speed-dial/template.js +41 -12
  28. package/src/navigation/tabs/template.js +52 -4
  29. package/src/shared/anchor.js +11 -0
  30. package/src/shared/top-layer.js +83 -0
  31. package/src/surfaces/accordion/style.css +14 -7
  32. package/src/surfaces/accordion/template.js +27 -20
  33. package/types/controls/button.d.ts +7 -1
  34. package/types/controls/rating.d.ts +4 -0
  35. package/types/feedback/dialog.d.ts +3 -1
  36. package/types/feedback/progress.d.ts +2 -0
  37. package/types/navigation/drawer.d.ts +8 -3
  38. package/types/navigation/menu.d.ts +17 -0
  39. package/types/navigation/speed-dial.d.ts +5 -0
  40. package/types/surfaces/accordion.d.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/ui",
3
- "version": "8.5.0",
3
+ "version": "9.0.0",
4
4
  "description": "A design system for Inglorious Web with render-function components and customizable themes",
5
5
  "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -57,7 +57,19 @@ export default {
57
57
  options: ["button", "submit", "reset", "menu"],
58
58
  description: "Native HTML button type attribute.",
59
59
  },
60
+ accept: {
61
+ control: "text",
62
+ description: "File types the picker offers (with `onFileSelect`).",
63
+ },
64
+ isMultiple: {
65
+ control: "boolean",
66
+ description: "Lets the picker select more than one file.",
67
+ },
60
68
  onClick: { action: "onClick" },
69
+ onFileSelect: {
70
+ action: "onFileSelect",
71
+ description: "Turns the button into a file picker.",
72
+ },
61
73
  },
62
74
  parameters: {
63
75
  docs: {
@@ -150,6 +162,16 @@ Round.args = {
150
162
  children: "+",
151
163
  }
152
164
 
165
+ export const FilePicker = {}
166
+ FilePicker.args = {
167
+ ...Default.args,
168
+ children: "Change photo",
169
+ variant: "outline",
170
+ shape: "pill",
171
+ accept: "image/jpeg,image/png,image/webp",
172
+ onFileSelect: () => {},
173
+ }
174
+
153
175
  export const CustomContent = {}
154
176
  CustomContent.args = {
155
177
  ...Default.args,
@@ -3,7 +3,7 @@
3
3
  * @typedef {import('@inglorious/web').TemplateResult} TemplateResult
4
4
  */
5
5
 
6
- import { html } from "@inglorious/web"
6
+ import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
9
 
@@ -12,10 +12,14 @@ import { applyElementProps } from "../../shared/apply-element-props.js"
12
12
  /**
13
13
  * Renders a themed button for actions and form submission.
14
14
  * Supports variants, sizes, shapes, pressed state, and full-width layout.
15
+ * With `onFileSelect`, the button also opens a file picker on click.
15
16
  * @example
16
17
  * // Stateless usage
17
18
  * button.render({ children: 'Click me', variant: 'primary' })
18
19
  *
20
+ * // File picker
21
+ * button.render({ children: 'Upload', accept: 'image/*', onFileSelect })
22
+ *
19
23
  * @param {ButtonProps} props
20
24
  * @returns {TemplateResult}
21
25
  */
@@ -31,10 +35,13 @@ export function render(props) {
31
35
  isPressed = false,
32
36
  isFullWidth = false,
33
37
  buttonType = "button",
34
- ariaLabel = "",
35
- isAriaPressed = false,
38
+ ariaLabel,
39
+ isAriaPressed,
36
40
  className = "",
41
+ accept,
42
+ isMultiple = false,
37
43
  onClick,
44
+ onFileSelect,
38
45
  ...rest
39
46
  } = props
40
47
 
@@ -57,17 +64,43 @@ export function render(props) {
57
64
  ...extraClasses,
58
65
  }
59
66
 
67
+ const handleClick = onFileSelect
68
+ ? (event) => {
69
+ onClick?.(event)
70
+ event.currentTarget.nextElementSibling.click()
71
+ }
72
+ : onClick
73
+
60
74
  return html`
61
75
  <button
62
76
  type=${buttonType}
63
- aria-label=${ariaLabel}
64
- aria-pressed=${isAriaPressed}
77
+ aria-label=${ariaLabel || nothing}
78
+ aria-pressed=${isAriaPressed ?? nothing}
65
79
  class=${classMap(classes)}
66
80
  ?disabled=${isDisabled}
67
- @click=${onClick}
81
+ @click=${handleClick}
68
82
  ${ref((element) => applyElementProps(element, rest))}
69
83
  >
70
84
  ${children}
71
85
  </button>
86
+ ${
87
+ onFileSelect
88
+ ? html`<input
89
+ type="file"
90
+ accept=${accept ?? ""}
91
+ ?multiple=${isMultiple}
92
+ ?disabled=${isDisabled}
93
+ hidden
94
+ @change=${(event) => handleFileChange(event, onFileSelect)}
95
+ />`
96
+ : nothing
97
+ }
72
98
  `
73
99
  }
100
+
101
+ function handleFileChange(event, onFileSelect) {
102
+ const { files } = event.target
103
+ if (files?.length) onFileSelect(files)
104
+ // Clear the input so picking the same file again still fires `change`
105
+ event.target.value = ""
106
+ }
@@ -5,7 +5,8 @@
5
5
  flex-direction: column;
6
6
  gap: var(--iw-space-1);
7
7
  font-family: var(--iw-font-family-sans);
8
- position: relative; /* required for dropdown positioning */
8
+ /* Each combobox anchors its own dropdown to its own frame. */
9
+ anchor-scope: --iw-combobox;
9
10
 
10
11
  &.iw-combobox-full-width {
11
12
  width: 100%;
@@ -38,6 +39,7 @@
38
39
  }
39
40
 
40
41
  .iw-combobox-frame {
42
+ anchor-name: --iw-combobox;
41
43
  display: flex;
42
44
  align-items: center;
43
45
  /* The gap spaces the text from the arrow, as it does for Input's icons. */
@@ -182,12 +184,17 @@
182
184
  }
183
185
  }
184
186
 
187
+ /* A popover in the top layer, placed under the frame and as wide as it,
188
+ flipping above when there is no room below. The browser's popover box
189
+ is centred in the viewport, so its insets, margin and colours are reset. */
185
190
  .iw-combobox-dropdown {
186
- position: absolute;
187
- top: 100%;
188
- left: 0;
189
- right: 0;
190
- z-index: 1000;
191
+ position-anchor: --iw-combobox;
192
+ position-area: block-end span-inline-end;
193
+ position-try-fallbacks: flip-block;
194
+ inset: auto;
195
+ width: anchor-size(width);
196
+ padding: 0;
197
+ color: var(--iw-color-text);
191
198
  display: flex;
192
199
  flex-direction: column;
193
200
  max-height: 16rem;
@@ -198,7 +205,7 @@
198
205
  border-start-end-radius: 0;
199
206
  background: var(--iw-color-bg);
200
207
  box-shadow: var(--iw-shadow-lg);
201
- margin-top: -1px;
208
+ margin: -1px 0 0;
202
209
 
203
210
  .iw-combobox-dropdown-search {
204
211
  width: 100%;
@@ -12,6 +12,7 @@ import { when } from "@inglorious/web/directives/when"
12
12
 
13
13
  import { Chip } from "../../data-display/chip/index.js"
14
14
  import { fallbackLabel } from "../../shared/fallback-label.js"
15
+ import { syncPopover } from "../../shared/top-layer.js"
15
16
  import {
16
17
  filterOptions,
17
18
  formatOption,
@@ -263,7 +264,12 @@ export const Combobox = {
263
264
 
264
265
  /**
265
266
  * Renders the dropdown panel that contains search input, options, and empty/loading states.
266
- * Shown only when the combobox is open.
267
+ * Shown only when the combobox is open, as a popover in the top layer so no
268
+ * container can clip it, anchored under the control by CSS.
269
+ *
270
+ * A manual popover: the control that opens it is not a button, so it cannot
271
+ * be the popover's invoker, and a light dismiss would close the dropdown on
272
+ * the very press that toggles it. Outside presses and Esc are handled here.
267
273
  * @param {ComboboxProps} props
268
274
  * @returns {TemplateResult}
269
275
  */
@@ -276,7 +282,11 @@ export const Combobox = {
276
282
  id=${id ? `${id}-control-listbox` : nothing}
277
283
  class="iw-combobox-dropdown"
278
284
  role="listbox"
279
- ${ref((el) => trackOutsideClick(props, el))}
285
+ popover="manual"
286
+ ${ref((el) => {
287
+ syncPopover(el, { isOpen: true })
288
+ trackOutsideClick(props, el)
289
+ })}
280
290
  >
281
291
  ${when(isSearchable, () => this.renderSearchInput?.(props))}
282
292
  ${when(isLoading, () => this.renderLoading?.(props))}
@@ -58,6 +58,10 @@
58
58
  inset: 0;
59
59
  opacity: 0;
60
60
  cursor: pointer;
61
+
62
+ &::file-selector-button {
63
+ cursor: inherit;
64
+ }
61
65
  }
62
66
 
63
67
  .iw-upload-content {
@@ -1,17 +1,39 @@
1
+ /* A fieldset of native radios: the inputs stay in the page for the keyboard
2
+ and assistive tech but out of sight, and each star is the label that
3
+ checks its radio. */
1
4
  .iw-rating {
2
5
  display: inline-flex;
3
6
  align-items: center;
4
7
  gap: var(--iw-space-1);
8
+ margin: 0;
9
+ padding: 0;
10
+ border: 0;
11
+ min-inline-size: 0;
5
12
 
6
13
  .iw-rating-item {
7
- border: 0;
8
- background: transparent;
9
- padding: 0;
10
- margin: 0;
14
+ position: relative;
11
15
  cursor: pointer;
12
16
  color: var(--iw-color-warning);
13
17
  font-size: 1.25rem;
14
18
  line-height: 1;
19
+ border-radius: var(--iw-radius-sm);
20
+ }
21
+
22
+ .iw-rating-input {
23
+ position: absolute;
24
+ inset: 0;
25
+ margin: 0;
26
+ opacity: 0;
27
+ cursor: inherit;
28
+ }
29
+
30
+ .iw-rating-item:has(.iw-rating-input:focus-visible) {
31
+ outline: 2px solid var(--iw-color-primary);
32
+ outline-offset: 2px;
33
+ }
34
+
35
+ &:disabled .iw-rating-item {
36
+ cursor: default;
15
37
  }
16
38
 
17
39
  &.iw-rating-sm .iw-rating-item {
@@ -3,7 +3,7 @@
3
3
  * @typedef {import('@inglorious/web').TemplateResult} TemplateResult
4
4
  */
5
5
 
6
- import { html } from "@inglorious/web"
6
+ import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
 
9
9
  const DEFAULT_VALUE = 0
@@ -12,12 +12,16 @@ const INDEX_TO_RATING = 1
12
12
 
13
13
  /**
14
14
  * Renders a star (or custom symbol) rating control.
15
+ * Each star is a native radio, so the arrow keys move between them, the group
16
+ * is announced as one choice, and the value submits with a form under `name`.
15
17
  * Supports max value, read-only/disabled states, and emits selection changes.
16
18
  * @param {RatingProps} props
17
19
  * @returns {TemplateResult}
18
20
  */
19
21
  export function render(props) {
20
22
  const {
23
+ id,
24
+ name,
21
25
  value = DEFAULT_VALUE,
22
26
  max = DEFAULT_MAX,
23
27
  isDisabled = false,
@@ -25,9 +29,14 @@ export function render(props) {
25
29
  symbol = "★",
26
30
  emptySymbol = "☆",
27
31
  size = "md",
32
+ ariaLabel = "Rating",
28
33
  onChange,
29
34
  } = props
30
35
 
36
+ // Radios are a group by sharing a name: without one, the arrow keys do not
37
+ // move between the stars.
38
+ const groupName = name || id
39
+
31
40
  const classes = {
32
41
  "iw-rating": true,
33
42
  [`iw-rating-${size}`]: size !== "md",
@@ -36,22 +45,30 @@ export function render(props) {
36
45
  }
37
46
 
38
47
  return html`
39
- <div class=${classMap(classes)} role="radiogroup" aria-label="rating">
48
+ <fieldset
49
+ id=${id || nothing}
50
+ class=${classMap(classes)}
51
+ aria-label=${ariaLabel}
52
+ ?disabled=${isDisabled || isReadOnly}
53
+ >
40
54
  ${Array.from({ length: max }, (_, index) => {
41
55
  const ratingValue = index + INDEX_TO_RATING
42
56
  const isActive = ratingValue <= value
43
57
  return html`
44
- <button
45
- type="button"
46
- class="iw-rating-item"
47
- aria-label=${`rate-${ratingValue}`}
48
- ?disabled=${isDisabled || isReadOnly}
49
- @click=${() => onChange?.(ratingValue)}
50
- >
51
- ${isActive ? symbol : emptySymbol}
52
- </button>
58
+ <label class="iw-rating-item">
59
+ <input
60
+ class="iw-rating-input"
61
+ type="radio"
62
+ name=${groupName || nothing}
63
+ value=${ratingValue}
64
+ aria-label=${`${ratingValue} of ${max}`}
65
+ .checked=${ratingValue === value}
66
+ @change=${() => onChange?.(ratingValue)}
67
+ />
68
+ <span aria-hidden="true">${isActive ? symbol : emptySymbol}</span>
69
+ </label>
53
70
  `
54
71
  })}
55
- </div>
72
+ </fieldset>
56
73
  `
57
74
  }
@@ -10,7 +10,8 @@ import { ref } from "@inglorious/web/directives/ref"
10
10
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
11
 
12
12
  /**
13
- * Renders a toggle switch backed by a checkbox input.
13
+ * Renders a toggle switch backed by a checkbox input, which `role="switch"`
14
+ * has assistive tech announce as on/off rather than checked.
14
15
  * Use it for boolean settings with optional labels and disabled state.
15
16
  * @param {SwitchProps} props
16
17
  * @returns {TemplateResult}
@@ -46,6 +47,7 @@ export function render(props) {
46
47
  id=${inputId || nothing}
47
48
  class="iw-switch-input"
48
49
  type="checkbox"
50
+ role="switch"
49
51
  name=${name}
50
52
  .checked=${isChecked}
51
53
  ?disabled=${isDisabled}
@@ -1,10 +1,22 @@
1
+ /* The bubble is anchored to the trigger by CSS: the browser places it on the
2
+ requested side and flips it to the opposite one when it would leave the
3
+ viewport. Fixed, so a container that clips its overflow does not cut it. */
1
4
  .iw-tooltip {
2
- position: relative;
5
+ anchor-scope: --iw-tooltip;
3
6
  display: inline-flex;
4
7
  align-items: center;
5
8
 
9
+ .iw-tooltip-trigger {
10
+ anchor-name: --iw-tooltip;
11
+ }
12
+
6
13
  .iw-tooltip-bubble {
7
- position: absolute;
14
+ position: fixed;
15
+ position-anchor: --iw-tooltip;
16
+ position-area: top;
17
+ position-try-fallbacks: flip-block;
18
+ margin-block-end: var(--iw-space-2);
19
+ width: max-content;
8
20
  z-index: var(--iw-z-tooltip);
9
21
  opacity: 0;
10
22
  pointer-events: none;
@@ -15,9 +27,6 @@
15
27
  padding: var(--iw-space-2) var(--iw-space-3);
16
28
  box-shadow: var(--iw-shadow-md);
17
29
  transition: opacity 150ms ease;
18
- left: 50%;
19
- bottom: calc(100% + var(--iw-space-2));
20
- transform: translateX(-50%);
21
30
  white-space: normal;
22
31
  }
23
32
 
@@ -38,20 +47,19 @@
38
47
  }
39
48
 
40
49
  &.iw-tooltip-bottom .iw-tooltip-bubble {
41
- top: calc(100% + var(--iw-space-2));
42
- bottom: auto;
50
+ position-area: bottom;
51
+ margin: var(--iw-space-2) 0 0;
43
52
  }
44
53
 
45
54
  &.iw-tooltip-left .iw-tooltip-bubble {
46
- left: auto;
47
- right: calc(100% + var(--iw-space-2));
48
- bottom: 50%;
49
- transform: translateY(50%);
55
+ position-area: left;
56
+ position-try-fallbacks: flip-inline;
57
+ margin: 0 var(--iw-space-2) 0 0;
50
58
  }
51
59
 
52
60
  &.iw-tooltip-right .iw-tooltip-bubble {
53
- left: calc(100% + var(--iw-space-2));
54
- bottom: 50%;
55
- transform: translateY(50%);
61
+ position-area: right;
62
+ position-try-fallbacks: flip-inline;
63
+ margin: 0 0 0 var(--iw-space-2);
56
64
  }
57
65
  }
@@ -1028,6 +1028,7 @@ Dialog.render({
1028
1028
  preview: () =>
1029
1029
  Dialog.render({
1030
1030
  isOpen: true,
1031
+ isModal: false,
1031
1032
  title: "Delete record",
1032
1033
  description: "This action cannot be undone.",
1033
1034
  actions: previewRow([
@@ -1173,8 +1174,7 @@ Drawer.render({
1173
1174
  })`,
1174
1175
  preview: () =>
1175
1176
  Drawer.render({
1176
- isOpen: true,
1177
- hasBackdrop: false,
1177
+ variant: "permanent",
1178
1178
  title: "Navigation",
1179
1179
  children: List.render({
1180
1180
  items: [
@@ -1225,7 +1225,8 @@ Link.render({
1225
1225
  code: `import { Menu } from "@inglorious/ui/menu"
1226
1226
 
1227
1227
  Menu.render({
1228
- isOpen: true,
1228
+ id: "record-actions",
1229
+ trigger: "Actions",
1229
1230
  items: [
1230
1231
  { label: "Edit" },
1231
1232
  { label: "Duplicate" },
@@ -1235,7 +1236,8 @@ Menu.render({
1235
1236
  })`,
1236
1237
  preview: () =>
1237
1238
  Menu.render({
1238
- isOpen: true,
1239
+ id: "record-actions",
1240
+ trigger: "Actions",
1239
1241
  items: [
1240
1242
  { label: "Edit" },
1241
1243
  { label: "Duplicate" },
@@ -1268,7 +1270,6 @@ Pagination.render({
1268
1270
  code: `import { SpeedDial } from "@inglorious/ui/speed-dial"
1269
1271
 
1270
1272
  SpeedDial.render({
1271
- isOpen: true,
1272
1273
  actions: [
1273
1274
  { label: "Add", icon: "+" },
1274
1275
  { label: "Share", icon: "↗" }
@@ -1276,7 +1277,6 @@ SpeedDial.render({
1276
1277
  })`,
1277
1278
  preview: () =>
1278
1279
  SpeedDial.render({
1279
- isOpen: true,
1280
1280
  actions: [
1281
1281
  { label: "Add", icon: "+" },
1282
1282
  { label: "Share", icon: "↗" },
@@ -251,17 +251,23 @@
251
251
  }
252
252
 
253
253
  .iw-primitive-preview .iw-backdrop,
254
- .iw-primitive-preview .iw-dialog-backdrop,
255
254
  .iw-primitive-preview .iw-snackbar,
256
255
  .iw-primitive-preview .iw-drawer {
257
256
  position: absolute;
258
257
  }
259
258
 
260
- .iw-primitive-preview .iw-backdrop,
261
- .iw-primitive-preview .iw-dialog-backdrop {
259
+ .iw-primitive-preview .iw-backdrop {
262
260
  inset: 0;
263
261
  }
264
262
 
263
+ /* Shown in place (isModal: false), so it can sit in the preview's flow
264
+ rather than where the browser positions a non-modal dialog. */
265
+ .iw-primitive-preview .iw-dialog {
266
+ position: static;
267
+ min-width: 0;
268
+ max-width: 100%;
269
+ }
270
+
265
271
  .iw-primitive-preview .iw-drawer {
266
272
  top: var(--iw-space-3);
267
273
  bottom: var(--iw-space-3);
@@ -278,8 +284,8 @@
278
284
  color: var(--iw-color-text-secondary);
279
285
  }
280
286
 
281
- .iw-dashboard-progress-list .iw-progress-linear-track {
282
- height: 0.25rem;
287
+ .iw-dashboard-progress-list .iw-progress-linear {
288
+ --iw-progress-height: 0.25rem;
283
289
  }
284
290
 
285
291
  .iw-dashboard-user-subtitle {
@@ -9,15 +9,18 @@ export default {
9
9
  render: createRender(Dialog),
10
10
  argTypes: {
11
11
  isOpen: { control: "boolean" },
12
+ isModal: { control: "boolean" },
12
13
  title: { control: "text" },
13
14
  description: { control: "text" },
14
15
  onClose: { action: "onClose" },
15
16
  },
16
17
  }
17
18
 
19
+ // In place, so the docs page that shows every story stays usable.
18
20
  export const Open = {
19
21
  args: {
20
22
  isOpen: true,
23
+ isModal: false,
21
24
  title: "Delete item",
22
25
  description: "This action cannot be undone.",
23
26
  children: html`<p>Are you sure you want to continue?</p>`,
@@ -25,3 +28,9 @@ export const Open = {
25
28
  <button class="iw-button iw-button-primary">Confirm</button>`,
26
29
  },
27
30
  }
31
+
32
+ // Modal takes over the page, so it is left out of the docs page.
33
+ export const Modal = {
34
+ tags: ["!autodocs"],
35
+ args: { ...Open.args, isModal: true },
36
+ }
@@ -1,15 +1,7 @@
1
- .iw-dialog-backdrop {
2
- position: fixed;
3
- inset: 0;
4
- background: color-mix(in srgb, var(--iw-color-black) 55%, transparent);
5
- display: flex;
6
- align-items: center;
7
- justify-content: center;
8
- z-index: var(--iw-z-modal, 1050);
9
- padding: var(--iw-space-4);
10
- backdrop-filter: blur(2px);
11
- }
12
-
1
+ /* A native <dialog>, so the browser's own box is reset: the surface inside
2
+ carries the padding, which leaves the dialog element itself reachable only
3
+ through the backdrop. Modal dialogs sit in the top layer, above any
4
+ z-index, and the browser centres them. */
13
5
  .iw-dialog {
14
6
  background: var(--iw-color-surface);
15
7
  color: var(--iw-color-text);
@@ -18,6 +10,15 @@
18
10
  min-width: min(32rem, 90vw);
19
11
  max-width: min(48rem, 90vw);
20
12
  box-shadow: var(--iw-shadow-lg, 0 12px 40px rgba(0, 0, 0, 0.3));
13
+ padding: 0;
14
+ }
15
+
16
+ .iw-dialog::backdrop {
17
+ background: color-mix(in srgb, var(--iw-color-black) 55%, transparent);
18
+ backdrop-filter: blur(2px);
19
+ }
20
+
21
+ .iw-dialog-surface {
21
22
  padding: var(--iw-space-4);
22
23
  }
23
24