docs(appearance): add bilingual package guides
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: Atomic composition
|
||||
description: Build a custom appearance surface from controlled components without the preset dialog.
|
||||
order: 1
|
||||
toc:
|
||||
- id: controlled-atoms
|
||||
title: Controlled atoms
|
||||
- id: compose-card
|
||||
title: Compose a theme card
|
||||
- id: connected-layer
|
||||
title: State bindings
|
||||
---
|
||||
|
||||
## Controlled atoms {#controlled-atoms}
|
||||
|
||||
`ThemeModeField`, `CompactLayoutField`, `ThemeColorPicker`, and `ThemeToggle` are driven by values and callbacks. They do not require preference context, so they can work with forms, preview tools, or another state library.
|
||||
|
||||
```tsx
|
||||
<ThemeModeField value={mode} onValueChange={setMode} />
|
||||
<CompactLayoutField checked={compacted} onCheckedChange={setCompacted} />
|
||||
```
|
||||
|
||||
## Compose a theme card {#compose-card}
|
||||
|
||||
`ThemeConfigCard` combines a preview with base and accent color controls while keeping the configuration owned by the caller.
|
||||
|
||||
```tsx
|
||||
<ThemeConfigCard
|
||||
active={resolvedTheme === "light"}
|
||||
compacted={compacted}
|
||||
config={lightTheme}
|
||||
onConfigChange={setLightTheme}
|
||||
scheme="light"
|
||||
/>
|
||||
```
|
||||
|
||||
For a more granular layout, compose `ThemePreview`, `baseColorSwatches`, `accentColorSwatches`, and `ThemeColorSwatchButton` directly.
|
||||
|
||||
## State bindings {#connected-layer}
|
||||
|
||||
`AppearanceSettings` is the default binding layer between the atoms and the shared `PreferencesProvider`. A custom surface can call `usePreference` from `@workspace/preferences` for individual values without reusing the default layout.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: Appearance settings dialog
|
||||
description: Compose a trigger, controller, and hotkeys into a complete appearance settings flow.
|
||||
order: 1
|
||||
toc:
|
||||
- id: controller
|
||||
title: Mount the controller
|
||||
- id: trigger
|
||||
title: Add a trigger
|
||||
- id: custom-handle
|
||||
title: Independent dialog instance
|
||||
---
|
||||
|
||||
## Mount the controller {#controller}
|
||||
|
||||
Mount one `AppearanceController` inside the application's shared `PreferencesProvider`. It composes the default settings content and registers `Mod+,` to open the dialog and `Mod+D` to toggle the theme.
|
||||
|
||||
```tsx
|
||||
<PreferencesProvider
|
||||
effects={[appearancePreferenceEffect]}
|
||||
initialPreferences={preferences}
|
||||
>
|
||||
<AppearanceController />
|
||||
<App />
|
||||
</PreferencesProvider>
|
||||
```
|
||||
|
||||
## Add a trigger {#trigger}
|
||||
|
||||
Place `AppearanceDialogTrigger` in a user menu, settings button, or command palette. By default it connects to the handle used by the default controller.
|
||||
|
||||
```tsx
|
||||
<AppearanceDialogTrigger render={<button type="button" />}>
|
||||
Appearance
|
||||
</AppearanceDialogTrigger>
|
||||
```
|
||||
|
||||
## Independent dialog instance {#custom-handle}
|
||||
|
||||
When a page needs an isolated appearance dialog, pass the same new handle to its trigger and controller.
|
||||
|
||||
```tsx
|
||||
const handle = createAppearanceDialogHandle()
|
||||
|
||||
<AppearanceDialogTrigger handle={handle} render={<button type="button" />}>
|
||||
Open theme settings
|
||||
</AppearanceDialogTrigger>
|
||||
<AppearanceController handle={handle} />
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
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>
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Understand the Appearance package atoms, theme data, and preference boundary.
|
||||
order: 1
|
||||
toc:
|
||||
- id: package-role
|
||||
title: Package role
|
||||
- id: atomic-components
|
||||
title: Atomic components
|
||||
- id: preferences
|
||||
title: Preference integration
|
||||
---
|
||||
|
||||
## Package role {#package-role}
|
||||
|
||||
`@workspace/appearance` provides the definitions, effects, theme data, and UI components for theme modes, base colors, accent colors, and compact layouts. It does not create its own provider or store. The application aggregates it with other preference packages through `@workspace/preferences`.
|
||||
|
||||
## Atomic components {#atomic-components}
|
||||
|
||||
The controlled components can be composed independently without preference context:
|
||||
|
||||
- `ThemeModeField` selects light, dark, or system mode.
|
||||
- `CompactLayoutField` toggles the main content width.
|
||||
- `ThemeColorPicker` and `ThemeColorSwatchButton` select theme colors.
|
||||
- `ThemePreview` previews a set of theme variables.
|
||||
- `ThemeConfigCard` combines the preview, base color, and accent color controls.
|
||||
- `ThemeToggle` switches between light and dark themes.
|
||||
|
||||
`AppearanceSettings`, `AppearanceDialog`, and `AppearanceController` are optional composition layers built from these atoms.
|
||||
|
||||
## Preference integration {#preferences}
|
||||
|
||||
```tsx
|
||||
import {
|
||||
createDefaultPreferences,
|
||||
PreferencesProvider,
|
||||
} from "@workspace/preferences"
|
||||
import {
|
||||
AppearanceController,
|
||||
appearancePreferenceDefinitions,
|
||||
appearancePreferenceEffect,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
const preferenceDefinitions = {
|
||||
...appearancePreferenceDefinitions,
|
||||
}
|
||||
const initialPreferences = createDefaultPreferences(preferenceDefinitions)
|
||||
const preferenceEffects = [appearancePreferenceEffect] as const
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={initialPreferences}
|
||||
>
|
||||
<AppearanceController />
|
||||
</PreferencesProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Add definitions and effects from locale, keyboard, or other packages to the same objects. The application can persist every `{ key, value }` update through the shared `onPreferenceChange` and parse cookies on the server with the aggregated definitions.
|
||||
Reference in New Issue
Block a user