Files
2026-03-09 22:30:41 +00:00

183 lines
6.0 KiB
SCSS

@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-visible > .mat-focus-indicator::before {
content: '';
@if ($focus-indicator-inherits-shape) {
border-radius: inherit;
}
}
}
@mixin mat-private-button-ripple(
$ripple-color-token, $state-layer-color-token, $disabled-state-layer-color-token,
$hover-state-layer-opacity-token, $focus-state-layer-opacity-token,
$pressed-state-layer-opacity-token, $fallbacks
) {
.mat-ripple-element {
background-color: token-utils.slot($ripple-color-token, $fallbacks);
}
.mat-mdc-button-persistent-ripple::before {
background-color: token-utils.slot($state-layer-color-token, $fallbacks);
}
&.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before {
background-color: token-utils.slot($disabled-state-layer-color-token, $fallbacks);
}
&:hover > .mat-mdc-button-persistent-ripple::before {
opacity: token-utils.slot($hover-state-layer-opacity-token, $fallbacks);
}
&.cdk-program-focused,
&.cdk-keyboard-focused,
&.mat-mdc-button-disabled-interactive:focus {
> .mat-mdc-button-persistent-ripple::before {
opacity: token-utils.slot($focus-state-layer-opacity-token, $fallbacks);
}
}
&:active > .mat-mdc-button-persistent-ripple::before {
opacity: token-utils.slot($pressed-state-layer-opacity-token, $fallbacks);
}
}
// 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;
}
}
@mixin mat-private-button-touch-target(
$is-square,
$touch-target-size-token,
$touch-target-display-token,
$fallbacks) {
.mat-mdc-button-touch-target {
position: absolute;
top: 50%;
height: token-utils.slot($touch-target-size-token, $fallbacks);
display: token-utils.slot($touch-target-display-token, $fallbacks);
@if $is-square {
left: 50%;
width: token-utils.slot($touch-target-size-token, $fallbacks);
transform: translate(-50%, -50%);
} @else {
left: 0;
right: 0;
transform: translateY(-50%);
}
}
}
@mixin mat-private-button-horizontal-layout(
$icon-spacing-token, $icon-offset-token, $with-icon-horizontal-padding-token, $fallbacks) {
$icon-spacing: token-utils.slot($icon-spacing-token, $fallbacks, true);
$icon-offset: token-utils.slot($icon-offset-token, $fallbacks, true);
@if ($with-icon-horizontal-padding-token) {
// stylelint-disable-next-line selector-class-pattern
&:has(.material-icons, mat-icon, [matButtonIcon]) {
padding: 0 token-utils.slot($with-icon-horizontal-padding-token, $fallbacks, true);
}
}
// MDC expects button icons to contain this HTML content:
// ```html
// <span class="mdc-button__icon material-icons">favorite</span>
// ```
// 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;
}
}
}