UNPKG

12 kBSCSSView Raw
1//
2// Copyright 2020 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 'sass:string';
27@use './css';
28@use './gss';
29
30/// Configuration for custom properties.
31/// @see {mixin} configure
32$_config: (
33 emit-custom-properties: true,
34 emit-fallback-values: true,
35 emit-fallback-vars: true,
36);
37
38/// Configure options for custom properties. The configuration will be applied
39/// within the scope of the mixin's content and reset when the mixin scope ends.
40///
41/// @example - scss
42/// @include configure($emit-fallback-values: false) {
43/// // No fallback values will be emitted within this mixin scope
44/// @include another-mixin();
45/// }
46///
47/// All parameters must be provided as argument lists.
48///
49/// @link https://sass-lang.com/documentation/values/lists#argument-lists
50///
51/// @param {Bool} $emit-custom-properties [true] - Enable or disable all
52/// custom property emission.
53/// @param {Bool} $emit-fallback-values [true] - Enable or disable emission of
54/// fallback CSS static values. This does not include dynamic `var()`
55/// fallback values.
56/// @param {Bool} $emit-fallback-vars [true] - Enable or disable emission of
57/// fallback `var()` values.
58@mixin configure($config...) {
59 @if not meta.content-exists() {
60 @error 'content is required for configure()';
61 }
62
63 $config: meta.keywords($config);
64 @each $key, $value in $config {
65 @if $value == null {
66 $config: map.remove($config, $key);
67 }
68 }
69
70 @if list.length($config) == 0 {
71 @content;
72 } @else {
73 $previous: $_config;
74 // Use !global to avoid shadowing
75 // https://sass-lang.com/documentation/variables#shadowing
76 $_config: map.merge($_config, $config) !global;
77 @content;
78 $_config: $previous !global;
79 }
80}
81
82/// Returns true if the parameter is a custom property Map.
83///
84/// @param {*} $value - the value to test.
85/// @return true if the value is a custom property Map, or false if not.
86@function is-custom-prop($value) {
87 @return meta.type-of($value) == 'map' and map.has-key($value, 'varname');
88}
89
90/// Indicates whether or not a value is a custom property var() string.
91///
92/// @example - scss
93/// $is-prop-string: is-custom-prop-string('var(--foo)'); // true
94///
95/// @param {*} $value - The value to test.
96/// @return {Bool} True if the value is a custom property var() string, or
97/// false if not.
98@function is-custom-prop-string($value) {
99 @return meta.type-of($value) == 'string' and string.slice($value, 1, 4) ==
100 'var(';
101}
102
103/// Returns true if $prop1's varname and fallback values are deeply equal to
104/// $prop2's varname and fallback values.
105///
106/// @param {Map} $prop1 - the first value to test.
107/// @param {Map} $prop2 - the second value to test.
108/// @return true if both properties are deeply equal
109@function are-equal($prop1, $prop2) {
110 @return create-var($prop1) == create-var($prop2);
111}
112
113/// Creates a custom property Map.
114///
115/// @param {String} $varname - the custom property name.
116/// @param {String | Number | Map} - the fallback value (may be another custom
117/// property Map). May be null.
118/// @return a custom property Map.
119@function create($varname, $fallback: null) {
120 @if string.slice($varname, 1, 2) != '--' {
121 $varname: create-varname($varname);
122 }
123
124 @return (varname: $varname, fallback: $fallback);
125}
126
127/// Global prefix for all custom properties.
128$_varname-prefix: 'mdc';
129
130/// Create a custom property variable name with the global prefix.
131///
132/// @example - scss
133/// $varname: create-varname(foo); // --mdc-foo
134///
135/// @param {String} $name - The name of the custom property.
136/// @return {String} The full CSS custom property variable name.
137@function create-varname($name) {
138 @return --#{$_varname-prefix}-#{$name};
139}
140
141/// Returns the custom property variable name of a custom property Map.
142///
143/// @param {Map} $custom-prop - a custom property Map.
144/// @return the custom property variable name defined by the Map.
145@function get-varname($custom-prop) {
146 @return map.get($custom-prop, 'varname');
147}
148
149/// Returns the fallback value of a custom property Map. May be null if the
150/// custom property does not have a fallback.
151///
152/// @param {Map} $custom-prop - a custom property Map.
153/// @param {Bool} $shallow - if true, return the first fallback value, which
154/// may be another custom property Map. Defaults to false, which will return
155/// the deep final fallback value.
156/// @return the fallback value of a custom property Map. May be null.
157@function get-fallback($custom-prop, $shallow: false) {
158 $fallback: map.get($custom-prop, 'fallback');
159 @if is-custom-prop($fallback) and not $shallow {
160 @return get-fallback($fallback);
161 }
162
163 @return $fallback;
164}
165
166/// Creates a new custom property Map and returns it with the specified new
167/// fallback value.
168///
169/// @param {Map} $custom-prop - the custom property Map to copy.
170/// @param {String | Number | Map} $new-fallback - the new fallback value of the
171/// custom property Map. May be null.
172/// @param {Bool} $shallow - if true, set the first fallback value. Defaults to
173/// false, which will set the deep final fallback value.
174/// @return a new custom property Map with the new fallback value.
175@function set-fallback($custom-prop, $new-fallback, $shallow: false) {
176 $varname: get-varname($custom-prop);
177 $first-fallback: get-fallback($custom-prop, $shallow: true);
178
179 @if is-custom-prop($first-fallback) and not $shallow {
180 // The first fallback is a custom property and $shallow is false. Deeply
181 // set the fallback value of the custom property and get the new custom
182 // property Map returned.
183 $new-fallback: set-fallback($first-fallback, $new-fallback);
184 }
185
186 @return create($varname, $new-fallback);
187}
188
189/// Creates and returns a CSS `var()` function value represented by the provided
190/// custom property Map.
191///
192/// If custom properties are disabled, this function will return the custom
193/// property's fallback value instead, which may be `null` and result in an
194/// empty declaration.
195///
196/// @param {Map} $custom-prop - A custom property Map.
197/// @return {*} A CSS value, typically a `var()` function, representing the
198/// custom property. The returned value may change depending on the current
199/// configuration options for custom properties and whether or not a
200/// fallback value is present in the custom property Map.
201@function create-var($custom-prop) {
202 @if not map.get($_config, emit-custom-properties) {
203 // If configured not to emit custom properties and a request is made for a
204 // custom prop's CSS value, return its fallback value. If this is null, it
205 // will result in an empty declaration.
206 @return get-fallback($custom-prop);
207 }
208
209 $varname: get-varname($custom-prop);
210 $fallback: get-fallback($custom-prop, $shallow: true);
211
212 $emit-fallback-vars: map.get($_config, emit-fallback-vars);
213 $fallback-is-prop: is-custom-prop($fallback);
214 @if $fallback-is-prop and $emit-fallback-vars {
215 @return var($varname, create-var($fallback));
216 }
217
218 $emit-fallback-values: map.get($_config, emit-fallback-values);
219 @if $fallback and not $fallback-is-prop and $emit-fallback-values {
220 @return var($varname, $fallback);
221 }
222
223 @return var($varname);
224}
225
226/// Retrieves the CSS declaration value for a custom property Map. This is
227/// typically a `var()` function.
228///
229/// If custom properties are disabled, the custom property's fallback value
230/// will be returned instead. If the fallback value is `null`, an error will
231/// be thrown.
232///
233/// @param {Map} $custom-prop - The custom property Map to retrieve a
234/// declaration value for.
235/// @return {*} The CSS declaration value.
236@function get-declaration-value($custom-prop) {
237 $emit-custom-properties: map.get($_config, emit-custom-properties);
238 $fallback: get-fallback($custom-prop);
239 @if not $emit-custom-properties and not $fallback {
240 @error 'Custom properties are disabled and #{get-varname($custom-prop)} does not have a fallback value.';
241 }
242
243 @if not $emit-custom-properties {
244 @return $fallback;
245 }
246
247 @return create-var($custom-prop);
248}
249
250/// Retrieves the CSS fallback declaration value for a custom property Map.
251/// This is typically a static CSS value if a custom property has a fallback, or
252/// null if it does not.
253///
254/// This function will always return `null` if custom properties or fallback
255/// values are disabled.
256///
257/// @param {Map} $custom-prop - The custom property Map to retrieve a fallback
258/// declaration value for.
259/// @return {String | null} The CSS fallback declaration value.
260@function get-declaration-fallback($custom-prop) {
261 $emit-custom-properties: map.get($_config, emit-custom-properties);
262 $emit-fallback-values: map.get($_config, emit-fallback-values);
263 @if not $emit-custom-properties or not $emit-fallback-values {
264 @return null;
265 }
266
267 @return get-fallback($custom-prop);
268}
269
270/// Emits a CSS declaration for a custom property. A custom property may either
271/// be provided as the value to a CSS property string to be emitted as a var()
272/// function, or as a standalone value to be emitted as a custom property
273/// declaration.
274///
275/// @example - scss
276/// @include declaration(color, create(--foo, teal));
277/// // color: var(--foo, teal);
278/// @include declaration(create(--foo, teal));
279/// // --foo: teal;
280///
281/// Standalone custom properties must have a fallback value to emit as a CSS
282/// declaration.
283///
284/// The declaration emitted for a custom property may change or be ignored
285/// based on the current configuration for custom properties.
286///
287/// @see {mixin} css.declaration
288/// @see {mixin} configuration
289///
290/// @param {String} $property - The CSS property of the declaration or the
291/// custom property Map to emit.
292/// @param {Map} $custom-prop - A custom property Map for the property's value.
293/// Optional if $property is the custom property Map to emit.
294/// @param {Map} $gss - An optional Map of GSS annotations to add.
295/// @param {Bool} $important - If true, add `!important` to the declaration.
296@mixin declaration($property, $custom-prop: null, $gss: (), $important: false) {
297 @if $property {
298 $value: null;
299 $fallback-value: null;
300 @if is-custom-prop($property) {
301 @if map.get($_config, emit-custom-properties) {
302 $custom-prop: $property;
303 $property: get-varname($custom-prop);
304 $value: get-fallback($custom-prop, $shallow: true);
305 @if is-custom-prop($value) {
306 $value: create-var($value);
307 }
308 }
309 } @else {
310 @if not is-custom-prop($custom-prop) {
311 @error "Invalid custom property: #{$custom-prop}. Must be a Map with 'varname' and 'fallback'.";
312 }
313
314 $value: get-declaration-value($custom-prop);
315 $fallback-value: get-declaration-fallback($custom-prop);
316 }
317
318 @include css.declaration(
319 $property,
320 $value,
321 $fallback-value: $fallback-value,
322 $gss: $gss,
323 $important: $important
324 );
325 }
326}