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
package/README.md CHANGED
@@ -162,9 +162,10 @@ MICL doubles as a working tour of what production CSS can do in 2026. A few of t
162
162
  Each component's README documents its approach and its graceful-degradation story.
163
163
 
164
164
  ## Foundations 🪟
165
- Separate CSS files, based on the [Material Design Layout Foundation](https://m3.material.io/foundations/layout/layout-overview), provide foundational styles that are not tied to a single component. The **Layout** foundation includes styles for the **window frame**, **rail region** and **panes** that adapt to the available screen space, ensuring your layout follows Material Design's adaptive design guidelines. The **Field** foundation arranges form fields in a grid with the standard Material vertical rhythm, optionally led by an icon column.
165
+ Separate CSS files, based on the [Material Design Layout Foundation](https://m3.material.io/foundations/layout/layout-overview), provide foundational styles that are not tied to a single component. The **Layout** foundation includes styles for the **window frame**, **rail region** and **panes** that adapt to the available screen space, ensuring your layout follows Material Design's adaptive design guidelines. The **Field** foundation arranges form fields in a grid with the standard Material vertical rhythm, optionally led by an icon column. The **Form** foundation ships no CSS: it validates a form with the browser's own Constraint Validation API and renders the result as MICL error styling.
166
166
 
167
167
  - [x] [Field](foundations/field/README.md)
168
+ - [x] [Form](foundations/form/README.md)
168
169
  - [x] [Layout](foundations/layout/README.md)
169
170
 
170
171
  ## Available components ✅
@@ -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' as layout;
23
23
  @use '../../styles/elevation';
24
24
  @use '../../styles/shape';
25
25
  @use '../../styles/statelayer';
@@ -4,7 +4,7 @@ This component implements the [Material Design 3 Expressive Badge](https://m3.ma
4
4
  ## Basic Usage
5
5
 
6
6
  ### HTML
7
- To add a large badge, use a `<span>` element with the `micl-badge` class and add a short text inside. For a small badge, which is a simple dot, add the `micl-badge--small` class and leave the element empty.
7
+ To add a large badge, use a `<span>` element with the `micl-badge` class and add a short string of text inside. Keep the text to four characters at most, including a `+` (for example `999+`); anything longer is clipped. For a small badge, which is a simple dot, add the `micl-badge--small` class and leave the element empty.
8
8
 
9
9
  ```HTML
10
10
  <span class="micl-badge">57</span>
@@ -28,26 +28,56 @@ Or import all MICL styles:
28
28
  No custom JavaScript is required for the core functionality of this component.
29
29
 
30
30
  ### Live Demo
31
- A live example of the [Badge component](https://henkpb.github.io/micl/index.html) is available to interact with.
31
+ Badges are shown on the navigation items of the [Navigation bar](https://henkpb.github.io/micl/navigationbar.html) and [Navigation rail](https://henkpb.github.io/micl/navigationrail.html) pages, and on the [MICL home page](https://henkpb.github.io/micl/index.html).
32
32
 
33
33
  ## Anchoring
34
- Badges are typically placed on top of other elements, like icons. To anchor a badge to a specific element, use CSS `anchor positioning`.
34
+ Badges are placed on top of other elements, typically icons, using CSS anchor positioning:
35
35
 
36
- 1. Assign a unique `anchor-name` to the element you want to anchor the badge to. The value should start with `--`.
37
- 2. Use the `position-anchor` CSS property on the badge and set its value to the `anchor-name` of the target element.
36
+ 1. Give the element the badge belongs to an `anchor-name`. The value must start with `--`.
37
+ 2. Put the badge after that element in the markup and set its `position-anchor` to the same name.
38
38
 
39
39
  ```HTML
40
40
  <span class="material-symbols-outlined" style="anchor-name:--inbox" aria-hidden="true">inbox</span>
41
- <span class="micl-badge" style="position-anchor:--inbox">57</span>
41
+ <span class="micl-badge" style="position-anchor:--inbox" aria-hidden="true">57</span>
42
42
  ```
43
43
 
44
- You can fine-tune the badge's position relative to its anchor using the following CSS variables:
44
+ The badge is positioned relative to the center of its anchor, assuming a `--md-sys-icon-size` (24px) icon glyph is centered in it.
45
45
 
46
- | Variable name | Default Value | Description |
47
- | ------------- | ------------- | ----------- |
48
- | `--md-comp-badge-inline-offset` | `0px` | Adjusts the horizontal position of the badge |
49
- | `--md-comp-badge-block-offset` | `0px` | Adjusts the vertical position of the badge |
46
+ Because the badge is absolutely positioned, its anchor must exist within the badge's containing block (the nearest positioned ancestor, or the document root). For naturally positioned elements like navigation items, the anchor name only needs to be unique within that specific item. Elsewhere, use a unique name per page or limit the names' reach with `anchor-scope` on a common ancestor.
50
47
 
48
+ ### Trailing placement
49
+ When the icon is followed by text, a large badge on the icon would overlap the label. Following the Material Design guidance, place it after the label instead by adding the `micl-badge--trailing` class. The badge then needs no anchor: it is laid out in the item's flow, vertically centered, with a small gap after the label.
50
+
51
+ ```HTML
52
+ <a href="#" class="micl-navigationrail__item" aria-label="Favorites, 84 new">
53
+ <span class="micl-navigationrail__icon material-symbols-outlined" aria-hidden="true">favorite</span>
54
+ <span class="micl-navigationrail__text">Favorites</span>
55
+ <span class="micl-badge micl-badge--trailing" aria-hidden="true">84</span>
56
+ </a>
57
+ ```
58
+
59
+ Small badges keep their place on the icon in these layouts.
60
+
61
+ ### Fine-tuning
62
+ You can fine-tune the position of an anchored badge using the following custom properties:
63
+
64
+ | Custom property | Meaning | Default |
65
+ |---|---|---|
66
+ | `--md-comp-badge-inline-offset` | Moves the badge along the inline (horizontal) axis | `0px` |
67
+ | `--md-comp-badge-block-offset` | Moves the badge along the block (vertical) axis | `0px` |
68
+
69
+ ## Accessibility
70
+ Badges are purely visual. To ensure screen readers interpret them correctly, hide the badge itself using `aria-hidden="true"` and update the destination's accessible name to include the badge's information (e.g., the number for a large badge, or "New notification" for a small one):
71
+
72
+ ```HTML
73
+ <a href="#" class="micl-navigationbar__item" aria-label="Inbox, 57 new messages">
74
+ <span class="micl-navigationbar__icon material-symbols-outlined" style="anchor-name:--inbox" aria-hidden="true">inbox</span>
75
+ <span class="micl-navigationbar__text">Inbox</span>
76
+ <span class="micl-badge" style="position-anchor:--inbox" aria-hidden="true">57</span>
77
+ </a>
78
+ ```
79
+
80
+ Update the accessible name whenever the badge content changes.
51
81
 
52
82
  ## Theming
53
83
  Each badge 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 badges.
@@ -57,6 +87,7 @@ Each badge can be themed with CSS custom properties that follow the Material Des
57
87
  | `--md-comp-badge-size` | The height and width of the small badge | `6px` |
58
88
  | `--md-comp-badge-large-size` | The height and minimum width of the large badge | `16px` |
59
89
  | `--md-comp-badge-large-padding` | The horizontal padding used for the large badge | `4px` |
90
+ | `--md-comp-badge-trailing-space` | The gap between the label and a trailing badge | `4px` |
60
91
  | `--md-comp-badge-color` | The background color of the badge | `--md-sys-color-error` |
61
92
  | `--md-comp-badge-large-label-text-color` | The text color of the large badge | `--md-sys-color-on-error` |
62
93
 
@@ -69,4 +100,4 @@ Each badge can be themed with CSS custom properties that follow the Material Des
69
100
  ```
70
101
 
71
102
  ## Compatibility
72
- This component uses **anchor positioning**, 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.
103
+ This component uses **anchor positioning**, a modern CSS feature that may not be supported in older browsers; without it the badge is placed where it occurs in the markup. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/anchor#browser_compatibility) for details.
@@ -26,22 +26,23 @@
26
26
  .micl-badge {
27
27
  --_size: var(--md-comp-badge-size, 6px);
28
28
  --_large-size: var(--md-comp-badge-large-size, 16px);
29
+ --_icon: var(--md-sys-icon-size, 24px);
29
30
  --_dx: var(--md-comp-badge-inline-offset, 0px);
30
31
  --_dy: var(--md-comp-badge-block-offset, 0px);
31
32
 
32
33
  box-sizing: border-box;
33
- position: fixed;
34
- inset: 0;
34
+ position: absolute;
35
+ inset: auto;
35
36
  margin: 0;
36
- overflow-x: hidden;
37
+ overflow: clip;
37
38
  background-color: var(--md-comp-badge-color, var(--md-sys-color-error));
38
39
  color: var(--md-comp-badge-large-label-text-color, var(--md-sys-color-on-error));
39
40
 
40
41
  &.micl-badge--small {
41
42
  inline-size: var(--_size);
42
43
  block-size: var(--_size);
43
- inset-block-start: calc(anchor(start) + var(--_dy));
44
- inset-inline-start: calc(anchor(end) - var(--_size) + var(--_dx));
44
+ inset-block-start: calc(anchor(center) - var(--_icon) / 2 + var(--_dy));
45
+ inset-inline-start: calc(anchor(center) + var(--_icon) / 2 - var(--_size) + var(--_dx));
45
46
  padding: 0;
46
47
  border-radius: calc(var(--_size) / 2);
47
48
  }
@@ -52,24 +53,17 @@
52
53
  max-inline-size: 34px;
53
54
  min-inline-size: var(--_large-size);
54
55
  block-size: var(--_large-size);
55
- inset-block-start: calc(anchor(start) - (var(--_large-size) - 14px) + var(--_dy));
56
- inset-inline-start: calc(anchor(end) - 12px + var(--_dx));
56
+ inset-block-start: calc(anchor(center) - var(--_icon) / 2 - (var(--_large-size) - 14px) + var(--_dy));
57
+ inset-inline-start: calc(anchor(center) + var(--_icon) / 2 - 12px + var(--_dx));
57
58
  padding-inline: var(--md-comp-badge-large-padding, 4px);
58
59
  border-radius: calc(var(--_large-size) / 2);
59
60
  }
60
- }
61
-
62
- @supports not (top: anchor(bottom)) {
63
- .micl-badge {
64
- position: absolute;
65
- inset: 0 !important;
66
- margin-block: calc(12px + var(--_dy)) auto;
67
-
68
- &.micl-badge--small {
69
- margin-inline-start: calc(var(--md-sys-icon-size, 24px) - var(--_size) - var(--_dx));
70
- }
71
- &:not(.micl-badge--small) {
72
- margin-inline-start: calc(12px - var(--_dx));
73
- }
61
+ &.micl-badge--trailing {
62
+ position: static;
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ align-self: center;
67
+ margin-inline-start: var(--md-comp-badge-trailing-space, 4px);
74
68
  }
75
69
  }
@@ -1,5 +1,6 @@
1
1
  # Bottom sheet
2
- This component implements the [Material Design 3 Expressive Bottom sheet](https://m3.material.io/components/bottom-sheets/overview) design. Bottom sheets show secondary content anchored to the bottom of the screen.
2
+
3
+ This component implements the [Material Design 3 Expressive Bottom sheet](https://m3.material.io/components/bottom-sheets/overview) specification. Bottom sheets display secondary content anchored to the bottom of the screen.
3
4
 
4
5
  ## Basic Usage
5
6
 
@@ -21,13 +22,14 @@ Import the bottom sheet styles into your project:
21
22
  @use "material-inspired-component-library/dist/bottomsheet";
22
23
  ```
23
24
 
24
- Or import all MICL styles:
25
+ Alternatively, import all MICL styles at once:
26
+
25
27
  ```CSS
26
28
  @use "material-inspired-component-library/styles";
27
29
  ```
28
30
 
29
31
  ### JavaScript
30
- This component requires JavaScript to support **resizable** bottom sheets. The library will automatically initialize new components as they're added to the DOM.
32
+ This component requires JavaScript to support **resizable** bottom sheets. The library automatically initializes new components as they are added to the DOM.
31
33
 
32
34
  ```JavaScript
33
35
  import micl from "material-inspired-component-library/dist/micl";
@@ -37,7 +39,9 @@ import micl from "material-inspired-component-library/dist/micl";
37
39
  A live example of the [Bottom sheet component](https://henkpb.github.io/micl/bottomsheet.html) is available to interact with.
38
40
 
39
41
  ## Variants
40
- Setting the `popover` attribute to `manual` lets a bottom sheet co-exist and be interactive with the rest of the page.
42
+
43
+ ### Co-Existing Bottom Sheet
44
+ Setting the `popover` attribute to `manual` allows a bottom sheet to remain open while the user interacts with the rest of the page. Because clicking outside the sheet will not dismiss it, you must provide a dedicated close button.
41
45
 
42
46
  ```HTML
43
47
  <dialog id="mybottomsheet" class="micl-bottomsheet" popover="manual">
@@ -47,10 +51,16 @@ Setting the `popover` attribute to `manual` lets a bottom sheet co-exist and be
47
51
  </dialog>
48
52
  ```
49
53
 
54
+ To open or close a manual popover bottom sheet, link a button to it using the `popovertarget` attribute:
55
+
56
+ ```HTML
57
+ <button type="button" popovertarget="mybottomsheet">Open Bottom Sheet</button>
58
+ ```
59
+
50
60
  ### Modal Bottom Sheet
51
- A **modal** bottom 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.
61
+ A **modal** bottom sheet blocks access to the rest of the page and must be explicitly dismissed by the user. This is suitable for critical tasks or information requiring the user's full attention.
52
62
 
53
- To create a modal bottom sheet, use the `<dialog>` element without the `popover` attribute. Add the `closedby="any"` to allow the bottom sheet to be dismissed by a user clicking outside of it.
63
+ To create a modal bottom sheet, use the `<dialog>` element *without* the `popover` attribute. Add the `closedby="any"` attribute to allow the bottom sheet to be dismissed when the user clicks the backdrop.
54
64
 
55
65
  ```HTML
56
66
  <dialog id="mybottomsheet" class="micl-bottomsheet" closedby="any">
@@ -60,14 +70,15 @@ To create a modal bottom sheet, use the `<dialog>` element without the `popover`
60
70
  </dialog>
61
71
  ```
62
72
 
63
- To open or close a bottom sheet, add the markup of a button that is linked to the bottom sheet using the `popovertarget` attribute:
73
+ Because a modal bottom sheet is not a popover, `popovertarget` will not work. Use the `command` and `commandfor` attributes instead:
64
74
 
65
75
  ```HTML
66
- <button type="button" popovertarget="mybottomsheet">Open Bottom Sheet</button>
76
+ <button type="button" command="show-modal" commandfor="mybottomsheet">Open Bottom Sheet</button>
77
+ <button type="button" command="close" commandfor="mybottomsheet">Close</button>
67
78
  ```
68
79
 
69
80
  ### Resizable Bottom Sheet
70
- To create a **resizable** bottom sheet, include a heading and an optional drag-handle.
81
+ To create a **resizable** bottom sheet, add a `micl-bottomsheet__headline` drag area above the content. While the visual drag-handle inside this area is optional, it is required for keyboard resizing and provides the only visual cue that the sheet is resizable.
71
82
 
72
83
  ```HTML
73
84
  <dialog id="mybottomsheet" class="micl-bottomsheet" popover>
@@ -80,38 +91,58 @@ To create a **resizable** bottom sheet, include a heading and an optional drag-h
80
91
  </dialog>
81
92
  ```
82
93
 
83
- The initial height is determined by its content and capped at 50% of the screen's height. You can define specific preset heights by using the `data-miclsnapheights` attribute.
94
+ Upon opening, the bottom sheet defaults to the height of its content, capped at 50% of the screen's height. When resized, it can expand to the full height of the screen minus the `--md-comp-bottomsheet-margin-top` value. You can define specific preset snap points using the `data-miclsnapheights` attribute.
84
95
 
85
96
  ```HTML
86
97
  <dialog data-miclsnapheights="0,200,420" ...>
87
98
  ```
88
99
 
89
- The value `0` is equivalent to closing the bottom sheet. When a user clicks the drag-handle, the bottom sheet toggles between these preset heights and its initial height. The bottom sheet will also snap to these preset heights as a user drags the heading, and settles on the nearest preset when released. Dragging the bottom sheet down to less than 48px closes it.
100
+ **Behavior of Snap Heights:**
101
+ * The value `0` equates to closing the bottom sheet.
102
+ * Preset heights exceeding the maximum allowed height are ignored.
103
+ * Activating the drag-handle via keyboard cycles through the initial height and all valid preset heights in ascending order.
104
+ * When dragging via pointer, the sheet snaps to the nearest preset upon release.
105
+ * Dragging the sheet below 48px automatically closes it.
106
+
107
+
90
108
 
91
109
  ## Theming
92
- Each bottom sheet style can be themed with CSS custom properties that follow the Material Design 3 component-token naming convention. The properties are declared on the component itself and can be changed on any appropriate parent element to affect its child bottom sheets.
110
+
111
+ Each bottom sheet style can be themed using CSS custom properties aligned with the Material Design 3 component-token naming convention. Declare these properties on the component itself, or on an appropriate parent element to affect all child bottom sheets.
93
112
 
94
113
  | Custom property | Meaning | Default |
95
- |---|---|---|
96
- | `--md-comp-bottomsheet-height` | The height of the opened bottom sheet (managed by the library for resizable bottom sheets) | `max-content` |
97
- | `--md-comp-bottomsheet-margin` | The space between the bottom sheet and the sides of the screen, on screens wider than 640px | `56px` |
98
- | `--md-comp-bottomsheet-margin-top` | The minimum space between the bottom sheet and the top of the screen | `72px` |
99
- | `--md-comp-bottomsheet-padding` | The amount of space between the edges and the content | `24px` |
100
- | `--md-comp-bottomsheet-container-color` | The background color of the bottom sheet | `--md-sys-color-surface-container-low` |
101
- | `--md-comp-bottomsheet-container-shape` | The corner rounding of the bottom sheet | `--md-sys-shape-corner-extra-large-top` |
102
- | `--md-comp-bottomsheet-container-elevation` | The shadow (elevation) of the bottom sheet | `--md-sys-elevation-level1` |
114
+ | --- | --- | --- |
115
+ | `--md-comp-bottomsheet-height` | The height of the opened bottom sheet (managed dynamically for resizable sheets) | `max-content` |
116
+ | `--md-comp-bottomsheet-margin` | The space between the sheet and screen edges (medium breakpoint and wider) | `56px` |
117
+ | `--md-comp-bottomsheet-margin-top` | The minimum space between the sheet and the top of the screen | `72px` |
118
+ | `--md-comp-bottomsheet-padding` | The padding between the sheet edges and its content | `24px` |
119
+ | `--md-comp-bottomsheet-container-color` | The background color | `--md-sys-color-surface-container-low` |
120
+ | `--md-comp-bottomsheet-container-shape` | The corner rounding | `--md-sys-shape-corner-extra-large-top` |
121
+ | `--md-comp-bottomsheet-container-elevation` | The shadow depth (elevation) | `--md-sys-elevation-level1` |
103
122
  | `--md-comp-bottomsheet-drag-handle-width` | The width of the drag handle | `32px` |
104
123
  | `--md-comp-bottomsheet-drag-handle-height` | The height of the drag handle | `4px` |
105
124
  | `--md-comp-bottomsheet-drag-handle-color` | The color of the drag handle | `--md-sys-color-on-surface-variant` |
106
125
  | `--md-comp-bottomsheet-focus-indicator-color` | The color of the drag handle's keyboard-focus outline | `--md-sys-color-secondary` |
126
+ | `--md-comp-bottomsheet-motion-spatial` | The easing function for height changes (uses a spring animation that slightly overshoots before settling) | `--md-sys-motion-expressive-slow-spatial` |
127
+ | `--md-comp-bottomsheet-motion-duration` | Animation duration for opening and resizing | `650ms` |
128
+ | `--md-comp-bottomsheet-motion-duration-reverse` | Animation duration for closing | `500ms` |
107
129
 
108
130
  **Example: Changing the background color of the bottom sheet**
109
131
 
110
132
  ```HTML
111
- <dialog class="micl-bottomsheet" popover style="--md-comp-bottomsheet-container-color:var(--md-sys-color-surface-container)">
133
+ <dialog class="micl-bottomsheet" popover style="--md-comp-bottomsheet-container-color: var(--md-sys-color-surface-container);">
112
134
  ...
113
135
  </dialog>
114
136
  ```
115
137
 
138
+ ## Accessibility
139
+
140
+ * **Always label the bottom sheet:** Point `aria-labelledby` to the heading inside it, or provide an `aria-label` directly on the `<dialog>` if there is no visible heading.
141
+ * **Label the drag-handle:** Because it is an icon-only button, it requires a descriptive `aria-label` (e.g., "Resize bottom sheet").
142
+ * **Ensure keyboard accessibility:** A drag area without a handle can only be resized via pointer. Always include the drag-handle or provide alternative UI controls to adjust the sheet's height.
143
+ * **Provide a clear exit:** Modal and `popover="manual"` bottom sheets are not dismissed by clicking outside of them; you must include a dedicated close button within the sheet.
144
+ * **Respect motion preferences:** All motion animations are automatically disabled if the user has requested reduced motion at the OS level (the sheet will appear, resize, and close instantly).
145
+
116
146
  ## Compatibility
117
- This component uses the Popover API, which might not be supported in your browser. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API#api.htmlelement.popover) for details.
147
+
148
+ This component relies on the Popover API and the `interpolate-size` CSS property (which animates the height between zero and `max-content`). Review browser compatibility grids for the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) and [interpolate-size](https://developer.mozilla.org/en-US/docs/Web/CSS/interpolate-size) if you are targeting older browsers.
@@ -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';
@@ -33,47 +34,55 @@
33
34
  @include statelayer.token('focus-indicator-outer-offset');
34
35
  @include statelayer.token('backdrop-opacity');
35
36
 
37
+ @property --micl-bottomsheet-scroll {
38
+ syntax: '<custom-ident>';
39
+ inherits: true;
40
+ initial-value: hidden;
41
+ }
42
+
36
43
  dialog.micl-bottomsheet {
37
- --md-comp-bottomsheet-height: max-content;
38
- --md-comp-bottomsheet-margin: 56px;
39
- --md-comp-bottomsheet-margin-top: 72px;
40
- --md-comp-bottomsheet-padding: 24px;
41
- --md-comp-bottomsheet-drag-handle-width: 32px;
42
- --md-comp-bottomsheet-drag-handle-height: 4px;
43
- --md-comp-bottomsheet-motion-spatial: #{motion.$md-sys-motion-expressive-slow-spatial};
44
- --md-comp-bottomsheet-motion-duration: #{motion.$md-sys-motion-expressive-slow-spatial-duration};
45
- --md-comp-bottomsheet-motion-duration-reverse: #{motion.$md-sys-motion-expressive-default-spatial-duration};
46
-
47
- --micl-duration: var(--md-comp-bottomsheet-motion-duration-reverse);
44
+ --_height: var(--md-comp-bottomsheet-height, max-content);
45
+ --_margin: var(--md-comp-bottomsheet-margin, 56px);
46
+ --_margin-top: var(--md-comp-bottomsheet-margin-top, 72px);
47
+ --_padding: var(--md-comp-bottomsheet-padding, 24px);
48
+ --_drag-handle-width: var(--md-comp-bottomsheet-drag-handle-width, 32px);
49
+ --_drag-handle-height: var(--md-comp-bottomsheet-drag-handle-height, 4px);
50
+ --_motion-spatial: var(--md-comp-bottomsheet-motion-spatial, #{motion.$md-sys-motion-expressive-slow-spatial});
51
+ --_motion-duration: var(--md-comp-bottomsheet-motion-duration, #{motion.$md-sys-motion-expressive-slow-spatial-duration});
52
+ --_motion-duration-reverse: var(--md-comp-bottomsheet-motion-duration-reverse, #{motion.$md-sys-motion-expressive-default-spatial-duration});
53
+
54
+ --micl-duration: var(--_motion-duration-reverse);
48
55
  --micl-easing: #{motion.$md-sys-motion-easing-emphasized-accelerate};
56
+ --micl-bottomsheet-scroll: hidden;
57
+ --_settle-delay: 0s;
49
58
 
50
59
  box-sizing: border-box;
51
60
  position: fixed;
52
61
  inset-block: auto 0;
62
+ inset-inline: 0;
53
63
  block-size: 0;
54
- max-block-size: 50vh;
64
+ max-block-size: 50dvb;
55
65
  inline-size: 100%;
56
-
57
- &[data-miclfitheight] {
58
- max-block-size: calc(100vh - var(--md-comp-bottomsheet-margin-top));
59
- }
60
- min-inline-size: 100%;
61
- margin: var(--md-comp-bottomsheet-margin-top) 0 0 0 ;
66
+ margin: 0;
62
67
  padding: 0;
63
68
  border: none;
64
69
  border-radius: var(--md-comp-bottomsheet-container-shape, var(--md-sys-shape-corner-extra-large-top));
65
70
  background-color: var(--md-comp-bottomsheet-container-color, var(--md-sys-color-surface-container-low));
66
71
  box-shadow: var(--md-comp-bottomsheet-container-elevation, var(--md-sys-elevation-level1));
67
72
  opacity: 0;
68
- overflow-y: hidden;
73
+ overflow: hidden;
69
74
  interpolate-size: allow-keywords;
70
75
 
76
+ &[data-miclfitheight] {
77
+ max-block-size: calc(100dvb - var(--_margin-top));
78
+ }
71
79
  &:not(.micl-bottomsheet--resizing) {
72
80
  transition:
73
81
  opacity var(--micl-duration) var(--micl-easing),
74
- block-size var(--micl-duration) var(--md-comp-bottomsheet-motion-spatial),
82
+ block-size var(--micl-duration) var(--_motion-spatial),
75
83
  overlay var(--micl-duration) linear allow-discrete,
76
- display var(--micl-duration) linear allow-discrete;
84
+ display var(--micl-duration) linear allow-discrete,
85
+ --micl-bottomsheet-scroll 0s var(--_settle-delay) allow-discrete;
77
86
  }
78
87
  .micl-bottomsheet__headline {
79
88
  box-sizing: border-box;
@@ -81,18 +90,18 @@ dialog.micl-bottomsheet {
81
90
  flex-shrink: 0;
82
91
  align-items: center;
83
92
  inline-size: 100%;
84
- block-size: var(--md-sys-target-size, 48px);
93
+ block-size: var(--md-sys-state-target-size, 48px);
85
94
  justify-content: center;
86
95
  cursor: grab;
87
96
  touch-action: none;
97
+ -webkit-tap-highlight-color: transparent;
88
98
 
89
99
  .micl-bottomsheet__draghandle {
90
100
  box-sizing: content-box;
91
- inline-size: var(--md-comp-bottomsheet-drag-handle-width);
92
- block-size: var(--md-comp-bottomsheet-drag-handle-height);
93
- padding: 16px calc((var(--md-sys-target-size, 48px) - var(--md-comp-bottomsheet-drag-handle-width)) / 2);
101
+ inline-size: var(--_drag-handle-width);
102
+ block-size: var(--_drag-handle-height);
103
+ padding: 16px calc((var(--md-sys-state-target-size, 48px) - var(--_drag-handle-width)) / 2);
94
104
  border: none;
95
- // only shapes the focus ring; the visible handle is the ::before below
96
105
  border-radius: var(--md-sys-shape-corner-extra-large);
97
106
  background: none;
98
107
  cursor: pointer;
@@ -101,7 +110,7 @@ dialog.micl-bottomsheet {
101
110
  content: '';
102
111
  display: block;
103
112
  block-size: 100%;
104
- border-radius: calc(var(--md-comp-bottomsheet-drag-handle-height) / 2);
113
+ border-radius: calc(var(--_drag-handle-height) / 2);
105
114
  background-color: var(--md-comp-bottomsheet-drag-handle-color, var(--md-sys-color-on-surface-variant));
106
115
  }
107
116
  &:focus-visible {
@@ -110,14 +119,14 @@ dialog.micl-bottomsheet {
110
119
  }
111
120
  }
112
121
  }
113
- &.micl-bottomsheet--resizing .micl-bottomsheet__headline {
114
- cursor: grabbing;
115
- }
116
-
117
122
  .micl-bottomsheet__content {
118
123
  flex: 1 1 auto;
119
- padding: 0 var(--md-comp-bottomsheet-padding) var(--md-comp-bottomsheet-padding) var(--md-comp-bottomsheet-padding);
120
- overflow: hidden auto;
124
+ padding: 0 var(--_padding) var(--_padding) var(--_padding);
125
+ overflow: hidden var(--micl-bottomsheet-scroll);
126
+
127
+ &:first-child {
128
+ padding-block-start: var(--_padding);
129
+ }
121
130
  }
122
131
 
123
132
  &::backdrop {
@@ -129,17 +138,20 @@ dialog.micl-bottomsheet {
129
138
  }
130
139
  &:popover-open,
131
140
  &[open] {
132
- --micl-duration: var(--md-comp-bottomsheet-motion-duration);
141
+ --micl-duration: var(--_motion-duration);
133
142
  --micl-easing: #{motion.$md-sys-motion-easing-emphasized-decelerate};
143
+ --micl-bottomsheet-scroll: auto;
144
+ --_settle-delay: var(--_motion-duration);
134
145
 
135
146
  display: flex;
136
147
  flex-direction: column;
137
- block-size: var(--md-comp-bottomsheet-height);
138
- opacity: 100%;
148
+ block-size: var(--_height);
149
+ opacity: 1;
139
150
 
140
151
  @starting-style {
141
152
  block-size: 0;
142
153
  opacity: 0;
154
+ --micl-bottomsheet-scroll: hidden;
143
155
  }
144
156
  }
145
157
  &[open]::backdrop {
@@ -149,15 +161,20 @@ dialog.micl-bottomsheet {
149
161
  background-color: rgba(0, 0, 0, 0);
150
162
  }
151
163
  }
152
- }
164
+ &.micl-bottomsheet--resizing {
165
+ --micl-bottomsheet-scroll: hidden;
153
166
 
154
- @media (min-width: 641px) {
155
- dialog.micl-bottomsheet {
156
- inline-size: min(100vw - (2 * var(--md-comp-bottomsheet-margin)), 640px);
157
- max-inline-size: 640px;
158
- min-inline-size: min(100vw - (2 * var(--md-comp-bottomsheet-margin)), 640px);
159
- inset-inline-start: calc(((100vw - min(100vw - (2 * var(--md-comp-bottomsheet-margin)), 640px)) / 2) - var(--md-comp-bottomsheet-margin));
160
- margin: var(--md-comp-bottomsheet-margin);
161
- margin-block-end: 0;
167
+ .micl-bottomsheet__headline {
168
+ cursor: grabbing;
169
+ }
170
+ }
171
+
172
+ @media (prefers-reduced-motion: reduce) {
173
+ --_motion-duration: 0ms;
174
+ --_motion-duration-reverse: 0ms;
175
+ }
176
+ @media (min-width: layout.$md-sys-breakpoint-medium-min) {
177
+ inline-size: min(100% - (2 * var(--_margin)), 640px);
178
+ margin-inline: auto;
162
179
  }
163
180
  }
@@ -48,8 +48,8 @@ $focused: ':is(:focus-visible, :has(input:focus-visible))';
48
48
  @include typography.scale('label-large');
49
49
 
50
50
  %micl-button-base {
51
- --md-comp-button-motion-effects: #{motion.$md-sys-motion-expressive-fast-spatial};
52
- --md-comp-button-motion-duration: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
51
+ --_motion-effects: var(--md-comp-button-motion-effects, #{motion.$md-sys-motion-expressive-fast-spatial});
52
+ --_motion-duration: var(--md-comp-button-motion-duration, #{motion.$md-sys-motion-expressive-fast-spatial-duration});
53
53
  --micl-ripple: 1;
54
54
 
55
55
  box-sizing: border-box;
@@ -75,10 +75,10 @@ $focused: ':is(:focus-visible, :has(input:focus-visible))';
75
75
  -webkit-tap-highlight-color: transparent;
76
76
  cursor: pointer;
77
77
  transition:
78
- border-radius var(--md-comp-button-motion-duration) var(--md-comp-button-motion-effects),
79
- box-shadow var(--md-comp-button-motion-duration) var(--md-comp-button-motion-effects),
78
+ border-radius var(--_motion-duration) var(--_motion-effects),
79
+ box-shadow var(--_motion-duration) var(--_motion-effects),
80
80
  background-size 0ms,
81
- --statelayer-opacity var(--md-comp-button-motion-duration) linear;
81
+ --statelayer-opacity var(--_motion-duration) linear;
82
82
 
83
83
  &#{$disabled} {
84
84
  cursor: default;
@@ -106,7 +106,7 @@ $focused: ':is(:focus-visible, :has(input:focus-visible))';
106
106
  }
107
107
  .micl-button__icon {
108
108
  font-variation-settings: 'FILL' 0;
109
- transition: font-variation-settings var(--md-comp-button-motion-duration) linear;
109
+ transition: font-variation-settings var(--_motion-duration) linear;
110
110
  }
111
111
  &.micl-button--toggle:not([aria-pressed="true"]) .micl-button__icon::before {
112
112
  content: attr(data-miclicon);
@@ -122,10 +122,10 @@ $focused: ':is(:focus-visible, :has(input:focus-visible))';
122
122
  &::before {
123
123
  content: "";
124
124
  position: absolute;
125
- block-size: var(--md-sys-target-size, 48px);
125
+ block-size: var(--md-sys-state-target-size, 48px);
126
126
  inline-size: 100%;
127
- min-inline-size: var(--md-sys-target-size, 48px);
128
- inset: calc((var(--micl-height) - var(--md-sys-target-size, 48px)) / 2) 0 0 calc((min(100%, var(--md-sys-target-size, 48px)) - var(--md-sys-target-size, 48px)) / 2);
127
+ min-inline-size: var(--md-sys-state-target-size, 48px);
128
+ inset: calc((var(--micl-height) - var(--md-sys-state-target-size, 48px)) / 2) 0 0 calc((min(100%, var(--md-sys-state-target-size, 48px)) - var(--md-sys-state-target-size, 48px)) / 2);
129
129
  background-color: transparent;
130
130
  }
131
131
  }