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
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
@use '../../styles/motion';
|
|
23
23
|
@use '../../styles/shape';
|
|
24
24
|
@use '../../styles/statelayer';
|
|
25
|
-
@use '../../styles/
|
|
26
|
-
@use './label';
|
|
25
|
+
@use '../../styles/togglelabel';
|
|
27
26
|
|
|
28
27
|
@include shape.corner('full');
|
|
29
28
|
|
|
@@ -33,6 +32,7 @@
|
|
|
33
32
|
@include statelayer.token('pressed-state-layer-opacity');
|
|
34
33
|
@include statelayer.token('disabled-state-layer-opacity');
|
|
35
34
|
@include statelayer.token('focus-indicator-thickness');
|
|
35
|
+
@include statelayer.token('focus-indicator-outer-offset');
|
|
36
36
|
@include statelayer.token('ripple-opacity-factor');
|
|
37
37
|
@include statelayer.token('ripple-duration');
|
|
38
38
|
@include statelayer.property;
|
|
@@ -51,28 +51,33 @@ $checkbox: 'input[type=checkbox].micl-checkbox';
|
|
|
51
51
|
--_size: var(--md-comp-checkbox-container-size, 18px);
|
|
52
52
|
--_outline-width: var(--md-comp-checkbox-outline-width, 2px);
|
|
53
53
|
--_check-thickness: var(--md-comp-checkbox-check-thickness, 2px);
|
|
54
|
+
--_layer: max(var(--md-sys-state-layer-size, 40px), calc(var(--_size) + 22px));
|
|
55
|
+
--_target: max(var(--md-sys-state-target-size, 48px), calc(var(--_layer) + 8px));
|
|
54
56
|
--statelayer-color: var(--md-sys-color-on-surface);
|
|
55
57
|
|
|
56
58
|
appearance: none;
|
|
57
59
|
box-sizing: border-box;
|
|
58
60
|
position: relative;
|
|
59
|
-
inline-size: var(--
|
|
60
|
-
min-inline-size: var(--
|
|
61
|
-
block-size: var(--
|
|
62
|
-
min-block-size: var(--
|
|
61
|
+
inline-size: var(--_target);
|
|
62
|
+
min-inline-size: var(--_target);
|
|
63
|
+
block-size: var(--_target);
|
|
64
|
+
min-block-size: var(--_target);
|
|
63
65
|
margin: 0;
|
|
64
|
-
border: calc((var(--
|
|
66
|
+
border: calc((var(--_target) - var(--_layer)) / 2) solid transparent;
|
|
65
67
|
background-clip: content-box;
|
|
66
68
|
background-color: transparent;
|
|
67
69
|
-webkit-tap-highlight-color: transparent;
|
|
68
70
|
border-radius: var(--md-sys-shape-corner-full);
|
|
69
|
-
outline-offset:
|
|
71
|
+
outline-offset: calc(
|
|
72
|
+
var(--md-sys-state-focus-indicator-outer-offset, 2px)
|
|
73
|
+
- ((var(--_target) - var(--_layer)) / 2)
|
|
74
|
+
);
|
|
70
75
|
|
|
71
76
|
// The check mark: two currentColor borders rotated into a check, revealed by a
|
|
72
77
|
// combination of block-size and clip-path transitions. currentColor, so that the
|
|
73
78
|
// error and disabled states recolour it with a single color declaration.
|
|
74
79
|
&::before {
|
|
75
|
-
--_check-width: calc(var(--_size)
|
|
80
|
+
--_check-width: calc(var(--_size) * 5 / 6);
|
|
76
81
|
--_check-height: calc(0.4667 * var(--_check-width));
|
|
77
82
|
|
|
78
83
|
content: "";
|
|
@@ -82,14 +87,14 @@ $checkbox: 'input[type=checkbox].micl-checkbox';
|
|
|
82
87
|
block-size: 0;
|
|
83
88
|
inset: 0;
|
|
84
89
|
inset-block-start: calc(-0.25 * var(--_size));
|
|
85
|
-
inset-inline-start:
|
|
90
|
+
inset-inline-start: calc(var(--_check-thickness) / 2);
|
|
86
91
|
margin: auto;
|
|
87
92
|
color: var(--md-comp-checkbox-selected-icon-color, var(--md-sys-color-on-primary));
|
|
88
93
|
border-block-end: var(--_check-thickness) currentColor solid;
|
|
89
94
|
border-inline-start: var(--_check-thickness) currentColor solid;
|
|
90
95
|
transform: rotate(-45deg);
|
|
91
96
|
transform-origin: center;
|
|
92
|
-
clip-path: rect(0
|
|
97
|
+
clip-path: rect(0 var(--_check-thickness) 0 0);
|
|
93
98
|
z-index: 1;
|
|
94
99
|
transition:
|
|
95
100
|
block-size var(--md-sys-motion-duration-short3) var(--md-sys-motion-duration-short3) motion.$md-sys-motion-easing-standard-decelerate,
|
|
@@ -111,7 +116,7 @@ $checkbox: 'input[type=checkbox].micl-checkbox';
|
|
|
111
116
|
inset: 0;
|
|
112
117
|
margin: auto;
|
|
113
118
|
border: var(--_outline-width) solid var(--_outline-color);
|
|
114
|
-
border-radius: 2px;
|
|
119
|
+
border-radius: var(--md-comp-checkbox-container-shape, 2px);
|
|
115
120
|
background-color: var(--_container-color);
|
|
116
121
|
transition:
|
|
117
122
|
background-color var(--md-comp-checkbox-motion-duration-reverse) motion.$md-sys-motion-easing-emphasized var(--md-comp-checkbox-motion-duration-reverse),
|
|
@@ -122,7 +127,7 @@ $checkbox: 'input[type=checkbox].micl-checkbox';
|
|
|
122
127
|
|
|
123
128
|
&::before {
|
|
124
129
|
block-size: var(--_check-height);
|
|
125
|
-
clip-path: rect(0 calc(var(--
|
|
130
|
+
clip-path: rect(0 calc(var(--_check-width) - var(--_check-thickness)) var(--_size) 0);
|
|
126
131
|
transition:
|
|
127
132
|
block-size var(--md-sys-motion-duration-short3) var(--md-comp-checkbox-motion-spatial),
|
|
128
133
|
clip-path var(--md-sys-motion-duration-long4) var(--md-sys-motion-duration-short3) var(--md-comp-checkbox-motion-spatial);
|
|
@@ -137,7 +142,7 @@ $checkbox: 'input[type=checkbox].micl-checkbox';
|
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
&:indeterminate::before {
|
|
140
|
-
inset-inline-start:
|
|
145
|
+
inset-inline-start: var(--_check-thickness);
|
|
141
146
|
border-inline-start-width: 0px;
|
|
142
147
|
transform: rotate(0deg);
|
|
143
148
|
}
|
|
@@ -241,28 +246,33 @@ $checkbox: 'input[type=checkbox].micl-checkbox';
|
|
|
241
246
|
}
|
|
242
247
|
|
|
243
248
|
#{$checkbox}:not(:disabled) + label,
|
|
244
|
-
label:has(+ #{$checkbox}:not(:disabled))
|
|
249
|
+
label:has(+ #{$checkbox}:not(:disabled)),
|
|
250
|
+
label:has(#{$checkbox}:not(:disabled)) {
|
|
245
251
|
@extend %micl-toggle-label-pointer;
|
|
246
252
|
}
|
|
247
253
|
#{$checkbox} + label,
|
|
248
|
-
label:has(+ #{$checkbox})
|
|
254
|
+
label:has(+ #{$checkbox}),
|
|
255
|
+
label:has(#{$checkbox}) {
|
|
249
256
|
@extend %micl-toggle-label;
|
|
250
257
|
}
|
|
251
258
|
#{$checkbox}:disabled + label,
|
|
252
259
|
label:has(+ #{$checkbox}:disabled) {
|
|
253
260
|
@extend %micl-toggle-label-disabled;
|
|
254
261
|
}
|
|
262
|
+
label:has(#{$checkbox}:disabled) {
|
|
263
|
+
@extend %micl-toggle-label-disabled-text;
|
|
264
|
+
}
|
|
255
265
|
|
|
256
266
|
#{$checkbox}:dir(rtl) {
|
|
257
267
|
&::before {
|
|
258
|
-
clip-path: rect(0 var(--_size) var(--_size) calc(var(--_size) -
|
|
268
|
+
clip-path: rect(0 var(--_size) var(--_size) calc(var(--_size) - var(--_check-thickness)));
|
|
259
269
|
}
|
|
260
270
|
&:checked::before {
|
|
261
|
-
clip-path: rect(0 var(--_size) var(--_size)
|
|
271
|
+
clip-path: rect(0 var(--_size) var(--_size) var(--_check-thickness));
|
|
262
272
|
transform: rotate(45deg);
|
|
263
273
|
}
|
|
264
274
|
&:indeterminate::before {
|
|
265
|
-
clip-path: rect(0 var(--_size) var(--_size)
|
|
275
|
+
clip-path: rect(0 var(--_size) var(--_size) var(--_check-thickness));
|
|
266
276
|
transform: rotate(0deg);
|
|
267
277
|
}
|
|
268
278
|
}
|
|
@@ -176,9 +176,9 @@
|
|
|
176
176
|
&::before {
|
|
177
177
|
content: "";
|
|
178
178
|
position: absolute;
|
|
179
|
-
inset-block-start: calc((100% - var(--md-sys-target-size, 48px)) / 2);
|
|
179
|
+
inset-block-start: calc((100% - var(--md-sys-state-target-size, 48px)) / 2);
|
|
180
180
|
inset-inline-start: 0;
|
|
181
|
-
block-size: var(--md-sys-target-size, 48px);
|
|
181
|
+
block-size: var(--md-sys-state-target-size, 48px);
|
|
182
182
|
inline-size: 100%;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
@@ -195,10 +195,10 @@
|
|
|
195
195
|
&::before {
|
|
196
196
|
content: "";
|
|
197
197
|
position: absolute;
|
|
198
|
-
inset-block-start: calc((100% - var(--md-sys-target-size, 48px)) / 2);
|
|
199
|
-
inset-inline-start: calc((100% - var(--md-sys-target-size, 48px)) / 2);
|
|
200
|
-
block-size: var(--md-sys-target-size, 48px);
|
|
201
|
-
inline-size: var(--md-sys-target-size, 48px);
|
|
198
|
+
inset-block-start: calc((100% - var(--md-sys-state-target-size, 48px)) / 2);
|
|
199
|
+
inset-inline-start: calc((100% - var(--md-sys-state-target-size, 48px)) / 2);
|
|
200
|
+
block-size: var(--md-sys-state-target-size, 48px);
|
|
201
|
+
inline-size: var(--md-sys-state-target-size, 48px);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
.micl-chip__avatar {
|
|
@@ -86,7 +86,7 @@ dialog.micl-dialog.micl-datepicker {
|
|
|
86
86
|
--md-comp-date-picker-button-motion-duration: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
|
|
87
87
|
|
|
88
88
|
// Internal alias: the target size is referenced from many descendants.
|
|
89
|
-
--_target: var(--md-sys-target-size, 48px);
|
|
89
|
+
--_target: var(--md-sys-state-target-size, 48px);
|
|
90
90
|
|
|
91
91
|
inline-size: var(--md-comp-date-picker-modal-container-width, 360px);
|
|
92
92
|
|
|
@@ -56,10 +56,10 @@ $iconbuttons: ':is([class*="micl-iconbutton-standard-"], [class*="micl-iconbutto
|
|
|
56
56
|
display: flex;
|
|
57
57
|
align-items: center;
|
|
58
58
|
justify-content: center;
|
|
59
|
-
block-size: var(--md-sys-target-size, 48px);
|
|
59
|
+
block-size: var(--md-sys-state-target-size, 48px);
|
|
60
60
|
inline-size: 100%;
|
|
61
|
-
min-inline-size: var(--md-sys-target-size, 48px);
|
|
62
|
-
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);
|
|
61
|
+
min-inline-size: var(--md-sys-state-target-size, 48px);
|
|
62
|
+
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);
|
|
63
63
|
background-color: transparent;
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -390,11 +390,15 @@ $unselected: ':not(:checked, :has(input[type=checkbox]:checked), #{$open-accordi
|
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
#{$items} {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
// The compact list controls. The checkbox and the radio button derive all four of their
|
|
394
|
+
// size properties and their transparent border from these two, so overriding them shrinks
|
|
395
|
+
// the box without leaving the pseudo-elements wider than the padding box that centres
|
|
396
|
+
// them: sizing the element directly left an 18px container inside a 16px padding box,
|
|
397
|
+
// which start-aligned it.
|
|
398
|
+
input[type=checkbox].micl-checkbox,
|
|
399
|
+
input[type=radio].micl-radio {
|
|
400
|
+
--_layer: max(24px, var(--_size));
|
|
401
|
+
--_target: var(--_layer);
|
|
398
402
|
}
|
|
399
403
|
}
|
|
400
404
|
|
|
@@ -8,12 +8,12 @@ To create a simple navigation rail, use a `<nav>` element with the `micl-navigat
|
|
|
8
8
|
|
|
9
9
|
```HTML
|
|
10
10
|
<nav id="mynavigationrail" class="micl-navigationrail">
|
|
11
|
-
<div class="micl-navigationrail__content"
|
|
12
|
-
<a href="#" class="micl-navigationrail__item"
|
|
11
|
+
<div class="micl-navigationrail__content">
|
|
12
|
+
<a href="#" class="micl-navigationrail__item" aria-current="page">
|
|
13
13
|
<span class="micl-navigationrail__icon material-symbols-outlined" aria-hidden="true">inbox</span>
|
|
14
14
|
<span class="micl-navigationrail__text">Inbox</span>
|
|
15
15
|
</a>
|
|
16
|
-
<a href="#" class="micl-navigationrail__item"
|
|
16
|
+
<a href="#" class="micl-navigationrail__item">
|
|
17
17
|
<span class="micl-navigationrail__icon material-symbols-outlined" aria-hidden="true">outbox</span>
|
|
18
18
|
<span class="micl-navigationrail__text">Outbox</span>
|
|
19
19
|
</a>
|
|
@@ -35,11 +35,12 @@ Or import all MICL styles:
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
### JavaScript
|
|
38
|
-
|
|
38
|
+
No custom JavaScript is required for the core functionality of this component.
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> Opening and closing a modal navigation rail is handled natively by the `<dialog>` element and its invoker buttons, and the navigation items are ordinary links, activated with `Enter` like any other link.
|
|
42
|
+
>
|
|
43
|
+
> Marking the current destination is left to the application: give the item for the current page the `aria-current="page"` attribute.
|
|
43
44
|
|
|
44
45
|
### Live Demo
|
|
45
46
|
A live example of the [Navigation rail component](https://henkpb.github.io/micl/navigationrail.html) is available to interact with.
|
|
@@ -62,7 +63,7 @@ The basic example creates a **collapsed** navigation rail. Add a menu button to
|
|
|
62
63
|
aria-label="Toggle navigation rail"
|
|
63
64
|
></button>
|
|
64
65
|
</div>
|
|
65
|
-
<div class="micl-navigationrail__content"
|
|
66
|
+
<div class="micl-navigationrail__content">
|
|
66
67
|
...
|
|
67
68
|
</div>
|
|
68
69
|
</nav>
|
|
@@ -86,7 +87,7 @@ A **modal** navigation rail is hidden until the user clicks a menu button. When
|
|
|
86
87
|
autofocus
|
|
87
88
|
>menu_open</button>
|
|
88
89
|
</div>
|
|
89
|
-
<div class="micl-navigationrail__content"
|
|
90
|
+
<div class="micl-navigationrail__content">
|
|
90
91
|
...
|
|
91
92
|
</div>
|
|
92
93
|
</dialog>
|
|
@@ -11,6 +11,8 @@ To add a basic radio button, use the `<input type="radio">` element with the `mi
|
|
|
11
11
|
<label for="myradio">First choice</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-radio` input. You are encouraged to customize these CSS settings to match your design system.
|
|
15
|
+
|
|
14
16
|
### CSS
|
|
15
17
|
Import the radio button styles into your project:
|
|
16
18
|
|
|
@@ -32,9 +34,49 @@ A live example of the [Radio button component](https://henkpb.github.io/micl/rad
|
|
|
32
34
|
## Variants
|
|
33
35
|
A radio button can be disabled by adding the `disabled` attribute to the `<input>` element.
|
|
34
36
|
|
|
35
|
-
The Radio
|
|
37
|
+
The Radio button 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.
|
|
38
|
+
|
|
39
|
+
To vertically align a radio button with its label, wrap both in an element with styling similar to the following:
|
|
40
|
+
|
|
41
|
+
```CSS
|
|
42
|
+
.my-valign-class {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: row;
|
|
45
|
+
align-items: center;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```HTML
|
|
50
|
+
<div class="my-valign-class">
|
|
51
|
+
<input type="radio" id="myradio" class="micl-radio" name="foo">
|
|
52
|
+
<label for="myradio">First choice</label>
|
|
53
|
+
</div>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Radio group
|
|
57
|
+
Radio buttons are only meaningful as a group. Give every radio button for a given question the same `name`, and the browser makes the choices mutually exclusive for you — selecting one clears the others, and only the selected value is submitted.
|
|
58
|
+
|
|
59
|
+
The group is also a single stop in the tab order: <kbd>Tab</kbd> moves into and out of it, while the arrow keys move the selection between the buttons inside it. This behaviour is native, so it needs no JavaScript and no `role` attributes.
|
|
36
60
|
|
|
37
|
-
|
|
61
|
+
Wrap the group in a `<fieldset>` with a `<legend>` so that assistive technology announces the question along with each of the options:
|
|
62
|
+
|
|
63
|
+
```HTML
|
|
64
|
+
<fieldset>
|
|
65
|
+
<legend>Delivery</legend>
|
|
66
|
+
<div class="my-valign-class">
|
|
67
|
+
<input type="radio" id="ship1" class="micl-radio" name="delivery" value="standard" checked>
|
|
68
|
+
<label for="ship1">Standard</label>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="my-valign-class">
|
|
71
|
+
<input type="radio" id="ship2" class="micl-radio" name="delivery" value="express">
|
|
72
|
+
<label for="ship2">Express</label>
|
|
73
|
+
</div>
|
|
74
|
+
</fieldset>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Always mark one option as `checked`, unless leaving the question unanswered is a valid state. A group with no preselected option cannot be returned to its initial state by the user once a choice has been made.
|
|
78
|
+
|
|
79
|
+
Adding the `required` attribute to a radio button makes the whole group mandatory — the browser will not submit the form until one of the buttons carrying that `name` is selected. The [Form foundation](../../foundations/form/README.md) leaves radio buttons to the browser's own reporting, so no MICL-specific error class is involved.
|
|
38
80
|
|
|
39
81
|
## Theming
|
|
40
82
|
Each radio button 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 radio buttons.
|
|
@@ -50,19 +92,17 @@ Each radio button can be themed with CSS custom properties that follow the Mater
|
|
|
50
92
|
|
|
51
93
|
```HTML
|
|
52
94
|
<div style="--md-comp-radio-button-icon-size:28px">
|
|
53
|
-
<input type="radio" id="myradio" class="micl-radio">
|
|
95
|
+
<input type="radio" id="myradio" class="micl-radio" name="foo">
|
|
54
96
|
<label for="myradio">Large radio button</label>
|
|
55
97
|
</div>
|
|
56
98
|
```
|
|
57
99
|
|
|
58
|
-
|
|
100
|
+
## Compatibility
|
|
101
|
+
This component relies on a few recent CSS features, which may not be fully supported in your browser:
|
|
59
102
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</div>
|
|
65
|
-
```
|
|
103
|
+
| Feature | Used for |
|
|
104
|
+
|---|---|
|
|
105
|
+
| [Relative RGB color values](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#browser_compatibility) | The state layer and the ripple |
|
|
106
|
+
| [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) | Styling a `<label>` that precedes its radio button |
|
|
66
107
|
|
|
67
|
-
|
|
68
|
-
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.
|
|
108
|
+
Please check the linked browser-compatibility tables for details.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
@use '../../styles/motion';
|
|
23
23
|
@use '../../styles/shape';
|
|
24
24
|
@use '../../styles/statelayer';
|
|
25
|
-
@use '
|
|
25
|
+
@use '../../styles/togglelabel';
|
|
26
26
|
|
|
27
27
|
@include shape.corner('full');
|
|
28
28
|
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
@include statelayer.token('pressed-state-layer-opacity');
|
|
33
33
|
@include statelayer.token('disabled-state-layer-opacity');
|
|
34
34
|
@include statelayer.token('focus-indicator-thickness');
|
|
35
|
+
@include statelayer.token('focus-indicator-outer-offset');
|
|
35
36
|
@include statelayer.token('ripple-opacity-factor');
|
|
36
37
|
@include statelayer.token('ripple-duration');
|
|
37
38
|
@include statelayer.property;
|
|
@@ -46,26 +47,31 @@ $radio: 'input[type=radio].micl-radio';
|
|
|
46
47
|
|
|
47
48
|
--_size: var(--md-comp-radio-button-icon-size, 20px);
|
|
48
49
|
--_outline-width: var(--md-comp-radio-button-outline-width, 2px);
|
|
50
|
+
--_layer: max(var(--md-sys-state-layer-size, 40px), calc(var(--_size) + 20px));
|
|
51
|
+
--_target: max(var(--md-sys-state-target-size, 48px), calc(var(--_layer) + 8px));
|
|
52
|
+
--statelayer-color: var(--md-sys-color-on-surface);
|
|
49
53
|
|
|
50
54
|
appearance: none;
|
|
51
55
|
box-sizing: border-box;
|
|
52
56
|
position: relative;
|
|
53
|
-
inline-size: var(--
|
|
54
|
-
min-inline-size: var(--
|
|
55
|
-
block-size: var(--
|
|
56
|
-
min-block-size: var(--
|
|
57
|
+
inline-size: var(--_target);
|
|
58
|
+
min-inline-size: var(--_target);
|
|
59
|
+
block-size: var(--_target);
|
|
60
|
+
min-block-size: var(--_target);
|
|
57
61
|
margin: 0;
|
|
58
|
-
border: calc((var(--
|
|
62
|
+
border: calc((var(--_target) - var(--_layer)) / 2) solid transparent;
|
|
59
63
|
background-clip: content-box;
|
|
60
64
|
background-color: transparent;
|
|
61
65
|
-webkit-tap-highlight-color: transparent;
|
|
62
66
|
border-radius: var(--md-sys-shape-corner-full);
|
|
63
|
-
outline-offset:
|
|
67
|
+
outline-offset: calc(
|
|
68
|
+
var(--md-sys-state-focus-indicator-outer-offset, 2px)
|
|
69
|
+
- ((var(--_target) - var(--_layer)) / 2)
|
|
70
|
+
);
|
|
64
71
|
|
|
65
|
-
// The ring and the dot in one box: the dot is the primary background clipped to the
|
|
66
|
-
// content box, hidden while the padding fills the ring, revealed when the checked
|
|
67
|
-
// padding shrinks to reveal a dot of half the icon size.
|
|
68
72
|
&::after {
|
|
73
|
+
--_outline-color: var(--md-comp-radio-button-unselected-icon-color, var(--md-sys-color-on-surface-variant));
|
|
74
|
+
|
|
69
75
|
content: "";
|
|
70
76
|
box-sizing: border-box;
|
|
71
77
|
position: absolute;
|
|
@@ -74,7 +80,7 @@ $radio: 'input[type=radio].micl-radio';
|
|
|
74
80
|
inset: 0;
|
|
75
81
|
margin: auto;
|
|
76
82
|
padding: calc((var(--_size) / 2) - var(--_outline-width));
|
|
77
|
-
border: var(--_outline-width) solid var(--
|
|
83
|
+
border: var(--_outline-width) solid var(--_outline-color);
|
|
78
84
|
background-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
|
|
79
85
|
background-clip: content-box;
|
|
80
86
|
border-radius: var(--md-sys-shape-corner-full);
|
|
@@ -82,12 +88,17 @@ $radio: 'input[type=radio].micl-radio';
|
|
|
82
88
|
padding var(--md-comp-radio-button-motion-duration-reverse) var(--md-comp-radio-button-motion-effects),
|
|
83
89
|
border-color var(--md-comp-radio-button-motion-duration-reverse) motion.$md-sys-motion-easing-emphasized;
|
|
84
90
|
}
|
|
85
|
-
&:checked
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
&:checked {
|
|
92
|
+
--statelayer-color: var(--md-sys-color-primary);
|
|
93
|
+
|
|
94
|
+
&::after {
|
|
95
|
+
--_outline-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
|
|
96
|
+
|
|
97
|
+
padding: calc((var(--_size) / 4) - var(--_outline-width));
|
|
98
|
+
transition:
|
|
99
|
+
padding var(--md-comp-radio-button-motion-duration) var(--md-comp-radio-button-motion-effects),
|
|
100
|
+
border-color var(--md-comp-radio-button-motion-duration) motion.$md-sys-motion-easing-emphasized;
|
|
101
|
+
}
|
|
91
102
|
}
|
|
92
103
|
&:not(:disabled) {
|
|
93
104
|
--micl-ripple: 1;
|
|
@@ -108,31 +119,33 @@ $radio: 'input[type=radio].micl-radio';
|
|
|
108
119
|
background-size 0ms,
|
|
109
120
|
--statelayer-opacity var(--md-comp-radio-button-motion-duration) linear;
|
|
110
121
|
|
|
111
|
-
&:hover,
|
|
112
|
-
&:focus-visible,
|
|
113
|
-
&:active {
|
|
114
|
-
--statelayer-color: var(--md-sys-color-on-surface);
|
|
115
|
-
|
|
116
|
-
&:checked {
|
|
117
|
-
--statelayer-color: var(--md-sys-color-primary);
|
|
118
|
-
}
|
|
119
|
-
&::after {
|
|
120
|
-
border-color: var(--md-sys-color-on-surface);
|
|
121
|
-
}
|
|
122
|
-
&:checked::after {
|
|
123
|
-
border-color: var(--md-comp-radio-button-selected-icon-color, var(--md-sys-color-primary));
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
122
|
&:hover {
|
|
127
123
|
--statelayer-opacity: var(--md-sys-state-hover-state-layer-opacity, 8%);
|
|
124
|
+
|
|
125
|
+
&:not(:checked)::after {
|
|
126
|
+
--_outline-color: var(--md-sys-color-on-surface);
|
|
127
|
+
}
|
|
128
128
|
}
|
|
129
129
|
&:focus-visible {
|
|
130
130
|
--statelayer-opacity: var(--md-sys-state-focus-state-layer-opacity, 10%);
|
|
131
131
|
|
|
132
132
|
outline: var(--md-sys-state-focus-indicator-thickness, 3px) solid var(--md-sys-color-secondary);
|
|
133
|
+
|
|
134
|
+
&:not(:checked)::after {
|
|
135
|
+
--_outline-color: var(--md-sys-color-on-surface);
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
&:active {
|
|
139
|
+
--statelayer-color: var(--md-sys-color-on-surface);
|
|
135
140
|
--statelayer-opacity: var(--md-sys-state-pressed-state-layer-opacity, 10%);
|
|
141
|
+
|
|
142
|
+
&:not(:checked) {
|
|
143
|
+
--statelayer-color: var(--md-sys-color-primary);
|
|
144
|
+
|
|
145
|
+
&::after {
|
|
146
|
+
--_outline-color: var(--md-sys-color-on-surface);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
136
149
|
}
|
|
137
150
|
&.micl-rippling {
|
|
138
151
|
animation: micl-ripple var(--md-sys-state-ripple-duration);
|
|
@@ -142,7 +155,7 @@ $radio: 'input[type=radio].micl-radio';
|
|
|
142
155
|
opacity: var(--md-sys-state-disabled-state-layer-opacity, 38%);
|
|
143
156
|
|
|
144
157
|
&::after {
|
|
145
|
-
|
|
158
|
+
--_outline-color: var(--md-sys-color-on-surface);
|
|
146
159
|
}
|
|
147
160
|
&:checked::after {
|
|
148
161
|
background-color: var(--md-sys-color-on-surface);
|
|
@@ -151,14 +164,19 @@ $radio: 'input[type=radio].micl-radio';
|
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
#{$radio}:not(:disabled) + label,
|
|
154
|
-
label:has(+ #{$radio}:not(:disabled))
|
|
167
|
+
label:has(+ #{$radio}:not(:disabled)),
|
|
168
|
+
label:has(#{$radio}:not(:disabled)) {
|
|
155
169
|
@extend %micl-toggle-label-pointer;
|
|
156
170
|
}
|
|
157
171
|
#{$radio} + label,
|
|
158
|
-
label:has(+ #{$radio})
|
|
172
|
+
label:has(+ #{$radio}),
|
|
173
|
+
label:has(#{$radio}) {
|
|
159
174
|
@extend %micl-toggle-label;
|
|
160
175
|
}
|
|
161
176
|
#{$radio}:disabled + label,
|
|
162
177
|
label:has(+ #{$radio}:disabled) {
|
|
163
178
|
@extend %micl-toggle-label-disabled;
|
|
164
179
|
}
|
|
180
|
+
label:has(#{$radio}:disabled) {
|
|
181
|
+
@extend %micl-toggle-label-disabled-text;
|
|
182
|
+
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
// re-anchor it to this button's box.
|
|
62
62
|
&::before {
|
|
63
63
|
inset: auto;
|
|
64
|
-
inset-block-start: calc(anchor(var(--micl-stepper-anchor) center) - var(--md-sys-target-size, 48px) / 2);
|
|
64
|
+
inset-block-start: calc(anchor(var(--micl-stepper-anchor) center) - var(--md-sys-state-target-size, 48px) / 2);
|
|
65
65
|
inset-inline-start: anchor(var(--micl-stepper-anchor) start);
|
|
66
66
|
inset-inline-end: anchor(var(--micl-stepper-anchor) end);
|
|
67
67
|
inline-size: auto;
|
|
@@ -11,6 +11,8 @@ To add a basic switch, use the `<input type="checkbox">` element with the `micl-
|
|
|
11
11
|
<label for="myswitch">My choice</label>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
**Note on labels**: The component applies `cursor: pointer` and the color role **on surface** to the `<label>` element immediately preceding, immediately following, or wrapping the `<input type="checkbox">` with the `micl-switch` class. You are encouraged to customize these CSS settings to match your design system.
|
|
15
|
+
|
|
14
16
|
### CSS
|
|
15
17
|
Import the switch styles into your project:
|
|
16
18
|
|
|
@@ -30,7 +32,7 @@ No custom JavaScript is required for the core functionality of this component.
|
|
|
30
32
|
A live example of the [Switch component](https://henkpb.github.io/micl/switch.html) is available to interact with.
|
|
31
33
|
|
|
32
34
|
## Variants
|
|
33
|
-
By default, the component displays an icon on the switch handle in both the selected and unselected state. To remove the icon in the unselected state,
|
|
35
|
+
By default, the component displays an icon on the switch handle in both the selected and unselected state. To remove the icon in the unselected state, set the corresponding CSS custom property to an empty string:
|
|
34
36
|
|
|
35
37
|
```CSS
|
|
36
38
|
#myswitch {
|
|
@@ -48,8 +50,6 @@ A switch can be disabled by adding the `disabled` attribute to the `<input>` ele
|
|
|
48
50
|
|
|
49
51
|
The Switch 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.
|
|
50
52
|
|
|
51
|
-
The component applies `cursor: pointer` and the color role **on surface** to the `<label>` element immediately preceding or following the `<input type="checkbox">` with the `micl-switch` class. You are encouraged to customize these CSS settings to match your design system.
|
|
52
|
-
|
|
53
53
|
## Theming
|
|
54
54
|
Each switch 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 switches.
|
|
55
55
|
|
|
@@ -68,6 +68,25 @@ Each switch can be themed with CSS custom properties that follow the Material De
|
|
|
68
68
|
| `--md-comp-switch-selected-track-color` | The track color when the switch is "on" | `--md-sys-color-primary` |
|
|
69
69
|
| `--md-comp-switch-selected-handle-color` | The handle color when the switch is "on" | `--md-sys-color-on-primary` |
|
|
70
70
|
| `--md-comp-switch-selected-icon-color` | The icon color when the switch is "on" | `--md-sys-color-on-primary-container` |
|
|
71
|
+
| `--md-comp-switch-unselected-hover-handle-color` | The handle color when the switch is "off" and hovered | `--md-sys-color-on-surface-variant` |
|
|
72
|
+
| `--md-comp-switch-unselected-focus-handle-color` | The handle color when the switch is "off" and focused | `--md-sys-color-on-surface-variant` |
|
|
73
|
+
| `--md-comp-switch-unselected-pressed-handle-color` | The handle color when the switch is "off" and pressed | `--md-sys-color-on-surface-variant` |
|
|
74
|
+
| `--md-comp-switch-selected-hover-handle-color` | The handle color when the switch is "on" and hovered | `--md-sys-color-primary-container` |
|
|
75
|
+
| `--md-comp-switch-selected-focus-handle-color` | The handle color when the switch is "on" and focused | `--md-sys-color-primary-container` |
|
|
76
|
+
| `--md-comp-switch-selected-pressed-handle-color` | The handle color when the switch is "on" and pressed | `--md-sys-color-primary-container` |
|
|
77
|
+
| `--md-comp-switch-unselected-state-layer-color` | The state layer color around the handle when the switch is "off" | `--md-sys-color-on-surface` |
|
|
78
|
+
| `--md-comp-switch-selected-state-layer-color` | The state layer color around the handle when the switch is "on" | `--md-sys-color-primary` |
|
|
79
|
+
| `--md-comp-switch-focus-indicator-color` | The color of the focus indicator | `--md-sys-color-secondary` |
|
|
80
|
+
| `--md-comp-switch-disabled-unselected-track-color` | The track color when the switch is disabled and "off" | `--md-sys-color-surface-container-highest` |
|
|
81
|
+
| `--md-comp-switch-disabled-unselected-track-outline-color` | The track border color when the switch is disabled and "off" | `--md-sys-color-on-surface` |
|
|
82
|
+
| `--md-comp-switch-disabled-unselected-handle-color` | The handle color when the switch is disabled and "off" | `--md-sys-color-on-surface` |
|
|
83
|
+
| `--md-comp-switch-disabled-unselected-icon-color` | The icon color when the switch is disabled and "off" | `--md-sys-color-surface-container-highest` |
|
|
84
|
+
| `--md-comp-switch-disabled-selected-track-color` | The track color when the switch is disabled and "on" | `--md-sys-color-on-surface` |
|
|
85
|
+
| `--md-comp-switch-disabled-selected-handle-color` | The handle color when the switch is disabled and "on" | `--md-sys-color-surface` |
|
|
86
|
+
| `--md-comp-switch-disabled-selected-icon-color` | The icon color when the switch is disabled and "on" | `--md-sys-color-on-surface` |
|
|
87
|
+
| `--md-comp-switch-disabled-track-opacity` | The opacity of the track when the switch is disabled | `12%` |
|
|
88
|
+
|
|
89
|
+
Because this component follows the Material Design 3 token set, the handle requires distinct colors for each interaction state. Modifying `--md-comp-switch-selected-handle-color` only changes the *resting* state. To ensure a consistent theme, remember to also override the matching `-hover-`, `-focus-`, and `-pressed-` properties.
|
|
71
90
|
|
|
72
91
|
**Example: Changing the width of the switch**
|
|
73
92
|
|
|
@@ -79,4 +98,13 @@ Each switch can be themed with CSS custom properties that follow the Material De
|
|
|
79
98
|
```
|
|
80
99
|
|
|
81
100
|
## Compatibility
|
|
82
|
-
This component
|
|
101
|
+
This component relies on several recent CSS features, which may not be fully supported in your browser:
|
|
102
|
+
|
|
103
|
+
| Feature | Used for |
|
|
104
|
+
|---|---|
|
|
105
|
+
| [Relative RGB color values](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#browser_compatibility) | Dimming the icon of a disabled, selected switch |
|
|
106
|
+
| [`color-mix()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix) | The state layer around the handle |
|
|
107
|
+
| [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) | Styling a `<label>` that precedes its switch |
|
|
108
|
+
| [`:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir) | Mirroring the icon for right-to-left languages |
|
|
109
|
+
|
|
110
|
+
Please check the linked browser-compatibility tables for details.
|