Skip to content
Docs

Theming

Fuji UI appearance is shaped by four global settings on FujiProvider: a theme, a material, a radius, and an elevation. Set them once and every component follows, because each reads the same design tokens rather than branching on the appearance in JavaScript. This page covers all four settings plus the colors, tokens, and shadows they drive.

Theme

There are two themes: light (default) and dark. The theme sets the page background, foreground, border, and tone colors. Set it once, globally; there is no per-component theme prop.

tsx
import { FujiProvider } from "@fujiui/react";

<FujiProvider
  persist
  defaultTheme="dark"
  defaultMaterial="glass"
  defaultRadius="soft"
  defaultElevation="floating"
>
  <App />
</FujiProvider>

Each of the four settings can be controlled (theme, material, radius, elevation) or uncontrolled (defaultTheme, defaultMaterial, and so on), with a matching onThemeChange, onMaterialChange, onRadiusChange, or onElevationChange callback, and all four are exposed through useFujiConfig(). With persist, the provider saves them together as one preference under the fuji-appearance key, so a choice survives navigation, a new tab, and refresh. A preference saved by an older version as {"theme":"glass"} is read as dark + glass.

These swatches read the active appearance live

Every swatch reads its color directly from the active theme and material. Use the theme and glass controls in the header to watch them change without a page reload.

Background

--fuji-background

Background (subtle)

--fuji-background-subtle

Surface (subtle)

--fuji-surface-subtle

Surface

--fuji-surface

Surface (strong)

--fuji-surface-strong

Surface (overlay)

--fuji-surface-overlay

Foreground

--fuji-foreground

Foreground (muted)

--fuji-foreground-muted

Foreground (subtle)

--fuji-foreground-subtle

Default

--fuji-default

Border

--fuji-border

How appearance switching works

FujiProvider stamps data-fuji-theme, data-fuji-material, data-fuji-radius, and data-fuji-elevation on its root element, and the root provider (the one with persist) mirrors them onto <html> for native form control and color-scheme support. Every token is scoped under those attributes, so flipping one re-resolves every affected value in a single paint, with no remount and no React state threaded through the tree. Overlays such as dialogs, menus, popovers, and tooltips render in a portal outside the provider subtree; each Fuji UI overlay re-stamps all four attributes on its portalled root, so overlay content always matches the active appearance.

Material

Material is the second global axis: solid (default) or glass. It is independent of the theme, so dark mode and glass combine freely. Glass layers translucency and backdrop blur over whichever theme is active instead of bringing a palette of its own: the background, foreground, and tone colors still come from the theme.

tsx
import { FujiProvider } from "@fujiui/react";

// Light theme + glass: frosted light glass with dark text
<FujiProvider theme="light" material="glass">
  <App />
</FujiProvider>

// Dark theme + glass: dark glass with light text
<FujiProvider theme="dark" material="glass">
  <App />
</FujiProvider>

The theme is the glass tint

Glass has no tint setting of its own: a light theme gives light glass and a dark theme gives dark glass. To tint one region differently from the rest of the page, such as a bright photo section in a dark app, nest a provider around just that region with the theme the glass should read as. Nested providers do not inherit material, so declare it again alongside theme.

tsx
import { FujiProvider } from "@fujiui/react";

<FujiProvider theme="dark" material="glass">
  <App>
    <FujiProvider theme="light" material="glass">
      <BrightPhotoSection />
    </FujiProvider>
  </App>
</FujiProvider>

What gets the material

Glass is for surfaces that sit over other content. Cards and panels, chrome such as Sidebar and Pagination, and overlays such as dialogs, drawers, menus, popovers, selects, and toasts take the translucent tint and backdrop blur. Buttons, fields, switches, sliders, and the rest of the control layer stay solid, so they read against the glass surface instead of competing with it, and a form never pays for one blur layer per input.

Backdrops

Glass composites over whatever is behind it. By default that is the active theme's own flat background. Add the shipped fuji-glass-atmosphereclass to an app root or panel for Fuji UI's gradient backdrop; it applies only under glass, so it is safe to render unconditionally. Your own photo, brand gradient, or color works too. Behind arbitrary or user-supplied imagery, put text on --fuji-surface or --fuji-surface-raised rather than --fuji-surface-subtle.

Fallbacks and accessibility

When backdrop-filter is unsupported, or the user has prefers-reduced-transparency: reduce set, glass surfaces fall back to opaque panels instead of a blurred effect; readability always wins over the visual effect. Print styles similarly flatten every theme to plain black on white.

Radius

Radius is the third global axis: cornered (default) or soft. It controls three scoped tokens (control, panel, and overlay radius), so every button, card, and dialog stays visually consistent with each other.

tsx
import { FujiProvider } from "@fujiui/react";

<FujiProvider radius="soft">
  <App />
</FujiProvider>

Cornered

control 6px · panel 10px · overlay 14px

Soft

control 14px · panel 20px · overlay 26px

Circular elements stay circular. Avatars, radio dots, switches, and status dots use rounded-full directly; radius tokens only apply to rectangular controls, panels, and overlays, never to semantically circular shapes.

Elevation

Elevation is the fourth global axis: regular (default) or floating. regular keeps the restrained shadows shown below. floating swaps in deeper, softly layered shadows plus a small inset highlight at meaningful boundaries such as cards, panels, menus, and overlays. It changes shadow depth only; it never adds scale or hover motion to static surfaces. In dark mode, floating uses deeper, darker layered shadows with a subtle inset highlight so raised surfaces still read against the near black canvas. Both modes drive the same shadow tokens, so every elevated component follows the mode automatically.

control floating

card floating

panel floating

overlay floating

Colors and tokens

Every color is a CSS custom property prefixed --fuji-, scoped per theme and material in the package stylesheet. Components reference these tokens, never raw hex values, through matching Tailwind utilities such as bg-fuji-surface-strong.

Surface hierarchy

Four surface levels express depth without relying on shadow alone: subtle for hover areas and inactive tabs, the base surface for cards and panels, strong for inputs and selected states, and overlay for dialogs, drawers, menus, popovers, and tooltips. Shadow depth is the separate elevation setting described above.

ComponentTone vs. StatusTone

Fuji UI has two separate tone vocabularies, and they are not interchangeable. ComponentTone (default | fire | water | forest | sun) is purely decorative - it powers Button, IconButton, ButtonGroup, Badge, Icon, Slider, Rating, Avatar, and similar visual-only tone props. Choosing tone="fire" on a Badge says nothing about status - it is a look, the same as picking a color swatch.

StatusTone (default | success | warning | danger | info) is semantic - the value itself carries meaning for accessibility, and it never changes regardless of the decorative palette in use. It is used by Alert, Toast, Result, StatusIndicator, Progress, CircularProgress, and Timeline, always through a variant prop rather than tone. Each status name maps internally onto one decorative token purely for styling: success → forest, warning → sun, danger → fire, info → water.

Decorative - ComponentTone

defaultfirewaterforestsun
<Badge tone="fire">fire</Badge>

Semantic - StatusTone

defaultsuccesswarningdangerinfo
<StatusIndicator variant="danger" label="danger" />

Semantic colors

Success, warning, danger, and info are deliberately muted, never neon, and keep the same hue in every theme and material, so meaning never depends on the active appearance.

Success

--fuji-forest

Warning

--fuji-sun

Danger

--fuji-fire

Info

--fuji-water

A note on the old ‘primary’ tone

Fuji UI's native visual language treats default as the main action color. Earlier versions also shipped a primary tone that was a pure alias of default - both resolved to the same token (--fuji-primary aliased --fuji-default) in every theme. primary has since been removed in favor of a single canonical default tone. The default and contained combination still adapts automatically per theme: black on light and warm off-white on dark, in either material. If a distinct accent hue is introduced later, only the token aliases change.

Shadows

Shadows are layered (two stacked box-shadows per level) and soft, never a single flat drop shadow, and grow stronger from control to overlay. Not every element gets a visible shadow; subtle surfaces and ghost buttons intentionally have none.

control

Buttons, inputs, small controls

card

Cards, standard panels

panel

Elevated panels, dropdowns

overlay

Dialogs, drawers, popovers, menus