diff --git a/packages/mdc-checkbox/_checkbox-theme.scss b/packages/mdc-checkbox/_checkbox-theme.scss index 36b4b98a7c1..4f15e3392bf 100644 --- a/packages/mdc-checkbox/_checkbox-theme.scss +++ b/packages/mdc-checkbox/_checkbox-theme.scss @@ -68,6 +68,95 @@ $density-config: ( $ripple-target: '.mdc-checkbox__ripple'; +/// Material baseline light theme configuration of checkbox. +/// See https://material.io/components/selection-controls for spec. +$light-theme: ( + density-scale: 0, + checkmark-color: $mark-color, + container-checked-color: theme-color.prop-value($baseline-theme-color), + container-checked-hover-color: null, + container-disabled-color: $disabled-color, + outline-color: $border-color, + outline-hover-color: null, + ripple-color: theme-color.$on-surface, + ripple-opacity: ripple-theme.$dark-ink-opacities, + ripple-checked-color: theme-color.prop-value($baseline-theme-color), + ripple-checked-opacity: ripple-theme.$dark-ink-opacities, +); + +/// Sets theme to checkbox based on provided theme configuration. +/// Only emits theme related styles. Theme styles should be included after +/// `static-styles()`. +/// @param {Map} $theme - Theme configuration to use for theming checkbox. +@mixin theme($theme, $query: feature-targeting.all()) { + $ripple-color: map.get($theme, ripple-color); + $ripple-opacity: map.get($theme, ripple-opacity); + @if $ripple-color { + @include ripple-color( + $color: $ripple-color, + $opacity-map: $ripple-opacity, + $query: $query + ); + } + + $ripple-checked-color: map.get($theme, ripple-checked-color); + $ripple-checked-opacity: map.get($theme, ripple-checked-opacity); + @if $ripple-checked-color { + @include focus-indicator-color( + $color: $ripple-checked-color, + $opacity-map: $ripple-checked-opacity, + $query: $query + ); + } + + $density-scale: map.get($theme, density-scale); + @if $density-scale != null { + @include density($density-scale: $density-scale, $query: $query); + } + + $outline-color: map.get($theme, outline-color); + $container-checked-color: map.get($theme, container-checked-color); + @if ($outline-color and $container-checked-color) { + @include container-colors( + $unmarked-stroke-color: $outline-color, + $marked-stroke-color: $container-checked-color, + $marked-fill-color: $container-checked-color, + $query: $query + ); + } + + $outline-hover-color: map.get($theme, outline-hover-color); + $container-checked-hover-color: map.get( + $theme, + container-checked-hover-color + ); + @if ($outline-hover-color and $container-checked-hover-color) { + @include ripple-theme.states-selector() { + @include container-colors( + $unmarked-stroke-color: $outline-hover-color, + $marked-stroke-color: $container-checked-hover-color, + $marked-fill-color: $container-checked-hover-color, + $query: $query + ); + } + } + + $container-disabled-color: map.get($theme, container-disabled-color); + @if $container-disabled-color { + @include disabled-container-colors( + $unmarked-stroke-color: $container-disabled-color, + $marked-fill-color: $container-disabled-color, + $query: $query + ); + } + + $checkmark-color: map.get($theme, checkmark-color); + @if $checkmark-color { + @include ink-color($checkmark-color, $query: $query); + @include disabled-ink-color($checkmark-color, $query: $query); + } +} + /// /// Sets density scale for checkbox. /// @@ -314,7 +403,26 @@ $ripple-target: '.mdc-checkbox__ripple'; } } -@mixin focus-indicator-color($color, $query: feature-targeting.all()) { +/// Sets ripple color when checkbox is not in checked state. +@mixin ripple-color( + $color, + $opacity-map: null, + $query: feature-targeting.all() +) { + @include ripple-theme.states( + $color: $color, + $opacity-map: $opacity-map, + $query: $query, + $ripple-target: $ripple-target + ); +} + +/// Sets focus indicator color when checkbox is in checked state. +@mixin focus-indicator-color( + $color, + $opacity-map: null, + $query: feature-targeting.all() +) { $feat-color: feature-targeting.create-target($query, color); .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background::before, @@ -330,6 +438,7 @@ $ripple-target: '.mdc-checkbox__ripple'; &.mdc-checkbox--selected { @include ripple-theme.states( $color: $color, + $opacity-map: $opacity-map, $query: $query, $ripple-target: $ripple-target ); diff --git a/packages/mdc-checkbox/_checkbox.scss b/packages/mdc-checkbox/_checkbox.scss index baf32cde5ce..b3589e1d0d4 100644 --- a/packages/mdc-checkbox/_checkbox.scss +++ b/packages/mdc-checkbox/_checkbox.scss @@ -209,23 +209,8 @@ /// Checkbox styles that are customizable should go here. @mixin theme-styles($query: feature-targeting.all()) { .mdc-checkbox { - @include checkbox-theme.focus-indicator-color( - checkbox-theme.$baseline-theme-color, - $query: $query - ); - @include checkbox-theme.density( - checkbox-theme.$density-scale, - $query: $query - ); + @include checkbox-theme.theme(checkbox-theme.$light-theme, $query: $query); } - - @include checkbox-theme.container-colors($query: $query); - @include checkbox-theme.disabled-container-colors($query: $query); - @include checkbox-theme.ink-color(checkbox-theme.$mark-color, $query: $query); - @include checkbox-theme.disabled-ink-color( - checkbox-theme.$mark-color, - $query: $query - ); } /// Checkbox's ripple styles. @@ -242,15 +227,14 @@ $query: $query, $ripple-target: checkbox-theme.$ripple-target ); - @include ripple-theme.states( - $color: on-surface, - $query: $query, - $ripple-target: checkbox-theme.$ripple-target - ); @include ripple.radius-unbounded( $query: $query, $ripple-target: checkbox-theme.$ripple-target ); + @include ripple-theme.behind-content( + checkbox-theme.$ripple-target, + $query: $query + ); } #{checkbox-theme.$ripple-target} { @@ -283,11 +267,7 @@ } @mixin child--upgraded_ { - // Due to the myriad of selector combos used to properly style a CSS-only checkbox, all of - // which have varying selector precedence and make use of transitions, it is cleaner and more - // efficient here to simply use !important, since the mdc-checkbox--anim-* classes will take - // over from here. - transition: none !important; + transition: none; } // Animation diff --git a/packages/mdc-ripple/_ripple-theme.scss b/packages/mdc-ripple/_ripple-theme.scss index 7fdd96367d1..5108c05a7ae 100644 --- a/packages/mdc-ripple/_ripple-theme.scss +++ b/packages/mdc-ripple/_ripple-theme.scss @@ -241,13 +241,15 @@ $pressed-light-ink-opacity: 0.32 !default; $color: theme-color.prop-value(on-surface), $has-nested-focusable-element: false, $query: feature-targeting.all(), - $ripple-target: '&' + $ripple-target: '&', + $opacity-map: null ) { @include states-interactions_( $color: $color, $has-nested-focusable-element: $has-nested-focusable-element, $query: $query, - $ripple-target: $ripple-target + $ripple-target: $ripple-target, + $opacity-map: $opacity-map ); } @@ -317,17 +319,20 @@ $pressed-light-ink-opacity: 0.32 !default; $has-nested-focusable-element, $opacity-modifier: 0, $query: feature-targeting.all(), - $ripple-target: '&' + $ripple-target: '&', + $opacity-map: null ) { @include target-selector($ripple-target) { @include states-base-color($color, $query); } - $opacity-map: ( - hover: states-opacity($color, hover) + $opacity-modifier, - focus: states-opacity($color, focus) + $opacity-modifier, - press: states-opacity($color, press) + $opacity-modifier, - ); + @if $opacity-map == null { + $opacity-map: ( + hover: states-opacity($color, hover) + $opacity-modifier, + focus: states-opacity($color, focus) + $opacity-modifier, + press: states-opacity($color, press) + $opacity-modifier, + ); + } @include states-opacities( $opacity-map, @@ -348,6 +353,34 @@ $pressed-light-ink-opacity: 0.32 !default; } } +/// Selector for hover, active and focus states. +@mixin states-selector() { + &:hover, + &:active, + &:focus, + &.mdc-ripple-upgraded--background-focused { + @content; + } +} + +/// Keep the ripple (State overlay) behind the content. +@mixin behind-content($ripple-target: '&', $query: feature-targeting.all()) { + // Needed for IE11. Without this, IE11 renders the state layer completely + // underneath the container, making it invisible. + $feat-structure: feature-targeting.create-target($query, structure); + + @include feature-targeting.targets($feat-structure) { + z-index: 0; + } + + #{$ripple-target}::before, + #{$ripple-target}::after { + @include feature-targeting.targets($feat-structure) { + z-index: -1; + } + } +} + @function states-opacity($color, $state) { $opacity-map: states-opacities_($color);