UNPKG

21.6 kBSCSSView Raw
1//
2// Copyright 2017 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// stylelint-disable selector-class-pattern --
24// Selector '.mdc-*' should only be used in this project.
25
26@use 'sass:math';
27@use '@material/animation/functions';
28@use '@material/animation/variables' as animation-variables;
29@use '@material/checkbox/functions' as checkbox-functions;
30@use '@material/checkbox/variables' as checkbox-variables;
31@use '@material/elevation/mixins' as elevation-mixins;
32@use '@material/density/functions' as density-functions;
33@use '@material/feature-targeting/feature-targeting';
34@use '@material/focus-ring/focus-ring';
35@use '@material/ripple/ripple';
36@use '@material/ripple/ripple-theme';
37@use '@material/rtl/rtl';
38@use '@material/theme/theme';
39@use '@material/touch-target/mixins' as touch-target-mixins;
40@use '@material/typography/typography';
41@use '@material/shape/mixins' as shape-mixins;
42@use '@material/shape/functions' as shape-functions;
43@use './variables';
44@use '@material/elevation/functions' as elevation-functions;
45@use '@material/theme/theme-color';
46@use './trailingaction/mixins' as trailing-action-mixins;
47
48$ripple-target: '.mdc-chip__ripple';
49
50@mixin core-styles($query: feature-targeting.all()) {
51 @include without-ripple($query: $query);
52 @include ripple($query: $query);
53}
54
55@mixin without-ripple($query: feature-targeting.all()) {
56 $feat-animation: feature-targeting.create-target($query, animation);
57 $feat-color: feature-targeting.create-target($query, color);
58 $feat-structure: feature-targeting.create-target($query, structure);
59
60 @include leading-icon-color(variables.$icon-color, $query: $query);
61 @include trailing-icon-color(variables.$icon-color, $query: $query);
62 @include leading-icon-size(variables.$leading-icon-size, $query: $query);
63 @include trailing-icon-size(variables.$trailing-icon-size, $query: $query);
64 @include trailing-icon-margin($query: $query);
65 @include touch-target-mixins.wrapper($query); // COPYBARA_COMMENT_THIS_LINE
66 // prettier-ignore
67 @include elevation-mixins.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE
68
69 .mdc-chip {
70 @include shape-radius(variables.$shape-radius, $query: $query);
71 @include fill-color(variables.$fill-color-default, $query: $query);
72 @include ink-color-without-ripple_(
73 variables.$ink-color-default,
74 $query: $query
75 );
76 @include typography.typography(body2, $query: $query);
77 @include density($density-scale: 0, $query: $query);
78 @include leading-icon-margin($query: $query);
79 @include elevation-mixins.overlay-surface-position($query: $query);
80 @include elevation-mixins.overlay-dimensions(100%, $query: $query);
81
82 @include feature-targeting.targets($feat-structure) {
83 display: inline-flex;
84 // position: relative; already set in mdc-elevation-overlay-surface-position
85 align-items: center;
86 box-sizing: border-box;
87 padding: 0 variables.$horizontal-padding;
88 border-width: 0;
89 outline: none;
90 cursor: pointer;
91 -webkit-appearance: none;
92
93 &::-moz-focus-inner {
94 padding: 0;
95 border: 0;
96 }
97 }
98
99 &:hover {
100 @include feature-targeting.targets($feat-color) {
101 @include theme.property(color, on-surface);
102 }
103 }
104
105 .mdc-chip__touch {
106 @include touch-target-mixins.touch-target($query: $query);
107 }
108 }
109
110 .mdc-chip--exit {
111 @include feature-targeting.targets($feat-color) {
112 transition: variables.$exit-transition;
113 }
114
115 @include feature-targeting.targets($feat-structure) {
116 opacity: 0;
117 }
118 }
119
120 .mdc-chip__overflow {
121 @include feature-targeting.targets($feat-structure) {
122 text-overflow: ellipsis;
123 overflow: hidden;
124 }
125 }
126
127 .mdc-chip__text {
128 @include feature-targeting.targets($feat-structure) {
129 white-space: nowrap;
130 }
131 }
132
133 .mdc-chip__icon {
134 @include feature-targeting.targets($feat-structure) {
135 border-radius: 50%;
136 outline: none;
137 vertical-align: middle;
138 }
139 }
140
141 .mdc-chip__checkmark {
142 @include feature-targeting.targets($feat-structure) {
143 height: variables.$leading-icon-size;
144 }
145 }
146
147 .mdc-chip__checkmark-path {
148 @include feature-targeting.targets($feat-animation) {
149 transition: checkbox-functions.transition-exit(
150 stroke-dashoffset,
151 variables.$checkmark-animation-delay,
152 variables.$checkmark-animation-duration
153 );
154 }
155
156 @include feature-targeting.targets($feat-structure) {
157 stroke-width: 2px;
158 stroke-dashoffset: checkbox-variables.$mark-path-length_;
159 stroke-dasharray: checkbox-variables.$mark-path-length_;
160 }
161 }
162
163 .mdc-chip__primary-action:focus {
164 @include feature-targeting.targets($feat-structure) {
165 outline: none;
166 }
167 }
168
169 .mdc-chip--selected .mdc-chip__checkmark-path {
170 @include feature-targeting.targets($feat-structure) {
171 stroke-dashoffset: 0;
172 }
173 }
174
175 .mdc-chip__icon--leading,
176 .mdc-chip__icon--trailing {
177 @include feature-targeting.targets($feat-structure) {
178 // Make these positioned elements, such that they're stacked above the
179 // touch target element (`mdc-chip__touch`), so that clicks reach the
180 // icons (e.g. for removable input chips).
181 position: relative;
182 }
183 }
184
185 // Change color of selected choice chips
186
187 .mdc-chip-set--choice {
188 .mdc-chip {
189 @include selected-ink-color-without-ripple_(primary, $query: $query);
190 }
191
192 .mdc-chip--selected {
193 @include feature-targeting.targets($feat-color) {
194 @include theme.property(background-color, surface);
195 }
196 }
197 }
198
199 // Add leading checkmark to filter chips with no leading icon
200
201 .mdc-chip__checkmark-svg {
202 @include feature-targeting.targets($feat-structure) {
203 width: 0;
204 height: variables.$leading-icon-size;
205 }
206
207 @include feature-targeting.targets($feat-animation) {
208 transition: width variables.$width-animation-duration
209 animation-variables.$standard-curve-timing-function;
210 }
211 }
212
213 .mdc-chip--selected .mdc-chip__checkmark-svg {
214 @include feature-targeting.targets($feat-structure) {
215 width: variables.$leading-icon-size;
216 }
217 }
218
219 // Add leading checkmark to filter chips with a leading icon
220
221 .mdc-chip-set--filter {
222 .mdc-chip__icon--leading {
223 @include feature-targeting.targets($feat-animation) {
224 transition: opacity variables.$opacity-animation-duration linear;
225 transition-delay: variables.$leading-icon-delay;
226 }
227
228 @include feature-targeting.targets($feat-structure) {
229 opacity: 1;
230 }
231
232 + .mdc-chip__checkmark {
233 @include feature-targeting.targets($feat-animation) {
234 transition: opacity variables.$opacity-animation-duration linear;
235
236 // Delay the checkmark transition.
237 transition-delay: variables.$checkmark-with-leading-icon-delay;
238 }
239
240 @include feature-targeting.targets($feat-structure) {
241 opacity: 0;
242 }
243
244 .mdc-chip__checkmark-svg {
245 @include feature-targeting.targets($feat-animation) {
246 transition: width 0ms;
247 }
248 }
249 }
250 }
251
252 .mdc-chip--selected .mdc-chip__icon--leading {
253 @include feature-targeting.targets($feat-structure) {
254 opacity: 0;
255 }
256
257 + .mdc-chip__checkmark {
258 @include feature-targeting.targets($feat-structure) {
259 // This ensures that the checkmark has zero width while the leading icon is still animating.
260 width: 0;
261 opacity: 1;
262 }
263 }
264 }
265
266 .mdc-chip__icon--leading-hidden.mdc-chip__icon--leading {
267 @include feature-targeting.targets($feat-structure) {
268 width: 0;
269 }
270
271 @include feature-targeting.targets($feat-structure) {
272 // This ensures that the leading icon doesn't fade in while the checkmark is fading out.
273 opacity: 0;
274 }
275
276 + .mdc-chip__checkmark {
277 @include feature-targeting.targets($feat-structure) {
278 width: variables.$leading-icon-size;
279 }
280 }
281 }
282 }
283}
284
285@mixin ripple($query: feature-targeting.all()) {
286 @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
287 $feat-structure: feature-targeting.create-target($query, structure);
288
289 .mdc-chip {
290 @include ripple.surface($query: $query, $ripple-target: $ripple-target);
291 @include ripple.radius-bounded(
292 $query: $query,
293 $ripple-target: $ripple-target
294 );
295 @include ink-color-ripple_(variables.$ink-color-default, $query: $query);
296
297 #{$ripple-target} {
298 @include ripple.target-common($query: $query);
299
300 @include feature-targeting.targets($feat-structure) {
301 overflow: hidden;
302 }
303 }
304 }
305
306 .mdc-chip-set--choice {
307 .mdc-chip {
308 @include selected-ink-color-ripple_(primary, $query: $query);
309 }
310 }
311}
312
313@mixin set-core-styles($query: feature-targeting.all()) {
314 $feat-animation: feature-targeting.create-target($query, animation);
315 $feat-structure: feature-targeting.create-target($query, structure);
316
317 @include feature-targeting.targets($feat-animation) {
318 @keyframes mdc-chip-entry {
319 from {
320 transform: scale(0.8);
321 opacity: 0.4;
322 }
323
324 to {
325 transform: scale(1);
326 opacity: 1;
327 }
328 }
329 }
330
331 .mdc-chip-set {
332 @include set-spacing(8px, $query: $query);
333
334 @include feature-targeting.targets($feat-structure) {
335 display: flex;
336 flex-wrap: wrap;
337 box-sizing: border-box;
338 }
339 }
340
341 .mdc-chip-set--input .mdc-chip {
342 @include feature-targeting.targets($feat-animation) {
343 animation: mdc-chip-entry 100ms
344 animation-variables.$deceleration-curve-timing-function;
345 }
346 }
347}
348
349@mixin shape-radius(
350 $radius,
351 $rtl-reflexive: false,
352 $density-scale: variables.$density-scale,
353 $query: feature-targeting.all()
354) {
355 $height: density-functions.prop-value(
356 $density-config: variables.$density-config,
357 $density-scale: $density-scale,
358 $property-name: height,
359 );
360
361 @include shape-mixins.radius(
362 $radius,
363 $rtl-reflexive,
364 $component-height: $height,
365 $query: $query
366 );
367
368 #{$ripple-target} {
369 @include shape-mixins.radius(
370 $radius,
371 $rtl-reflexive,
372 $component-height: $height,
373 $query: $query
374 );
375 }
376}
377
378@mixin fill-color-accessible($color, $query: feature-targeting.all()) {
379 $fill-tone: theme-color.tone($color);
380
381 @include fill-color($color, $query: $query);
382
383 @if ($fill-tone == 'dark') {
384 @include ink-color(text-primary-on-dark, $query: $query);
385 @include selected-ink-color(text-primary-on-dark, $query: $query);
386 @include leading-icon-color(text-primary-on-dark, $query: $query);
387 @include trailing-icon-color(text-primary-on-dark, $query: $query);
388 } @else {
389 @include ink-color(text-primary-on-light, $query: $query);
390 @include selected-ink-color(text-primary-on-light, $query: $query);
391 @include leading-icon-color(text-primary-on-light, $query: $query);
392 @include trailing-icon-color(text-primary-on-light, $query: $query);
393 }
394}
395
396@mixin fill-color($color, $query: feature-targeting.all()) {
397 $feat-color: feature-targeting.create-target($query, color);
398
399 @include feature-targeting.targets($feat-color) {
400 @include theme.property(background-color, $color);
401 }
402}
403
404@mixin ink-color($color, $query: feature-targeting.all()) {
405 @include ink-color-ripple_($color, $query: $query);
406 @include ink-color-without-ripple_($color, $query: $query);
407}
408
409@mixin ink-color-without-ripple_($color, $query) {
410 $feat-color: feature-targeting.create-target($query, color);
411
412 @include feature-targeting.targets($feat-color) {
413 @include theme.property(color, $color);
414 }
415
416 &:hover {
417 @include feature-targeting.targets($feat-color) {
418 @include theme.property(color, $color);
419 }
420 }
421}
422
423@mixin ink-color-ripple_($color, $query) {
424 @include ripple-theme.states(
425 $color,
426 true,
427 $query: $query,
428 $ripple-target: $ripple-target
429 );
430}
431
432@mixin selected-ink-color($color, $query: feature-targeting.all()) {
433 @include selected-ink-color-ripple_($color, $query: $query);
434 @include selected-ink-color-without-ripple_($color, $query: $query);
435}
436
437@mixin selected-ink-color-without-ripple_($color, $query) {
438 $feat-color: feature-targeting.create-target($query, color);
439
440 &.mdc-chip--selected {
441 @include feature-targeting.targets($feat-color) {
442 @include theme.property(color, $color);
443 }
444
445 @include leading-icon-color($color, $query: $query);
446
447 &:hover {
448 @include feature-targeting.targets($feat-color) {
449 @include theme.property(color, $color);
450 }
451 }
452 }
453
454 .mdc-chip__checkmark-path {
455 @include feature-targeting.targets($feat-color) {
456 @include theme.property(stroke, $color);
457 }
458 }
459}
460
461@mixin selected-ink-color-ripple_($color, $query) {
462 &.mdc-chip {
463 @include ripple-theme.states-selected(
464 $color,
465 $has-nested-focusable-element: true,
466 $query: $query,
467 $ripple-target: $ripple-target
468 );
469 }
470}
471
472@mixin outline(
473 $width: 1px,
474 $style: solid,
475 $color: theme-color.prop-value(on-surface),
476 $query: feature-targeting.all()
477) {
478 @include outline-width($width, $query: $query);
479 @include outline-style($style, $query: $query);
480 @include outline-color($color, $query: $query);
481}
482
483@mixin outline-color($color, $query: feature-targeting.all()) {
484 $feat-color: feature-targeting.create-target($query, color);
485
486 @include feature-targeting.targets($feat-color) {
487 @include theme.property(border-color, $color);
488 }
489}
490
491@mixin outline-style($style, $query: feature-targeting.all()) {
492 $feat-structure: feature-targeting.create-target($query, structure);
493
494 @include feature-targeting.targets($feat-structure) {
495 border-style: $style;
496 }
497}
498
499@mixin outline-width(
500 $width,
501 $horizontal-padding: variables.$horizontal-padding,
502 $query: feature-targeting.all()
503) {
504 $feat-structure: feature-targeting.create-target($query, structure);
505
506 // Note: Adjust padding to maintain consistent width with non-outlined chips
507 $horizontal-padding-value: math.max($horizontal-padding - $width, 0);
508
509 @include feature-targeting.targets($feat-structure) {
510 padding-right: $horizontal-padding-value;
511 padding-left: $horizontal-padding-value;
512 border-width: $width;
513 }
514
515 #{$ripple-target} {
516 @include feature-targeting.targets($feat-structure) {
517 top: -$width;
518 left: -$width;
519 border: $width solid transparent;
520 }
521 }
522}
523
524@mixin horizontal-padding($padding, $query: feature-targeting.all()) {
525 $feat-structure: feature-targeting.create-target($query, structure);
526
527 @include feature-targeting.targets($feat-structure) {
528 padding-right: $padding;
529 padding-left: $padding;
530 }
531}
532
533@mixin height($height, $query: feature-targeting.all()) {
534 $feat-structure: feature-targeting.create-target($query, structure);
535
536 @include feature-targeting.targets($feat-structure) {
537 height: $height;
538 }
539}
540
541@mixin set-spacing($gap-size, $query: feature-targeting.all()) {
542 $feat-structure: feature-targeting.create-target($query, structure);
543
544 @include feature-targeting.targets($feat-structure) {
545 padding: math.div($gap-size, 2);
546 }
547
548 .mdc-chip {
549 @include feature-targeting.targets($feat-structure) {
550 margin: math.div($gap-size, 2);
551 }
552 }
553
554 .mdc-chip--touch {
555 @include touch-target-mixins.margin(
556 $component-height: variables.$height,
557 $query: $query
558 );
559 }
560}
561
562@mixin leading-icon-color(
563 $color,
564 $opacity: variables.$icon-opacity,
565 $query: feature-targeting.all()
566) {
567 $feat-color: feature-targeting.create-target($query, color);
568
569 .mdc-chip__icon--leading {
570 @include feature-targeting.targets($feat-color) {
571 color: rgba(theme-color.prop-value($color), $opacity);
572 }
573 }
574}
575
576@mixin trailing-icon-color(
577 $color,
578 $opacity: variables.$icon-opacity,
579 $hover-opacity: variables.$trailing-icon-hover-opacity,
580 $focus-opacity: variables.$trailing-icon-focus-opacity,
581 $query: feature-targeting.all()
582) {
583 $feat-color: feature-targeting.create-target($query, color);
584
585 @include trailing-action-mixins.color($color, $query: $query);
586
587 // TODO(b/151980552): Remove the following block
588 .mdc-chip__icon--trailing {
589 @include feature-targeting.targets($feat-color) {
590 color: rgba(theme-color.prop-value($color), $opacity);
591 }
592
593 &:hover {
594 @include feature-targeting.targets($feat-color) {
595 color: rgba(theme-color.prop-value($color), $hover-opacity);
596 }
597 }
598
599 &:focus {
600 @include feature-targeting.targets($feat-color) {
601 color: rgba(theme-color.prop-value($color), $focus-opacity);
602 }
603 }
604 }
605}
606
607// For customizing icon size, we need to increase specifity to ensure
608// overrides apply. Styles defined in the .material-icons CSS class are
609// loaded separately, so the order of CSS definitions is not guaranteed.
610
611@mixin leading-icon-size($size, $query: feature-targeting.all()) {
612 .mdc-chip__icon.mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
613 @include icon-size_($size, $query: $query);
614 }
615}
616
617@mixin trailing-icon-size($size, $query: feature-targeting.all()) {
618 @include trailing-action-mixins.size($size, $query: $query);
619
620 // TODO(b/151980552): Remove the following block
621 .mdc-chip__icon.mdc-chip__icon--trailing {
622 @include icon-size_($size, $query: $query);
623 }
624}
625
626@mixin icon-size_($size, $query) {
627 $feat-structure: feature-targeting.create-target($query, structure);
628
629 @include feature-targeting.targets($feat-structure) {
630 width: $size;
631 height: $size;
632 font-size: $size;
633 }
634}
635
636@mixin leading-icon-margin(
637 $left-margin: variables.$leading-icon-margin-left,
638 $right-margin: variables.$leading-icon-margin-right,
639 $query: feature-targeting.all()
640) {
641 $feat-structure: feature-targeting.create-target($query, structure);
642
643 &.mdc-chip--selected .mdc-chip__checkmark,
644 .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
645 @include feature-targeting.targets($feat-structure) {
646 @include rtl.reflexive-property(margin, $left-margin, $right-margin);
647 }
648 }
649}
650
651@mixin trailing-icon-margin(
652 $left-margin: variables.$trailing-icon-margin-left,
653 $right-margin: variables.$trailing-icon-margin-right,
654 $query: feature-targeting.all()
655) {
656 $feat-structure: feature-targeting.create-target($query, structure);
657
658 @include trailing-action-mixins.horizontal-spacing(
659 $left-margin,
660 $right-margin,
661 $query: $query
662 );
663
664 // TODO(b/151980552): Remove the following block
665 .mdc-chip__icon--trailing {
666 @include feature-targeting.targets($feat-structure) {
667 @include rtl.reflexive-property(margin, $left-margin, $right-margin);
668 }
669 }
670}
671
672@mixin elevation-transition($query: feature-targeting.all()) {
673 $feat-animation: feature-targeting.create-target($query, animation);
674
675 @include feature-targeting.targets($feat-animation) {
676 transition: elevation-functions.transition-value();
677 }
678
679 &.mdc-chip--exit {
680 @include feature-targeting.targets($feat-animation) {
681 transition: elevation-functions.transition-value(),
682 variables.$exit-transition;
683 }
684 }
685}
686
687///
688/// Sets the density scale for chips.
689///
690/// @param {Number | String} $density-scale - Density scale value for component.
691/// Supported density scale values are `-2`, `-1`, `0`.
692///
693@mixin density($density-scale, $query: feature-targeting.all()) {
694 $height: density-functions.prop-value(
695 $density-config: variables.$density-config,
696 $density-scale: $density-scale,
697 $property-name: height,
698 );
699
700 @include height($height, $query: $query);
701
702 @if $density-scale != 0 {
703 @include touch-target-reset_($query: $query);
704 }
705}
706
707///
708/// Resets touch target-related styles. This is called from the density mixin to
709/// automatically remove the increased touch target, since dense components
710/// don't have the same default a11y requirements.
711/// @access private
712///
713@mixin touch-target-reset_($query: feature-targeting.all()) {
714 $feat-structure: feature-targeting.create-target($query, structure);
715
716 // Selector is necessary here to override original specificity.
717 &.mdc-chip--touch {
718 @include feature-targeting.targets($feat-structure) {
719 margin-top: 0;
720 margin-bottom: 0;
721 }
722 }
723
724 .mdc-chip__touch {
725 @include feature-targeting.targets($feat-structure) {
726 display: none;
727 }
728 }
729}
730
731@mixin high-contrast-focus {
732 // High contrast mode focus for chips.
733 .mdc-chip__focus-ring {
734 display: none;
735 }
736
737 @include ripple-theme.focus() {
738 .mdc-chip__focus-ring {
739 z-index: 1;
740 display: block;
741 @include focus-ring.focus-ring();
742 }
743 }
744}