/* liquid-glass. Layout and accessibility for the glass; its filters are set inline by the script, as literal values Safari can read. */ /* The glass sits behind the surface's content, so the surface must be its own stacking context. It must not be a backdrop root itself: no filter, opacity below 1, mask or backdrop-filter, or the glass would only see the surface. */ :has(> .liquid-glass) { isolation: isolate; } .liquid-glass, .liquid-glass-layer, .liquid-glass-hue, .liquid-glass-light { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; } .liquid-glass { z-index: -1; } .liquid-glass[data-fit="fixed"] { position: fixed; } .liquid-glass[data-fit="scroll"] { bottom: auto; } .liquid-glass-layer[data-layer="body"] { transition: backdrop-filter 300ms ease, background-color 300ms ease; } /* Rim layers wait for their masks; unmasked, they would brighten the whole surface. */ .liquid-glass:not([data-ready]) > :is([data-layer="shade"], [data-layer="glow"], [data-layer="edge"], [data-layer="highlight"]) { display: none; } /* Floating glass (the `floating` option) casts a drop shadow from its surface, without a ring or lit line: the glass draws its own edge, and an inset line would sit under the glass and bend with it. Set --liquid-glass-shadow to change it. */ [data-liquid-glass][data-liquid-glass-floating] { box-shadow: var(--liquid-glass-shadow, 0 8px 20px -6px rgb(0 0 0 / 0.5), 0 24px 48px -12px rgb(0 0 0 / 0.6)); } [data-liquid-glass="light"][data-liquid-glass-floating] { box-shadow: var(--liquid-glass-shadow, 0 6px 16px -4px rgb(30 30 40 / 0.1), 0 20px 40px -12px rgb(30 30 40 / 0.16)); } /* A dialog sits higher, so its shadow is deeper and wider (`floating: "dialog"`). */ [data-liquid-glass][data-liquid-glass-floating="dialog"] { box-shadow: var(--liquid-glass-dialog-shadow, 0 6px 12px -4px rgb(0 0 0 / 0.3), 0 20px 40px -12px rgb(0 0 0 / 0.5), 0 48px 96px -24px rgb(0 0 0 / 0.7)); } [data-liquid-glass="light"][data-liquid-glass-floating="dialog"] { box-shadow: var(--liquid-glass-dialog-shadow, 0 6px 12px -4px rgb(30 30 40 / 0.06), 0 20px 40px -12px rgb(30 30 40 / 0.14), 0 48px 96px -24px rgb(30 30 40 / 0.22)); } /* Interactive glass (the `interactive` option) lights up from within: the pointer warms a patch of glass beneath it, and a press blooms out from there. Letting go, the light fades where it spread and only then gathers back. Plain alpha, never a blend mode, which would isolate the glass and cut it off from the page. */ @property --liquid-glass-light-size { syntax: ""; inherits: false; initial-value: 56px; } @property --liquid-glass-light { syntax: ""; inherits: false; initial-value: 0; } .liquid-glass-light { --liquid-glass-light-size: 56px; --liquid-glass-light: 0; background: radial-gradient( circle var(--liquid-glass-light-size) at var(--liquid-glass-x, -999px) var(--liquid-glass-y, -999px), rgb(255 255 255 / var(--liquid-glass-light)), rgb(255 255 255 / calc(var(--liquid-glass-light) * 0.3)) 40%, transparent ); transition: --liquid-glass-light 360ms ease-out, --liquid-glass-light-size 240ms ease-out 360ms; } [data-liquid-glass-pointer] > .liquid-glass > .liquid-glass-light { --liquid-glass-light: 0.07; } [data-liquid-glass-pressed] > .liquid-glass > .liquid-glass-light { --liquid-glass-light-size: 150px; --liquid-glass-light: 0.24; transition: --liquid-glass-light 90ms ease-out, --liquid-glass-light-size 450ms cubic-bezier(0.23, 1, 0.32, 1); } [data-liquid-glass="light"][data-liquid-glass-pointer] > .liquid-glass > .liquid-glass-light { --liquid-glass-light: 0.3; } [data-liquid-glass="light"][data-liquid-glass-pressed] > .liquid-glass > .liquid-glass-light { --liquid-glass-light: 0.6; } /* Controls on a glass surface don't get glass of their own: Apple's guidance is to avoid glass on glass and make them a thin part of the material. A well brightens the glass beneath it. No element between it and the surface may be a backdrop root. */ [data-liquid-glass] .liquid-glass-well { background: transparent; -webkit-backdrop-filter: brightness(1.45); backdrop-filter: brightness(1.45); transition: backdrop-filter 150ms ease-out, scale 150ms ease-out, color 150ms ease-out; } [data-liquid-glass] .liquid-glass-well:hover { -webkit-backdrop-filter: brightness(1.8); backdrop-filter: brightness(1.8); } [data-liquid-glass="light"] .liquid-glass-well { -webkit-backdrop-filter: brightness(0.92); backdrop-filter: brightness(0.92); } [data-liquid-glass="light"] .liquid-glass-well:hover { -webkit-backdrop-filter: brightness(0.86); backdrop-filter: brightness(0.86); } /* A well can't fade with its surface's contents, or the glass beneath would blank, so it eases back to clear glass as the surface dematerializes. */ [data-liquid-glass][data-liquid-glass-leaving] .liquid-glass-well { -webkit-backdrop-filter: brightness(1); backdrop-filter: brightness(1); } /* Materializing, for surfaces that appear and disappear under CSS control: run these on .liquid-glass-layer with the surface's own timing. The surface must not fade or scale instead: opacity hides the glass, and a changing scale leaves Chromium's backdrop-filter with a blank backdrop for frames. The script sets each layer's --liquid-glass-clear, --liquid-glass-full and --liquid-glass-wash. materialize() and dematerialize() do the same from script. */ @keyframes liquid-glass-materialize { from { backdrop-filter: var(--liquid-glass-clear); background-color: transparent; } to { backdrop-filter: var(--liquid-glass-full); background-color: var(--liquid-glass-wash, transparent); } } @keyframes liquid-glass-dematerialize { from { backdrop-filter: var(--liquid-glass-full); background-color: var(--liquid-glass-wash, transparent); } to { backdrop-filter: var(--liquid-glass-clear); background-color: transparent; } } /* Tabs (attachLiquidGlassTabs). The selected tab is a well in the track's glass; moving, the thumb lifts into a clear lens above the labels and drops back in. Every fade is a filter transition, never opacity: Chromium drops a backdrop filter for frames while its element fades. */ [data-liquid-glass-tabs] { position: relative; isolation: isolate; -webkit-user-select: none; user-select: none; } /* On another glass surface the track is a recessed tint, not glass of its own. */ [data-liquid-glass-tabs-on-glass] { background-color: rgb(0 0 0 / 0.2); box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.25); } [data-liquid-glass-tabs-on-glass][data-liquid-glass-tabs-appearance="light"] { background-color: rgb(0 0 0 / 0.045); box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.06); } [data-liquid-glass-tab] { position: relative; z-index: 1; } .liquid-glass-tabs-light, .liquid-glass-tabs-well, .liquid-glass-tabs-lens { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; } /* The pointer lights the glass beneath it. */ @property --liquid-glass-tabs-light-size { syntax: ""; inherits: false; initial-value: 56px; } .liquid-glass-tabs-light { z-index: -1; --liquid-glass-tabs-light-size: 56px; -webkit-mask-image: radial-gradient(circle var(--liquid-glass-tabs-light-size) at var(--liquid-glass-tabs-x, -999px) var(--liquid-glass-tabs-y, -999px), #000, transparent); mask-image: radial-gradient(circle var(--liquid-glass-tabs-light-size) at var(--liquid-glass-tabs-x, -999px) var(--liquid-glass-tabs-y, -999px), #000, transparent); transition: backdrop-filter 240ms ease-out, --liquid-glass-tabs-light-size 300ms ease-out; } [data-liquid-glass-tabs-pointer] > .liquid-glass-tabs-light { -webkit-backdrop-filter: brightness(1.3) saturate(1.15); backdrop-filter: brightness(1.3) saturate(1.15); } [data-liquid-glass-tabs-appearance="light"][data-liquid-glass-tabs-pointer] > .liquid-glass-tabs-light { -webkit-backdrop-filter: brightness(1.05) saturate(1.1); backdrop-filter: brightness(1.05) saturate(1.1); } /* As iOS's tab bar does, a press brightens the glass under it, and letting go the light spreads across the bar as it fades. */ [data-liquid-glass-tabs-press] > .liquid-glass-tabs-light { --liquid-glass-tabs-light-size: 40px; -webkit-backdrop-filter: brightness(1.6) saturate(1.2); backdrop-filter: brightness(1.6) saturate(1.2); transition: backdrop-filter 100ms ease-out, --liquid-glass-tabs-light-size 100ms ease-out; } [data-liquid-glass-tabs-appearance="light"][data-liquid-glass-tabs-press] > .liquid-glass-tabs-light { -webkit-backdrop-filter: brightness(1.05) saturate(1.15); backdrop-filter: brightness(1.05) saturate(1.15); } [data-liquid-glass-tabs-release] > .liquid-glass-tabs-light { --liquid-glass-tabs-light-size: 160px; -webkit-backdrop-filter: brightness(1); backdrop-filter: brightness(1); transition: backdrop-filter 500ms ease-out, --liquid-glass-tabs-light-size 500ms cubic-bezier(0.23, 1, 0.32, 1); } @media (prefers-reduced-motion: reduce) { [data-liquid-glass-tabs-press] > .liquid-glass-tabs-light, [data-liquid-glass-tabs-release] > .liquid-glass-tabs-light { --liquid-glass-tabs-light-size: 56px; } } /* Hovering another tab brightens its patch of glass, as the well does. */ [data-liquid-glass-tab]::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; pointer-events: none; transition: backdrop-filter 150ms ease-out; } [data-liquid-glass-tab]:not([data-liquid-glass-tab-selected]):hover::before { -webkit-backdrop-filter: brightness(1.2); backdrop-filter: brightness(1.2); } [data-liquid-glass-tabs-appearance="light"] [data-liquid-glass-tab]:not([data-liquid-glass-tab-selected]):hover::before { -webkit-backdrop-filter: brightness(0.96); backdrop-filter: brightness(0.96); } /* The selected tab can be dragged across the others. */ [data-liquid-glass-tabs="horizontal"] [data-liquid-glass-tab-selected] { touch-action: pan-y; } /* Under the labels at rest, over them while it is a lens (data-raised), so it can bend and magnify them. It goes back under them as it finishes dropping in, by when it no longer magnifies. Its shadow grows with the lift (--liquid-glass-tabs-lift, 0 to 1). */ .liquid-glass-tabs-thumb { position: absolute; top: 0; left: 0; z-index: 0; pointer-events: none; box-shadow: 0 8px 20px -6px rgb(0 0 0 / calc(0.35 * var(--liquid-glass-tabs-lift, 0))), 0 2px 6px -2px rgb(0 0 0 / calc(0.15 * var(--liquid-glass-tabs-lift, 0))); } .liquid-glass-tabs-thumb:not([data-shown]) { visibility: hidden; } .liquid-glass-tabs-thumb[data-raised] { z-index: 2; } [data-liquid-glass-tabs-appearance="light"] > .liquid-glass-tabs-thumb { box-shadow: 0 8px 20px -6px rgb(0 0 0 / calc(0.18 * var(--liquid-glass-tabs-lift, 0))), 0 2px 6px -2px rgb(0 0 0 / calc(0.08 * var(--liquid-glass-tabs-lift, 0))); } /* The selected tab is a well in the glass: brighter glass in dark, dimmer in light, with nothing painted over it. It clears as the lens lifts and refills after it drops, by --liquid-glass-tabs-platter (1 at rest, 0 clear), and the pointer over it brightens it further. Safari ignores custom properties in -webkit-backdrop-filter, so it keeps the resting values. */ .liquid-glass-tabs-well { -webkit-backdrop-filter: brightness(1.45); backdrop-filter: brightness(calc(1 + 0.45 * var(--liquid-glass-tabs-platter, 1))); box-shadow: 0 1px 3px rgb(0 0 0 / calc(0.3 * var(--liquid-glass-tabs-platter, 1))); transition: backdrop-filter 180ms ease-out, box-shadow 180ms ease-out; } [data-liquid-glass-tabs-hover] > .liquid-glass-tabs-thumb > .liquid-glass-tabs-well { -webkit-backdrop-filter: brightness(1.7); backdrop-filter: brightness(calc(1 + 0.7 * var(--liquid-glass-tabs-platter, 1))); } [data-liquid-glass-tabs-appearance="light"] > .liquid-glass-tabs-thumb > .liquid-glass-tabs-well { -webkit-backdrop-filter: brightness(0.92); backdrop-filter: brightness(calc(1 - 0.08 * var(--liquid-glass-tabs-platter, 1))); box-shadow: none; } [data-liquid-glass-tabs-appearance="light"][data-liquid-glass-tabs-hover] > .liquid-glass-tabs-thumb > .liquid-glass-tabs-well { -webkit-backdrop-filter: brightness(0.86); backdrop-filter: brightness(calc(1 - 0.14 * var(--liquid-glass-tabs-platter, 1))); } /* Moving, those values change every frame; a transition would only lag them. */ .liquid-glass-tabs-thumb[data-moving] > .liquid-glass-tabs-well { transition: none; } /* The thumb is a clear lens over the well: the rim bends what is beneath, and the body only lifts it a little. At rest that is the track under it; lifted, the labels. */ .liquid-glass-tabs-lens { isolation: isolate; z-index: 1; } .liquid-glass-tabs-lens .liquid-glass-layer { transition: backdrop-filter 180ms ease-out; } .liquid-glass-tabs-thumb[data-moving] .liquid-glass-tabs-lens .liquid-glass-layer { transition: none; } /* With more contrast asked for, the sides darken and the edge becomes the contrasting border Apple gives glass under Increase Contrast. */ @media (prefers-contrast: more) { .liquid-glass[data-appearance="dark"] > [data-layer="shade"] { -webkit-backdrop-filter: brightness(0.1) !important; backdrop-filter: brightness(0.1) !important; } .liquid-glass[data-appearance="light"] > [data-layer="shade"] { -webkit-backdrop-filter: brightness(0.6) !important; backdrop-filter: brightness(0.6) !important; } .liquid-glass[data-appearance="dark"] > [data-layer="edge"] { -webkit-backdrop-filter: brightness(0.3) !important; backdrop-filter: brightness(0.3) !important; } .liquid-glass[data-appearance="light"] > [data-layer="edge"] { -webkit-backdrop-filter: brightness(0.5) !important; backdrop-filter: brightness(0.5) !important; } } @media (prefers-reduced-transparency: reduce) { .liquid-glass { display: none; } [data-liquid-glass] { background: var(--liquid-glass-solid); } [data-liquid-glass] .liquid-glass-well { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgb(255 255 255 / 0.05); } .liquid-glass-tabs-light, .liquid-glass-tabs-lens { display: none; } [data-liquid-glass-tab]::before { content: none; } /* Over the light and hover wells, which are more specific. */ [data-liquid-glass-tabs] > .liquid-glass-tabs-thumb > .liquid-glass-tabs-well { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; background-color: rgb(255 255 255 / 0.1); } } @media (forced-colors: active) { .liquid-glass { display: none; } .liquid-glass-tabs-well { outline: 1px solid CanvasText; } }