From 744845ad10dd10979206fa4a243ca14ab1e80c43 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 20 Jul 2022 18:38:37 +0300 Subject: [PATCH 1/4] feat(elevations): add functions and mixins --- _index.scss | 1 + sass/_index.scss | 1 + sass/elevations/_functions.scss | 66 ++++++++++++++++++++++++ sass/elevations/_index.scss | 2 + sass/elevations/_mixins.scss | 27 ++++++++++ sass/elevations/presets/_material.scss | 36 ++++++++++++++ test/_elevations.spec.scss | 69 ++++++++++++++++++++++++++ test/_index.scss | 1 + test/e2e/theme.scss | 2 + 9 files changed, 205 insertions(+) create mode 100644 sass/elevations/_functions.scss create mode 100644 sass/elevations/_index.scss create mode 100644 sass/elevations/_mixins.scss create mode 100644 sass/elevations/presets/_material.scss create mode 100644 test/_elevations.spec.scss diff --git a/_index.scss b/_index.scss index 19934b82..e9aa2dd6 100644 --- a/_index.scss +++ b/_index.scss @@ -1,3 +1,4 @@ @forward './sass/color'; @forward './sass/typography'; +@forward './sass/elevations'; @forward './sass/utils'; diff --git a/sass/_index.scss b/sass/_index.scss index 12432a40..ea7597ec 100644 --- a/sass/_index.scss +++ b/sass/_index.scss @@ -1,3 +1,4 @@ @forward 'color'; +@forward 'elevations'; @forward 'typography'; @forward 'utils'; diff --git a/sass/elevations/_functions.scss b/sass/elevations/_functions.scss new file mode 100644 index 00000000..50646c75 --- /dev/null +++ b/sass/elevations/_functions.scss @@ -0,0 +1,66 @@ +@use 'sass:meta'; +@use 'sass:map'; +@use 'sass:list'; + +$factor: var(--ig-elevation-factor, 1); + +@function _is-shadow($shadow) { + $lastIndex: list.length($shadow); + + @if type-of($shadow) != list { + @return false; + } + + @if $lastIndex < 2 or $lastIndex > 5 { + @return false; + } + + @if list.separator($shadow) != space { + @return false; + } + + @return true; +} + +@function _transform-shadow($shadow) { + $result: (); + + @each $value in $shadow { + @if meta.type-of($value) != color and $value != 0 and $value != inset { + $result: list.append($result, calc($factor * $value)); + } @else { + $result: list.append($result, $value); + } + } + + @return $result; +} + +@function box-shadow($shadows) { + $result: (); + $s: (); + + @if meta.type-of($shadows) != list { + @error 'shadow expects a list of box-shadow values. ex.: shadow((0 2px 2px black))'; + } + + @if list.separator($shadows) == space { + $s: list.append($s, $shadows); + } + + @if list.separator($shadows) == comma { + $s: $shadows; + } + + @each $shadow in $s { + @if _is-shadow($shadow) { + $result: list.append($result, _transform-shadow($shadow), $separator: comma); + } + } + + @return $result; +} + +@function elevation($name) { + @return var(--ig-elevation-#{$name}); +} diff --git a/sass/elevations/_index.scss b/sass/elevations/_index.scss new file mode 100644 index 00000000..fcecd8c9 --- /dev/null +++ b/sass/elevations/_index.scss @@ -0,0 +1,2 @@ +@forward 'functions'; +@forward 'mixins'; diff --git a/sass/elevations/_mixins.scss b/sass/elevations/_mixins.scss new file mode 100644 index 00000000..d46c99d4 --- /dev/null +++ b/sass/elevations/_mixins.scss @@ -0,0 +1,27 @@ +@use '../utils' as *; + +/// Generates CSS variables for a given elevations map. +/// @access public +/// @param {Map} $elevations - The elevations map to use to generate CSS variables. +/// +/// @example scss Generate CSS variables for elevations. +/// $elevations: ( +/// small: shadow(0 .125rem .25rem rgba(0 0 0 / 75%)), +/// medium: shadow(0 .25rem .5rem rgba(0 0 0 / 85%)), +/// large: shadow(0 .75rem 1rem rgba(0 0 0 / 95%)), +/// ); +/// @include elevation($elevations); +/// +@mixin elevations($elevations) { + $scope: if(is-root(), ':root', '&'); + + #{$scope} { + @each $level, $shadow in $elevations { + --ig-elevation-#{$level}: #{$shadow}; + } + } +} + +@mixin elevation($name) { + box-shadow: var(--ig-elevation-#{$name}); +} diff --git a/sass/elevations/presets/_material.scss b/sass/elevations/presets/_material.scss new file mode 100644 index 00000000..67bdef1c --- /dev/null +++ b/sass/elevations/presets/_material.scss @@ -0,0 +1,36 @@ +@use '../index' as *; + +//// +/// @group elevations +/// @author Simeon Simeonoff +//// +$color-1: rgba(0 0 0 / 26%); +$color-2: rgba(0 0 0 / 12%); +$color-3: rgba(0 0 0 / 8%); +$elevations: ( + 0: none, + 1: box-shadow((0 1px 3px 0 $color-1, 0 1px 1px 0 $color-2, 0 2px 1px -1px $color-3)), + 2: box-shadow((0 1px 5px 0 $color-1, 0 2px 2px 0 $color-2, 0 3px 1px -2px $color-3)), + 3: box-shadow((0 1px 8px 0 $color-1, 0 3px 4px 0 $color-2, 0 3px 3px -2px $color-3)), + 4: box-shadow((0 2px 4px -1px $color-1, 0 4px 5px 0 $color-2, 0 1px 10px 0 $color-3)), + 5: box-shadow((0 3px 5px -1px $color-1, 0 5px 8px 0 $color-2, 0 1px 14px 0 $color-3)), + 6: box-shadow((0 3px 5px -1px $color-1, 0 6px 10px 0 $color-2, 0 1px 18px 0 $color-3)), + 7: box-shadow((0 4px 5px -2px $color-1, 0 7px 10px 1px $color-2, 0 2px 16px 1px $color-3)), + 8: box-shadow((0 5px 5px -3px $color-1, 0 8px 10px 1px $color-2, 0 3px 14px 2px $color-3)), + 9: box-shadow((0 5px 6px -3px $color-1, 0 9px 12px 1px $color-2, 0 3px 16px 2px $color-3)), + 10: box-shadow((0 6px 6px -3px $color-1, 0 10px 14px 1px $color-2, 0 4px 18px 3px $color-3)), + 11: box-shadow((0 6px 7px -4px $color-1, 0 11px 15px 1px $color-2, 0 4px 20px 3px $color-3)), + 12: box-shadow((0 7px 8px -4px $color-1, 0 12px 17px 2px $color-2, 0 5px 22px 4px $color-3)), + 13: box-shadow((0 7px 8px -4px $color-1, 0 13px 19px 2px $color-2, 0 5px 24px 4px $color-3)), + 14: box-shadow((0 7px 9px -4px $color-1, 0 14px 21px 2px $color-2, 0 5px 26px 4px $color-3)), + 15: box-shadow((0 8px 9px -5px $color-1, 0 15px 22px 2px $color-2, 0 6px 28px 5px $color-3)), + 16: box-shadow((0 8px 10px -5px $color-1, 0 16px 24px 2px $color-2, 0 6px 30px 5px $color-3)), + 17: box-shadow((0 8px 11px -5px $color-1, 0 17px 26px 2px $color-2, 0 6px 32px 5px $color-3)), + 18: box-shadow((0 9px 11px -5px $color-1, 0 18px 28px 2px $color-2, 0 7px 34px 6px $color-3)), + 19: box-shadow((0 9px 12px -6px $color-1, 0 19px 29px 2px $color-2, 0 7px 36px 6px $color-3)), + 20: box-shadow((0 10px 13px -6px $color-1, 0 20px 31px 3px $color-2, 0 8px 38px 7px $color-3)), + 21: box-shadow((0 10px 13px -6px $color-1, 0 21px 33px 3px $color-2, 0 8px 40px 7px $color-3)), + 22: box-shadow((0 10px 14px -6px $color-1, 0 22px 35px 3px $color-2, 0 8px 42px 7px $color-3)), + 23: box-shadow((0 11px 14px -7px $color-1, 0 23px 36px 3px $color-2, 0 9px 44px 8px $color-3)), + 24: box-shadow((0 11px 15px -7px $color-1, 0 24px 38px 3px $color-2, 0 9px 46px 8px $color-3)) +); diff --git a/test/_elevations.spec.scss b/test/_elevations.spec.scss new file mode 100644 index 00000000..92a8931b --- /dev/null +++ b/test/_elevations.spec.scss @@ -0,0 +1,69 @@ +@use 'sass:meta'; +@use 'sass:list'; +@use '../node_modules/sass-true/' as *; +@use '../index' as *; + +@include describe('Elevations') { + @include it('should return an empty list for a list of non box-shadow values') { + $raw: (pink, yellow, 9px, 'black'); + $transformed: box-shadow($raw); + $expected: (); + + @include assert-equal($transformed, $expected); + } + + @include it('should produce a box shadow that reacts to CSS --ig-elevation-factor changes') { + $raw: (0 0 2px 3px black, 0 6px 9px orange); + $transformed: box-shadow($raw); + $expected: ( + 0 0 calc($factor * 2px) calc($factor * 3px) black, + 0 calc($factor * 6px) calc($factor * 9px) orange + ); + + @include assert-equal($transformed, $expected); + } + + @include it('should produce CSS variables for a map of shadows') { + $shadows: ( + small: box-shadow(0 .125rem .25rem rgba(0 0 0 / 75%)), + medium: box-shadow(0 .25rem .5rem rgba(0 0 0 / 85%)), + large: box-shadow(0 .75rem 1rem rgba(0 0 0 / 95%)), + ); + + @include assert() { + @include output() { + @include elevations($shadows); + } + + @include expect() { + @each $key, $value in $shadows { + --ig-elevation-#{$key}: #{$value}; + } + } + } + } + + @include it('should retrieve a CSS variable elevation by name') { + @include assert() { + @include output() { + box-shadow: elevation(small); + } + + @include expect() { + box-shadow: var(--ig-elevation-small); + } + } + } + + @include it('should include box-shadow styles for an elevation level') { + @include assert() { + @include output() { + @include elevation(small); + } + + @include expect() { + box-shadow: var(--ig-elevation-small); + } + } + } +} diff --git a/test/_index.scss b/test/_index.scss index df883586..f806a912 100644 --- a/test/_index.scss +++ b/test/_index.scss @@ -1,3 +1,4 @@ @use '../node_modules/sass-true/' as *; @use 'color.spec' as *; @use 'typography.spec' as *; +@use 'elevations.spec' as *; diff --git a/test/e2e/theme.scss b/test/e2e/theme.scss index 5b361409..9b16aa34 100644 --- a/test/e2e/theme.scss +++ b/test/e2e/theme.scss @@ -1,4 +1,5 @@ @use '../../index' as *; +@use '../../sass/elevations/presets/material' as *; $h1: (font-size: 96px); $h2: (font-size: 70px); @@ -34,3 +35,4 @@ $palette: palette($primary: #9fe, $secondary: orange, $surface: #fff); @include palette($palette); @include typography("'Roboto', sans-serif", $scale); +@include elevations($elevations); From abb646cb18e32e2cf58b08545eeceae1608a83cb Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 20 Jul 2022 18:59:22 +0300 Subject: [PATCH 2/4] Update _mixins.scss --- sass/elevations/_mixins.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sass/elevations/_mixins.scss b/sass/elevations/_mixins.scss index d46c99d4..8e14ec48 100644 --- a/sass/elevations/_mixins.scss +++ b/sass/elevations/_mixins.scss @@ -6,9 +6,9 @@ /// /// @example scss Generate CSS variables for elevations. /// $elevations: ( -/// small: shadow(0 .125rem .25rem rgba(0 0 0 / 75%)), -/// medium: shadow(0 .25rem .5rem rgba(0 0 0 / 85%)), -/// large: shadow(0 .75rem 1rem rgba(0 0 0 / 95%)), +/// small: box-shadow(0 .125rem .25rem rgba(0 0 0 / 75%)), +/// medium: box-shadow(0 .25rem .5rem rgba(0 0 0 / 85%)), +/// large: box-shadow(0 .75rem 1rem rgba(0 0 0 / 95%)), /// ); /// @include elevation($elevations); /// From 531abe6a481f9f1b057da8adf4df664be5ba20c5 Mon Sep 17 00:00:00 2001 From: didimmova Date: Thu, 21 Jul 2022 17:55:28 +0300 Subject: [PATCH 3/4] docs(elevations): add docs for elevations --- sass/elevations/_functions.scss | 21 +++++++++++++++++++++ sass/elevations/_mixins.scss | 9 ++++++++- sass/elevations/presets/_material.scss | 10 ++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/sass/elevations/_functions.scss b/sass/elevations/_functions.scss index 50646c75..1a64a798 100644 --- a/sass/elevations/_functions.scss +++ b/sass/elevations/_functions.scss @@ -2,8 +2,16 @@ @use 'sass:map'; @use 'sass:list'; +//// +/// @group elevations +//// + $factor: var(--ig-elevation-factor, 1); +/// Checks if the passed argument is a valid box-shadow. +/// @access public +/// @param {List} $shadow - A list of shadow properties. +/// @return {Boolean} - Returns a boolean depending on if the passed argument is a valid shadow. @function _is-shadow($shadow) { $lastIndex: list.length($shadow); @@ -22,6 +30,10 @@ $factor: var(--ig-elevation-factor, 1); @return true; } +/// Transforms the passed box-shadow list according to the elevation factor value. +/// @access public +/// @param {List} $shadow - A list of shadow properties. +/// @return {List} - The transformed shadow list. @function _transform-shadow($shadow) { $result: (); @@ -36,6 +48,12 @@ $factor: var(--ig-elevation-factor, 1); @return $result; } +/// Sorts out a list of valid only box-shadows. +/// @access public +/// @param {List} $shadows - A list of shadow values. +/// @return {List} - The transformed shadow list. +/// @requires {function} _is-shadow +/// @requires {function} _transform-shadow @function box-shadow($shadows) { $result: (); $s: (); @@ -61,6 +79,9 @@ $factor: var(--ig-elevation-factor, 1); @return $result; } +/// Gets a CSS elevation variable by name. +/// @access public +/// @param {String} $name - The name of the shadow. @function elevation($name) { @return var(--ig-elevation-#{$name}); } diff --git a/sass/elevations/_mixins.scss b/sass/elevations/_mixins.scss index 8e14ec48..f1c59ffc 100644 --- a/sass/elevations/_mixins.scss +++ b/sass/elevations/_mixins.scss @@ -1,5 +1,9 @@ @use '../utils' as *; +//// +/// @group elevations +//// + /// Generates CSS variables for a given elevations map. /// @access public /// @param {Map} $elevations - The elevations map to use to generate CSS variables. @@ -10,7 +14,7 @@ /// medium: box-shadow(0 .25rem .5rem rgba(0 0 0 / 85%)), /// large: box-shadow(0 .75rem 1rem rgba(0 0 0 / 95%)), /// ); -/// @include elevation($elevations); +/// @include elevations($elevations); /// @mixin elevations($elevations) { $scope: if(is-root(), ':root', '&'); @@ -22,6 +26,9 @@ } } +/// Includes box-shadow styles for an elevation by name +/// @access public +/// @param {String} $name - The name of the shadow. @mixin elevation($name) { box-shadow: var(--ig-elevation-#{$name}); } diff --git a/sass/elevations/presets/_material.scss b/sass/elevations/presets/_material.scss index 67bdef1c..957a18d0 100644 --- a/sass/elevations/presets/_material.scss +++ b/sass/elevations/presets/_material.scss @@ -4,6 +4,16 @@ /// @group elevations /// @author Simeon Simeonoff //// + +/// Level 1 - Umbra Shadows +/// Level 2 - Penumbra Shadows +/// Level 3 - Ambient Shadows +/// @access public +/// @property {Color} $color-1 - The color used to generate umbra shadows. +/// @property {Color} $color-2 - The color used to generate penumbra shadows. +/// @property {Color} $color-3 - The color used to generate ambient shadows. +/// @property {Map} $elevations - А map of 24 shadow elevations with the umbra, penumbra and ambient shadows. + $color-1: rgba(0 0 0 / 26%); $color-2: rgba(0 0 0 / 12%); $color-3: rgba(0 0 0 / 8%); From 8368fbe3ce46fd28630383c4bcf3629f0c8b6de0 Mon Sep 17 00:00:00 2001 From: didimmova Date: Fri, 22 Jul 2022 09:39:35 +0300 Subject: [PATCH 4/4] docs(elevations): switch property with type annotation --- sass/elevations/presets/_material.scss | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sass/elevations/presets/_material.scss b/sass/elevations/presets/_material.scss index 957a18d0..ffc18cf5 100644 --- a/sass/elevations/presets/_material.scss +++ b/sass/elevations/presets/_material.scss @@ -6,17 +6,18 @@ //// /// Level 1 - Umbra Shadows -/// Level 2 - Penumbra Shadows -/// Level 3 - Ambient Shadows -/// @access public -/// @property {Color} $color-1 - The color used to generate umbra shadows. -/// @property {Color} $color-2 - The color used to generate penumbra shadows. -/// @property {Color} $color-3 - The color used to generate ambient shadows. -/// @property {Map} $elevations - А map of 24 shadow elevations with the umbra, penumbra and ambient shadows. - +/// @type {Color} $color-1 - The color used to generate umbra shadows. $color-1: rgba(0 0 0 / 26%); + +/// Level 2 - Penumbra Shadows +/// @type {Color} $color-2 - The color used to generate penumbra shadows. $color-2: rgba(0 0 0 / 12%); + +/// Level 3 - Ambient Shadows +/// @type {Color} $color-3 - The color used to generate ambient shadows. $color-3: rgba(0 0 0 / 8%); + +/// @type {Map} $elevations - А map of 24 shadow elevations with the umbra, penumbra and ambient shadows. $elevations: ( 0: none, 1: box-shadow((0 1px 3px 0 $color-1, 0 1px 1px 0 $color-2, 0 2px 1px -1px $color-3)),