90 lines
2.7 KiB
Plaintext
90 lines
2.7 KiB
Plaintext
|
|
---
|
||
|
|
title: Set up appearance preferences
|
||
|
|
description: Aggregate appearance definitions and effects with the application's shared preferences.
|
||
|
|
order: 1
|
||
|
|
toc:
|
||
|
|
- id: provider
|
||
|
|
title: Aggregate preferences
|
||
|
|
- id: effects
|
||
|
|
title: Register effects
|
||
|
|
- id: theme-style
|
||
|
|
title: Inject theme variables
|
||
|
|
- id: persistence
|
||
|
|
title: Persist updates
|
||
|
|
---
|
||
|
|
|
||
|
|
## Aggregate preferences {#provider}
|
||
|
|
|
||
|
|
Build one complete preference definition map at the application boundary. Spread definitions from appearance, locale, keyboard, and other feature packages into this map, then create or load the initial values.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import {
|
||
|
|
createDefaultPreferences,
|
||
|
|
PreferencesProvider,
|
||
|
|
} from "@workspace/preferences"
|
||
|
|
import {
|
||
|
|
appearancePreferenceDefinitions,
|
||
|
|
appearancePreferenceEffect,
|
||
|
|
} from "@workspace/appearance"
|
||
|
|
|
||
|
|
const preferenceDefinitions = {
|
||
|
|
...appearancePreferenceDefinitions,
|
||
|
|
...localePreferenceDefinitions,
|
||
|
|
}
|
||
|
|
|
||
|
|
const initialPreferences = createDefaultPreferences(preferenceDefinitions)
|
||
|
|
const preferenceEffects = [
|
||
|
|
appearancePreferenceEffect,
|
||
|
|
localePreferenceEffect,
|
||
|
|
] as const
|
||
|
|
|
||
|
|
export function Root({ children }: { children: React.ReactNode }) {
|
||
|
|
return (
|
||
|
|
<PreferencesProvider
|
||
|
|
effects={preferenceEffects}
|
||
|
|
initialPreferences={initialPreferences}
|
||
|
|
>
|
||
|
|
{children}
|
||
|
|
</PreferencesProvider>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
`defaultAppearancePreferences` remains available when only the appearance slice is needed, but the definition map is the preferred source when several packages are combined.
|
||
|
|
|
||
|
|
## Register effects {#effects}
|
||
|
|
|
||
|
|
`appearancePreferenceEffect` combines two exported callbacks: `applyAppearancePreferenceEffect` updates document classes and theme variables before paint, while `subscribeSystemThemePreferenceEffect` watches the operating-system theme after paint. Register the composed effect in the stable `effects` array passed to `PreferencesProvider`.
|
||
|
|
|
||
|
|
## Inject theme variables {#theme-style}
|
||
|
|
|
||
|
|
Use `buildThemeStyleSheet` to generate the CSS variables for light, dark, and system modes. Keep the stable element ID so the appearance effect can update the same node when color preferences change.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import {
|
||
|
|
buildThemeStyleSheet,
|
||
|
|
THEME_STYLE_ELEMENT_ID,
|
||
|
|
} from "@workspace/appearance"
|
||
|
|
|
||
|
|
;<style
|
||
|
|
id={THEME_STYLE_ELEMENT_ID}
|
||
|
|
dangerouslySetInnerHTML={{
|
||
|
|
__html: buildThemeStyleSheet(themeLight, themeDark),
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Persist updates {#persistence}
|
||
|
|
|
||
|
|
The shared `onPreferenceChange` receives a typed `{ key, value }` only when a value actually changes. Forward it to a server function or cookie writer owned by the application.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
<PreferencesProvider
|
||
|
|
effects={preferenceEffects}
|
||
|
|
initialPreferences={preferences}
|
||
|
|
onPreferenceChange={(update) => savePreference(update)}
|
||
|
|
>
|
||
|
|
{children}
|
||
|
|
</PreferencesProvider>
|
||
|
|
```
|