@use '@angular/cdk'; @use '../core/style/layout-common'; @use '../core/tokens/m2/mat/switch' as tokens-mat-switch; @use '../core/tokens/m2/mdc/switch' as tokens-mdc-switch; @use '../core/tokens/token-utils'; @use '../core/style/vendor-prefixes'; $_mdc-slots: (tokens-mdc-switch.$prefix, tokens-mdc-switch.get-token-slots()); $_mat-slots: (tokens-mat-switch.$prefix, tokens-mat-switch.get-token-slots()); $_interactive-disabled-selector: '.mat-mdc-slide-toggle-disabled-interactive.mdc-switch--disabled'; .mdc-switch { align-items: center; background: none; border: none; cursor: pointer; display: inline-flex; flex-shrink: 0; margin: 0; outline: none; overflow: visible; padding: 0; position: relative; @include token-utils.use-tokens($_mdc-slots...) { @include token-utils.create-token-slot(width, track-width); } &.mdc-switch--disabled { cursor: default; pointer-events: none; } &.mat-mdc-slide-toggle-disabled-interactive { pointer-events: auto; } } .mdc-switch__track { overflow: hidden; position: relative; width: 100%; @include token-utils.use-tokens($_mdc-slots...) { @include token-utils.create-token-slot(height, track-height); @include token-utils.create-token-slot(border-radius, track-shape); .mdc-switch--disabled.mdc-switch & { @include token-utils.create-token-slot(opacity, disabled-track-opacity); } } &::before, &::after { border: 1px solid transparent; border-radius: inherit; box-sizing: border-box; content: ''; height: 100%; left: 0; position: absolute; width: 100%; @include token-utils.use-tokens($_mat-slots...) { @include token-utils.create-token-slot(border-width, track-outline-width); @include token-utils.create-token-slot(border-color, track-outline-color); .mdc-switch--selected & { @include token-utils.create-token-slot(border-width, selected-track-outline-width); @include token-utils.create-token-slot(border-color, selected-track-outline-color); } .mdc-switch--disabled & { @include token-utils.create-token-slot(border-width, disabled-unselected-track-outline-width); @include token-utils.create-token-slot(border-color, disabled-unselected-track-outline-color); } } } @include cdk.high-contrast { border-color: currentColor; } &::before { transition: transform 75ms 0ms cubic-bezier(0, 0, 0.2, 1); transform: translateX(0); @include token-utils.use-tokens($_mdc-slots...) { @include token-utils.create-token-slot(background, unselected-track-color); } .mdc-switch--selected & { transition: transform 75ms 0ms cubic-bezier(0.4, 0, 0.6, 1); transform: translateX(100%); [dir='rtl'] .mdc-switch--selected & { transform: translateX(-100%); } } @include token-utils.use-tokens($_mat-slots...) { .mdc-switch--selected & { @include token-utils.create-token-slot(opacity, hidden-track-opacity); @include token-utils.create-token-slot(transition, hidden-track-transition); } .mdc-switch--unselected & { @include token-utils.create-token-slot(opacity, visible-track-opacity); @include token-utils.create-token-slot(transition, visible-track-transition); } } @include token-utils.use-tokens($_mdc-slots...) { .mdc-switch:enabled:hover:not(:focus):not(:active) & { @include token-utils.create-token-slot(background, unselected-hover-track-color); } .mdc-switch:enabled:focus:not(:active) & { @include token-utils.create-token-slot(background, unselected-focus-track-color); } .mdc-switch:enabled:active & { @include token-utils.create-token-slot(background, unselected-pressed-track-color); } #{$_interactive-disabled-selector}:hover:not(:focus):not(:active) &, #{$_interactive-disabled-selector}:focus:not(:active) &, #{$_interactive-disabled-selector}:active &, .mdc-switch.mdc-switch--disabled & { @include token-utils.create-token-slot(background, disabled-unselected-track-color); } } } &::after { transform: translateX(-100%); @include token-utils.use-tokens($_mdc-slots...) { @include token-utils.create-token-slot(background, selected-track-color); } [dir='rtl'] & { transform: translateX(100%); } .mdc-switch--selected & { transform: translateX(0); } @include token-utils.use-tokens($_mat-slots...) { .mdc-switch--selected & { @include token-utils.create-token-slot(opacity, visible-track-opacity); @include token-utils.create-token-slot(transition, visible-track-transition); } .mdc-switch--unselected & { @include token-utils.create-token-slot(opacity, hidden-track-opacity); @include token-utils.create-token-slot(transition, hidden-track-transition); } } @include token-utils.use-tokens($_mdc-slots...) { .mdc-switch:enabled:hover:not(:focus):not(:active) & { @include token-utils.create-token-slot(background, selected-hover-track-color); } .mdc-switch:enabled:focus:not(:active) & { @include token-utils.create-token-slot(background, selected-focus-track-color); } .mdc-switch:enabled:active & { @include token-utils.create-token-slot(background, selected-pressed-track-color); } #{$_interactive-disabled-selector}:hover:not(:focus):not(:active) &, #{$_interactive-disabled-selector}:focus:not(:active) &, #{$_interactive-disabled-selector}:active &, .mdc-switch.mdc-switch--disabled & { @include token-utils.create-token-slot(background, disabled-selected-track-color); } } } } .mdc-switch__handle-track { height: 100%; pointer-events: none; position: absolute; top: 0; transition: transform 75ms 0ms cubic-bezier(0.4, 0, 0.2, 1); left: 0; right: auto; transform: translateX(0); @include token-utils.use-tokens($_mdc-slots...) { width: calc(100% - #{token-utils.get-token-variable(handle-width)}); } [dir='rtl'] & { left: auto; right: 0; } .mdc-switch--selected & { transform: translateX(100%); [dir='rtl'] & { transform: translateX(-100%); } } } .mdc-switch__handle { display: flex; pointer-events: auto; position: absolute; top: 50%; transform: translateY(-50%); left: 0; right: auto; // Used for M3 animations. Does not affect the M2 slide-toggle // because the width and height are static, and the margin is unused. transition: width 75ms cubic-bezier(0.4, 0, 0.2, 1), height 75ms cubic-bezier(0.4, 0, 0.2, 1), margin 75ms cubic-bezier(0.4, 0, 0.2, 1); @include token-utils.use-tokens($_mdc-slots...) { @include token-utils.create-token-slot(width, handle-width); @include token-utils.create-token-slot(height, handle-height); @include token-utils.create-token-slot(border-radius, handle-shape); } [dir='rtl'] & { left: auto; right: 0; } @include token-utils.use-tokens($_mat-slots...) { .mat-mdc-slide-toggle .mdc-switch--unselected & { @include token-utils.create-token-slot(width, unselected-handle-size); @include token-utils.create-token-slot(height, unselected-handle-size); @include token-utils.create-token-slot(margin, unselected-handle-horizontal-margin); &:has(.mdc-switch__icons) { @include token-utils.create-token-slot(margin, unselected-with-icon-handle-horizontal-margin); } } .mat-mdc-slide-toggle .mdc-switch--selected & { @include token-utils.create-token-slot(width, selected-handle-size); @include token-utils.create-token-slot(height, selected-handle-size); @include token-utils.create-token-slot(margin, selected-handle-horizontal-margin); &:has(.mdc-switch__icons) { @include token-utils.create-token-slot(margin, selected-with-icon-handle-horizontal-margin); } } .mat-mdc-slide-toggle &:has(.mdc-switch__icons) { @include token-utils.create-token-slot(width, with-icon-handle-size); @include token-utils.create-token-slot(height, with-icon-handle-size); } .mat-mdc-slide-toggle .mdc-switch:active:not(.mdc-switch--disabled) & { @include token-utils.create-token-slot(width, pressed-handle-size); @include token-utils.create-token-slot(height, pressed-handle-size); } .mat-mdc-slide-toggle .mdc-switch--selected:active:not(.mdc-switch--disabled) & { @include token-utils.create-token-slot(margin, selected-pressed-handle-horizontal-margin); } .mat-mdc-slide-toggle .mdc-switch--unselected:active:not(.mdc-switch--disabled) & { @include token-utils.create-token-slot(margin, unselected-pressed-handle-horizontal-margin); } .mdc-switch--disabled.mdc-switch--selected &::after { @include token-utils.create-token-slot(opacity, disabled-selected-handle-opacity); } .mdc-switch--disabled.mdc-switch--unselected &::after { @include token-utils.create-token-slot(opacity, disabled-unselected-handle-opacity); } } &::before, &::after { border: 1px solid transparent; border-radius: inherit; box-sizing: border-box; content: ''; width: 100%; height: 100%; left: 0; position: absolute; top: 0; transition: background-color 75ms 0ms cubic-bezier(0.4, 0, 0.2, 1), border-color 75ms 0ms cubic-bezier(0.4, 0, 0.2, 1); z-index: -1; @include cdk.high-contrast { border-color: currentColor; } } @include token-utils.use-tokens($_mdc-slots...) { &::after { .mdc-switch--selected:enabled & { @include token-utils.create-token-slot(background, selected-handle-color); } .mdc-switch--selected:enabled:hover:not(:focus):not(:active) & { @include token-utils.create-token-slot(background, selected-hover-handle-color); } .mdc-switch--selected:enabled:focus:not(:active) & { @include token-utils.create-token-slot(background, selected-focus-handle-color); } .mdc-switch--selected:enabled:active & { @include token-utils.create-token-slot(background, selected-pressed-handle-color); } #{$_interactive-disabled-selector}.mdc-switch--selected:hover:not(:focus):not(:active) &, #{$_interactive-disabled-selector}.mdc-switch--selected:focus:not(:active) &, #{$_interactive-disabled-selector}.mdc-switch--selected:active &, .mdc-switch--selected.mdc-switch--disabled & { @include token-utils.create-token-slot(background, disabled-selected-handle-color); } .mdc-switch--unselected:enabled & { @include token-utils.create-token-slot(background, unselected-handle-color); } .mdc-switch--unselected:enabled:hover:not(:focus):not(:active) & { @include token-utils.create-token-slot(background, unselected-hover-handle-color); } .mdc-switch--unselected:enabled:focus:not(:active) & { @include token-utils.create-token-slot(background, unselected-focus-handle-color); } .mdc-switch--unselected:enabled:active & { @include token-utils.create-token-slot(background, unselected-pressed-handle-color); } .mdc-switch--unselected.mdc-switch--disabled & { @include token-utils.create-token-slot(background, disabled-unselected-handle-color); } } &::before { @include token-utils.create-token-slot(background, handle-surface-color); } } } .mdc-switch__shadow { border-radius: inherit; bottom: 0; left: 0; position: absolute; right: 0; top: 0; @include token-utils.use-tokens($_mdc-slots...) { .mdc-switch:enabled & { @include token-utils.create-token-slot(box-shadow, handle-elevation-shadow); } #{$_interactive-disabled-selector}:hover:not(:focus):not(:active) &, #{$_interactive-disabled-selector}:focus:not(:active) &, #{$_interactive-disabled-selector}:active &, .mdc-switch.mdc-switch--disabled & { @include token-utils.create-token-slot(box-shadow, disabled-handle-elevation-shadow); } } } .mdc-switch__ripple { left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); z-index: -1; @include token-utils.use-tokens($_mdc-slots...) { @include token-utils.create-token-slot(width, state-layer-size); @include token-utils.create-token-slot(height, state-layer-size); } &::after { content: ''; opacity: 0; .mdc-switch--disabled & { display: none; } .mat-mdc-slide-toggle-disabled-interactive & { display: block; } .mdc-switch:hover & { opacity: 0.04; transition: 75ms opacity cubic-bezier(0, 0, 0.2, 1); } // Needs a little more specificity so the :hover styles don't override it. .mat-mdc-slide-toggle.mat-mdc-slide-toggle-focused .mdc-switch & { opacity: 0.12; } @include token-utils.use-tokens($_mdc-slots...) { #{$_interactive-disabled-selector}:enabled:focus &, #{$_interactive-disabled-selector}:enabled:active &, #{$_interactive-disabled-selector}:enabled:hover:not(:focus) &, .mdc-switch--unselected:enabled:hover:not(:focus) & { @include token-utils.create-token-slot(background, unselected-hover-state-layer-color); } .mdc-switch--unselected:enabled:focus & { @include token-utils.create-token-slot(background, unselected-focus-state-layer-color); } .mdc-switch--unselected:enabled:active & { @include token-utils.create-token-slot(background, unselected-pressed-state-layer-color); @include token-utils.create-token-slot(opacity, unselected-pressed-state-layer-opacity); transition: opacity 75ms linear; } .mdc-switch--selected:enabled:hover:not(:focus) & { @include token-utils.create-token-slot(background, selected-hover-state-layer-color); } .mdc-switch--selected:enabled:focus & { @include token-utils.create-token-slot(background, selected-focus-state-layer-color); } .mdc-switch--selected:enabled:active & { @include token-utils.create-token-slot(background, selected-pressed-state-layer-color); @include token-utils.create-token-slot(opacity, selected-pressed-state-layer-opacity); transition: opacity 75ms linear; } } } } .mdc-switch__icons { position: relative; height: 100%; width: 100%; z-index: 1; @include token-utils.use-tokens($_mdc-slots...) { .mdc-switch--disabled.mdc-switch--unselected & { @include token-utils.create-token-slot(opacity, disabled-unselected-icon-opacity); } .mdc-switch--disabled.mdc-switch--selected & { @include token-utils.create-token-slot(opacity, disabled-selected-icon-opacity); } } } .mdc-switch__icon { bottom: 0; left: 0; margin: auto; position: absolute; right: 0; top: 0; opacity: 0; transition: opacity 30ms 0ms cubic-bezier(0.4, 0, 1, 1); @include token-utils.use-tokens($_mdc-slots...) { .mdc-switch--unselected & { @include token-utils.create-token-slot(width, unselected-icon-size); @include token-utils.create-token-slot(height, unselected-icon-size); @include token-utils.create-token-slot(fill, unselected-icon-color); } .mdc-switch--unselected.mdc-switch--disabled & { @include token-utils.create-token-slot(fill, disabled-unselected-icon-color); } .mdc-switch--selected & { @include token-utils.create-token-slot(width, selected-icon-size); @include token-utils.create-token-slot(height, selected-icon-size); @include token-utils.create-token-slot(fill, selected-icon-color); } .mdc-switch--selected.mdc-switch--disabled & { @include token-utils.create-token-slot(fill, disabled-selected-icon-color); } } } .mdc-switch--selected .mdc-switch__icon--on, .mdc-switch--unselected .mdc-switch__icon--off { opacity: 1; transition: opacity 45ms 30ms cubic-bezier(0, 0, 0.2, 1); } .mat-mdc-slide-toggle { @include vendor-prefixes.user-select(none); display: inline-block; -webkit-tap-highlight-color: transparent; // Remove the native outline since we use the ripple for focus indication. outline: 0; // The ripple needs extra specificity so the base ripple styling doesn't override its `position`. .mat-mdc-slide-toggle-ripple, .mdc-switch__ripple::after { @include layout-common.fill(); border-radius: 50%; // Disable pointer events for the ripple container so that it doesn't eat the mouse events meant // for the input. Pointer events can be safely disabled because the ripple trigger element is // the host element. pointer-events: none; // Fixes the ripples not clipping to the border radius on Safari. Uses `:not(:empty)` // in order to avoid creating extra layers when there aren't any ripples. &:not(:empty) { transform: translateZ(0); } } // Needs a little more specificity so the :hover styles don't override it. // For slide-toggles render the focus indicator when we know // the hidden input is focused (slightly different for each control). &.mat-mdc-slide-toggle-focused .mat-focus-indicator::before { content: ''; } .mat-internal-form-field { @include token-utils.use-tokens($_mat-slots...) { @include token-utils.create-token-slot(color, label-text-color); @include token-utils.create-token-slot(font-family, label-text-font); @include token-utils.create-token-slot(line-height, label-text-line-height); @include token-utils.create-token-slot(font-size, label-text-size); @include token-utils.create-token-slot(letter-spacing, label-text-tracking); @include token-utils.create-token-slot(font-weight, label-text-weight); } } .mat-ripple-element { opacity: 0.12; } // Slide-toggle components have to set `border-radius: 50%` in order to support density scaling // which will clip a square focus indicator so we have to turn it into a circle. .mat-focus-indicator::before { border-radius: 50%; } &._mat-animation-noopable { .mdc-switch__handle-track, .mdc-switch__icon, .mdc-switch__handle::before, .mdc-switch__handle::after, .mdc-switch__track::before, .mdc-switch__track::after { transition: none; } } // If our slide-toggle is enabled the cursor on label should appear as a pointer. .mdc-switch:enabled + .mdc-label { cursor: pointer; } // TODO(wagnermaciel): Use our custom token system to emit this css rule. .mdc-switch--disabled + label { color: var(--mdc-switch-disabled-label-text-color); } }