Lynx/Modules/Zero/ThemeProvider
@sigx/lynx-zero · Beta · Component library

ThemeProvider#

The theme host: a view with the zx-root and zx-theme-<name> classes that a skin's compiled theme blocks select on. Selecting a theme swaps one class; no theme values pass through JavaScript.

Import#

TSX
import { ThemeProvider } from '@sigx/lynx-zero';

Most apps never render it directly: ZeroRoot renders a ThemeProvider with the overlay outlet inside it, and takes the same props. Use ThemeProvider alone for a nested theme scope.

How it works#

A skin compiled for Lynx declares every theme as static CSS. .zx-root holds the default tokens, and each theme is a full .zx-root.zx-theme-<name> block. The provider owns three things:

  • The host classes: zx-root, plus zx-theme-<name> for the active theme, plus your class.
  • Follow-system: while following, the OS color scheme picks the theme through the shared registry (pickThemeFor). The scheme is seeded natively before the first paint, so the first render already uses the right theme.
  • The font scale: at a scale other than 1, it re-emits the skin's scalable --text-* ramp inline as scaled px. The --text-fixed-* ramp, used for control chrome, never scales.

The host also paints --color-base-100 as its background and --color-base-content as its text color, and it is a flex column, so a ScrollView flex={1} inside it gets a bounded height.

Theme names and pairs come from the registry a skin seeds when you import it. See theming.

Following the system scheme#

With no props, the root provider follows the OS scheme and uses the skin's default light and dark themes. Name them with light and dark:

TSX
import { ThemeProvider } from '@sigx/lynx-zero';

<ThemeProvider light="nord" dark="dim">
    {/* recolors when the OS scheme flips */}
</ThemeProvider>;

initial pins a theme and stops following the system until followSystem() is called:

TSX
<ThemeProvider initial="sunset">{/* … */}</ThemeProvider>

Root and nested providers#

The outermost provider binds the global themeController, so a headless themeController.set('dark') from a store or a service re-renders it. A nested provider is a content island with its own state that recolors only its subtree, for example a preview pane:

TSX
import { Col, ThemeProvider } from '@sigx/lynx-zero';

function PreviewPane() {
    return (
        <ThemeProvider initial="dark">
            <Col padding={16}>
                <text>Always dark</text>
            </Col>
        </ThemeProvider>
    );
}

A nested provider with no fontScale inherits the enclosing scale. An overlay opened from inside a nested provider renders in ZeroRoot's outlet, which is outside the nested provider's view, so it does not take the nested theme.

Reading and changing the theme#

useTheme() returns the nearest provider's controller, or the global one outside any provider. It never throws.

TSX
import { Button, useTheme } from '@sigx/lynx-zero';

function ThemeToggle() {
    const theme = useTheme();
    return (
        <Button onPress={() => theme.toggle()}>
            <text>Theme: {theme.name}</text>
        </Button>
    );
}

Props#

PropTypeDescription
initialThemeNamePins this theme, ignoring the system scheme until followSystem().
lightThemeNameTheme while the system is light. Defaults to the registry's light default.
darkThemeNameTheme while the system is dark. Defaults to the registry's dark default.
fontScalenumberInitial in-app text-scale multiplier. 1 is the stylesheet ramp.
classstringExtra classes, appended after the theme classes.
styleRecord<string, string | number>Extra inline style on the host view, merged after the defaults.

ThemeName is string.

Slots#

SlotDescription
defaultThe themed subtree.

See also#

  • ZeroRoot: ThemeProvider plus the overlay outlet.
  • Theming: the controller, font scale, per-screen themes and the status bar.
  • Zero DaisyUI: the themes the daisyUI skin registers.