Customize the Liquid Glass material, light and dark tokens, and Tailwind aliases.
The core package ships one stylesheet that contains the design tokens, the shared glass material, and the Tailwind source scan. Import it once in your global CSS and style the rest of the application around it.
@import "tailwindcss";
@import "@xingxingmofashu/ui";Tokens switch through .light / .dark classes or
data-theme="light" / data-theme="dark" attributes. Light is the default even
when no selector is present.
<html class="dark"><html data-theme="dark">The two mechanisms are interchangeable. A nested .light or
[data-theme="light"] subtree can switch back to light tokens inside a dark
document, which is useful for previews and isolated panels.
<html class="dark">
<body>
<main class="dark">...</main>
<aside data-theme="light">...</aside>
</body>
</html>Set the theme before the first paint when possible. Reading localStorage
during render causes a hydration mismatch; set the class in an inline script or
use a theme library such as next-themes with suppressHydrationWarning on the
<html> element.
Glass presets are an optional plugin, not part of the core stylesheet. The
plugin's stylesheet ships the thin, soft, regular, and thick materials,
and its React entry manages data-glass state and persistence.
See Glass presets for setup, preset values, controlled state, scoped overrides, and SSR behavior.
The material is split into two layers:
--ui-ink, --ui-ink-active,
--ui-surface, --ui-glass-surface, --ui-glass-overlay-surface,
--ui-glass-border, --ui-glass-highlight, --ui-glass-rim,
--ui-glass-focus, --ui-glass-inner-border, --ui-glass-tone,
--ui-glass-contrast, --ui-shadow-glass, and --ui-drop-shadow-label.--ui-glass-blur, --ui-glass-saturate, --ui-glass-inner-inset,
--ui-glass-rim-inset, and the other shared --ui-glass-* parameters.
Presets may change blur and saturation. Component-specific values such as the
checkbox rim or slider lens stay in that component's theme recipe.The package defines them inside @layer theme. An unlayered :root block in
your application therefore wins without an !important:
@import "tailwindcss";
@import "@xingxingmofashu/ui";
:root {
--ui-glass-surface: rgb(255 255 255 / 0.5);
--ui-glass-overlay-surface: rgb(255 255 255 / 0.8);
--ui-glass-border: rgb(15 23 42 / 0.16);
--ui-glass-blur: 20px;
--ui-glass-saturate: 160%;
}
.dark,
[data-theme="dark"] {
--ui-glass-surface: rgb(20 20 20 / 0.75);
--ui-glass-overlay-surface: rgb(20 20 20 / 0.86);
--ui-glass-border: rgb(255 255 255 / 0.24);
}The default thin material pairs a 20% light veil or 62% dark veil with a
4px backdrop blur at 150% saturation. The veil strength balances transparency
against text contrast, so tune it for the imagery in your application: test over
the busiest and darkest background, not only over a plain page.
Text-bearing overlays—Dialog, Alert Dialog, Drawer, Popover, Menu, Select,
Toast, and Tooltip—use .ui-glass-overlay and the stronger
--ui-glass-overlay-surface veil (64% light / 76% dark by default). The page
stays visible through the panel while body copy gets a stronger base. The fill
still varies with the page behind it, so verify secondary text contrast over
your busiest imagery. The glass preset plugin adjusts both surface tokens.
Overlays no longer use --ui-surface: if you relied on that override for
overlay backgrounds, set --ui-glass-overlay-surface instead. Set both tokens
when opaque surfaces and overlays should move together.
The stylesheet exposes Tailwind color aliases for the most common tokens:
| Utility family | Token |
|---|---|
text-ui-ink, bg-ui-ink | --ui-ink |
bg-ui-glass-surface | --ui-glass-surface |
bg-ui-glass-overlay-surface | --ui-glass-overlay-surface |
border-ui-glass-border | --ui-glass-border |
outline-ui-glass-focus | --ui-glass-focus |
bg-ui-surface | --ui-surface |
Use these aliases for your own layout so custom chrome follows the same theme as the components. Arbitrary values still work when you need a one-off:
<div className="rounded-3xl border border-ui-glass-border bg-ui-glass-surface p-6 text-ui-ink">
Matches the component material.
</div>The shared material is also exported as CSS classes. Combine them when you need a glass panel that is not one of the shipped components:
<div className="ui-glass ui-glass-inner ui-glass-highlight relative overflow-hidden rounded-3xl p-6">
<span aria-hidden="true" className="ui-glass-rim" />
<p className="relative z-10 text-sm text-ui-ink">Custom glass panel</p>
</div>.ui-glass draws the translucent fill, double border, specular shadow, and
backdrop blur..ui-glass-overlay swaps the fill for the stronger overlay veil; use it for
text-bearing panels that float above page content..ui-glass-inner adds the soft inner outline through ::before..ui-glass-highlight adds the diagonal glare through ::after..ui-glass-rim is a decorative child element; render it with
aria-hidden="true"..ui-glass-chip is a compact surface for small readouts.Most components already compose these classes. Use them directly only for surfaces the package does not provide.
Every component accepts className and style. They merge into the
component's primary element: the root for most components, the popup for
Dialog, Alert Dialog, Drawer, Popover, Menu, and Tooltip, the outer root for
Input, the field root for Select, and each toast for Toast. Use them for
layout, not for replacing the material:
<Button className="w-full" style={{ maxWidth: 320 }} label="Continue" />Every component also accepts ui, an object keyed by the component's
data-slot names. Use it to style internal slots while keeping the
single-structure API:
<Card
title="Deployment"
description="Production"
ui={{
glass: "bg-white/40",
header: "pb-0",
title: "tracking-tight",
}}
>
<p className="text-sm text-ui-ink/70">Build 128 is live.</p>
</Card>Values are ClassValue, so strings, arrays, and class maps work. The merge
order is theme recipe, ui, then className; for collection components such as
Accordion and Toggle Group, root ui is applied first, then each item's ui,
then the item's className. Inspect the DOM data-slot attribute or the
component API table for the available keys.
Use style for values that must be computed at runtime, including per-instance
CSS variables. Inline styles override matching properties from the theme and
className; keep color, blur, and shadow changes on the shared tokens.
For full control, import a compiled theme recipe:
import buttonTheme from "@xingxingmofashu/ui/theme/button";
const styles = buttonTheme({ size: "lg" });
export function CustomButton() {
return <button className={styles.root()}>Custom action</button>;
}The recipe returns slot classes, so a custom component can reuse the same geometry and material without copying Tailwind classes.
Transitions and pressed effects include motion-reduce variants. Users who set
prefers-reduced-motion: reduce keep the layout and state changes but lose the
decorative motion.
The visual material relies on modern CSS: backdrop-filter, color-mix,
container units, individual translate / scale properties, and :has.
Provide a sensible fallback background for browsers that do not support
backdrop-filter, or limit support to current evergreen browsers.
.dark or data-theme="dark" before first paint.data-glass materials.:root and .dark block for custom material.ui for internal slots, className and style for layout, and tokens for material changes.