diff --git a/packages/mdc-button/_mixins.scss b/packages/mdc-button/_mixins.scss index a4a955e0a19..893f6c5adec 100644 --- a/packages/mdc-button/_mixins.scss +++ b/packages/mdc-button/_mixins.scss @@ -61,7 +61,7 @@ $ripple-target: ".mdc-button__ripple"; // postcss-bem-linter: define button .mdc-button { @include base_($query); - @include shape-radius(small, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); @include container-fill-color(transparent, $query); @include disabled-container-fill-color(transparent, $query); // The icon CSS class overrides styles defined in the .material-icons CSS @@ -335,22 +335,32 @@ $ripple-target: ".mdc-button__ripple"; } } -@mixin shape-radius($radius, -$rtl-reflexive: false, -$density-scale: variables.$density-scale, -$query: feature-targeting-functions.all()) { - +@mixin shape-radius( + $radius, + $rtl-reflexive: false, + $density-scale: variables.$density-scale, + $query: feature-targeting-functions.all() +) { $height: density-functions.prop-value( $density-config: variables.$density-config, $density-scale: $density-scale, $property-name: height, ); - $resolved-radius: shape-functions.resolve-percentage-radius($height, $radius); - @include shape-mixins.radius($resolved-radius, $rtl-reflexive, $query: $query); + @include shape-mixins.radius( + $radius, + $rtl-reflexive, + $component-height: $height, + $query: $query + ); #{$ripple-target} { - @include shape-mixins.radius($resolved-radius, $rtl-reflexive, $query: $query); + @include shape-mixins.radius( + $radius, + $rtl-reflexive, + $component-height: $height, + $query: $query + ); } } diff --git a/packages/mdc-card/_mixins.scss b/packages/mdc-card/_mixins.scss index 98f20fb231e..c38200fcc7c 100644 --- a/packages/mdc-card/_mixins.scss +++ b/packages/mdc-card/_mixins.scss @@ -51,7 +51,7 @@ @include elevation-mixins.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE .mdc-card { - @include shape-radius(medium, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); @include fill-color(surface, $query); @include elevation-mixins.overlay-surface-position($query: $query); @include elevation-mixins.overlay-dimensions(100%, $query: $query); diff --git a/packages/mdc-card/_variables.scss b/packages/mdc-card/_variables.scss index 6a3cf107a48..256a00ef9b9 100644 --- a/packages/mdc-card/_variables.scss +++ b/packages/mdc-card/_variables.scss @@ -27,3 +27,4 @@ $action-icon-color: rgba(variables.prop-value(on-surface), variables.text-emphasis(medium)) !default; $outline-color: color.mix(variables.prop-value(on-surface), variables.prop-value(surface), 12%) !default; $outline-width: 1px !default; +$shape-radius: medium !default; diff --git a/packages/mdc-chips/_mixins.scss b/packages/mdc-chips/_mixins.scss index 238ba430975..e3cc379728d 100644 --- a/packages/mdc-chips/_mixins.scss +++ b/packages/mdc-chips/_mixins.scss @@ -63,7 +63,7 @@ $ripple-target: ".mdc-chip__ripple"; @include elevation-mixins.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE .mdc-chip { - @include shape-radius(50%, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); @include fill-color(variables.$fill-color-default, $query: $query); @include ink-color-without-ripple_(variables.$ink-color-default, $query: $query); @include typography-mixins.typography(body2, $query: $query); @@ -345,14 +345,16 @@ $ripple-target: ".mdc-chip__ripple"; ); @include shape-mixins.radius( - shape-functions.resolve-percentage-radius($height, $radius), + $radius, $rtl-reflexive, + $component-height: $height, $query: $query); #{$ripple-target} { @include shape-mixins.radius( - shape-functions.resolve-percentage-radius($height, $radius), + $radius, $rtl-reflexive, + $component-height: $height, $query: $query ); } diff --git a/packages/mdc-chips/_variables.scss b/packages/mdc-chips/_variables.scss index cb2b332e3f9..b7d03b6b3a7 100644 --- a/packages/mdc-chips/_variables.scss +++ b/packages/mdc-chips/_variables.scss @@ -29,6 +29,7 @@ $fill-color-default: color.mix(theme-variables.prop-value(on-surface), theme-var $ink-color-default: rgba(theme-variables.prop-value(on-surface), .87) !default; $horizontal-padding: 12px !default; $height: 32px !default; +$shape-radius: 50% !default; $minimum-height: 24px !default; $maximum-height: $height !default; diff --git a/packages/mdc-dialog/_mixins.scss b/packages/mdc-dialog/_mixins.scss index 1024cb0070f..b6b0719e52c 100644 --- a/packages/mdc-dialog/_mixins.scss +++ b/packages/mdc-dialog/_mixins.scss @@ -67,7 +67,7 @@ @include min-width(variables.$min-width, $query: $query); @include max-width(variables.$max-width, variables.$margin, $query: $query); @include max-height(null, variables.$margin, $query: $query); - @include shape-radius(medium, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); @include feature-targeting-mixins.targets($feat-structure) { // Use `display: none` instead of `visibility: hidden` to avoid recalculating layout when the dialog is closed. diff --git a/packages/mdc-dialog/_variables.scss b/packages/mdc-dialog/_variables.scss index 685005db680..e49ac0879e7 100644 --- a/packages/mdc-dialog/_variables.scss +++ b/packages/mdc-dialog/_variables.scss @@ -33,6 +33,7 @@ $scroll-divider-opacity: .12 !default; $min-width: 280px !default; $max-width: 560px !default; $margin: 16px !default; +$shape-radius: medium !default; $title-bottom-padding: 9px !default; $actions-padding: 8px !default; diff --git a/packages/mdc-drawer/_mixins.scss b/packages/mdc-drawer/_mixins.scss index 80f243a4840..aa0c1b0cbf0 100644 --- a/packages/mdc-drawer/_mixins.scss +++ b/packages/mdc-drawer/_mixins.scss @@ -21,6 +21,7 @@ // @use "sass:list"; +@use "sass:meta"; @use "@material/feature-targeting/functions"; @use "@material/feature-targeting/mixins" as feature-targeting-mixins; @use "@material/animation/functions" as functions2; @@ -54,8 +55,8 @@ @include item-text-ink-color(variables.$item-inactive-ink-color, $query: $query); @include item-activated-icon-ink-color(variables.$item-activated-ink-color, $query: $query); @include item-activated-text-ink-color(variables.$item-activated-ink-color, $query: $query); - @include shape-radius(large, $query: $query); - @include item-shape-radius(4px, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); + @include item-shape-radius(variables.$item-shape-radius, $query: $query); @include z-index(variables.$z-index, $query: $query); @include width(variables.$width, $query: $query); @@ -281,9 +282,11 @@ } @mixin shape-radius($radius, $query: functions.all()) { - @if list.length($radius) > 2 { + // Check type since $radius may be a custom property Map + $is-list: meta.type-of($radius) == "list"; + @if $is-list and list.length($radius) > 2 { @error "Invalid radius: '#{$radius}' component doesn't allow customizing all corners"; - } @else if list.length($radius) == 2 { + } @else if $is-list and list.length($radius) == 2 { $radius: 0 list.nth($radius, 1) list.nth($radius, 2) 0; } @else { $radius: 0 $radius $radius 0; diff --git a/packages/mdc-drawer/_variables.scss b/packages/mdc-drawer/_variables.scss index 6ea97a364e9..d0262462f05 100644 --- a/packages/mdc-drawer/_variables.scss +++ b/packages/mdc-drawer/_variables.scss @@ -43,6 +43,8 @@ $divider-opacity: .12 !default; $width: 256px !default; $list-item-spacing: 4px !default; $surface-padding: 16px !default; +$shape-radius: large !default; +$item-shape-radius: small !default; // Animations $animation-enter: 250ms !default; diff --git a/packages/mdc-fab/_mixins.scss b/packages/mdc-fab/_mixins.scss index 47f0c55a56f..a7a9439815f 100644 --- a/packages/mdc-fab/_mixins.scss +++ b/packages/mdc-fab/_mixins.scss @@ -213,6 +213,9 @@ $ripple-target: ".mdc-fab__ripple"; @mixin shape-radius($radius, $rtl-reflexive: false, $query: feature-targeting-functions.all()) { &:not(.mdc-fab--extended) { + // Do not specify $component-height for shape radius. FABs are circular, + // which means they can use percentage border radius without resolving to + // a component height. @include shape-mixins.radius($radius, $rtl-reflexive, $query: $query); #{$ripple-target} { @@ -222,16 +225,20 @@ $ripple-target: ".mdc-fab__ripple"; } @mixin extended-shape-radius($radius, $rtl-reflexive: false, $query: feature-targeting-functions.all()) { + // Extended FABs _do_ need a $component-height since they are not circular. + // Percentage radii must be resolved. @include shape-mixins.radius( - shape-functions.resolve-percentage-radius(variables.$extended-height, $radius), + $radius, $rtl-reflexive, + $component-height: variables.$extended-height, $query: $query ); #{$ripple-target} { @include shape-mixins.radius( - shape-functions.resolve-percentage-radius(variables.$extended-height, $radius), + $radius, $rtl-reflexive, + $component-height: variables.$extended-height, $query: $query ); } @@ -244,7 +251,7 @@ $icon-enter-duration_: 180ms; @include elevation-mixins.overlay-surface-position($query: $query); @include elevation-mixins.overlay-dimensions(100%, $query: $query); @include elevation-mixins.elevation(6, $query: $query); - @include shape-radius(50%, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); $feat-animation: feature-targeting-functions.create-target($query, animation); $feat-structure: feature-targeting-functions.create-target($query, structure); @@ -334,7 +341,7 @@ $icon-enter-duration_: 180ms; @mixin extended_($query: feature-targeting-functions.all()) { @include typography-mixins.typography(button, $query: $query); - @include extended-shape-radius(50%, $query: $query); + @include extended-shape-radius(variables.$shape-radius, $query: $query); @include extended-padding(variables.$extended-icon-padding, variables.$extended-label-padding, $query: $query); $feat-structure: feature-targeting-functions.create-target($query, structure); diff --git a/packages/mdc-fab/_variables.scss b/packages/mdc-fab/_variables.scss index b4e5f7afa0c..0e84992dfc8 100644 --- a/packages/mdc-fab/_variables.scss +++ b/packages/mdc-fab/_variables.scss @@ -25,3 +25,4 @@ $extended-label-padding: 20px !default; $height: 56px !default; $mini-height: 40px !default; $extended-height: 48px !default; +$shape-radius: 50% !default; diff --git a/packages/mdc-image-list/_mixins.scss b/packages/mdc-image-list/_mixins.scss index c0555a8c506..b7d96f5eee3 100644 --- a/packages/mdc-image-list/_mixins.scss +++ b/packages/mdc-image-list/_mixins.scss @@ -79,7 +79,7 @@ } @include aspect(1, $query: $query); - @include shape-radius(0, $query: $query); + @include shape-radius(variables.$shape-radius, $query: $query); .mdc-image-list__supporting { @include feature-targeting-mixins.targets($feat-color) { diff --git a/packages/mdc-image-list/_variables.scss b/packages/mdc-image-list/_variables.scss index b2bfd85bacc..1ff19b88ba3 100644 --- a/packages/mdc-image-list/_variables.scss +++ b/packages/mdc-image-list/_variables.scss @@ -24,3 +24,4 @@ $icon-size: 24px !default; $text-protection-background-color: rgba(0, 0, 0, .6) !default; $text-protection-height: 48px !default; $text-protection-horizontal-padding: 16px !default; +$shape-radius: 0 !default; diff --git a/packages/mdc-list/_mixins.scss b/packages/mdc-list/_mixins.scss index 8e947a80d53..a82f99c9945 100644 --- a/packages/mdc-list/_mixins.scss +++ b/packages/mdc-list/_mixins.scss @@ -398,10 +398,13 @@ $property-name: height, ); - $resolved-radius: shape-functions.resolve-percentage-radius($height, $radius); - .mdc-list-item { - @include shape-mixins.radius($resolved-radius, $rtl-reflexive, $query: $query); + @include shape-mixins.radius( + $radius, + $rtl-reflexive, + $component-height: $height, + $query: $query + ); } } diff --git a/packages/mdc-menu-surface/_mixins.scss b/packages/mdc-menu-surface/_mixins.scss index 4e10599a743..030634df74d 100644 --- a/packages/mdc-menu-surface/_mixins.scss +++ b/packages/mdc-menu-surface/_mixins.scss @@ -42,7 +42,7 @@ @include elevation-mixins.elevation($z-value: 8, $query: $query); @include fill-color(surface, $query); @include ink-color(on-surface, $query); - @include shape-radius(medium, false, $query); + @include shape-radius(variables.$shape-radius, false, $query); @include feature-targeting-mixins.targets($feat-structure) { @include rtl-mixins.reflexive-property(transform-origin, top left, top right); diff --git a/packages/mdc-menu-surface/_variables.scss b/packages/mdc-menu-surface/_variables.scss index cff1729451d..e3a1e14b794 100644 --- a/packages/mdc-menu-surface/_variables.scss +++ b/packages/mdc-menu-surface/_variables.scss @@ -25,3 +25,4 @@ $fade-out-duration: .075s !default; $scale-duration: .12s !default; $min-distance-from-edge: 32px !default; $z-index: 8 !default; // One above mdc-dialog +$shape-radius: medium !default; diff --git a/packages/mdc-shape/README.md b/packages/mdc-shape/README.md index 20017eae1b8..73644587dc6 100644 --- a/packages/mdc-shape/README.md +++ b/packages/mdc-shape/README.md @@ -38,7 +38,7 @@ npm install @material/shape ### Sass Variables -Components are categorized as small, medium and large in shape system. Overriding below sass variables applies shape (rounded) to respective categories. For example, overriding `$medium-radius` variable would apply shape to all components that belong to medium category. +Components are categorized as small, medium, and large in the Material shape system. Overriding the below Sass variables will change all components in their respective categories. Variable | Description --- | --- @@ -48,22 +48,32 @@ Variable | Description Please refer [Material Design guidelines: Shape](https://material.io/go/design-shape) to learn about how components are categorized. +**Note: Only rounded shape designs are currently supported.** + +### CSS Custom Properties + +CSS Custom Property | Description +--- | --- +`--mdc-shape-small` | Rounded shape radius size for small components. Default value `4px`. +`--mdc-shape-medium` | Rounded shape radius size for small components. Default value `4px`. +`--mdc-shape-large` | Rounded shape radius size for small components. Default value `0`. + +**Note: Do not use percentage values with custom properties, since they cannot be resolved by `shape.radius()` at runtime.** + ### Sass Mixins Mixin | Description --- | --- `radius($radius, $rtl-reflexive)` | Shape API used by all other components to apply radius to appropriate corners. `$radius` can be single value or list of up to 4 radius corner values. Set `$rtl-reflexive` to true to flip the radius in RTL case, `false` by default. -> Use `resolve-percentage-radius` sass function to resolve percentage unit value to absolute radius value. - ### Sass Functions Function | Description --- | --- +`resolve-radius($radius, $component-height)` | Returns the resolved radius value of a shape category - `large`, `medium`, or `small`. If $radius is not a category, this function returns the value itself if valid. Valid values are numbers or percentages. `$component-height` should be provided if `$radius` may be a percentage. `flip-radius($radius)` | Flips the radius values in RTL context. `$radius` is list of 2-4 corner values. -`resolve-percentage-radius($component-height, $radius)` | Calculates the absolute radius value based on its component height. Use this for fixed height components only. `mask-radius($radius, $masked-corners)` | Accepts radius number or list of 2-4 radius values and returns 4 value list with masked corners as mentioned in `$masked-corners`. -`prop-value($radius)` | Returns `$radius` value of shape category - `large`, `medium` or `small`. Otherwise, it returns the `$radius` itself if valid. `$radius` can be a single value or list of up to 4. +`unpack-radius($radius)` | Unpacks shorthand values for border-radius (i.e. lists of 1-3 values). If a list of 4 values is given, it is returned as-is. ### Additional Information @@ -74,10 +84,10 @@ Styles for applying shape to a fixed height component such as button looks like ```scss @use "@material/button"; -@include shape.radius(shape.resolve-percentage-radius(button.$height, $radius)); +@include shape.radius($radius, $component-height: button.$height); ``` -Where, `button.$height` is the height of standard button and `$radius` is the size of shape. `resolve-percentage-radius` function is used to resolve percentage unit value to absolute `$radius` value based on component height. +Where `button.$height` is the height of standard button and `$radius` is the size of the shape. `shape.radius()` will resolve any percentage unit value to an absolute radius value based on the component's height. #### Shapes for dynamic height components @@ -87,7 +97,7 @@ Styles for applying shapes to dynamic height component such as card looks like t @include shape.radius($radius); ``` -Where, `$radius` is absolute value only. +Where `$radius` is an absolute value only. #### Shapes for components on specific corners @@ -97,11 +107,11 @@ Styles for applying shapes for specific corners such as drawer looks like this: @include shape.radius(0 $radius $radius 0, $rtl-reflexive: true); ``` -Where, only top-right & bottom-right corners are customizable and it automatically flips radius values based on RTL context when `$rtl-reflexive` is set to true. +Where only top-right & bottom-right corners are customizable. `shape.radius()` will automatically flip radius values based on RTL context if `$rtl-reflexive` is set to true. #### Component theming -The styles for applying custom shape to button component instance looks like this: +The styles for applying custom shape to a button component looks like this: ```scss @use "@material/button"; @@ -111,6 +121,6 @@ The styles for applying custom shape to button component instance looks like thi } ``` -In this example, the above styles applies 50% (pill) shape to button. It can also be absolute value (e.g., `8px`); +In this example, the above style applies a 50% pill shape to the button. It could also be an absolute value (e.g., `8px`); -> You would indirectly use the Shape API through respective component's mixin which takes care of applying radius to applicable corners for all its variants. +> The Shape API is typically used indirectly through each respective component's mixin, which takes care of setting height and applying radius to applicable corners for all of its variants. diff --git a/packages/mdc-shape/_functions.scss b/packages/mdc-shape/_functions.scss index 8a1744fb534..ca0f737a501 100644 --- a/packages/mdc-shape/_functions.scss +++ b/packages/mdc-shape/_functions.scss @@ -24,6 +24,7 @@ @use "sass:map"; @use "sass:math"; @use "sass:meta"; +@use "@material/theme/custom-properties"; @use "./variables"; // @@ -52,6 +53,8 @@ } } +// +// @deprecated use `resolve-radius()` and provide $component-height. // // Resolves the percentage unit radius to appropriate absolute radius value based on component height. // Use this for fixed height components only. @@ -76,6 +79,75 @@ } } +/// Returns the resolved radius value of a shape category - `large`, `medium`, +/// or `small`. If $radius is not a category, this function returns the value +/// itself if valid. Valid values are numbers or percentages. +/// +/// If a percentage is provided, $component-height should be specified if the +/// width of the component does not match the height. +/// +/// $radius may be a single value or a list of 1 to 4 values. +/// +/// Examples: +/// +/// shape.resolve-radius(small) => (varname: --mdc-shape-small, fallback: 4px) +/// shape.resolve-radius((varname: --custom-shape, fallback: small)) => +/// (varname: --custom-shape, fallback: (varname: --mdc-shape-small, fallback: 4px)) +/// shape.resolve-radius(8px) => 8px +/// shape.resolve-radius(50%, $component-height: 36px) => 16px +/// +/// @param {String | Number | Map | List} $radius - the radius shape category or +/// radius value to resolve. May be a number, custom property Map, or a List +/// of those values. +/// @return {Number | Map | List} the resolved radius value. May be a number, +/// a custom property Map, or a List if $radius was a List. +@function resolve-radius($radius, $component-height: null) { + @if meta.type-of($radius) == "list" { + // $radius is a List + @if list.length($radius) > 4 or list.length($radius) < 1 { + @error "mdc-shape: Invalid radius: #{$radius}. Radius must be between 1 and 4 values."; + } + + $radii: (); + @each $corner in $radius { + $radii: list.append( + $radii, + resolve-radius($corner, $component-height: $component-height) + ); + } + + @return $radii; + } + + @if map.has-key(variables.$category-keywords, $radius) { + // $radius is a category value + @return resolve-radius( + map.get(variables.$category-keywords, $radius), + $component-height: $component-height + ); + } @else if custom-properties.is-custom-prop($radius) { + // $radius is a custom property Map + $fallback: resolve-radius( + custom-properties.get-fallback($radius, $shallow: true), + $component-height: $component-height + ); + @return custom-properties.set-fallback($radius, $fallback); + } @else { + // $radius is a value + @if meta.type-of($radius) != "number" { + @error "mdc-shape: Invalid radius: #{$radius}. Must be a number."; + } + + @if math.unit($radius) == "%" and meta.type-of($component-height) == "number" { + $radius: _resolve-radius-percentage($radius, $component-height); + } + + @return $radius; + } +} + +// +// @deprecated use `resolve-radius()` for custom property support // // Returns $radius value of shape category - `large`, `medium` or `small`. // Otherwise, it returns the $radius itself if valid. @@ -97,10 +169,11 @@ @for $i from 1 through list.length($radius) { $corner: list.nth($radius, $i); - @if map.has-key(variables.$category-values, $corner) { + @if map.has-key(variables.$category-keywords, $corner) { // If a category is encountered within a list of radii, apply the category's value for the corresponding corner + $category-value: custom-properties.get-fallback(map.get(variables.$category-keywords, $corner)); $radius-values: - list.append($radius-values, list.nth(unpack-radius_(map.get(variables.$category-values, $corner)), $i)); + list.append($radius-values, list.nth(unpack-radius($category-value), $i)); } @else { $radius-values: list.append($radius-values, validate-radius-value_($corner)); } @@ -108,8 +181,8 @@ @return $radius-values; } @else { - @if map.has-key(variables.$category-values, $radius) { - @return map.get(variables.$category-values, $radius); + @if map.has-key(variables.$category-keywords, $radius) { + @return custom-properties.get-fallback(map.get(variables.$category-keywords, $radius)); } @else { @return validate-radius-value_($radius); } @@ -137,7 +210,7 @@ @error "Expected masked-corners of length 4 but got '#{list.length($masked-corners)}'."; } - $radius: unpack-radius_($radius); + $radius: unpack-radius($radius); @return if(list.nth($masked-corners, 1) == 1, list.nth($radius, 1), 0) if(list.nth($masked-corners, 2) == 1, list.nth($radius, 2), 0) @@ -145,44 +218,53 @@ if(list.nth($masked-corners, 4) == 1, list.nth($radius, 4), 0); } -// -// Unpacks shorthand values for border-radius (i.e. lists of 1-3 values). -// If a list of 4 values is given, it is returned as-is. -// -// Examples: -// -// 1. mdc-shape-unpack-radius_(4px) => 4px 4px 4px 4px -// 2. mdc-shape-unpack-radius_(4px 2px) => 4px 2px 4px 2px -// 3. mdc-shape-unpack-radius_(4px 2px 2px) => 4px 2px 2px 2px -// 2. mdc-shape-unpack-radius_(4px 2px 0 2px) => 4px 2px 0 2px -// -// TODO: This is private for purposes of getting it into a patch; make it public for a future minor/major release. -// -@function unpack-radius_($radius) { - @if list.length($radius) == 4 { +/// Unpacks shorthand values for border-radius (i.e. lists of 1-3 values). +/// If a list of 4 values is given, it is returned as-is. +/// +/// Examples: +/// +/// shape.unpack-radius(4px) => 4px 4px 4px 4px +/// shape.unpack-radius(4px 2px) => 4px 2px 4px 2px +/// shape.unpack-radius(4px 2px 2px) => 4px 2px 2px 2px +/// shape.unpack-radius(4px 2px 0 2px) => 4px 2px 0 2px +/// +/// @param {Number | Map | List} $radius - List of 1 to 4 radius numbers. +/// @return {List} a List of 4 radius numbers. +@function unpack-radius($radius) { + @if meta.type-of($radius) == "map" or list.length($radius) == 1 { + @return $radius $radius $radius $radius; + } @else if list.length($radius) == 4 { @return $radius; } @else if list.length($radius) == 3 { @return list.nth($radius, 1) list.nth($radius, 2) list.nth($radius, 3) list.nth($radius, 2); } @else if list.length($radius) == 2 { @return list.nth($radius, 1) list.nth($radius, 2) list.nth($radius, 1) list.nth($radius, 2); - } @else if list.length($radius) == 1 { - @return $radius $radius $radius $radius; } @error "Invalid radius: '#{$radius}' is more than 4 values"; } +/// Resolve a percentage radius into a number. +/// +/// @param {Number} $percentage - the radius percentage. +/// @param {Number} $component-height - the height of the component. +/// @return {Number} the resolved radius as a number. +@function _resolve-radius-percentage($percentage, $component-height) { + // Converts the percentage to number without unit. Example: 50% => 50. + $percentage: $percentage / ($percentage * 0 + 1); + @return $component-height * ($percentage / 100);; +} + +// @deprecated use `_resolve-radius-percentage()` directly @function resolve-percentage-for-corner_($component-height, $radius) { @if meta.type-of($radius) == "number" and math.unit($radius) == "%" { - // Converts the percentage to number without unit. Example: 50% => 50. - $percentage: $radius / ($radius * 0 + 1); - - @return $component-height * ($percentage / 100); + @return _resolve-radius-percentage($radius, $component-height); } @else { @return $radius; } } +/// @deprecated @function validate-radius-value_($radius) { $is-number: meta.type-of($radius) == "number"; diff --git a/packages/mdc-shape/_mixins.scss b/packages/mdc-shape/_mixins.scss index 63fd5a73d91..47930f5f1d5 100644 --- a/packages/mdc-shape/_mixins.scss +++ b/packages/mdc-shape/_mixins.scss @@ -21,29 +21,74 @@ // @use "sass:list"; +@use "sass:meta"; @use "@material/feature-targeting/functions" as feature-targeting-functions; @use "@material/feature-targeting/mixins" as feature-targeting-mixins; @use "@material/rtl/mixins" as rtl-mixins; +@use "@material/theme/mixins" as theme; @use "./variables"; @use "./functions"; -@mixin radius($radius, $rtl-reflexive: false, $query: feature-targeting-functions.all()) { +@mixin radius($radius, $rtl-reflexive: false, $component-height: null, $query: feature-targeting-functions.all()) { $feat-structure: feature-targeting-functions.create-target($query, structure); @include feature-targeting-mixins.targets($feat-structure) { + $has-multiple-corners: meta.type-of($radius) == "list" and list.length($radius) > 1; // Even if $rtl-reflexive is true, only emit RTL styles if we can't easily tell that the given radius is symmetrical - $needs-flip: $rtl-reflexive and list.length($radius) > 1; - - @if ($needs-flip) { - /* @noflip */ + $needs-flip: $rtl-reflexive and $has-multiple-corners; + $radius: functions.resolve-radius($radius, $component-height: $component-height); + @if not $has-multiple-corners { + @include theme.property(border-radius, $radius); + } @else { + $gss: (noflip: $needs-flip); + $radii: functions.unpack-radius($radius); + @include theme.property( + border-top-left-radius, + list.nth($radii, 1), + $gss: $gss + ); + @include theme.property( + border-top-right-radius, + list.nth($radii, 2), + $gss: $gss + ); + @include theme.property( + border-bottom-right-radius, + list.nth($radii, 3), + $gss: $gss + ); + @include theme.property( + border-bottom-left-radius, + list.nth($radii, 4), + $gss: $gss + ); } - border-radius: functions.prop-value($radius); - @if ($needs-flip) { @include rtl-mixins.rtl { - /* @noflip */ - border-radius: functions.flip-radius(functions.prop-value($radius)); + // If it needs to be flipped, it will always have multiple corners + $gss: (noflip: true); + $radii: functions.flip-radius(functions.unpack-radius($radius)); + @include theme.property( + border-top-left-radius, + list.nth($radii, 1), + $gss: $gss + ); + @include theme.property( + border-top-right-radius, + list.nth($radii, 2), + $gss: $gss + ); + @include theme.property( + border-bottom-right-radius, + list.nth($radii, 3), + $gss: $gss + ); + @include theme.property( + border-bottom-left-radius, + list.nth($radii, 4), + $gss: $gss + ); } } } diff --git a/packages/mdc-shape/_variables.scss b/packages/mdc-shape/_variables.scss index dc33dc06849..0f51944bfde 100644 --- a/packages/mdc-shape/_variables.scss +++ b/packages/mdc-shape/_variables.scss @@ -25,9 +25,18 @@ $small-component-radius: 4px !default; $medium-component-radius: 4px !default; $large-component-radius: 0 !default; -// Shape category mapping. -$category-values: ( - small: $small-component-radius, - medium: $medium-component-radius, - large: $large-component-radius, +// Shape keyword mapping +$category-keywords: ( + small: ( + varname: --mdc-shape-small, + fallback: $small-component-radius + ), + medium: ( + varname: --mdc-shape-medium, + fallback: $medium-component-radius + ), + large: ( + varname: --mdc-shape-large, + fallback: $large-component-radius + ) ) !default; diff --git a/packages/mdc-shape/package.json b/packages/mdc-shape/package.json index fd40ff89b3c..f7acf8683be 100644 --- a/packages/mdc-shape/package.json +++ b/packages/mdc-shape/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@material/feature-targeting": "^6.0.0", - "@material/rtl": "^6.0.0" + "@material/rtl": "^6.0.0", + "@material/theme": "^6.0.0" } } diff --git a/packages/mdc-snackbar/_variables.scss b/packages/mdc-snackbar/_variables.scss index 500b0bf8603..ac452b1e8d2 100644 --- a/packages/mdc-snackbar/_variables.scss +++ b/packages/mdc-snackbar/_variables.scss @@ -39,7 +39,7 @@ $viewport-margin-wide: 24px !default; $padding: 8px !default; $elevation: 6 !default; -$shape-radius: small !default; // Key from $mdc-shape-category-values or CSS length value (e.g., 4px) +$shape-radius: small !default; // Key from shape.$category-keywords or CSS length value (e.g., 4px) $z-index: 8 !default; // One above mdc-dialog // These variables need to be kept in sync with the values in constants.js. diff --git a/packages/mdc-top-app-bar/_mixins.scss b/packages/mdc-top-app-bar/_mixins.scss index e83ceb77a7a..e35d4f97f7f 100644 --- a/packages/mdc-top-app-bar/_mixins.scss +++ b/packages/mdc-top-app-bar/_mixins.scss @@ -21,6 +21,7 @@ // @use "sass:list"; +@use "sass:meta"; @use "@material/animation/variables" as animation-variables; @use "@material/ripple/mixins" as ripple-mixins; @use "@material/theme/variables" as theme-variables; // for mdc-theme-accessible-ink-color @@ -56,7 +57,7 @@ } @mixin short-shape-radius($radius, $rtl-reflexive: true) { - @if list.length($radius) > 1 { + @if meta.type-of($radius) == "list" and list.length($radius) > 1 { @error "Invalid radius: '#{$radius}' component doesn't allow customizing all corners"; } @@ -66,8 +67,9 @@ #{$selector} { @include shape-mixins.radius( - functions.resolve-percentage-radius(variables.$dense-row-height, $radius), - $rtl-reflexive + $radius, + $rtl-reflexive, + $component-height: variables.$dense-row-height ); } }