/* * The menus: the exposed dropdown a field drops open, and what `` cannot say in utilities. * * Two lists wear the dropdown, so a form reads as one family: * * - ``'s: the native with no field chrome — M3's menus embed a text field, not a text field component. */ [data-menu-filter] { position: sticky; inset-block-start: 0; z-index: 1; display: flex; align-items: center; gap: 0.75rem; min-block-size: 3rem; padding-inline: 1rem; border-block-end: 1px solid var(--md-sys-color-outline-variant); background: var(--material-menu-surface, var(--md-sys-color-surface-container-low)); } [data-menu-filter] input { flex: 1; min-inline-size: 0; border: 0; background: transparent; padding: 0; color: inherit; font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); outline: none; } [data-menu-filter] input::placeholder { color: color-mix(in srgb, var(--material-menu-ink, var(--md-sys-color-on-surface)) 70%, transparent); } /* The filter hides what the query leaves out with the `hidden` attribute; a row's own `display: flex` utility would otherwise beat the user agent's `[hidden] { display: none }`. */ :is([data-menu], [data-menu-sheet]) [hidden] { display: none; } /* `data-active` is the row the arrow keys are on while the focus stays in the field — the state layer the roving focus would have drawn, without taking the focus off the field. A chosen or current row keeps its own colour. */ :is([data-menu], [data-menu-sheet]) [role^="menuitem"][data-active]:not([aria-checked="true"], [aria-current="page"]) { background: color-mix(in srgb, var(--material-menu-ink, var(--md-sys-color-on-surface)) 8%, transparent); } /* * ``: the copy of the list in the bottom sheet a compact window opens * instead of the popover. The sheet is surface-container-low, as the standard menu is, so the * fallbacks above already paint its field; the field reaches both edges of the sheet and holds * its icon where the rows hold theirs, 24px in (the list is 8px in, a row's own padding 16px). */ [data-menu-sheet] [data-menu-filter] { padding-inline: 1.5rem; } /* * A submenu there opens in place: its list stands under the item, inset by 16px, instead of in a * popover beside it, which a sheet has no room for (M3 calls submenus "best suited to large * screens", docs/reference/m3/components-actions-communication-containment.md § Menus). The * markup is the popover's, never shown as one: the user agent's `[popover]` box is undone, the * list is shown while its item says `aria-expanded="true"`, and it fades in on the effects track. */ [data-menu-sheet] [data-submenu] { position: static; inset: auto; inline-size: auto; max-inline-size: none; block-size: auto; max-block-size: none; margin: 0; padding-block: 0.125rem; padding-inline: 1rem 0; overflow: visible; border-radius: 0; background: transparent; color: inherit; box-shadow: none; opacity: 1; scale: none; transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } [data-menu-sheet] [aria-expanded="true"] + [data-submenu] { display: block; @starting-style { opacity: 0; } } /* The chevron turns from the way a popover would open to the way this list did: down, which is a quarter turn the other way on the chevron `rtl:-scale-x-100` mirrors — so the same RTL test. */ [data-menu-sheet] [data-submenu-chevron] { transition: rotate var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast); } [data-menu-sheet] [aria-expanded="true"] > [data-submenu-chevron] { rotate: 90deg; &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) { rotate: -90deg; } } [data-md-field-menu] { max-block-size: 18rem; overflow-y: auto; padding-block: 0.5rem; border-radius: var(--md-sys-shape-corner-xs); background: var(--md-sys-color-surface-container); box-shadow: var(--md-sys-elevation-2); } /* As a popover (``), it hangs under its field, as wide, and goes above only when there is no room below. */ [data-md-field-menu][popover] { inset: auto; top: anchor(bottom); left: anchor(left); width: anchor-size(width); margin: 0.25rem 0; border: 0; padding-inline: 0; color: var(--md-sys-color-on-surface); position-try-fallbacks: flip-block; } [data-md-field-option][aria-disabled="true"] { color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent); cursor: default; } [data-md-field-option] { display: flex; align-items: center; gap: 0.75rem; min-block-size: 3rem; padding-inline: 1rem; color: var(--md-sys-color-on-surface); font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); cursor: pointer; } /* `data-active` is the row the arrow keys or the pointer are on. */ [data-md-field-option][data-active] { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); } [data-md-field-option][aria-selected="true"] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); } [data-md-field-option][aria-selected="true"][data-active] { background: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 8%, var(--md-sys-color-secondary-container)); } [data-md-field-check] { margin-inline-start: auto; } [data-md-field-option]:not([aria-selected="true"]) [data-md-field-check] { visibility: hidden; } @supports (appearance: base-select) { select[data-md-field-control], select[data-md-field-control]::picker(select) { appearance: base-select; } /* The menu. The browser anchors it to the select; the select covers its whole field (field.css), so the menu is at least the field's width. It hangs under the field and goes above only when it does not fit there — the browser's own order tries the roomier side first, which opened a menu upwards with room to spare under it. It opens with a fade and a grow on the spatial spring and closes with a fade, as `` does. */ select[data-md-field-control]::picker(select) { position-try-order: normal; max-block-size: 18rem; max-inline-size: calc(100vw - 2rem); margin-block: 0.25rem; padding-block: 0.5rem; border: 0; border-radius: var(--md-sys-shape-corner-xs); background: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface); box-shadow: var(--md-sys-elevation-2); opacity: 0; transform-origin: top; transition-property: opacity, display, overlay; transition-duration: var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-effects-fast); transition-behavior: allow-discrete; } select[data-md-field-control]:open::picker(select) { opacity: 1; transition-property: opacity, scale, display, overlay; transition-duration: var(--md-sys-motion-spatial-fast-duration); transition-timing-function: var(--md-sys-motion-spatial-fast); } @starting-style { select[data-md-field-control]:open::picker(select) { opacity: 0; scale: 0.95; } } select[data-md-field-control] option { display: flex; align-items: center; gap: 0.75rem; min-block-size: 3rem; padding-block: 0; padding-inline: 1rem; background: transparent; color: var(--md-sys-color-on-surface); font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); outline: none; cursor: pointer; } @media (hover: hover) { select[data-md-field-control] option:hover { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); } } select[data-md-field-control] option:focus-visible { background: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent); } select[data-md-field-control] option:checked { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); } @media (hover: hover) { select[data-md-field-control] option:checked:hover { background: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 8%, var(--md-sys-color-secondary-container)); } } select[data-md-field-control] option:checked:focus-visible { background: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 10%, var(--md-sys-color-secondary-container)); } select[data-md-field-control] option:disabled { background: transparent; color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent); cursor: default; } /* The tick, at the row's end: the browser draws it before the label, and hides it on every row but the chosen one. The Material Symbol, as a mask, so it takes the row's ink. */ select[data-md-field-control] option::checkmark { content: ""; order: 1; flex: none; inline-size: 1.5rem; block-size: 1.5rem; margin-inline-start: auto; background-color: currentColor; mask: url("../../svg/symbols/outlined/check.svg") center / contain no-repeat; } }