UNPKG

17.8 kBSCSSView Raw
1//
2// Copyright 2016 Google Inc.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
23// Selector '.mdc-*' should only be used in this project.
24// stylelint-disable selector-class-pattern
25
26@use 'sass:map';
27@use 'sass:math';
28@use '@material/animation/functions' as functions2;
29@use '@material/animation/variables' as animation-variables;
30@use '@material/density/functions' as density-functions;
31@use '@material/dom/dom';
32@use '@material/feature-targeting/feature-targeting';
33@use '@material/ripple/ripple';
34@use '@material/ripple/ripple-theme';
35@use '@material/touch-target/mixins' as touch-target-mixins;
36@use '@material/theme/theme-color';
37@use './checkbox-custom-properties';
38@use '@material/theme/theme';
39@use '@material/theme/color-custom-properties';
40@use '@material/theme/custom-properties';
41@use '@material/touch-target/variables' as touch-target-variables;
42@use './checkbox-theme';
43
44///
45/// Checkbox and ripple styles.
46///
47@mixin core-styles($query: feature-targeting.all()) {
48 @include without-ripple($query);
49 @include ripple-styles($query);
50}
51
52/// Checkbox styles (Excluding ripple styles).
53///
54/// NOTE: This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
55/// checkbox styles. It is recommended that most users use `mdc-checkbox-core-styles` instead.
56// TODO(b/162887560): Rename to `checkbox-without-ripple-styles()`
57@mixin without-ripple($query: feature-targeting.all()) {
58 // TODO(b/165005345): Include theme-styles() after static-styles().
59 @include theme-styles($query: $query);
60 @include static-styles($query: $query);
61}
62
63/// Checkbox static styles.
64/// Checkbox styles that are not customizable should go here.
65@mixin static-styles($query: feature-targeting.all()) {
66 $feat-animation: feature-targeting.create-target($query, animation);
67 $feat-structure: feature-targeting.create-target($query, structure);
68
69 @include touch-target-mixins.wrapper($query); // COPYBARA_COMMENT_THIS_LINE
70
71 @include feature-targeting.targets($feat-animation) {
72 @include mark-keyframes_;
73 }
74
75 .mdc-checkbox {
76 @include feature-targeting.targets($feat-structure) {
77 @include base_;
78 }
79 }
80
81 @include dom.forced-colors-mode {
82 @include checkbox-theme.disabled-container-colors(
83 $unmarked-stroke-color: GrayText,
84 $unmarked-fill-color: transparent,
85 $marked-stroke-color: GrayText,
86 $marked-fill-color: transparent,
87 $query: $query
88 );
89 @include checkbox-theme.disabled-ink-color(GrayText, $query: $query);
90
91 .mdc-checkbox__mixedmark {
92 @include feature-targeting.targets($feat-structure) {
93 margin: 0 1px; // Extra horizontal space around mixedmark symbol.
94 }
95 }
96 }
97
98 // Needed to disable hover effects on CSS-only (non-JS) checkboxes
99 .mdc-checkbox--disabled {
100 @include feature-targeting.targets($feat-structure) {
101 @include disabled_;
102 }
103 }
104
105 .mdc-checkbox__background {
106 @include background_($query);
107 }
108
109 .mdc-checkbox__checkmark {
110 @include checkmark_($query);
111 }
112
113 .mdc-checkbox__checkmark-path {
114 @include checkmark-path_($query);
115 }
116
117 .mdc-checkbox__mixedmark {
118 @include mixedmark_($query);
119 }
120
121 .mdc-checkbox--anim {
122 @include feature-targeting.targets($feat-animation) {
123 @include anim_;
124 }
125 }
126
127 .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background,
128 .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background,
129 .mdc-checkbox__native-control[data-indeterminate='true']
130 ~ .mdc-checkbox__background {
131 @include feature-targeting.targets($feat-animation) {
132 @include background--marked_;
133 }
134
135 .mdc-checkbox__checkmark-path {
136 @include feature-targeting.targets($feat-structure) {
137 @include checkmark-path--marked_;
138 }
139 }
140 }
141
142 .mdc-checkbox__native-control {
143 @include feature-targeting.targets($feat-structure) {
144 @include native-control_;
145 }
146
147 &:disabled {
148 @include feature-targeting.targets($feat-structure) {
149 @include disabled_;
150 }
151 }
152 }
153
154 .mdc-checkbox--touch {
155 @include checkbox-theme.touch-target(
156 custom-properties.create(
157 checkbox-state-layer-size,
158 touch-target-variables.$height
159 ),
160 custom-properties.create(
161 checkbox-state-layer-size,
162 checkbox-theme.$ripple-size
163 ),
164 $query: $query
165 );
166 }
167
168 .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background {
169 .mdc-checkbox__checkmark {
170 @include checkmark--checked_($query);
171 }
172
173 .mdc-checkbox__mixedmark {
174 @include feature-targeting.targets($feat-structure) {
175 @include mixedmark--checked_;
176 }
177 }
178 }
179 .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background,
180 .mdc-checkbox__native-control[data-indeterminate='true']
181 ~ .mdc-checkbox__background {
182 .mdc-checkbox__checkmark {
183 @include checkmark--indeterminate_($query);
184 }
185
186 .mdc-checkbox__mixedmark {
187 @include feature-targeting.targets($feat-structure) {
188 @include mixedmark--indeterminate_;
189 }
190 }
191 }
192
193 // JS checkbox
194 .mdc-checkbox.mdc-checkbox--upgraded {
195 .mdc-checkbox__background,
196 .mdc-checkbox__checkmark,
197 .mdc-checkbox__checkmark-path,
198 .mdc-checkbox__mixedmark {
199 @include feature-targeting.targets($feat-animation) {
200 @include child--upgraded_;
201 }
202 }
203 }
204}
205
206/// Checkbox theme styles.
207/// Checkbox styles that are customizable should go here.
208@mixin theme-styles($query: feature-targeting.all()) {
209 .mdc-checkbox {
210 @include checkbox-theme.theme-deprecated(
211 checkbox-theme.$light-theme-deprecated,
212 $query: $query
213 );
214 }
215}
216
217/// Checkbox's ripple styles.
218///
219/// NOTE: This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
220/// checkbox styles. It is recommended that most users use `mdc-checkbox-core-styles` instead.
221@mixin ripple-styles($query: feature-targeting.all()) {
222 $feat-structure: feature-targeting.create-target($query, structure);
223
224 @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
225
226 .mdc-checkbox {
227 @include ripple.surface(
228 $query: $query,
229 $ripple-target: checkbox-theme.$ripple-target
230 );
231 @include ripple.radius-unbounded(
232 $query: $query,
233 $ripple-target: checkbox-theme.$ripple-target
234 );
235 @include ripple-theme.behind-content(
236 checkbox-theme.$ripple-target,
237 $query: $query
238 );
239 }
240
241 #{checkbox-theme.$ripple-target} {
242 @include ripple.target-common($query: $query);
243 }
244}
245
246@mixin base_ {
247 display: inline-block;
248 position: relative;
249 flex: 0 0 checkbox-theme.$icon-size;
250 box-sizing: content-box;
251 width: checkbox-theme.$icon-size;
252 height: checkbox-theme.$icon-size;
253 line-height: 0;
254 white-space: nowrap;
255 cursor: pointer;
256 vertical-align: bottom;
257}
258
259@mixin disabled_ {
260 cursor: default;
261 pointer-events: none;
262}
263
264@mixin child--upgraded_ {
265 transition: none;
266}
267
268// Animation
269
270@mixin anim_ {
271 $mdc-checkbox-indeterminate-change-duration_: 500ms;
272
273 // stylelint-disable selector-max-type
274
275 &-unchecked-checked,
276 &-unchecked-indeterminate,
277 &-checked-unchecked,
278 &-indeterminate-unchecked {
279 .mdc-checkbox__background {
280 animation-duration: checkbox-theme.$transition-duration * 2;
281 animation-timing-function: linear;
282 }
283 }
284
285 &-unchecked-checked {
286 .mdc-checkbox__checkmark-path {
287 // Instead of delaying the animation, we simply multiply its length by 2 and begin the
288 // animation at 50% in order to prevent a flash of styles applied to a checked checkmark
289 // as the background is fading in before the animation begins.
290 animation: mdc-checkbox-unchecked-checked-checkmark-path
291 checkbox-theme.$transition-duration * 2 linear 0s;
292 transition: none;
293 }
294 }
295
296 &-unchecked-indeterminate {
297 .mdc-checkbox__mixedmark {
298 animation: mdc-checkbox-unchecked-indeterminate-mixedmark
299 checkbox-theme.$transition-duration linear 0s;
300 transition: none;
301 }
302 }
303
304 &-checked-unchecked {
305 .mdc-checkbox__checkmark-path {
306 animation: mdc-checkbox-checked-unchecked-checkmark-path
307 checkbox-theme.$transition-duration linear 0s;
308 transition: none;
309 }
310 }
311
312 &-checked-indeterminate {
313 .mdc-checkbox__checkmark {
314 animation: mdc-checkbox-checked-indeterminate-checkmark
315 checkbox-theme.$transition-duration linear 0s;
316 transition: none;
317 }
318
319 .mdc-checkbox__mixedmark {
320 animation: mdc-checkbox-checked-indeterminate-mixedmark
321 checkbox-theme.$transition-duration linear 0s;
322 transition: none;
323 }
324 }
325
326 &-indeterminate-checked {
327 .mdc-checkbox__checkmark {
328 animation: mdc-checkbox-indeterminate-checked-checkmark
329 $mdc-checkbox-indeterminate-change-duration_ linear 0s;
330 transition: none;
331 }
332
333 .mdc-checkbox__mixedmark {
334 animation: mdc-checkbox-indeterminate-checked-mixedmark
335 $mdc-checkbox-indeterminate-change-duration_ linear 0s;
336 transition: none;
337 }
338 }
339
340 &-indeterminate-unchecked {
341 .mdc-checkbox__mixedmark {
342 // stylelint-disable-next-line declaration-colon-space-after
343 animation: mdc-checkbox-indeterminate-unchecked-mixedmark
344 $mdc-checkbox-indeterminate-change-duration_ * 0.6 linear 0s;
345 transition: none;
346 }
347 }
348
349 // stylelint-enable selector-max-type
350}
351
352@mixin background_($query: feature-targeting.all()) {
353 $feat-animation: feature-targeting.create-target($query, animation);
354 $feat-structure: feature-targeting.create-target($query, structure);
355 $feat-color: feature-targeting.create-target($query, color);
356
357 @include feature-targeting.targets($feat-structure) {
358 display: inline-flex;
359 position: absolute;
360 align-items: center;
361 justify-content: center;
362 box-sizing: border-box;
363 width: checkbox-theme.$icon-size;
364 height: checkbox-theme.$icon-size;
365 // border-color is overridden by the mdc-checkbox-unmarked-stroke-color() mixin
366 border: checkbox-theme.$border-width solid currentColor;
367 border-radius: 2px;
368 background-color: transparent;
369 pointer-events: none;
370 will-change: background-color, border-color;
371 }
372
373 @include feature-targeting.targets($feat-animation) {
374 transition: transition-exit(background-color), transition-exit(border-color);
375 }
376}
377
378@mixin background--marked_ {
379 transition: transition-enter(border-color), transition-enter(background-color);
380}
381
382// stylelint-disable block-no-empty -- For backward compatibility.
383@mixin focus-indicator_($query: feature-targeting.all()) {
384}
385@mixin focus-indicator--focused_($query: feature-targeting.all()) {
386}
387// stylelint-enable block-no-empty
388
389// Native input
390
391@mixin native-control_ {
392 position: absolute;
393 margin: 0;
394 padding: 0;
395 opacity: 0;
396 cursor: inherit;
397}
398
399// Check mark
400
401@mixin checkmark_($query: feature-targeting.all()) {
402 $feat-animation: feature-targeting.create-target($query, animation);
403 $feat-structure: feature-targeting.create-target($query, structure);
404
405 @include feature-targeting.targets($feat-structure) {
406 position: absolute;
407 top: 0;
408 right: 0;
409 bottom: 0;
410 left: 0;
411 width: 100%;
412 opacity: 0;
413 }
414
415 @include feature-targeting.targets($feat-animation) {
416 transition: transition-exit(
417 opacity,
418 0ms,
419 checkbox-theme.$transition-duration * 2
420 );
421 }
422
423 .mdc-checkbox--upgraded & {
424 @include feature-targeting.targets($feat-structure) {
425 opacity: 1;
426 }
427 }
428}
429
430@mixin checkmark--checked_($query: feature-targeting.all()) {
431 $feat-animation: feature-targeting.create-target($query, animation);
432 $feat-structure: feature-targeting.create-target($query, structure);
433
434 @include feature-targeting.targets($feat-animation) {
435 transition: transition-enter(
436 opacity,
437 0ms,
438 checkbox-theme.$transition-duration * 2
439 ),
440 transition-enter(transform, 0ms, checkbox-theme.$transition-duration * 2);
441 }
442
443 @include feature-targeting.targets($feat-structure) {
444 opacity: 1;
445 }
446}
447
448@mixin checkmark--indeterminate_($query: feature-targeting.all()) {
449 $feat-animation: feature-targeting.create-target($query, animation);
450 $feat-structure: feature-targeting.create-target($query, structure);
451
452 @include feature-targeting.targets($feat-structure) {
453 transform: rotate(45deg);
454 opacity: 0;
455 }
456
457 @include feature-targeting.targets($feat-animation) {
458 transition: transition-exit(
459 opacity,
460 0ms,
461 checkbox-theme.$transition-duration
462 ),
463 transition-exit(transform, 0ms, checkbox-theme.$transition-duration);
464 }
465}
466
467// Check mark path
468
469@mixin checkmark-path_($query: feature-targeting.all()) {
470 $feat-animation: feature-targeting.create-target($query, animation);
471 $feat-structure: feature-targeting.create-target($query, structure);
472
473 @include feature-targeting.targets($feat-animation) {
474 transition: transition-exit(
475 stroke-dashoffset,
476 0ms,
477 checkbox-theme.$transition-duration * 2
478 );
479 }
480
481 @include feature-targeting.targets($feat-structure) {
482 stroke: currentColor;
483 stroke-width: checkbox-theme.$mark-stroke-size * 1.3;
484 stroke-dashoffset: $mark-path-length_;
485 stroke-dasharray: $mark-path-length_;
486 }
487}
488
489@mixin checkmark-path--marked_ {
490 stroke-dashoffset: 0;
491}
492
493// Mixed mark
494
495@mixin mixedmark_($query: feature-targeting.all()) {
496 $feat-animation: feature-targeting.create-target($query, animation);
497 $feat-structure: feature-targeting.create-target($query, structure);
498
499 @include feature-targeting.targets($feat-structure) {
500 width: 100%;
501 height: 0;
502 transform: scaleX(0) rotate(0deg);
503 border-width: math.div(math.floor(checkbox-theme.$mark-stroke-size), 2);
504 border-style: solid;
505 opacity: 0;
506 }
507
508 @include feature-targeting.targets($feat-animation) {
509 transition: transition-exit(opacity), transition-exit(transform);
510 }
511}
512
513@mixin mixedmark--checked_ {
514 transform: scaleX(1) rotate(-45deg);
515}
516
517@mixin mixedmark--indeterminate_ {
518 transform: scaleX(1) rotate(0deg);
519 opacity: 1;
520}
521
522@function transition-enter(
523 $property,
524 $delay: 0ms,
525 $duration: checkbox-theme.$transition-duration
526) {
527 @return functions2.enter($property, $duration, $delay);
528}
529
530@function transition-exit(
531 $property,
532 $delay: 0ms,
533 $duration: checkbox-theme.$transition-duration
534) {
535 @return functions2.exit-temporary($property, $duration, $delay);
536}
537
538// Manual calculation done on SVG
539$mark-path-length_: 29.7833385 !default;
540$indeterminate-checked-easing-function_: cubic-bezier(0.14, 0, 0, 1) !default;
541
542@mixin mark-keyframes_ {
543 @keyframes mdc-checkbox-unchecked-checked-checkmark-path {
544 0%,
545 50% {
546 stroke-dashoffset: $mark-path-length_;
547 }
548
549 50% {
550 animation-timing-function: animation-variables.$deceleration-curve-timing-function;
551 }
552
553 100% {
554 stroke-dashoffset: 0;
555 }
556 }
557
558 @keyframes mdc-checkbox-unchecked-indeterminate-mixedmark {
559 0%,
560 68.2% {
561 transform: scaleX(0);
562 }
563
564 68.2% {
565 animation-timing-function: cubic-bezier(0, 0, 0, 1);
566 }
567
568 100% {
569 transform: scaleX(1);
570 }
571 }
572
573 @keyframes mdc-checkbox-checked-unchecked-checkmark-path {
574 from {
575 animation-timing-function: animation-variables.$acceleration-curve-timing-function;
576 opacity: 1;
577 stroke-dashoffset: 0;
578 }
579
580 to {
581 opacity: 0;
582 stroke-dashoffset: $mark-path-length_ * -1;
583 }
584 }
585
586 @keyframes mdc-checkbox-checked-indeterminate-checkmark {
587 from {
588 animation-timing-function: animation-variables.$deceleration-curve-timing-function;
589 transform: rotate(0deg);
590 opacity: 1;
591 }
592
593 to {
594 transform: rotate(45deg);
595 opacity: 0;
596 }
597 }
598
599 @keyframes mdc-checkbox-indeterminate-checked-checkmark {
600 from {
601 animation-timing-function: $indeterminate-checked-easing-function_;
602 transform: rotate(45deg);
603 opacity: 0;
604 }
605
606 to {
607 transform: rotate(360deg);
608 opacity: 1;
609 }
610 }
611
612 @keyframes mdc-checkbox-checked-indeterminate-mixedmark {
613 from {
614 animation-timing-function: mdc-animation-deceleration-curve-timing-function;
615 transform: rotate(-45deg);
616 opacity: 0;
617 }
618
619 to {
620 transform: rotate(0deg);
621 opacity: 1;
622 }
623 }
624
625 @keyframes mdc-checkbox-indeterminate-checked-mixedmark {
626 from {
627 animation-timing-function: $indeterminate-checked-easing-function_;
628 transform: rotate(0deg);
629 opacity: 1;
630 }
631
632 to {
633 transform: rotate(315deg);
634 opacity: 0;
635 }
636 }
637
638 @keyframes mdc-checkbox-indeterminate-unchecked-mixedmark {
639 0% {
640 animation-timing-function: linear;
641 transform: scaleX(1);
642 opacity: 1;
643 }
644
645 32.8%,
646 100% {
647 transform: scaleX(0);
648 opacity: 0;
649 }
650 }
651}