123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- @use '../core/style/vendor-prefixes';
- @use '../core/tokens/m2/mdc/tab-indicator' as tokens-mdc-tab-indicator;
- @use '../core/tokens/m2/mdc/secondary-navigation-tab' as tokens-mdc-secondary-navigation-tab;
- @use '../core/tokens/m2/mat/tab-header' as tokens-mat-tab-header;
- @use '../core/tokens/m2/mat/tab-header-with-background' as tokens-mat-tab-header-with-background;
- @use '../core/tokens/token-utils';
- $mat-tab-animation-duration: 500ms !default;
- // Combines the various structural styles we need for the tab group and tab nav bar.
- @mixin structural-styles {
- .mdc-tab {
- min-width: 90px;
- padding: 0 24px;
- display: flex;
- flex: 1 0 auto;
- justify-content: center;
- box-sizing: border-box;
- border: none;
- outline: none;
- text-align: center;
- white-space: nowrap;
- cursor: pointer;
- z-index: 1;
- }
- .mdc-tab__content {
- display: flex;
- align-items: center;
- justify-content: center;
- height: inherit;
- pointer-events: none;
- }
- .mdc-tab__text-label {
- transition: 150ms color linear;
- display: inline-block;
- line-height: 1;
- z-index: 2;
- }
- .mdc-tab--active .mdc-tab__text-label {
- transition-delay: 100ms;
- }
- ._mat-animation-noopable .mdc-tab__text-label {
- transition: none;
- }
- .mdc-tab-indicator {
- display: flex;
- position: absolute;
- top: 0;
- left: 0;
- justify-content: center;
- width: 100%;
- height: 100%;
- pointer-events: none;
- z-index: 1;
- }
- .mdc-tab-indicator__content {
- transition: var(--mat-tab-animation-duration, 250ms) transform cubic-bezier(0.4, 0, 0.2, 1);
- transform-origin: left;
- opacity: 0;
- }
- .mdc-tab-indicator__content--underline {
- align-self: flex-end;
- box-sizing: border-box;
- width: 100%;
- border-top-style: solid;
- }
- .mdc-tab-indicator--active .mdc-tab-indicator__content {
- opacity: 1;
- }
- ._mat-animation-noopable, .mdc-tab-indicator--no-transition {
- .mdc-tab-indicator__content {
- transition: none;
- }
- }
- .mat-mdc-tab-ripple.mat-mdc-tab-ripple {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
- pointer-events: none;
- }
- }
- @mixin tab {
- -webkit-tap-highlight-color: transparent;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- text-decoration: none;
- // Tabs might be `button` elements so we have to reset the user agent styling.
- background: none;
- @include token-utils.use-tokens(
- tokens-mdc-secondary-navigation-tab.$prefix,
- tokens-mdc-secondary-navigation-tab.get-token-slots()
- ) {
- @include token-utils.create-token-slot(height, container-height);
- }
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- @include token-utils.create-token-slot(font-family, label-text-font);
- @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(line-height, label-text-line-height);
- @include token-utils.create-token-slot(font-weight, label-text-weight);
- }
- &.mdc-tab {
- // MDC's tabs stretch to fit the header by default, whereas stretching on our current ones
- // is an opt-in behavior. Also technically we don't need to combine the two classes, but
- // we need the extra specificity to avoid issues with CSS insertion order.
- flex-grow: 0;
- }
- .mdc-tab-indicator__content--underline {
- @include token-utils.use-tokens(
- tokens-mdc-tab-indicator.$prefix,
- tokens-mdc-tab-indicator.get-token-slots()
- ) {
- @include token-utils.create-token-slot(border-color, active-indicator-color);
- @include token-utils.create-token-slot(border-top-width, active-indicator-height);
- @include token-utils.create-token-slot(border-radius, active-indicator-shape);
- }
- }
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- &:hover .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, inactive-hover-label-text-color);
- }
- &:focus .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, inactive-focus-label-text-color);
- }
- &.mdc-tab--active {
- .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, active-label-text-color);
- }
- .mdc-tab__ripple::before,
- .mat-ripple-element {
- @include token-utils.create-token-slot(background-color, active-ripple-color);
- }
- &:hover {
- .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, active-hover-label-text-color);
- }
- .mdc-tab-indicator__content--underline {
- @include token-utils.create-token-slot(border-color, active-hover-indicator-color);
- }
- }
- &:focus {
- .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, active-focus-label-text-color);
- }
- .mdc-tab-indicator__content--underline {
- @include token-utils.create-token-slot(border-color, active-focus-indicator-color);
- }
- }
- }
- }
- &.mat-mdc-tab-disabled {
- // MDC doesn't support disabled tabs so we need to improvise.
- opacity: 0.4;
- // We use `pointer-events` to make the element unclickable when it's disabled, rather than
- // preventing the default action through JS, because we can't prevent the action reliably
- // due to other directives potentially registering their events earlier. This shouldn't cause
- // the user to click through, because we always have a header behind the tab. Furthermore, this
- // saves us some CSS, because we don't have to add `:not(.mat-mdc-tab-disabled)` to all the
- // hover and focus selectors.
- pointer-events: none;
- // We also need to prevent content from being clickable.
- .mdc-tab__content {
- pointer-events: none;
- }
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- .mdc-tab__ripple::before,
- .mat-ripple-element {
- @include token-utils.create-token-slot(background-color, disabled-ripple-color);
- }
- }
- }
- // Used to render out the background tint when hovered/focused. Usually this is done by
- // MDC's ripple styles, however we're using our own ripples due to size concerns.
- .mdc-tab__ripple::before {
- content: '';
- display: block;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- opacity: 0;
- pointer-events: none;
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- @include token-utils.create-token-slot(background-color, inactive-ripple-color);
- }
- }
- .mdc-tab__text-label {
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- @include token-utils.create-token-slot(color, inactive-label-text-color);
- }
- // We support projecting icons into the tab. These styles ensure that they're centered.
- display: inline-flex;
- align-items: center;
- }
- .mdc-tab__content {
- // Required for `fitInkBarToContent` to work. This used to be included with MDC's
- // `without-ripple` mixin, but that no longer appears to be the case with `static-styles`.
- // Since the latter is ~10kb smaller, we include this one extra style ourselves.
- position: relative;
- // MDC sets `pointer-events: none` on the content which prevents interactions with the
- // nested content. Re-enable it since we allow nesting any content in the tab (see #26195).
- pointer-events: auto;
- }
- // We need to handle the hover and focus indication ourselves, because we don't use MDC's ripple.
- &:hover .mdc-tab__ripple::before {
- opacity: 0.04;
- }
- &.cdk-program-focused,
- &.cdk-keyboard-focused {
- .mdc-tab__ripple::before {
- opacity: 0.12;
- }
- }
- .mat-ripple-element {
- opacity: 0.12;
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- @include token-utils.create-token-slot(background-color, inactive-ripple-color);
- }
- }
- }
- // Structural styles for a tab header. Used by both `mat-tab-header` and `mat-tab-nav-bar`.
- // We need this styles on top of MDC's, because MDC doesn't support pagination like ours.
- @mixin paginated-tab-header {
- .mat-mdc-tab-header {
- display: flex;
- overflow: hidden;
- position: relative;
- flex-shrink: 0;
- }
- .mdc-tab-indicator .mdc-tab-indicator__content {
- transition-duration: var(--mat-tab-animation-duration, 250ms);
- }
- .mat-mdc-tab-header-pagination {
- @include vendor-prefixes.user-select(none);
- position: relative;
- display: none;
- justify-content: center;
- align-items: center;
- min-width: 32px;
- cursor: pointer;
- z-index: 2;
- -webkit-tap-highlight-color: transparent;
- touch-action: none;
- box-sizing: content-box;
- outline: 0;
- &::-moz-focus-inner {
- border: 0;
- }
- .mat-ripple-element {
- opacity: 0.12;
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- @include token-utils.create-token-slot(background-color, inactive-ripple-color);
- }
- }
- .mat-mdc-tab-header-pagination-controls-enabled & {
- display: flex;
- }
- }
- // The pagination control that is displayed on the left side of the tab header.
- .mat-mdc-tab-header-pagination-before,
- .mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after {
- padding-left: 4px;
- .mat-mdc-tab-header-pagination-chevron {
- transform: rotate(-135deg);
- }
- }
- // The pagination control that is displayed on the right side of the tab header.
- .mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before,
- .mat-mdc-tab-header-pagination-after {
- padding-right: 4px;
- .mat-mdc-tab-header-pagination-chevron {
- transform: rotate(45deg);
- }
- }
- .mat-mdc-tab-header-pagination-chevron {
- border-style: solid;
- border-width: 2px 2px 0 0;
- height: 8px;
- width: 8px;
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- @include token-utils.create-token-slot(border-color, pagination-icon-color);
- }
- }
- .mat-mdc-tab-header-pagination-disabled {
- box-shadow: none;
- cursor: default;
- pointer-events: none;
- .mat-mdc-tab-header-pagination-chevron {
- opacity: 0.4;
- }
- }
- .mat-mdc-tab-list {
- flex-grow: 1;
- position: relative;
- transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1);
- ._mat-animation-noopable & {
- transition: none;
- }
- }
- }
- // Structural styles for the element that wraps the paginated header items.
- @mixin paginated-tab-header-item-wrapper($parent) {
- display: flex;
- flex: 1 0 auto;
- // We need to set the parent here explicitly, in order to prevent the alignment
- // from any parent tab groups from propagating down to the children when nesting.
- // Note that these are used as inputs so they shouldn't be changed to `mat-mdc-`.
- [mat-align-tabs='center'] > #{$parent} & {
- justify-content: center;
- }
- [mat-align-tabs='end'] > #{$parent} & {
- justify-content: flex-end;
- }
- // Prevent the header from collapsing when it is a drop list. This is useful,
- // because its height may become zero once all the tabs are dragged out.
- // Note that ideally we would do this by default, rather than only in a drop
- // list, but it ended up being hugely breaking internally.
- .cdk-drop-list &,
- &.cdk-drop-list {
- @include token-utils.use-tokens(
- tokens-mdc-secondary-navigation-tab.$prefix,
- tokens-mdc-secondary-navigation-tab.get-token-slots()
- ) {
- @include token-utils.create-token-slot(min-height, container-height);
- }
- }
- }
- // Structural styles for the element that wraps the paginated container's content.
- // Include a selector for an inverted header if the header may be optionally positioned on the
- // bottom of the content.
- @mixin paginated-tab-header-container($inverted-header-selector: null) {
- display: flex;
- flex-grow: 1;
- overflow: hidden;
- z-index: 1;
- @include token-utils.use-tokens(
- tokens-mat-tab-header.$prefix,
- tokens-mat-tab-header.get-token-slots()
- ) {
- border-bottom-style: solid;
- @include token-utils.create-token-slot(border-bottom-width, divider-height);
- @include token-utils.create-token-slot(border-bottom-color, divider-color);
- @if ($inverted-header-selector) {
- #{$inverted-header-selector} & {
- border-bottom: none;
- border-top-style: solid;
- @include token-utils.create-token-slot(border-top-width, divider-height);
- @include token-utils.create-token-slot(border-top-color, divider-color);
- }
- }
- }
- }
- @mixin paginated-tab-header-with-background($header-selector, $tab-selector) {
- &.mat-tabs-with-background {
- @include token-utils.use-tokens(
- tokens-mat-tab-header-with-background.$prefix,
- tokens-mat-tab-header-with-background.get-token-slots()
- ) {
- // Note that these selectors target direct descendants so
- // that the styles don't apply to any nested tab groups.
- > #{$header-selector}, > .mat-mdc-tab-header-pagination {
- // Set background color for the tab group
- @include token-utils.create-token-slot(background-color, background-color);
- }
- // Note: this is only scoped to primary, because the legacy tabs had the incorrect behavior
- // where setting both a background and `mat-accent` would add the background, but keep
- // accent on the selected tab. There are some internal apps whose design depends on this now
- // so we have to replicate it here.
- &.mat-primary > #{$header-selector} {
- // Set labels to contrast against background
- #{$tab-selector} .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, foreground-color);
- }
- .mdc-tab-indicator__content--underline {
- @include token-utils.create-token-slot(border-color, foreground-color);
- }
- }
- &:not(.mat-primary) > #{$header-selector} {
- #{$tab-selector}:not(.mdc-tab--active) {
- .mdc-tab__text-label {
- @include token-utils.create-token-slot(color, foreground-color);
- }
- .mdc-tab-indicator__content--underline {
- @include token-utils.create-token-slot(border-color, foreground-color);
- }
- }
- }
- > #{$header-selector}, > .mat-mdc-tab-header-pagination {
- .mat-mdc-tab-header-pagination-chevron,
- .mat-focus-indicator::before {
- @include token-utils.create-token-slot(border-color, foreground-color);
- }
- .mat-ripple-element, .mdc-tab__ripple::before {
- @include token-utils.create-token-slot(background-color, foreground-color);
- }
- .mat-mdc-tab-header-pagination-chevron {
- @include token-utils.create-token-slot(color, foreground-color);
- }
- }
- }
- }
- }
|