Foundations · Yugen Labs
Glass
A translucent material for the navigation layer that floats above content: toolbars, tab bars, sidebars, menus and transient surfaces. Content stays in focus; glass only sits on the control layer.
Overview
Inspired by Liquid Glass, adapted to the web and to Yugen's tokens. Turn it on per component with material="glass"; the default look doesn't change. Scroll inside the frame below: photos slide under the top bar and the tab bar, and the tab bar minimizes when you scroll down.
import { Toolbar, ToolbarButton, TabsList, TabsTrigger, Tabs, useScrollMinimize } from "yugen-ui";
function TripBars() {
const minimized = useScrollMinimize();
return (
<>
<header className="scroll-edge sticky top-0 z-10 flex justify-center pt-3">
<Toolbar material="glass" aria-label="Trip actions">
<ToolbarButton icon={<Share20Regular />} aria-label="Share" />
</Toolbar>
</header>
{/* … content … */}
<Tabs defaultValue="home" className="fixed inset-x-0 bottom-3 flex justify-center">
<TabsList material="glass" minimized={minimized} aria-label="Sections">
<TabsTrigger value="home" icon={<Home20Regular />}>Home</TabsTrigger>
</TabsList>
</Tabs>
</>
);
}Variants
glass is the general one and follows the light / dark theme. glass-clear goes over photos and media: always put glass-scrim on the image so white text stays legible. glass-tinted (Button appearance="glass-prominent") is for one primary action.
Refraction
Like light through the rim of a glass, the edges of a glass surface bend what is behind them; the middle stays frosted so text keeps its contrast. It shows best over photos.

The red bridge over the moat
Photo: mko294, CC BY 4.0
import { GlassRefraction, YugenProvider } from "yugen-ui";
export function App() {
return (
<YugenProvider>
<GlassRefraction />
{/* … app … */}
</YugenProvider>
);
}- Render <GlassRefraction /> once at the root of the app. It watches every glass, glass-clear and glass-tinted element (portals included) and builds one SVG filter per size and corner radius.
- Only Chromium browsers draw SVG filters in backdrop-filter. Safari and Firefox keep the regular glass; nothing else to do.
- Off wherever blur is off: Reduce transparency, Increase contrast, forced colors, the high-contrast theme, glass inside glass. Surfaces larger than 400,000 px² keep the regular glass.
Components
| Component | Prop | Notes |
|---|---|---|
| Toolbar | material | Floating capsule bar; buttons inside get concentric corners. |
| TabsList | material · minimized | The selected tab is a rounded fill. minimized keeps only the icons (use with useScrollMinimize). |
| Nav | material | Floating sidebar with padding and rounded corners. |
| SearchBox / Input | appearance="glass" | A field inside a glass bar. |
| Button | appearance="glass" · "glass-prominent" | A button over content; prominent uses the brand color. |
| MenuPopover · PopoverSurface · TooltipContent | material | Transient surfaces opened from a glass bar. Menu and Popover grow out of the button that opens them and shrink back into it (unless motion is reduced). |
| Slider · Switch | material="glass" | The thumb turns to glass while pressed or dragged: larger on Slider, longer on Switch. |
| GlassRefraction | — | Render once at the app root: the edges of every glass surface refract (Chromium). |
| DialogSurface · DrawerSurface · SheetContent | material | A glass bottom sheet is inset from the screen edges and turns opaque at the highest snap point. |
Glass for the whole app
glassNavigationDefaults is a set of default props for YugenProvider: every navigation component with glass gets material="glass" in one place. A material prop on an instance still wins, such as glass-clear over a photo.
import { YugenProvider, glassNavigationDefaults } from "yugen-ui";
<YugenProvider applyToDocument defaults={glassNavigationDefaults}>
<App />
</YugenProvider>
// Together with other defaults
<YugenProvider applyToDocument defaults={{ ...glassNavigationDefaults, Button: { shape: "circular" } }}>
<App />
</YugenProvider>It covers Toolbar, TabsList, Nav, MenuPopover, MenuSubPopover, PopoverSurface, TooltipContent, DialogSurface, DrawerSurface and SheetContent. See Default props & density on the Theming page.
Utilities
| Utility | Use |
|---|---|
glass | Theme-aware glass background with an edge highlight, plus text / hover tokens that keep contrast. |
glass-clear | Glass over photos / media, with white text. |
glass-tinted | Brand-colored glass for one primary action. |
glass-scrim | A dimming layer on the image under glass-clear. |
scroll-edge · scroll-edge-above | Content scrolling under a sticky bar area blurs and fades out, instead of a divider. Put it on the full-width bar area. |
rounded-concentric | Inner radius = the parent's --concentric-radius − --concentric-padding. |
<!-- Thanh tự làm: nền glass, nút bo đồng tâm -->
<nav class="glass sticky top-3 rounded-circular p-1 [--concentric-radius:var(--borderRadiusCircular)] [--concentric-padding:--spacing(1)]">
<a class="rounded-concentric px-3 py-1.5" href="/">Home</a>
</nav>
<!-- Trên ảnh: làm tối ảnh rồi đặt glass-clear -->
<div class="relative">
<img src="…" alt="" />
<div class="glass-scrim absolute inset-0"></div>
<div class="glass-clear absolute bottom-3 inset-s-3 rounded-circular px-3 py-1.5">…</div>
</div>Accessibility
Glass opacity is chosen so primary and secondary text reach at least 4.5:1 even over pure white or pure black: 80% white in light themes, 82% grey in dark themes. Inside glass, faint text (foreground-3, -4) uses the foreground-2 color and brand text uses a deeper shade. glass-clear needs glass-scrim: white text reaches 5.9:1 even over a white image.
| Situation | Glass becomes |
|---|---|
prefers-reduced-transparency: reduce | Opaque, no blur. |
prefers-contrast: more | Opaque, with a visible border. |
| High-contrast theme | Black background, white border. |
forced-colors: active | System colors, no blur. |
| No backdrop-filter support | Opaque. |
| Glass inside glass | Tint only, no second blur. |
Test with the operating system's Reduce transparency, Increase contrast, Reduce motion and forced-colors modes, over the brightest and darkest images your app can show.
Performance
- One backdrop-filter per bar: the buttons inside share it instead of having their own.
- Avoid glass over large areas that change every frame (video, animated canvas): the blur is recomputed each frame.
- Nested glass turns off the inner blur on its own.
- Refraction: surfaces of the same size share a filter, and the map is only rebuilt once a size change settles. Like blur, it is recomputed when the content behind changes.
Do and don't
- Use glass for bars, tab bars, sidebars and menus that float above content.
- Put glass-scrim on photos under glass-clear.
- Keep one primary glass-prominent action per view.
- Put glass on content: cards, list rows, table cells.
- Stack glass on glass.
- Use glass-clear without a scrim over bright images.