UNPKG

8.05 kBSCSSView Raw
1//
2// Copyright 2021 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 '@material/animation/animation';
27@use '@material/dom/dom';
28@use '@material/elevation/elevation';
29@use '@material/ripple/ripple-theme';
30@use '@material/ripple/ripple';
31@use '@material/focus-ring/focus-ring';
32@use '@material/rtl/rtl';
33@use '@material/theme/gss';
34
35$animation-duration: 75ms;
36$icon-exit-duration: 0.4 * $animation-duration;
37$icon-enter-duration: $animation-duration - $icon-exit-duration;
38$ripple-target: '.mdc-switch__ripple';
39
40@mixin static-styles() {
41 @include static-styles-without-ripple();
42
43 .mdc-switch {
44 @include ripple.common; // COPYBARA_COMMENT_THIS_LINE
45 @include ripple.surface($ripple-target: $ripple-target);
46 @include ripple.radius-unbounded($ripple-target: $ripple-target);
47
48 .mdc-switch__focus-ring-wrapper {
49 width: 100%;
50 position: absolute;
51 // IE11 hacks
52 top: 50%;
53 @include rtl.ignore-next-line();
54 left: 50%;
55 @include rtl.ignore-next-line();
56 transform: translate(-50%, -50%);
57 // end IE11 hacks
58 }
59
60 @include ripple-theme.focus {
61 .mdc-switch__focus-ring {
62 @include focus-ring.focus-ring();
63 }
64 }
65 }
66}
67
68@mixin static-styles-without-ripple() {
69 @include elevation.overlay-common; // COPYBARA_COMMENT_THIS_LINE
70
71 .mdc-switch {
72 @include root;
73
74 &:disabled {
75 @include disabled;
76 }
77 }
78
79 .mdc-switch__track {
80 @include track;
81 @include track-off;
82
83 .mdc-switch--selected & {
84 @include track-on;
85 }
86 }
87
88 .mdc-switch__handle-track {
89 @include handle-track;
90 @include handle-track-off;
91
92 .mdc-switch--selected & {
93 @include handle-track-on;
94 }
95 }
96
97 .mdc-switch__handle {
98 @include handle;
99 }
100
101 .mdc-switch__shadow {
102 @include shadow;
103 }
104
105 .mdc-elevation-overlay {
106 @include overlay;
107 }
108
109 .mdc-switch__ripple {
110 @include ripple;
111
112 .mdc-switch:disabled & {
113 @include ripple-disabled;
114 }
115 }
116
117 .mdc-switch__icons {
118 @include icons;
119 }
120
121 .mdc-switch__icon {
122 @include icon;
123 @include icon-hidden;
124 }
125
126 .mdc-switch--selected .mdc-switch__icon--on,
127 .mdc-switch--unselected .mdc-switch__icon--off {
128 @include icon-visible;
129 }
130}
131
132@mixin root() {
133 align-items: center;
134 background: none;
135 border: none;
136 cursor: pointer;
137 display: inline-flex;
138 flex-shrink: 0; // Stop from collapsing in flex containers
139 margin: 0;
140 outline: none;
141 overflow: visible;
142 padding: 0;
143 position: relative;
144}
145
146@mixin disabled() {
147 cursor: default;
148 pointer-events: none;
149}
150
151@mixin track() {
152 overflow: hidden;
153 position: relative;
154 width: 100%;
155
156 &::before,
157 &::after {
158 border: 1px solid transparent; // high contrast mode
159 border-radius: inherit;
160 box-sizing: border-box;
161 content: '';
162 height: 100%;
163 @include gss.annotate($noflip: true);
164 left: 0;
165 position: absolute;
166 width: 100%;
167
168 // Added for Firefox 94 which broke transparent borders in Windows HCM.
169 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1740924.
170 @include dom.forced-colors-mode($exclude-ie11: true) {
171 border-color: currentColor;
172 }
173 }
174}
175
176@mixin track-on() {
177 &::before {
178 transition: animation.exit-temporary(transform, $animation-duration);
179 transform: translateX(100%);
180 @include rtl.rtl {
181 transform: translateX(-100%);
182 }
183 }
184
185 &::after {
186 transition: animation.enter(transform, $animation-duration);
187 transform: translateX(0);
188 }
189}
190
191@mixin track-off() {
192 &::before {
193 transition: animation.enter(transform, $animation-duration);
194 transform: translateX(0);
195 }
196
197 &::after {
198 transition: animation.exit-temporary(transform, $animation-duration);
199 transform: translateX(-100%);
200 @include rtl.rtl {
201 transform: translateX(100%);
202 }
203 }
204}
205
206@mixin handle-track() {
207 height: 100%;
208 // The handle track is used to move the handle across the width of the switch
209 // and may overflow the bounds of the component. It should not be used for
210 // pointer events.
211 pointer-events: none;
212 position: absolute;
213 top: 0; // Needed for IE11
214 transition: animation.standard(transform, $animation-duration);
215 // IE11 needs explicit left/right
216 @include rtl.reflexive(left, 0, right, auto);
217}
218
219@mixin handle-track-on() {
220 transform: translateX(100%);
221
222 @include rtl.rtl {
223 transform: translateX(-100%);
224 }
225}
226
227@mixin handle-track-off() {
228 transform: translateX(0);
229}
230
231@mixin handle() {
232 display: flex;
233 pointer-events: auto;
234 position: absolute;
235 top: 50%;
236 transform: translateY(-50%);
237 // IE11 needs explicit left/right
238 @include rtl.reflexive(left, 0, right, auto);
239
240 &::before,
241 &::after {
242 border: 1px solid transparent; // high contrast mode
243 border-radius: inherit;
244 box-sizing: border-box;
245 content: '';
246 width: 100%;
247 height: 100%;
248 @include gss.annotate($noflip: true);
249 left: 0;
250 position: absolute;
251 top: 0; // IE11 fix
252 transition: animation.standard(background-color, $animation-duration),
253 animation.standard(border-color, $animation-duration);
254 // Move the handle background colors beneath the shadow overlay color,
255 // rather than move the overlay on top of the handle with a positive
256 // z-index, which would require moving all other content on top of the
257 // overlay with an even greater z-index.
258 z-index: -1;
259
260 // Added for Firefox 94 which broke transparent borders in Windows HCM.
261 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1740924.
262 @include dom.forced-colors-mode($exclude-ie11: true) {
263 border-color: currentColor;
264 }
265 }
266}
267
268@mixin shadow() {
269 border-radius: inherit;
270 bottom: 0;
271 @include gss.annotate($noflip: true);
272 left: 0;
273 position: absolute;
274 @include gss.annotate($noflip: true);
275 right: 0;
276 top: 0;
277}
278
279@mixin overlay() {
280 bottom: 0;
281 @include gss.annotate($noflip: true);
282 left: 0;
283 @include gss.annotate($noflip: true);
284 right: 0;
285 top: 0;
286}
287
288@mixin ripple() {
289 @include gss.annotate($noflip: true);
290 left: 50%;
291 position: absolute;
292 top: 50%;
293 transform: translate(-50%, -50%);
294 // Move ripple beneath shadow overlay and handle background colors (see
295 // handle() mixin for explanation).
296 z-index: -1;
297}
298
299@mixin ripple-disabled {
300 display: none;
301}
302
303@mixin icons() {
304 height: 100%;
305 position: relative;
306 width: 100%;
307 z-index: 1;
308}
309
310@mixin icon() {
311 bottom: 0;
312 @include gss.annotate($noflip: true);
313 left: 0;
314 // IE11 needs top/right/bottom/left + margin instead of translate(-50%, -50%)
315 // because of SVG centering issues
316 margin: auto;
317 position: absolute;
318 @include gss.annotate($noflip: true);
319 right: 0;
320 top: 0;
321}
322
323@mixin icon-hidden() {
324 opacity: 0;
325 transition: animation.exit-permanent(opacity, $icon-exit-duration);
326}
327
328@mixin icon-visible() {
329 opacity: 1;
330 transition: animation.enter(
331 opacity,
332 $icon-enter-duration,
333 $delay: $icon-exit-duration
334 );
335}