diff --git a/src/components/Alert/Alert.jsx b/src/components/Alert/Alert.jsx index d07852e2..34bf8623 100644 --- a/src/components/Alert/Alert.jsx +++ b/src/components/Alert/Alert.jsx @@ -70,7 +70,8 @@ Alert.propTypes = { */ children: PropTypes.node.isRequired, /** - * [Color variant](/docs/foundation/colors#component-colors) to clarify importance and meaning of the alert. + * Color variant to clarify importance and meaning of the alert. Implements + * [Feedback and Neutral color collections](/docs/foundation/collections#colors). */ color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark']), /** diff --git a/src/components/Alert/Alert.module.scss b/src/components/Alert/Alert.module.scss index 285e7476..cd8c2b72 100644 --- a/src/components/Alert/Alert.module.scss +++ b/src/components/Alert/Alert.module.scss @@ -1,10 +1,10 @@ @use "sass:map"; @use "../../styles/theme/typography"; @use "../../styles/tools/accessibility"; +@use "../../styles/tools/collections"; @use "../../styles/tools/reset"; @use "settings"; @use "theme"; -@use "tools"; @layer components.alert { .root { @@ -66,35 +66,13 @@ top: -0.1em; } - .isRootColorSuccess { - @include tools.color(success); - } - - .isRootColorWarning { - @include tools.color(warning); - } - - .isRootColorDanger { - @include tools.color(danger); - } - - .isRootColorHelp { - @include tools.color(help); - } - - .isRootColorInfo { - @include tools.color(info); - } - - .isRootColorNote { - @include tools.color(note); - } - - .isRootColorLight { - @include tools.color(light); - } - - .isRootColorDark { - @include tools.color(dark); + @each $color in settings.$colors { + @include collections.generate-class( + $prefix: "rui-", + $component-name: "Alert", + $variant-name: "color", + $variant-value: $color, + $properties: settings.$themeable-properties, + ); } } diff --git a/src/components/Alert/README.md b/src/components/Alert/README.md index 5abf8452..befc886e 100644 --- a/src/components/Alert/README.md +++ b/src/components/Alert/README.md @@ -31,8 +31,8 @@ See [API](#api) for all available options. ## Color Variants -All [component colors](/docs/foundation/colors#component-colors) are supported by -alert to cover all possible needs of your project. +To cover all possible needs of your project, Alert is available in colors from +[Feedback and Neutral color collections](/docs/foundation/collections#colors). ### Success @@ -208,9 +208,10 @@ convention looks as follows: Where: -- `` is one of supported - [component colors](/docs/foundation/colors#component-colors) - (see alert [color variants](#color-variants) and [API](#api)), +- `` is a value from supported + [color collections](/docs/foundation/collections#colors) + (check [color variants](#color-variants) and [API](#api) to see which + collections are supported), - `` is one of `color` (color of text), `foreground-color` (color of border, icon, links, and emphasis), or `background-color`. diff --git a/src/components/Alert/_settings.scss b/src/components/Alert/_settings.scss index 036e8a37..b7ab4487 100644 --- a/src/components/Alert/_settings.scss +++ b/src/components/Alert/_settings.scss @@ -1,7 +1,12 @@ +@use "sass:list"; @use "sass:map"; +@use "../../styles/settings/collections"; @use "../../styles/theme/typography"; @use "theme"; $font-size: map.get(typography.$font-size-values, 1); $line-height: typography.$line-height-base; $min-height: calc(#{$font-size} * #{$line-height} + 2 * #{theme.$padding}); + +$colors: list.join(collections.$feedback-colors, collections.$neutral-colors); +$themeable-properties: color, foreground-color, background-color; diff --git a/src/components/Alert/_theme.scss b/src/components/Alert/_theme.scss index 208ed6fc..8dc449e4 100644 --- a/src/components/Alert/_theme.scss +++ b/src/components/Alert/_theme.scss @@ -4,46 +4,3 @@ $border-width: var(--rui-Alert__border-width); $border-radius: var(--rui-Alert__border-radius); $emphasis-font-weight: var(--rui-Alert__emphasis__font-weight); $stripe-width: var(--rui-Alert__stripe__width); - -$colors: ( - success: ( - color: var(--rui-Alert--success__color), - foreground-color: var(--rui-Alert--success__foreground-color), - background-color: var(--rui-Alert--success__background-color), - ), - warning: ( - color: var(--rui-Alert--warning__color), - foreground-color: var(--rui-Alert--warning__foreground-color), - background-color: var(--rui-Alert--warning__background-color), - ), - danger: ( - color: var(--rui-Alert--danger__color), - foreground-color: var(--rui-Alert--danger__foreground-color), - background-color: var(--rui-Alert--danger__background-color), - ), - info: ( - color: var(--rui-Alert--info__color), - foreground-color: var(--rui-Alert--info__foreground-color), - background-color: var(--rui-Alert--info__background-color), - ), - help: ( - color: var(--rui-Alert--help__color), - foreground-color: var(--rui-Alert--help__foreground-color), - background-color: var(--rui-Alert--help__background-color), - ), - note: ( - color: var(--rui-Alert--note__color), - foreground-color: var(--rui-Alert--note__foreground-color), - background-color: var(--rui-Alert--note__background-color), - ), - light: ( - color: var(--rui-Alert--light__color), - foreground-color: var(--rui-Alert--light__foreground-color), - background-color: var(--rui-Alert--light__background-color), - ), - dark: ( - color: var(--rui-Alert--dark__color), - foreground-color: var(--rui-Alert--dark__foreground-color), - background-color: var(--rui-Alert--dark__background-color), - ), -); diff --git a/src/components/Alert/_tools.scss b/src/components/Alert/_tools.scss deleted file mode 100644 index 0608c962..00000000 --- a/src/components/Alert/_tools.scss +++ /dev/null @@ -1,10 +0,0 @@ -@use "sass:map"; -@use "theme"; - -@mixin color($color) { - $color-variant-properties: map.get(theme.$colors, $color); - - --rui-local-color: #{map.get($color-variant-properties, color)}; - --rui-local-foreground-color: #{map.get($color-variant-properties, foreground-color)}; - --rui-local-background-color: #{map.get($color-variant-properties, background-color)}; -} diff --git a/src/components/Badge/Badge.module.scss b/src/components/Badge/Badge.module.scss index 7e57d903..d63d1ec6 100644 --- a/src/components/Badge/Badge.module.scss +++ b/src/components/Badge/Badge.module.scss @@ -28,7 +28,7 @@ } @each $color in settings.$colors { - @include collections.generate-properties( + @include collections.generate-class( $prefix: "rui-", $component-name: "Badge", $modifier-name: "priority", @@ -51,7 +51,7 @@ } @each $color in settings.$colors { - @include collections.generate-properties( + @include collections.generate-class( $prefix: "rui-", $component-name: "Badge", $modifier-name: "priority", diff --git a/src/components/Button/Button.jsx b/src/components/Button/Button.jsx index 3fa732fc..c934f13e 100644 --- a/src/components/Button/Button.jsx +++ b/src/components/Button/Button.jsx @@ -133,7 +133,8 @@ Button.propTypes = { */ block: PropTypes.bool, /** - * [Color variant](/docs/foundation/colors#component-colors) to clarify importance and meaning of the button. + * Color variant to clarify importance and meaning of the alert. Implements + * [Action, Feedback and Neutral color collections](/docs/foundation/collections#colors). */ color: PropTypes.oneOf( ['primary', 'secondary', 'selected', 'success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark'], diff --git a/src/components/Button/Button.module.scss b/src/components/Button/Button.module.scss index 91d5b61f..7eb9b06b 100644 --- a/src/components/Button/Button.module.scss +++ b/src/components/Button/Button.module.scss @@ -1,2 +1,183 @@ -@use "base"; -@use "priorities"; +// 1. ButtonGroup gap is implemented using the `margin` property so the buttons can overlap and reduce duplicate +// borders. + +@use "sass:map"; +@use "../../styles/tools/breakpoint"; +@use "../../styles/tools/collections"; +@use "settings"; +@use "theme"; +@use "tools"; + +@layer components.button { + .root { + @include tools.button(); + } + + .label { + display: block; + } + + .beforeLabel, + .afterLabel, + .startCorner, + .endCorner, + .feedbackIcon { + display: flex; + align-items: baseline; + justify-content: center; + } + + .startCorner, + .endCorner { + position: absolute; + top: -0.35rem; + z-index: 2; + } + + .startCorner { + left: 0; + margin-left: -0.35rem; + } + + .endCorner { + right: 0; + margin-right: -0.35rem; + } + + .feedbackIcon { + position: absolute; + inset: 0; + z-index: 1; + align-items: center; + } + + .isRootSizeSmall { + @include tools.button-size(small); + } + + .isRootSizeMedium { + @include tools.button-size(medium); + } + + .isRootSizeLarge { + @include tools.button-size(large); + } + + .isRootBlock { + width: 100%; + } + + .hasRootFeedback:disabled { + opacity: theme.$feedback-opacity; + cursor: theme.$feedback-cursor; + } + + .hasRootFeedback .label, + .hasRootFeedback .beforeLabel, + .hasRootFeedback .afterLabel { + color: transparent; + } + + .isRootInButtonGroup, + .isRootInInputGroup { + z-index: map.get(settings.$group-z-indexes, button); + + &:not(:first-child) { + border-start-start-radius: var(--rui-local-inner-border-radius); + border-end-start-radius: var(--rui-local-inner-border-radius); + } + + &:not(:last-child) { + border-start-end-radius: var(--rui-local-inner-border-radius); + border-end-end-radius: var(--rui-local-inner-border-radius); + } + } + + .isRootInButtonGroup:not(:first-child) { + margin-inline-start: var(--rui-local-gap); // 1. + + &::before { + content: ""; + position: absolute; + top: calc(-1 * #{theme.$border-width}); + bottom: calc(-1 * #{theme.$border-width}); + left: calc(-1 * #{theme.$border-width}); + z-index: map.get(settings.$group-z-indexes, separator); + border-left: var(--rui-local-separator-width) solid var(--rui-local-separator-color); + transform: translateX(calc(-0.5 * var(--rui-local-gap) - 50%)); + } + } + + .isRootInButtonGroup:focus, + .isRootInButtonGroup:not(:disabled):hover { + z-index: map.get(settings.$group-z-indexes, button-hover); + } + + .isRootInButtonGroup .startCorner, + .isRootInInputGroup .startCorner, + .isRootInButtonGroup .endCorner, + .isRootInInputGroup .endCorner { + z-index: map.get(settings.$group-z-indexes, button-overflowing-elements); + } + + .hasLabelHidden, + .hasLabelVisibleSm, + .hasLabelVisibleMd, + .hasLabelVisibleLg, + .hasLabelVisibleXl, + .hasLabelVisibleX2l, + .hasLabelVisibleX3l { + @include tools.hide-label(); + } + + .hasLabelVisibleSm { + @include breakpoint.up(sm) { + @include tools.show-label(); + } + } + + .hasLabelVisibleMd { + @include breakpoint.up(md) { + @include tools.show-label(); + } + } + + .hasLabelVisibleLg { + @include breakpoint.up(lg) { + @include tools.show-label(); + } + } + + .hasLabelVisibleXl { + @include breakpoint.up(xl) { + @include tools.show-label(); + } + } + + .hasLabelVisibleX2l { + @include breakpoint.up(x2l) { + @include tools.show-label(); + } + } + + .hasLabelVisibleX3l { + @include breakpoint.up(x3l) { + @include tools.show-label(); + } + } + + @each $priority in map.keys(settings.$themeable-properties) { + @each $color in settings.$colors { + @include collections.generate-class( + $prefix: "rui-", + $component-name: "Button", + $modifier-name: "priority", + $modifier-value: $priority, + $variant-name: "color", + $variant-value: $color, + $generate-interaction-states: true, + $properties: map.get(settings.$themeable-properties, $priority), + ); + } + } +} diff --git a/src/components/Button/README.md b/src/components/Button/README.md index 7e43d509..be732e6a 100644 --- a/src/components/Button/README.md +++ b/src/components/Button/README.md @@ -40,8 +40,9 @@ lowest: 2. outline 3. flat -All priorities come in supported -[component colors](/docs/foundation/colors#component-colors). +All priorities are available in colors from supported +[color collections](/docs/foundation/collections#colors). +Check [API](#api) to see which collections are supported. ### Filled @@ -257,7 +258,7 @@ Disabled state makes the action unavailable. When user's action triggers an asynchronous process on background, the button's feedback state (not to be mistaken with -[feedback colors](/docs/foundation/colors#component-colors)) can be indicated by +[feedback colors](/docs/foundation/colors#feedback-colors)) can be indicated by showing an icon. The icon replaces button's label while retaining original dimensions of the button. Buttons in feedback state are automatically disabled to prevent unwanted interaction. @@ -431,9 +432,10 @@ Where: - `` is one of `filled`, `outline`, or `flat` (see [Priorities](#priorities) and [API](#api)), -- `` is one of supported - [component colors](/docs/foundation/colors#component-colors) - (see color variants of [each priority](#priorities) and [API](#api)), +- `` is a value from supported + [color collections](/docs/foundation/collections#colors) + (check color variants of [each priority](#priorities) and [API](#api) to see + which collections are supported), - `` is one of `default`, `hover`, `active`, or `disabled` (the last one being optional), - `` is one of: diff --git a/src/components/Button/_base.scss b/src/components/Button/_base.scss deleted file mode 100644 index 7c2648ac..00000000 --- a/src/components/Button/_base.scss +++ /dev/null @@ -1,156 +0,0 @@ -// 1. ButtonGroup gap is implemented using the `margin` property so the buttons can overlap and reduce duplicate -// borders. - -@use "sass:map"; -@use "../../styles/tools/breakpoint"; -@use "settings"; -@use "theme"; -@use "tools"; - -@layer components.button { - .root { - @include tools.button(); - } - - .label { - display: block; - } - - .beforeLabel, - .afterLabel, - .startCorner, - .endCorner, - .feedbackIcon { - display: flex; - align-items: baseline; - justify-content: center; - } - - .startCorner, - .endCorner { - position: absolute; - top: -0.35rem; - z-index: 2; - } - - .startCorner { - left: 0; - margin-left: -0.35rem; - } - - .endCorner { - right: 0; - margin-right: -0.35rem; - } - - .feedbackIcon { - position: absolute; - inset: 0; - z-index: 1; - align-items: center; - } - - .isRootSizeSmall { - @include tools.button-size(small); - } - - .isRootSizeMedium { - @include tools.button-size(medium); - } - - .isRootSizeLarge { - @include tools.button-size(large); - } - - .isRootBlock { - width: 100%; - } - - .hasRootFeedback:disabled { - opacity: theme.$feedback-opacity; - cursor: theme.$feedback-cursor; - } - - .hasRootFeedback .label, - .hasRootFeedback .beforeLabel, - .hasRootFeedback .afterLabel { - color: transparent; - } - - .isRootInButtonGroup, - .isRootInInputGroup { - z-index: map.get(settings.$group-z-indexes, button); - - &:not(:first-child) { - border-start-start-radius: var(--rui-local-inner-border-radius); - border-end-start-radius: var(--rui-local-inner-border-radius); - } - - &:not(:last-child) { - border-start-end-radius: var(--rui-local-inner-border-radius); - border-end-end-radius: var(--rui-local-inner-border-radius); - } - } - - .isRootInButtonGroup:not(:first-child) { - margin-inline-start: var(--rui-local-gap); // 1. - } - - .isRootInButtonGroup:focus, - .isRootInButtonGroup:not(:disabled):hover { - z-index: map.get(settings.$group-z-indexes, button-hover); - } - - .isRootInButtonGroup .startCorner, - .isRootInInputGroup .startCorner, - .isRootInButtonGroup .endCorner, - .isRootInInputGroup .endCorner { - z-index: map.get(settings.$group-z-indexes, button-overflowing-elements); - } - - .hasLabelHidden, - .hasLabelVisibleSm, - .hasLabelVisibleMd, - .hasLabelVisibleLg, - .hasLabelVisibleXl, - .hasLabelVisibleX2l, - .hasLabelVisibleX3l { - @include tools.hide-label(); - } - - .hasLabelVisibleSm { - @include breakpoint.up(sm) { - @include tools.show-label(); - } - } - - .hasLabelVisibleMd { - @include breakpoint.up(md) { - @include tools.show-label(); - } - } - - .hasLabelVisibleLg { - @include breakpoint.up(lg) { - @include tools.show-label(); - } - } - - .hasLabelVisibleXl { - @include breakpoint.up(xl) { - @include tools.show-label(); - } - } - - .hasLabelVisibleX2l { - @include breakpoint.up(x2l) { - @include tools.show-label(); - } - } - - .hasLabelVisibleX3l { - @include breakpoint.up(x3l) { - @include tools.show-label(); - } - } -} diff --git a/src/components/Button/_priorities.scss b/src/components/Button/_priorities.scss deleted file mode 100644 index eb94ce6a..00000000 --- a/src/components/Button/_priorities.scss +++ /dev/null @@ -1,149 +0,0 @@ -@use "sass:map"; -@use "settings"; -@use "theme"; -@use "tools"; - -@layer components.button { - .isRootPriorityFilled.isRootColorPrimary { - @include tools.button-color(filled, primary); - } - - .isRootPriorityFilled.isRootColorSecondary { - @include tools.button-color(filled, secondary); - } - - .isRootPriorityFilled.isRootColorSelected { - @include tools.button-color(filled, selected); - } - - .isRootPriorityFilled.isRootColorSuccess { - @include tools.button-color(filled, success); - } - - .isRootPriorityFilled.isRootColorWarning { - @include tools.button-color(filled, warning); - } - - .isRootPriorityFilled.isRootColorDanger { - @include tools.button-color(filled, danger); - } - - .isRootPriorityFilled.isRootColorHelp { - @include tools.button-color(filled, help); - } - - .isRootPriorityFilled.isRootColorInfo { - @include tools.button-color(filled, info); - } - - .isRootPriorityFilled.isRootColorNote { - @include tools.button-color(filled, note); - } - - .isRootPriorityFilled.isRootColorLight { - @include tools.button-color(filled, light); - } - - .isRootPriorityFilled.isRootColorDark { - @include tools.button-color(filled, dark); - } - - .isRootPriorityOutline.isRootColorPrimary { - @include tools.button-color(outline, primary); - } - - .isRootPriorityOutline.isRootColorSecondary { - @include tools.button-color(outline, secondary); - } - - .isRootPriorityOutline.isRootColorSelected { - @include tools.button-color(outline, selected); - } - - .isRootPriorityOutline.isRootColorSuccess { - @include tools.button-color(outline, success); - } - - .isRootPriorityOutline.isRootColorWarning { - @include tools.button-color(outline, warning); - } - - .isRootPriorityOutline.isRootColorDanger { - @include tools.button-color(outline, danger); - } - - .isRootPriorityOutline.isRootColorHelp { - @include tools.button-color(outline, help); - } - - .isRootPriorityOutline.isRootColorInfo { - @include tools.button-color(outline, info); - } - - .isRootPriorityOutline.isRootColorNote { - @include tools.button-color(outline, note); - } - - .isRootPriorityOutline.isRootColorLight { - @include tools.button-color(outline, light); - } - - .isRootPriorityOutline.isRootColorDark { - @include tools.button-color(outline, dark); - } - - .isRootPriorityFlat.isRootColorPrimary { - @include tools.button-color(flat, primary); - } - - .isRootPriorityFlat.isRootColorSecondary { - @include tools.button-color(flat, secondary); - } - - .isRootPriorityFlat.isRootColorSelected { - @include tools.button-color(flat, selected); - } - - .isRootPriorityFlat.isRootColorSuccess { - @include tools.button-color(flat, success); - } - - .isRootPriorityFlat.isRootColorWarning { - @include tools.button-color(flat, warning); - } - - .isRootPriorityFlat.isRootColorDanger { - @include tools.button-color(flat, danger); - } - - .isRootPriorityFlat.isRootColorHelp { - @include tools.button-color(flat, help); - } - - .isRootPriorityFlat.isRootColorInfo { - @include tools.button-color(flat, info); - } - - .isRootPriorityFlat.isRootColorNote { - @include tools.button-color(flat, note); - } - - .isRootPriorityFlat.isRootColorLight { - @include tools.button-color(flat, light); - } - - .isRootPriorityFlat.isRootColorDark { - @include tools.button-color(flat, dark); - } - - .isRootInButtonGroup:not(:first-child)::before { - content: ""; - position: absolute; - top: calc(-1 * #{theme.$border-width}); - bottom: calc(-1 * #{theme.$border-width}); - left: calc(-1 * #{theme.$border-width}); - z-index: map.get(settings.$group-z-indexes, separator); - border-left: var(--rui-local-separator-width) solid var(--rui-local-separator-color); - transform: translateX(calc(-0.5 * var(--rui-local-gap) - 50%)); - } -} diff --git a/src/components/Button/_settings.scss b/src/components/Button/_settings.scss index 9065491f..71f3908b 100644 --- a/src/components/Button/_settings.scss +++ b/src/components/Button/_settings.scss @@ -1,3 +1,5 @@ +@use "sass:list"; +@use "../../styles/settings/collections"; @use "../../styles/theme/typography"; @use "../../styles/tools/spacing"; @@ -12,9 +14,12 @@ $group-z-indexes: ( button-overflowing-elements: 3, ); -$themeable-variants: (primary, secondary, selected, success, warning, danger, help, info, note, light, dark); -$themeable-states: (default, hover, active, disabled); -$themeable-priority-properties: ( +$colors: + list.join( + list.join(collections.$action-colors, collections.$feedback-colors), + collections.$neutral-colors + ); +$themeable-properties: ( filled: (color, border-color, background, box-shadow), outline: (color, border-color, background), flat: (color, background), diff --git a/src/components/Button/_theme.scss b/src/components/Button/_theme.scss index ec62d574..c5814f95 100644 --- a/src/components/Button/_theme.scss +++ b/src/components/Button/_theme.scss @@ -1,6 +1,3 @@ -// Priority and variant specific theme options are obtained dynamically because there is way too -// many of them to maintain manually. See `_tools.scss` for details. - $font-weight: var(--rui-Button__font-weight); $letter-spacing: var(--rui-Button__letter-spacing); $text-transform: var(--rui-Button__text-transform); diff --git a/src/components/Button/_tools.scss b/src/components/Button/_tools.scss index 4d193257..70796fab 100644 --- a/src/components/Button/_tools.scss +++ b/src/components/Button/_tools.scss @@ -1,71 +1,23 @@ -// 1. Due to complexity, it's easier and safer to resolve variant custom properties dynamically with -// a mixin. This way it's also easier to make theming adjustments to all supported modifications -// in one place. This applies to the following button priorities: +// 1. Get ready for position of corner elements and group separator. // -// - filled -// - outline -// - flat +// 2. Icon buttons should appear as squares, that's why width and height is equal here. // -// 2. Disabled state theming is optional so `default` theme options are used if no `disabled` -// styling is provided for specific variant. -// -// 3. Get ready for position of corner elements and group separator. -// -// 4. Icon buttons should appear as squares, that's why width and height is equal here. -// -// 5. Use original padding to restore square buttons back to their size. +// 3. Use original padding to restore square buttons back to their size. @use "sass:list"; @use "sass:map"; -@use "sass:math"; @use "../../styles/tools/accessibility"; -@use "../../styles/tools/breakpoint"; @use "../../styles/tools/transition"; @use "settings"; @use "theme"; -// 1. -@mixin _get-themeable-properties($priority, $color, $state) { - @if not list.index(map.keys(settings.$themeable-priority-properties), $priority) { - @error "Invalid priority specified! #{$priority} doesn't exist. " - + "Choose one of #{settings.$themeable-priority-properties}."; - } - - @if not list.index(settings.$themeable-variants, $color) { - @error "Invalid variant specified! #{$color} doesn't exist. " - + "Choose one of #{settings.$themeable-variants}."; - } - - @if not list.index(settings.$themeable-states, $state) { - @error "Invalid state specified! #{$state} doesn't exist. " - + "Choose one of #{settings.$themeable-states}."; - } - - $properties: map.get(settings.$themeable-priority-properties, $priority); - - // 2. - @if $state == "disabled" { - @each $property in $properties { - --rui-local-#{$property}: - var( - --rui-Button--#{$priority}--#{$color}--#{$state}__#{$property}, - var(--rui-Button--#{$priority}--#{$color}--default__#{$property}) - ); - } - } @else { - @each $property in $properties { - --rui-local-#{$property}: var(--rui-Button--#{$priority}--#{$color}--#{$state}__#{$property}); - } - } -} - -// 4. +// 2. @mixin _button-square() { --rui-local-padding: 0; --rui-local-width: var(--rui-local-height); } -// 5. +// 3. @mixin _button-size-restore() { --rui-local-padding: var(--rui-local-padding-original); --rui-local-width: unset; @@ -74,7 +26,7 @@ @mixin button() { @include transition.add((opacity, color, border-color, background-color, box-shadow)); - position: relative; // 3. + position: relative; // 1. display: inline-flex; column-gap: settings.$icon-spacing; align-items: center; @@ -112,26 +64,10 @@ --rui-local-height: #{map.get($properties, height)}; --rui-local-padding: #{map.get($properties, padding-y)} #{map.get($properties, padding-x)}; - --rui-local-padding-original: #{map.get($properties, padding-y)} #{map.get($properties, padding-x)}; // 5. + --rui-local-padding-original: #{map.get($properties, padding-y)} #{map.get($properties, padding-x)}; // 3. --rui-local-font-size: #{map.get($properties, font-size)}; } -@mixin button-color($priority, $color) { - @include _get-themeable-properties($priority, $color, default); - - &:disabled { - @include _get-themeable-properties($priority, $color, disabled); - } - - &:not(:disabled):hover { - @include _get-themeable-properties($priority, $color, hover); - } - - &:not(:disabled):active { - @include _get-themeable-properties($priority, $color, active); - } -} - @mixin hide-label() { @include _button-square(); diff --git a/src/components/Card/Card.jsx b/src/components/Card/Card.jsx index 074ade48..8616ee55 100644 --- a/src/components/Card/Card.jsx +++ b/src/components/Card/Card.jsx @@ -44,7 +44,8 @@ Card.propTypes = { */ children: PropTypes.node.isRequired, /** - * [Color variant](/docs/foundation/colors#component-colors) to clarify importance and meaning of the card. + * Color to clarify importance and meaning of the card. Implements + * [Feedback and Neutral color collections](/docs/foundation/collections#colors). */ color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark']), /** diff --git a/src/components/Card/Card.module.scss b/src/components/Card/Card.module.scss index 046c9be3..03befec5 100644 --- a/src/components/Card/Card.module.scss +++ b/src/components/Card/Card.module.scss @@ -1,7 +1,8 @@ // 1. Retain equal card widths in flex and grid layouts independently on their content. +@use "../../styles/tools/collections"; +@use "settings"; @use "theme"; -@use "tools"; @layer components.card { .root { @@ -33,36 +34,14 @@ box-shadow: theme.$raised-box-shadow; } - .isRootColorSuccess { - @include tools.color(success); - } - - .isRootColorWarning { - @include tools.color(warning); - } - - .isRootColorDanger { - @include tools.color(danger); - } - - .isRootColorHelp { - @include tools.color(help); - } - - .isRootColorInfo { - @include tools.color(info); - } - - .isRootColorNote { - @include tools.color(note); - } - - .isRootColorLight { - @include tools.color(light); - } - - .isRootColorDark { - @include tools.color(dark); + @each $color in settings.$colors { + @include collections.generate-class( + $prefix: "rui-", + $component-name: "Card", + $variant-name: "color", + $variant-value: $color, + $properties: settings.$themeable-properties, + ); } .isRootDisabled { diff --git a/src/components/Card/README.md b/src/components/Card/README.md index b77aac76..1604f519 100644 --- a/src/components/Card/README.md +++ b/src/components/Card/README.md @@ -147,9 +147,8 @@ for card content. ## Color Variants -Card supports all -[component colors](/docs/foundation/colors#component-colors) to cover different needs -of your app. +To cover all possible needs of your project, Card is available in colors from +[Feedback and Neutral color collections](/docs/foundation/collections#colors). ```docoff-react-preview @@ -307,9 +306,10 @@ looks as follows: Where: -- `` is one of supported - [component colors](/docs/foundation/colors#component-colors) - (see [color variants](#color-variants) and [API](#api)), +- `` is a value from supported + [color collections](/docs/foundation/collections#colors) + (check [color variants](#color-variants) and [API](#api) to see which + collections are supported), - `` is one of `color` (color of text), `border-color`, or `background-color`. diff --git a/src/components/Card/_settings.scss b/src/components/Card/_settings.scss new file mode 100644 index 00000000..c055c8b4 --- /dev/null +++ b/src/components/Card/_settings.scss @@ -0,0 +1,5 @@ +@use "sass:list"; +@use "../../styles/settings/collections"; + +$colors: list.join(collections.$feedback-colors, collections.$neutral-colors); +$themeable-properties: color, border-color, background-color; diff --git a/src/components/Card/_theme.scss b/src/components/Card/_theme.scss index a5f08b1c..819da99f 100644 --- a/src/components/Card/_theme.scss +++ b/src/components/Card/_theme.scss @@ -7,46 +7,3 @@ $raised-box-shadow: var(--rui-Card--raised__box-shadow); $disabled-background-color: var(--rui-Card--disabled__background-color); $disabled-opacity: var(--rui-Card--disabled__opacity); - -$colors: ( - success: ( - color: var(--rui-Card--success__color), - border-color: var(--rui-Card--success__border-color), - background-color: var(--rui-Card--success__background-color), - ), - warning: ( - color: var(--rui-Card--warning__color), - border-color: var(--rui-Card--warning__border-color), - background-color: var(--rui-Card--warning__background-color), - ), - danger: ( - color: var(--rui-Card--danger__color), - border-color: var(--rui-Card--danger__border-color), - background-color: var(--rui-Card--danger__background-color), - ), - info: ( - color: var(--rui-Card--info__color), - border-color: var(--rui-Card--info__border-color), - background-color: var(--rui-Card--info__background-color), - ), - help: ( - color: var(--rui-Card--help__color), - border-color: var(--rui-Card--help__border-color), - background-color: var(--rui-Card--help__background-color), - ), - note: ( - color: var(--rui-Card--note__color), - border-color: var(--rui-Card--note__border-color), - background-color: var(--rui-Card--note__background-color), - ), - light: ( - color: var(--rui-Card--light__color), - border-color: var(--rui-Card--light__border-color), - background-color: var(--rui-Card--light__background-color), - ), - dark: ( - color: var(--rui-Card--dark__color), - border-color: var(--rui-Card--dark__border-color), - background-color: var(--rui-Card--dark__background-color), - ), -); diff --git a/src/components/Card/_tools.scss b/src/components/Card/_tools.scss deleted file mode 100644 index ee155fc9..00000000 --- a/src/components/Card/_tools.scss +++ /dev/null @@ -1,10 +0,0 @@ -@use "sass:map"; -@use "theme"; - -@mixin color($color) { - $color-variant-properties: map.get(theme.$colors, $color); - - --rui-local-color: #{map.get($color-variant-properties, color)}; - --rui-local-border-color: #{map.get($color-variant-properties, border-color)}; - --rui-local-background-color: #{map.get($color-variant-properties, background-color)}; -} diff --git a/src/styles/tools/_collections.scss b/src/styles/tools/_collections.scss index 2d557398..f506a08f 100644 --- a/src/styles/tools/_collections.scss +++ b/src/styles/tools/_collections.scss @@ -1,36 +1,191 @@ @use "string" as rui-string; +// Mixin to generate CSS custom properties for a set of visual properties. +// +// 1. Generates a CSS custom property for each property in the `$properties` list. +// 2. Theming of the disabled state is optional, so the `default` theme options are used (via CSS custom property +// fallback) if no `disabled` styling for the specific variant is provided by user. +// +// @param {String} $prefix - The prefix for the CSS custom properties. +// @param {String} $component-name - The name of the component. +// @param {String} $modifier-value - The value of the modifier. +// @param {String} $variant-value - The value of the variant. +// @param {String} $interaction-state - The interaction state. +// @param {List} $properties - The list of properties to generate CSS custom properties for. +// +// Example: +// +// @include generate-properties( +// $prefix: "rui-", +// $component-name: "Card", +// $variant-name: "color", +// $variant-value: "success", +// $properties: color, border-color, background-color, +// ); +// +// … will output: +// +// --rui-local-color: var(--rui-Card--success__color); +// --rui-local-border-color: var(--rui-Card--success__border-color); +// --rui-local-background-color: var(--rui-Card--success__background-color); + @mixin generate-properties( + $prefix, + $component-name, + $modifier-value: null, + $variant-value, + $interaction-state: null, + $properties, +) { + @each $property in $properties { + $modifier: if($modifier-value, "--" + $modifier-value, ""); + $state: if($interaction-state, "--" + $interaction-state, ""); + + // 1. + --#{$prefix}local-#{$property}: + var( + #{ + "--" + + $prefix + + $component-name + + $modifier + + "--" + + $variant-value + + $state + + "__" + + $property + } + #{if( + $interaction-state and $interaction-state == "disabled", + ", var(--" + + $prefix + + $component-name + + $modifier + + "--" + + $variant-value + + "--default__" + + $property + + ")", + "" + )} + ); // 2. + } +} + +// Mixin to generate CSS classes for a component variant. +// +// @param {String} $prefix - The prefix for the CSS custom properties. +// @param {String} $component-name - The name of the component. +// @param {String} $modifier-name - Optional name of the class name modifier. +// @param {String} $modifier-name - Optional value of the class name modifier. +// @param {String} $variant-name - The name of the variant. +// @param {String} $variant-value - The value of the variant. +// @param {Boolean} $generate-interaction-states - Whether to generate interaction states (disabled, hover, active). +// @param {List} $properties - The list of properties to generate CSS custom properties for. +// +// Examples: +// +// @include collections.generate-class( +// $prefix: "rui-", +// $component-name: "Card", +// $variant-name: "color", +// $variant-value: "success", +// $properties: color, border-color, background-color, +// ); +// +// … will output: +// +// .isRootColorSuccess { +// --rui-local-color: var(--rui-Card--success__color); +// --rui-local-border-color: var(--rui-Card--success__border-color); +// --rui-local-background-color: var(--rui-Card--success__background-color); +// } +// +// @include collections.generate-class( +// $prefix: "rui-", +// $component-name: "Button", +// $modifier-name: "priority", +// $modifier-value: "flat", +// $variant-name: "color", +// $variant-value: "success", +// $generate-interaction-states: true, +// $properties: color, background, +// ); +// +// … will output: +// +// .isRootPriorityFlat.isRootColorSuccess { +// --rui-local-color: var(--rui-Button--flat--success--default__color); +// --rui-local-background: var(--rui-Button--flat--success--default__background); +// } +// .isRootPriorityFlat.isRootColorSuccess:disabled { +// --rui-local-color: +// var( +// --rui-Button--flat--success--disabled__color, +// var(--rui-Button--flat--success--default__color) +// ); +// --rui-local-background: +// var( +// --rui-Button--flat--success--disabled__background, +// var(--rui-Button--flat--success--default__background) +// ); +// } +// .isRootPriorityFlat.isRootColorSuccess:not(:disabled):hover { +// --rui-local-color: var(--rui-Button--flat--success--hover__color); +// --rui-local-background: var(--rui-Button--flat--success--hover__background); +// } +// .isRootPriorityFlat.isRootColorSuccess:not(:disabled):active { +// --rui-local-color: var(--rui-Button--flat--success--active__color); +// --rui-local-background: var(--rui-Button--flat--success--active__background); +// } + +@mixin generate-class( $prefix, $component-name, $modifier-name: null, $modifier-value: null, $variant-name, $variant-value, + $generate-interaction-states: false, $properties, ) { - $combined-class-name: + $modifier-class-name: if( $modifier-name and $modifier-value, ".isRoot#{rui-string.capitalize($modifier-name)}#{rui-string.capitalize($modifier-value)}", "" ); + $variant-class-name: ".isRoot#{rui-string.capitalize($variant-name)}#{rui-string.capitalize($variant-value)}"; + + #{$modifier-class-name}#{$variant-class-name} { + @if $generate-interaction-states { + $interaction-state-selector-map: ( + default: "&", + disabled: "&:disabled", + hover: "&:not(:disabled):hover", + active: "&:not(:disabled):active", + ); - #{$combined-class-name}.isRoot#{rui-string.capitalize($variant-name)}#{rui-string.capitalize($variant-value)} { - @each $property in $properties { - --#{$prefix}local-#{$property}: - var( - #{ - "--" - + $prefix - + $component-name - + if($modifier-value, "--" + $modifier-value, "") - + "--" - + $variant-value - + "__" - + $property - } - ); + @each $interaction-state, $interaction-state-selector in $interaction-state-selector-map { + #{$interaction-state-selector} { + @include generate-properties( + $prefix: $prefix, + $component-name: $component-name, + $modifier-value: $modifier-value, + $variant-value: $variant-value, + $interaction-state: $interaction-state, + $properties: $properties, + ); + } + } + } @else { + @include generate-properties( + $prefix: $prefix, + $component-name: $component-name, + $modifier-value: $modifier-value, + $variant-value: $variant-value, + $properties: $properties, + ); } } }