// // Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // @import "@material/animation/variables"; @import "@material/theme/functions"; @import "@material/theme/mixins"; @import "./keyframes"; @import "./variables"; // Ensure that styles needed by any component using MDC Ripple are emitted, but only once. // (Every component using MDC Ripple imports these mixins, but doesn't necessarily import mdc-ripple.scss.) // This variable is not intended to be overridden externally; it uses !default to avoid being reset // every time this file is imported. $mdc-ripple-common-styles-emitted_: false !default; @if not $mdc-ripple-common-styles-emitted_ { $mdc-ripple-common-styles-emitted_: true; @include mdc-ripple-keyframes_; // Styles used to detect buggy behavior of CSS custom properties in Edge. // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11495448/ // This is included in _mixins.scss rather than mdc-ripple.scss so that it will be // present for other components which rely on ripple as well as mdc-ripple itself. .mdc-ripple-surface--test-edge-var-bug { --mdc-ripple-surface-test-edge-var: 1px solid #000; visibility: hidden; &::before { border: var(--mdc-ripple-surface-test-edge-var); } } } @mixin mdc-ripple-surface() { --mdc-ripple-fg-size: 0; --mdc-ripple-left: 0; --mdc-ripple-top: 0; --mdc-ripple-fg-scale: 1; --mdc-ripple-fg-translate-end: 0; --mdc-ripple-fg-translate-start: 0; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); &::before, &::after { position: absolute; border-radius: 50%; opacity: 0; pointer-events: none; content: ""; will-change: transform, opacity; } &::before { transition: opacity $mdc-states-wash-duration linear; } // Common styles for upgraded surfaces (some of these depend on custom properties set via JS or other mixins) &.mdc-ripple-upgraded::after { top: 0; left: 0; transform: scale(0); transform-origin: center center; } &.mdc-ripple-upgraded--unbounded::after { top: var(--mdc-ripple-top, 0); left: var(--mdc-ripple-left, 0); } &.mdc-ripple-upgraded--foreground-activation::after { animation: $mdc-ripple-translate-duration mdc-ripple-fg-radius-in forwards, $mdc-ripple-fade-in-duration mdc-ripple-fg-opacity-in forwards; } &.mdc-ripple-upgraded--foreground-deactivation::after { animation: $mdc-ripple-fade-out-duration mdc-ripple-fg-opacity-out; // Retain transform from mdc-ripple-fg-radius-in activation transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1)); } } @mixin mdc-states-base-color($color) { // Opacity styles are here (rather than in mdc-ripple-surface) to ensure that opacity is re-initialized for // cases where this mixin is used to override another inherited use of itself, // without needing to re-include mdc-ripple-surface. &::before, &::after { @include mdc-theme-prop(background-color, $color, $edgeOptOut: true); } } @mixin mdc-states-hover-opacity($opacity) { // Background wash styles, for both CSS-only and upgraded stateful surfaces &:hover::before { opacity: $opacity; } } @mixin mdc-states-focus-opacity($opacity, $has-nested-focusable-element: false) { // Focus overrides hover by reusing the ::before pseudo-element. // :focus-within generally works on non-MS browsers and matches when a *child* of the element has focus. // It is useful for cases where a component has a focusable element within the root node, e.g. text field, // but undesirable in general in case of nested stateful components. // We use a modifier class for JS-enabled surfaces to support all use cases in all browsers. $cssOnlyFocusSelector: if( $has-nested-focusable-element, "&:not(.mdc-ripple-upgraded):focus::before, &:not(.mdc-ripple-upgraded):focus-within::before", "&:not(.mdc-ripple-upgraded):focus::before" ); #{$cssOnlyFocusSelector}, &.mdc-ripple-upgraded--background-focused::before { // Note that this duration is only effective on focus, not blur transition-duration: 75ms; opacity: $opacity; } } @mixin mdc-states-press-opacity($opacity) { // Styles for non-upgraded (CSS-only) stateful surfaces &:not(.mdc-ripple-upgraded) { // Apply press additively by using the ::after pseudo-element &::after { transition: opacity $mdc-ripple-fade-out-duration linear; } &:active::after { transition-duration: $mdc-ripple-fade-in-duration; opacity: $opacity; } } &.mdc-ripple-upgraded { --mdc-ripple-fg-opacity: $opacity; } } // Simple mixin for base states which automatically selects opacity values based on whether the ink color is // light or dark. @mixin mdc-states($color: black, $has-nested-focusable-element: false) { @include mdc-states-interactions_($color, $has-nested-focusable-element); } // Simple mixin for activated states which automatically selects opacity values based on whether the ink color is // light or dark. @mixin mdc-states-activated($color, $has-nested-focusable-element: false) { $opacity-map: mdc-states-opacities_($color); $activated-opacity: map-get($opacity-map, "activated"); &--activated { // Stylelint seems to think that '&' qualifies as a type selector here? // stylelint-disable-next-line selector-max-type &::before { opacity: $activated-opacity; } @include mdc-states-interactions_($color, $has-nested-focusable-element, $activated-opacity); } } // Simple mixin for selected states which automatically selects opacity values based on whether the ink color is // light or dark. @mixin mdc-states-selected($color, $has-nested-focusable-element: false) { $opacity-map: mdc-states-opacities_($color); $selected-opacity: map-get($opacity-map, "selected"); &--selected { // stylelint-disable-next-line selector-max-type &::before { opacity: $selected-opacity; } @include mdc-states-interactions_($color, $has-nested-focusable-element, $selected-opacity); } } @mixin mdc-ripple-radius($radius: 100%) { &::before, &::after { top: calc(50% - #{$radius}); left: calc(50% - #{$radius}); width: $radius * 2; height: $radius * 2; } // Background ripple styles &.mdc-ripple-upgraded::before { top: calc(50% - #{$radius}); left: calc(50% - #{$radius}); width: $radius * 2; height: $radius * 2; transform: scale(var(--mdc-ripple-fg-scale, 0)); } &.mdc-ripple-upgraded--unbounded::before { top: var(--mdc-ripple-top, calc(50% - #{$radius / 2})); left: var(--mdc-ripple-left, calc(50% - #{$radius / 2})); width: var(--mdc-ripple-fg-size, $radius); height: var(--mdc-ripple-fg-size, $radius); transform: scale(var(--mdc-ripple-fg-scale, 0)); } // Foreground ripple styles &.mdc-ripple-upgraded::after { width: var(--mdc-ripple-fg-size, $radius); height: var(--mdc-ripple-fg-size, $radius); } } // // Legacy // @mixin mdc-ripple-color($color: black, $opacity: .06) { // Opacity styles are here (rather than in mdc-ripple-surface) to ensure that opacity is re-initialized for // cases where this mixin is used to override another inherited use of itself, // without needing to re-include mdc-ripple-surface. &::before, &::after { @include mdc-ripple-color_($color, $opacity); opacity: 0; } // Note: when :active is applied, :focus is already applied, which will effectively double the effect. &:not(.mdc-ripple-upgraded) { &:hover::before, &:focus::before, &:active::after { transition-duration: 85ms; opacity: .6; } } &.mdc-ripple-upgraded--background-focused::before { opacity: .99999; } &.mdc-ripple-upgraded--background-active-fill::before { transition-duration: 120ms; opacity: 1; } &.mdc-ripple-upgraded::after { // Set this to 1 for backwards compatibility with how the keyframes were originally coded for use with this mixin --mdc-ripple-fg-opacity: 1; } } // // Private // @function mdc-states-opacities_($color) { $color-value: mdc-theme-prop-value($color); $opacity-map: if( mdc-theme-tone($color-value) == "light", $mdc-ripple-light-ink-opacities, $mdc-ripple-dark-ink-opacities ); @return $opacity-map; } @mixin mdc-states-interactions_($color, $has-nested-focusable-element, $opacity-modifier: 0) { $opacity-map: mdc-states-opacities_($color); @include mdc-states-base-color($color); @include mdc-states-hover-opacity(map-get($opacity-map, "hover") + $opacity-modifier); @include mdc-states-focus-opacity(map-get($opacity-map, "focus") + $opacity-modifier, $has-nested-focusable-element); @include mdc-states-press-opacity(map-get($opacity-map, "press") + $opacity-modifier); } // Note: This can be removed when we remove the legacy mdc-ripple-color mixin. @mixin mdc-ripple-color_($color, $opacity) { // stylelint-disable at-rule-empty-line-before, block-closing-brace-newline-after @if type-of($color) == "color" { background-color: rgba($color, $opacity); } @else { // Assume $color is a theme property name $theme-value: map-get($mdc-theme-property-values, $color); $css-var: var(--mdc-theme-#{$color}, $theme-value); background-color: rgba($theme-value, $opacity); // See: https://drafts.csswg.org/css-color/#modifying-colors // While this is currently unsupported as of now, it will begin to work by default as browsers // begin to implement the CSS 4 color spec. @supports (background-color: color(green a(10%))) { background-color: color(#{$css-var} a(#{percentage($opacity)})); } } // stylelint-enable at-rule-empty-line-before, block-closing-brace-newline-after }