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
@@ -30,25 +30,11 @@ No custom JavaScript is required for the core functionality of this component.
30
30
  ### Live Demo
31
31
  A live example of the [Card component](https://henkpb.github.io/micl/card.html) is available to interact with.
32
32
 
33
- ## Variants
34
- Cards are available in **three distinct styles**:
35
-
36
- - `micl-card-elevated`: A card with a subtle shadow, visually lifted from the background. This is the style shown in Basic Usage.
37
-
38
- - `micl-card-filled`: A card with a solid background color, blending more seamlessly with its surroundings.
39
- ```HTML
40
- <div class="micl-card-filled">
41
- <p>This is a filled card.</p>
42
- </div>
43
- ```
44
-
45
- - `micl-card-outlined`: A card with a clear border, often used for less prominent content or to indicate interactivity.
46
-
47
- ### Card Content Structure
33
+ ## Anatomy
48
34
  While the card container is the only required element, the Card component provides several optional utility classes to help structure your card's content:
49
35
 
50
36
  ```HTML
51
- <div class="micl-card-outlined" tabindex="0">
37
+ <div class="micl-card-outlined">
52
38
  <img alt="Descriptive image text" class="micl-card__image" src="/path/to/your/image.jpg">
53
39
 
54
40
  <div class="micl-card__headline-m">
@@ -69,9 +55,17 @@ While the card container is the only required element, the Card component provid
69
55
  - `micl-card__headline-m` (medium) - *Used in the example above*
70
56
  - `micl-card__headline-l` (large)
71
57
 
72
- - `micl-card__image`: Applies styling to an `<img>` element or an element with a `background-image` style used as the primary image for the card.
58
+ A headline occupies a single line and is ellipsed when it does not fit. To let it run onto more lines before being ellipsed, increase the `--md-comp-card-headline-line-clamp` value:
59
+
60
+ ```HTML
61
+ <div class="micl-card__headline-m" style="--md-comp-card-headline-line-clamp:2">
62
+ <h2>A headline long enough to need a second line</h2>
63
+ </div>
64
+ ```
73
65
 
74
- - `micl-card__subhead`: For secondary headings, displayed with a smaller font than the main headline.
66
+ - `micl-card__image`: Applies styling to an `<img>` element or an element with a `background-image` style used as the primary image for the card. The media carries the card's corner shape on every side, including the edges that face the card's interior.
67
+
68
+ - `micl-card__subhead`: For secondary headings, displayed with a smaller font than the main headline. Like a headline it occupies a single line and is ellipsed when it does not fit; increase the `--md-comp-card-subhead-line-clamp` value to allow more lines.
75
69
 
76
70
  - `micl-card__supporting-text`: Intended for short descriptions or supplementary information, displayed with a smaller font.
77
71
 
@@ -79,8 +73,35 @@ While the card container is the only required element, the Card component provid
79
73
 
80
74
  - `micl-card__actions`: A flexible container for any action buttons.
81
75
 
76
+ ## Variants
77
+ Cards are available in **three distinct styles**:
78
+
79
+ - `micl-card-elevated`: A card with a subtle shadow, visually lifted from the background. This is the style shown in Basic Usage.
80
+
81
+ - `micl-card-filled`: A card with a solid background color, blending more seamlessly with its surroundings.
82
+ ```HTML
83
+ <div class="micl-card-filled">
84
+ <p>This is a filled card.</p>
85
+ </div>
86
+ ```
87
+
88
+ - `micl-card-outlined`: A card with a clear border, often used for less prominent content or to indicate interactivity.
89
+
82
90
  ### Actionable Cards
83
- For cards that are themselves clickable or interactive (e.g., to navigate to another page), specify the `tabindex="0"` attribute on the card container. Non-actionable cards, which contain interactive elements like buttons or links within their content, should *not* have a tabindex on the card container itself.
91
+ For a card that is itself clickable — the whole card navigates somewhere — use an `<a>` element with an `href` as the card container. The card then picks up the hover, focus, pressed and ripple states, and is operable with the keyboard and announced correctly by assistive technology without any extra attributes or JavaScript.
92
+
93
+ ```HTML
94
+ <a class="micl-card-outlined" href="/article/42">
95
+ <div class="micl-card__headline-m">
96
+ <h2>Card headline</h2>
97
+ </div>
98
+ <p class="micl-card__supporting-text">The whole card is the link.</p>
99
+ </a>
100
+ ```
101
+
102
+ The same states are also applied to a card carrying `tabindex="0"`, which is the escape hatch for a card that acts on something other than a navigation. A `tabindex` only makes the card focusable, though — it does **not** make it operable. If you use it, you must supply the activation yourself: give the card the right `role` (usually `button`), and handle both `click` and the <kbd>Enter</kbd>/<kbd>Space</kbd> keys, or the card will be a focus stop that does nothing.
103
+
104
+ Non-actionable cards, which contain interactive elements like buttons or links within their content, should be a `<div>` with neither an `href` nor a `tabindex` on the card container itself.
84
105
 
85
106
  ### Expandable Cards
86
107
  Cards can also serve as containers for expandable detail areas using the `<details>` and `<summary>` elements. In this scenario, the `<summary>` element becomes the actionable part of the card.
@@ -101,7 +122,7 @@ Cards can also serve as containers for expandable detail areas using the `<detai
101
122
  ```
102
123
 
103
124
  ### Compact Cards
104
- Add the `micl-card--compact` to the main `<div>` element (or, the `<summary>` element for expandable cards) to create a compact version of the card header.
125
+ Add the `micl-card--compact` class to the main `<div>` element (or, the `<summary>` element for expandable cards) to create a compact version of the card header.
105
126
 
106
127
  ### States
107
128
  - **Disabled Cards**: To visually indicate a disabled card (e.g., non-interactive), add the `inert` attribute to the card container.
@@ -113,10 +134,15 @@ Each card can be themed with CSS custom properties that follow the Material Desi
113
134
 
114
135
  | Custom property | Meaning | Default |
115
136
  |---|---|---|
116
- | `--md-comp-card-margin` | Sets the spacing between adjacent cards both horizontally and vertically | `8px` |
117
137
  | `--md-comp-card-padding-inline` | Defines the amount of space between the left and right edges of a card and its content | `16px` |
118
138
  | `--md-comp-card-content-padding-block` | The amount of vertical padding reserved for the content area | `16px` |
119
- | `--md-comp-card-gap` | Defines the amount of vertical space between structural elements inside the card | `8px` |
139
+ | `--md-comp-card-gap` | Defines the amount of space between structural elements inside the card, and between the buttons in a `micl-card__actions` row | `8px` |
140
+ | `--md-comp-card-headline-line-clamp` | The number of lines a headline may run to before it is ellipsed | `1` |
141
+ | `--md-comp-card-subhead-line-clamp` | The number of lines a subhead may run to before it is ellipsed | `1` |
142
+ | `--md-comp-card-motion-duration` | How long the detail area of an expandable card takes to open and close, and how long a state layer takes to fade | `--md-sys-motion-expressive-default-spatial-duration` |
143
+ | `--md-comp-card-motion-spatial` | The easing curve used while the detail area of an expandable card opens and closes | `--md-sys-motion-expressive-default-spatial` |
144
+
145
+ The spacing *between* adjacent cards is not a property of the card itself — it is applied by the [Layout foundation](../../foundations/layout/README.md), which sets the gap on a `micl-pane` or `micl-pane__column` that holds cards. Set `--md-sys-card-margin` on, or above, that container.
120
146
 
121
147
  Each card style additionally supports the following CSS custom properties, as defined in the [Material Design 3 Expressive Card Specification](https://m3.material.io/components/cards/specs). The hover, focus, pressed and dragged properties only apply to actionable cards.
122
148
 
@@ -206,17 +232,19 @@ Each card style additionally supports the following CSS custom properties, as de
206
232
  | `--md-comp-outlined-card-dragged-state-layer-color` | The state layer color of the card while dragged | `--md-sys-color-on-surface` |
207
233
  | `--md-comp-outlined-card-dragged-state-layer-opacity` | The state layer opacity of the card while dragged | `--md-sys-state-dragged-state-layer-opacity` |
208
234
 
209
- **Example: Changing the card margins**
235
+ **Example: Changing the spacing between cards**
236
+
237
+ The gap comes from the pane that holds the cards, so set the property on the `micl-pane` (or on any of its ancestors):
210
238
 
211
239
  ```HTML
212
- <div style="--md-comp-card-margin:4px">
240
+ <section class="micl-pane" style="--md-sys-card-margin:4px">
213
241
  <div class="micl-card-filled">
214
242
  <p>This is a filled card.</p>
215
243
  </div>
216
244
  <div class="micl-card-filled">
217
245
  <p>This is another filled card.</p>
218
246
  </div>
219
- </div>
247
+ </section>
220
248
  ```
221
249
 
222
250
  To change the amount of rounding of a filled card's corners, you could for example add a CSS rule to your stylesheet:
@@ -228,6 +256,8 @@ To change the amount of rounding of a filled card's corners, you could for examp
228
256
  ```
229
257
 
230
258
  ## Compatibility
259
+ To preserve its rounded corners, a card clips its internal content. Because of this, anything meant to extend past the card's edges (like a dropdown menu or tooltip) will be cut off unless it is rendered in the top layer with the `popover` attribute or a `<dialog>`, as the MICL Menu and Dialog components are.
260
+
231
261
  This component utilizes relative RGB color values, which may not be fully supported in your browser. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#browser_compatibility) for details.
232
262
 
233
263
  The Card component uses the `interpolate-size` CSS property to smoothly open and close the detail area of a Details disclosure element, which might not be supported in your browser. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/interpolate-size#browser_compatibility) for details.
@@ -84,7 +84,8 @@
84
84
  --_card-background-color: var(--md-comp-#{$name}-card-disabled-container-color, #{$inert-bg-default});
85
85
  --_card-elevation: var(--md-comp-#{$name}-card-disabled-container-elevation, #{$inert-elev-default});
86
86
  --_card-opacity: var(--md-comp-#{$name}-card-disabled-container-opacity, var(--md-sys-state-disabled-state-layer-opacity));
87
- --_card-pointer-events: none;
87
+
88
+ pointer-events: none;
88
89
  }
89
90
  }
90
91
 
@@ -94,18 +95,23 @@
94
95
  --_card-focus-outline-offset: var(--md-comp-#{$name}-card-focus-indicator-outline-offset, var(--md-sys-state-focus-indicator-outer-offset));
95
96
  }
96
97
 
98
+ $actionable: ':is([tabindex="0"], a[href])';
99
+
97
100
  @mixin card-interactive($state) {
98
- &[tabindex='0']:not([inert]):#{$state},
99
- &:not([tabindex='0']):not([inert]):has(> summary:#{$state}) {
101
+ &#{$actionable}:not([inert]):#{$state},
102
+ &:not(#{$actionable}):not([inert]):has(> summary:#{$state}) {
100
103
  @content;
101
104
  }
102
105
  }
103
106
 
104
107
  $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
105
108
 
109
+ $headlines: ':is(.micl-card__headline-s, .micl-card__headline-m, .micl-card__headline-l)';
110
+
106
111
  #{$cards} {
107
- --md-comp-card-motion-spatial: #{motion.$md-sys-motion-expressive-default-spatial};
108
- --md-comp-card-motion-duration: #{motion.$md-sys-motion-expressive-default-spatial-duration};
112
+ --_card-motion-spatial: var(--md-comp-card-motion-spatial, #{motion.$md-sys-motion-expressive-default-spatial});
113
+ --_card-motion-duration: var(--md-comp-card-motion-duration, #{motion.$md-sys-motion-expressive-default-spatial-duration});
114
+ --_card-ripple-duration: var(--md-sys-state-ripple-duration);
109
115
 
110
116
  --_card-color: var(--md-sys-color-on-surface-variant);
111
117
 
@@ -113,25 +119,27 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
113
119
  flex-direction: column;
114
120
  block-size: fit-content;
115
121
  margin: 0;
116
- outline: none;
117
- overflow: hidden;
118
- interpolate-size: allow-keywords;
119
-
122
+ border: var(--_card-border, none);
120
123
  border-radius: var(--_card-shape);
124
+ outline: none;
121
125
  background-color: var(--_card-background-color);
122
126
  box-shadow: var(--_card-elevation);
123
- border: var(--_card-border, none);
127
+ text-decoration: none;
124
128
  opacity: var(--_card-opacity, 1);
125
- pointer-events: var(--_card-pointer-events, auto);
129
+ color: inherit;
130
+ overflow: hidden;
131
+ interpolate-size: allow-keywords;
126
132
 
127
133
  &:not(.micl-card--compact):not(:has(>summary)) {
128
134
  row-gap: var(--md-comp-card-gap, 8px);
129
135
  }
130
136
 
131
- &[tabindex='0']:not([inert]),
137
+ &#{$actionable}:not([inert]),
132
138
  &:not([inert]) > summary {
133
139
  --micl-ripple: 1;
134
140
 
141
+ --statelayer-color: var(--_card-hover-state-color);
142
+
135
143
  background-image:
136
144
  radial-gradient(
137
145
  circle at var(--micl-x, center) var(--micl-y, center),
@@ -143,9 +151,10 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
143
151
  background-repeat: no-repeat;
144
152
  background-position: var(--micl-x, center) var(--micl-y, center), 0 0;
145
153
  background-size: 0%, 100%;
154
+ -webkit-tap-highlight-color: transparent;
146
155
  transition:
147
156
  background-size 0ms,
148
- --statelayer-opacity var(--md-comp-card-motion-duration) linear;
157
+ --statelayer-opacity var(--_card-motion-duration) linear;
149
158
 
150
159
  &:is(:hover, :focus-visible, :active, .micl-card--dragging) {
151
160
  --_card-color: var(--md-sys-color-on-surface);
@@ -169,7 +178,7 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
169
178
  cursor: grabbing;
170
179
  }
171
180
  &.micl-rippling {
172
- animation: micl-ripple calc(var(--md-sys-state-ripple-duration) * 1.722);
181
+ animation: micl-ripple calc(var(--_card-ripple-duration) * 1.722);
173
182
  }
174
183
 
175
184
  .micl-card__content {
@@ -184,14 +193,14 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
184
193
  @include card-interactive('focus-visible') {
185
194
  box-shadow: var(--_card-focus-elevation);
186
195
  border-color: var(--_card-focus-border-color, transparent);
187
- outline: var(--_card-focus-outline-thickness, 0px) solid var(--_card-focus-outline-color, transparent);
188
- outline-offset: var(--_card-focus-outline-offset, 0px);
196
+ outline: var(--_card-focus-outline-thickness) solid var(--_card-focus-outline-color);
197
+ outline-offset: var(--_card-focus-outline-offset);
189
198
  }
190
199
  @include card-interactive('active') {
191
200
  box-shadow: var(--_card-pressed-elevation);
192
201
  border-color: var(--_card-pressed-border-color, transparent);
193
202
  }
194
- &[tabindex='0']:not([inert]).micl-card--dragging {
203
+ &#{$actionable}:not([inert]).micl-card--dragging {
195
204
  box-shadow: var(--_card-dragged-elevation);
196
205
  border-color: var(--_card-dragged-border-color, transparent);
197
206
  }
@@ -200,8 +209,8 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
200
209
  block-size: 0;
201
210
  overflow: hidden;
202
211
  transition:
203
- block-size var(--md-comp-card-motion-duration) var(--md-comp-card-motion-spatial),
204
- content-visibility var(--md-comp-card-motion-duration) linear allow-discrete;
212
+ block-size var(--_card-motion-duration) var(--_card-motion-spatial),
213
+ content-visibility var(--_card-motion-duration) linear allow-discrete;
205
214
  }
206
215
  &[open]::details-content {
207
216
  block-size: auto;
@@ -213,13 +222,16 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
213
222
  outline: none;
214
223
  list-style: none;
215
224
  cursor: pointer;
216
- -webkit-tap-highlight-color: transparent;
217
225
 
218
226
  &:not(.micl-card--compact) {
219
227
  row-gap: var(--md-comp-card-gap, 8px);
220
228
  }
221
229
  }
222
230
 
231
+ @media (prefers-reduced-motion: reduce) {
232
+ --_card-motion-duration: 0ms;
233
+ --_card-ripple-duration: 0ms;
234
+ }
223
235
  }
224
236
 
225
237
  .micl-card-elevated {
@@ -281,6 +293,7 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
281
293
  .micl-card__image {
282
294
  display: block;
283
295
  inline-size: 100%;
296
+ object-fit: contain;
284
297
  background-position: center;
285
298
  background-repeat: no-repeat;
286
299
  background-size: contain;
@@ -289,12 +302,12 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
289
302
 
290
303
  #{$cards},
291
304
  #{$cards} > summary {
292
- &> [class*='micl-card__headline-']:first-child {
305
+ &> #{$headlines}:first-child {
293
306
  padding-block-start: var(--md-comp-card-content-padding-block, 16px);
294
307
  }
295
308
  }
296
309
 
297
- [class*='micl-card__headline-'] {
310
+ #{$headlines} {
298
311
  display: flex;
299
312
  align-items: center;
300
313
  column-gap: var(--md-comp-card-gap, 8px);
@@ -304,17 +317,15 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
304
317
  flex: 1 2 fit-content;
305
318
  margin: 0;
306
319
  padding-inline: var(--md-comp-card-padding-inline, 16px);
320
+ display: -webkit-box;
307
321
  overflow: hidden;
308
- text-overflow: ellipsis;
309
- white-space: nowrap;
322
+ -webkit-box-orient: vertical;
323
+ -webkit-line-clamp: var(--md-comp-card-headline-line-clamp, 1);
310
324
 
311
325
  &:has(+ button) {
312
326
  padding-inline-end: 0;
313
327
  }
314
328
  }
315
- &:has(button) {
316
- margin-block: 0;
317
- }
318
329
  }
319
330
 
320
331
  @each $abbrev, $size in ('s': 'small', 'm': 'medium', 'l': 'large') {
@@ -325,13 +336,17 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
325
336
 
326
337
  .micl-card__subhead {
327
338
  @include typography.title-medium;
328
- display: block;
329
- margin: calc(-1 * var(--md-comp-card-gap, 8px)) 0 var(--md-comp-card-gap, 8px) 0;
330
- padding: 0 var(--md-comp-card-padding-inline, 16px);
339
+ display: -webkit-box;
331
340
  overflow: hidden;
332
- text-overflow: ellipsis;
333
- white-space: nowrap;
341
+ margin: 0 0 var(--md-comp-card-gap, 8px) 0;
342
+ padding: 0 var(--md-comp-card-padding-inline, 16px);
343
+ -webkit-box-orient: vertical;
344
+ -webkit-line-clamp: var(--md-comp-card-subhead-line-clamp, 1);
334
345
  color: var(--_card-color);
346
+
347
+ &:not(:first-child) {
348
+ margin-block-start: calc(-1 * var(--md-comp-card-gap, 8px));
349
+ }
335
350
  }
336
351
 
337
352
  .micl-card__supporting-text {
@@ -354,7 +369,7 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
354
369
  display: flex;
355
370
  flex-shrink: 0;
356
371
  flex-wrap: wrap;
357
- gap: 8px;
372
+ gap: var(--md-comp-card-gap, 8px);
358
373
  padding-block: 0 var(--md-comp-card-content-padding-block, 16px);
359
374
  padding-inline: var(--md-comp-card-padding-inline, 16px);
360
375
 
@@ -366,24 +381,23 @@ $cards: ':is(.micl-card-elevated, .micl-card-filled, .micl-card-outlined)';
366
381
  }
367
382
  }
368
383
 
369
- .micl-card--compact,
370
- summary.micl-card--compact {
384
+ .micl-card--compact {
371
385
  display: grid;
372
386
  grid-template-columns: 20% 1fr;
373
- padding-block-end: 0;
374
387
 
375
388
  .micl-card__image {
376
389
  grid-column: 1;
377
390
  grid-row: 1 / span 4;
378
391
  block-size: 100%;
392
+ object-fit: cover;
379
393
  background-size: cover;
380
394
  }
381
- [class*='micl-card__headline-'],
395
+ #{$headlines},
382
396
  .micl-card__subhead, .micl-card__supporting-text {
383
397
  grid-column: 2;
384
398
  min-inline-size: 0;
385
399
  }
386
- [class*='micl-card__headline-'], .micl-card__supporting-text {
400
+ #{$headlines}, .micl-card__supporting-text {
387
401
  margin-block-start: var(--md-comp-card-gap, 8px);
388
402
  }
389
403
  .micl-card__subhead {
@@ -397,3 +411,8 @@ summary.micl-card--compact {
397
411
  margin-block-end: var(--md-comp-card-gap, 8px);
398
412
  }
399
413
  }
414
+
415
+ summary.micl-card--compact {
416
+ display: grid;
417
+ padding-block-end: 0;
418
+ }
@@ -1,5 +1,5 @@
1
1
  # Checkbox
2
- This component implements the [Material Design 3 Expressive Checkbox](https://m3.material.io/components/checkbox/overview) design. A checkbox allows a user to select one or more options from a number of choices.
2
+ This component implements the [Material Design 3 Expressive Checkbox](https://m3.material.io/components/checkbox/overview) design. A checkbox allows a user to select one or more options from a set of choices.
3
3
 
4
4
  ## Basic Usage
5
5
 
@@ -11,6 +11,8 @@ To add a basic checkbox, use the `<input type="checkbox">` element with the `mic
11
11
  <label for="mycheckbox">Bar</label>
12
12
  ```
13
13
 
14
+ **Note on labels**: The component automatically applies `cursor: pointer` and the **on surface** color role to any `<label>` immediately preceding, immediately following, or wrapping a `micl-checkbox` input. You are encouraged to customize these CSS settings to match your design system.
15
+
14
16
  ### CSS
15
17
  Import the checkbox styles into your project:
16
18
 
@@ -24,7 +26,7 @@ Or import all MICL styles:
24
26
  ```
25
27
 
26
28
  ### JavaScript
27
- This component requires JavaScript to support checkbox groups:
29
+ This component only requires JavaScript when using checkbox groups:
28
30
 
29
31
  `import micl from "material-inspired-component-library/dist/micl";`
30
32
 
@@ -34,13 +36,28 @@ This will initialize any checkbox group, including those that will be added to t
34
36
  A live example of the [Checkbox component](https://henkpb.github.io/micl/checkbox.html) is available to interact with.
35
37
 
36
38
  ## Variants
37
- Adding the `micl-checkbox--error` CSS class to the `<input>` element will create an error-checkbox as specified by the Material Design 3 specification.
38
-
39
39
  A checkbox can be disabled by adding the `disabled` attribute to the `<input>` element.
40
40
 
41
- The Checkbox component respects the element's computed direction, automatically adjusting its layout for right-to-left (RTL) languages — whether the `dir` attribute (including `dir="auto"`) is set on the element itself or inherited from an ancestor.
41
+ The Checkbox component respects the element's computed direction, automatically adjusting its layout for right-to-left (RTL) languages — whether the `dir` attribute is set on the element itself or inherited from an ancestor.
42
+
43
+ Adding the `micl-checkbox--error` CSS class to the `<input>` element will create an error-checkbox as specified by the Material Design 3 specification. You only need to set the class yourself when you drive the error state by hand — see [Validation](#validation) for letting the form foundation add and remove it from the checkbox's own validity.
42
44
 
43
- The component applies `cursor: pointer` and the color role **on surface** to the `<label>` element immediately preceding or following an `<input type="checkbox">` with the `micl-checkbox` class. You are encouraged to customize these CSS settings to match your design system.
45
+ To vertically align a checkbox with its label, wrap both in an element with styling similar to the following:
46
+
47
+ ```CSS
48
+ .my-valign-class {
49
+ display: flex;
50
+ flex-direction: row;
51
+ align-items: center;
52
+ }
53
+ ```
54
+
55
+ ```HTML
56
+ <div class="my-valign-class">
57
+ <input type="checkbox" id="mycheckbox" class="micl-checkbox">
58
+ <label for="mycheckbox">Checkbox</label>
59
+ </div>
60
+ ```
44
61
 
45
62
  ## Checkbox group
46
63
  You can establish a parent-child relationship among checkboxes. To do this, wrap the entire set of related checkboxes in an element using the `micl-checkbox-group` class. The designated parent checkbox must also include the `micl-checkbox__parent` class.
@@ -59,26 +76,18 @@ You can establish a parent-child relationship among checkboxes. To do this, wrap
59
76
 
60
77
  To visually improve the layout, such as by indenting child checkboxes, use wrapper elements and utility classes:
61
78
 
62
- ```CSS
63
- .my-utility-class {
64
- display: flex;
65
- flex-direction: row;
66
- align-items: center;
67
- }
68
- ```
69
-
70
79
  ```HTML
71
80
  <div class="micl-checkbox-group">
72
- <div class="my-utility-class">
81
+ <div class="my-valign-class">
73
82
  <input type="checkbox" id="cb0" class="micl-checkbox micl-checkbox__parent" value="c0">
74
83
  <label for="cb0">Choices</label>
75
84
  </div>
76
- <div style="padding-inline-start:16px">
77
- <div class="my-utility-class">
85
+ <div role="group" style="padding-inline-start:16px">
86
+ <div class="my-valign-class">
78
87
  <input type="checkbox" id="cb1" class="micl-checkbox" value="c1">
79
88
  <label for="cb1">First Choice</label>
80
89
  </div>
81
- <div class="my-utility-class">
90
+ <div class="my-valign-class">
82
91
  <input type="checkbox" id="cb2" class="micl-checkbox" checked value="c2">
83
92
  <label for="cb2">Second Choice</label>
84
93
  </div>
@@ -87,7 +96,66 @@ To visually improve the layout, such as by indenting child checkboxes, use wrapp
87
96
  </div>
88
97
  ```
89
98
 
90
- Note that checkbox groups support **nesting**, allowing a `micl-checkbox-group` to contain other `micl-checkbox-group` elements for multi-level hierarchies.
99
+ Note that checkbox groups support **nesting**, allowing a `micl-checkbox-group` to contain other `micl-checkbox-group` elements for multi-level hierarchies. Each group must contain exactly one `micl-checkbox__parent`; a nested group without one is not tracked by its ancestor.
100
+
101
+ The parent checkbox reflects the state of its descendants: **checked** when all of them are selected, **unchecked** when none are, and **indeterminate** when the selection is partial — including when that partial selection sits inside a nested group. Because an indeterminate parent is not itself checked, it is left out of form submission until everything below it is selected, and clicking it selects the whole group.
102
+
103
+ Disabled checkboxes take no part in this. The parent neither changes them nor counts them, so a group whose only unselected checkbox is disabled still reports as fully selected, and a disabled checkbox that is checked does not by itself make its parent indeterminate. A nested group containing nothing but disabled checkboxes is skipped entirely rather than counting as an unselected branch.
104
+
105
+ ## Validation
106
+ A single checkbox is made mandatory with the standard `required` attribute. On top of that, the form foundation adds **count validation** for a set of related checkboxes, so you can express rules such as "pick at least two" or "pick exactly three".
107
+
108
+ ### Error state
109
+ Whenever the form foundation validates a checkbox, it adds the `micl-checkbox--error` class to any `<input type="checkbox">` carrying the `micl-checkbox` class that has a validation message, and removes the class again once the checkbox becomes valid. If you are using the form foundation, there is no need to toggle this class manually.
110
+
111
+ ### Counting checkboxes in a fieldset
112
+ Wrap the related checkboxes in a `<fieldset>` and describe the rule with data attributes:
113
+
114
+ | Attribute | Meaning |
115
+ |---|---|
116
+ | `data-miclvalidate-checkboxes-name` | The `name` shared by the checkboxes to count. Required. |
117
+ | `data-miclvalidate-message` | The message shown when the rule is broken. Required — without it the fieldset is skipped. |
118
+ | `data-miclvalidate-checkboxes-count-min` | The smallest allowed number of checked checkboxes. |
119
+ | `data-miclvalidate-checkboxes-count-max` | The largest allowed number of checked checkboxes. |
120
+ | `data-miclvalidate-checkboxes-count-equal` | The exact number of checked checkboxes required. |
121
+
122
+ At least one of the three `count` attributes must be present. They may be combined, and the fieldset is invalid as soon as any one of them is violated.
123
+
124
+ ```HTML
125
+ <form id="myform">
126
+ <fieldset
127
+ data-miclvalidate-checkboxes-name="topping"
128
+ data-miclvalidate-checkboxes-count-min="2"
129
+ data-miclvalidate-checkboxes-count-max="3"
130
+ data-miclvalidate-message="Please choose two or three toppings.">
131
+ <legend>Toppings</legend>
132
+ <input type="checkbox" id="t1" class="micl-checkbox" name="topping" value="olives">
133
+ <label for="t1">Olives</label>
134
+ <input type="checkbox" id="t2" class="micl-checkbox" name="topping" value="capers">
135
+ <label for="t2">Capers</label>
136
+ ...
137
+ </fieldset>
138
+ </form>
139
+ ```
140
+
141
+ ### JavaScript
142
+ Count validation must be triggered manually. Import the form foundation and call it, typically within a submit handler:
143
+
144
+ ```JavaScript
145
+ import form from "material-inspired-component-library/dist/foundations/form";
146
+
147
+ document.getElementById("myform").addEventListener("submit", event => {
148
+ if (!form.validateForm(event.currentTarget, true)) {
149
+ event.preventDefault();
150
+ }
151
+ });
152
+ ```
153
+
154
+ `validateForm(form, doReport)` checks every fieldset and every control in the form and returns `true` when all of them are valid. `validateFieldSet(fieldset, doReport)` does the same for a single `<fieldset>`. When `doReport` is `true` the browser additionally shows its own validation bubble; omit it to apply the error state silently.
155
+
156
+ The message is attached to the **first** checkbox of the named set — that is the control that reports as invalid — and is cleared again as soon as that checkbox changes.
157
+
158
+ Note that the counted set is defined by the `name` attribute, independently of the checkbox-group classes. If you combine count validation with a [checkbox group](#checkbox-group), give the `micl-checkbox__parent` checkbox a **different** `name` or none at all, otherwise it is counted alongside its children once they are all selected.
91
159
 
92
160
  ## Theming
93
161
  Each checkbox 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 checkboxes.
@@ -95,6 +163,7 @@ Each checkbox can be themed with CSS custom properties that follow the Material
95
163
  | Custom property | Meaning | Default |
96
164
  |---|---|---|
97
165
  | `--md-comp-checkbox-container-size` | The size of the checkbox itself | `18px` |
166
+ | `--md-comp-checkbox-container-shape` | The corner radius of the checkbox | `2px` |
98
167
  | `--md-comp-checkbox-outline-width` | The thickness of the checkbox's border | `2px` |
99
168
  | `--md-comp-checkbox-check-thickness` | The thickness of the check mark | `2px` |
100
169
  | `--md-comp-checkbox-unselected-outline-color` | The border color of an unselected checkbox | `--md-sys-color-on-surface-variant` |
@@ -110,14 +179,14 @@ Each checkbox can be themed with CSS custom properties that follow the Material
110
179
  </div>
111
180
  ```
112
181
 
113
- To vertically align a checkbox with its label, wrap both in an element that has a styling like suggested below:
182
+ ## Compatibility
183
+ This component relies on several recent CSS features, which may not be fully supported in your browser:
114
184
 
115
- ```HTML
116
- <div style="display:flex;flex-direction:row;align-items:center">
117
- <input type="checkbox" id="mycheckbox" class="micl-checkbox">
118
- <label for="mycheckbox">Checkbox</label>
119
- </div>
120
- ```
185
+ | Feature | Used for |
186
+ |---|---|
187
+ | [Relative RGB color values](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#browser_compatibility) | The state layer and the ripple |
188
+ | [`clip-path: rect()`](https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape/rect) | Revealing the check mark — without it the mark does not draw correctly |
189
+ | [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) | Styling a `<label>` that precedes its checkbox |
190
+ | [`:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir) | Mirroring the check mark for right-to-left languages |
121
191
 
122
- ## Compatibility
123
- This component utilizes relative RGB color values, which may not be fully supported in your browser. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#browser_compatibility) for details.
192
+ Please check the linked browser-compatibility tables for details.