material-inspired-component-library 9.1.1 → 9.1.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.
Files changed (61) hide show
  1. package/components/appbar/index.scss +1 -1
  2. package/components/badge/README.md +43 -12
  3. package/components/badge/index.scss +15 -21
  4. package/components/bottomsheet/README.md +53 -22
  5. package/components/bottomsheet/index.scss +61 -44
  6. package/components/button/_shared.scss +6 -6
  7. package/components/card/README.md +1 -1
  8. package/components/checkbox/index.scss +10 -10
  9. package/components/datepicker/README.md +184 -59
  10. package/components/datepicker/index.scss +84 -20
  11. package/components/dialog/README.md +151 -56
  12. package/components/dialog/index.scss +50 -62
  13. package/components/iconbutton/index.scss +5 -5
  14. package/components/list/index.scss +6 -6
  15. package/components/menu/index.scss +13 -13
  16. package/components/navigationbar/index.scss +7 -1
  17. package/components/navigationrail/index.scss +27 -22
  18. package/components/radio/index.scss +8 -8
  19. package/components/select/index.scss +3 -3
  20. package/components/sidesheet/README.md +90 -37
  21. package/components/sidesheet/index.scss +51 -41
  22. package/components/slider/README.md +35 -9
  23. package/components/slider/index.scss +182 -153
  24. package/components/snackbar/README.md +43 -25
  25. package/components/snackbar/index.scss +22 -27
  26. package/components/stepper/index.scss +4 -4
  27. package/components/switch/index.scss +13 -13
  28. package/components/timepicker/README.md +131 -55
  29. package/components/timepicker/index.scss +46 -25
  30. package/dist/appbar.css +1 -1
  31. package/dist/badge.css +1 -1
  32. package/dist/bottomsheet.css +1 -1
  33. package/dist/bottomsheet.js +1 -1
  34. package/dist/button.css +1 -1
  35. package/dist/checkbox.css +1 -1
  36. package/dist/chip.css +1 -1
  37. package/dist/datepicker.css +1 -1
  38. package/dist/datepicker.js +1 -1
  39. package/dist/dialog.css +1 -1
  40. package/dist/iconbutton.css +1 -1
  41. package/dist/layout.css +1 -1
  42. package/dist/list.css +1 -1
  43. package/dist/menu.css +1 -1
  44. package/dist/micl.css +1 -1
  45. package/dist/micl.js +1 -1
  46. package/dist/navigationbar.css +1 -1
  47. package/dist/navigationrail.css +1 -1
  48. package/dist/radio.css +1 -1
  49. package/dist/select.css +1 -1
  50. package/dist/sidesheet.css +1 -1
  51. package/dist/slider.css +1 -1
  52. package/dist/slider.js +1 -1
  53. package/dist/snackbar.css +1 -1
  54. package/dist/snackbar.js +1 -1
  55. package/dist/stepper.css +1 -1
  56. package/dist/switch.css +1 -1
  57. package/dist/timepicker.css +1 -1
  58. package/dist/timepicker.js +1 -1
  59. package/foundations/layout/_tokens.scss +55 -0
  60. package/foundations/layout/index.scss +2 -35
  61. package/package.json +1 -1
@@ -1,102 +1,170 @@
1
1
  # Dialog
2
- This component implements the [Material Design 3 Expressive Dialog](https://m3.material.io/components/dialogs/overview) design. A dialog is a small window that prompts the user to make a decision or enter additional information.
2
+ This component implements the [Material Design 3 Expressive Dialog](https://m3.material.io/components/dialogs/overview) design. A dialog is a small, focused window that prompts the user to make a decision or enter additional information.
3
3
 
4
4
  ## Basic Usage
5
5
 
6
6
  ### HTML
7
- To create a basic dialog, use the `<dialog>` element with the `micl-dialog` class. You can open and close the dialog using JavaScript, or you can use a control element, such as a button, to open and close the dialog.
7
+
8
+ To create a dialog, use the `<dialog>` element with the `micl-dialog` class and open it from a control element, such as a button.
8
9
 
9
10
  ```HTML
10
- <dialog id="mydialog" class="micl-dialog" popover closedby="any" aria-labelledby="mytitle" aria-describedby="mydesc">
11
+ <dialog id="mydialog" class="micl-dialog" closedby="closerequest" aria-labelledby="mytitle" aria-describedby="mydesc">
11
12
  <div class="micl-dialog__headline">
12
13
  <h2 id="mytitle">Basic dialog</h2>
13
14
  <span id="mydesc" class="micl-dialog__supporting-text">An example of a basic dialog</span>
14
15
  </div>
16
+ <form method="dialog" class="micl-dialog__actions">
17
+ <button class="micl-button-text-s" value="" autofocus>Cancel</button>
18
+ <button class="micl-button-text-s" value="ok">OK</button>
19
+ </form>
15
20
  </dialog>
16
21
 
17
- <button type="button" popovertarget="mydialog">Open Basic Dialog</button>
18
- ```
22
+ <button type="button" class="micl-button-filled-m" command="show-modal" commandfor="mydialog">Open Basic Dialog</button>
19
23
 
20
- - The `popover` attribute makes the dialog a non-modal (light dismiss) popover.
24
+ ```
21
25
 
22
- - The `closedby="any"` attribute allows the dialog to be closed by clicking or tapping outside of it, or by pressing the <kbd>Esc</kbd> key.
26
+ * `command="show-modal"` combined with `commandfor` opens the dialog as a **modal**: the browser moves it to the top layer, traps keyboard focus inside it, and makes the rest of the page inert. This is the Material Design default and the recommended approach for most use cases.
27
+ * `closedby="closerequest"` is the default for a modal dialog and allows the <kbd>Esc</kbd> key to close it. Using `closedby="any"` will additionally close the dialog when the user clicks outside of it. Avoid `closedby="none"`, as it removes the <kbd>Esc</kbd> key escape route.
28
+ * `aria-labelledby` points to the heading and `aria-describedby` to the supporting text, ensuring assistive technologies announce both when the dialog opens.
29
+ * Buttons inside a `<form method="dialog">` close the dialog when activated, and the activating button's `value` is passed to `dialog.returnValue`. Give a canceling button an explicit `value=""`—a button without a `value` attribute leaves `returnValue` unchanged from its previous state. An empty `returnValue` is the conventional way to signal a cancellation.
30
+ * `autofocus` designates the control that receives focus when the dialog opens. Place this on the least destructive action.
23
31
 
24
32
  ### CSS
33
+
25
34
  Import the dialog styles into your project:
26
35
 
27
36
  ```CSS
28
37
  @use "material-inspired-component-library/dist/button";
29
38
  @use "material-inspired-component-library/dist/iconbutton";
30
39
  @use "material-inspired-component-library/dist/dialog";
40
+
31
41
  ```
32
42
 
33
43
  Or import all MICL styles:
44
+
34
45
  ```CSS
35
46
  @use "material-inspired-component-library/styles";
47
+
36
48
  ```
37
49
 
38
50
  ### JavaScript
39
- No custom JavaScript is required for the core functionality of this component, as the native popover attribute handles the open/close behavior.
51
+
52
+ No custom JavaScript is required for the core functionality of this component. The native `command` and `popover` attributes handle the open and close behaviors entirely.
40
53
 
41
54
  ### Live Demo
42
- A live example of the [Dialog component](https://henkpb.github.io/micl/dialog.html) is available to interact with.
43
55
 
44
- ## Variants
45
- When dialogs with the `popover` attribute are opened, they animate from the control element to the center of the screen. They can be easily dismissed by clicking outside or pressing <kbd>Esc</kbd>.
56
+ A live example of the [Dialog component](https://henkpb.github.io/micl/dialog.html) is available for interaction.
57
+
58
+ ## Anatomy
59
+
60
+ A dialog consists of up to three sections:
61
+
62
+ * `micl-dialog__headline`: The header of the dialog. It usually contains:
63
+ * A heading element (`<h1>`-`<h6>`).
64
+ * An optional icon (`micl-dialog__icon`). When present, the icon, heading, and supporting text are centered.
65
+ * An optional sub-header (`micl-dialog__subhead`). Text that exceeds one line is truncated with an ellipsis.
66
+ * An optional `micl-dialog__supporting-text` element describing the dialog's purpose.
46
67
 
47
- Removing the `popover` attribute creates a more intrusive **modal** dialog. This type of dialog requires the user to interact with its buttons or press the <kbd>Esc</kbd> key to close it.
68
+
69
+ * `micl-dialog__content`: The optional main content area where additional information is placed. This section scrolls if the content exceeds the dialog's maximum height.
70
+ * `micl-dialog__actions`: A container for action buttons. In a modal dialog, these must be placed inside a `<form method="dialog">`.
48
71
 
49
72
  ```HTML
50
- <dialog id="mydialog" class="micl-dialog" closedby="closerequest" role="alertdialog" aria-labelledby="mytitle" aria-describedby="mydesc">
73
+ <dialog id="mydialog" class="micl-dialog" closedby="closerequest" aria-labelledby="mytitle" aria-describedby="mydesc">
51
74
  <div class="micl-dialog__headline">
52
75
  <span class="micl-dialog__icon material-symbols-outlined" aria-hidden="true">info</span>
53
- <h2 id="mytitle">Modal dialog</h2>
54
- <span id="mydesc" class="micl-dialog__supporting-text">An example of a modal dialog</span>
76
+ <h2 id="mytitle">Headline</h2>
77
+ <span class="micl-dialog__subhead">Sub-header</span>
78
+ <span id="mydesc" class="micl-dialog__supporting-text">Supporting text</span>
55
79
  </div>
56
- <div class="micl-dialog__actions">
57
- <form method="dialog">
58
- <button class="micl-button-text-s" autofocus>Cancel</button>
59
- <button class="micl-button-text-s" value="save">Save</button>
60
- </form>
80
+ <div class="micl-dialog__content">
81
+ …
61
82
  </div>
83
+ <form method="dialog" class="micl-dialog__actions">
84
+ <button class="micl-button-text-s" value="" autofocus>Cancel</button>
85
+ <button class="micl-button-text-s" value="ok">OK</button>
86
+ </form>
62
87
  </dialog>
63
88
 
64
- <button type="button" command="show-modal" commandfor="mydialog">Open Modal Dialog</button>
65
89
  ```
66
90
 
67
- - The `closedby="closerequest"` attribute restricts closing methods, typically requiring an explicit action within the dialog.
91
+ ## Variants
92
+
93
+ ### Alert dialog
68
94
 
69
- By default, modal dialogs open in the center of the screen. You can anchor a modal dialog to a control element using the `micl-dialog--docked` class and CSS Anchor settings, causing it to open relative to that element:
95
+ A dialog that interrupts the user with an urgent message must use `role="alertdialog"` to ensure assistive technologies announce it appropriately. Keep the content concise and provide at least one confirming and one dismissive action.
70
96
 
71
97
  ```HTML
72
- <dialog id="mydialog" class="micl-dialog micl-dialog--docked" style="position-anchor:--myanchor">
98
+ <dialog id="mydialog" class="micl-dialog" closedby="closerequest" role="alertdialog" aria-labelledby="mytitle" aria-describedby="mydesc">
99
+ <div class="micl-dialog__headline">
100
+ <span class="micl-dialog__icon material-symbols-outlined" aria-hidden="true">delete</span>
101
+ <h2 id="mytitle">Delete selected images?</h2>
102
+ <span id="mydesc" class="micl-dialog__supporting-text">Images will be permanently removed from all synchronized devices.</span>
103
+ </div>
104
+ <form method="dialog" class="micl-dialog__actions">
105
+ <button class="micl-button-text-s" value="" autofocus>Cancel</button>
106
+ <button class="micl-button-text-s" value="dodelete">Delete</button>
107
+ </form>
73
108
  </dialog>
74
109
 
75
- <button type="button" popovertarget="mydialog" style="anchor-name:--myanchor">Open Modal Dialog</button>
110
+ <button type="button" class="micl-button-outlined-m" command="show-modal" commandfor="mydialog">Delete images</button>
111
+
76
112
  ```
77
113
 
78
- ### Dialog Structure Sections
79
- A dialog typically consists of three main sections to organize its content:
114
+ ### Light-dismiss dialog
80
115
 
81
- - `micl-dialog__headline`: The header of the dialog. It usually contains:
116
+ Adding the `popover` attribute renders the dialog as a popover instead of a modal. It animates outward from the opening control element and closes automatically when the user clicks outside of it or presses <kbd>Esc</kbd>.
82
117
 
83
- - A heading element (`<h1>`-`<h6>`).
118
+ ```HTML
119
+ <dialog id="mydialog" class="micl-dialog" popover aria-labelledby="mytitle" aria-describedby="mydesc">
120
+ <div class="micl-dialog__headline">
121
+ <h2 id="mytitle">Light-dismiss dialog</h2>
122
+ <span id="mydesc" class="micl-dialog__supporting-text">An example of a dialog that closes when you click outside it</span>
123
+ </div>
124
+ <div class="micl-dialog__actions">
125
+ <button type="button" class="micl-button-text-s" popovertarget="mydialog" popovertargetaction="hide" autofocus>OK</button>
126
+ </div>
127
+ </dialog>
128
+
129
+ <button type="button" class="micl-button-outlined-m" popovertarget="mydialog">Open Dialog</button>
84
130
 
85
- - An optional icon (use `micl-dialog__icon`).
131
+ ```
132
+
133
+ > [!IMPORTANT]
134
+ > A popover is not a modal. The browser does not trap focus inside it nor does it make the rest of the page inert. Review the Accessibility guidelines before choosing this variant.
86
135
 
87
- - An optional sub-header (e.g., `<span class="micl-dialog__subhead">`).
136
+ Because a popover is governed by the Popover API rather than standard dialog behavior, two modal attributes have no effect here:
88
137
 
89
- - An optional `micl-dialog__supporting-text` element, describing the dialog's purpose.
138
+ * `closedby` is ignored. Light dismissal and <kbd>Esc</kbd> key support are handled natively by the `popover` attribute.
139
+ * `<form method="dialog">` will not close a popover. You must close it using a button with `popovertarget` and `popovertargetaction="hide"`, as shown above.
90
140
 
91
- - `micl-dialog__content`: The optional main content area of the dialog, where additional information can be placed.
141
+ ### Docked dialog
92
142
 
93
- - `micl-dialog__actions`: A container for action buttons that allow the user to perform actions related to the dialog or close it. Actions are typically placed in a `<form method="dialog">` for native HTML dialog closing.
143
+ A docked dialog opens adjacent to the control element rather than centering on the screen. Add the `micl-dialog--docked` class, assign an `anchor-name` to the control element, and link the dialog using `position-anchor`:
144
+
145
+ ```HTML
146
+ <dialog id="mydialog" class="micl-dialog micl-dialog--docked" style="position-anchor:--myanchor" popover aria-labelledby="mytitle">
147
+ <div class="micl-dialog__headline">
148
+ <h2 id="mytitle">Docked dialog</h2>
149
+ </div>
150
+ <div class="micl-dialog__actions">
151
+ <button type="button" class="micl-button-text-s" popovertarget="mydialog" popovertargetaction="hide">Close</button>
152
+ </div>
153
+ </dialog>
154
+
155
+ <button type="button" class="micl-button-outlined-m" popovertarget="mydialog" style="anchor-name:--myanchor">Open Docked Dialog</button>
156
+
157
+ ```
158
+
159
+ * The dialog places itself below the control element and flips above it if there is insufficient vertical space.
160
+ * A docked dialog fades in and out in place, rather than scaling out from the control element.
94
161
 
95
162
  ### Full-screen dialog
96
- A full-screen dialog covers the entire viewport, primarily on smaller screens. On screens wider than 560 pixels, a full-screen dialog behaves like a basic dialog. Use the `micl-dialog--fullscreen` modifier class for this variant:
163
+
164
+ A full-screen dialog covers the entire viewport on compact windows (599px and narrower). On wider windows, it behaves like a basic dialog. Use the `micl-dialog--fullscreen` modifier class:
97
165
 
98
166
  ```HTML
99
- <dialog id="mydialog" class="micl-dialog micl-dialog--fullscreen" closedby="none" aria-labelledby="mytitle" aria-describedby="mydesc">
167
+ <dialog id="mydialog" class="micl-dialog micl-dialog--fullscreen" closedby="closerequest" aria-labelledby="mytitle" aria-describedby="mydesc">
100
168
  <form method="dialog" class="micl-dialog__headline">
101
169
  <button class="micl-dialog__fullscreen micl-iconbutton-standard-s material-symbols-outlined" aria-label="Close">close</button>
102
170
  <span class="micl-dialog__icon material-symbols-outlined" aria-hidden="true">person</span>
@@ -107,44 +175,71 @@ A full-screen dialog covers the entire viewport, primarily on smaller screens. O
107
175
  <span id="mydesc" class="micl-dialog__supporting-text">This dialog covers the whole screen.</span>
108
176
  </div>
109
177
  <form method="dialog" class="micl-dialog__actions">
110
- <button class="micl-button-text-s" autofocus>Cancel</button>
178
+ <button class="micl-button-text-s" value="" autofocus>Cancel</button>
111
179
  <button class="micl-button-text-s" value="dosave">Save</button>
112
180
  </form>
113
181
  </dialog>
114
182
 
115
- <button type="button" command="show-modal" commandfor="mydialog">Open Full-Screen Dialog</button>
183
+ <button type="button" class="micl-button-outlined-m" command="show-modal" commandfor="mydialog">Open Full-Screen Dialog</button>
184
+
116
185
  ```
117
186
 
118
- - In full-screen mode, `micl-dialog__fullscreen` buttons placed directly within the `micl-dialog__headline` become visible, while the `micl-dialog__icon` and `micl-dialog__actions` at the bottom are hidden.
187
+ * In full-screen mode, `micl-dialog__fullscreen` buttons placed directly within the `micl-dialog__headline` become visible. The `micl-dialog__icon` and the standard `micl-dialog__actions` at the bottom are hidden.
188
+ * On wider screens, the layout reverts: `micl-dialog__fullscreen` buttons are hidden, and the standard bottom actions (`micl-dialog__actions`) are displayed.
189
+ * Scrolling within `micl-dialog__content` applies elevation to the header. This effect relies on scroll-driven animations.
190
+
191
+ ## Accessibility
119
192
 
120
- - When not in full-screen mode (e.g., on wider screens), the `micl-dialog__fullscreen` buttons are hidden, and the standard dialog actions (`micl-dialog__actions`) are visible.
193
+ * **Prefer a modal dialog.** A dialog opened via `command="show-modal"` (or `dialog.showModal()`) correctly utilizes the top layer, traps focus, and hides background content from screen readers. A `popover` dialog does none of this. Reserve the light-dismiss variant strictly for short, non-critical context.
194
+ * **Always label the dialog.** Point `aria-labelledby` to the primary heading element. If supporting text is present, link it using `aria-describedby`.
195
+ * **Use `role="alertdialog"` exclusively** for dialogs that interrupt the user with urgent messages requiring immediate response.
196
+ * **Maintain keyboard navigation.** `closedby="closerequest"` is the default for a reason. Using `closedby="none"` removes the <kbd>Esc</kbd> key escape route and should only be used when a user *must not* abandon the process (and even then, an explicit close button must remain).
197
+ * **Assign `autofocus`** to the control that should receive focus upon opening, prioritizing the dismissive action over destructive ones. Without it, browsers will focus the dialog container itself.
198
+ * **Provide context for icons.** Give icon-only buttons an `aria-label`, and mark decorative glyphs (like `micl-dialog__icon`) with `aria-hidden="true"`.
199
+ * **Respect motion preferences.** Motion is disabled if the user has requested reduced motion at the OS level; the dialog will appear and disappear instantly.
121
200
 
122
201
  ## Theming
123
- Each dialog can be themed with CSS custom properties that follow the Material Design 3 component-token naming convention. Set them on any appropriate parent element to affect its child dialogs.
202
+
203
+ Dialogs can be themed using CSS custom properties following the Material Design 3 component-token naming convention. Apply them to a parent element to style its child dialogs.
124
204
 
125
205
  | Custom property | Meaning | Default |
126
- |---|---|---|
127
- | `--md-comp-dialog-min-width` | The minimum width of a dialog | `280px` |
128
- | `--md-comp-dialog-max-width` | The maximum width of a dialog | `560px` |
129
- | `--md-comp-dialog-padding` | The inner padding between the dialog's edge and its content | `24px` |
130
- | `--md-comp-dialog-headline-space` | The vertical spacing between the elements in the header | `16px` |
131
- | `--md-comp-dialog-container-color` | The background color of the dialog | `--md-sys-color-surface-container-high` |
132
- | `--md-comp-dialog-container-shape` | The corner rounding of the dialog | `--md-sys-shape-corner-extra-large` |
133
- | `--md-comp-dialog-container-elevation` | The shadow (elevation) of the dialog | `--md-sys-elevation-level3` |
134
- | `--md-comp-dialog-headline-color` | The text color of the headline | `--md-sys-color-on-surface` |
135
- | `--md-comp-dialog-supporting-text-color` | The text color of the supporting text | `--md-sys-color-on-surface-variant` |
136
- | `--md-comp-dialog-icon-color` | The color of the dialog icon | `--md-sys-color-secondary` |
137
- | `--md-comp-dialog-icon-size` | The size of the dialog icon | `--md-sys-icon-size` |
138
- | `--md-comp-full-screen-dialog-container-color` | The background color of a full-screen dialog | `--md-sys-color-surface` |
206
+ | --- | --- | --- |
207
+ | `--md-comp-dialog-min-width` | Minimum width of a dialog | `280px` |
208
+ | `--md-comp-dialog-max-width` | Maximum width of a dialog | `560px` |
209
+ | `--md-comp-dialog-margin` | Minimum padding between the dialog and the viewport edge | `48px` |
210
+ | `--md-comp-dialog-padding` | Inner padding between the dialog edge and its content | `24px` |
211
+ | `--md-comp-dialog-headline-space` | Vertical spacing between header elements | `16px` |
212
+ | `--md-comp-dialog-container-color` | Dialog background color | `--md-sys-color-surface-container-high` |
213
+ | `--md-comp-dialog-container-shape` | Dialog border radius (corner rounding) | `--md-sys-shape-corner-extra-large` |
214
+ | `--md-comp-dialog-container-elevation` | Dialog shadow depth (elevation) | `--md-sys-elevation-level3` |
215
+ | `--md-comp-dialog-content-color` | Text color for the main content area | `--md-sys-color-on-surface` |
216
+ | `--md-comp-dialog-headline-color` | Text color for the headline | `--md-sys-color-on-surface` |
217
+ | `--md-comp-dialog-subhead-color` | Text color for the sub-header | `--md-sys-color-on-surface` |
218
+ | `--md-comp-dialog-supporting-text-color` | Text color for supporting text | `--md-sys-color-on-surface-variant` |
219
+ | `--md-comp-dialog-icon-color` | Color of the header icon | `--md-sys-color-secondary` |
220
+ | `--md-comp-dialog-icon-size` | Size of the header icon | `--md-sys-icon-size` |
221
+ | `--md-comp-dialog-motion-spatial` | The easing function used when the dialog opens. Uses a spring animation that slightly overshoots the final size before settling | `--md-sys-motion-expressive-fast-spatial` |
222
+ | `--md-comp-dialog-motion-duration` | Animation duration for opening | `650ms` |
223
+ | `--md-comp-dialog-motion-duration-reverse` | Animation duration for closing | `350ms` |
224
+ | `--md-comp-full-screen-dialog-container-color` | Background color of a full-screen dialog | `--md-sys-color-surface` |
139
225
 
140
226
  **Example: Changing the dialog padding**
141
227
 
142
228
  ```HTML
143
- <div style="--md-comp-dialog-padding:16px">
229
+ <div style="--md-comp-dialog-padding: 16px;">
144
230
  <dialog class="micl-dialog">
145
231
  </dialog>
146
232
  </div>
233
+
147
234
  ```
148
235
 
149
236
  ## Compatibility
150
- This component uses **popover anchor positioning** to place the dialog in relation to its invoker. This is a modern CSS feature that may not be fully supported in all browsers. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/anchor#browser_compatibility) for details.
237
+
238
+ This component utilizes modern web platform features. Review [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/anchor#browser_compatibility) before deploying it in production environments.
239
+
240
+ * **Open/Close Mechanisms:** Relies on [Invoker commands](https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API) (`command` / `commandfor`) and the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API). To support legacy browsers, you must implement manual `showModal()` JavaScript calls.
241
+ * **Positioning:** [CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/anchor) dynamically places docked dialogs. In unsupported browsers, light-dismiss dialogs will appear centered rather than scaling out from the control element, and docked dialogs will fall back to the center of the screen.
242
+ * **Animations:** Uses [`@starting-style`](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) and [`transition-behavior: allow-discrete`](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-behavior) for open/close transitions. Without these, the dialog appears instantly.
243
+ * **Dynamic Sizing:** [`interpolate-size`](https://developer.mozilla.org/en-US/docs/Web/CSS/interpolate-size) allows the dialog to animate smoothly to its calculated width. In browsers that do not yet support this (e.g., Firefox 156), the dialog still fades and travels, but assumes its final dimensions immediately.
244
+ * **Advanced Styling:** Utilizes [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has), [`:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir), and [relative color syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors) for icon layout, right-to-left placement, and backdrop rendering.
245
+ * **Header Elevation:** Uses [Scroll-driven animations](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline/scroll) to elevate the header when content scrolls. Where unsupported, the header remains flat—a purely cosmetic fallback.
@@ -19,6 +19,7 @@
19
19
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
20
  // SOFTWARE.
21
21
 
22
+ @use '../../foundations/layout/tokens' as layout;
22
23
  @use '../../styles/elevation';
23
24
  @use '../../styles/motion';
24
25
  @use '../../styles/shape';
@@ -32,31 +33,25 @@
32
33
  @include shape.corner('none');
33
34
  @include shape.corner('extra-large');
34
35
 
35
- @include statelayer.token('hover-state-layer-opacity');
36
36
  @include statelayer.token('backdrop-opacity');
37
- @include statelayer.property;
38
37
 
39
38
  @include typography.scale('headline-small');
40
39
  @include typography.scale('title-large');
41
40
  @include typography.scale('title-medium');
42
41
  @include typography.scale('body-medium');
43
42
 
44
- :root {
45
- --_dialog-dir-factor: 1;
46
- }
47
- :dir(rtl) {
48
- --_dialog-dir-factor: -1;
49
- }
50
-
51
43
  dialog.micl-dialog {
52
- --statelayer-color: var(--md-sys-color-primary);
53
- --md-comp-dialog-motion-duration: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
54
- --md-comp-dialog-motion-duration-reverse: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
44
+ --_motion-spatial: var(--md-comp-dialog-motion-spatial, #{motion.$md-sys-motion-expressive-fast-spatial});
45
+ --_motion-duration: var(--md-comp-dialog-motion-duration, #{motion.$md-sys-motion-expressive-slow-spatial-duration});
46
+ --_motion-duration-reverse: var(--md-comp-dialog-motion-duration-reverse, #{motion.$md-sys-motion-expressive-fast-spatial-duration});
55
47
 
56
- --micl-duration: var(--md-comp-dialog-motion-duration-reverse);
57
- --micl-easing: #{motion.$md-sys-motion-easing-emphasized-decelerate};
48
+ --micl-duration: var(--_motion-duration-reverse);
49
+ --micl-spatial: #{motion.$md-sys-motion-easing-emphasized-accelerate};
50
+ --micl-easing: #{motion.$md-sys-motion-easing-emphasized-accelerate};
58
51
 
52
+ --_dialog-dir-factor: 1;
59
53
  --_dialog-background-color: var(--md-comp-dialog-container-color, var(--md-sys-color-surface-container-high));
54
+ --_margin: var(--md-comp-dialog-margin, 48px);
60
55
  --_padding: var(--md-comp-dialog-padding, 24px);
61
56
  --_translate: translate(calc(var(--_dialog-dir-factor, 1) * -50%), -50%);
62
57
 
@@ -64,48 +59,46 @@ dialog.micl-dialog {
64
59
  display: none;
65
60
  flex-direction: column;
66
61
  min-inline-size: var(--md-comp-dialog-min-width, 280px);
67
- max-inline-size: var(--md-comp-dialog-max-width, 560px);
68
- max-block-size: 100vh;
62
+ max-inline-size: min(var(--md-comp-dialog-max-width, 560px), calc(100% - var(--_margin)));
63
+ max-block-size: calc(100dvb - var(--_margin));
69
64
  position-anchor: auto;
70
- inset-block-start: anchor(start);
71
- inset-inline-start: anchor(start);
65
+ inset-block: anchor(start) auto;
66
+ inset-inline: anchor(start) auto;
72
67
  transform: var(--_translate) scale(50%);
73
68
  padding: 0;
74
69
  margin: 0;
75
70
  outline: none;
76
71
  border: none;
77
72
  border-radius: var(--md-comp-dialog-container-shape, var(--md-sys-shape-corner-extra-large));
73
+ color: var(--md-comp-dialog-content-color, var(--md-sys-color-on-surface));
78
74
  background-color: var(--_dialog-background-color);
79
- background-image: linear-gradient(rgb(from var(--statelayer-color) r g b / var(--statelayer-opacity)));
80
- background-repeat: no-repeat;
81
75
  box-shadow: var(--md-comp-dialog-container-elevation, var(--md-sys-elevation-level3));
82
76
  opacity: 0;
83
77
  overflow: hidden;
84
78
  interpolate-size: allow-keywords;
85
79
  transition:
86
- inset-block-start var(--micl-duration) linear,
87
- inset-inline-start var(--micl-duration) linear,
88
- inline-size var(--micl-duration) linear,
89
- block-size var(--micl-duration) linear,
90
- transform var(--micl-duration) linear,
91
- opacity var(--micl-duration) var(--micl-easing),
80
+ inset-block-start var(--micl-duration) var(--micl-easing),
81
+ inset-inline-start var(--micl-duration) var(--micl-easing),
82
+ inline-size var(--micl-duration) var(--micl-spatial),
83
+ block-size var(--micl-duration) var(--micl-spatial),
84
+ transform var(--micl-duration) var(--micl-spatial),
85
+ opacity var(--micl-duration) linear,
92
86
  overlay var(--micl-duration) linear allow-discrete,
93
- display var(--micl-duration) linear allow-discrete,
94
- --statelayer-opacity var(--md-comp-dialog-motion-duration) linear;
87
+ display var(--micl-duration) linear allow-discrete;
95
88
 
96
89
  @starting-style {
97
90
  block-size: fit-content;
98
91
  inline-size: fit-content;
99
- inset-block-start: anchor(start);
100
- inset-inline-start: anchor(start);
101
- opacity: 0;
102
- transform: var(--_translate) scale(50%);
103
92
  }
104
93
 
94
+ &:dir(rtl) {
95
+ --_dialog-dir-factor: -1;
96
+ }
105
97
  &:popover-open,
106
98
  &[open] {
107
- --micl-duration: var(--md-comp-dialog-motion-duration);
108
- --micl-easing: #{motion.$md-sys-motion-easing-emphasized-accelerate};
99
+ --micl-duration: var(--_motion-duration);
100
+ --micl-spatial: var(--_motion-spatial);
101
+ --micl-easing: #{motion.$md-sys-motion-easing-emphasized};
109
102
 
110
103
  display: flex;
111
104
  inset-block-start: 50%;
@@ -128,22 +121,14 @@ dialog.micl-dialog {
128
121
  inset-block-start: 50%;
129
122
  inset-inline-start: 50%;
130
123
  }
131
-
132
- &:popover-open,
133
- &[open] {
134
- @starting-style {
135
- inset-block-start: 50%;
136
- inset-inline-start: 50%;
137
- }
138
- }
139
124
  }
140
125
 
141
126
  &::backdrop {
142
127
  background-color: rgba(0, 0, 0, 0);
143
128
  transition:
144
- background-color var(--md-comp-dialog-motion-duration) linear,
145
- overlay var(--md-comp-dialog-motion-duration) linear allow-discrete,
146
- display var(--md-comp-dialog-motion-duration) linear allow-discrete;
129
+ background-color var(--micl-duration) linear,
130
+ overlay var(--micl-duration) linear allow-discrete,
131
+ display var(--micl-duration) linear allow-discrete;
147
132
  }
148
133
  &[open]::backdrop {
149
134
  background-color: rgba(0, 0, 0, var(--md-sys-state-backdrop-opacity, 32%));
@@ -157,7 +142,10 @@ dialog.micl-dialog {
157
142
  inset-block: anchor(end) auto;
158
143
  inset-inline: anchor(start) auto;
159
144
  transform: none;
160
- transition: none;
145
+ transition:
146
+ opacity var(--micl-duration) linear,
147
+ overlay var(--micl-duration) linear allow-discrete,
148
+ display var(--micl-duration) linear allow-discrete;
161
149
  position-try-fallbacks: flip-block;
162
150
  }
163
151
 
@@ -173,6 +161,7 @@ dialog.micl-dialog {
173
161
 
174
162
  &:has(> .micl-dialog__icon) {
175
163
  align-items: center;
164
+ text-align: center;
176
165
  }
177
166
  .micl-dialog__icon {
178
167
  --_icon-size: var(--md-comp-dialog-icon-size, var(--md-sys-icon-size, 24px));
@@ -192,11 +181,10 @@ dialog.micl-dialog {
192
181
  .micl-dialog__subhead {
193
182
  @include typography.title-medium;
194
183
 
195
- padding-inline: var(--_padding);
196
184
  overflow: hidden;
197
185
  text-overflow: ellipsis;
198
186
  white-space: nowrap;
199
- color: var(--md-sys-color-on-surface)
187
+ color: var(--md-comp-dialog-subhead-color, var(--md-sys-color-on-surface));
200
188
  }
201
189
  &:has(+ .micl-dialog__actions) {
202
190
  padding-block-end: 0;
@@ -218,21 +206,22 @@ dialog.micl-dialog {
218
206
  justify-content: flex-end;
219
207
  column-gap: 8px;
220
208
  padding: var(--_padding);
221
- opacity: 1;
222
- transition:
223
- opacity var(--md-comp-dialog-motion-duration) linear allow-discrete,
224
- overlay var(--md-comp-dialog-motion-duration) linear allow-discrete,
225
- display var(--md-comp-dialog-motion-duration) linear allow-discrete;
209
+ }
210
+
211
+ @media (prefers-reduced-motion: reduce) {
212
+ --_motion-duration: 0ms;
213
+ --_motion-duration-reverse: 0ms;
226
214
  }
227
215
  }
228
216
 
229
217
  dialog.micl-dialog.micl-dialog--fullscreen {
230
- @media (max-width: 560px) {
218
+ @media (max-width: layout.$md-sys-breakpoint-compact-max) {
231
219
  --_dialog-background-color: var(--md-comp-full-screen-dialog-container-color, var(--md-sys-color-surface));
232
220
 
233
- inline-size: 100vw;
234
- block-size: 100vh;
235
- max-inline-size: 100vw;
221
+ inline-size: 100%;
222
+ block-size: 100dvb;
223
+ max-inline-size: 100%;
224
+ max-block-size: 100dvb;
236
225
  border-radius: var(--md-sys-shape-corner-none, 0px);
237
226
  box-shadow: var(--md-sys-elevation-level0);
238
227
  timeline-scope: --headlineTimeline;
@@ -243,6 +232,7 @@ dialog.micl-dialog.micl-dialog--fullscreen {
243
232
  block-size: 56px;
244
233
  flex-direction: row;
245
234
  align-items: center;
235
+ text-align: start;
246
236
  gap: 8px;
247
237
  padding-block: 4px;
248
238
  padding-inline: 8px 16px;
@@ -262,26 +252,24 @@ dialog.micl-dialog.micl-dialog--fullscreen {
262
252
  white-space: nowrap;
263
253
  }
264
254
  .micl-dialog__icon {
265
- display:none;
255
+ display: none;
266
256
  }
267
257
  }
268
258
  .micl-dialog__content {
269
259
  scroll-timeline: --headlineTimeline block;
270
- scroll-timeline: --headlineTimeline vertical;
271
260
  }
272
261
  .micl-dialog__actions {
273
262
  display: none;
274
- opacity: 0;
275
263
  }
276
264
  }
277
- @media (min-width: 561px) {
265
+ @media (min-width: layout.$md-sys-breakpoint-medium-min) {
278
266
  .micl-dialog__fullscreen {
279
267
  display: none;
280
268
  }
281
269
  }
282
270
  }
283
271
 
284
- @media (max-width: 560px) {
272
+ @media (max-width: layout.$md-sys-breakpoint-compact-max) {
285
273
  body:has(dialog.micl-dialog.micl-dialog--fullscreen:popover-open) {
286
274
  overflow-y: hidden;
287
275
  }
@@ -65,8 +65,8 @@ $iconbuttons: ':is([class*="micl-iconbutton-standard-"], [class*="micl-iconbutto
65
65
  }
66
66
 
67
67
  #{$iconbuttons} {
68
- --md-comp-icon-button-motion-effects: #{motion.$md-sys-motion-expressive-fast-spatial};
69
- --md-comp-icon-button-motion-duration: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
68
+ --_motion-effects: var(--md-comp-icon-button-motion-effects, #{motion.$md-sys-motion-expressive-fast-spatial});
69
+ --_motion-duration: var(--md-comp-icon-button-motion-duration, #{motion.$md-sys-motion-expressive-fast-spatial-duration});
70
70
  --micl-ripple: 1;
71
71
 
72
72
  box-sizing: border-box;
@@ -96,10 +96,10 @@ $iconbuttons: ':is([class*="micl-iconbutton-standard-"], [class*="micl-iconbutto
96
96
  font-variation-settings: 'FILL' 0;
97
97
  cursor: pointer;
98
98
  transition:
99
- border-radius var(--md-comp-icon-button-motion-duration) var(--md-comp-icon-button-motion-effects),
100
- font-variation-settings var(--md-comp-icon-button-motion-duration) linear,
99
+ border-radius var(--_motion-duration) var(--_motion-effects),
100
+ font-variation-settings var(--_motion-duration) linear,
101
101
  background-size 0ms,
102
- --statelayer-opacity var(--md-comp-icon-button-motion-duration) linear;
102
+ --statelayer-opacity var(--_motion-duration) linear;
103
103
 
104
104
  &:disabled {
105
105
  box-shadow: var(--md-sys-elevation-level0);
@@ -165,8 +165,8 @@ $unselected: ':not(:checked, :has(input[type=checkbox]:checked), #{$open-accordi
165
165
  }
166
166
 
167
167
  .micl-list {
168
- --md-comp-list-motion-effects: #{motion.$md-sys-motion-expressive-fast-spatial};
169
- --md-comp-list-motion-duration: #{motion.$md-sys-motion-expressive-default-effects-duration};
168
+ --_list-motion-effects: var(--md-comp-list-motion-effects, #{motion.$md-sys-motion-expressive-fast-spatial});
169
+ --_list-motion-duration: var(--md-comp-list-motion-duration, #{motion.$md-sys-motion-expressive-default-effects-duration});
170
170
  --md-comp-accordion-motion-spatial: #{motion.$md-sys-motion-expressive-default-spatial};
171
171
  --md-comp-accordion-motion-duration: #{motion.$md-sys-motion-expressive-default-spatial-duration};
172
172
  --md-comp-divider-space: 0.5px;
@@ -269,9 +269,9 @@ $unselected: ':not(:checked, :has(input[type=checkbox]:checked), #{$open-accordi
269
269
  background-size: 0%, 100%;
270
270
  list-style: none;
271
271
  transition:
272
- border-radius var(--md-comp-list-motion-duration) var(--md-comp-list-motion-effects),
272
+ border-radius var(--_list-motion-duration) var(--_list-motion-effects),
273
273
  background-size 0ms,
274
- --statelayer-opacity var(--md-comp-list-motion-duration) linear;
274
+ --statelayer-opacity var(--_list-motion-duration) linear;
275
275
  -webkit-tap-highlight-color: transparent;
276
276
 
277
277
  &:first-of-type {
@@ -407,11 +407,11 @@ $unselected: ':not(:checked, :has(input[type=checkbox]:checked), #{$open-accordi
407
407
  font-size: var(--md-comp-list-item-leading-icon-expressive-size, 20px);
408
408
  font-variation-settings: 'FILL' 0;
409
409
  color: var(--_leading-icon-color);
410
- transition: font-variation-settings var(--md-comp-list-motion-duration) linear;
410
+ transition: font-variation-settings var(--_list-motion-duration) linear;
411
411
 
412
412
  &.micl-list-item__icon--expander {
413
413
  transform: rotate(0deg);
414
- transition: transform var(--md-comp-list-motion-duration) linear;
414
+ transition: transform var(--_list-motion-duration) linear;
415
415
  }
416
416
  }
417
417
  .micl-list-item__text ~ .micl-list-item__icon {