UNPKG

12.6 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:color';
24@use 'sass:list';
25@use 'sass:map';
26@use 'sass:math';
27@use 'sass:meta';
28@use 'sass:string';
29@use './custom-properties';
30@use './keys';
31
32@function _linear-channel-value($channel-value) {
33 $normalized-channel-value: math.div($channel-value, 255);
34 @if $normalized-channel-value < 0.03928 {
35 @return math.div($normalized-channel-value, 12.92);
36 }
37
38 @return math.pow(math.div($normalized-channel-value + 0.055, 1.055), 2.4);
39}
40
41// Calculate the luminance for a color.
42// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
43@function luminance($color) {
44 $red: _linear-channel-value(color.red($color));
45 $green: _linear-channel-value(color.green($color));
46 $blue: _linear-channel-value(color.blue($color));
47
48 @return 0.2126 * $red + 0.7152 * $green + 0.0722 * $blue;
49}
50
51// Calculate the contrast ratio between two colors.
52// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
53@function contrast($back, $front) {
54 $backLum: luminance($back) + 0.05;
55 $foreLum: luminance($front) + 0.05;
56
57 @return math.div(math.max($backLum, $foreLum), math.min($backLum, $foreLum));
58}
59
60// Determine whether the color is 'light' or 'dark'.
61@function tone($color) {
62 @if $color == 'dark' or $color == 'light' {
63 @return $color;
64 }
65
66 @if meta.type-of($color) != 'color' {
67 @warn '#{$color} is not a color. Falling back to "dark" tone.';
68 @return 'dark';
69 }
70
71 $minimumContrast: 3.1;
72
73 $lightContrast: contrast($color, white);
74 $darkContrast: contrast($color, rgba(black, 0.87));
75
76 @if ($lightContrast < $minimumContrast) and ($darkContrast > $lightContrast) {
77 @return 'light';
78 } @else {
79 @return 'dark';
80 }
81}
82
83// Determine whether to use dark or light text on top of given color to meet accessibility standards for contrast.
84// Returns 'dark' if the given color is light and 'light' if the given color is dark.
85@function contrast-tone($color) {
86 @return if(tone($color) == 'dark', 'light', 'dark');
87}
88
89///
90/// @param $color Target color in any color format.
91/// @return Returns hash in string format that uniquely represents
92/// any given color format. Useful for generating unique keyframe names.
93/// @example
94/// `color-hash(#6200ee)` => "6200ee"
95/// `color-hash(rgb(255, 112, 112))` => "ff7070"
96/// `color-hash((varname: --my-fancy-color, fallback: teal))` => 'teal'
97/// `color-hash((varname: --my-fancy-color, fallback: null))` => '--my-fancy-color'
98///
99@function color-hash($color) {
100 @if custom-properties.is-custom-prop($color) {
101 $color-value: custom-properties.get-fallback($color);
102
103 @if (custom-properties.is-custom-prop-string($color-value)) {
104 $varEndIndex: if(
105 string.index($color-value, ', '),
106 string.index($color-value, ', ') - 1,
107 -2
108 );
109 @return string.slice($color-value, 5, $varEndIndex);
110 }
111
112 @if (meta.type-of($color-value) == 'color') {
113 @return _get-hex-string($color-value);
114 }
115
116 @return custom-properties.get-varname($color);
117 }
118
119 @if meta.type-of($color) == 'string' {
120 @return $color;
121 }
122
123 @return _get-hex-string($color);
124}
125
126@function _get-hex-string($color) {
127 @return string.slice(color.ie-hex-str($color), 2); // Index starts at 1
128}
129
130//
131// Main theme colors for your brand.
132//
133// If you're a user customizing your color scheme in SASS, these are probably the only variables you need to change.
134//
135
136$primary: #6200ee !default; // baseline purple, 500 tone
137$on-primary: if(contrast-tone($primary) == 'dark', #000, #fff) !default;
138
139// The $mdc-theme-accent variable is DEPRECATED - it exists purely for backward compatibility.
140// The $mdc-theme-secondary* variables should be used for all new projects.
141/// @deprecated - use $secondary
142$accent: #018786 !default; // baseline teal, 600 tone
143$secondary: $accent !default;
144$on-secondary: if(contrast-tone($secondary) == 'dark', #000, #fff) !default;
145$background: #fff !default; // White
146
147$surface: #fff !default;
148$on-surface: if(contrast-tone($surface) == 'dark', #000, #fff) !default;
149
150$error: #b00020 !default;
151$on-error: if(contrast-tone($error) == 'dark', #000, #fff) !default;
152
153//
154// Text colors according to light vs dark and text type.
155//
156
157$text-colors: (
158 dark: (
159 primary: rgba(black, 0.87),
160 secondary: rgba(black, 0.54),
161 hint: rgba(black, 0.38),
162 disabled: rgba(black, 0.38),
163 icon: rgba(black, 0.38),
164 ),
165 light: (
166 primary: white,
167 secondary: rgba(white, 0.7),
168 hint: rgba(white, 0.5),
169 disabled: rgba(white, 0.5),
170 icon: rgba(white, 0.5),
171 ),
172) !default;
173
174$text-emphasis: (
175 high: 0.87,
176 medium: 0.6,
177 disabled: 0.38,
178) !default;
179
180@function ink-color-for-fill_($text-style, $fill-color) {
181 $contrast-tone: contrast-tone($fill-color);
182
183 @return map.get(map.get($text-colors, $contrast-tone), $text-style);
184}
185
186//
187// Primary text colors for each of the theme colors.
188//
189
190/// @deprecated Use individual variables (`$primary`, `$secondary`). Do not
191/// override this Map of variables.
192$property-values: (
193 primary: $primary,
194 secondary: $secondary,
195 background: $background,
196 surface: $surface,
197 error: $error,
198 on-primary: $on-primary,
199 on-secondary: $on-secondary,
200 on-surface: $on-surface,
201 on-error: $on-error,
202 text-primary-on-background: ink-color-for-fill_(primary, $background),
203 text-secondary-on-background: ink-color-for-fill_(secondary, $background),
204 text-hint-on-background: ink-color-for-fill_(hint, $background),
205 text-disabled-on-background: ink-color-for-fill_(disabled, $background),
206 text-icon-on-background: ink-color-for-fill_(icon, $background),
207 text-primary-on-light: ink-color-for-fill_(primary, light),
208 text-secondary-on-light: ink-color-for-fill_(secondary, light),
209 text-hint-on-light: ink-color-for-fill_(hint, light),
210 text-disabled-on-light: ink-color-for-fill_(disabled, light),
211 text-icon-on-light: ink-color-for-fill_(icon, light),
212 text-primary-on-dark: ink-color-for-fill_(primary, dark),
213 text-secondary-on-dark: ink-color-for-fill_(secondary, dark),
214 text-hint-on-dark: ink-color-for-fill_(hint, dark),
215 text-disabled-on-dark: ink-color-for-fill_(disabled, dark),
216 text-icon-on-dark: ink-color-for-fill_(icon, dark),
217) !default;
218
219@include keys.set-values(
220 $property-values,
221 $options: (custom-property-prefix: theme)
222);
223
224// A copy of the property values Map that is used to detect compile-time changes
225// for Angular support.
226$_property-values-copy: $property-values;
227
228/// Checks if the global $mdc-theme-property-values was dynamically changed at
229/// compile time. Typically, $property-values is configured once, but a Sass
230/// hack allows the variable to be changed multiple times and effectively
231/// support dynamic values.
232///
233/// Angular uses this in their dynamic theming. This function checks if this
234/// scenario has occurred and returns the current global value that should be
235/// used instead of the key store value.
236///
237/// @deprecated The function should not be used externally. It will be removed
238/// when $mdc-theme-property-values is fully deprecated and removed.
239@function deprecated-get-global-theme-key-value-if-changed($key) {
240 // Determine if we need to use a compile-time updated value to support
241 // Angular.
242 $current-global-value: map.get($property-values, $key);
243 $configured-global-value: map.get($_property-values-copy, $key);
244 @if $current-global-value != $configured-global-value {
245 // $mdc-theme-property-values was changed at compile time. Return the new
246 // compile-time value.
247 @return (value: $current-global-value, changed: true);
248 }
249
250 @return (changed: false);
251}
252
253// @deprecated use theme.property(). If you need to ensure the value is not a
254// custom property, use custom-properties.is-custom-prop() to check if the value
255// is a custom prop, then custom-properties.get-fallback() to get its value.
256// If `$style` is a color (a literal color value, `currentColor`, or a CSS custom property), it is returned verbatim.
257// Otherwise, `$style` is treated as a theme property name, and the corresponding value from
258// `$mdc-theme-property-values` is returned. If this also fails, an error is thrown.
259//
260// This is mainly useful in situations where `mdc-theme-prop` cannot be used directly (e.g., `box-shadow`).
261//
262// Examples:
263//
264// 1. mdc-theme-prop-value(primary) => "#6200ee"
265// 2. mdc-theme-prop-value(blue) => 'blue'
266//
267// NOTE: This function must be defined in _variables.scss instead of _functions.scss to avoid circular imports.
268@function prop-value($style) {
269 @if custom-properties.is-custom-prop($style) {
270 @return custom-properties.get-fallback($style);
271 }
272
273 @if is-valid-theme-prop-value_($style) {
274 @return $style;
275 }
276
277 @if is-theme-key($style) {
278 // Determine if we need to use a compile-time updated value to support
279 // Angular.
280 $result: deprecated-get-global-theme-key-value-if-changed($style);
281 @if map.get($result, changed) {
282 @return map.get($result, value);
283 }
284 }
285
286 @return keys.resolve($style);
287}
288
289// NOTE: This function must be defined in _variables.scss instead of _functions.scss to avoid circular imports.
290@function accessible-ink-color($fill-color, $text-style: primary) {
291 $fill-color-value: prop-value($fill-color);
292 $color-map-for-tone: map.get($text-colors, contrast-tone($fill-color-value));
293
294 @if not map.has-key($color-map-for-tone, $text-style) {
295 @error "Invalid $text-style: '#{$text-style}'. Choose one of: #{map.keys($color-map-for-tone)}";
296 }
297
298 @return map.get($color-map-for-tone, $text-style);
299}
300
301// NOTE: This function is depended upon by mdc-theme-prop-value (above) and thus must be defined in this file.
302@function is-valid-theme-prop-value_($style) {
303 @return meta.type-of($style) == 'color' or $style == 'currentColor' or
304 str_slice($style, 1, 4) == 'var(' or $style == 'inherit' or $style ==
305 'transparent' or
306 // NOTE: `GrayText` is deprecated, but is the only feasible way to convey the
307 // correct high-contrast mode colors in alignment with Windows system colors.
308 $style == 'GrayText';
309}
310
311@function text-emphasis($emphasis) {
312 @return map.get($text-emphasis, $emphasis);
313}
314
315@function is-theme-key($style) {
316 @return map.has-key($property-values, $style);
317}
318
319@function get-theme-keys() {
320 @return map.keys($property-values);
321}
322
323///
324/// @param {Color|String} Color property key name (i.e., `primary`, `secondary`,
325/// etc).
326/// @return Returns custom property map containing CSS custom property and
327/// fallback value (i.e., (varname: ..., fallback: ...). Returns color if
328/// valid color value is provided. Throws error otherwise.
329/// @examples
330/// 1. get-custom-property(primary)
331/// => (varname: --mdc-theme-primary, fallback: #6200ee)
332///
333/// 2. get-custom-property(#fff)
334/// => #fff
335///
336@function get-custom-property($color) {
337 $is-tokens-custom-prop: meta.type-of($color) == 'string' and
338 string.index($color, '--') != null;
339 @if custom-properties.is-custom-prop($color) or
340 $is-tokens-custom-prop or
341 is-valid-theme-prop-value_($color)
342 {
343 @return $color;
344 } @else if is-theme-key($color) {
345 $custom-prop: keys.create-custom-property($color);
346
347 // Determine if we need to use a compile-time updated value to support
348 // Angular.
349 $result: deprecated-get-global-theme-key-value-if-changed($color);
350 @if map.get($result, changed) {
351 $custom-prop: custom-properties.set-fallback(
352 $custom-prop,
353 map.get($result, value)
354 );
355 }
356
357 @return $custom-prop;
358 } @else {
359 @error "Invalid theme property: '#{$color}'. Choose one of: #{get-theme-keys()}";
360 }
361}