material-inspired-component-library 9.1.0 → 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.
- package/README.md +2 -1
- package/components/appbar/index.scss +1 -1
- package/components/badge/README.md +43 -12
- package/components/badge/index.scss +15 -21
- package/components/bottomsheet/README.md +53 -22
- package/components/bottomsheet/index.scss +62 -45
- package/components/button/_shared.scss +9 -9
- package/components/card/README.md +55 -25
- package/components/card/index.scss +57 -38
- package/components/checkbox/README.md +97 -28
- package/components/checkbox/index.scss +39 -29
- package/components/chip/index.scss +6 -6
- package/components/datepicker/README.md +184 -59
- package/components/datepicker/index.scss +85 -21
- package/components/dialog/README.md +151 -56
- package/components/dialog/index.scss +50 -62
- package/components/iconbutton/index.scss +8 -8
- package/components/list/index.scss +15 -11
- package/components/menu/index.scss +13 -13
- package/components/navigationbar/index.scss +7 -1
- package/components/navigationrail/index.scss +27 -22
- package/components/radio/README.md +52 -12
- package/components/radio/index.scss +59 -41
- package/components/select/index.scss +3 -3
- package/components/sidesheet/README.md +90 -37
- package/components/sidesheet/index.scss +51 -41
- package/components/slider/README.md +35 -9
- package/components/slider/index.scss +182 -153
- package/components/snackbar/README.md +43 -25
- package/components/snackbar/index.scss +22 -27
- package/components/stepper/index.scss +5 -5
- package/components/switch/README.md +32 -4
- package/components/switch/index.scss +57 -52
- package/components/timepicker/README.md +131 -55
- package/components/timepicker/index.scss +47 -26
- package/dist/appbar.css +1 -1
- package/dist/badge.css +1 -1
- package/dist/bottomsheet.css +1 -1
- package/dist/bottomsheet.js +1 -1
- package/dist/button.css +1 -1
- package/dist/card.css +1 -1
- package/dist/checkbox.css +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/chip.css +1 -1
- package/dist/components/checkbox/index.d.ts +1 -0
- package/dist/datepicker.css +1 -1
- package/dist/datepicker.js +1 -1
- package/dist/dialog.css +1 -1
- package/dist/foundations/form/index.js +1 -1
- package/dist/iconbutton.css +1 -1
- package/dist/layout.css +1 -1
- package/dist/list.css +1 -1
- package/dist/menu.css +1 -1
- package/dist/micl.css +1 -1
- package/dist/micl.js +1 -1
- package/dist/navigationbar.css +1 -1
- package/dist/navigationrail.css +1 -1
- package/dist/radio.css +1 -1
- package/dist/select.css +1 -1
- package/dist/sidesheet.css +1 -1
- package/dist/slider.css +1 -1
- package/dist/slider.js +1 -1
- package/dist/snackbar.css +1 -1
- package/dist/snackbar.js +1 -1
- package/dist/stepper.css +1 -1
- package/dist/stepper.js +1 -1
- package/dist/switch.css +1 -1
- package/dist/timepicker.css +1 -1
- package/dist/timepicker.js +1 -1
- package/foundations/layout/_tokens.scss +55 -0
- package/foundations/layout/index.scss +5 -35
- package/package.json +2 -2
- package/{components/checkbox/_label.scss → styles/togglelabel.scss} +11 -3
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
@include statelayer.token('focus-state-layer-opacity');
|
|
32
32
|
@include statelayer.token('pressed-state-layer-opacity');
|
|
33
33
|
@include statelayer.token('disabled-state-layer-opacity');
|
|
34
|
+
@include statelayer.token('focus-indicator-thickness');
|
|
34
35
|
@include statelayer.property;
|
|
35
36
|
|
|
36
37
|
@include typography.scale('headline-large');
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
%view-transition-base {
|
|
42
43
|
block-size: fit-content;
|
|
43
44
|
opacity: 1;
|
|
44
|
-
transition-duration: var(--
|
|
45
|
+
transition-duration: var(--_datepicker-motion-spatial-duration);
|
|
45
46
|
|
|
46
47
|
&.micl-datepicker__view-hidden {
|
|
47
48
|
block-size: 0;
|
|
@@ -65,12 +66,12 @@
|
|
|
65
66
|
background-repeat: no-repeat;
|
|
66
67
|
background-size: 100%;
|
|
67
68
|
background-clip: content-box;
|
|
68
|
-
transition: --statelayer-opacity var(--
|
|
69
|
+
transition: --statelayer-opacity var(--_datepicker-button-motion-duration) linear;
|
|
69
70
|
|
|
70
71
|
&:hover {
|
|
71
72
|
--statelayer-opacity: var(--md-sys-state-hover-state-layer-opacity);
|
|
72
73
|
}
|
|
73
|
-
&:focus-visible {
|
|
74
|
+
&:is(:focus-visible, :has(input:focus-visible)) {
|
|
74
75
|
--statelayer-opacity: var(--md-sys-state-focus-state-layer-opacity);
|
|
75
76
|
}
|
|
76
77
|
&:active {
|
|
@@ -78,15 +79,20 @@
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
%focus-indicator {
|
|
83
|
+
outline: var(--md-sys-state-focus-indicator-thickness, 3px) solid var(--md-comp-date-picker-focus-indicator-color, var(--md-sys-color-secondary));
|
|
84
|
+
outline-offset: -1px;
|
|
85
|
+
}
|
|
86
|
+
|
|
81
87
|
dialog.micl-dialog.micl-datepicker {
|
|
82
|
-
--md-comp-date-picker-motion-spatial
|
|
83
|
-
--md-comp-date-picker-motion-spatial-duration
|
|
84
|
-
--md-comp-date-picker-motion-duration
|
|
85
|
-
--md-comp-date-picker-motion-duration-reverse
|
|
86
|
-
--md-comp-date-picker-button-motion-duration
|
|
88
|
+
--_datepicker-motion-spatial: var(--md-comp-date-picker-motion-spatial, #{motion.$md-sys-motion-expressive-fast-effects});
|
|
89
|
+
--_datepicker-motion-spatial-duration: var(--md-comp-date-picker-motion-spatial-duration, #{motion.$md-sys-motion-expressive-default-spatial-duration});
|
|
90
|
+
--_datepicker-motion-duration: var(--md-comp-date-picker-motion-duration, #{motion.$md-sys-motion-expressive-default-effects-duration});
|
|
91
|
+
--_datepicker-motion-duration-reverse: var(--md-comp-date-picker-motion-duration-reverse, #{motion.$md-sys-motion-expressive-fast-effects-duration});
|
|
92
|
+
--_datepicker-button-motion-duration: var(--md-comp-date-picker-button-motion-duration, #{motion.$md-sys-motion-expressive-fast-spatial-duration});
|
|
87
93
|
|
|
88
94
|
// Internal alias: the target size is referenced from many descendants.
|
|
89
|
-
--_target: var(--md-sys-target-size, 48px);
|
|
95
|
+
--_target: var(--md-sys-state-target-size, 48px);
|
|
90
96
|
|
|
91
97
|
inline-size: var(--md-comp-date-picker-modal-container-width, 360px);
|
|
92
98
|
|
|
@@ -153,7 +159,7 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
153
159
|
min-block-size: 330px;
|
|
154
160
|
padding-inline: 0;
|
|
155
161
|
overflow: hidden auto;
|
|
156
|
-
transition: min-block-size var(--
|
|
162
|
+
transition: min-block-size var(--_datepicker-motion-spatial-duration) var(--_datepicker-motion-spatial);
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
.micl-dialog__actions {
|
|
@@ -179,6 +185,13 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
179
185
|
margin-block: -2px;
|
|
180
186
|
}
|
|
181
187
|
}
|
|
188
|
+
|
|
189
|
+
@media (prefers-reduced-motion: reduce) {
|
|
190
|
+
--_datepicker-motion-spatial-duration: 0ms;
|
|
191
|
+
--_datepicker-motion-duration: 0ms;
|
|
192
|
+
--_datepicker-motion-duration-reverse: 0ms;
|
|
193
|
+
--_datepicker-button-motion-duration: 0ms;
|
|
194
|
+
}
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
.micl-datepicker__inputmode {
|
|
@@ -275,10 +288,10 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
275
288
|
overflow: hidden;
|
|
276
289
|
transform: translateX(0);
|
|
277
290
|
transition:
|
|
278
|
-
transform var(--
|
|
279
|
-
opacity var(--
|
|
280
|
-
block-size var(--
|
|
281
|
-
display var(--
|
|
291
|
+
transform var(--_datepicker-motion-spatial-duration) var(--_datepicker-motion-spatial),
|
|
292
|
+
opacity var(--_datepicker-motion-duration-reverse) linear,
|
|
293
|
+
block-size var(--_datepicker-motion-duration-reverse) linear allow-discrete,
|
|
294
|
+
display var(--_datepicker-motion-duration-reverse) linear allow-discrete;
|
|
282
295
|
|
|
283
296
|
&.micl-datepicker__view-hidden {
|
|
284
297
|
block-size: 0%;
|
|
@@ -293,6 +306,38 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
293
306
|
&.micl-moveleft {
|
|
294
307
|
transform: translateX(-50%);
|
|
295
308
|
}
|
|
309
|
+
// The flex row is laid out from the inline start, so the off-screen half of
|
|
310
|
+
// the strip sits on the opposite side in a right-to-left context.
|
|
311
|
+
&:dir(rtl) {
|
|
312
|
+
&.micl-startright,
|
|
313
|
+
&.micl-moveleft {
|
|
314
|
+
transform: translateX(50%);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
&.micl-datepicker__nudge-min,
|
|
318
|
+
&.micl-datepicker__nudge-max {
|
|
319
|
+
animation: micl-datepicker-nudge var(--_datepicker-motion-duration) ease-in-out;
|
|
320
|
+
}
|
|
321
|
+
&.micl-datepicker__nudge-min {
|
|
322
|
+
--_nudge: 8px;
|
|
323
|
+
}
|
|
324
|
+
&.micl-datepicker__nudge-max {
|
|
325
|
+
--_nudge: -8px;
|
|
326
|
+
}
|
|
327
|
+
&:dir(rtl) {
|
|
328
|
+
&.micl-datepicker__nudge-min {
|
|
329
|
+
--_nudge: -8px;
|
|
330
|
+
}
|
|
331
|
+
&.micl-datepicker__nudge-max {
|
|
332
|
+
--_nudge: 8px;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@keyframes micl-datepicker-nudge {
|
|
338
|
+
50% {
|
|
339
|
+
translate: var(--_nudge, 0) 0;
|
|
340
|
+
}
|
|
296
341
|
}
|
|
297
342
|
.micl-datepicker__calendar {
|
|
298
343
|
flex-shrink: 0;
|
|
@@ -304,12 +349,16 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
304
349
|
|
|
305
350
|
box-sizing: border-box;
|
|
306
351
|
display: grid;
|
|
307
|
-
grid-template-columns: repeat(7, var(--_target));
|
|
308
352
|
grid-template-rows: 40px repeat(6, var(--_target));
|
|
309
353
|
block-size: 100%;
|
|
310
354
|
inline-size: 100%;
|
|
311
355
|
padding-inline: 12px;
|
|
312
356
|
|
|
357
|
+
> * {
|
|
358
|
+
display: grid;
|
|
359
|
+
grid-template-columns: repeat(7, var(--_target));
|
|
360
|
+
}
|
|
361
|
+
|
|
313
362
|
span, time {
|
|
314
363
|
box-sizing: border-box;
|
|
315
364
|
display: inline-flex;
|
|
@@ -368,12 +417,16 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
368
417
|
&:is(.micl-datepicker__today, .micl-datepicker__selected):is(:hover, :focus-visible, :active) {
|
|
369
418
|
--statelayer-color: currentColor;
|
|
370
419
|
}
|
|
371
|
-
&.micl-
|
|
420
|
+
&.micl-datepicker__disabled {
|
|
372
421
|
color: rgb(from var(--md-sys-color-on-surface) r g b / var(--md-sys-state-disabled-state-layer-opacity));
|
|
422
|
+
pointer-events: none;
|
|
373
423
|
}
|
|
374
|
-
&:not(.micl-
|
|
424
|
+
&:not(.micl-datepicker__disabled) {
|
|
375
425
|
cursor: pointer;
|
|
376
426
|
}
|
|
427
|
+
&:focus-visible {
|
|
428
|
+
@extend %focus-indicator;
|
|
429
|
+
}
|
|
377
430
|
}
|
|
378
431
|
}
|
|
379
432
|
|
|
@@ -403,15 +456,23 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
403
456
|
|
|
404
457
|
span {
|
|
405
458
|
visibility: hidden;
|
|
406
|
-
color:
|
|
459
|
+
color: inherit;
|
|
407
460
|
}
|
|
408
461
|
&:has(input:checked) {
|
|
409
|
-
|
|
462
|
+
color: var(--md-comp-date-picker-selection-month-selected-label-text-color, var(--md-sys-color-on-surface));
|
|
463
|
+
background-color: var(--md-comp-date-picker-selection-month-selected-container-color, var(--md-sys-color-surface-variant));
|
|
410
464
|
|
|
411
465
|
span {
|
|
412
466
|
visibility: visible;
|
|
413
467
|
}
|
|
414
468
|
}
|
|
469
|
+
&:has(input:disabled) {
|
|
470
|
+
color: rgb(from var(--md-sys-color-on-surface) r g b / var(--md-sys-state-disabled-state-layer-opacity));
|
|
471
|
+
pointer-events: none;
|
|
472
|
+
}
|
|
473
|
+
&:has(input:focus-visible) {
|
|
474
|
+
@extend %focus-indicator;
|
|
475
|
+
}
|
|
415
476
|
}
|
|
416
477
|
}
|
|
417
478
|
.micl-datepicker__years {
|
|
@@ -444,10 +505,13 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
444
505
|
background-color: var(--md-comp-date-picker-selection-year-selected-container-color, var(--md-sys-color-primary));
|
|
445
506
|
|
|
446
507
|
// The state layer of the selected year follows its label color.
|
|
447
|
-
&:is(:hover, :focus-visible
|
|
508
|
+
&:is(:hover, :active, :has(input:focus-visible)) {
|
|
448
509
|
--statelayer-color: currentColor;
|
|
449
510
|
}
|
|
450
511
|
}
|
|
512
|
+
&:has(input:focus-visible) {
|
|
513
|
+
@extend %focus-indicator;
|
|
514
|
+
}
|
|
451
515
|
}
|
|
452
516
|
}
|
|
453
517
|
:is(.micl-datepicker__months, .micl-datepicker__years) input[type=radio] {
|
|
@@ -475,6 +539,6 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
475
539
|
}
|
|
476
540
|
}
|
|
477
541
|
|
|
478
|
-
.micl-
|
|
542
|
+
.micl-datepicker__no-transition {
|
|
479
543
|
transition: none !important;
|
|
480
544
|
}
|
|
@@ -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
|
-
|
|
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"
|
|
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"
|
|
18
|
-
```
|
|
22
|
+
<button type="button" class="micl-button-filled-m" command="show-modal" commandfor="mydialog">Open Basic Dialog</button>
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
```
|
|
21
25
|
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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"
|
|
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">
|
|
54
|
-
<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-
|
|
57
|
-
|
|
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
|
-
|
|
91
|
+
## Variants
|
|
92
|
+
|
|
93
|
+
### Alert dialog
|
|
68
94
|
|
|
69
|
-
|
|
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
|
|
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"
|
|
110
|
+
<button type="button" class="micl-button-outlined-m" command="show-modal" commandfor="mydialog">Delete images</button>
|
|
111
|
+
|
|
76
112
|
```
|
|
77
113
|
|
|
78
|
-
###
|
|
79
|
-
A dialog typically consists of three main sections to organize its content:
|
|
114
|
+
### Light-dismiss dialog
|
|
80
115
|
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
+
### Docked dialog
|
|
92
142
|
|
|
93
|
-
|
|
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
|
-
|
|
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="
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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` |
|
|
128
|
-
| `--md-comp-dialog-max-width` |
|
|
129
|
-
| `--md-comp-dialog-
|
|
130
|
-
| `--md-comp-dialog-
|
|
131
|
-
| `--md-comp-dialog-
|
|
132
|
-
| `--md-comp-dialog-container-
|
|
133
|
-
| `--md-comp-dialog-container-
|
|
134
|
-
| `--md-comp-dialog-
|
|
135
|
-
| `--md-comp-dialog-
|
|
136
|
-
| `--md-comp-dialog-
|
|
137
|
-
| `--md-comp-dialog-
|
|
138
|
-
| `--md-comp-
|
|
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
|
-
|
|
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.
|