# Applying a theme (/docs/applying-a-theme)



A themed subtree needs the shared Luke UI stylesheet and one theme stylesheet. Both are static CSS.
Luke UI does not inject styles at runtime.

## Import the stylesheets [#import-the-stylesheets]

Import `@luke-ui/react/stylesheet.css` and one theme's stylesheet, for example
`@luke-ui/react/themes/tactile/stylesheet.css`. A theme stylesheet themes the whole document from
`:root`, with no class and no JavaScript. Import one bundled theme, and Luke UI does not load the
other.

## Apply the root class [#apply-the-root-class]

Apply `rootClassName` to an element you own, such as the application shell. It supplies the reset
and base typography. It carries no theme identity, so it works the same way for a bundled theme and
a custom theme.

apps/docs/src/samples/theming/apply-theme.tsx

```tsx
import '@luke-ui/react/stylesheet.css';
import '@luke-ui/react/themes/tactile/stylesheet.css';
import { rootClassName } from '@luke-ui/react/theme';
import type { PropsWithChildren } from 'react';

export function App({ children }: PropsWithChildren) {
	return (
		<div className={rootClassName} data-color-mode="dark">
			{children}
		</div>
	);
}
```

## Choose a colour mode [#choose-a-colour-mode]

Leave `data-color-mode` unset to follow `prefers-color-scheme`. Set `data-color-mode="light"` or
`data-color-mode="dark"` when a root or nested scope needs a fixed mode.

An explicit mode also sets the native `color-scheme` property. Browser controls and scrollbars then
match the Luke UI content.

## Nest colour-mode scopes [#nest-colour-mode-scopes]

Colour modes nest freely. Use a nested mode when one part of the interface must stay light or dark
independently of its parent.

apps/docs/src/examples/theming/color-mode-override.tsx

```tsx
import { Box } from '@luke-ui/react/box';
import { Button } from '@luke-ui/react/button';
import { Cluster } from '@luke-ui/react/cluster';
import { Stack } from '@luke-ui/react/stack';
import { Text } from '@luke-ui/react/text';
import { vars } from '@luke-ui/react/theme';
import { useId, useState } from 'react';

export default () => {
	const [parentMode, setParentMode] = useState<'light' | 'dark'>('light');
	const labelId = useId();

	return (
		<Box
			backgroundColor="surface.canvas"
			color={vars.color.text.primary}
			data-color-mode={parentMode}
			padding="sp24"
		>
			<Stack gap="sp16">
				<Stack gap="sp8">
					<Text elementType="strong" fontWeight="emphasis" id={labelId}>
						Parent colour mode
					</Text>
					<Cluster aria-labelledby={labelId} gap="sp8" role="group">
						{(['light', 'dark'] as const).map((option) => (
							<Button
								aria-pressed={parentMode === option}
								key={option}
								onPress={() => setParentMode(option)}
								prominence={parentMode === option ? 'high' : 'standard'}
							>
								{option === 'light' ? 'Light' : 'Dark'}
							</Button>
						))}
					</Cluster>
				</Stack>
				<Box
					backgroundColor="surface.floating"
					borderColor="decorative"
					borderRadius="surface"
					borderStyle="solid"
					borderWidth="thin"
					color={vars.color.text.primary}
					padding="sp16"
				>
					<Text>This panel follows the parent mode.</Text>
				</Box>
				<Box
					backgroundColor="surface.floating"
					borderColor="decorative"
					borderRadius="surface"
					borderStyle="solid"
					borderWidth="thin"
					color={vars.color.text.primary}
					data-color-mode="dark"
					padding="sp16"
				>
					<Text>This panel is fixed to dark mode.</Text>
				</Box>
			</Stack>
		</Box>
	);
};
```

apps/docs/src/samples/theming/nested-color-mode.tsx

```tsx
import type { PropsWithChildren } from 'react';

export function DarkPageWithLightPreview({ children }: PropsWithChildren) {
	return (
		<div data-color-mode="dark">
			Dark application
			<section data-color-mode="light">{children}</section>
		</div>
	);
}
```

## Avoid a colour-mode flash [#avoid-a-colour-mode-flash]

Render `rootClassName` and any known explicit mode in the initial HTML. When the mode comes from a
client-side preference, set `data-color-mode` before the themed UI paints.

Use the same value for the server HTML and the first client render. This avoids a hydration warning.

## Portals need nothing extra [#portals-need-nothing-extra]

A theme stylesheet themes the whole document from `:root`, so a body-level portal inherits it with
no class applied.

A colour mode does not follow a portal out of its branch. A portal rendered into `document.body`
takes the document's mode, not a mode set on a nested scope near its trigger. Set `data-color-mode`
on the portal root yourself when it must match a nested scope.

## Add an identity class for more than one theme [#add-an-identity-class-for-more-than-one-theme]

Loading one theme needs no identity class. Loading more than one theme in the same document does, so
an explicit class can win over another theme's `:root` fallback.

Each bundled theme exports its identity class as `themeClassName` from its own entrypoint, for
example `@luke-ui/react/themes/tactile`. Apply it to `<html>`, or to the root of the subtree that
needs it.

An authored theme gets its class the same way, from `getThemeClassName` in `@luke-ui/react/theme`.
Pass the `name` your `ThemeInput` declares. It returns the same class the generated stylesheet
selects on, and throws when the name is not kebab-case.

apps/docs/src/samples/theming/multi-theme-app.tsx

```tsx
import '@luke-ui/react/stylesheet.css';
import '@luke-ui/react/themes/tactile/stylesheet.css';
import { getThemeClassName, rootClassName } from '@luke-ui/react/theme';
import { cx } from '@luke-ui/react/utils';
import type { PropsWithChildren } from 'react';

// The same kebab-case `name` the product theme's `ThemeInput` declares.
const productThemeClassName = getThemeClassName('product');

type AppProps = PropsWithChildren<{ productStylesheetHref: string }>;

export function App({ children, productStylesheetHref }: AppProps) {
	return (
		<>
			<link href={productStylesheetHref} rel="stylesheet" />
			<div className={cx(rootClassName, productThemeClassName)}>{children}</div>
		</>
	);
}
```

Theme identities do not nest. Never place one identity class inside another identity's subtree. A
nested identity resolves its own identity-owned values correctly, and its colour, depth, and
action-control-finish values also resolve correctly under the system-controlled mode. But an
explicit `data-color-mode` scope on or inside the nested identity makes those mode-dependent values
compete with the ancestor identity at equal precedence, so stylesheet order decides the winner.

## Continue learning [#continue-learning]

<Cards>
  <Card href="/docs/authoring-a-theme" title="Authoring a theme">
    Create a product-owned stylesheet.
  </Card>

  <Card href="/docs/token-reference" title="Token reference">
    Browse public semantic values for custom elements.
  </Card>

  <Card href="/docs/theming" title="Theming">
    See how identity, colour mode, and tokens fit together.
  </Card>
</Cards>
