@use '@angular/cdk'; @use '../core/tokens/m2/mat/sidenav' as tokens-mat-sidenav; @use '../core/tokens/token-utils'; @use '../core/style/variables'; @use '../core/style/layout-common'; $drawer-content-z-index: 1; $drawer-side-drawer-z-index: 2; $drawer-backdrop-z-index: 3; $drawer-over-drawer-z-index: 4; // Mixin that creates a new stacking context. // see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context @mixin drawer-stacking-context($z-index: 1) { position: relative; // Use a z-index to create a new stacking context. (We can't use transform because it breaks fixed // positioning inside of the transformed element). z-index: $z-index; } .mat-drawer-container { // We need a stacking context here so that the backdrop and drawers are clipped to the // MatDrawerContainer. This creates a new z-index stack so we use low numbered z-indices. // We create another stacking context in the '.mat-drawer-content' and in each drawer so that // the application content does not get messed up with our own CSS. @include drawer-stacking-context(); @include token-utils.use-tokens( tokens-mat-sidenav.$prefix, tokens-mat-sidenav.get-token-slots() ) { @include token-utils.create-token-slot(color, content-text-color); @include token-utils.create-token-slot(background-color, content-background-color); } box-sizing: border-box; // Need this to take up space in the layout. display: block; // Hide the drawers when they're closed. overflow: hidden; // TODO(hansl): Update this with a more robust solution. &[fullscreen] { @include layout-common.fill(); &.mat-drawer-container-has-open { overflow: hidden; } } // When the consumer explicitly enabled the backdrop, // we have to pull the side drawers above it. &.mat-drawer-container-explicit-backdrop .mat-drawer-side { z-index: $drawer-backdrop-z-index; } // Note that the `NoopAnimationsModule` is being handled inside of the component code. &.ng-animate-disabled, .ng-animate-disabled & { .mat-drawer-backdrop, .mat-drawer-content { transition: none; } } } .mat-drawer-backdrop { @include layout-common.fill(); display: block; // Because of the new stacking context, the z-index stack is new and we can use our own // numbers. z-index: $drawer-backdrop-z-index; // We use 'visibility: hidden | visible' because 'display: none' will not animate any // transitions, while visibility will interpolate transitions properly. // see https://developer.mozilla.org/en-US/docs/Web/CSS/visibility, the Interpolation // section. visibility: hidden; &.mat-drawer-shown { visibility: visible; @include token-utils.use-tokens( tokens-mat-sidenav.$prefix, tokens-mat-sidenav.get-token-slots() ) { @include token-utils.create-token-slot(background-color, scrim-color); } } .mat-drawer-transition & { transition: { duration: variables.$swift-ease-out-duration; timing-function: variables.$swift-ease-out-timing-function; property: background-color, visibility; } } @include cdk.high-contrast { opacity: 0.5; } } .mat-drawer-content { @include drawer-stacking-context($drawer-content-z-index); display: block; height: 100%; overflow: auto; &.mat-drawer-content-hidden { opacity: 0; } .mat-drawer-transition & { transition: { duration: variables.$swift-ease-out-duration; timing-function: variables.$swift-ease-out-timing-function; property: transform, margin-left, margin-right; } } } .mat-drawer { $high-contrast-border: solid 1px currentColor; @include drawer-stacking-context($drawer-over-drawer-z-index); @include token-utils.use-tokens( tokens-mat-sidenav.$prefix, tokens-mat-sidenav.get-token-slots() ) { @include token-utils.create-token-slot(color, container-text-color); @include token-utils.create-token-slot(box-shadow, container-elevation-shadow); @include token-utils.create-token-slot(background-color, container-background-color); @include token-utils.create-token-slot(border-top-right-radius, container-shape); @include token-utils.create-token-slot(border-bottom-right-radius, container-shape); @include token-utils.create-token-slot(width, container-width); } display: block; position: absolute; top: 0; bottom: 0; z-index: 3; outline: 0; box-sizing: border-box; overflow-y: auto; // TODO(kara): revisit scrolling behavior for drawers transform: translate3d(-100%, 0, 0); &, [dir='rtl'] &.mat-drawer-end { @include cdk.high-contrast { border-right: $high-contrast-border; } } [dir='rtl'] &, &.mat-drawer-end { @include cdk.high-contrast { border-left: $high-contrast-border; border-right: none; } } &.mat-drawer-side { z-index: $drawer-side-drawer-z-index; } &.mat-drawer-end { right: 0; transform: translate3d(100%, 0, 0); @include token-utils.use-tokens( tokens-mat-sidenav.$prefix, tokens-mat-sidenav.get-token-slots() ) { @include token-utils.create-token-slot(border-top-left-radius, container-shape); @include token-utils.create-token-slot(border-bottom-left-radius, container-shape); border-top-right-radius: 0; border-bottom-right-radius: 0; } } [dir='rtl'] & { @include token-utils.use-tokens( tokens-mat-sidenav.$prefix, tokens-mat-sidenav.get-token-slots() ) { @include token-utils.create-token-slot(border-top-left-radius, container-shape); @include token-utils.create-token-slot(border-bottom-left-radius, container-shape); border-top-right-radius: 0; border-bottom-right-radius: 0; transform: translate3d(100%, 0, 0); &.mat-drawer-end { @include token-utils.create-token-slot(border-top-right-radius, container-shape); @include token-utils.create-token-slot(border-bottom-right-radius, container-shape); border-top-left-radius: 0; border-bottom-left-radius: 0; left: 0; right: auto; transform: translate3d(-100%, 0, 0); } } } // Usually the `visibility: hidden` added by the animation is enough to prevent focus from // entering the hidden drawer content, but children with their own `visibility` can override it. // This is a fallback that completely hides the content when the element becomes hidden. // Note that we can't do this in the animation definition, because the style gets recomputed too // late, breaking the animation because Angular didn't have time to figure out the target // transform. This can also be achieved with JS, but it has issues when starting an // animation before the previous one has finished. &[style*='visibility: hidden'] { display: none; } } .mat-drawer-side { box-shadow: none; @include token-utils.use-tokens( tokens-mat-sidenav.$prefix, tokens-mat-sidenav.get-token-slots() ) { @include token-utils.create-token-slot(border-right-color, container-divider-color); border-right-width: 1px; border-right-style: solid; &.mat-drawer-end { @include token-utils.create-token-slot(border-left-color, container-divider-color); border-left-width: 1px; border-left-style: solid; border-right: none; } [dir='rtl'] & { @include token-utils.create-token-slot(border-left-color, container-divider-color); border-left-width: 1px; border-left-style: solid; border-right: none; // Clears the default LTR border. &.mat-drawer-end { @include token-utils.create-token-slot(border-right-color, container-divider-color); border-right-width: 1px; border-right-style: solid; border-left: none; } } } } // Note that this div isn't strictly necessary on all browsers, however we need it in // order to avoid a layout issue in Chrome. The issue is that in RTL mode the browser doesn't // account for the sidenav's scrollbar while positioning, which ends up pushing it partially // out of the screen. We work around the issue by having the scrollbar be on this inner container. .mat-drawer-inner-container { width: 100%; height: 100%; overflow: auto; } .mat-sidenav-fixed { position: fixed; }