UNPKG

11 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/elevation/mixins' as elevation-mixins;
28@use '@material/feature-targeting/feature-targeting';
29@use '@material/ripple/ripple';
30@use '@material/ripple/ripple-theme';
31@use '@material/rtl/rtl';
32@use '@material/shape/mixins' as shape-mixins;
33@use '@material/theme/theme';
34@use './variables';
35@use '@material/theme/theme-color';
36@use '@material/dom/mixins' as dom-mixins;
37
38//
39// Public
40//
41
42$ripple-target: '.mdc-card__ripple';
43
44@mixin core-styles($query: feature-targeting.all()) {
45 @include without-ripple($query);
46 @include ripple($query);
47}
48
49// This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
50// card styles. It is recommended that most users use `mdc-card-core-styles` instead.
51@mixin without-ripple($query: feature-targeting.all()) {
52 // postcss-bem-linter: define card
53
54 $feat-color: feature-targeting.create-target($query, color);
55 $feat-structure: feature-targeting.create-target($query, structure);
56
57 // prettier-ignore
58 @include elevation-mixins.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE
59
60 .mdc-card {
61 @include shape-radius(variables.$shape-radius, $query: $query);
62 @include fill-color(surface, $query);
63 @include elevation-mixins.overlay-surface-position($query: $query);
64 @include elevation-mixins.overlay-dimensions(100%, $query: $query);
65 @include elevation-mixins.elevation(1, $query: $query);
66
67 @include feature-targeting.targets($feat-structure) {
68 @include container-layout_;
69 }
70
71 // Transparent card border for high-contrast mode.
72 &::after {
73 @include shape-radius(variables.$shape-radius, $query: $query);
74 @include dom-mixins.transparent-border($query: $query);
75 @include feature-targeting.targets($feat-structure) {
76 pointer-events: none;
77 }
78 }
79 }
80
81 .mdc-card--outlined {
82 @include elevation-mixins.elevation(0, $query: $query);
83 @include outline(variables.$outline-color, $query: $query);
84
85 // Outlined card already displays border in high-contrast mode. Overwriting
86 // styles set above to remove a duplicate border.
87 &::after {
88 @include feature-targeting.targets($feat-structure) {
89 border: none;
90 }
91 }
92 }
93
94 .mdc-card__content {
95 @include feature-targeting.targets($feat-structure) {
96 border-radius: inherit;
97 height: 100%;
98 }
99 }
100
101 //
102 // Media
103 //
104
105 .mdc-card__media {
106 @include feature-targeting.targets($feat-structure) {
107 position: relative; // Child element `__media-content` has `position: absolute`
108 box-sizing: border-box;
109 background-repeat: no-repeat;
110 background-position: center;
111 background-size: cover;
112 }
113
114 &::before {
115 @include feature-targeting.targets($feat-structure) {
116 display: block;
117 content: '';
118 }
119 }
120 }
121
122 .mdc-card__media:first-child {
123 @include feature-targeting.targets($feat-structure) {
124 border-top-left-radius: inherit;
125 border-top-right-radius: inherit;
126 }
127 }
128
129 .mdc-card__media:last-child {
130 @include feature-targeting.targets($feat-structure) {
131 border-bottom-left-radius: inherit;
132 border-bottom-right-radius: inherit;
133 }
134 }
135
136 .mdc-card__media--square {
137 @include media-aspect-ratio(1, 1, $query);
138 }
139
140 .mdc-card__media--16-9 {
141 @include media-aspect-ratio(16, 9, $query);
142 }
143
144 .mdc-card__media-content {
145 @include feature-targeting.targets($feat-structure) {
146 position: absolute;
147 top: 0;
148 right: 0;
149 bottom: 0;
150 left: 0;
151 box-sizing: border-box;
152 }
153 }
154
155 //
156 // Primary action
157 //
158
159 .mdc-card__primary-action {
160 @include feature-targeting.targets($feat-structure) {
161 @include container-layout_;
162
163 position: relative; // Needed to prevent the ripple wash from overflowing the container in IE and Edge
164 outline: none;
165 color: inherit;
166 text-decoration: none;
167 cursor: pointer;
168 overflow: hidden;
169 }
170 }
171
172 .mdc-card__primary-action:first-child {
173 @include feature-targeting.targets($feat-structure) {
174 border-top-left-radius: inherit;
175 border-top-right-radius: inherit;
176 }
177 }
178
179 .mdc-card__primary-action:last-child {
180 @include feature-targeting.targets($feat-structure) {
181 border-bottom-left-radius: inherit;
182 border-bottom-right-radius: inherit;
183 }
184 }
185
186 //
187 // Action row
188 //
189
190 .mdc-card__actions {
191 @include feature-targeting.targets($feat-structure) {
192 @include actions-layout_;
193
194 min-height: 52px;
195 padding: 8px;
196 }
197 }
198
199 .mdc-card__actions--full-bleed {
200 @include feature-targeting.targets($feat-structure) {
201 padding: 0;
202 }
203 }
204
205 .mdc-card__action-buttons,
206 .mdc-card__action-icons {
207 @include feature-targeting.targets($feat-structure) {
208 @include actions-layout_;
209 }
210 }
211
212 .mdc-card__action-icons {
213 @include feature-targeting.targets($feat-color) {
214 @include theme.property(color, variables.$action-icon-color);
215 }
216
217 @include feature-targeting.targets($feat-structure) {
218 flex-grow: 1;
219 justify-content: flex-end;
220 }
221 }
222
223 .mdc-card__action-buttons + .mdc-card__action-icons {
224 @include feature-targeting.targets($feat-structure) {
225 @include rtl.reflexive-box(margin, left, 16px);
226 }
227 }
228
229 //
230 // Action items
231 //
232
233 .mdc-card__action {
234 @include feature-targeting.targets($feat-structure) {
235 @include actions-layout_(inline-flex);
236
237 justify-content: center;
238 cursor: pointer;
239 user-select: none;
240 }
241
242 &:focus {
243 @include feature-targeting.targets($feat-structure) {
244 outline: none;
245 }
246 }
247 }
248
249 //
250 // Action buttons
251 //
252
253 .mdc-card__action--button {
254 @include feature-targeting.targets($feat-structure) {
255 @include rtl.reflexive-box(margin, right, 8px);
256
257 padding: 0 8px;
258 }
259
260 &:last-child {
261 @include feature-targeting.targets($feat-structure) {
262 @include rtl.reflexive-box(margin, right, 0);
263 }
264 }
265 }
266
267 .mdc-card__actions--full-bleed .mdc-card__action--button {
268 @include feature-targeting.targets($feat-structure) {
269 justify-content: space-between;
270 width: 100%;
271 height: auto;
272 max-height: none;
273 margin: 0;
274 padding: 8px 16px;
275 @include rtl.ignore-next-line();
276 text-align: left;
277 }
278
279 @include rtl.rtl {
280 @include feature-targeting.targets($feat-structure) {
281 @include rtl.ignore-next-line();
282 text-align: right;
283 }
284 }
285 }
286
287 //
288 // Action icons
289 //
290
291 .mdc-card__action--icon {
292 @include feature-targeting.targets($feat-structure) {
293 // Icon buttons are taller than buttons, so we need to adjust their margins to prevent the action row from
294 // expanding.
295 margin: -6px 0;
296
297 // Same padding as mdc-icon-button.
298 padding: 12px;
299 }
300 }
301
302 .mdc-card__action--icon:not(:disabled) {
303 @include feature-targeting.targets($feat-color) {
304 @include theme.property(color, variables.$action-icon-color);
305 }
306 }
307
308 // postcss-bem-linter: end
309}
310
311// This API is intended for use by frameworks that may want to separate the ripple-related styles from the other
312// card styles. It is recommended that most users use `mdc-card-core-styles` instead.
313@mixin ripple($query: feature-targeting.all()) {
314 @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
315 $feat-structure: feature-targeting.create-target($query, structure);
316
317 .mdc-card__primary-action {
318 @include ripple.surface($query, $ripple-target: $ripple-target);
319 @include ripple.radius-bounded(
320 $query: $query,
321 $ripple-target: $ripple-target
322 );
323 @include ripple-theme.states(
324 $query: $query,
325 $ripple-target: $ripple-target
326 );
327
328 #{$ripple-target} {
329 @include feature-targeting.targets($feat-structure) {
330 box-sizing: content-box;
331 height: 100%;
332 overflow: hidden;
333 left: 0;
334 pointer-events: none;
335 position: absolute;
336 top: 0;
337 width: 100%;
338 }
339 }
340
341 @include ripple-theme.focus {
342 &::after {
343 @include dom-mixins.transparent-border(
344 $border-width: 5px,
345 $border-style: double,
346 $query: $query
347 );
348 }
349 }
350 }
351}
352
353@mixin fill-color($color, $query: feature-targeting.all()) {
354 $feat-color: feature-targeting.create-target($query, color);
355
356 @include feature-targeting.targets($feat-color) {
357 @include theme.property(background-color, $color);
358 }
359}
360
361@mixin outline(
362 $color,
363 $thickness: variables.$outline-width,
364 $query: feature-targeting.all()
365) {
366 $feat-color: feature-targeting.create-target($query, color);
367 $feat-structure: feature-targeting.create-target($query, structure);
368
369 @include feature-targeting.targets($feat-structure) {
370 border-width: $thickness;
371 border-style: solid;
372 }
373
374 @include feature-targeting.targets($feat-color) {
375 border-color: theme-color.prop-value($color);
376 }
377}
378
379@mixin shape-radius(
380 $radius,
381 $rtl-reflexive: false,
382 $query: feature-targeting.all()
383) {
384 @include shape-mixins.radius($radius, $rtl-reflexive, $query: $query);
385}
386
387@mixin media-aspect-ratio($x, $y, $query: feature-targeting.all()) {
388 $feat-structure: feature-targeting.create-target($query, structure);
389
390 &::before {
391 @include feature-targeting.targets($feat-structure) {
392 // This clever trick brought to you by: http://www.mademyday.de/css-height-equals-width-with-pure-css.html
393 margin-top: math.percentage(math.div($y, $x));
394 }
395 }
396}
397
398//
399// Private
400//
401
402@mixin container-layout_ {
403 display: flex;
404 flex-direction: column;
405 box-sizing: border-box;
406}
407
408@mixin actions-layout_($display: flex) {
409 display: $display;
410 flex-direction: row;
411 align-items: center;
412 box-sizing: border-box;
413}