@use '../core/tokens/token-utils'; @use '../core/style/layout-common'; // Adds styles necessary to provide stateful interactions with the button. This includes providing // content for the state container's ::before and ::after so that they can be given a background // color and opacity for states like hover, active, and focus. Additionally, adds styles to the // ripple and state container so that they fill the button, match the border radius, and avoid // pointer events. @mixin mat-private-button-interactive($focus-indicator-inherits-shape: true) { -webkit-tap-highlight-color: transparent; // The ripple container should match the bounds of the entire button. .mat-mdc-button-ripple, .mat-mdc-button-persistent-ripple, .mat-mdc-button-persistent-ripple::before { @include layout-common.fill; // Disable pointer events for the ripple container and state overlay because the container // will overlay the user content and we don't want to disable mouse events on the user content. // Pointer events can be safely disabled because the ripple trigger element is the host element. pointer-events: none; // Inherit the border radius from the parent so that state overlay and ripples don't exceed the // parent button boundaries. Note that an inherited border radius does not work properly if // the actual button element does have a border because it causes the inner content to be // smaller. We have special logic for stroked buttons to handle this scenario. border-radius: inherit; } // This style used to be applied by the MatRipple // directive, which is no longer attached to this element. .mat-mdc-button-ripple { overflow: hidden; } // We use ::before so that we can reuse some of MDC's theming. .mat-mdc-button-persistent-ripple::before { content: ''; opacity: 0; } // The content should appear over the state and ripple layers, otherwise they may adversely affect // the accessibility of the text content. .mdc-button__label, .mat-icon { z-index: 1; position: relative; } // The focus indicator should match the bounds of the entire button. .mat-focus-indicator { @include layout-common.fill(); @if ($focus-indicator-inherits-shape) { border-radius: inherit; } } &:focus > .mat-focus-indicator::before { content: ''; @if ($focus-indicator-inherits-shape) { border-radius: inherit; } } } @mixin mat-private-button-ripple($prefix, $slots) { @include token-utils.use-tokens($prefix, $slots) { .mat-ripple-element { @include token-utils.create-token-slot(background-color, ripple-color); } .mat-mdc-button-persistent-ripple::before { @include token-utils.create-token-slot(background-color, state-layer-color); } &.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { @include token-utils.create-token-slot(background-color, disabled-state-layer-color); } &:hover > .mat-mdc-button-persistent-ripple::before { @include token-utils.create-token-slot(opacity, hover-state-layer-opacity); } &.cdk-program-focused, &.cdk-keyboard-focused, &.mat-mdc-button-disabled-interactive:focus { > .mat-mdc-button-persistent-ripple::before { @include token-utils.create-token-slot(opacity, focus-state-layer-opacity); } } &:active > .mat-mdc-button-persistent-ripple::before { @include token-utils.create-token-slot(opacity, pressed-state-layer-opacity); } } } // MDC's disabled buttons define a default cursor with pointer-events none. However, they select // :disabled for this, which does not affect anchor tags. // TODO(andrewseguin): Discuss with the MDC team about a mixin we can call for applying this style, // and note that having pointer-events may have unintended side-effects, e.g. allowing the user // to click the target underneath the button. @mixin mat-private-button-disabled() { // `[disabled]` shouldn't be necessary, but we keep it to maintain // compatibility with apps setting it through host bindings. &[disabled], &.mat-mdc-button-disabled { cursor: default; pointer-events: none; @content; } &.mat-mdc-button-disabled-interactive { pointer-events: auto; } } // Adds an elevation shadow to a button. @mixin mat-private-button-elevation($token-name) { // MDC outputs a variable that is the same as the token name, but suffixed with `-shadow`. box-shadow: token-utils.get-token-variable($token-name + '-shadow'); } @mixin mat-private-button-touch-target($is-square, $prefix, $slots) { .mat-mdc-button-touch-target { position: absolute; top: 50%; height: 48px; @if $is-square { left: 50%; width: 48px; transform: translate(-50%, -50%); } @else { left: 0; right: 0; transform: translateY(-50%); } @include token-utils.use-tokens($prefix, $slots) { @include token-utils.create-token-slot(display, touch-target-display); } } } @mixin mat-private-button-horizontal-layout($prefix, $slots, $has-with-icon-padding) { @include token-utils.use-tokens($prefix, $slots) { $icon-spacing: token-utils.get-token-variable(icon-spacing, true); $icon-offset: token-utils.get-token-variable(icon-offset, true); @if ($has-with-icon-padding) { $with-icon-horizontal-padding: token-utils.get-token-variable(with-icon-horizontal-padding, true); // stylelint-disable-next-line selector-class-pattern &:has(.material-icons, mat-icon, [matButtonIcon]) { padding: 0 $with-icon-horizontal-padding; } } // MDC expects button icons to contain this HTML content: // ```html // favorite // ``` // However, Angular Material expects a `mat-icon` instead. The following // styles will lay out the icons appropriately. & > .mat-icon { margin-right: $icon-spacing; margin-left: $icon-offset; [dir='rtl'] & { margin-right: $icon-offset; margin-left: $icon-spacing; } } .mdc-button__label + .mat-icon { margin-right: $icon-offset; margin-left: $icon-spacing; [dir='rtl'] & { margin-right: $icon-spacing; margin-left: $icon-offset; } } } }