/* * 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: 360px` on a wrapper. The leading and trailing padding is 24px unfocused and * 16px focused, as the search specs table gives it. * * Hovered and focused buttons in the bar take M3's state-layer opacities (tokens/state.css), and the * docked scrim is M3's scrim role at 32%, which no system token names. The breakpoint that makes the * view full screen is decided in resources/js/search.js (`upTo('medium')`, 600px). * * [data-md-search] the root; data-md-open, data-md-full-screen, data-md-trigger * [data-md-search-scrim] over the page while the view is docked * [data-md-search-trigger] data-md-trigger="icon" only: the icon button that expands the view * [data-md-search-bar] the pill, above the view * [data-md-search-leading], [data-md-search-field] (the combobox around * [data-md-search-input]), [data-md-search-clear], [data-md-search-trailing] * [data-md-search-view] the container behind the bar * [data-md-search-suggestions] before the first keystroke * [data-md-search-results] once something is typed; [data-md-search-empty] when nothing is * [data-md-search-status] the polite live region that counts whichever list is on screen */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @import './icon.css'; @layer material.components { [data-md-search] { --search-height: 56px; /* M3: min 360px, max 720px, and wider while focused. */ --search-width: 100%; --search-open-width: 720px; position: relative; } [data-md-search][data-md-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-md-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-md-search-scrim] { opacity: 0; } } [data-md-search-bar] { position: relative; z-index: 1; display: flex; align-items: center; gap: var(--md-sys-measurement-space50); width: min(100%, var(--search-width)); height: var(--search-height); padding-inline: 12px; 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-md-search][data-md-open] [data-md-search-bar] { width: min(100%, var(--search-open-width)); padding-inline: var(--md-sys-measurement-space50); } @media (hover: hover) { [data-md-search]:not([data-md-open]) [data-md-search-bar]:hover { background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), var(--md-sys-color-surface-container-high)); } } [data-md-search][data-md-open] [data-md-search-bar] { background-color: transparent; } [data-md-search-leading], [data-md-search-clear] { display: grid; flex: none; place-items: center; width: var(--md-sys-measurement-space600); height: var(--md-sys-measurement-space600); border-radius: var(--md-sys-shape-corner-full); } [data-md-search-leading] { color: var(--md-sys-color-on-surface); } [data-md-search-clear] { color: var(--md-sys-color-on-surface-variant); cursor: pointer; } [data-md-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-md-search-bar] button:hover { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent); } } [data-md-search-bar] button:focus-visible { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), 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-md-search-field] { display: flex; flex: 1 1 0%; min-width: 0; height: 100%; } [data-md-search-input] { flex: 1 1 0%; min-width: 0; height: 100%; padding-inline: var(--md-sys-measurement-space50); 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-md-search-input]::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: 1; } [data-md-search-input]::-webkit-search-cancel-button, [data-md-search-input]::-webkit-search-decoration { appearance: none; display: none; } [data-md-search-bar]:has([data-md-search-input]:placeholder-shown) [data-md-search-clear] { display: none; } [data-md-search-trailing] { display: flex; flex: none; align-items: center; gap: var(--md-sys-measurement-space50); padding-inline-end: var(--md-sys-measurement-space50); } [data-md-search][data-md-open] [data-md-search-trailing] { display: none; } [data-md-search-view] { position: absolute; inset-inline: 0; top: 0; display: flex; flex-direction: column; max-width: var(--search-open-width); max-height: min(640px, 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-md-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-md-search-view]:has([data-md-search-results], [data-md-search-suggestions]) { min-height: 240px; } /* 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-md-search-results], [data-md-search-suggestions] { min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-block: var(--md-sys-measurement-space100); } /* 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-md-search][data-md-trigger='icon'] { flex: none; width: var(--md-sys-measurement-space600); height: var(--md-sys-measurement-space600); } [data-md-search-trigger] { display: grid; place-items: center; width: var(--md-sys-measurement-space600); height: var(--md-sys-measurement-space600); 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-md-search-trigger]:hover { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent); } } [data-md-search-trigger]:focus-visible { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), transparent); outline: 3px solid var(--md-sys-color-secondary); outline-offset: -3px; } [data-md-search-trigger]:active { background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), transparent); } [data-md-search][data-md-trigger='icon']:not([data-md-open]) [data-md-search-bar] { display: none; } /* Full screen, on a compact window. */ [data-md-search][data-md-full-screen] [data-md-search-bar] { position: fixed; inset: 0 0 auto; width: auto; height: calc(var(--md-sys-measurement-space900) + var(--material-safe-top, env(safe-area-inset-top))); padding-top: var(--material-safe-top, env(safe-area-inset-top)); padding-inline: var(--md-sys-measurement-space50); border-radius: 0; } [data-md-search][data-md-full-screen] [data-md-search-view] { position: fixed; inset: 0; max-width: none; max-height: none; padding-top: calc(var(--md-sys-measurement-space900) + 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; } /* What the caller says when nothing matches: body-medium in on-surface-variant, padded like a list row (16px across, 12px down). */ [data-md-search-empty] { padding: 12px var(--md-sys-measurement-space200); color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-body-md); letter-spacing: var(--md-sys-typescale-body-md-tracking); font-variation-settings: normal; } }