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.
Files changed (73) hide show
  1. package/README.md +2 -1
  2. package/components/appbar/index.scss +1 -1
  3. package/components/badge/README.md +43 -12
  4. package/components/badge/index.scss +15 -21
  5. package/components/bottomsheet/README.md +53 -22
  6. package/components/bottomsheet/index.scss +62 -45
  7. package/components/button/_shared.scss +9 -9
  8. package/components/card/README.md +55 -25
  9. package/components/card/index.scss +57 -38
  10. package/components/checkbox/README.md +97 -28
  11. package/components/checkbox/index.scss +39 -29
  12. package/components/chip/index.scss +6 -6
  13. package/components/datepicker/README.md +184 -59
  14. package/components/datepicker/index.scss +85 -21
  15. package/components/dialog/README.md +151 -56
  16. package/components/dialog/index.scss +50 -62
  17. package/components/iconbutton/index.scss +8 -8
  18. package/components/list/index.scss +15 -11
  19. package/components/menu/index.scss +13 -13
  20. package/components/navigationbar/index.scss +7 -1
  21. package/components/navigationrail/index.scss +27 -22
  22. package/components/radio/README.md +52 -12
  23. package/components/radio/index.scss +59 -41
  24. package/components/select/index.scss +3 -3
  25. package/components/sidesheet/README.md +90 -37
  26. package/components/sidesheet/index.scss +51 -41
  27. package/components/slider/README.md +35 -9
  28. package/components/slider/index.scss +182 -153
  29. package/components/snackbar/README.md +43 -25
  30. package/components/snackbar/index.scss +22 -27
  31. package/components/stepper/index.scss +5 -5
  32. package/components/switch/README.md +32 -4
  33. package/components/switch/index.scss +57 -52
  34. package/components/timepicker/README.md +131 -55
  35. package/components/timepicker/index.scss +47 -26
  36. package/dist/appbar.css +1 -1
  37. package/dist/badge.css +1 -1
  38. package/dist/bottomsheet.css +1 -1
  39. package/dist/bottomsheet.js +1 -1
  40. package/dist/button.css +1 -1
  41. package/dist/card.css +1 -1
  42. package/dist/checkbox.css +1 -1
  43. package/dist/checkbox.js +1 -1
  44. package/dist/chip.css +1 -1
  45. package/dist/components/checkbox/index.d.ts +1 -0
  46. package/dist/datepicker.css +1 -1
  47. package/dist/datepicker.js +1 -1
  48. package/dist/dialog.css +1 -1
  49. package/dist/foundations/form/index.js +1 -1
  50. package/dist/iconbutton.css +1 -1
  51. package/dist/layout.css +1 -1
  52. package/dist/list.css +1 -1
  53. package/dist/menu.css +1 -1
  54. package/dist/micl.css +1 -1
  55. package/dist/micl.js +1 -1
  56. package/dist/navigationbar.css +1 -1
  57. package/dist/navigationrail.css +1 -1
  58. package/dist/radio.css +1 -1
  59. package/dist/select.css +1 -1
  60. package/dist/sidesheet.css +1 -1
  61. package/dist/slider.css +1 -1
  62. package/dist/slider.js +1 -1
  63. package/dist/snackbar.css +1 -1
  64. package/dist/snackbar.js +1 -1
  65. package/dist/stepper.css +1 -1
  66. package/dist/stepper.js +1 -1
  67. package/dist/switch.css +1 -1
  68. package/dist/timepicker.css +1 -1
  69. package/dist/timepicker.js +1 -1
  70. package/foundations/layout/_tokens.scss +55 -0
  71. package/foundations/layout/index.scss +5 -35
  72. package/package.json +2 -2
  73. package/{components/checkbox/_label.scss → styles/togglelabel.scss} +11 -3
@@ -22,7 +22,7 @@
22
22
  @use '../../styles/motion';
23
23
  @use '../../styles/shape';
24
24
  @use '../../styles/statelayer';
25
- @use '../checkbox/label';
25
+ @use '../../styles/togglelabel';
26
26
 
27
27
  @include shape.corner('full');
28
28
 
@@ -32,6 +32,7 @@
32
32
  @include statelayer.token('pressed-state-layer-opacity');
33
33
  @include statelayer.token('disabled-state-layer-opacity');
34
34
  @include statelayer.token('focus-indicator-thickness');
35
+ @include statelayer.token('focus-indicator-outer-offset');
35
36
  @include statelayer.token('ripple-opacity-factor');
36
37
  @include statelayer.token('ripple-duration');
37
38
  @include statelayer.property;
@@ -40,32 +41,37 @@
40
41
  $radio: 'input[type=radio].micl-radio';
41
42
 
42
43
  #{$radio} {
43
- --md-comp-radio-button-motion-effects: #{motion.$md-sys-motion-expressive-slow-effects};
44
- --md-comp-radio-button-motion-duration: #{motion.$md-sys-motion-expressive-slow-effects-duration};
45
- --md-comp-radio-button-motion-duration-reverse: #{motion.$md-sys-motion-expressive-default-effects-duration};
44
+ --_motion-effects: var(--md-comp-radio-button-motion-effects, #{motion.$md-sys-motion-expressive-slow-effects});
45
+ --_motion-duration: var(--md-comp-radio-button-motion-duration, #{motion.$md-sys-motion-expressive-slow-effects-duration});
46
+ --_motion-duration-reverse: var(--md-comp-radio-button-motion-duration-reverse, #{motion.$md-sys-motion-expressive-default-effects-duration});
46
47
 
47
48
  --_size: var(--md-comp-radio-button-icon-size, 20px);
48
49
  --_outline-width: var(--md-comp-radio-button-outline-width, 2px);
50
+ --_layer: max(var(--md-sys-state-layer-size, 40px), calc(var(--_size) + 20px));
51
+ --_target: max(var(--md-sys-state-target-size, 48px), calc(var(--_layer) + 8px));
52
+ --statelayer-color: var(--md-sys-color-on-surface);
49
53
 
50
54
  appearance: none;
51
55
  box-sizing: border-box;
52
56
  position: relative;
53
- inline-size: var(--md-sys-target-size, 48px);
54
- min-inline-size: var(--md-sys-target-size, 48px);
55
- block-size: var(--md-sys-target-size, 48px);
56
- min-block-size: var(--md-sys-target-size, 48px);
57
+ inline-size: var(--_target);
58
+ min-inline-size: var(--_target);
59
+ block-size: var(--_target);
60
+ min-block-size: var(--_target);
57
61
  margin: 0;
58
- border: calc((var(--md-sys-target-size, 48px) - var(--md-sys-state-layer-size, 40px)) / 2) solid transparent;
62
+ border: calc((var(--_target) - var(--_layer)) / 2) solid transparent;
59
63
  background-clip: content-box;
60
64
  background-color: transparent;
61
65
  -webkit-tap-highlight-color: transparent;
62
66
  border-radius: var(--md-sys-shape-corner-full);
63
- outline-offset: -7px;
67
+ outline-offset: calc(
68
+ var(--md-sys-state-focus-indicator-outer-offset, 2px)
69
+ - ((var(--_target) - var(--_layer)) / 2)
70
+ );
64
71
 
65
- // The ring and the dot in one box: the dot is the primary background clipped to the
66
- // content box, hidden while the padding fills the ring, revealed when the checked
67
- // padding shrinks to reveal a dot of half the icon size.
68
72
  &::after {
73
+ --_outline-color: var(--md-comp-radio-button-unselected-icon-color, var(--md-sys-color-on-surface-variant));
74
+
69
75
  content: "";
70
76
  box-sizing: border-box;
71
77
  position: absolute;
@@ -74,20 +80,25 @@ $radio: 'input[type=radio].micl-radio';
74
80
  inset: 0;
75
81
  margin: auto;
76
82
  padding: calc((var(--_size) / 2) - var(--_outline-width));
77
- border: var(--_outline-width) solid var(--md-comp-radio-button-unselected-icon-color, var(--md-sys-color-on-surface-variant));
83
+ border: var(--_outline-width) solid var(--_outline-color);
78
84
  background-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
79
85
  background-clip: content-box;
80
86
  border-radius: var(--md-sys-shape-corner-full);
81
87
  transition:
82
- padding var(--md-comp-radio-button-motion-duration-reverse) var(--md-comp-radio-button-motion-effects),
83
- border-color var(--md-comp-radio-button-motion-duration-reverse) motion.$md-sys-motion-easing-emphasized;
88
+ padding var(--_motion-duration-reverse) var(--_motion-effects),
89
+ border-color var(--_motion-duration-reverse) motion.$md-sys-motion-easing-emphasized;
84
90
  }
85
- &:checked::after {
86
- border-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
87
- padding: calc(0.15 * var(--_size));
88
- transition:
89
- padding var(--md-comp-radio-button-motion-duration) var(--md-comp-radio-button-motion-effects),
90
- border-color var(--md-comp-radio-button-motion-duration) motion.$md-sys-motion-easing-emphasized;
91
+ &:checked {
92
+ --statelayer-color: var(--md-sys-color-primary);
93
+
94
+ &::after {
95
+ --_outline-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
96
+
97
+ padding: calc((var(--_size) / 4) - var(--_outline-width));
98
+ transition:
99
+ padding var(--_motion-duration) var(--_motion-effects),
100
+ border-color var(--_motion-duration) motion.$md-sys-motion-easing-emphasized;
101
+ }
91
102
  }
92
103
  &:not(:disabled) {
93
104
  --micl-ripple: 1;
@@ -106,33 +117,35 @@ $radio: 'input[type=radio].micl-radio';
106
117
  cursor: pointer;
107
118
  transition:
108
119
  background-size 0ms,
109
- --statelayer-opacity var(--md-comp-radio-button-motion-duration) linear;
120
+ --statelayer-opacity var(--_motion-duration) linear;
110
121
 
111
- &:hover,
112
- &:focus-visible,
113
- &:active {
114
- --statelayer-color: var(--md-sys-color-on-surface);
115
-
116
- &:checked {
117
- --statelayer-color: var(--md-sys-color-primary);
118
- }
119
- &::after {
120
- border-color: var(--md-sys-color-on-surface);
121
- }
122
- &:checked::after {
123
- border-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
124
- }
125
- }
126
122
  &:hover {
127
123
  --statelayer-opacity: var(--md-sys-state-hover-state-layer-opacity, 8%);
124
+
125
+ &:not(:checked)::after {
126
+ --_outline-color: var(--md-sys-color-on-surface);
127
+ }
128
128
  }
129
129
  &:focus-visible {
130
130
  --statelayer-opacity: var(--md-sys-state-focus-state-layer-opacity, 10%);
131
131
 
132
132
  outline: var(--md-sys-state-focus-indicator-thickness, 3px) solid var(--md-sys-color-secondary);
133
+
134
+ &:not(:checked)::after {
135
+ --_outline-color: var(--md-sys-color-on-surface);
136
+ }
133
137
  }
134
138
  &:active {
139
+ --statelayer-color: var(--md-sys-color-on-surface);
135
140
  --statelayer-opacity: var(--md-sys-state-pressed-state-layer-opacity, 10%);
141
+
142
+ &:not(:checked) {
143
+ --statelayer-color: var(--md-sys-color-primary);
144
+
145
+ &::after {
146
+ --_outline-color: var(--md-sys-color-on-surface);
147
+ }
148
+ }
136
149
  }
137
150
  &.micl-rippling {
138
151
  animation: micl-ripple var(--md-sys-state-ripple-duration);
@@ -142,7 +155,7 @@ $radio: 'input[type=radio].micl-radio';
142
155
  opacity: var(--md-sys-state-disabled-state-layer-opacity, 38%);
143
156
 
144
157
  &::after {
145
- border-color: var(--md-sys-color-on-surface);
158
+ --_outline-color: var(--md-sys-color-on-surface);
146
159
  }
147
160
  &:checked::after {
148
161
  background-color: var(--md-sys-color-on-surface);
@@ -151,14 +164,19 @@ $radio: 'input[type=radio].micl-radio';
151
164
  }
152
165
 
153
166
  #{$radio}:not(:disabled) + label,
154
- label:has(+ #{$radio}:not(:disabled)) {
167
+ label:has(+ #{$radio}:not(:disabled)),
168
+ label:has(#{$radio}:not(:disabled)) {
155
169
  @extend %micl-toggle-label-pointer;
156
170
  }
157
171
  #{$radio} + label,
158
- label:has(+ #{$radio}) {
172
+ label:has(+ #{$radio}),
173
+ label:has(#{$radio}) {
159
174
  @extend %micl-toggle-label;
160
175
  }
161
176
  #{$radio}:disabled + label,
162
177
  label:has(+ #{$radio}:disabled) {
163
178
  @extend %micl-toggle-label-disabled;
164
179
  }
180
+ label:has(#{$radio}:disabled) {
181
+ @extend %micl-toggle-label-disabled-text;
182
+ }
@@ -41,8 +41,8 @@ $selects: ':is(.micl-textfield-filled, .micl-textfield-outlined) > select';
41
41
  --_line-height: var(--md-comp-text-field-container-height, 56px);
42
42
  }
43
43
  #{$selects} {
44
- --md-comp-list-motion-effects: #{motion.$md-sys-motion-expressive-fast-spatial};
45
- --md-comp-list-motion-duration: #{motion.$md-sys-motion-expressive-default-effects-duration};
44
+ --_list-motion-effects: var(--md-comp-list-motion-effects, #{motion.$md-sys-motion-expressive-fast-spatial});
45
+ --_list-motion-duration: var(--md-comp-list-motion-duration, #{motion.$md-sys-motion-expressive-default-effects-duration});
46
46
 
47
47
  appearance: base-select;
48
48
  line-height: var(--_line-height);
@@ -168,7 +168,7 @@ $selects: ':is(.micl-textfield-filled, .micl-textfield-outlined) > select';
168
168
  // the list transition is repeated here without it.
169
169
  transition:
170
170
  background-size 0ms,
171
- --statelayer-opacity var(--md-comp-list-motion-duration) linear;
171
+ --statelayer-opacity var(--_list-motion-duration) linear;
172
172
  }
173
173
  }
174
174
  }
@@ -1,5 +1,6 @@
1
1
  # Side sheet
2
- This component implements the [Material Design 3 Expressive Side sheet](https://m3.material.io/components/side-sheets/overview) design. Side sheets provide optional content and actions without interrupting the main content.
2
+
3
+ This component implements the [Material Design 3 Expressive Side sheet](https://m3.material.io/components/side-sheets/overview) specification. Side sheets display optional content and actions without interrupting the user's workflow in the main content area.
3
4
 
4
5
  ## Basic Usage
5
6
 
@@ -14,6 +15,7 @@ To create a standard side sheet, use the `<dialog>` element with the `popover` a
14
15
  type="button"
15
16
  class="micl-iconbutton-standard-s material-symbols-outlined"
16
17
  popovertarget="mysidesheet"
18
+ aria-label="Close"
17
19
  >close</button>
18
20
  </div>
19
21
  <div class="micl-sidesheet__content">
@@ -32,7 +34,7 @@ Import the side sheet styles into your project:
32
34
  @use "material-inspired-component-library/dist/sidesheet";
33
35
  ```
34
36
 
35
- Or import all MICL styles:
37
+ Alternatively, import all MICL styles at once:
36
38
  ```CSS
37
39
  @use "material-inspired-component-library/styles";
38
40
  ```
@@ -41,25 +43,67 @@ Or import all MICL styles:
41
43
  No custom JavaScript is required for the core functionality of the side sheet component.
42
44
 
43
45
  ### Live Demo
44
- A live example of the [Side sheet component](https://henkpb.github.io/micl/sidesheet.html) is available to interact with.
46
+ A live interactive demo of the [Side sheet component](https://henkpb.github.io/micl/sidesheet.html) is available.
47
+
48
+ ## Anatomy
49
+ A **standard** side sheet allows the rest of the page to remain usable by dynamically narrowing the main content area to make room for itself. Its headline and actions container are both optional; a side sheet may consist of nothing but content.
50
+
51
+ | Element | Meaning |
52
+ | --- | --- |
53
+ | `dialog.micl-sidesheet` | The container. Add the `popover` attribute for a standard side sheet; omit it for a modal one. |
54
+ | `.micl-sidesheet__headline` | The header row: holds an optional back button, a heading, and an optional close button. |
55
+ | `.micl-sidesheet__content` | The scrollable body of the side sheet. |
56
+ | `.micl-sidesheet__actions` | An optional row of buttons pinned to the bottom, usually preceded by an `<hr class="micl-divider">`. |
57
+
58
+ A side sheet is **docked**, meaning it spans the full height of the viewport and sits flush against the trailing edge.
59
+
60
+ > [!WARNING]
61
+ > The **standard** side sheet component dynamically applies CSS rules to the `<body>` element to properly resize the main content area when the sheet is open. Overriding these rules may cause unexpected layout behavior. The applied rules are:
62
+ > ```CSS
63
+ > box-sizing: border-box;
64
+ > margin: 0;
65
+ > max-inline-size: /* varies dynamically based on side sheet state */
66
+ > transition: /* handles max-inline-size animation */
67
+ > ```
68
+ >
69
+ > These rules only take effect on medium window size classes (600px) and wider. On compact screens lacking horizontal space, the main content area is left untouched and the side sheet simply overlays it.
45
70
 
46
71
  ## Variants
47
- A **modal** side sheet blocks access to the rest of the page and must be dismissed explicitly by the user. This is suitable for critical tasks or information that requires a user's full attention.
72
+ Using a plain `popover` attribute makes the side sheet an *auto* popover: it closes automatically when the user presses <kbd>Esc</kbd> or clicks anywhere outside of it. This behavior is ideal for temporary panels like settings menus.
48
73
 
49
- To create a modal side sheet, use the `<dialog>` element without the `popover` attribute. Use `closedby="closerequest"` to prevent the side sheet from being dismissed by clicking outside of it. You'll also need a button or other control with `popovertarget` to close it.
74
+ ### Co-Existing Side Sheet
75
+ For a side sheet that should remain open while the user interacts with the main content, use `popover="manual"` instead. A manual popover is only dismissed by its own close button:
50
76
 
77
+ ```HTML
78
+ <dialog id="mysidesheet" class="micl-sidesheet" popover="manual" aria-labelledby="mytitle">
79
+ ...
80
+ </dialog>
81
+ ```
82
+
83
+ To open a **standard** side sheet, link a trigger button to the sheet's ID using `popovertarget`:
84
+ ```HTML
85
+ <button type="button" popovertarget="mysidesheet">Open Side Sheet</button>
86
+ ```
87
+
88
+ ### Modal Side Sheet
89
+ A **modal** side sheet blocks access to the rest of the page and requires the user to explicitly dismiss it. This variant is suitable for critical tasks or information requiring full attention.
90
+
91
+ To create a modal side sheet, use the `<dialog>` element *without* the `popover` attribute. Because it isn't a popover, `popovertarget` has no effect. Instead, you must provide a button (or another control) using `command="close"` to dismiss it. A modal side sheet is inherently not dismissed by outside clicks; explicitly setting `closedby="closerequest"` enforces this behavior while keeping the <kbd>Esc</kbd> key functional. (If you *do* want outside clicks to close it, use `closedby="any"` instead).
51
92
  ```HTML
52
93
  <dialog id="mysidesheet" class="micl-sidesheet" closedby="closerequest" aria-labelledby="mytitle">
53
94
  <div class="micl-sidesheet__headline">
54
95
  <button
55
96
  type="button"
56
97
  class="micl-iconbutton-standard-s material-symbols-outlined"
98
+ aria-label="Back"
57
99
  >arrow_back</button>
58
100
  <h2 id="mytitle">Title</h2>
59
101
  <button
60
102
  type="button"
61
103
  class="micl-iconbutton-standard-s material-symbols-outlined"
62
- popovertarget="mysidesheet"
104
+ command="close"
105
+ commandfor="mysidesheet"
106
+ aria-label="Close"
63
107
  >close</button>
64
108
  </div>
65
109
  <div class="micl-sidesheet__content">
@@ -72,54 +116,63 @@ To create a modal side sheet, use the `<dialog>` element without the `popover` a
72
116
  </dialog>
73
117
  ```
74
118
 
75
- To open a standard or modal side sheet, link a button to the side sheet's ID using the `popovertarget` attribute:
119
+ To open a **modal** side sheet, use the `show-modal` command instead:
76
120
  ```HTML
77
- <button type="button" popovertarget="mysidesheet">Open Side Sheet</button>
121
+ <button type="button" command="show-modal" commandfor="mysidesheet">Open Side Sheet</button>
78
122
  ```
79
123
 
80
- The back-button and the actions-container are optional. To remove the vertical divider of the standard side sheet, assign zero to the following CSS variable:
81
- ```CSS
82
- #mysidesheet {
83
- --md-comp-sidesheet-divider-thickness: 0;
84
- }
85
- ```
86
-
87
- > [!WARNING]
88
- > The **standard** side sheet component adds CSS rules to the `<body>` element to properly resize the main content area when the side sheet is open. Overriding these rules may cause the component to behave unexpectedly. The rules that are applied are:
89
- > ```CSS
90
- > box-sizing: border-box;
91
- > margin: 0;
92
- > max-inline-size: ...varies depending on if the side sheet is opened...
93
- > transition: ...transition on max-inline-size...
94
- > ```
124
+ ## Accessibility
125
+ * **Name the side sheet:** A `<dialog>` is exposed to assistive technologies as a dialog. Always provide an accessible name by pointing `aria-labelledby` to the headline's heading `id`.
126
+ * **Name the icon buttons:** Always provide a descriptive `aria-label` for icon buttons, like the back and close buttons.
127
+ * **Manage initial focus:** A standard side sheet relies on the Popover API, which automatically moves focus into the side sheet when the user tabs away from the trigger button, and returns focus upon closing. A modal side sheet natively focuses its first focusable descendant. This is rarely the desired behavior if the first element is a "Back" button. Add the `autofocus` attribute to the control that should legitimately receive focus first. *(Note: placing `autofocus` directly on the `<dialog>` element is ignored).*
128
+ * **Scrolling by keyboard:** When the content container overflows, modern browsers make it a distinct tab stop (often with a visible focus ring) so it can be scrolled via arrow keys. This is a deliberate accessibility feature; do not suppress the focus ring.
129
+ * **Dismissal constraints:** A side sheet opened with `popover` or `showModal()` is dismissed via the <kbd>Esc</kbd> key. A `popover="manual"` side sheet is not, meaning a visible close button is strictly required.
130
+ * **Respect motion preferences:** All motion is automatically disabled if the user has requested reduced motion at the OS level. The side sheet (and the main content resizing) will toggle instantly.
95
131
 
96
132
  ## Theming
97
- Each side sheet style can be themed with CSS custom properties that follow the Material Design 3 component-token naming convention.
133
+ Each side sheet can be themed using CSS custom properties formatted to match the Material Design 3 component-token naming convention.
98
134
 
99
135
  | Custom property | Meaning | Default |
100
- |---|---|---|
101
- | `--md-comp-sidesheet-container-width` | The default (and minimum allowed) width of the side sheet | `256px` |
102
- | `--md-comp-sidesheet-container-max-width` | The largest allowed width of the side sheet | `400px` |
103
- | `--md-comp-sidesheet-padding` | The amount of space between the vertical edges and the content | `--md-sys-layout-window-margin` |
136
+ | --- | --- | --- |
137
+ | `--md-comp-sidesheet-container-width` | The width of the side sheet | `256px` |
138
+ | `--md-comp-sidesheet-container-max-width` | The maximum allowed width of the side sheet; any larger `--md-comp-sidesheet-container-width` is clamped to this | `400px` |
139
+ | `--md-comp-sidesheet-padding` | The padding between the vertical edges and the content | `--md-sys-layout-window-margin` |
140
+ | `--md-comp-sidesheet-headline-height` | The minimum height of the headline row; increase this if using icon buttons larger than `micl-iconbutton-standard-s` | `40px` |
104
141
  | `--md-comp-sidesheet-container-shape` | The corner rounding of the standard side sheet | `--md-sys-shape-corner-none` |
142
+ | `--md-comp-sidesheet-content-color` | The text color inside the side sheet | `--md-sys-color-on-surface` |
105
143
  | `--md-comp-sidesheet-standard-container-color` | The background color of the standard side sheet | `--md-sys-color-surface` |
106
- | `--md-comp-sidesheet-divider-thickness` | The thickness of the vertical divider of the standard side sheet | `1px` |
107
- | `--md-comp-sidesheet-divider-color` | The color of the vertical divider of the standard side sheet | `--md-sys-color-outline` |
108
- | `--md-comp-sidesheet-standard-container-elevation` | The shadow (elevation) of the standard side sheet | `--md-sys-elevation-level0` |
144
+ | `--md-comp-sidesheet-divider-thickness` | The thickness of the standard side sheet's vertical divider | `1px` |
145
+ | `--md-comp-sidesheet-divider-color` | The color of the standard side sheet's vertical divider | `--md-sys-color-outline-variant` |
146
+ | `--md-comp-sidesheet-standard-container-elevation` | The shadow depth (elevation) of the standard side sheet | `--md-sys-elevation-level0` |
109
147
  | `--md-comp-sidesheet-modal-container-color` | The background color of the modal side sheet | `--md-sys-color-surface-container-low` |
110
- | `--md-comp-sidesheet-modal-container-elevation` | The shadow (elevation) of the modal side sheet | `--md-sys-elevation-level1` |
111
- | `--md-comp-sidesheet-modal-container-shape` | The corner rounding of the modal side sheet, applied to the two corners facing the content | `--md-sys-shape-corner-large` |
148
+ | `--md-comp-sidesheet-modal-container-elevation` | The shadow depth (elevation) of the modal side sheet | `--md-sys-elevation-level1` |
149
+ | `--md-comp-sidesheet-modal-container-shape` | The corner rounding of the modal side sheet (applied to the two corners facing the main content) | `--md-sys-shape-corner-large` |
150
+ | `--md-comp-sidesheet-motion-spatial` | The easing function for sliding the side sheet (uses a spring animation that slightly overshoots before settling) | `--md-sys-motion-expressive-slow-spatial` |
151
+ | `--md-comp-sidesheet-motion-duration` | Animation duration for opening | `650ms` |
152
+ | `--md-comp-sidesheet-motion-duration-reverse` | Animation duration for closing | `500ms` |
112
153
 
113
-
114
- **Example: Changing the width of the sidesheet**
154
+ **Example: Changing the width of the side sheet**
115
155
 
116
156
  ```HTML
117
- <body style="--md-comp-sidesheet-container-width:320px">
157
+ <body style="--md-comp-sidesheet-container-width: 320px;">
118
158
  <dialog id="mysidesheet" class="micl-sidesheet" popover>
119
159
  ...
120
160
  </dialog>
121
161
  </body>
122
162
  ```
123
163
 
164
+ To remove the vertical divider of a standard side sheet, set the following CSS variable to zero:
165
+ ```CSS
166
+ #mysidesheet {
167
+ --md-comp-sidesheet-divider-thickness: 0;
168
+ }
169
+ ```
170
+
171
+ > [!NOTE]
172
+ > For a **standard** side sheet, define the `--md-comp-sidesheet-container-width` custom property on the `<body>` (or `:root`) element rather than on the side sheet itself. The CSS rule responsible for shrinking the main content area reads this variable from the body. Setting it only on the `<dialog>` would cause the side sheet and the main content area to fall out of sync.
173
+
124
174
  ## Compatibility
125
- This component uses the Popover API, which might not be supported in all browsers. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API#api.htmlelement.popover) for details.
175
+
176
+ The standard side sheet relies on the Popover API. Please review [browser compatibility for the Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) if you are targeting older browsers.
177
+
178
+ The modal side sheet is opened and closed using the `command` and `commandfor` attributes. Review [browser compatibility for the Invoker Commands API](https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API). If these attributes are not supported in your target environments, calling `showModal()` and `close()` via JavaScript serves as an effective fallback.
@@ -19,7 +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';
22
+ @use '../../foundations/layout/tokens';
23
23
  @use '../../styles/elevation';
24
24
  @use '../../styles/motion';
25
25
  @use '../../styles/shape';
@@ -36,33 +36,32 @@
36
36
 
37
37
  @include typography.scale('title-large');
38
38
 
39
- :root {
40
- --md-comp-sidesheet-container-width: 256px;
41
- --md-comp-sidesheet-container-max-width: 400px;
42
- --md-comp-sidesheet-padding: var(--md-sys-layout-window-margin, 24px);
43
- }
44
- body {
45
- --md-comp-sidesheet-motion-spatial: #{motion.$md-sys-motion-expressive-slow-spatial};
46
- --md-comp-sidesheet-motion-duration: #{motion.$md-sys-motion-expressive-slow-spatial-duration};
47
- --md-comp-sidesheet-motion-duration-reverse: #{motion.$md-sys-motion-expressive-default-spatial-duration};
48
- }
39
+ $container-width: 256px;
40
+ $container-max-width: 400px;
49
41
 
50
42
  dialog.micl-sidesheet {
43
+ --_width: min(var(--md-comp-sidesheet-container-width, #{$container-width}), var(--md-comp-sidesheet-container-max-width, #{$container-max-width}));
44
+ --_padding: var(--md-comp-sidesheet-padding, var(--md-sys-layout-window-margin, 24px));
45
+ --_motion-spatial: var(--md-comp-sidesheet-motion-spatial, #{motion.$md-sys-motion-expressive-slow-spatial});
46
+ --_motion-duration: var(--md-comp-sidesheet-motion-duration, #{motion.$md-sys-motion-expressive-slow-spatial-duration});
47
+ --_motion-duration-reverse: var(--md-comp-sidesheet-motion-duration-reverse, #{motion.$md-sys-motion-expressive-default-spatial-duration});
48
+
51
49
  --micl-spring-buffer: 40px;
52
- --micl-duration: var(--md-comp-sidesheet-motion-duration-reverse);
50
+ --micl-duration: var(--_motion-duration-reverse);
53
51
 
54
52
  box-sizing: border-box;
55
53
  display: none;
56
54
  flex-direction: column;
57
- min-inline-size: calc(var(--md-comp-sidesheet-container-width) + var(--micl-spring-buffer));
58
- max-inline-size: calc(var(--md-comp-sidesheet-container-max-width) + var(--micl-spring-buffer));
59
- block-size: 100%;
60
- min-block-size: 100%;
55
+ inline-size: calc(var(--_width) + var(--micl-spring-buffer));
56
+ max-inline-size: none;
57
+ block-size: 100dvb;
58
+ max-block-size: none;
61
59
  margin-block: 0;
62
60
  margin-inline-start: auto;
63
- margin-inline-end: calc(-1 * (var(--md-comp-sidesheet-container-max-width) + var(--micl-spring-buffer)));
64
- padding-block: var(--md-comp-sidesheet-padding) 0;
61
+ margin-inline-end: calc(-1 * (var(--_width) + var(--micl-spring-buffer)));
62
+ padding-block: var(--_padding) 0;
65
63
  padding-inline: 0 var(--micl-spring-buffer);
64
+ color: var(--md-comp-sidesheet-content-color, var(--md-sys-color-on-surface));
66
65
  background-color: var(--md-comp-sidesheet-standard-container-color, var(--md-sys-color-surface));
67
66
  border: none;
68
67
  border-radius: var(--md-comp-sidesheet-container-shape, var(--md-sys-shape-corner-none));
@@ -71,15 +70,15 @@ dialog.micl-sidesheet {
71
70
  overflow: hidden;
72
71
  transition:
73
72
  opacity var(--micl-duration) linear,
74
- margin-inline-end var(--micl-duration) var(--md-comp-sidesheet-motion-spatial),
73
+ margin-inline-end var(--micl-duration) var(--_motion-spatial),
75
74
  overlay var(--micl-duration) linear allow-discrete,
76
75
  display var(--micl-duration) linear allow-discrete;
77
76
 
78
77
  .micl-sidesheet__headline {
79
78
  display: flex;
80
79
  align-items: center;
81
- block-size: var(--md-sys-typescale-title-large-line-height);
82
- padding-inline: var(--md-comp-sidesheet-padding);
80
+ min-block-size: var(--md-comp-sidesheet-headline-height, 40px);
81
+ padding-inline: var(--_padding);
83
82
  margin-block-end: 16px;
84
83
  column-gap: 12px;
85
84
  color: var(--md-sys-color-on-surface-variant);
@@ -92,19 +91,16 @@ dialog.micl-sidesheet {
92
91
  overflow: hidden;
93
92
  text-overflow: ellipsis;
94
93
  white-space: nowrap;
95
- user-select: none;
96
94
  }
97
95
  button {
98
- margin-inline: -12px;
99
- }
100
- button:first-child {
101
- margin-inline-start: -16px;
96
+ margin-inline: -8px;
102
97
  }
103
98
  }
104
99
  .micl-sidesheet__content {
105
100
  box-sizing: border-box;
106
101
  flex: 1 1 auto;
107
- padding-inline: var(--md-comp-sidesheet-padding);
102
+ padding-block-end: var(--_padding);
103
+ padding-inline: var(--_padding);
108
104
  overflow: hidden auto;
109
105
  }
110
106
  .micl-sidesheet__actions {
@@ -113,8 +109,8 @@ dialog.micl-sidesheet {
113
109
  flex-shrink: 0;
114
110
  align-items: center;
115
111
  justify-content: start;
116
- padding-block: 16px var(--md-comp-sidesheet-padding);
117
- padding-inline: var(--md-comp-sidesheet-padding);
112
+ padding-block: 16px var(--_padding);
113
+ padding-inline: var(--_padding);
118
114
  column-gap: 8px;
119
115
  overflow: hidden;
120
116
  }
@@ -126,18 +122,18 @@ dialog.micl-sidesheet {
126
122
  box-shadow: var(--md-comp-sidesheet-modal-container-elevation, var(--md-sys-elevation-level1));
127
123
  }
128
124
  &:popover-open {
129
- border-inline-start: var(--md-comp-sidesheet-divider-thickness, 1px) solid var(--md-comp-sidesheet-divider-color, var(--md-sys-color-outline, gray));
125
+ border-inline-start: var(--md-comp-sidesheet-divider-thickness, 1px) solid var(--md-comp-sidesheet-divider-color, var(--md-sys-color-outline-variant, gray));
130
126
  }
131
127
  &:popover-open,
132
128
  &[open] {
133
- --micl-duration: var(--md-comp-sidesheet-motion-duration);
129
+ --micl-duration: var(--_motion-duration);
134
130
 
135
131
  display: flex;
136
132
  margin-inline-end: calc(-1 * var(--micl-spring-buffer));
137
133
  opacity: 100%;
138
134
 
139
135
  @starting-style {
140
- margin-inline-end: calc(-1 * (var(--md-comp-sidesheet-container-max-width) + var(--micl-spring-buffer)));
136
+ margin-inline-end: calc(-1 * (var(--_width) + var(--micl-spring-buffer)));
141
137
  opacity: 0;
142
138
  }
143
139
  }
@@ -152,15 +148,29 @@ dialog.micl-sidesheet {
152
148
  background-color: rgba(0, 0, 0, 0);
153
149
  }
154
150
  }
155
- }
156
151
 
157
- body:has(dialog.micl-sidesheet[popover]) {
158
- box-sizing: border-box;
159
- max-inline-size: 100%;
160
- margin: 0;
161
- transition: max-inline-size var(--md-comp-sidesheet-motion-duration-reverse);
152
+ @media (prefers-reduced-motion: reduce) {
153
+ --_motion-duration: 0ms;
154
+ --_motion-duration-reverse: 0ms;
155
+ }
162
156
  }
163
- body:has(dialog.micl-sidesheet:popover-open) {
164
- max-inline-size: calc(100vw - var(--md-comp-sidesheet-container-max-width));
165
- transition: max-inline-size var(--md-comp-sidesheet-motion-duration) var(--md-comp-sidesheet-motion-spatial);
157
+
158
+ @media (min-width: tokens.$md-sys-breakpoint-medium-min) {
159
+ body:has(dialog.micl-sidesheet[popover]) {
160
+ box-sizing: border-box;
161
+ max-inline-size: 100%;
162
+ margin: 0;
163
+ transition: max-inline-size var(--md-comp-sidesheet-motion-duration-reverse, #{motion.$md-sys-motion-expressive-default-spatial-duration});
164
+ }
165
+ body:has(dialog.micl-sidesheet:popover-open) {
166
+ max-inline-size: calc(100% - min(var(--md-comp-sidesheet-container-width, #{$container-width}), var(--md-comp-sidesheet-container-max-width, #{$container-max-width})));
167
+ transition: max-inline-size var(--md-comp-sidesheet-motion-duration, #{motion.$md-sys-motion-expressive-slow-spatial-duration}) var(--md-comp-sidesheet-motion-spatial, #{motion.$md-sys-motion-expressive-slow-spatial});
168
+ }
169
+
170
+ @media (prefers-reduced-motion: reduce) {
171
+ body:has(dialog.micl-sidesheet[popover]),
172
+ body:has(dialog.micl-sidesheet:popover-open) {
173
+ transition-duration: 0s;
174
+ }
175
+ }
166
176
  }