Lynx/Modules/Zero/API reference
@sigx/lynx-zero · Stable · Component library

API reference#

Every export of @sigx/lynx-zero v0.28.0 — the headless contract, shared style/press/tabs helpers, the theme engine, layout primitives and the Tailwind preset.

@sigx/lynx-zero is the foundation a Lynx design system builds on. It owns the shared vocabulary (sizes, semantic colors, tokens, prop fragments, the press event), the theme registry and runtime, the headless tabs-selection contract and the layout primitives. A DS (such as @sigx/lynx-daisyui) extends these types and re-exports the runtime.

Most exports live on the main barrel. The Tailwind preset is a subpath-only export (@sigx/lynx-zero/preset), and useScreenTheme is exposed from its own subpath (@sigx/lynx-zero/screen-theme) because it depends on the optional @sigx/lynx-navigation peer.

Components#

Each component has its own page with usage, props, slots and events — see the Components catalog:

  • ThemeProvider — applies a theme's CSS vars to a subtree; the root provider binds the global singleton, nested providers are content sub-scopes.
  • StatusBarSync — side-effect-only component that keeps the OS status / navigation bar tint legible against the active global theme.
  • Row — flex-row container (flexDirection: row).
  • Col — flex-column container (flexDirection: column).
  • Center — flex container centering its children on both axes.
  • Spacer — spacing element: a fixed square of size, or flex: 1 to fill when size is omitted.
  • ScrollView — scrollable container over the Lynx <scroll-view>.

Their prop types and related APIs remain documented below.

Entry points#

TypeScript
import {
  // contract + shared helpers
  resolveColorToken, resolveSpacing, resolveBoxStyle,
  PRESSED_SCALE, PRESSED_OPACITY,
  useTabsSelection, provideTabsSelection,
  // theme engine
  ThemeProvider, StatusBarSync, themeController, useTheme,
  registerTheme, extendTheme, listThemes, completeTheme,
  pickThemeFor, pairOf, variantOf, colorsOf, radiusOf, sizesOf,
  mixColors, useThemeColors, toHexColor, withAlpha,
  // layout primitives
  Row, Col, Center, Spacer, ScrollView,
} from '@sigx/lynx-zero';

// Tailwind preset (subpath only)
import { zeroPreset, contractColors, contractFontSizes, lynxLayoutPlugin } from '@sigx/lynx-zero/preset';

// screen-theme (subpath only; needs the @sigx/lynx-navigation peer)
import { useScreenTheme } from '@sigx/lynx-zero/screen-theme';

Contract#

The shared vocabulary that components and themes agree on. These are the single source of truth a DS extends — sizes, semantic colors, the token derivations, the reusable prop fragments and the canonical press event. A DS never redeclares these; it imports and layers on top.

SizeScale#

TypeScript
type SizeScale = 'xs' | 'sm' | 'md' | 'lg' | 'xl';

Shared component size scale. DS sizes are this type (extended, never redeclared).

COLOR_VARIANT_LIST#

TypeScript
const COLOR_VARIANT_LIST: readonly [
  'primary', 'secondary', 'accent', 'neutral',
  'info', 'success', 'warning', 'error',
];

Single source of truth for the semantic color vocabulary; ColorVariant and every token derivation come from this tuple.

ColorVariant#

TypeScript
type ColorVariant = typeof COLOR_VARIANT_LIST[number];

Semantic color names — the shared color prop vocabulary. A DS maps each name onto its palette.

CoreColorToken#

TypeScript
type CoreColorToken =
  | ColorVariant
  | `${ColorVariant}-content`
  | 'base-100' | 'base-200' | 'base-300' | 'base-content';

Tokens authored by every theme: each variant, its -content pairing, and the base surfaces.

SoftColorToken#

TypeScript
type SoftColorToken = `${ColorVariant}-soft`;

Tinted-surface tokens, one per variant; materialized in the palette at registration (a softMix of the variant color into base-100).

ColorToken#

TypeScript
type ColorToken = CoreColorToken | SoftColorToken;

The full set of semantic color tokens every registered theme carries, exposed at runtime as --color-<token>.

BackgroundValue#

TypeScript
type BackgroundValue = ColorToken | (string & {});

A semantic color token (autocompleted) or any raw CSS color string.

WithClass#

TypeScript
type WithClass = Define.Prop<'class', string, false>;

Prop fragment: arbitrary extra classes, appended after the DS-computed ones.

WithDisabled#

TypeScript
type WithDisabled = Define.Prop<'disabled', boolean, false>;

Prop fragment: disabled (non-interactive plus DS disabled styling).

WithColor#

TypeScript
type WithColor = Define.Prop<'color', ColorVariant, false>;

Prop fragment: the semantic color of the component.

WithSize#

TypeScript
type WithSize = Define.Prop<'size', SizeScale, false>;

Prop fragment: component size on the shared scale.

WithAccessibility#

TypeScript
type WithAccessibility =
  & Define.Prop<'accessibility-element', boolean, false>
  & Define.Prop<'accessibility-label', string, false>
  & Define.Prop<'accessibility-role', string, false>
  & Define.Prop<'accessibility-trait', string, false>
  & Define.Prop<'accessibility-status', string, false>;

Prop fragment: accessibility passthrough mirroring the @sigx/lynx-gestures Pressable host-view surface.

PressEvent#

TypeScript
type PressEvent = Define.Event<'press', void>;

The shared press event. The sigx convention is onPress (not onTap / onClick).

resolveColorToken#

TypeScript
function resolveColorToken(value: string): string;

Maps a known semantic token to var(--color-<token>); anything else (hex / rgb / var(...)) passes through unchanged.

Shared styles#

Box-model and spacing helpers shared by the layout primitives and DS components. These convert the friendly prop shapes (SpacingValue, BoxProps) into Lynx-safe long-form style objects.

SpacingValue#

TypeScript
type SpacingValue =
  | number
  | { x?: number; y?: number; top?: number; right?: number; bottom?: number; left?: number };

Spacing input for padding / margin: a number applies to all sides, or pass a per-axis (x / y) / per-side object.

BoxProps#

TypeScript
interface BoxProps {
  width?: number | string;
  height?: number | string;
  flex?: number;
  background?: BackgroundValue;
  borderRadius?: number;
  padding?: SpacingValue;
  margin?: SpacingValue;
}

The box-model props shared by layout primitives and DS components.

resolveSpacing#

TypeScript
function resolveSpacing(
  value: SpacingValue | undefined,
  prefix: 'padding' | 'margin',
): Record<string, number>;

Expands a SpacingValue into long-form { paddingTop, ... } / { marginTop, ... } style entries; returns {} for undefined.

resolveBoxStyle#

TypeScript
function resolveBoxStyle(props: BoxProps): Record<string, unknown>;

Converts BoxProps to a Lynx style object: resolves color tokens and writes long-form flex (flexGrow / flexShrink / flexBasis / minHeight) to avoid Lynx flex-collapse.

Shared press#

Defaults for the press-feedback affordance. Lynx has no CSS :active, so Pressable applies these on the main thread.

PRESSED_SCALE#

TypeScript
const PRESSED_SCALE = 0.97;

Default press-feedback scale applied by Pressable on the main thread.

PRESSED_OPACITY#

TypeScript
const PRESSED_OPACITY = 0.85;

Default press-feedback opacity for interactive components.

Shared tabs selection#

The headless tabs-selection contract: a reactive isActive() / select() pair that a DS Tabs container injects into its Tabs. Tabs read it through useTabsSelection; the container provides it with provideTabsSelection.

TabsSelection#

TypeScript
interface TabsSelection {
  isActive(value: string): boolean;
  select(value: string): void;
}

The headless tabs-selection contract.

useTabsSelection#

TypeScript
const useTabsSelection: () => TabsSelection; // defineInjectable<TabsSelection>(() => NO_SELECTION)

Injects the nearest Tabs container's selection. Outside any container it resolves to an inert selection — never active, and select() is a no-op.

provideTabsSelection#

TypeScript
function provideTabsSelection(
  getActive: () => string | undefined,
  onSelect: (value: string) => void,
): void;

Provides a TabsSelection for the subtree. Call it from the DS Tabs container setup, passing getters into the reactive props so isActive() reads stay reactive.

Theme engine#

The theme runtime and registry. ThemeProvider applies a theme's CSS vars to a subtree; themeController / useTheme read and mutate the active theme; the registry functions register, derive and inspect themes; and the color helpers resolve the palette to concrete literals for native widgets that cannot read CSS vars.

ThemeProviderProps#

TypeScript
type ThemeProviderProps =
  & Define.Prop<'initial', ThemeName, false>
  & Define.Prop<'light', ThemeName, false>
  & Define.Prop<'dark', ThemeName, false>
  & Define.Prop<'fontScale', number, false>
  & Define.Prop<'class', string, false>
  & Define.Prop<'style', Record<string, string | number>, false>
  & Define.Slot<'default'>;

ThemeProvider props. initial pins a theme (ignoring the system); light / dark are the choices used while following the system; fontScale seeds the initial text-scale multiplier.

StatusBarSyncProps#

TypeScript
type StatusBarSyncProps = Define.Prop<'matchBackground', boolean, false>;

matchBackground is currently a reserved no-op (future Android base-100 bar background).

themeController#

TypeScript
const themeController: ThemeController;

The global headless theme controller — import and call it from anywhere, with no ThemeProvider ancestor required. useTheme()'s default returns this, and StatusBarSync binds to it.

useTheme#

TypeScript
const useTheme: () => ThemeController; // defineInjectable<ThemeController>(() => themeController)

Accesses the active ThemeController — the nearest ThemeProvider, or the global themeController at the root / headless. Never throws.

ThemeController#

TypeScript
interface ThemeController {
  readonly name: ThemeName;
  readonly followingSystem: boolean;
  set(name: ThemeName): void;
  toggle(): void;
  followSystem(): void;
  readonly fontScale: number;
  setFontScale(scale: number): void;
}

Reactive handle over theme selection: read / track name, followingSystem and fontScale; mutate via set / toggle / followSystem / setFontScale.

ThemeName#

TypeScript
type ThemeName = string & {};

The theme class set on the provider host view. A plain string; a DS layers a literal union on top for autocomplete. Multi-class names (e.g. 'daisy-light daisy-rounded') are accepted.

listThemes#

TypeScript
function listThemes(): readonly Theme[];

All registered themes in insertion order (a shallow copy; each entry is a full Theme, suitable for swatch rendering). Re-exported via ThemeProvider.

registerTheme#

TypeScript
function registerTheme(theme: ThemeInput): void;

Registers or replaces (by name) a theme; the palette is completed on the way in (completeTheme computes the omitted *-soft tints). Call it at module load, before mounting.

extendTheme#

TypeScript
function extendTheme(base: string, patch: {
  name: string;
  variant?: ThemeVariant;
  pair?: string;
  colors?: Partial<ThemePalette>;
  radius?: ThemeRadius;
  sizes?: ThemeSizes;
  softMix?: number;
}): Theme;

Derives a new full Theme from a registered base, overriding tokens and recomputing any soft tints the patch did not set. Throws if base is not registered. Pass the result to registerTheme(). The derived theme never inherits staticCss — it is a new name with no generated stylesheet rule behind it, so it paints through the inline path.

pickThemeFor#

TypeScript
function pickThemeFor(scheme: ThemeVariant): string;

The default theme name for a system color scheme — the first registered theme of that variant, else the first of any, else ''.

pairOf#

TypeScript
function pairOf(name: string): string;

Resolves a theme's paired theme (used by toggle()) — follows the declared pair, else the first of the opposite variant; returns the input unchanged if it is not registered.

variantOf#

TypeScript
function variantOf(name: string | undefined): ThemeVariant | undefined;

The variant of a registered theme, or undefined if it is not registered.

colorsOf#

TypeScript
function colorsOf(name: string | undefined): ThemePalette | undefined;

The full, completed color palette of a registered theme, or undefined.

radiusOf#

TypeScript
function radiusOf(name: string | undefined): ThemeRadius | undefined;

The roundness overrides of a registered theme, if any.

sizesOf#

TypeScript
function sizesOf(name: string | undefined): ThemeSizes | undefined;

The base size-unit overrides of a registered theme, if any.

completeTheme#

TypeScript
function completeTheme(input: ThemeInput): Theme;

Completes a theme's palette: computes any omitted *-soft token as softMix of the variant color into base-100 (in JS). Idempotent; explicit softs are kept.

ThemeInput#

TypeScript
interface ThemeInput {
  name: string;
  variant: ThemeVariant;
  colors: ThemePaletteInput;
  pair?: string;
  radius?: ThemeRadius;
  sizes?: ThemeSizes;
  softMix?: number;
  staticCss?: boolean;
}

What a theme author writes: core tokens required, *-soft optional. softMix defaults to 0.16.

staticCss declares that the design system generated a stylesheet rule for this theme from the same registry data at build time — a .theme-name { --color-*: …; background-color: …; color: … } rule plus its @media (prefers-color-scheme: …) twin under .lynx-zero.scheme-<variant>-<theme-name>. ThemeProvider then lets the CSS engine resolve the palette instead of declaring it inline; hasStaticCss(name) reads the flag. Leave it unset for anything registered at runtime — a tenant palette fetched from a backend, or a theme derived with extendTheme(). Claiming staticCss for a theme whose CSS does not exist leaves the app with no palette at all.

ThemePaletteInput#

TypeScript
type ThemePaletteInput =
  & Record<CoreColorToken, string>
  & Partial<Record<SoftColorToken, string>>;

The author-side palette: every core token required, the *-soft tints optional.

Theme#

TypeScript
interface Theme extends Omit<ThemeInput, 'colors'> {
  colors: ThemePalette;
}

A registered theme — the same shape as ThemeInput, with the palette completed (no holes).

ThemePalette#

TypeScript
type ThemePalette = Record<ColorToken, string>;

The full registered color palette — every semantic token including *-soft. This is what colorsOf() returns.

ThemeRadius#

TypeScript
interface ThemeRadius {
  selector?: string;
  field?: string;
  box?: string;
}

Roundness token overrides, emitted as --radius-selector / --radius-field / --radius-box.

ThemeSizes#

TypeScript
interface ThemeSizes {
  selector?: string;
  field?: string;
}

Base size-unit overrides, emitted as --size-selector / --size-field; component dimensions are integer multiples.

ThemeVariant#

TypeScript
type ThemeVariant = 'light' | 'dark';

The theme variant — drives follow-system selection and status-bar tint.

mixColors#

TypeScript
function mixColors(color: string, base: string, ratio: number): string;

Mixes ratio of color into base (linear sRGB per-channel, like color-mix in srgb) and returns a hex string. Returns base unchanged if either input is unparseable or ratio is non-finite. Accepts #rgb / #rrggbb / #rrggbbaa / rgb() / rgba().

useThemeColors#

TypeScript
function useThemeColors(): ThemeColors;

Resolves the active, scoped theme palette to concrete color literals for consumers that cannot read CSS vars (native inputs, richtext, SVG). Reactive via useTheme(). Platform: iOS and Android.

ThemeColors#

TypeScript
interface ThemeColors {
  colorOf(token: ColorToken, alpha?: number): string;
}

colorOf(token, alpha?) returns the active palette value normalized to hex, with an optional alpha (0–1) appended as #RRGGBBAA. Returns '' when no theme is registered.

toHexColor#

TypeScript
function toHexColor(color: string): string;

Normalizes an engine-safe palette color (#RGB / #RGBA / rgb() / rgba()) to full-form hex (#RRGGBB / #RRGGBBAA); unknown notations pass through.

withAlpha#

TypeScript
function withAlpha(hex: string, alpha: number): string;

Appends an alpha channel (0–1) to a hex color, producing #RRGGBBAA. Non-hex / non-#RRGGBB-coercible input passes through; a non-finite alpha is treated as opaque.

useScreenTheme#

TypeScript
// from '@sigx/lynx-zero/screen-theme'
function useScreenTheme(name: ThemeName): void;

Pins the global theme while a navigation screen is focused, and restores it on blur (including resuming follow-system). Built on useFocusEffect from @sigx/lynx-navigation (an optional peer). Not exported from the barrel — import it from @sigx/lynx-zero/screen-theme.

Layout primitives#

Flexbox containers and a spacer, built on Lynx views, that consume the shared box-model / spacing helpers. Each accepts class for arbitrary extra utilities.

RowProps#

TypeScript
type RowProps =
  & Define.Prop<'gap', number, false>
  & Define.Prop<'align', 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline', false>
  & Define.Prop<'justify', 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly', false>
  & Define.Prop<'wrap', boolean, false>
  & Define.Prop<'padding', SpacingValue, false>
  & Define.Prop<'margin', SpacingValue, false>
  & Define.Prop<'width', number | string, false>
  & Define.Prop<'height', number | string, false>
  & Define.Prop<'flex', number, false>
  & Define.Prop<'background', BackgroundValue, false>
  & Define.Prop<'borderRadius', number, false>
  & Define.Prop<'class', string, false>
  & Define.Slot<'default'>;

Row props.

ColProps#

TypeScript
type ColProps = /* identical shape to RowProps */;

Col props — identical shape to RowProps.

CenterProps#

TypeScript
type CenterProps =
  & Define.Prop<'width', number | string, false>
  & Define.Prop<'height', number | string, false>
  & Define.Prop<'flex', number, false>
  & Define.Prop<'background', BackgroundValue, false>
  & Define.Prop<'borderRadius', number, false>
  & Define.Prop<'class', string, false>
  & Define.Slot<'default'>;

Center props.

SpacerProps#

TypeScript
type SpacerProps =
  & Define.Prop<'size', number, false>
  & Define.Prop<'class', string, false>;

Spacer props (no default slot).

ScrollViewProps#

TypeScript
type ScrollViewProps =
  & Define.Prop<'direction', 'vertical' | 'horizontal', false>
  & Define.Prop<'height', number | string, false>
  & Define.Prop<'width', number | string, false>
  & Define.Prop<'flex', number, false>
  & Define.Prop<'showScrollbar', boolean, false>
  & Define.Prop<'bounces', boolean, false>
  & Define.Prop<'class', string, false>
  & Define.Slot<'default'>;

ScrollView props.

Preset#

The Tailwind layer that wires the semantic tokens, the --text-* ramp and the Lynx-correct fill utility into your config. These are subpath-only exports — import them from @sigx/lynx-zero/preset, not the main barrel.

zeroPreset#

TypeScript
// from '@sigx/lynx-zero/preset'
const zeroPreset: Partial<Config>; // tailwindcss Config

The @sigx/lynx-zero Tailwind preset: composes the semantic color tokens, the --text-* font-size ramp and the .flex-fill utility. Subpath export only.

contractColors#

TypeScript
// from '@sigx/lynx-zero/preset'
const contractColors: Record<string, string>;

Every semantic color token (including *-soft) mapped to var(--color-*); merged into Tailwind theme.extend.colors. Subpath export only.

contractFontSizes#

TypeScript
// from '@sigx/lynx-zero/preset'
const contractFontSizes: Record<string, string>;

Re-points the Tailwind text-xstext-3xl sizes to the shared --text-* ramp; merged via theme.extend.fontSize. Subpath export only.

lynxLayoutPlugin#

TypeScript
// from '@sigx/lynx-zero/preset'
const lynxLayoutPlugin: ReturnType<typeof plugin>; // tailwindcss plugin

A Tailwind plugin shipping .flex-fill — the Lynx-correct long-form "fill remaining space" utility (flexGrow / flexShrink 1, flexBasis 0, minHeight 0). Subpath export only.

Examples#

A headless theme registered at module load, then applied around a layout built from the primitives:

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

registerTheme({
  name: 'brand-light',
  variant: 'light',
  pair: 'brand-dark',
  colors: {
    primary: '#4f46e5',
    'primary-content': '#ffffff',
    secondary: '#0ea5e9',
    'secondary-content': '#ffffff',
    accent: '#f59e0b',
    'accent-content': '#1a1a1a',
    neutral: '#374151',
    'neutral-content': '#ffffff',
    info: '#0284c7',
    'info-content': '#ffffff',
    success: '#16a34a',
    'success-content': '#ffffff',
    warning: '#d97706',
    'warning-content': '#1a1a1a',
    error: '#dc2626',
    'error-content': '#ffffff',
    'base-100': '#ffffff',
    'base-200': '#f3f4f6',
    'base-300': '#e5e7eb',
    'base-content': '#1f2937',
  },
});
TSX
import { ThemeProvider, StatusBarSync, Col, Row, Center, Spacer } from '@sigx/lynx-zero';

function App() {
  return (
    <ThemeProvider initial="brand-light">
      <StatusBarSync />
      <Col flex={1} gap={16} padding={20} background="base-100">
        <Row gap={12} align="center" justify="space-between">
          <view class="text-base-content text-lg">Dashboard</view>
          <Spacer size={8} />
        </Row>
        <Center flex={1} background="base-200" borderRadius={12}>
          <view class="text-base-content">Centered content</view>
        </Center>
      </Col>
    </ThemeProvider>
  );
}

Reading and mutating the active theme via the controller (toggling between a theme and its pair):

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

function ThemeToggle() {
  const theme = useTheme();
  return (
    <Row gap={8} align="center">
      <view class="text-base-content">Theme: {theme.name}</view>
      <view bindtap={() => theme.toggle()} class="text-primary">Toggle</view>
    </Row>
  );
}

Resolving palette tokens to concrete hex for a native widget that cannot read CSS vars:

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

function SwatchColors() {
  const colors = useThemeColors();
  const primary = colors.colorOf('primary');
  const faintPrimary = colors.colorOf('primary', 0.12); // #RRGGBBAA
  return <view style={{ backgroundColor: faintPrimary }}>{primary}</view>;
}

Per-screen theme pinning (requires the optional @sigx/lynx-navigation peer):

TSX
import { useScreenTheme } from '@sigx/lynx-zero/screen-theme';

function SettingsScreen() {
  useScreenTheme('brand-dark'); // pinned while focused, restored on blur
  return <view class="text-base-content">Settings</view>;
}

Wiring the preset into a Tailwind config:

TypeScript
import type { Config } from 'tailwindcss';
import { zeroPreset } from '@sigx/lynx-zero/preset';

const config: Partial<Config> = {
  presets: [zeroPreset],
};

export default config;

See also#

  • Overview — what Zero is and how a DS builds on it.
  • Installation — package setup and the Tailwind preset.
  • Usage — setup and task-based guides.