Skip to content
Material 3 Expressivev1.0.1
Guide

Theming

Create themes, override tokens, and nest theme scopes.

Import the complete stylesheet once and render a provider around the scope that should receive Material 3 tokens:

import { Material3Provider } from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'

export function App() {
  return <Material3Provider colorMode="system">...</Material3Provider>
}

The provider defaults to the complete immutable theme and system color mode. It renders a .m3e-theme div; className, style, and other div attributes are forwarded to that element.

Create and extend themes

Theme utilities accept partial overrides, validate the resulting complete theme, and return a new deeply frozen value:

import { Material3Provider } from '@language-lit/material3-expressive'
import { createTheme } from '@language-lit/material3-expressive/theme'

const compactTheme = createTheme({
  density: { scale: -1 },
  reference: {
    typeface: {
      brand: ['Roboto Flex', 'sans-serif'],
    },
  },
})

export function App() {
  return <Material3Provider theme={compactTheme}>...</Material3Provider>
}

The ./theme and ./tokens subpaths contain no React runtime and are safe for server data modules. The root entry is the convenient React client surface because it also exports the provider and hooks.

Color schemes reference the theme's reference.palette paths. Override a palette value when a role keeps the same tonal mapping, or override a role's $ref when it should use a different palette tone. Invalid references and contrast-breaking schemes are rejected.

Nested scopes

Every provider starts from a complete default scope, then applies its theme's differences. A nested provider is isolated from its parent:

<Material3Provider theme={brandTheme}>
  <MainContent />
  <Material3Provider theme={editorTheme} colorMode="dark">
    <Editor />
  </Material3Provider>
</Material3Provider>

SSR and resolved mode

systemModeFallback controls the deterministic server and hydration snapshot for useResolvedColorMode. Static CSS still selects the browser's visual system scheme before hydration.

const mode = useResolvedColorMode() // "light" or "dark"
const theme = useMaterial3Theme()

These hooks use separate contexts, so a component that only reads resolved mode does not subscribe to the complete theme object.

For applications that inspect data-m3e-resolved-color-mode before hydration, preventColorSchemeFlash emits an optional static initialization script. Pass the request's CSP nonce through nonce. The script is absent by default and never changes the document root.