_button-base.scss 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. @use '../core/tokens/token-utils';
  2. @use '../core/style/layout-common';
  3. // Adds styles necessary to provide stateful interactions with the button. This includes providing
  4. // content for the state container's ::before and ::after so that they can be given a background
  5. // color and opacity for states like hover, active, and focus. Additionally, adds styles to the
  6. // ripple and state container so that they fill the button, match the border radius, and avoid
  7. // pointer events.
  8. @mixin mat-private-button-interactive($focus-indicator-inherits-shape: true) {
  9. -webkit-tap-highlight-color: transparent;
  10. // The ripple container should match the bounds of the entire button.
  11. .mat-mdc-button-ripple,
  12. .mat-mdc-button-persistent-ripple,
  13. .mat-mdc-button-persistent-ripple::before {
  14. @include layout-common.fill;
  15. // Disable pointer events for the ripple container and state overlay because the container
  16. // will overlay the user content and we don't want to disable mouse events on the user content.
  17. // Pointer events can be safely disabled because the ripple trigger element is the host element.
  18. pointer-events: none;
  19. // Inherit the border radius from the parent so that state overlay and ripples don't exceed the
  20. // parent button boundaries. Note that an inherited border radius does not work properly if
  21. // the actual button element does have a border because it causes the inner content to be
  22. // smaller. We have special logic for stroked buttons to handle this scenario.
  23. border-radius: inherit;
  24. }
  25. // This style used to be applied by the MatRipple
  26. // directive, which is no longer attached to this element.
  27. .mat-mdc-button-ripple {
  28. overflow: hidden;
  29. }
  30. // We use ::before so that we can reuse some of MDC's theming.
  31. .mat-mdc-button-persistent-ripple::before {
  32. content: '';
  33. opacity: 0;
  34. }
  35. // The content should appear over the state and ripple layers, otherwise they may adversely affect
  36. // the accessibility of the text content.
  37. .mdc-button__label,
  38. .mat-icon {
  39. z-index: 1;
  40. position: relative;
  41. }
  42. // The focus indicator should match the bounds of the entire button.
  43. .mat-focus-indicator {
  44. @include layout-common.fill();
  45. @if ($focus-indicator-inherits-shape) {
  46. border-radius: inherit;
  47. }
  48. }
  49. &:focus > .mat-focus-indicator::before {
  50. content: '';
  51. @if ($focus-indicator-inherits-shape) {
  52. border-radius: inherit;
  53. }
  54. }
  55. }
  56. @mixin mat-private-button-ripple($prefix, $slots) {
  57. @include token-utils.use-tokens($prefix, $slots) {
  58. .mat-ripple-element {
  59. @include token-utils.create-token-slot(background-color, ripple-color);
  60. }
  61. .mat-mdc-button-persistent-ripple::before {
  62. @include token-utils.create-token-slot(background-color, state-layer-color);
  63. }
  64. &.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before {
  65. @include token-utils.create-token-slot(background-color, disabled-state-layer-color);
  66. }
  67. &:hover > .mat-mdc-button-persistent-ripple::before {
  68. @include token-utils.create-token-slot(opacity, hover-state-layer-opacity);
  69. }
  70. &.cdk-program-focused,
  71. &.cdk-keyboard-focused,
  72. &.mat-mdc-button-disabled-interactive:focus {
  73. > .mat-mdc-button-persistent-ripple::before {
  74. @include token-utils.create-token-slot(opacity, focus-state-layer-opacity);
  75. }
  76. }
  77. &:active > .mat-mdc-button-persistent-ripple::before {
  78. @include token-utils.create-token-slot(opacity, pressed-state-layer-opacity);
  79. }
  80. }
  81. }
  82. // MDC's disabled buttons define a default cursor with pointer-events none. However, they select
  83. // :disabled for this, which does not affect anchor tags.
  84. // TODO(andrewseguin): Discuss with the MDC team about a mixin we can call for applying this style,
  85. // and note that having pointer-events may have unintended side-effects, e.g. allowing the user
  86. // to click the target underneath the button.
  87. @mixin mat-private-button-disabled() {
  88. // `[disabled]` shouldn't be necessary, but we keep it to maintain
  89. // compatibility with apps setting it through host bindings.
  90. &[disabled],
  91. &.mat-mdc-button-disabled {
  92. cursor: default;
  93. pointer-events: none;
  94. @content;
  95. }
  96. &.mat-mdc-button-disabled-interactive {
  97. pointer-events: auto;
  98. }
  99. }
  100. // Adds an elevation shadow to a button.
  101. @mixin mat-private-button-elevation($token-name) {
  102. // MDC outputs a variable that is the same as the token name, but suffixed with `-shadow`.
  103. box-shadow: token-utils.get-token-variable($token-name + '-shadow');
  104. }
  105. @mixin mat-private-button-touch-target($is-square, $prefix, $slots) {
  106. .mat-mdc-button-touch-target {
  107. position: absolute;
  108. top: 50%;
  109. height: 48px;
  110. @if $is-square {
  111. left: 50%;
  112. width: 48px;
  113. transform: translate(-50%, -50%);
  114. } @else {
  115. left: 0;
  116. right: 0;
  117. transform: translateY(-50%);
  118. }
  119. @include token-utils.use-tokens($prefix, $slots) {
  120. @include token-utils.create-token-slot(display, touch-target-display);
  121. }
  122. }
  123. }
  124. @mixin mat-private-button-horizontal-layout($prefix, $slots, $has-with-icon-padding) {
  125. @include token-utils.use-tokens($prefix, $slots) {
  126. $icon-spacing: token-utils.get-token-variable(icon-spacing, true);
  127. $icon-offset: token-utils.get-token-variable(icon-offset, true);
  128. @if ($has-with-icon-padding) {
  129. $with-icon-horizontal-padding:
  130. token-utils.get-token-variable(with-icon-horizontal-padding, true);
  131. // stylelint-disable-next-line selector-class-pattern
  132. &:has(.material-icons, mat-icon, [matButtonIcon]) {
  133. padding: 0 $with-icon-horizontal-padding;
  134. }
  135. }
  136. // MDC expects button icons to contain this HTML content:
  137. // ```html
  138. // <span class="mdc-button__icon material-icons">favorite</span>
  139. // ```
  140. // However, Angular Material expects a `mat-icon` instead. The following
  141. // styles will lay out the icons appropriately.
  142. & > .mat-icon {
  143. margin-right: $icon-spacing;
  144. margin-left: $icon-offset;
  145. [dir='rtl'] & {
  146. margin-right: $icon-offset;
  147. margin-left: $icon-spacing;
  148. }
  149. }
  150. .mdc-button__label + .mat-icon {
  151. margin-right: $icon-offset;
  152. margin-left: $icon-spacing;
  153. [dir='rtl'] & {
  154. margin-right: $icon-spacing;
  155. margin-left: $icon-offset;
  156. }
  157. }
  158. }
  159. }