UNPKG

12.3 kBSCSSView Raw
1//
2// Copyright 2018 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/density/functions' as density-functions;
28@use '@material/elevation/mixins' as elevation-mixins;
29@use '@material/feature-targeting/feature-targeting';
30@use '@material/ripple/ripple';
31@use '@material/ripple/ripple-theme';
32@use '@material/rtl/rtl';
33@use '@material/theme/theme';
34@use './functions';
35@use './variables';
36@use '@material/dom/mixins' as dom-mixins;
37
38// Temporary suffix to add to shared selectors between versions for migration
39$deprecated-suffix: '-deprecated' !default;
40
41//
42// Public
43//
44
45@mixin core-styles($query: feature-targeting.all()) {
46 @include without-ripple($query);
47 @include ripple($query);
48}
49
50// This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
51// switch styles. It is recommended that most users use `mdc-switch-core-styles` instead.
52@mixin without-ripple($query: feature-targeting.all()) {
53 // postcss-bem-linter: define switch
54
55 $feat-animation: feature-targeting.create-target($query, animation);
56 $feat-structure: feature-targeting.create-target($query, structure);
57
58 @include density(variables.$density-scale, $query: $query);
59
60 .mdc-switch#{$deprecated-suffix} {
61 @include feature-targeting.targets($feat-structure) {
62 @include base_;
63 }
64
65 @include toggled-on-track-color(variables.$baseline-theme-color, $query);
66 @include toggled-on-thumb-color(variables.$baseline-theme-color, $query);
67 @include toggled-off-track-color(
68 variables.$toggled-off-track-color,
69 $query
70 );
71 @include toggled-off-thumb-color(
72 variables.$toggled-off-thumb-color,
73 $query
74 );
75 }
76
77 .mdc-switch__native-control {
78 @include feature-targeting.targets($feat-structure) {
79 @include native-control_;
80 }
81 @include feature-targeting.targets($feat-animation) {
82 transition: functions.transition(transform);
83 }
84 }
85
86 .mdc-switch__track#{$deprecated-suffix} {
87 @include track_($query);
88 }
89
90 .mdc-switch__thumb-underlay {
91 @include thumb-underlay_($query);
92 }
93
94 .mdc-switch__thumb {
95 @include thumb_($query);
96 }
97
98 .mdc-switch--checked {
99 @include feature-targeting.targets($feat-structure) {
100 .mdc-switch__track#{$deprecated-suffix} {
101 @include track-checked_;
102 }
103
104 .mdc-switch__thumb-underlay {
105 @include thumb-underlay-checked_;
106 }
107
108 .mdc-switch__native-control {
109 @include native-control-checked_;
110 }
111 }
112 }
113
114 .mdc-switch--disabled {
115 @include feature-targeting.targets($feat-structure) {
116 @include disabled-base_;
117
118 .mdc-switch__thumb {
119 @include thumb-disabled_;
120 }
121
122 .mdc-switch__native-control {
123 @include native-control-disabled_;
124 }
125 }
126 }
127
128 // postcss-bem-linter: end
129}
130
131// This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
132// switch styles. It is recommended that most users use `mdc-switch-core-styles` instead.
133@mixin ripple($query: feature-targeting.all()) {
134 @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
135
136 .mdc-switch#{$deprecated-suffix} {
137 @include toggled-off-ripple-color(
138 variables.$toggled-off-ripple-color,
139 $query
140 );
141 }
142
143 .mdc-switch__thumb-underlay {
144 @include ripple.surface($query);
145 @include ripple.radius-unbounded(100%, $query);
146 @include ripple-theme.states(
147 variables.$baseline-theme-color,
148 false,
149 $query
150 );
151 }
152}
153
154@mixin toggled-on-color($color, $query: feature-targeting.all()) {
155 @include toggled-on-track-color($color, $query);
156 @include toggled-on-thumb-color($color, $query);
157 @include toggled-on-ripple-color($color, $query);
158}
159
160@mixin toggled-off-color($color, $query: feature-targeting.all()) {
161 @include toggled-off-track-color($color, $query);
162 @include toggled-off-thumb-color($color, $query);
163 @include toggled-off-ripple-color($color, $query);
164}
165
166@mixin toggled-on-track-color($color, $query: feature-targeting.all()) {
167 $feat-color: feature-targeting.create-target($query, color);
168
169 &.mdc-switch--checked .mdc-switch__track#{$deprecated-suffix} {
170 @include feature-targeting.targets($feat-color) {
171 @include theme.property(background-color, $color);
172 }
173 }
174}
175
176@mixin toggled-on-thumb-color($color, $query: feature-targeting.all()) {
177 $feat-color: feature-targeting.create-target($query, color);
178
179 &.mdc-switch--checked .mdc-switch__thumb {
180 @include feature-targeting.targets($feat-color) {
181 @include theme.property(background-color, $color);
182 @include theme.property(border-color, $color);
183 }
184 }
185}
186
187@mixin toggled-on-ripple-color($color, $query: feature-targeting.all()) {
188 &.mdc-switch--checked .mdc-switch__thumb-underlay {
189 @include ripple-theme.states($color, false, $query);
190 }
191}
192
193@mixin toggled-off-track-color($color, $query: feature-targeting.all()) {
194 $feat-color: feature-targeting.create-target($query, color);
195
196 &:not(.mdc-switch--checked) .mdc-switch__track#{$deprecated-suffix} {
197 @include feature-targeting.targets($feat-color) {
198 @include theme.property(background-color, $color);
199 }
200 }
201}
202
203@mixin toggled-off-thumb-color($color, $query: feature-targeting.all()) {
204 $feat-color: feature-targeting.create-target($query, color);
205
206 &:not(.mdc-switch--checked) .mdc-switch__thumb {
207 @include feature-targeting.targets($feat-color) {
208 @include theme.property(background-color, $color);
209 @include theme.property(border-color, $color);
210 }
211 }
212}
213
214@mixin toggled-off-ripple-color($color, $query: feature-targeting.all()) {
215 &:not(.mdc-switch--checked) .mdc-switch__thumb-underlay {
216 @include ripple-theme.states($color, false, $query);
217 }
218}
219
220///
221/// Sets density scale for switch.
222///
223/// @param {Number | String} $density-scale - Density scale value for component.
224/// Supported density scale values are `-5`, `-4`, `-3`, `-2`, `-1`,
225/// `0` (default).
226///
227@mixin density($density-scale, $query: feature-targeting.all()) {
228 $size: density-functions.prop-value(
229 $density-config: variables.$density-config,
230 $density-scale: $density-scale,
231 $property-name: size,
232 );
233
234 @include ripple-size($size, $query: $query);
235}
236
237@mixin ripple-size($ripple-size, $query: feature-targeting.all()) {
238 $feat-structure: feature-targeting.create-target($query, structure);
239
240 // Position for the tap target that contains the thumb to align the thumb
241 // correctly offset from the track.
242 $tap-target-initial-position: math.div(-$ripple-size, 2) +
243 math.div(variables.$thumb-diameter, 2);
244 // Value to cover the whole switch area (including the ripple) with the
245 // native control.
246 $native-control-width: variables.$track-width +
247 ($ripple-size - variables.$thumb-diameter);
248
249 .mdc-switch__thumb-underlay {
250 @include feature-targeting.targets($feat-structure) {
251 @include rtl.reflexive-position(left, $tap-target-initial-position);
252
253 // Ensures the knob is centered on the track.
254 top: -(math.div($ripple-size - variables.$track-height, 2));
255 width: $ripple-size;
256 height: $ripple-size;
257 }
258 }
259
260 .mdc-switch__native-control {
261 @include feature-targeting.targets($feat-structure) {
262 width: $native-control-width;
263 height: $ripple-size;
264 }
265 }
266}
267
268///
269/// Customizes ripple opacities surrounding the thumb in `hover`, `focus`, or `press` states
270/// The customizations apply to both on and off switches to ensure symmetry
271/// @param {map} $opacity-map - map specifying custom opacity of zero or more states
272///
273@mixin ripple-states-opacity(
274 $opacity-map: (),
275 $query: feature-targeting.all()
276) {
277 // Ensure sufficient specificity to override base state opacities
278 &.mdc-switch#{$deprecated-suffix} .mdc-switch__thumb-underlay {
279 @include ripple-theme.states-opacities($opacity-map, $query: $query);
280 }
281}
282
283//
284// Private
285//
286
287// Structure
288@mixin base_ {
289 display: inline-block;
290 position: relative;
291 outline: none;
292 user-select: none;
293}
294
295@mixin track_($query: feature-targeting.all()) {
296 $feat-animation: feature-targeting.create-target($query, animation);
297 $feat-structure: feature-targeting.create-target($query, structure);
298
299 @include feature-targeting.targets($feat-structure) {
300 box-sizing: border-box;
301 width: variables.$track-width;
302 height: variables.$track-height;
303 border: 1px solid transparent;
304 border-radius: math.div(variables.$track-height, 2);
305 opacity: 0.38;
306 }
307
308 @include feature-targeting.targets($feat-animation) {
309 transition: functions.transition(opacity),
310 functions.transition(background-color), functions.transition(border-color);
311 }
312}
313
314@mixin thumb-underlay_($query: feature-targeting.all()) {
315 $feat-animation: feature-targeting.create-target($query, animation);
316 $feat-color: feature-targeting.create-target($query, color);
317 $feat-structure: feature-targeting.create-target($query, structure);
318
319 @include feature-targeting.targets($feat-structure) {
320 display: flex;
321 position: absolute;
322 align-items: center;
323 justify-content: center;
324 transform: translateX(0);
325 }
326
327 @include feature-targeting.targets($feat-animation) {
328 transition: functions.transition(transform),
329 functions.transition(background-color), functions.transition(border-color);
330 }
331}
332
333@mixin native-control_ {
334 @include rtl.reflexive-position(left, 0);
335
336 position: absolute;
337 top: 0;
338 margin: 0;
339 opacity: 0;
340 cursor: pointer;
341 pointer-events: auto;
342}
343
344@mixin thumb_($query: feature-targeting.all()) {
345 $feat-structure: feature-targeting.create-target($query, structure);
346 $feat-color: feature-targeting.create-target($query, color);
347
348 @include elevation-mixins.elevation($z-value: 2, $query: $query);
349
350 @include feature-targeting.targets($feat-structure) {
351 box-sizing: border-box;
352 width: variables.$thumb-diameter;
353 height: variables.$thumb-diameter;
354 border: math.div(variables.$thumb-diameter, 2) solid;
355 border-radius: 50%;
356 // Allow events to go through to the native control, necessary for IE and Edge.
357 pointer-events: none;
358 z-index: 1;
359 }
360}
361
362// Checked state
363
364@mixin track-checked_ {
365 opacity: 0.54;
366}
367
368@mixin thumb-underlay-checked_ {
369 transform: translateX(variables.$thumb-active-margin);
370
371 @include rtl.rtl {
372 transform: translateX(-(variables.$thumb-active-margin));
373 }
374}
375
376@mixin native-control-checked_ {
377 // Translate the native control the opposite direction so that the tap target stays the same.
378 transform: translateX(-(variables.$thumb-active-margin));
379
380 @include rtl.rtl {
381 transform: translateX(variables.$thumb-active-margin);
382 }
383}
384
385// Disabled state
386
387@mixin disabled-base_ {
388 opacity: 0.38;
389 pointer-events: none;
390}
391
392@mixin thumb-disabled_ {
393 border-width: 1px; // In high contrast mode, only show outline of knob.
394}
395
396@mixin native-control-disabled_ {
397 cursor: default;
398 pointer-events: none;
399}
400
401///
402/// Includes ad-hoc high contrast mode support.
403///
404@mixin high-contrast-mode-shim($query: feature-targeting.all()) {
405 & .mdc-ripple-upgraded--background-focused .mdc-switch__thumb::before {
406 @include dom-mixins.transparent-border($border-width: 3px);
407 }
408}