// preview in
saved for every page

StyleProvider

Sets the skin, the color mode and the brand color for everything inside it. Put one at the root of your app, and nest more for islands that look different, like a dark card or a tenant's brand.

Client componentSource ↗

Installation

Set up Camaleo once (Installation) and import the stylesheet of every skin you render, islands included. A skin without its CSS falls back to the defaults.

import { StyleProvider, useStyleScope, PortalScope, Portal, STYLE_NAMES } from "@camaleo/react";

For the whole page, a Server Component can spread styleScopeProps({ styleName, mode, brand }) on <html> instead: same attributes, no client code. See Installation for the CSS and the fonts.

Anatomy

StyleProvider renders a <div> with data-style, data-mode and the brand tokens as inline variables. Every [data-style] element redeclares all the tokens and paints its own background, text color and font, so a nested island starts clean instead of inheriting its parent's skin. The mode and the brand, on the other hand, flow down until an island sets its own.

Popups (Menu, Select, Popover, Dialog, Drawer…) render in <body>, outside every provider. They wrap their content in a portal-scope element that copies the scope of the provider they were opened from.

Examples

Every preview already sits inside a StyleProvider: the one the bar above controls. These examples nest a second one, so you can see an island next to the page around it.

minimal · light · #3fa34d
Every Sunday

The Sunday Letter

Five stories about the city, picked by our editors and delivered at 7 a.m.

Skin for your workspace
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft
NewDraft

Workspace skin: minimal.

All skins side by side

Click a tile. A theme picker shows each choice in its own island, so people see the skin before they commit to it. Mode and brand still come from the page.

Invoice #1042

Due on November 15, 2026

$1,280.00

Switch skin at runtime

Pick a skin and flip the switch. New props only swap attributes, so nothing remounts and the state inside survives.

Open now

Sol Bakery

Sourdough and pastries, 12 Oak Street

Open now

Vida Clinic

Family doctors, 480 Park Avenue

A brand per tenant

Set only brand to give each customer their color. Pass the page's skin from useStyleScope(), because styleName doesn't inherit.

Q3-report.pdf2.4 MB · edited by Bruno Costa

Popups keep the island's style

Open the menu. It renders in <body>, yet it keeps the island's skin, mode and brand, because every popup re-applies the scope it was opened from.

Total: $86.002 items · free shipping

Your own portal

Click Pay. Portal moves content to another node and keeps the island's style; use it, or wrap your own createPortal in PortalScope.

Skin
minimal
Mode
light
Brand
#3fa34d

Read the current scope

useStyleScope() returns the nearest provider's props. Use it to pass the skin on, or to color a chart or canvas that can't read CSS.

Yellow
Lime
Ink

Hard brand colors stay readable

Yellow, pale lime, near black: the brand scale picks black or white text for every step, so labels keep at least 4.5:1 in any skin and mode.

In context

These are whole pieces of UI you can copy, with an island inside the skin you picked.

Appearance

Applies to your account on every device.

Mode
Accent color
Preview
Appearance settingsStyleProvider · Select · RadioGroup · Card · Switch

Your 2026 in numbers

Thank you for a great year. Here is what your store did since January.

Orders12,480
New customers3,215
Average rating4.8
Campaign sectionStyleProvider · Heading · Stat · Button

Vida Clinic

Pick a time for Thursday, November 12.

Embedded booking widgetStyleProvider · useStyleScope · Card · Select · Button

Guidelines

Good morning, Ana. You have 3 orders to ship today.

What's new in October

Bulk refunds, a faster search and dark mode for invoices.

DoAn island for a whole section.

A news panel, a campaign or an embedded widget can have its own identity. The rest of the page keeps one skin.

Don’tDifferent skins inside one form.

Three fields in three skins look broken, and people stop trusting the form.

Upgrade to Team and invite up to 20 people.

DoPass the skin on when you change the mode.

styleName={useStyleScope()?.["data-style"]} with mode="dark" gives a dark card in the page's own skin.

Upgrade to Team and invite up to 20 people.

Don’tOnly mode on a nested provider.

Without styleName the island falls back to minimal. Switch the skin in the bar above: this card never changes.

Props

PropTypeDefaultDescription
styleNameStyleName (13 skins)"minimal"The skin. It does not inherit: a nested provider without it falls back to minimal. Load the skin's stylesheet too (@camaleo/styles/<name>.css). A skin of your own works too: any name your CSS targets with [data-style="<name>"].
modeColorMode—light, dark or system. Omit to inherit the mode from the parent scope (the root follows the system).
brandstring—Any CSS color. It becomes an 11-step scale with readable text on every step; the skin decides where the brand shows up. Omit to inherit the parent's brand.

All <div> props pass through; style merges with the brand variables. useStyleScope() returns { "data-style", "data-mode"?, style? } or null outside a provider. PortalScope takes only children. Portal takes children and container (default document.body). styleScopeProps(options) returns the same attributes for a Server Component.

Styling hooks

The provider is the source of the tokens every component reads. Target its attributes to change a skin, or set a token on an island to change only that section.

Attributes

data-styleminimal · neumorphism · retro-pixel · editorial · neubrutalism · retro · glass · swiss · progressive · clay · material · cupertino · y2k
data-modelight · dark · system (absent: inherited)
style--cml-brand and --cml-brand-50 … -950, each with -fg and -hover
data-slot"portal-scope" on the popup wrapper in <body>

Tokens it reads

/* rounder controls, only in the glass skin */
[data-style="glass"] {
  --cml-radius-control: 14px;
}

/* a quieter surface for one island */
.help-center[data-style] {
  --cml-surface: light-dark(var(--cml-brand-50), var(--cml-brand-950));
}

Accessibility

—The provider isn't interactive and takes no focus. Keyboard behavior comes from the components inside it.
Was this page helpful? · Edit this page on GitHub