327 lines
12 KiB
SCSS
327 lines
12 KiB
SCSS
//
|
|
// Copyright 2020 Google Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
@use 'sass:list';
|
|
@use 'sass:map';
|
|
@use 'sass:meta';
|
|
@use 'sass:string';
|
|
@use './css';
|
|
@use './gss';
|
|
|
|
/// Configuration for custom properties.
|
|
/// @see {mixin} configure
|
|
$_config: (
|
|
emit-custom-properties: true,
|
|
emit-fallback-values: true,
|
|
emit-fallback-vars: true,
|
|
);
|
|
|
|
/// Configure options for custom properties. The configuration will be applied
|
|
/// within the scope of the mixin's content and reset when the mixin scope ends.
|
|
///
|
|
/// @example - scss
|
|
/// @include configure($emit-fallback-values: false) {
|
|
/// // No fallback values will be emitted within this mixin scope
|
|
/// @include another-mixin();
|
|
/// }
|
|
///
|
|
/// All parameters must be provided as argument lists.
|
|
///
|
|
/// @link https://sass-lang.com/documentation/values/lists#argument-lists
|
|
///
|
|
/// @param {Bool} $emit-custom-properties [true] - Enable or disable all
|
|
/// custom property emission.
|
|
/// @param {Bool} $emit-fallback-values [true] - Enable or disable emission of
|
|
/// fallback CSS static values. This does not include dynamic `var()`
|
|
/// fallback values.
|
|
/// @param {Bool} $emit-fallback-vars [true] - Enable or disable emission of
|
|
/// fallback `var()` values.
|
|
@mixin configure($config...) {
|
|
@if not meta.content-exists() {
|
|
@error 'content is required for configure()';
|
|
}
|
|
|
|
$config: meta.keywords($config);
|
|
@each $key, $value in $config {
|
|
@if $value == null {
|
|
$config: map.remove($config, $key);
|
|
}
|
|
}
|
|
|
|
@if list.length($config) == 0 {
|
|
@content;
|
|
} @else {
|
|
$previous: $_config;
|
|
// Use !global to avoid shadowing
|
|
// https://sass-lang.com/documentation/variables#shadowing
|
|
$_config: map.merge($_config, $config) !global;
|
|
@content;
|
|
$_config: $previous !global;
|
|
}
|
|
}
|
|
|
|
/// Returns true if the parameter is a custom property Map.
|
|
///
|
|
/// @param {*} $value - the value to test.
|
|
/// @return true if the value is a custom property Map, or false if not.
|
|
@function is-custom-prop($value) {
|
|
@return meta.type-of($value) == 'map' and map.has-key($value, 'varname');
|
|
}
|
|
|
|
/// Indicates whether or not a value is a custom property var() string.
|
|
///
|
|
/// @example - scss
|
|
/// $is-prop-string: is-custom-prop-string('var(--foo)'); // true
|
|
///
|
|
/// @param {*} $value - The value to test.
|
|
/// @return {Bool} True if the value is a custom property var() string, or
|
|
/// false if not.
|
|
@function is-custom-prop-string($value) {
|
|
@return meta.type-of($value) == 'string' and string.slice($value, 1, 4) ==
|
|
'var(';
|
|
}
|
|
|
|
/// Returns true if $prop1's varname and fallback values are deeply equal to
|
|
/// $prop2's varname and fallback values.
|
|
///
|
|
/// @param {Map} $prop1 - the first value to test.
|
|
/// @param {Map} $prop2 - the second value to test.
|
|
/// @return true if both properties are deeply equal
|
|
@function are-equal($prop1, $prop2) {
|
|
@return create-var($prop1) == create-var($prop2);
|
|
}
|
|
|
|
/// Creates a custom property Map.
|
|
///
|
|
/// @param {String} $varname - the custom property name.
|
|
/// @param {String | Number | Map} - the fallback value (may be another custom
|
|
/// property Map). May be null.
|
|
/// @return a custom property Map.
|
|
@function create($varname, $fallback: null) {
|
|
@if string.slice($varname, 1, 2) != '--' {
|
|
$varname: create-varname($varname);
|
|
}
|
|
|
|
@return (varname: $varname, fallback: $fallback);
|
|
}
|
|
|
|
/// Global prefix for all custom properties.
|
|
$_varname-prefix: 'mdc';
|
|
|
|
/// Create a custom property variable name with the global prefix.
|
|
///
|
|
/// @example - scss
|
|
/// $varname: create-varname(foo); // --mdc-foo
|
|
///
|
|
/// @param {String} $name - The name of the custom property.
|
|
/// @return {String} The full CSS custom property variable name.
|
|
@function create-varname($name) {
|
|
@return --#{$_varname-prefix}-#{$name};
|
|
}
|
|
|
|
/// Returns the custom property variable name of a custom property Map.
|
|
///
|
|
/// @param {Map} $custom-prop - a custom property Map.
|
|
/// @return the custom property variable name defined by the Map.
|
|
@function get-varname($custom-prop) {
|
|
@return map.get($custom-prop, 'varname');
|
|
}
|
|
|
|
/// Returns the fallback value of a custom property Map. May be null if the
|
|
/// custom property does not have a fallback.
|
|
///
|
|
/// @param {Map} $custom-prop - a custom property Map.
|
|
/// @param {Bool} $shallow - if true, return the first fallback value, which
|
|
/// may be another custom property Map. Defaults to false, which will return
|
|
/// the deep final fallback value.
|
|
/// @return the fallback value of a custom property Map. May be null.
|
|
@function get-fallback($custom-prop, $shallow: false) {
|
|
$fallback: map.get($custom-prop, 'fallback');
|
|
@if is-custom-prop($fallback) and not $shallow {
|
|
@return get-fallback($fallback);
|
|
}
|
|
|
|
@return $fallback;
|
|
}
|
|
|
|
/// Creates a new custom property Map and returns it with the specified new
|
|
/// fallback value.
|
|
///
|
|
/// @param {Map} $custom-prop - the custom property Map to copy.
|
|
/// @param {String | Number | Map} $new-fallback - the new fallback value of the
|
|
/// custom property Map. May be null.
|
|
/// @param {Bool} $shallow - if true, set the first fallback value. Defaults to
|
|
/// false, which will set the deep final fallback value.
|
|
/// @return a new custom property Map with the new fallback value.
|
|
@function set-fallback($custom-prop, $new-fallback, $shallow: false) {
|
|
$varname: get-varname($custom-prop);
|
|
$first-fallback: get-fallback($custom-prop, $shallow: true);
|
|
|
|
@if is-custom-prop($first-fallback) and not $shallow {
|
|
// The first fallback is a custom property and $shallow is false. Deeply
|
|
// set the fallback value of the custom property and get the new custom
|
|
// property Map returned.
|
|
$new-fallback: set-fallback($first-fallback, $new-fallback);
|
|
}
|
|
|
|
@return create($varname, $new-fallback);
|
|
}
|
|
|
|
/// Creates and returns a CSS `var()` function value represented by the provided
|
|
/// custom property Map.
|
|
///
|
|
/// If custom properties are disabled, this function will return the custom
|
|
/// property's fallback value instead, which may be `null` and result in an
|
|
/// empty declaration.
|
|
///
|
|
/// @param {Map} $custom-prop - A custom property Map.
|
|
/// @return {*} A CSS value, typically a `var()` function, representing the
|
|
/// custom property. The returned value may change depending on the current
|
|
/// configuration options for custom properties and whether or not a
|
|
/// fallback value is present in the custom property Map.
|
|
@function create-var($custom-prop) {
|
|
@if not map.get($_config, emit-custom-properties) {
|
|
// If configured not to emit custom properties and a request is made for a
|
|
// custom prop's CSS value, return its fallback value. If this is null, it
|
|
// will result in an empty declaration.
|
|
@return get-fallback($custom-prop);
|
|
}
|
|
|
|
$varname: get-varname($custom-prop);
|
|
$fallback: get-fallback($custom-prop, $shallow: true);
|
|
|
|
$emit-fallback-vars: map.get($_config, emit-fallback-vars);
|
|
$fallback-is-prop: is-custom-prop($fallback);
|
|
@if $fallback-is-prop and $emit-fallback-vars {
|
|
@return var($varname, create-var($fallback));
|
|
}
|
|
|
|
$emit-fallback-values: map.get($_config, emit-fallback-values);
|
|
@if $fallback and not $fallback-is-prop and $emit-fallback-values {
|
|
@return var($varname, $fallback);
|
|
}
|
|
|
|
@return var($varname);
|
|
}
|
|
|
|
/// Retrieves the CSS declaration value for a custom property Map. This is
|
|
/// typically a `var()` function.
|
|
///
|
|
/// If custom properties are disabled, the custom property's fallback value
|
|
/// will be returned instead. If the fallback value is `null`, an error will
|
|
/// be thrown.
|
|
///
|
|
/// @param {Map} $custom-prop - The custom property Map to retrieve a
|
|
/// declaration value for.
|
|
/// @return {*} The CSS declaration value.
|
|
@function get-declaration-value($custom-prop) {
|
|
$emit-custom-properties: map.get($_config, emit-custom-properties);
|
|
$fallback: get-fallback($custom-prop);
|
|
@if not $emit-custom-properties and not $fallback {
|
|
@error 'Custom properties are disabled and #{get-varname($custom-prop)} does not have a fallback value.';
|
|
}
|
|
|
|
@if not $emit-custom-properties {
|
|
@return $fallback;
|
|
}
|
|
|
|
@return create-var($custom-prop);
|
|
}
|
|
|
|
/// Retrieves the CSS fallback declaration value for a custom property Map.
|
|
/// This is typically a static CSS value if a custom property has a fallback, or
|
|
/// null if it does not.
|
|
///
|
|
/// This function will always return `null` if custom properties or fallback
|
|
/// values are disabled.
|
|
///
|
|
/// @param {Map} $custom-prop - The custom property Map to retrieve a fallback
|
|
/// declaration value for.
|
|
/// @return {String | null} The CSS fallback declaration value.
|
|
@function get-declaration-fallback($custom-prop) {
|
|
$emit-custom-properties: map.get($_config, emit-custom-properties);
|
|
$emit-fallback-values: map.get($_config, emit-fallback-values);
|
|
@if not $emit-custom-properties or not $emit-fallback-values {
|
|
@return null;
|
|
}
|
|
|
|
@return get-fallback($custom-prop);
|
|
}
|
|
|
|
/// Emits a CSS declaration for a custom property. A custom property may either
|
|
/// be provided as the value to a CSS property string to be emitted as a var()
|
|
/// function, or as a standalone value to be emitted as a custom property
|
|
/// declaration.
|
|
///
|
|
/// @example - scss
|
|
/// @include declaration(color, create(--foo, teal));
|
|
/// // color: var(--foo, teal);
|
|
/// @include declaration(create(--foo, teal));
|
|
/// // --foo: teal;
|
|
///
|
|
/// Standalone custom properties must have a fallback value to emit as a CSS
|
|
/// declaration.
|
|
///
|
|
/// The declaration emitted for a custom property may change or be ignored
|
|
/// based on the current configuration for custom properties.
|
|
///
|
|
/// @see {mixin} css.declaration
|
|
/// @see {mixin} configuration
|
|
///
|
|
/// @param {String} $property - The CSS property of the declaration or the
|
|
/// custom property Map to emit.
|
|
/// @param {Map} $custom-prop - A custom property Map for the property's value.
|
|
/// Optional if $property is the custom property Map to emit.
|
|
/// @param {Map} $gss - An optional Map of GSS annotations to add.
|
|
/// @param {Bool} $important - If true, add `!important` to the declaration.
|
|
@mixin declaration($property, $custom-prop: null, $gss: (), $important: false) {
|
|
@if $property {
|
|
$value: null;
|
|
$fallback-value: null;
|
|
@if is-custom-prop($property) {
|
|
@if map.get($_config, emit-custom-properties) {
|
|
$custom-prop: $property;
|
|
$property: get-varname($custom-prop);
|
|
$value: get-fallback($custom-prop, $shallow: true);
|
|
@if is-custom-prop($value) {
|
|
$value: create-var($value);
|
|
}
|
|
}
|
|
} @else {
|
|
@if not is-custom-prop($custom-prop) {
|
|
@error "Invalid custom property: #{$custom-prop}. Must be a Map with 'varname' and 'fallback'.";
|
|
}
|
|
|
|
$value: get-declaration-value($custom-prop);
|
|
$fallback-value: get-declaration-fallback($custom-prop);
|
|
}
|
|
|
|
@include css.declaration(
|
|
$property,
|
|
$value,
|
|
$fallback-value: $fallback-value,
|
|
$gss: $gss,
|
|
$important: $important
|
|
);
|
|
}
|
|
}
|