/* * M3's search: a search bar that opens into a search view (SearchBarTokens, SearchViewTokens, * androidx Compose Material 3, Apache-2.0). * * The bar is a 56px pill in surface-container-high: a leading search icon, the input in * body-large, a clear button once something is typed, and whatever the caller trails it with (an * avatar, an icon button). Focus opens the view. On a medium or wider window the view is docked * over a scrim: the bar grows down into an extra-large-cornered container at elevation 3 that * holds the results. On a compact window it takes the whole screen, and the search icon turns into * a back arrow. `docked` keeps it docked at every width. * * Widths are M3's: never wider than 720px, and the bar grows to that width while it is focused — * `--search-width` is its resting width (the room it is given, by default) and * `--search-open-width` the focused one, so a caller that wants M3's 360px resting bar sets * `--search-width: 22.5rem` on a wrapper. The leading and trailing padding is 24px unfocused and * 16px focused, as the search specs table gives it. * * [data-search] the root; data-open, data-full-screen, data-trigger * [data-search-scrim] over the page while the view is docked * [data-search-trigger] data-trigger="icon" only: the icon button that expands the view * [data-search-bar] the pill, above the view * [data-search-leading], [data-search-field] (the combobox around [data-search-input]), * [data-search-clear], [data-search-trailing] * [data-search-view] the container behind the bar * [data-search-suggestions] before the first keystroke * [data-search-results] once something is typed * [data-search-status] the polite live region that counts whichever list is on screen */ @layer components { [data-search] { --search-height: 3.5rem; /* M3: min 360px, max 720px, and wider while focused. */ --search-width: 100%; --search-open-width: 45rem; } [data-search][data-open] { z-index: 50; } /* Docked, the view opens over the page: M3 puts a scrim under it. Behind the bar and the view inside the root's own stacking context, above everything the page draws outside it. */ [data-search-scrim] { position: fixed; inset: 0; z-index: -1; background-color: color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent); transition-property: opacity, display; transition-duration: var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-effects-fast); transition-behavior: allow-discrete; } @starting-style { [data-search-scrim] { opacity: 0; } } [data-search-bar] { position: relative; z-index: 1; display: flex; align-items: center; gap: 0.25rem; width: min(100%, var(--search-width)); height: var(--search-height); padding-inline: 0.75rem; border-radius: var(--md-sys-shape-corner-full); background-color: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); cursor: text; transition-property: background-color, width, padding-inline; transition-duration: var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-effects-fast); } /* Focused: wider, and the padding drops from 24px to the contained style's 16px. */ [data-search][data-open] [data-search-bar] { width: min(100%, var(--search-open-width)); padding-inline: 0.25rem; } @media (hover: hover) { [data-search]:not([data-open]) [data-search-bar]:hover { background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, var(--md-sys-color-surface-container-high)); } } [data-search][data-open] [data-search-bar] { background-color: transparent; } [data-search-leading], [data-search-clear] { display: grid; flex: none; place-items: center; width: 3rem; height: 3rem; border-radius: var(--md-sys-shape-corner-full); } [data-search-leading] { color: var(--md-sys-color-on-surface); } [data-search-clear] { color: var(--md-sys-color-on-surface-variant); cursor: pointer; } [data-search-bar] button { outline: none; transition: background-color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } @media (hover: hover) { [data-search-bar] button:hover { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 8%, transparent); } } [data-search-bar] button:focus-visible { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 10%, transparent); outline: 3px solid var(--md-sys-color-secondary); outline-offset: -3px; } /* The combobox wrapper is only a role holder; it has to lay out as the input used to. */ [data-search-field] { display: flex; flex: 1 1 0%; min-width: 0; height: 100%; } [data-search-input] { flex: 1 1 0%; min-width: 0; height: 100%; padding-inline: 0.25rem; appearance: none; background: transparent; color: var(--md-sys-color-on-surface); caret-color: var(--md-sys-color-primary); outline: none; font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); } [data-search-input]::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: 1; } [data-search-input]::-webkit-search-cancel-button, [data-search-input]::-webkit-search-decoration { appearance: none; display: none; } [data-search-bar]:has([data-search-input]:placeholder-shown) [data-search-clear] { display: none; } [data-search-trailing] { display: flex; flex: none; align-items: center; gap: 0.25rem; padding-inline-end: 0.25rem; } [data-search][data-open] [data-search-trailing] { display: none; } [data-search-view] { position: absolute; inset-inline: 0; top: 0; display: flex; flex-direction: column; max-width: var(--search-open-width); max-height: min(40rem, 70dvh); padding-top: var(--search-height); overflow: hidden; border-radius: var(--md-sys-shape-corner-xl); background-color: var(--md-sys-color-surface-container-high); box-shadow: var(--md-sys-elevation-3); transform-origin: top; transition-property: opacity, scale, display; transition-duration: var(--md-sys-motion-spatial-fast-duration); transition-timing-function: var(--md-sys-motion-spatial-fast); transition-behavior: allow-discrete; } @starting-style { [data-search-view] { opacity: 0; scale: 1 0.9; } } /* M3: the docked container is at least 240px tall — once there is something in it to be tall about; a search with nothing to show stays the height of its bar. */ [data-search-view]:has([data-search-results], [data-search-suggestions]) { min-height: 15rem; } /* No divider: that belongs to the divided style, which Expressive deprecates in favour of the contained one this file draws. The suggestions stand in the same place, before the first keystroke; only one of the two is ever on screen. */ [data-search-results], [data-search-suggestions] { min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-block: 0.5rem; } /* M3's second entry point: search as a secondary action, one icon button that expands into the full-screen view. The root keeps the button's 48px whether the view is open or not, so a toolbar does not shift under it, and the bar is the view's header rather than a resting bar. */ [data-search][data-trigger="icon"] { flex: none; width: 3rem; height: 3rem; } [data-search-trigger] { display: grid; place-items: center; width: 3rem; height: 3rem; border-radius: var(--md-sys-shape-corner-full); color: var(--md-sys-color-on-surface-variant); cursor: pointer; outline: none; transition: background-color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } @media (hover: hover) { [data-search-trigger]:hover { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 8%, transparent); } } [data-search-trigger]:focus-visible { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 10%, transparent); outline: 3px solid var(--md-sys-color-secondary); outline-offset: -3px; } [data-search-trigger]:active { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 10%, transparent); } [data-search][data-trigger="icon"]:not([data-open]) [data-search-bar] { display: none; } /* Full screen, on a compact window. */ [data-search][data-full-screen] [data-search-bar] { position: fixed; inset: 0 0 auto; width: auto; height: calc(4.5rem + var(--material-safe-top, env(safe-area-inset-top))); padding-top: var(--material-safe-top, env(safe-area-inset-top)); padding-inline: 0.25rem; border-radius: 0; } [data-search][data-full-screen] [data-search-view] { position: fixed; inset: 0; max-width: none; max-height: none; padding-top: calc(4.5rem + var(--material-safe-top, env(safe-area-inset-top))); /* The full-screen layout has its own container role, one step from the docked one. */ background-color: var(--md-sys-color-surface-container-low); border-radius: 0; box-shadow: none; } }