Theming
Light and dark, compact density, and your own accent, grays and corners with createTheme.
A theme is what ButteryProvider applies to everything inside it: a color scheme, a density, a motion preference and, optionally, your own accent, grays and corners from createTheme. Use these settings, rather than overriding tokens one by one, so every component changes together.
Light and dark
colorScheme is "system" by default, which follows the operating system, or "light" or "dark" to fix it. Every color token has a value for each, so components need nothing extra.
import type { ReactElement } from "react";
import { ButteryProvider } from "@butteryui/react";
import { App } from "./App.tsx";
export function Root(): ReactElement {
return (
<ButteryProvider colorScheme="system">
<App />
</ButteryProvider>
);
}Part of a page. A ButteryProvider inside another one sets the scheme for its part of the page only, which is how Elevation shows light and dark surfaces side by side.
Density
density="compact" shortens every control, from 34px fields to 30px and 48px calls to action to 40px, for screens that show a lot at once. The spacing scale stays the same.
Motion
reducedMotion is "user" by default, which follows the operating system's setting. "always" turns motion down for everyone, and "never" keeps it on. Every motion preset reads this setting, as the Motion pages explain.
Reading the settings in code
useButtery() returns what the nearest ButteryProvider resolved:
| Field | Value |
|---|---|
colorScheme | "light" or "dark", with "system" already settled |
density | "comfortable" or "compact" |
platform | "web", or "desktop" in a Tauri window |
reducedMotion | A boolean, true when motion is reduced |
import type { ReactElement } from "react";
import { Text } from "@butteryui/core";
import { useButtery } from "@butteryui/react";
export function SchemeNote(): ReactElement {
const { colorScheme } = useButtery();
return <Text variant="caption">Showing the {colorScheme} theme</Text>;
}Without a provider. useButtery() throws when there's no ButteryProvider above it. A component that has to work with or without one reads ButteryContext with use(ButteryContext) instead, which gives null outside a provider.
Your own theme
createTheme makes a theme from a few choices and returns only the variables that differ from buttery's defaults. Pass it to ButteryProvider's theme:
import type { ReactElement } from "react";
import { createTheme } from "@butteryui/core";
import { ButteryProvider } from "@butteryui/react";
import { App } from "./App.tsx";
const theme = createTheme({
accent: "#0f8f7a",
neutral: "warm",
radius: "soft",
});
export function Root(): ReactElement {
return (
<ButteryProvider theme={theme}>
<App />
</ButteryProvider>
);
}Each choice is optional:
| Option | What it sets | Default |
|---|---|---|
accent | The focus ring, selection and links, as hex | buttery's blue |
primary | The main button's color, as hex | Near-black, inverted in dark |
neutral | The tint of every gray: "gray", "warm" or "cool" | "gray" |
radius | Rounder or tighter corners: "soft", "default" or "crisp" | "default" |
density | The density the provider starts in | "comfortable" |
overrides | The final word on any color token, per scheme, under light and dark | None |
From an accent, buttery works out the shades for light and dark, and an accent-text that meets contrast. At every radius, nested surfaces stay concentric.