/* * M3 Expressive's top app bars (AppBarTokens, AppBarSmallTokens, AppBarMediumFlexibleTokens, * AppBarLargeFlexibleTokens, androidx Compose Material 3, Apache-2.0). * * small 64px, title-large title (label-medium subtitle), 4px from the edges, the title 16px * from the start or straight after the navigation icon * center the same, the title centred — a three-column grid, so it shrinks against whatever is * beside it rather than ellipsising underneath it * medium 112px (136px with a subtitle): the 64px row of icons over a headline-medium title * large 120px (152px with a subtitle): a display-small title * search the row holds a search bar, full width to 312px and then half of what is left * * The container is the surface, and surface-container once content scrolls under it. A medium or * large bar collapses into the small one without script moving anything: the bar is sticky at a * negative top, so it scrolls up until only 64px of it remain, while its row of icons is sticky at * 0 inside it and stays put — the big title scrolls away under the row. Script only says when that * has happened (resources/js/app-bar.js), so the small title can fade in and the colour change. * * Trailing actions given as a list collapse into an overflow menu below `medium` and come back * from it, by media query alone: both forms of every action are in the page, and each width hides * the one it does not use, so nothing flashes and no script counts. */ @layer components { [data-app-bar] { --app-bar-row: 4rem; --app-bar-height: var(--app-bar-row); z-index: 20; display: block; min-height: var(--app-bar-height); padding-top: var(--material-safe-top, env(safe-area-inset-top)); background-color: var(--md-sys-color-surface); color: var(--md-sys-color-on-surface); transition: background-color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default); } [data-app-bar][data-variant="medium"] { --app-bar-height: 7rem; } [data-app-bar][data-variant="medium"][data-subtitled] { --app-bar-height: 8.5rem; } [data-app-bar][data-variant="large"] { --app-bar-height: 7.5rem; } [data-app-bar][data-variant="large"][data-subtitled] { --app-bar-height: 9.5rem; } [data-app-bar][data-sticky] { position: sticky; top: 0; } /* `--app-bar-measured` is the bar's real height (a title can wrap), set by app-bar.js. */ [data-app-bar][data-sticky]:is([data-variant="medium"], [data-variant="large"]) { top: calc(var(--app-bar-row) - var(--app-bar-measured, var(--app-bar-height))); } [data-app-bar][data-scrolled] { background-color: var(--md-sys-color-surface-container); } [data-app-bar-row] { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: 0; min-height: var(--app-bar-row); padding-inline: 0.25rem; background-color: inherit; } [data-app-bar-leading], [data-app-bar-trailing] { display: flex; flex: none; align-items: center; color: var(--md-sys-color-on-surface-variant); } [data-app-bar-leading] { color: var(--md-sys-color-on-surface); } [data-app-bar-trailing] { margin-inline-start: auto; } /* `actions` as a list: every action is both an icon button and a menu item, and the width of the window says which one is drawn — up to two trailing icon buttons below `medium` and four from it, an overflow button counted among them (docs/reference/m3/components-navigation-selection-inputs.md § Top app bar → Behaviour and guidelines). The wrappers stay out of the row's layout until a width hides them. */ [data-app-bar-action], [data-app-bar-overflow] { display: contents; } @media (width < 37.5rem) { [data-app-bar-action][data-overflows-compact], [data-app-bar-overflow="medium"] { display: none; } } @media (width >= 37.5rem) { [data-app-bar-overflow="compact"] { display: none; } } [data-app-bar-headline] { display: flex; min-width: 0; flex: 1 1 0%; flex-direction: column; justify-content: center; padding-inline: 0.75rem; } [data-app-bar-row]:has([data-app-bar-leading]) [data-app-bar-headline] { padding-inline-start: 0.25rem; } [data-app-bar-title] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: var(--md-sys-typescale-title-lg); letter-spacing: var(--md-sys-typescale-title-lg-tracking); } [data-app-bar-subtitle] { overflow: hidden; color: var(--md-sys-color-on-surface-variant); text-overflow: ellipsis; white-space: nowrap; font: var(--md-sys-typescale-label-md); letter-spacing: var(--md-sys-typescale-label-md-tracking); } /* Centred: a three-column row — leading, headline, trailing — so the headline is centred in the window and shrinks against whatever is actually beside it. M3 allows two trailing icon buttons, which a fixed inset for one would have let a long title ellipsise underneath. */ [data-app-bar][data-variant="center"] [data-app-bar-row] { display: grid; grid-template-columns: 1fr auto 1fr; } [data-app-bar][data-variant="center"] [data-app-bar-leading] { grid-column: 1; justify-self: start; } [data-app-bar][data-variant="center"] [data-app-bar-headline] { grid-column: 2; min-width: 0; align-items: center; justify-self: center; padding-inline: 0.75rem; text-align: center; } [data-app-bar][data-variant="center"] [data-app-bar-trailing] { grid-column: 3; justify-self: end; margin-inline-start: 0; } /* The search bar fills the space between the leading and trailing elements until it is 312dp wide, and then grows into only half of what is left (docs/reference/m3/components-navigation-selection-inputs.md § Top app bar). The percentage is of the row rather than of that space, which CSS cannot name without measuring it; below 312px the cap never binds, so a phone still gets the whole row. */ [data-app-bar-search] { min-width: 0; max-width: calc(19.5rem + (100% - 19.5rem) / 2); flex: 1 1 0%; margin-inline: auto; padding-inline: 0.25rem; } /* Medium and large: the row's title is the collapsed one, shown once the big one has gone. */ [data-app-bar]:is([data-variant="medium"], [data-variant="large"]) [data-app-bar-row] [data-app-bar-headline] { opacity: 0; transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } [data-app-bar][data-collapsed] [data-app-bar-row] [data-app-bar-headline] { opacity: 1; } /* Heights from the tokens: the title (and subtitle) lines and the space under them fill what the row leaves — 36 + 12px medium, 36 + 20 + 16px with a subtitle; 44 + 12px large, 44 + 24 + 20px. */ [data-app-bar-expanded] { display: flex; min-height: calc(var(--app-bar-height) - var(--app-bar-row)); flex-direction: column; justify-content: flex-end; padding-inline: 1rem; padding-bottom: 0.75rem; } [data-app-bar][data-subtitled] [data-app-bar-expanded] { padding-bottom: 1rem; } [data-app-bar][data-variant="large"][data-subtitled] [data-app-bar-expanded] { padding-bottom: 1.25rem; } [data-app-bar-expanded] [data-app-bar-title] { white-space: normal; font: var(--md-sys-typescale-headline-md); letter-spacing: var(--md-sys-typescale-headline-md-tracking); } [data-app-bar-expanded] [data-app-bar-subtitle] { font: var(--md-sys-typescale-label-lg); letter-spacing: var(--md-sys-typescale-label-lg-tracking); } [data-app-bar][data-variant="large"] [data-app-bar-expanded] [data-app-bar-title] { font: var(--md-sys-typescale-display-sm); letter-spacing: var(--md-sys-typescale-display-sm-tracking); } [data-app-bar][data-variant="large"] [data-app-bar-expanded] [data-app-bar-subtitle] { font: var(--md-sys-typescale-title-md); letter-spacing: var(--md-sys-typescale-title-md-tracking); } }