Yugen UI

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.

Labs: prop and token names may change before 1.0

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.

Day 1
Day 2
Day 3
Day 4
Day 5
Day 6
Day 7
Day 8
Day 9
Day 10
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

ComponentPropNotes
ToolbarmaterialFloating capsule bar; buttons inside get concentric corners.
TabsListmaterial · minimizedThe selected tab is a rounded fill. minimized keeps only the icons (use with useScrollMinimize).
NavmaterialFloating sidebar with padding and rounded corners.
SearchBox / Inputappearance="glass"A field inside a glass bar.
Buttonappearance="glass" · "glass-prominent"A button over content; prominent uses the brand color.
MenuPopover · PopoverSurface · TooltipContentmaterialTransient 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 · Switchmaterial="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 · SheetContentmaterialA 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

UtilityUse
glassTheme-aware glass background with an edge highlight, plus text / hover tokens that keep contrast.
glass-clearGlass over photos / media, with white text.
glass-tintedBrand-colored glass for one primary action.
glass-scrimA dimming layer on the image under glass-clear.
scroll-edge · scroll-edge-aboveContent scrolling under a sticky bar area blurs and fades out, instead of a divider. Put it on the full-width bar area.
rounded-concentricInner 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.

SituationGlass becomes
prefers-reduced-transparency: reduceOpaque, no blur.
prefers-contrast: moreOpaque, with a visible border.
High-contrast themeBlack background, white border.
forced-colors: activeSystem colors, no blur.
No backdrop-filter supportOpaque.
Glass inside glassTint 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

Do
  • 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.
Don't
  • Put glass on content: cards, list rows, table cells.
  • Stack glass on glass.
  • Use glass-clear without a scrim over bright images.