Expressive deprecates the divided style, so the results lose their divider; the full-screen layout takes its own surface-container-low, one step from the docked one; the docked view opens over a scrim, as M3's variants table says it does; and the bar is bounded at M3's 720px, grows to that width while focused, and rests with the 24px leading and trailing padding the specs table gives an unfocused bar. --search-width sets the resting width for M3's 360px bar. Plan step 20, findings IN-07, IN-08, IN-09, IN-20 and IN-21. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
239 lines
8.1 KiB
CSS
239 lines
8.1 KiB
CSS
/*
|
|
* 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-search-scrim] over the page while the view is docked
|
|
* [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-results]
|
|
* [data-search-status] the polite live region that counts the results
|
|
*/
|
|
|
|
@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);
|
|
/* M3: the docked container is at least 240px tall and at most two thirds of the window. */
|
|
min-height: 15rem;
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* No divider: that belongs to the divided style, which Expressive deprecates in favour of the
|
|
contained one this file draws. */
|
|
[data-search-results] {
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
overscroll-behavior: contain;
|
|
padding-block: 0.5rem;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|