From aeb80bb3be8984d6d7c7379a7ddddb681a8b5095 Mon Sep 17 00:00:00 2001 From: didimmova Date: Mon, 17 Feb 2025 09:24:36 +0200 Subject: [PATCH 01/15] feat(palette): update text-contrast and color functions --- sass/color/_functions.scss | 54 ++++++++++---------------------------- test/_color.spec.scss | 53 ++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 68 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 70b5f121..4dabf726 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -106,7 +106,7 @@ $_enhanced-accessibility: false; $variant: map.get($shade, 'hsl'), '#{$variant}-contrast': text-contrast( - $background: map.get($shade, 'raw'), + $background: #{var(--ig-#{$name}-#{$variant})}, $contrast: 'AA', ), '#{$variant}-raw': map.get($shade, 'raw'), @@ -195,9 +195,14 @@ $_enhanced-accessibility: false; @if $palette { $s: map.get($palette, #{$color}); $base: map.get($s, #{$variant}); - $raw: if($contrast, map.get($s, #{$variant}-contrast), map.get($s, #{$variant}-raw)); + $raw: map.get($s, #{$variant}-raw); + $var: var(--ig-#{$color}-#{$variant}); - @return if($raw and $variant != '500', rgba($raw, $_alpha), rgba($base, $_alpha)); + @return if( + $contrast, + if($opacity, $_mix-alpha, $var), + if($raw and $variant != '500', rgba($raw, $_alpha), $base) + ); } @return if($contrast, $_mix-alpha, $_hsl-alpha); @@ -237,48 +242,17 @@ $_enhanced-accessibility: false; /// background: #09f /// color: text-contrast(#09f) /// } -@function text-contrast($background, $foreground: white, $contrast: 'AAA') { - @if meta.type-of($foreground) != 'list' and meta.type-of($background) != 'color' { - @return $background; - } - - $level: map.get( - ( - 'a': 3, - 'aa': 4.5, - 'aaa': 7, - ), - string.to-lower-case($contrast) - ); +@function text-contrast($background, $contrast: 'AAA') { + $contrast: string.to-lower-case($contrast); - @if not($level) { + @if not($contrast == 'a' or $contrast == 'aa' or $contrast == 'aaa') { @error '$contrast must be \'A\', \'AA\', or \'AAA\''; } - $candidates: (); + $level: var(--ig-contrast-#{string.to-lower-case($contrast)}); + $l: clamp(0, (l / $level - 1) * -infinity, 1); - @each $color in $foreground { - @if meta.type-of($color) != 'color' { - @return $background; - } - - $candidates: list.append($candidates, contrast($background, $color)); - } - - $foreground: list.nth($foreground, list.index($candidates, math.max($candidates...))); - - @if contrast($background, $foreground) >= $level { - @return $foreground; - } @else { - $lightContrast: contrast($background, white); - $darkContrast: contrast($background, black); - - @if $lightContrast > $darkContrast { - @return white; - } @else { - @return black; - } - } + @return #{oklch(from $background $l 0 h)}; } /// Mixes two colors to produce an opaque color. diff --git a/test/_color.spec.scss b/test/_color.spec.scss index 9eb8d4bc..1b6a9ea9 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -57,46 +57,44 @@ $_palette: palette( } @include describe('contrast') { - @include it('should return the passed background value if no valid colors are provided') { - $value: 'not a color'; - - @include assert-equal(text-contrast($value), $value); - } - - @include it('should return black for a white background') { + @include it('should return contrast value of a hex color') { $background: #fff; - @include assert-equal(text-contrast($background), #000); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); } - @include it('should return white for a black background') { - $background: #000; + @include it('should return contrast value of an hsl color') { + $background: hsl(0deg 0% 0%); - @include assert-equal(text-contrast($background), #fff); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); } - @include it('should return a contrasting color for a given background regardless of level case') { - $test-color: #000; + @include it('should return a contrast color of a relative syntax color') { + $background: oklch(from #09f l c h); - @include assert-equal(text-contrast($_primary, $test-color, 'AAA'), $test-color); - @include assert-equal(text-contrast($_primary, $test-color, 'aaa'), $test-color); - @include assert-equal(text-contrast($_primary, $test-color, 'Aaa'), $test-color); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); } - @include it('returns the best contrast color from a list of candidates') { - @include assert-equal(text-contrast($_primary, #fff #222 #333, 'AA'), #222); + @include it('should return a contrast color of a CSS variable reference'){ + $background: var(--ig-primary-500); + + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); } - @include it('should return an AAA contrasting color for a given background') { - $test-color: #000; + @include it('should return a contrasting color for a given background regardless of level case') { + $test-color: oklch(from #09f clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h); - @include assert-equal(text-contrast($_primary, $test-color, 'AAA'), $test-color); + @include assert-equal(text-contrast($_primary, $contrast: 'AAA'), $test-color); + @include assert-equal(text-contrast($_primary, $contrast: 'aaa'), $test-color); + @include assert-equal(text-contrast($_primary, $contrast: 'Aaa'), $test-color); } @include it('should return an AA contrasting color for a given background') { - $test-color: #222; + @include assert-equal(text-contrast($_primary, $contrast: 'AA'), oklch(from #09f clamp(0, (l / var(--ig-contrast-aa) - 1) * -infinity, 1) 0 h)); + } - @include assert-equal(text-contrast($_primary, $test-color, 'AA'), $test-color); + @include it('should return an A contrasting color for a given background') { + @include assert-equal(text-contrast($_primary, $contrast: 'A'), oklch(from #09f clamp(0, (l / var(--ig-contrast-a) - 1) * -infinity, 1) 0 h)); } } @@ -142,11 +140,10 @@ $_palette: palette( @include assert-equal($value, hsl(from (var(--ig-secondary-A400)) h s l / 1)); } - @include it('should return a contrast shade of type color w/ palette as only argument') { + @include it('should return a contrast shade of type string w/ palette as only argument') { $value: contrast-color($_palette, $opacity: .5); - $expected: rgba(0 0 0 / .5); + $expected: color-mix(in oklch, var(--ig-primary-500-contrast) 50%, transparent); - @include assert-equal(type-of($value), color); @include assert-equal($expected, $value); } @@ -163,9 +160,9 @@ $_palette: palette( @include assert-true(color($_palette, 'primary', '500')); @include assert-equal(color($_palette, 'primary', '500'), $_primary); @include assert-true(contrast-color($_palette, primary, 500)); - @include assert-equal(contrast-color($_palette, primary, 500), black); + @include assert-equal(contrast-color($_palette, primary, 500), var(--ig-primary-500-contrast)); @include assert-true(contrast-color($_palette, 'primary', '500')); - @include assert-equal(contrast-color($_palette, 'primary', '500'), black); + @include assert-equal(contrast-color($_palette, 'primary', '500'), var(--ig-primary-500-contrast)); } @include it('should generate an HSL color shade from a given base color') { From 40fe6885b3074bce60bcc4f48b55377248b54c95 Mon Sep 17 00:00:00 2001 From: didimmova Date: Mon, 17 Feb 2025 17:45:53 +0200 Subject: [PATCH 02/15] feat(color): update text-contrast function and palette mixin, remove contrast function --- sass/color/_functions.scss | 37 ++++++++----------------------------- sass/color/_mixins.scss | 4 +++- test/_color.spec.scss | 12 ++++-------- 3 files changed, 15 insertions(+), 38 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 4dabf726..6505e6e8 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -107,7 +107,6 @@ $_enhanced-accessibility: false; '#{$variant}-contrast': text-contrast( $background: #{var(--ig-#{$name}-#{$variant})}, - $contrast: 'AA', ), '#{$variant}-raw': map.get($shade, 'raw'), ) @@ -229,27 +228,25 @@ $_enhanced-accessibility: false; /// Returns a contrast color for a passed color. /// @access public /// @group Color -/// @param {Color} $background - The background color used to return a contrast/forground color for. -/// @param {Color | List} $foreground [#fff] - A list of foreground colors -/// that can be used with the provided background. -/// @param {AAA | AA | A} $contrast [AAA] - The contrast level according to WCAG 2.0 standards. +/// @param {Color} $background - The background color used to return a contrast/foreground color for. +/// @param {default| AAA | AA | A} $contrast [default] - The contrast level according to WCAG 2.0 standards. /// @require {function} contrast -/// @returns {Color} - Returns either white, black, or the provided foreground -/// color if it meets the required contrast level. +/// @returns {string} - Returns a relative syntax OKLCH color where the luminance is adjusted +/// based on the specified contrast level, resulting in either black or white. /// @link https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html /// @example scss /// .my-component { /// background: #09f /// color: text-contrast(#09f) /// } -@function text-contrast($background, $contrast: 'AAA') { +@function text-contrast($background, $contrast: 'default') { $contrast: string.to-lower-case($contrast); - @if not($contrast == 'a' or $contrast == 'aa' or $contrast == 'aaa') { - @error '$contrast must be \'A\', \'AA\', or \'AAA\''; + @if not($contrast == 'default' or $contrast == 'a' or $contrast == 'aa' or $contrast == 'aaa') { + @error '$contrast must be \'default\',\'A\', \'AA\', or \'AAA\''; } - $level: var(--ig-contrast-#{string.to-lower-case($contrast)}); + $level: if($contrast == 'default', var(--ig-contrast-level), var(--ig-contrast-#{string.to-lower-case($contrast)})); $l: clamp(0, (l / $level - 1) * -infinity, 1); @return #{oklch(from $background $l 0 h)}; @@ -288,24 +285,6 @@ $_enhanced-accessibility: false; @return (math.round(color.hue($color)), math.round(color.saturation($color)), math.round(color.lightness($color))); } -/// Calculates the contrast ratio between two colors. -/// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests -/// @access public -/// @group Color -/// @param {Color} $background - The background color. -/// @param {Color} $foreground - The foreground color. -/// @require {function} luminance -/// @require {function} to-fixed -/// @example scss -/// contrast(#09f, #000); -/// @returns {Number} - The contrast ratio between the background and foreground colors. -@function contrast($background, $foreground) { - $backLum: luminance($background) + 0.05; - $foreLum: luminance($foreground) + 0.05; - - @return to-fixed(math.div(math.max($backLum, $foreLum), math.min($backLum, $foreLum))); -} - /// Calculates the luminance for a given color. /// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests. /// @access public diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index e03c44ae..b3a9efc3 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -38,10 +38,12 @@ $_added: () !default; /// $palette: palette($primary: red, $secondary: blue, $gray: #000); /// @include palette($palette); /// @require {function} is-root -@mixin palette($palette, $contrast: true) { +@mixin palette($palette, $contrast: true, $contrast-level: 'aa') { $scope: if(is-root(), ':root', '&'); #{$scope} { + --ig-contrast-level: var(--ig-contrast-#{$contrast-level}); + @each $color, $shades in map.remove($palette, '_meta') { @each $shade, $value in $shades { @if not(string.index(to-string($shade), raw)) { diff --git a/test/_color.spec.scss b/test/_color.spec.scss index 1b6a9ea9..bd76ecc1 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -30,10 +30,6 @@ $_palette: palette( @include describe('Color') { @include describe('base') { - @include it('should calculate the contrast ratio between two colors') { - @include assert-equal(contrast($_primary, $_secondary), 1.19); - } - @include it('should mix two colors to produce an opaque color') { @include assert-equal(to-opaque(rgba(255, 255, 255, .32), #fff), #fff); @include assert-equal(to-opaque(rgba(233, 233, 233, .32), rgba(255, 255, 255, 0)), #f7f7f7); @@ -60,25 +56,25 @@ $_palette: palette( @include it('should return contrast value of a hex color') { $background: #fff; - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); } @include it('should return contrast value of an hsl color') { $background: hsl(0deg 0% 0%); - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); } @include it('should return a contrast color of a relative syntax color') { $background: oklch(from #09f l c h); - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); } @include it('should return a contrast color of a CSS variable reference'){ $background: var(--ig-primary-500); - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h)); + @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); } @include it('should return a contrasting color for a given background regardless of level case') { From b2b7e1bb614c3d97e90b609a4a1102f91f35158e Mon Sep 17 00:00:00 2001 From: didimmova Date: Tue, 18 Feb 2025 15:18:01 +0200 Subject: [PATCH 03/15] feat(palette): revert text-contrast function and add accessible-colors one --- sass/color/_functions.scss | 88 +++++++++++++++++++++++++++++++++----- sass/color/_mixins.scss | 1 + test/_color.spec.scss | 62 ++++++++++++++++++--------- 3 files changed, 119 insertions(+), 32 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 6505e6e8..118a8790 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -105,7 +105,7 @@ $_enhanced-accessibility: false; ( $variant: map.get($shade, 'hsl'), '#{$variant}-contrast': - text-contrast( + accessible-colors( $background: #{var(--ig-#{$name}-#{$variant})}, ), '#{$variant}-raw': map.get($shade, 'raw'), @@ -225,31 +225,97 @@ $_enhanced-accessibility: false; @return color($palette, $color, #{$variant}-contrast, $opacity); } -/// Returns a contrast color for a passed color. +/// Returns black or white color for a passed color. /// @access public /// @group Color /// @param {Color} $background - The background color used to return a contrast/foreground color for. -/// @param {default| AAA | AA | A} $contrast [default] - The contrast level according to WCAG 2.0 standards. -/// @require {function} contrast /// @returns {string} - Returns a relative syntax OKLCH color where the luminance is adjusted /// based on the specified contrast level, resulting in either black or white. +/// @example scss +/// .my-component { +/// background: #09f +/// color: accessible-colors(#09f) +/// } +@function accessible-colors($background) { + @return #{oklch(from $background var(--l) 0 h)}; +} + +/// Returns a contrast color for a passed color. +/// @access public +/// @group Color +/// @param {Color} $background - The background color used to return a contrast/forground color for. +/// @param {Color | List} $foreground [#fff] - A list of foreground colors +/// that can be used with the provided background. +/// @param {AAA | AA | A} $contrast [AAA] - The contrast level according to WCAG 2.0 standards. +/// @require {function} contrast +/// @returns {Color} - Returns either white, black, or the provided foreground +/// color if it meets the required contrast level. /// @link https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html /// @example scss /// .my-component { /// background: #09f /// color: text-contrast(#09f) /// } -@function text-contrast($background, $contrast: 'default') { - $contrast: string.to-lower-case($contrast); +@function text-contrast($background, $foreground: white, $contrast: 'AAA') { + @if meta.type-of($foreground) != 'list' and meta.type-of($background) != 'color' { + @return $background; + } + + $level: map.get( + ( + 'a': 3, + 'aa': 4.5, + 'aaa': 7, + ), + string.to-lower-case($contrast) + ); + + @if not($level) { + @error '$contrast must be \'A\', \'AA\', or \'AAA\''; + } + + $candidates: (); + + @each $color in $foreground { + @if meta.type-of($color) != 'color' { + @return $background; + } + + $candidates: list.append($candidates, contrast($background, $color)); + } + + $foreground: list.nth($foreground, list.index($candidates, math.max($candidates...))); + + @if contrast($background, $foreground) >= $level { + @return $foreground; + } @else { + $lightContrast: contrast($background, white); + $darkContrast: contrast($background, black); - @if not($contrast == 'default' or $contrast == 'a' or $contrast == 'aa' or $contrast == 'aaa') { - @error '$contrast must be \'default\',\'A\', \'AA\', or \'AAA\''; + @if $lightContrast > $darkContrast { + @return white; + } @else { + @return black; + } } +} - $level: if($contrast == 'default', var(--ig-contrast-level), var(--ig-contrast-#{string.to-lower-case($contrast)})); - $l: clamp(0, (l / $level - 1) * -infinity, 1); +/// Calculates the contrast ratio between two colors. +/// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests +/// @access public +/// @group Color +/// @param {Color} $background - The background color. +/// @param {Color} $foreground - The foreground color. +/// @require {function} luminance +/// @require {function} to-fixed +/// @example scss +/// contrast(#09f, #000); +/// @returns {Number} - The contrast ratio between the background and foreground colors. +@function contrast($background, $foreground) { + $backLum: luminance($background) + 0.05; + $foreLum: luminance($foreground) + 0.05; - @return #{oklch(from $background $l 0 h)}; + @return to-fixed(math.div(math.max($backLum, $foreLum), math.min($backLum, $foreLum))); } /// Mixes two colors to produce an opaque color. diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index b3a9efc3..c261bd24 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -43,6 +43,7 @@ $_added: () !default; #{$scope} { --ig-contrast-level: var(--ig-contrast-#{$contrast-level}); + --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); @each $color, $shades in map.remove($palette, '_meta') { @each $shade, $value in $shades { diff --git a/test/_color.spec.scss b/test/_color.spec.scss index bd76ecc1..228fdae7 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -53,44 +53,64 @@ $_palette: palette( } @include describe('contrast') { - @include it('should return contrast value of a hex color') { - $background: #fff; + @include it('should return an OKLCH color with relative color syntax from a hex value'){ + $background: #09f; - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); + @include assert-equal(accessible-colors($background), oklch(from #09f var(--l) 0 h)); } - @include it('should return contrast value of an hsl color') { - $background: hsl(0deg 0% 0%); + @include it('should return an OKLCH color with relative color syntax from an hsl value'){ + $background: hsl(204deg 100% 50%); - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); + @include assert-equal(accessible-colors($background), oklch(from hsl(204deg 100% 50%) var(--l) 0 h)); } - @include it('should return a contrast color of a relative syntax color') { - $background: oklch(from #09f l c h); + @include it('should return an OKLCH color with relative color syntax from a CSS variable value'){ + $background: var(--ig-primary-500); - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); + @include assert-equal(accessible-colors($background), oklch(from var(--ig-primary-500) var(--l) 0 h)); } - @include it('should return a contrast color of a CSS variable reference'){ - $background: var(--ig-primary-500); + @include it('should return the passed background value if no valid colors are provided') { + $value: 'not a color'; + + @include assert-equal(text-contrast($value), $value); + } + + @include it('should return black for a white background') { + $background: #fff; + + @include assert-equal(text-contrast($background), #000); + } + + @include it('should return white for a black background') { + $background: #000; - @include assert-equal(text-contrast($background), oklch(from $background clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1) 0 h)); + @include assert-equal(text-contrast($background), #fff); } @include it('should return a contrasting color for a given background regardless of level case') { - $test-color: oklch(from #09f clamp(0, (l / var(--ig-contrast-aaa) - 1) * -infinity, 1) 0 h); + $test-color: #000; - @include assert-equal(text-contrast($_primary, $contrast: 'AAA'), $test-color); - @include assert-equal(text-contrast($_primary, $contrast: 'aaa'), $test-color); - @include assert-equal(text-contrast($_primary, $contrast: 'Aaa'), $test-color); + @include assert-equal(text-contrast($_primary, $test-color, 'AAA'), $test-color); + @include assert-equal(text-contrast($_primary, $test-color, 'aaa'), $test-color); + @include assert-equal(text-contrast($_primary, $test-color, 'Aaa'), $test-color); } - @include it('should return an AA contrasting color for a given background') { - @include assert-equal(text-contrast($_primary, $contrast: 'AA'), oklch(from #09f clamp(0, (l / var(--ig-contrast-aa) - 1) * -infinity, 1) 0 h)); + @include it('returns the best contrast color from a list of candidates') { + @include assert-equal(text-contrast($_primary, #fff #222 #333, 'AA'), #222); + } + + @include it('should return an AAA contrasting color for a given background') { + $test-color: #000; + + @include assert-equal(text-contrast($_primary, $test-color, 'AAA'), $test-color); } - @include it('should return an A contrasting color for a given background') { - @include assert-equal(text-contrast($_primary, $contrast: 'A'), oklch(from #09f clamp(0, (l / var(--ig-contrast-a) - 1) * -infinity, 1) 0 h)); + @include it('should return an AA contrasting color for a given background') { + $test-color: #222; + + @include assert-equal(text-contrast($_primary, $test-color, 'AA'), $test-color); } } @@ -136,7 +156,7 @@ $_palette: palette( @include assert-equal($value, hsl(from (var(--ig-secondary-A400)) h s l / 1)); } - @include it('should return a contrast shade of type string w/ palette as only argument') { + @include it('should return a contrast shade w/ palette as only argument') { $value: contrast-color($_palette, $opacity: .5); $expected: color-mix(in oklch, var(--ig-primary-500-contrast) 50%, transparent); From 85cde3075f53bf263b91c1cfc20e00ff7a7172df Mon Sep 17 00:00:00 2001 From: didimmova Date: Tue, 18 Feb 2025 17:18:49 +0200 Subject: [PATCH 04/15] feat(palette): add fallback for contrast level --- sass/color/_mixins.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index c261bd24..2015ee37 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -42,7 +42,7 @@ $_added: () !default; $scope: if(is-root(), ':root', '&'); #{$scope} { - --ig-contrast-level: var(--ig-contrast-#{$contrast-level}); + --ig-contrast-level: var(--ig-contrast-#{$contrast-level}, var(--ig-contrast-aa)); --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); @each $color, $shades in map.remove($palette, '_meta') { From 63512fb326142e05f5dd8faeadce03c1b822ba3b Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 19 Feb 2025 17:20:14 +0200 Subject: [PATCH 05/15] refactor(colors): rename accsssible-colors function to adaptive-contrast --- sass/color/_functions.scss | 52 +++++++++++++++++++------------------- test/_color.spec.scss | 6 ++--- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 118a8790..22f07525 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -105,7 +105,7 @@ $_enhanced-accessibility: false; ( $variant: map.get($shade, 'hsl'), '#{$variant}-contrast': - accessible-colors( + adaptive-contrast( $background: #{var(--ig-#{$name}-#{$variant})}, ), '#{$variant}-raw': map.get($shade, 'raw'), @@ -225,19 +225,19 @@ $_enhanced-accessibility: false; @return color($palette, $color, #{$variant}-contrast, $opacity); } -/// Returns black or white color for a passed color. +/// Returns a CSS runtime calculated relative color(black or white) for a given color. /// @access public /// @group Color -/// @param {Color} $background - The background color used to return a contrast/foreground color for. -/// @returns {string} - Returns a relative syntax OKLCH color where the luminance is adjusted +/// @param {Color} $color - The base color used in the calculation. +/// @returns {string} - Returns a relative syntax OKLCH color where the lightness is adjusted /// based on the specified contrast level, resulting in either black or white. /// @example scss /// .my-component { -/// background: #09f -/// color: accessible-colors(#09f) +/// background: #09f; +/// color: accessible-colors(#09f); /// } -@function accessible-colors($background) { - @return #{oklch(from $background var(--l) 0 h)}; +@function adaptive-contrast($color) { + @return #{oklch(from $color var(--l) 0 h)}; } /// Returns a contrast color for a passed color. @@ -300,24 +300,6 @@ $_enhanced-accessibility: false; } } -/// Calculates the contrast ratio between two colors. -/// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests -/// @access public -/// @group Color -/// @param {Color} $background - The background color. -/// @param {Color} $foreground - The foreground color. -/// @require {function} luminance -/// @require {function} to-fixed -/// @example scss -/// contrast(#09f, #000); -/// @returns {Number} - The contrast ratio between the background and foreground colors. -@function contrast($background, $foreground) { - $backLum: luminance($background) + 0.05; - $foreLum: luminance($foreground) + 0.05; - - @return to-fixed(math.div(math.max($backLum, $foreLum), math.min($backLum, $foreLum))); -} - /// Mixes two colors to produce an opaque color. /// @access private /// @group Color @@ -351,6 +333,24 @@ $_enhanced-accessibility: false; @return (math.round(color.hue($color)), math.round(color.saturation($color)), math.round(color.lightness($color))); } +/// Calculates the contrast ratio between two colors. +/// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests +/// @access public +/// @group Color +/// @param {Color} $background - The background color. +/// @param {Color} $foreground - The foreground color. +/// @require {function} luminance +/// @require {function} to-fixed +/// @example scss +/// contrast(#09f, #000); +/// @returns {Number} - The contrast ratio between the background and foreground colors. +@function contrast($background, $foreground) { + $backLum: luminance($background) + 0.05; + $foreLum: luminance($foreground) + 0.05; + + @return to-fixed(math.div(math.max($backLum, $foreLum), math.min($backLum, $foreLum))); +} + /// Calculates the luminance for a given color. /// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests. /// @access public diff --git a/test/_color.spec.scss b/test/_color.spec.scss index 228fdae7..ebdd62fc 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -56,19 +56,19 @@ $_palette: palette( @include it('should return an OKLCH color with relative color syntax from a hex value'){ $background: #09f; - @include assert-equal(accessible-colors($background), oklch(from #09f var(--l) 0 h)); + @include assert-equal(adaptive-contrast($background), oklch(from #09f var(--l) 0 h)); } @include it('should return an OKLCH color with relative color syntax from an hsl value'){ $background: hsl(204deg 100% 50%); - @include assert-equal(accessible-colors($background), oklch(from hsl(204deg 100% 50%) var(--l) 0 h)); + @include assert-equal(adaptive-contrast($background), oklch(from hsl(204deg 100% 50%) var(--l) 0 h)); } @include it('should return an OKLCH color with relative color syntax from a CSS variable value'){ $background: var(--ig-primary-500); - @include assert-equal(accessible-colors($background), oklch(from var(--ig-primary-500) var(--l) 0 h)); + @include assert-equal(adaptive-contrast($background), oklch(from var(--ig-primary-500) var(--l) 0 h)); } @include it('should return the passed background value if no valid colors are provided') { From d0aeaf7b32cba7453a54f034dd48e2ba563783c7 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 19 Feb 2025 17:23:32 +0200 Subject: [PATCH 06/15] docs(colors): update sassdoc for adaptive colors --- sass/color/_functions.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 22f07525..38d3e9a8 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -233,8 +233,9 @@ $_enhanced-accessibility: false; /// based on the specified contrast level, resulting in either black or white. /// @example scss /// .my-component { -/// background: #09f; -/// color: accessible-colors(#09f); +/// --bg: #09f; +/// background: var(--bg); +/// color: adaptive-contrast(var(--bg)); /// } @function adaptive-contrast($color) { @return #{oklch(from $color var(--l) 0 h)}; From 2eef76c23c3945862142c49a68c402f6ccbfc39a Mon Sep 17 00:00:00 2001 From: didimmova Date: Wed, 19 Feb 2025 17:57:45 +0200 Subject: [PATCH 07/15] fix(colors): remove wrong parameter invocation --- sass/color/_functions.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 38d3e9a8..5cb9d3c9 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -104,10 +104,7 @@ $_enhanced-accessibility: false; $result, ( $variant: map.get($shade, 'hsl'), - '#{$variant}-contrast': - adaptive-contrast( - $background: #{var(--ig-#{$name}-#{$variant})}, - ), + '#{$variant}-contrast': adaptive-contrast(#{var(--ig-#{$name}-#{$variant})}), '#{$variant}-raw': map.get($shade, 'raw'), ) ); From d483c76496d8eeb2edbd4187b5c7032fa809ec71 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Thu, 20 Feb 2025 10:06:34 +0200 Subject: [PATCH 08/15] refactor(colors): add adaptive-contrast mixin --- sass/color/_mixins.scss | 41 ++++++++++++++++++++++++++++++++++++++--- test/_color.spec.scss | 12 ++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index 2015ee37..c740b277 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -29,6 +29,40 @@ $_added: () !default; } } +/// Sets up CSS custom properties for WCAG contrast calculations. +/// These properties are used to determine the appropriate text color contrast +/// based on WCAG accessibility guidelines. +/// @access public +/// @group Color +/// @param {String} $level ['aa'] - WCAG contrast level ('a', 'aa', or 'aaa') +/// @example scss - Using the mixin with default AA level +/// .my-component { +/// @include adaptive-contrast(); +/// } +/// @example scss - Using the mixin with AAA level +/// .my-component { +/// @include adaptive-contrast('aaa'); +/// } +/// @example scss - Generated CSS custom properties +/// :root { +/// --ig-wcga-a: 0.7; // Level A threshold +/// --ig-wcga-aa: 0.6; // Level AA threshold +/// --ig-wcga-aaa: 0.5; // Level AAA threshold +/// --ig-contrast-level: var(--ig-wcga-aaa); +/// --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); +/// } +@mixin adaptive-contrast($level: 'aaa') { + $scope: if(is-root(), ':root', '&'); + + #{$scope} { + --ig-wcga-a: 0.38; + --ig-wcga-aa: 0.54; + --ig-wcga-aaa: 0.6; + --ig-contrast-level: var(--ig-wcga-#{$level}); + --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); + } +} + /// Generates CSS variables for a given palette. /// @access public /// @group Palettes @@ -38,12 +72,13 @@ $_added: () !default; /// $palette: palette($primary: red, $secondary: blue, $gray: #000); /// @include palette($palette); /// @require {function} is-root -@mixin palette($palette, $contrast: true, $contrast-level: 'aa') { +@mixin palette($palette, $contrast: true, $contrast-level: 'aaa') { $scope: if(is-root(), ':root', '&'); #{$scope} { - --ig-contrast-level: var(--ig-contrast-#{$contrast-level}, var(--ig-contrast-aa)); - --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); + @if $contrast { + @include adaptive-contrast($contrast-level); + } @each $color, $shades in map.remove($palette, '_meta') { @each $shade, $value in $shades { diff --git a/test/_color.spec.scss b/test/_color.spec.scss index ebdd62fc..d5bf8766 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -54,21 +54,21 @@ $_palette: palette( @include describe('contrast') { @include it('should return an OKLCH color with relative color syntax from a hex value'){ - $background: #09f; + $color: #09f; - @include assert-equal(adaptive-contrast($background), oklch(from #09f var(--l) 0 h)); + @include assert-equal(adaptive-contrast($color), oklch(from #09f var(--l) 0 h)); } @include it('should return an OKLCH color with relative color syntax from an hsl value'){ - $background: hsl(204deg 100% 50%); + $color: hsl(204deg 100% 50%); - @include assert-equal(adaptive-contrast($background), oklch(from hsl(204deg 100% 50%) var(--l) 0 h)); + @include assert-equal(adaptive-contrast($color), oklch(from hsl(204deg 100% 50%) var(--l) 0 h)); } @include it('should return an OKLCH color with relative color syntax from a CSS variable value'){ - $background: var(--ig-primary-500); + $color: var(--ig-primary-500); - @include assert-equal(adaptive-contrast($background), oklch(from var(--ig-primary-500) var(--l) 0 h)); + @include assert-equal(adaptive-contrast($color), oklch(from var(--ig-primary-500) var(--l) 0 h)); } @include it('should return the passed background value if no valid colors are provided') { From b234984d5bf3e8211afc5e2492a5531fea6c26a9 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 21 Feb 2025 08:39:50 +0200 Subject: [PATCH 09/15] refactor(color): improve adaptive-contrast function --- sass/color/_functions.scss | 4 +++- sass/color/_mixins.scss | 15 ++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 5cb9d3c9..5a390080 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -235,7 +235,9 @@ $_enhanced-accessibility: false; /// color: adaptive-contrast(var(--bg)); /// } @function adaptive-contrast($color) { - @return #{oklch(from $color var(--l) 0 h)}; + $fn: meta.get-function('color', $css: true); + + @return meta.call($fn, from $color var(--y-contrast)); } /// Returns a contrast color for a passed color. diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index c740b277..eaa65aeb 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -51,15 +51,16 @@ $_added: () !default; /// --ig-contrast-level: var(--ig-wcga-aaa); /// --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); /// } -@mixin adaptive-contrast($level: 'aaa') { +@mixin adaptive-contrast($level: 'aa') { $scope: if(is-root(), ':root', '&'); #{$scope} { - --ig-wcga-a: 0.38; - --ig-wcga-aa: 0.54; - --ig-wcga-aaa: 0.6; - --ig-contrast-level: var(--ig-wcga-#{$level}); - --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); + --ig-wcag-a: 0.31; + --ig-wcag-aa: 0.185; + --ig-wcag-aaa: 0.178; + --ig-contrast-level: var(--ig-wcag-#{$level}); + --y: clamp(0, (y / var(--ig-contrast-level) - 1) * -infinity, 1); + --y-contrast: xyz-d65 var(--y) var(--y) var(--y); } } @@ -72,7 +73,7 @@ $_added: () !default; /// $palette: palette($primary: red, $secondary: blue, $gray: #000); /// @include palette($palette); /// @require {function} is-root -@mixin palette($palette, $contrast: true, $contrast-level: 'aaa') { +@mixin palette($palette, $contrast: true, $contrast-level: 'aa') { $scope: if(is-root(), ':root', '&'); #{$scope} { From 7a17c7243ab877396fcd1207051a71b1154b2a75 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 21 Feb 2025 08:40:13 +0200 Subject: [PATCH 10/15] spec(colors): refactor adaptive color tests --- test/_color.spec.scss | 85 ++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 25 deletions(-) diff --git a/test/_color.spec.scss b/test/_color.spec.scss index d5bf8766..dbe9bdfb 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -25,14 +25,14 @@ $_palette: palette( $info: $_info, $warn: $_warn, $error: $_error, - $variant: 'material' + $variant: 'material', ); @include describe('Color') { @include describe('base') { @include it('should mix two colors to produce an opaque color') { - @include assert-equal(to-opaque(rgba(255, 255, 255, .32), #fff), #fff); - @include assert-equal(to-opaque(rgba(233, 233, 233, .32), rgba(255, 255, 255, 0)), #f7f7f7); + @include assert-equal(to-opaque(rgba(255, 255, 255, 0.32), #fff), #fff); + @include assert-equal(to-opaque(rgba(233, 233, 233, 0.32), rgba(255, 255, 255, 0)), #f7f7f7); } @include it('converts a color to a list of HSL values') { @@ -53,22 +53,27 @@ $_palette: palette( } @include describe('contrast') { - @include it('should return an OKLCH color with relative color syntax from a hex value'){ + $fn: meta.get-function('color', $css: true); + + @include it('should return an adaptive contrast color from a hex value') { $color: #09f; - @include assert-equal(adaptive-contrast($color), oklch(from #09f var(--l) 0 h)); + @include assert-equal(adaptive-contrast($color), meta.call($fn, from #09f var(--y-contrast))); } - @include it('should return an OKLCH color with relative color syntax from an hsl value'){ + @include it('should return an adaptive contrast color from an hsl value') { $color: hsl(204deg 100% 50%); - @include assert-equal(adaptive-contrast($color), oklch(from hsl(204deg 100% 50%) var(--l) 0 h)); + @include assert-equal(adaptive-contrast($color), meta.call($fn, from hsl(204deg 100% 50%) var(--y-contrast))); } - @include it('should return an OKLCH color with relative color syntax from a CSS variable value'){ + @include it('should return an adaptive contrast color from a CSS variable value') { $color: var(--ig-primary-500); - @include assert-equal(adaptive-contrast($color), oklch(from var(--ig-primary-500) var(--l) 0 h)); + @include assert-equal( + adaptive-contrast($color), + meta.call($fn, from var(--ig-primary-500) var(--y-contrast)) + ); } @include it('should return the passed background value if no valid colors are provided') { @@ -143,28 +148,37 @@ $_palette: palette( } @include it('should return a shade as CSS variable w/ color as only argument') { - $value: color($color: secondary); + $value: color( + $color: secondary, + ); @include assert-equal(type-of($value), string); @include assert-equal($value, hsl(from (var(--ig-secondary-500)) h s l / 1)); } @include it('should return a shade of type string as CSS var w/ color and variant as only arguments') { - $value: color($color: secondary, $variant: 'A400'); + $value: color( + $color: secondary, + $variant: 'A400', + ); @include assert-equal(type-of($value), string); @include assert-equal($value, hsl(from (var(--ig-secondary-A400)) h s l / 1)); } @include it('should return a contrast shade w/ palette as only argument') { - $value: contrast-color($_palette, $opacity: .5); + $value: contrast-color($_palette, $opacity: 0.5); $expected: color-mix(in oklch, var(--ig-primary-500-contrast) 50%, transparent); @include assert-equal($expected, $value); } @include it('should return a contrast shade of type string as CSS var w/ color and variant as only arguments') { - $value: contrast-color($color: secondary, $variant: 'A400', $opacity: .25); + $value: contrast-color( + $color: secondary, + $variant: 'A400', + $opacity: 0.25, + ); @include assert-equal(type-of($value), string); @include assert-equal($value, color-mix(in oklch, var(--ig-secondary-A400-contrast) 25%, transparent)); @@ -187,7 +201,7 @@ $_palette: palette( $shade: shade($color, $_primary, $variant, null); $expected: ( raw: hsl(204deg 100% 44.5%), - hsl: #{hsl(from var(--ig-primary-500) h calc(s * 1.26) calc(l * 0.89))} + hsl: #{hsl(from var(--ig-primary-500) h calc(s * 1.26) calc(l * 0.89))}, ); @include assert-equal($shade, $expected); @@ -200,7 +214,7 @@ $_palette: palette( $shade: shade($color, null, $variant, $surface); $expected: ( raw: hsl(0deg 0% 98%), - hsl: #{hsl(from var(--ig-gray-500) h s 98%)} + hsl: #{hsl(from var(--ig-gray-500) h s 98%)}, ); // $surface is bright, return a darker shade of gray @@ -208,10 +222,12 @@ $_palette: palette( $surface: #444; $shade: shade($color, null, $variant, $surface); - $expected: #{var(--ig-#{$color}-h), var(--ig-#{$color}-s), 13%}; + $expected: #{var(--ig-#{$color}-h), + var(--ig-#{$color}-s), + 13%}; $expected: ( raw: hsl(0deg 0% 13%), - hsl: #{hsl(from var(--ig-gray-500) h s 13%)} + hsl: #{hsl(from var(--ig-gray-500) h s 13%)}, ); // $surface is dark, return a lighter shade of gray @@ -250,11 +266,11 @@ $_palette: palette( @include contains($selector: false) { :root { @each $color, $shades in map.remove($IPalette, '_meta') { - @each $shade in $shades { - $value: map.get($_palette, $color, $shade); + @each $shade in $shades { + $value: map.get($_palette, $color, $shade); - --ig-#{$color}-#{$shade}: #{$value}; - } + --ig-#{$color}-#{$shade}: #{$value}; + } } } } @@ -298,11 +314,11 @@ $_palette: palette( @include contains($selector: false) { :root { @each $color, $shades in map.remove($IPalette, '_meta') { - @each $shade in $shades { - $value: map.get($_palette, $color, $shade); + @each $shade in $shades { + $value: map.get($_palette, $color, $shade); - --ig-#{$color}-#{$shade}: #{$value}; - } + --ig-#{$color}-#{$shade}: #{$value}; + } } } } @@ -329,5 +345,24 @@ $_palette: palette( @include it('should convert a color to a list of HSL values') { @include assert-equal(to-hsl(black), (0deg, 0%, 0%)); } + + @include it('should include all necessarry CSS custom properties for adaptive contrast to work') { + @include assert() { + @include output($selector: false) { + @include adaptive-contrast('aaa'); + } + + @include contains($selector: false) { + :root { + --ig-wcag-a: 0.31; + --ig-wcag-aa: 0.183; + --ig-wcag-aaa: 0.178; + --ig-contrast-level: var(--ig-wcag-aaa); + --y: clamp(0, (y / var(--ig-contrast-level) - 1) * -infinity, 1); + --y-contrast: xyz-d50 var(--y) var(--y) var(--y); + } + } + } + } } } From 58b639201d278c55278a0ee0f23cdb4cb93574bb Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 21 Feb 2025 09:14:59 +0200 Subject: [PATCH 11/15] refactor(color): update color function and fix contrast shading --- sass/color/_functions.scss | 14 ++++---------- test/_color.spec.scss | 30 +++++++++++++++++++++--------- test/_themes.spec.scss | 4 ++-- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index 5a390080..d14f485d 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -185,23 +185,17 @@ $_enhanced-accessibility: false; $s: #{var(--ig-#{$color}-#{$variant})}; $contrast: if(meta.type-of($variant) == string, string.index($variant, 'contrast'), false); $_alpha: if($opacity, $opacity, 1); - $_hsl-alpha: hsl(from $s h s l / $_alpha); - $_mix-alpha: color-mix(in oklch, $s #{$_alpha * 100%}, transparent); + $_relative-color: hsl(from $s h s l / $_alpha); @if $palette { $s: map.get($palette, #{$color}); $base: map.get($s, #{$variant}); $raw: map.get($s, #{$variant}-raw); - $var: var(--ig-#{$color}-#{$variant}); - @return if( - $contrast, - if($opacity, $_mix-alpha, $var), - if($raw and $variant != '500', rgba($raw, $_alpha), $base) - ); + @return if($contrast, $_relative-color, if($raw and $variant != '500', rgba($raw, $_alpha), $base)); } - @return if($contrast, $_mix-alpha, $_hsl-alpha); + @return $_relative-color; } /// Retrieves a contrast text color for a given color variant from a color palette. @@ -237,7 +231,7 @@ $_enhanced-accessibility: false; @function adaptive-contrast($color) { $fn: meta.get-function('color', $css: true); - @return meta.call($fn, from $color var(--y-contrast)); + @return hsl(from meta.call($fn, from $color var(--y-contrast)) h 0 l); } /// Returns a contrast color for a passed color. diff --git a/test/_color.spec.scss b/test/_color.spec.scss index dbe9bdfb..f7d78c4c 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -58,13 +58,19 @@ $_palette: palette( @include it('should return an adaptive contrast color from a hex value') { $color: #09f; - @include assert-equal(adaptive-contrast($color), meta.call($fn, from #09f var(--y-contrast))); + @include assert-equal( + adaptive-contrast($color), + hsl(from meta.call($fn, from #09f var(--y-contrast)) h 0 l) + ); } @include it('should return an adaptive contrast color from an hsl value') { $color: hsl(204deg 100% 50%); - @include assert-equal(adaptive-contrast($color), meta.call($fn, from hsl(204deg 100% 50%) var(--y-contrast))); + @include assert-equal( + adaptive-contrast($color), + hsl(from meta.call($fn, from hsl(204deg 100% 50%) var(--y-contrast)) h 0 l) + ); } @include it('should return an adaptive contrast color from a CSS variable value') { @@ -72,7 +78,7 @@ $_palette: palette( @include assert-equal( adaptive-contrast($color), - meta.call($fn, from var(--ig-primary-500) var(--y-contrast)) + hsl(from meta.call($fn, from var(--ig-primary-500) var(--y-contrast)) h 0 l) ); } @@ -168,7 +174,7 @@ $_palette: palette( @include it('should return a contrast shade w/ palette as only argument') { $value: contrast-color($_palette, $opacity: 0.5); - $expected: color-mix(in oklch, var(--ig-primary-500-contrast) 50%, transparent); + $expected: hsl(from var(--ig-primary-500-contrast) h s l / 0.5); @include assert-equal($expected, $value); } @@ -181,7 +187,7 @@ $_palette: palette( ); @include assert-equal(type-of($value), string); - @include assert-equal($value, color-mix(in oklch, var(--ig-secondary-A400-contrast) 25%, transparent)); + @include assert-equal($value, hsl(from var(--ig-secondary-A400-contrast) h s l / 0.25)); } @include it('should retrieve colors from a palette regadless of type of key') { @@ -190,9 +196,15 @@ $_palette: palette( @include assert-true(color($_palette, 'primary', '500')); @include assert-equal(color($_palette, 'primary', '500'), $_primary); @include assert-true(contrast-color($_palette, primary, 500)); - @include assert-equal(contrast-color($_palette, primary, 500), var(--ig-primary-500-contrast)); + @include assert-equal( + contrast-color($_palette, primary, 500), + hsl(from var(--ig-primary-500-contrast) h s l / 1) + ); @include assert-true(contrast-color($_palette, 'primary', '500')); - @include assert-equal(contrast-color($_palette, 'primary', '500'), var(--ig-primary-500-contrast)); + @include assert-equal( + contrast-color($_palette, 'primary', '500'), + hsl(from var(--ig-primary-500-contrast) h s l / 1) + ); } @include it('should generate an HSL color shade from a given base color') { @@ -355,11 +367,11 @@ $_palette: palette( @include contains($selector: false) { :root { --ig-wcag-a: 0.31; - --ig-wcag-aa: 0.183; + --ig-wcag-aa: 0.185; --ig-wcag-aaa: 0.178; --ig-contrast-level: var(--ig-wcag-aaa); --y: clamp(0, (y / var(--ig-contrast-level) - 1) * -infinity, 1); - --y-contrast: xyz-d50 var(--y) var(--y) var(--y); + --y-contrast: xyz-d65 var(--y) var(--y) var(--y); } } } diff --git a/test/_themes.spec.scss b/test/_themes.spec.scss index 16bfdd6b..45a4a3af 100644 --- a/test/_themes.spec.scss +++ b/test/_themes.spec.scss @@ -104,8 +104,8 @@ $schema: ( type: 'light', background: hsl(from var(--ig-primary-400) h s l / 1), hover-background: hsl(from var(--ig-secondary-700) h s l / .26), - foreground: color-mix(in oklch, var(--ig-primary-400-contrast) 100%, transparent), - hover-foreground: color-mix(in oklch, var(--ig-secondary-700-contrast) 100%, transparent), + foreground: hsl(from var(--ig-primary-400-contrast) h s l / 1), + hover-foreground: hsl(from var(--ig-secondary-700-contrast) h s l / 1), border-style: solid, border-radius: .125rem, brushes: var(--chart-brushes), From bc1fd9e77143949e719a98cf4fa21adb9278cd1f Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 21 Feb 2025 10:06:39 +0200 Subject: [PATCH 12/15] refactor(colors): change where adaptive props are declared --- sass/color/_mixins.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index eaa65aeb..3ca203aa 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -76,11 +76,11 @@ $_added: () !default; @mixin palette($palette, $contrast: true, $contrast-level: 'aa') { $scope: if(is-root(), ':root', '&'); - #{$scope} { - @if $contrast { - @include adaptive-contrast($contrast-level); - } + @if $contrast { + @include adaptive-contrast($contrast-level); + } + #{$scope} { @each $color, $shades in map.remove($palette, '_meta') { @each $shade, $value in $shades { @if not(string.index(to-string($shade), raw)) { From dda5c92b7954d17666dcac8f2e5abbf862b0d347 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 21 Feb 2025 11:29:15 +0200 Subject: [PATCH 13/15] Update sass/color/_mixins.scss --- sass/color/_mixins.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index 3ca203aa..3d44badd 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -45,9 +45,9 @@ $_added: () !default; /// } /// @example scss - Generated CSS custom properties /// :root { -/// --ig-wcga-a: 0.7; // Level A threshold -/// --ig-wcga-aa: 0.6; // Level AA threshold -/// --ig-wcga-aaa: 0.5; // Level AAA threshold +/// --ig-wcga-a: 0.31; // Level A threshold +/// --ig-wcga-aa: 0.185; // Level AA threshold +/// --ig-wcga-aaa: 0.178; // Level AAA threshold /// --ig-contrast-level: var(--ig-wcga-aaa); /// --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); /// } From 36f0c362df838cfbaccab529db0a08c8eefd312d Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 21 Feb 2025 11:34:59 +0200 Subject: [PATCH 14/15] docs(adaptive-contrast): update mixin sassdoc --- sass/color/_mixins.scss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sass/color/_mixins.scss b/sass/color/_mixins.scss index 3d44badd..ba5bce7e 100644 --- a/sass/color/_mixins.scss +++ b/sass/color/_mixins.scss @@ -45,11 +45,12 @@ $_added: () !default; /// } /// @example scss - Generated CSS custom properties /// :root { -/// --ig-wcga-a: 0.31; // Level A threshold -/// --ig-wcga-aa: 0.185; // Level AA threshold -/// --ig-wcga-aaa: 0.178; // Level AAA threshold -/// --ig-contrast-level: var(--ig-wcga-aaa); -/// --l: clamp(0, (l / var(--ig-contrast-level) - 1) * -infinity, 1); +/// --ig-wcag-a: 0.31; // Level A threshold +/// --ig-wcag-aa: 0.185; // Level AA threshold +/// --ig-wcag-aaa: 0.178; // Level AAA threshold +/// --ig-contrast-level: var(--ig-wcag-aa); +/// --y: clamp(0, (y / var(--ig-contrast-level) - 1) * -infinity, 1); +/// --y-contrast: xyz-d65 var(--y) var(--y) var(--y); /// } @mixin adaptive-contrast($level: 'aa') { $scope: if(is-root(), ':root', '&'); From 85f016f4e05a52a45609bcad085d6dcb61d85791 Mon Sep 17 00:00:00 2001 From: didimmova Date: Mon, 24 Feb 2025 11:22:50 +0200 Subject: [PATCH 15/15] feat(color): update color function to return CSS var if no opacity --- sass/color/_functions.scss | 2 +- test/_color.spec.scss | 12 ++++++------ test/_themes.spec.scss | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sass/color/_functions.scss b/sass/color/_functions.scss index d14f485d..3156229f 100644 --- a/sass/color/_functions.scss +++ b/sass/color/_functions.scss @@ -185,7 +185,7 @@ $_enhanced-accessibility: false; $s: #{var(--ig-#{$color}-#{$variant})}; $contrast: if(meta.type-of($variant) == string, string.index($variant, 'contrast'), false); $_alpha: if($opacity, $opacity, 1); - $_relative-color: hsl(from $s h s l / $_alpha); + $_relative-color: if($opacity, hsl(from $s h s l / $opacity), $s); @if $palette { $s: map.get($palette, #{$color}); diff --git a/test/_color.spec.scss b/test/_color.spec.scss index f7d78c4c..5b71962c 100644 --- a/test/_color.spec.scss +++ b/test/_color.spec.scss @@ -150,7 +150,7 @@ $_palette: palette( $value: color(); @include assert-equal(type-of($value), string); - @include assert-equal($value, hsl(from (var(--ig-primary-500)) h s l / 1)); + @include assert-equal($value, var(--ig-primary-500)); } @include it('should return a shade as CSS variable w/ color as only argument') { @@ -159,7 +159,7 @@ $_palette: palette( ); @include assert-equal(type-of($value), string); - @include assert-equal($value, hsl(from (var(--ig-secondary-500)) h s l / 1)); + @include assert-equal($value, var(--ig-secondary-500)); } @include it('should return a shade of type string as CSS var w/ color and variant as only arguments') { @@ -169,7 +169,7 @@ $_palette: palette( ); @include assert-equal(type-of($value), string); - @include assert-equal($value, hsl(from (var(--ig-secondary-A400)) h s l / 1)); + @include assert-equal($value, var(--ig-secondary-A400)); } @include it('should return a contrast shade w/ palette as only argument') { @@ -198,12 +198,12 @@ $_palette: palette( @include assert-true(contrast-color($_palette, primary, 500)); @include assert-equal( contrast-color($_palette, primary, 500), - hsl(from var(--ig-primary-500-contrast) h s l / 1) + var(--ig-primary-500-contrast) ); @include assert-true(contrast-color($_palette, 'primary', '500')); @include assert-equal( contrast-color($_palette, 'primary', '500'), - hsl(from var(--ig-primary-500-contrast) h s l / 1) + var(--ig-primary-500-contrast) ); } @@ -341,7 +341,7 @@ $_palette: palette( $_palette: mocks.$handmade-palette; $_ref: color(null, primary, 800); - @include assert-equal($_ref, hsl(from var(--ig-primary-800) h s l / 1)); + @include assert-equal($_ref, var(--ig-primary-800)); @include assert() { @include output() { diff --git a/test/_themes.spec.scss b/test/_themes.spec.scss index 45a4a3af..672ac001 100644 --- a/test/_themes.spec.scss +++ b/test/_themes.spec.scss @@ -102,10 +102,10 @@ $schema: ( @include it('should output theme maps from schema definitions') { $theme: ( type: 'light', - background: hsl(from var(--ig-primary-400) h s l / 1), + background: var(--ig-primary-400), hover-background: hsl(from var(--ig-secondary-700) h s l / .26), - foreground: hsl(from var(--ig-primary-400-contrast) h s l / 1), - hover-foreground: hsl(from var(--ig-secondary-700-contrast) h s l / 1), + foreground: var(--ig-primary-400-contrast), + hover-foreground: var(--ig-secondary-700-contrast), border-style: solid, border-radius: .125rem, brushes: var(--chart-brushes),