UNPKG

10.5 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:color';
27@use 'sass:map';
28@use '@material/animation/functions' as functions2;
29@use '@material/animation/variables' as variables2;
30@use '@material/base/mixins' as base-mixins;
31@use '@material/theme/custom-properties';
32@use '@material/feature-targeting/feature-targeting';
33@use '@material/theme/theme';
34@use '@material/theme/theme-color';
35@use './ripple-theme';
36
37@mixin core-styles($query: feature-targeting.all()) {
38 $feat-structure: feature-targeting.create-target($query, structure);
39
40 .mdc-ripple-surface {
41 @include surface($query: $query);
42 @include ripple-theme.states($query: $query);
43 @include radius-bounded($query: $query);
44 @include surface-styles($query: $query);
45 }
46
47 .mdc-ripple-surface[data-mdc-ripple-is-unbounded],
48 .mdc-ripple-upgraded--unbounded {
49 @include radius-unbounded($query: $query);
50 @include unbounded-styles($query: $query);
51 }
52}
53
54/// Sets all states (including hover, focus, press, activated and selected) with
55/// given color as base color.
56///
57/// This mixin is for internal use only. Use `ripple-theme.states($color)` mixin
58/// to set interactive states (hover, focus & press) color.
59///
60/// @param {Color|String} $color - Target base color. Can be valid CSS color or
61/// a color string literal (i.e., `primary`, `secondary`, etc).
62@mixin states-for-color($color, $query: feature-targeting.all()) {
63 @include ripple-theme.states($color, $query: $query);
64 @include ripple-theme.states-activated($color, $query: $query);
65 @include ripple-theme.states-selected($color, $query: $query);
66}
67
68@mixin surface-styles($query: feature-targeting.all()) {
69 $feat-structure: feature-targeting.create-target($query, structure);
70
71 @include feature-targeting.targets($feat-structure) {
72 position: relative;
73 outline: none;
74 overflow: hidden;
75 }
76}
77
78@mixin unbounded-styles($query: feature-targeting.all()) {
79 $feat-structure: feature-targeting.create-target($query, structure);
80 @include feature-targeting.targets($feat-structure) {
81 overflow: visible;
82 }
83}
84
85@mixin common($query: feature-targeting.all()) {
86 $feat-animation: feature-targeting.create-target($query, animation);
87
88 // Ensure that styles needed by any component using MDC Ripple are emitted, but only once.
89 // (Every component using MDC Ripple imports these mixins, but doesn't necessarily import
90 // mdc-ripple.scss.)
91 @include feature-targeting.targets($feat-animation) {
92 @include base-mixins.emit-once('mdc-ripple/common/animation') {
93 @include keyframes_;
94 }
95 }
96}
97
98@mixin surface(
99 $query: feature-targeting.all(),
100 $ripple-target: '&',
101 $include-will-change: true // TODO(b/151931961): Remove once resolved
102) {
103 $feat-animation: feature-targeting.create-target($query, animation);
104 $feat-structure: feature-targeting.create-target($query, structure);
105
106 @include feature-targeting.targets($feat-structure) {
107 --mdc-ripple-fg-size: 0;
108 --mdc-ripple-left: 0;
109 --mdc-ripple-top: 0;
110 --mdc-ripple-fg-scale: 1;
111 --mdc-ripple-fg-translate-end: 0;
112 --mdc-ripple-fg-translate-start: 0;
113
114 -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
115 // TODO(b/151931961): Remove the following block once resolved
116 @if $include-will-change {
117 will-change: transform, opacity;
118 }
119 }
120
121 #{$ripple-target}::before,
122 #{$ripple-target}::after {
123 @include feature-targeting.targets($feat-structure) {
124 position: absolute;
125 border-radius: 50%;
126 opacity: 0;
127 pointer-events: none;
128 content: '';
129 }
130 }
131
132 #{$ripple-target}::before {
133 @include feature-targeting.targets($feat-animation) {
134 // Also transition background-color to avoid unnatural color flashes when toggling activated/selected state
135 transition: opacity ripple-theme.$states-wash-duration linear,
136 background-color ripple-theme.$states-wash-duration linear;
137 }
138
139 @include feature-targeting.targets($feat-structure) {
140 // Ensure that the ripple wash for hover/focus states is displayed on top of positioned child elements
141 @include theme.property(
142 z-index,
143 custom-properties.create(--mdc-ripple-z-index, 1)
144 );
145 }
146 }
147
148 #{$ripple-target}::after {
149 @include feature-targeting.targets($feat-structure) {
150 @include theme.property(
151 z-index,
152 custom-properties.create(--mdc-ripple-z-index, 0)
153 );
154 }
155 }
156
157 // Common styles for upgraded surfaces (some of these depend on custom properties set via JS or other mixins)
158
159 &.mdc-ripple-upgraded {
160 #{$ripple-target}::before {
161 @include feature-targeting.targets($feat-structure) {
162 transform: scale(var(--mdc-ripple-fg-scale, 1));
163 }
164 }
165
166 #{$ripple-target}::after {
167 @include feature-targeting.targets($feat-structure) {
168 top: 0;
169 /* @noflip */
170 left: 0;
171 transform: scale(0);
172 transform-origin: center center;
173 }
174 }
175 }
176
177 &.mdc-ripple-upgraded--unbounded {
178 #{$ripple-target}::after {
179 @include feature-targeting.targets($feat-structure) {
180 top: var(--mdc-ripple-top, 0);
181 /* @noflip */
182 left: var(--mdc-ripple-left, 0);
183 }
184 }
185 }
186
187 &.mdc-ripple-upgraded--foreground-activation {
188 #{$ripple-target}::after {
189 @include feature-targeting.targets($feat-animation) {
190 animation: mdc-ripple-fg-radius-in ripple-theme.$translate-duration
191 forwards,
192 mdc-ripple-fg-opacity-in ripple-theme.$fade-in-duration forwards;
193 }
194 }
195 }
196
197 &.mdc-ripple-upgraded--foreground-deactivation {
198 #{$ripple-target}::after {
199 @include feature-targeting.targets($feat-animation) {
200 animation: mdc-ripple-fg-opacity-out ripple-theme.$fade-out-duration;
201 }
202
203 @include feature-targeting.targets($feat-structure) {
204 // Retain transform from mdc-ripple-fg-radius-in activation
205 transform: translate(var(--mdc-ripple-fg-translate-end, 0))
206 scale(var(--mdc-ripple-fg-scale, 1));
207 }
208 }
209 }
210}
211
212@mixin radius-bounded(
213 $radius: 100%,
214 $query: feature-targeting.all(),
215 $ripple-target: '&'
216) {
217 $feat-struture: feature-targeting.create-target($query, structure);
218
219 #{$ripple-target}::before,
220 #{$ripple-target}::after {
221 @include feature-targeting.targets($feat-struture) {
222 top: calc(50% - #{$radius});
223 /* @noflip */
224 left: calc(50% - #{$radius});
225 width: $radius * 2;
226 height: $radius * 2;
227 }
228 }
229
230 &.mdc-ripple-upgraded {
231 #{$ripple-target}::after {
232 @include feature-targeting.targets($feat-struture) {
233 width: var(--mdc-ripple-fg-size, $radius);
234 height: var(--mdc-ripple-fg-size, $radius);
235 }
236 }
237 }
238}
239
240@mixin radius-unbounded(
241 $radius: 100%,
242 $query: feature-targeting.all(),
243 $ripple-target: '&'
244) {
245 $feat-struture: feature-targeting.create-target($query, structure);
246
247 #{$ripple-target}::before,
248 #{$ripple-target}::after {
249 @include feature-targeting.targets($feat-struture) {
250 top: calc(50% - #{$radius / 2});
251 /* @noflip */
252 left: calc(50% - #{$radius / 2});
253 width: $radius;
254 height: $radius;
255 }
256 }
257
258 &.mdc-ripple-upgraded {
259 #{$ripple-target}::before,
260 #{$ripple-target}::after {
261 @include feature-targeting.targets($feat-struture) {
262 top: var(--mdc-ripple-top, calc(50% - #{$radius / 2}));
263 /* @noflip */
264 left: var(--mdc-ripple-left, calc(50% - #{$radius / 2}));
265 width: var(--mdc-ripple-fg-size, $radius);
266 height: var(--mdc-ripple-fg-size, $radius);
267 }
268 }
269
270 #{$ripple-target}::after {
271 @include feature-targeting.targets($feat-struture) {
272 width: var(--mdc-ripple-fg-size, $radius);
273 height: var(--mdc-ripple-fg-size, $radius);
274 }
275 }
276 }
277}
278
279// Common styles for a ripple target element.
280// Used for components which have an inner ripple target element.
281@mixin target-common($query: feature-targeting.all()) {
282 $feat-structure: feature-targeting.create-target($query, structure);
283
284 @include feature-targeting.targets($feat-structure) {
285 position: absolute;
286 top: 0;
287 left: 0;
288 width: 100%;
289 height: 100%;
290 // Necessary for clicks on other inner elements (e.g. close icon in chip)
291 // to go through.
292 pointer-events: none;
293 }
294}
295
296@mixin keyframes_ {
297 @keyframes mdc-ripple-fg-radius-in {
298 from {
299 animation-timing-function: variables2.$standard-curve-timing-function;
300 // NOTE: For these keyframes, we do not need custom property fallbacks because they are only
301 // used in conjunction with `.mdc-ripple-upgraded`. Since MDCRippleFoundation checks to ensure
302 // that custom properties are supported within the browser before adding this class, we can
303 // safely use them without a fallback.
304 transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);
305 }
306
307 to {
308 transform: translate(var(--mdc-ripple-fg-translate-end, 0))
309 scale(var(--mdc-ripple-fg-scale, 1));
310 }
311 }
312
313 @keyframes mdc-ripple-fg-opacity-in {
314 from {
315 animation-timing-function: linear;
316 opacity: 0;
317 }
318
319 to {
320 opacity: var(--mdc-ripple-fg-opacity, 0);
321 }
322 }
323
324 @keyframes mdc-ripple-fg-opacity-out {
325 from {
326 animation-timing-function: linear;
327 opacity: var(--mdc-ripple-fg-opacity, 0);
328 }
329
330 to {
331 opacity: 0;
332 }
333 }
334}