material-inspired-component-library 9.0.2 → 9.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/components/bottomsheet/index.scss +2 -2
- package/components/button/_shared.scss +3 -3
- package/components/card/README.md +55 -25
- package/components/card/index.scss +57 -38
- package/components/checkbox/README.md +97 -28
- package/components/checkbox/index.scss +29 -19
- package/components/chip/index.scss +6 -6
- package/components/datepicker/index.scss +1 -1
- package/components/iconbutton/index.scss +3 -3
- package/components/list/index.scss +9 -5
- package/components/navigationrail/README.md +10 -9
- package/components/radio/README.md +52 -12
- package/components/radio/index.scss +53 -35
- package/components/stepper/index.scss +1 -1
- package/components/switch/README.md +32 -4
- package/components/switch/index.scss +45 -40
- package/components/timepicker/index.scss +1 -1
- package/dist/appbar.css +1 -1
- package/dist/bottomsheet.css +1 -1
- package/dist/button.css +1 -1
- package/dist/card.css +1 -1
- package/dist/checkbox.css +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/chip.css +1 -1
- package/dist/components/checkbox/index.d.ts +1 -0
- package/dist/datepicker.css +1 -1
- package/dist/foundations/form/index.js +1 -1
- package/dist/iconbutton.css +1 -1
- package/dist/layout.css +1 -1
- package/dist/list.css +1 -1
- package/dist/micl.css +1 -1
- package/dist/micl.d.ts +0 -1
- package/dist/micl.js +1 -1
- package/dist/navigationbar.css +1 -1
- package/dist/navigationrail.css +1 -1
- package/dist/navigationrail.js +1 -1
- package/dist/radio.css +1 -1
- package/dist/sidesheet.css +1 -1
- package/dist/snackbar.css +1 -1
- package/dist/stepper.css +1 -1
- package/dist/stepper.js +1 -1
- package/dist/switch.css +1 -1
- package/dist/timepicker.css +1 -1
- package/foundations/layout/index.scss +6 -3
- package/package.json +3 -3
- package/{components/checkbox/_label.scss → styles/togglelabel.scss} +11 -3
- package/dist/components/navigationrail/index.d.ts +0 -7
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 ✅
|
|
@@ -201,6 +202,9 @@ The library currently consists of the following components:
|
|
|
201
202
|
|
|
202
203
|
## Change Log ↪️
|
|
203
204
|
|
|
205
|
+
### 9.1.0 (05.09.2026)
|
|
206
|
+
- **Navigation rail**: Dropped the JavaScript.
|
|
207
|
+
|
|
204
208
|
### 9.0.0 (20.07.2026)
|
|
205
209
|
- **BREAKING**: Renamed all CSS custom properties from --md-sys- to --md-comp-.
|
|
206
210
|
- **BREAKING**: Renamed the layout class `micl-body` and its `micl-body--*` modifiers to `micl-panes`/`micl-panes--*`, aligning with the M3 Expressive layout terminology.
|
|
@@ -81,7 +81,7 @@ dialog.micl-bottomsheet {
|
|
|
81
81
|
flex-shrink: 0;
|
|
82
82
|
align-items: center;
|
|
83
83
|
inline-size: 100%;
|
|
84
|
-
block-size: var(--md-sys-target-size, 48px);
|
|
84
|
+
block-size: var(--md-sys-state-target-size, 48px);
|
|
85
85
|
justify-content: center;
|
|
86
86
|
cursor: grab;
|
|
87
87
|
touch-action: none;
|
|
@@ -90,7 +90,7 @@ dialog.micl-bottomsheet {
|
|
|
90
90
|
box-sizing: content-box;
|
|
91
91
|
inline-size: var(--md-comp-bottomsheet-drag-handle-width);
|
|
92
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);
|
|
93
|
+
padding: 16px calc((var(--md-sys-state-target-size, 48px) - var(--md-comp-bottomsheet-drag-handle-width)) / 2);
|
|
94
94
|
border: none;
|
|
95
95
|
// only shapes the focus ring; the visible handle is the ::before below
|
|
96
96
|
border-radius: var(--md-sys-shape-corner-extra-large);
|
|
@@ -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
|
}
|
|
@@ -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
|
-
##
|
|
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
|
+
## Card Content Structure
|
|
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"
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
&:not(
|
|
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
|
|
108
|
-
--md-comp-card-motion-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
|
-
|
|
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
|
-
|
|
127
|
+
text-decoration: none;
|
|
124
128
|
opacity: var(--_card-opacity, 1);
|
|
125
|
-
|
|
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
|
-
|
|
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(--
|
|
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(--
|
|
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
|
|
188
|
-
outline-offset: var(--_card-focus-outline-offset
|
|
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
|
-
|
|
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(--
|
|
204
|
-
content-visibility var(--
|
|
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
|
-
&>
|
|
305
|
+
&> #{$headlines}:first-child {
|
|
293
306
|
padding-block-start: var(--md-comp-card-content-padding-block, 16px);
|
|
294
307
|
}
|
|
295
308
|
}
|
|
296
309
|
|
|
297
|
-
|
|
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
|
-
|
|
309
|
-
|
|
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:
|
|
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
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
182
|
+
## Compatibility
|
|
183
|
+
This component relies on several recent CSS features, which may not be fully supported in your browser:
|
|
114
184
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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.
|