Lynx/Modules/Appearance/API reference
@sigx/lynx-appearance · Stable

API reference#

Exports of @sigx/lynx-appearance v0.26.0.

Import everything from the package entry:

TypeScript
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.

TypeScript
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.appearance before 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.

TypeScript
export function useSystemColorScheme(): PrimitiveSignal<ColorScheme> | Computed<ColorScheme>
  • Returns — a signal of ColorScheme. Read with .value.
  • If no AppearanceProvider is in scope, it returns a process-level fallback signal seeded once from lynx.__globalProps — the value is correct on cold start but does not update live. This lets a consumer such as a ThemeProvider use 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.

TypeScript
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).

useFontScale#

Background-thread reactive read of the OS font scale — the effective text-size multiplier the engine applies to every font-relevant CSS length. 1 is the default.

TypeScript
export function useFontScale(): Computed<number>
  • Returns — a stable Computed<number>. Read .value in render or effects and the component re-renders when the user changes the system text size.
  • Re-exported from @sigx/lynx-core, where it sits next to the native publisher.
  • Platform: shared (background thread).

The engine already scales plain <text> and font-size. You do not need this to honour Dynamic Type — that happens whether you read it or not. Use it to adapt around larger text: swap a row layout for a stacked one, grow custom-drawn text, size icons to match.

TSX
const scale = useFontScale();

return () => (
  <view style={{ flexDirection: scale.value > 1.3 ? 'column' : 'row' }}>

  </view>
);

useFontScaleMT#

Main-thread synchronous read of the effective font scale, for 'main thread'-marked worklet bodies. Reads lynx.__globalProps directly with no subscription — callers re-evaluate per worklet invocation.

TypeScript
export function useFontScaleMT(): number
  • Returns — the effective scale, or 1 when the publisher has not populated yet or on non-Lynx hosts.
  • Platform: shared (main thread).

readGlobalFontScale#

Synchronous, non-reactive read of the raw font-scale map.

TypeScript
export function readGlobalFontScale(): { scale: number; os: number } | null
  • scale — the policy-clamped effective value the engine applies.
  • os — the raw system setting (Dynamic Type / Configuration.fontScale), equal to scale unless the app's fontScale.min/max config clamped it.
  • Returns null when the publisher has not populated yet (early cold start) or outside a Lynx host (web preview, SSR, tests). Treat null as "unknown — assume 1".
  • Safe on both threads; __globalProps is mirrored across both.

Not reactive. A read taken during the render triggered by onFontScaleChanged can still see the previous os, because the globalProps mirror updates independently of the event. Prefer useFontScale for the live effective value and treat os as informational.

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.

TypeScript
export async function setSystemBarsStyle(
  opts: SystemBarsStyleInput,
): Promise<SetterResult>
  • opts — a SystemBarsStyleInput; fully partial.
  • Returns — the aggregate SetterResult: { ok: false } with the first non-'unsupported' failure's reason if 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).

TypeScript
export function setStatusBarStyle(
  style: SystemBarStyle,
): Promise<SetterResult>
  • style — a SystemBarStyle. '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 forward preferredStatusBarStyle to AppearanceModule.preferredStatusBarStyle for 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.

TypeScript
export function setStatusBarBackgroundColor(
  color: string | null,
): Promise<SetterResult>
  • color — a color string. Pass null (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.

TypeScript
export function setNavigationBarStyle(
  opts: { style: SystemBarStyle; color?: string },
): Promise<SetterResult>
  • opts.style — a SystemBarStyle for 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.

TypeScript
export function readGlobalColorScheme(): ColorScheme | null
  • Returns — the current ColorScheme, or null when the publisher has not populated yet (early cold start) or when running outside a Lynx host (web preview, SSR, tests). Treat null as "unknown, fall back to your default theme". Validates the value is exactly 'dark' or 'light', else returns null.
  • Safe on both the background and main threads, since __globalProps is 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).

TypeScript
export function isAvailable(): boolean
  • Returnstrue if 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.

TypeScript
export const useAppearanceContext = defineInjectable<AppearanceContextValue | null>(
  () => null,
)

Constants#

APPEARANCE_EVENT#

TypeScript
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'.

FONT_SCALE_EVENT#

TypeScript
export const FONT_SCALE_EVENT = 'onFontScaleChanged';

The global event fired by the Lynx engine — not a sigx publisher — whenever the host calls LynxView.updateFontScale(). Payload: { scale }, the new effective scale. The name is engine-owned upstream.

FONT_SCALE_GLOBAL_KEY#

TypeScript
export const FONT_SCALE_GLOBAL_KEY = 'fontScale';

The key under lynx.__globalProps where the native FontScalePublisher writes the font-scale map before the main thread's first paint — so a cold start renders at the right size rather than correcting after.

GLOBAL_PROPS_KEY#

TypeScript
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.

TypeScript
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.

TypeScript
export type SystemBarStyle = 'light' | 'dark';

SystemBarsStyleInput#

Argument to setSystemBarsStyle. Fully partial — omitted fields are left alone.

TypeScript
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, null clears 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.

TypeScript
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 when ok === 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.

TypeScript
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.

TypeScript
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.
  • default slot — your app content.

RawAppearanceProps#

Shape of the raw appearance map the native publisher writes under lynx.__globalProps.appearance.

TypeScript
export interface RawAppearanceProps {
  colorScheme?: ColorScheme;
}