UNPKG

11.5 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@use 'sass:list';
24@use 'sass:map';
25@use 'sass:meta';
26@use '@material/feature-targeting/feature-targeting';
27@use './css';
28@use './custom-properties';
29@use './gss';
30@use './keys';
31@use './replace';
32@use './theme-color';
33
34@mixin core-styles($query: feature-targeting.all()) {
35 $feat-color: feature-targeting.create-target($query, color);
36
37 :root {
38 @include feature-targeting.targets($feat-color) {
39 @each $style in theme-color.get-theme-keys() {
40 @include custom-properties.declaration(
41 keys.create-custom-property($style)
42 );
43 }
44 }
45 }
46
47 @each $style in theme-color.get-theme-keys() {
48 @if $style != 'background' and $style != 'surface' {
49 .mdc-theme--#{$style} {
50 @include feature-targeting.targets($feat-color) {
51 @include property(color, $style, $important: true);
52 }
53 }
54 } @else {
55 .mdc-theme--#{$style} {
56 @include feature-targeting.targets($feat-color) {
57 @include property(background-color, $style);
58 }
59 }
60 }
61 }
62
63 // CSS rules for using primary and secondary (plus light/dark variants) as background colors.
64 @each $style in ('primary', 'secondary') {
65 .mdc-theme--#{$style}-bg {
66 @include feature-targeting.targets($feat-color) {
67 @include property(background-color, $style, $important: true);
68 }
69 }
70 }
71}
72
73/// Applies a dynamic value to the specified property. This mixin should be used
74/// in theme style mixins when setting properties.
75///
76/// The value may be any of the following:
77/// - a standard CSS value
78/// - a custom property Map, e.g. (varname: --mdc-foo, fallback: blue)
79/// - a Material theme key String, e.g. 'primary', 'on-primary'
80///
81/// @example
82/// @include theme.property(color, teal);
83/// @include theme.property(color, custom-properties.create(foo, blue));
84/// @include theme.property(color, primary);
85///
86/// A `$replace` Map parameter may be provided to replace key/value pairs for
87/// string values. This can be used to substitute parameters in complex string
88/// values such as `calc()` with custom properties.
89///
90/// @example
91/// @include theme.property(
92/// width,
93/// calc(foo + bar),
94/// $replace: (foo: custom-properties.create(foo), bar: 8px)
95/// );
96///
97/// Note: Material theme key Strings (e.g. `primary`) are not supported as
98/// replacement values.
99///
100/// A CSS custom property declaration may be emitted by providing a custom
101/// property Map to `$property`. The fallback value (or `$value` if provided)
102/// will be used as the declaration value.
103///
104/// @example - scss
105/// .foo {
106/// @include theme.property(custom-properties.create(foo, teal));
107/// @include theme.property(custom-properties.create(bar, teal), blue);
108/// }
109///
110/// @example - css
111/// .foo {
112/// --mdc-foo: teal;
113/// --mdc-bar: blue;
114/// }
115///
116/// @param {String | Map} $property - The name of the CSS property. May also be
117/// a custom property Map to emit a custom propery declaration.
118/// @param {String | Number | Color | List | Map} $value - The property's value.
119/// This parameter may be omitted if `$property` is a custom property Map.
120/// @param {Map} $gss - Optional Map of GSS annotations to set.
121/// @param {Map} $replace - An optional Map of replacement key/value pairs if
122/// the `$value` is a string.
123/// @param {Bool} $important - Set to true to add an `!important` rule. Defaults
124/// to false.
125@mixin property(
126 $property,
127 $value: null,
128 $gss: (),
129 $replace: null,
130 $important: false
131) {
132 @if custom-properties.is-custom-prop($property) {
133 // $property is a custom property Map
134 // --mdc-foo: value;
135 @if $value {
136 $property: custom-properties.set-fallback(
137 $property,
138 $value,
139 $shallow: true
140 );
141 }
142
143 @include custom-properties.declaration(
144 $property,
145 $gss: $gss,
146 $important: $important
147 );
148 } @else if custom-properties.is-custom-prop($value) {
149 // $value is a custom property Map
150 // property: var(--mdc-foo, fallback);
151 @include custom-properties.declaration(
152 $property,
153 $value,
154 $gss: $gss,
155 $important: $important
156 );
157 } @else if keys.is-key($value) {
158 // $value is a key String
159 // property: key;
160 $custom-prop: keys.create-custom-property($value);
161
162 @if theme-color.is-theme-key($value) {
163 // Determine if we need to use a compile-time updated value to support
164 // Angular.
165 $key: $value;
166 // (changed: Bool, value: *)
167 $result: theme-color.deprecated-get-global-theme-key-value-if-changed(
168 $key
169 );
170
171 @if map.get($result, changed) {
172 // $mdc-theme-property-values was changed at compile time. Use the
173 // global value instead. Otherwise if it was not changed, continue
174 // using the key store normally.
175 $custom-prop: keys.create-custom-property($key);
176 $custom-prop: custom-properties.set-fallback(
177 $custom-prop,
178 map.get($result, value)
179 );
180 }
181 }
182
183 @include custom-properties.declaration(
184 $property,
185 $custom-prop,
186 $gss: $gss,
187 $important: $important
188 );
189 } @else {
190 // $value is a standard CSS value
191 // property: value;
192 $fallback: null;
193 @if $replace {
194 // If any replacements are null, treat the entire value as null (do not
195 // emit anything).
196 @each $name, $replacement in $replace {
197 @if $replacement == null {
198 $value: null;
199 }
200 }
201 }
202
203 @if $replace and $value {
204 @if meta.type-of($replace) != 'map' {
205 @error 'mdc-theme: Invalid replacement #{$replace}. Must be a Map.';
206 }
207
208 $replace-map-fallback: ();
209 $replace-map-value: ();
210 $needs-fallback: false;
211 @each $name, $replacement in $replace {
212 @if custom-properties.is-custom-prop($replacement) {
213 $replace-value: custom-properties.get-declaration-value($replacement);
214 $replace-fallback: custom-properties.get-declaration-fallback(
215 $replacement
216 );
217 @if $replace-fallback {
218 $needs-fallback: true;
219 }
220
221 $replace-map-value: map.set(
222 $replace-map-value,
223 $name,
224 $replace-value
225 );
226 $replace-map-fallback: map.set(
227 $replace-map-fallback,
228 $name,
229 $replace-fallback
230 );
231 } @else {
232 $replace-map-value: map.set($replace-map-value, $name, $replacement);
233 $replace-map-fallback: map.set(
234 $replace-map-fallback,
235 $name,
236 $replacement
237 );
238 }
239 }
240
241 @if meta.type-of($value) == 'string' {
242 @if $needs-fallback {
243 $fallback: replace.replace-string($value, $replace-map-fallback);
244 }
245 $value: replace.replace-string($value, $replace-map-value);
246 } @else if meta.type-of($value) == 'list' {
247 @if $needs-fallback {
248 $fallback: replace.replace-list($value, $replace-map-fallback);
249 }
250 $value: replace.replace-list($value, $replace-map-value);
251 } @else {
252 @error 'mdc-theme: Invalid replacement value #{$value}. $replace may only be used with string or list values.';
253 }
254 }
255
256 @include css.declaration(
257 $property,
258 $value,
259 $fallback-value: $fallback,
260 $gss: $gss,
261 $important: $important
262 );
263 }
264}
265
266// @deprecated use the `property()` mixin instead
267@mixin prop($property, $style, $important: false) {
268 @include property($property, $style, $important: $important);
269}
270
271/// Validates theme configuration keys by comparing it with original theme
272/// configuration, also validates theme values to see if it has any unsupported
273/// value formats.
274/// @param {Map} $origin-theme - Original theme configuration in Sass map format
275/// that has all supported keys.
276/// @param {Map} $custom-theme - Provided theme configuration in Sass map format
277/// that should be validated against `$origin-theme`.
278/// @examples
279/// @mixin theme($theme) {
280/// @include theme.validate-theme($light-theme, $theme);
281///
282/// // ...
283/// }
284@mixin validate-theme($origin-theme, $custom-theme, $test-only: false) {
285 @include validate-theme-keys(
286 $origin-theme,
287 $custom-theme,
288 $test-only: $test-only
289 );
290 @include _validate-theme-values($custom-theme, $test-only: $test-only);
291}
292
293/// Validates theme configuration keys by comparing it with original theme
294/// configuration.
295/// @see Use `validate-theme()` to validate both theme keys and theme values.
296/// @param {Map} $origin-theme - Original theme configuration in Sass map format
297/// that has all supported keys.
298/// @param {Map} $custom-theme - Provided theme configuration in Sass map format
299/// that should be validated against `$origin-theme`.
300@mixin validate-theme-keys($origin-theme, $custom-theme, $test-only: false) {
301 $origin-keys: map.keys($origin-theme);
302 $unsupported-keys: ();
303
304 @each $key, $value in $custom-theme {
305 @if (not list.index($origin-keys, $key)) {
306 $unsupported-keys: list.append(
307 $unsupported-keys,
308 $key,
309 $separator: comma
310 );
311 }
312 }
313
314 @if list.length($unsupported-keys) > 0 {
315 $error-message: 'Unsupported keys found: #{$unsupported-keys}. Expected one of: #{$origin-keys}.';
316
317 @if $test-only {
318 content: $error-message;
319 } @else {
320 @error $error-message;
321 }
322 }
323}
324
325/// Validates theme configuration values to see if it has any unsupported value
326/// formats.
327/// @see Use `validate-theme()` to validate both theme keys and theme values.
328/// @param {Map} $custom-theme - Provided theme configuration in Sass map format
329/// that needs to be validated.
330@mixin _validate-theme-values($custom-theme, $test-only: false) {
331 $unsupported-custom-prop-keys: ();
332
333 @each $key, $value in $custom-theme {
334 @if custom-properties.is-custom-prop($value) {
335 $unsupported-custom-prop-keys: list.append(
336 $unsupported-custom-prop-keys,
337 $key,
338 $separator: comma
339 );
340 }
341 }
342
343 @if list.length($unsupported-custom-prop-keys) > 0 {
344 $error-message: 'Custom properties are not supported for theme map keys: #{$unsupported-custom-prop-keys}';
345
346 @if $test-only {
347 content: $error-message;
348 } @else {
349 @error $error-message;
350 }
351 }
352}