API reference
Exports of @sigx/lynx-appearance v0.4.9.
Import everything from the package entry:
import {
AppearanceProvider,
useSystemColorScheme,
useSystemColorSchemeMT,
setStatusBarStyle,
setStatusBarBackgroundColor,
setNavigationBarStyle,
setSystemBarsStyle,
isAvailable,
readGlobalColorScheme,
} from '@sigx/lynx-appearance';
The native module is named Appearance and ships for both iOS and Android. No runtime permissions are required. Every native setter resolves (and never rejects) a SetterResult; on hosts where the module is not registered (web preview, SSR, tests, unlinked apps) they resolve { ok: false, reason: 'unsupported' }.
Components
AppearanceProvider
Mount once near the app root, above any consumer of useSystemColorScheme. It renders a <view class={props.class} style={props.style}> wrapping the default slot, seeds a background-side ColorScheme signal from readGlobalColorScheme (falling back to 'light'), exposes it via the useAppearanceContext DI handle, and subscribes to the native APPEARANCE_EVENT to update the signal live on system dark-mode flips. It unsubscribes on unmount.
export const AppearanceProvider = component<AppearanceProviderProps>(
({ props, slots }) => () => JSX,
)
- It is cheap: one signal plus one event subscription.
- The initial value is correct on cold start with no flash, because the native publisher writes
lynx.__globalProps.appearancebefore the main thread's first paint. - On unwired hosts (web preview, tests) the subscription simply never fires and the signal stays at the seeded default.
- See AppearanceProviderProps for props.
Hooks
useSystemColorScheme
Background-thread reactive read of the current system color scheme. Returns the live signal; components and effects that read .value re-render or re-run when the user flips dark mode in system settings.
export function useSystemColorScheme(): PrimitiveSignal<ColorScheme> | Computed<ColorScheme>
- Returns — a signal of
ColorScheme. Read with.value. - If no
AppearanceProvideris in scope, it returns a process-level fallback signal seeded once fromlynx.__globalProps— the value is correct on cold start but does not update live. This lets a consumer such as aThemeProvideruse the hook even without a provider mounted. - Platform: shared (background thread).
useSystemColorSchemeMT
Main-thread synchronous read of the current system color scheme, for use inside 'main thread'-marked worklet bodies. Reads lynx.__globalProps directly via readGlobalColorScheme with no subscription — callers re-evaluate on each worklet invocation.
export function useSystemColorSchemeMT(): ColorScheme
- Returns — the current
ColorScheme. Returns'light'when the publisher has not populated yet (cold start before first publish) or on non-Lynx hosts. - Platform: shared (main thread).
Functions
setSystemBarsStyle
Convenience setter that applies the status-bar tint plus (optionally) the status-bar background and nav-bar tint in one call. Fields are optional; omitting any leaves that surface untouched. The order is deterministic: status bar, then status-bar background, then navigation bar.
export async function setSystemBarsStyle(
opts: SystemBarsStyleInput,
): Promise<SetterResult>
opts— aSystemBarsStyleInput; fully partial.- Returns — the aggregate
SetterResult:{ ok: false }with the first non-'unsupported'failure'sreasonif any leg failed, otherwise{ ok: true }.'unsupported'legs (for example, status-bar background on iOS) are intentionally ignored so a partially-supported platform still reports success for the legs that apply. Resolves{ ok: false, reason: 'unsupported' }when no native module is registered. Never rejects. - Platform: shared.
setStatusBarStyle
Set the status-bar content tint (clock and icons).
export function setStatusBarStyle(
style: SystemBarStyle,
): Promise<SetterResult>
style— aSystemBarStyle.'light'= light content (white-ish icons; use behind a dark theme);'dark'= dark content (use behind a light theme).- Returns — a
SetterResult. On iOS the host view controller must forwardpreferredStatusBarStyletoAppearanceModule.preferredStatusBarStylefor this to visibly take effect (the lynx-cli iOS template wires this automatically). Resolves{ ok: false, reason: 'unsupported' }(never rejects) when the native module is not registered. - Platform: shared (effective on iOS + Android).
setStatusBarBackgroundColor
Android only — set the status-bar background color.
export function setStatusBarBackgroundColor(
color: string | null,
): Promise<SetterResult>
color— a color string. Passnull(or omit / pass'transparent') to clear.- Returns — a
SetterResult. iOS resolves{ ok: false, reason: 'unsupported' }, since iOS has no separate status-bar background. On Android 15+ (API 35) edge-to-edge is enforced and this is a no-op at the system level — overlay your own background view inside the safe-area top padding instead. Resolves{ ok: false, reason: 'unsupported' }when the native module is not registered. Never rejects. - Platform: Android only.
setNavigationBarStyle
Android only — set the navigation-bar content tint (style) plus an optional background color.
export function setNavigationBarStyle(
opts: { style: SystemBarStyle; color?: string },
): Promise<SetterResult>
opts.style— aSystemBarStylefor the nav-bar content tint.opts.color— optional background color.- Returns — a
SetterResult. iOS resolves{ ok: false, reason: 'unsupported' }, since there is no separate navigation bar. Resolves{ ok: false, reason: 'unsupported' }when the native module is not registered. Never rejects. - Platform: Android only.
readGlobalColorScheme
Synchronously read the current system color scheme from lynx.__globalProps.appearance.colorScheme.
export function readGlobalColorScheme(): ColorScheme | null
- Returns — the current
ColorScheme, ornullwhen the publisher has not populated yet (early cold start) or when running outside a Lynx host (web preview, SSR, tests). Treatnullas "unknown, fall back to your default theme". Validates the value is exactly'dark'or'light', else returnsnull. - Safe on both the background and main threads, since
__globalPropsis mirrored across both. - Platform: shared (background + main thread).
isAvailable
Quick check for whether the native Appearance module is registered in the current build (delegates to isModuleAvailable('Appearance') from @sigx/lynx-core).
export function isAvailable(): boolean
- Returns —
trueif the module is registered. All setters call this internally to short-circuit to the unsupported result, so you rarely need it directly. - Platform: shared.
Injectables
useAppearanceContext
The DI handle for the appearance context, created via defineInjectable. Its default factory returns null so consumers outside an AppearanceProvider get a clear null signal rather than a phantom 'light' signal that silently never updates.
export const useAppearanceContext = defineInjectable<AppearanceContextValue | null>(
() => null,
)
- Returns — the provided
AppearanceContextValuewhen inside a provider, otherwisenull. - Prefer the
useSystemColorSchemehook, which wraps this with a null-check and fallback signal. - Platform: shared.
Constants
APPEARANCE_EVENT
export const APPEARANCE_EVENT = 'appearanceChanged';
The GlobalEventEmitter event name fired by the native publishers (iOS AppearancePublisher.swift, Android AppearancePublisher.kt) every time the host's system color scheme flips. The payload mirrors the map stored under lynx.__globalProps.appearance (a { colorScheme } object). Exported as a single shared constant so the iOS/Android publishers and the JS listener agree on one string. Typed as the literal 'appearanceChanged'.
GLOBAL_PROPS_KEY
export const GLOBAL_PROPS_KEY = 'appearance';
The key under lynx.__globalProps where the native publisher writes the appearance map. A single string shared by the iOS/Android publishers, the JS reader, and tests. Typed as the literal 'appearance'.
Types
ColorScheme
The two color schemes the platform reports. The iOS unspecified and Android UI_MODE_NIGHT_UNDEFINED states both collapse to 'light' at the native publisher boundary, so JS only ever sees these two values.
export type ColorScheme = 'light' | 'dark';
SystemBarStyle
Tint of system-bar content (clock, icons), not its background. 'light' = light-colored icons (legible on a dark background); 'dark' = dark-colored icons.
export type SystemBarStyle = 'light' | 'dark';
SystemBarsStyleInput
Argument to setSystemBarsStyle. Fully partial — omitted fields are left alone.
export interface SystemBarsStyleInput {
/** Status-bar content tint. `'light'` = light icons (legible on dark bg). */
statusBar?: SystemBarStyle;
/** Android only — status-bar background color. `null` clears (transparent). */
statusBarBackground?: string | null;
/** Android only — navigation-bar tint + optional background. */
navigationBar?: { style: SystemBarStyle; color?: string };
}
statusBar— status-bar content tint.statusBarBackground— Android only; status-bar background color,nullclears to transparent.navigationBar— Android only; nav-bar tint plus an optional background color.
SetterResult
The result envelope returned (resolved, never rejected) by every native setter.
export interface SetterResult {
ok: boolean;
/** Present when `ok === false` — `'unsupported'` on iOS for nav-bar and
* status-bar-background calls, or an Android failure message. */
reason?: string;
}
ok— indicates success.reason— present only whenok === false. It is'unsupported'on iOS for nav-bar / status-bar-background calls and on any platform when the module is not registered, or an Android failure message (for example,'No hosting Activity found').
AppearanceContextValue
The DI shape exposed by AppearanceProvider. Holds a readonly background-side reactive colorScheme signal that re-renders consumers when the system color scheme flips.
export interface AppearanceContextValue {
/** BG-side reactive color scheme. Re-renders consumers on system flip. */
readonly colorScheme: PrimitiveSignal<ColorScheme>;
}
AppearanceProviderProps
Props for AppearanceProvider: an optional class, an optional style, and a default slot for children.
export type AppearanceProviderProps =
& Define.Prop<'class', string, false>
& Define.Prop<'style', Record<string, string | number>, false>
& Define.Slot<'default'>;
class— optional class string for the host view.style— optional inline style for the host view.defaultslot — your app content.
RawAppearanceProps
Shape of the raw appearance map the native publisher writes under lynx.__globalProps.appearance.
export interface RawAppearanceProps {
colorScheme?: ColorScheme;
}
colorScheme— optionalColorScheme.
