diff --git a/sass/_color-scheme.scss b/sass/_color-scheme.scss new file mode 100644 index 0000000000..28d515bc58 --- /dev/null +++ b/sass/_color-scheme.scss @@ -0,0 +1,83 @@ +@use 'sass:map'; +@use '@material/web/sass/color'; +@use '@material/web/sass/theme'; +@use '@material/web/sass/var'; +@use '@material/web/tokens'; + +// TODO(b/213571555): Following Sass mixins will call color scheme APIs which +// are generated using tokens compiler when ready. + +/// Declares set of CSS custom properties for baseline dark color scheme +/// overrides. +@mixin dark-color-scheme( + $color-values-dark: tokens.md-sys-color-values-dark-non-dynamic() +) { + @include color-scheme( + _get-color-scheme-var-fallbacks($color-values-dark), + $color-values-dark + ); +} + +/// Declares set of CSS custom properties for baseline light color scheme +/// overrides. +@mixin light-color-scheme( + $color-values-light: tokens.md-sys-color-values-light-non-dynamic() +) { + @include color-scheme( + _get-color-scheme-var-fallbacks($color-values-light), + $color-values-light + ); +} + +$_baseline-color-scheme: tokens.md-sys-color-values-light-non-dynamic(); + +/// Declares set of CSS custom properties for custom color scheme override. +/// @param {Map} $color-scheme - Map containing custom color scheme values. +/// @example +/// @include color-scheme((background: #f00)); +/// // --md-sys-color-background: #f00; +/// +/// @include color-scheme((background: var(--foo-custom-color, #f00)); +/// // --md-sys-color-background: var(--foo-custom-color, #f00) +@mixin color-scheme( + $color-scheme, + $baseline-color-scheme: $_baseline-color-scheme, + $prefix: var.create-name(sys-color) +) { + $color-scheme: theme.validate-theme($baseline-color-scheme, $color-scheme); + + @each $key, $value in $color-scheme { + #{$prefix}-#{$key}: #{$value}; + @include _declare-color-in-rgb($key, $value, $prefix); + } +} + +/// @param {Map} $color-scheme - Baseline color scheme map containing CSS vars +/// with fallback values. +/// @return {Map} - Returns a color scheme map with fallback values (concrete +/// color values) of provided CSS vars. +/// @example +/// _get-color-scheme-var-fallbacks(( +/// background: var(--md-sys-color-background, #f00), +/// ) +/// // (background: #f00) +@function _get-color-scheme-var-fallbacks($color-scheme) { + @each $key, $value in $color-scheme { + @if var.is-var($value) { + $color-scheme: map.set($color-scheme, $key, var.fallback($value)); + } + } + + @return $color-scheme; +} + +// TODO(b/213331407): Following colors are set in rgb (comma separated format) +// to enable mixing with alpha value. +// (e.g., rgb(var(--foo-sys-color-on-surface-rgb, 31,31,31), 0.38)) +// Remove once the attached bug is resolved. +@mixin _declare-color-in-rgb($color-key, $value, $prefix) { + @if var.is-var($value) { + $value: var.deep-fallback($value); + } + #{$prefix}-#{$color-key}-rgb: #{color.hex-to-rgb(#{$value})}; +} diff --git a/sass/test/_color-scheme.test.scss b/sass/test/_color-scheme.test.scss new file mode 100644 index 0000000000..0cb3631e20 --- /dev/null +++ b/sass/test/_color-scheme.test.scss @@ -0,0 +1,191 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'true' as test; +@use '../color-scheme'; + +@include test.describe('color-scheme') { + @include test.describe('dark-color-scheme()') { + // TODO(b/246011692): find a bigger way to test this without testing tokens + @include test.it('returns CSS custom property for dark theme') { + @include test.it('returns CSS custom property') { + @include test.assert { + @include test.output { + @include color-scheme.dark-color-scheme(); + } + + @include test.expect { + --md-sys-color-background: #1c1b1f; + --md-sys-color-background-rgb: 28, 27, 31; + --md-sys-color-error: #f2b8b5; + --md-sys-color-error-rgb: 242, 184, 181; + --md-sys-color-error-container: #8c1d18; + --md-sys-color-error-container-rgb: 140, 29, 24; + --md-sys-color-inverse-on-surface: #313033; + --md-sys-color-inverse-on-surface-rgb: 49, 48, 51; + --md-sys-color-inverse-primary: #6750a4; + --md-sys-color-inverse-primary-rgb: 103, 80, 164; + --md-sys-color-inverse-surface: #e6e1e5; + --md-sys-color-inverse-surface-rgb: 230, 225, 229; + --md-sys-color-on-background: #e6e1e5; + --md-sys-color-on-background-rgb: 230, 225, 229; + --md-sys-color-on-error: #601410; + --md-sys-color-on-error-rgb: 96, 20, 16; + --md-sys-color-on-error-container: #f2b8b5; + --md-sys-color-on-error-container-rgb: 242, 184, 181; + --md-sys-color-on-primary: #381e72; + --md-sys-color-on-primary-rgb: 56, 30, 114; + --md-sys-color-on-primary-container: #eaddff; + --md-sys-color-on-primary-container-rgb: 234, 221, 255; + --md-sys-color-on-secondary: #332d41; + --md-sys-color-on-secondary-rgb: 51, 45, 65; + --md-sys-color-on-secondary-container: #e8def8; + --md-sys-color-on-secondary-container-rgb: 232, 222, 248; + --md-sys-color-on-surface: #e6e1e5; + --md-sys-color-on-surface-rgb: 230, 225, 229; + --md-sys-color-on-surface-variant: #cac4d0; + --md-sys-color-on-surface-variant-rgb: 202, 196, 208; + --md-sys-color-on-tertiary: #492532; + --md-sys-color-on-tertiary-rgb: 73, 37, 50; + --md-sys-color-on-tertiary-container: #ffd8e4; + --md-sys-color-on-tertiary-container-rgb: 255, 216, 228; + --md-sys-color-outline: #938f99; + --md-sys-color-outline-rgb: 147, 143, 153; + --md-sys-color-primary: #d0bcff; + --md-sys-color-primary-rgb: 208, 188, 255; + --md-sys-color-primary-container: #4f378b; + --md-sys-color-primary-container-rgb: 79, 55, 139; + --md-sys-color-secondary: #ccc2dc; + --md-sys-color-secondary-rgb: 204, 194, 220; + --md-sys-color-secondary-container: #4a4458; + --md-sys-color-secondary-container-rgb: 74, 68, 88; + --md-sys-color-shadow: #000; + --md-sys-color-shadow-rgb: 0, 0, 0; + --md-sys-color-surface: #1c1b1f; + --md-sys-color-surface-rgb: 28, 27, 31; + --md-sys-color-surface-tint: #d1e1ff; + --md-sys-color-surface-tint-rgb: 209, 225, 255; + --md-sys-color-surface-tint-color: #d0bcff; + --md-sys-color-surface-tint-color-rgb: 208, 188, 255; + --md-sys-color-surface-variant: #49454f; + --md-sys-color-surface-variant-rgb: 73, 69, 79; + --md-sys-color-tertiary: #efb8c8; + --md-sys-color-tertiary-rgb: 239, 184, 200; + --md-sys-color-tertiary-container: #633b48; + --md-sys-color-tertiary-container-rgb: 99, 59, 72; + } + } + } + } + } + + @include test.describe('light-color-scheme()') { + // TODO(b/246011692): find a bigger way to test this without testing tokens + @include test.it('returns CSS custom propertyfor light theme') { + @include test.assert { + @include test.output { + @include color-scheme.light-color-scheme(); + } + + @include test.expect { + --md-sys-color-background: #fff; + --md-sys-color-background-rgb: 255, 255, 255; + --md-sys-color-error: #b3261e; + --md-sys-color-error-rgb: 179, 38, 30; + --md-sys-color-error-container: #f9dedc; + --md-sys-color-error-container-rgb: 249, 222, 220; + --md-sys-color-inverse-on-surface: #f4eff4; + --md-sys-color-inverse-on-surface-rgb: 244, 239, 244; + --md-sys-color-inverse-primary: #d0bcff; + --md-sys-color-inverse-primary-rgb: 208, 188, 255; + --md-sys-color-inverse-surface: #313033; + --md-sys-color-inverse-surface-rgb: 49, 48, 51; + --md-sys-color-on-background: #1c1b1f; + --md-sys-color-on-background-rgb: 28, 27, 31; + --md-sys-color-on-error: #fff; + --md-sys-color-on-error-rgb: 255, 255, 255; + --md-sys-color-on-error-container: #410e0b; + --md-sys-color-on-error-container-rgb: 65, 14, 11; + --md-sys-color-on-primary: #fff; + --md-sys-color-on-primary-rgb: 255, 255, 255; + --md-sys-color-on-primary-container: #21005d; + --md-sys-color-on-primary-container-rgb: 33, 0, 93; + --md-sys-color-on-secondary: #fff; + --md-sys-color-on-secondary-rgb: 255, 255, 255; + --md-sys-color-on-secondary-container: #1d192b; + --md-sys-color-on-secondary-container-rgb: 29, 25, 43; + --md-sys-color-on-surface: #1c1b1f; + --md-sys-color-on-surface-rgb: 28, 27, 31; + --md-sys-color-on-surface-variant: #49454f; + --md-sys-color-on-surface-variant-rgb: 73, 69, 79; + --md-sys-color-on-tertiary: #fff; + --md-sys-color-on-tertiary-rgb: 255, 255, 255; + --md-sys-color-on-tertiary-container: #31111d; + --md-sys-color-on-tertiary-container-rgb: 49, 17, 29; + --md-sys-color-outline: #79747e; + --md-sys-color-outline-rgb: 121, 116, 126; + --md-sys-color-primary: #6750a4; + --md-sys-color-primary-rgb: 103, 80, 164; + --md-sys-color-primary-container: #eaddff; + --md-sys-color-primary-container-rgb: 234, 221, 255; + --md-sys-color-secondary: #625b71; + --md-sys-color-secondary-rgb: 98, 91, 113; + --md-sys-color-secondary-container: #e8def8; + --md-sys-color-secondary-container-rgb: 232, 222, 248; + --md-sys-color-shadow: #000; + --md-sys-color-shadow-rgb: 0, 0, 0; + --md-sys-color-surface: #fff; + --md-sys-color-surface-rgb: 255, 255, 255; + --md-sys-color-surface-tint: #6991d6; + --md-sys-color-surface-tint-rgb: 105, 145, 214; + --md-sys-color-surface-tint-color: #6750a4; + --md-sys-color-surface-tint-color-rgb: 103, 80, 164; + --md-sys-color-surface-variant: #e7e0ec; + --md-sys-color-surface-variant-rgb: 231, 224, 236; + --md-sys-color-tertiary: #7d5260; + --md-sys-color-tertiary-rgb: 125, 82, 96; + --md-sys-color-tertiary-container: #ffd8e4; + --md-sys-color-tertiary-container-rgb: 255, 216, 228; + } + } + } + } + + @include test.describe('color-scheme()') { + @include test.it('returns CSS custom property') { + @include test.assert { + @include test.output { + @include color-scheme.color-scheme( + ( + background: #f00, + ) + ); + } + + @include test.expect { + --md-sys-color-background: #f00; + --md-sys-color-background-rgb: 255, 0, 0; + } + } + } + + @include test.it('returns CSS custom property with var') { + @include test.assert { + @include test.output { + @include color-scheme.color-scheme( + ( + background: var(--foo-custom-color, #f00), + ) + ); + } + + @include test.expect { + --md-sys-color-background: var(--foo-custom-color, #f00); + --md-sys-color-background-rgb: 255, 0, 0; + } + } + } + } +} diff --git a/sass/test/test.scss b/sass/test/test.scss index 95e7a5cd38..aa2790be01 100644 --- a/sass/test/test.scss +++ b/sass/test/test.scss @@ -3,7 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 // -@use './shape.test'; -@use './string-ext.test'; +@use './color-scheme.test'; @use './color.test'; @use './map-ext.test'; +@use './shape.test'; +@use './string-ext.test';