@lattice-ui/react-styleStable directiondepends on runtimeStyle is the visual foundation. A theme of design tokens, helpers for merging Roblox host props, a
recipe system for variant-driven styling, and the Box/Text primitives that render those props
onto host instances.
The core idea is the sx value: a partial set of host props, or a function from the current
Theme to host props. Everything here produces, resolves or merges sx values.
Import
import { Box, Text } from "@lattice-ui/react-style";import { ThemeProvider, useTheme, useThemeValue } from "@lattice-ui/react-style";import { createTheme, defaultLightTheme, defaultDarkTheme } from "@lattice-ui/react-style";import { createRecipe, mergeSx, resolveSx, mergeGuiProps } from "@lattice-ui/react-style";API reference
Box
A styled host primitive rendering a frame with an sx value resolved against the current theme.
asChild merges its props onto a single child element instead.
function Box(props: BoxProps): React.Element;
<Box sx={(theme) => ({ BackgroundColor3: theme.colors.surface })} Size={UDim2.fromOffset(200, 80)}> {children}</Box>Direct host props and the resolved sx merge with mergeGuiProps(rest, sx), so sx wins on a
collision and Event/Change handler tables compose rather than replace. With asChild, Box
requires exactly one valid child element and clones its props onto it via Slot.
| Prop | Type | Description |
|---|---|---|
| sx | Sx<StyleProps> | Host props, or a function of the theme returning host props, applied to the frame. |
| asChild | boolean | Merge resolved props onto the single child element instead of rendering a frame. |
| children | React.ReactNode | Frame children, or the single element to clone when asChild is set. |
| ...rest | GuiObject props | Any host props, merged with sx via mergeGuiProps (sx wins on conflicts; event handlers compose). |
Text
Identical to Box, but renders a textlabel.
function Text(props: TextProps): React.Element;
<Text Text="Hello" sx={(theme) => ({ TextColor3: theme.colors.textPrimary, TextSize: theme.typography.bodyMd.textSize })}/>| Prop | Type | Description |
|---|---|---|
| sx | Sx<StyleProps> | Host props, or a function of the theme returning host props, applied to the textlabel. |
| asChild | boolean | Merge resolved props onto the single child element instead of rendering a textlabel. |
| children | React.ReactNode | Children, or the single element to clone when asChild is set. |
| ...rest | GuiObject props | Any host props, merged with sx via mergeGuiProps (sx wins on conflicts; event handlers compose). |
ThemeProvider
Provides the active theme to the tree and exposes a setter, controlled (theme) or uncontrolled
(defaultTheme), with onThemeChange notified on every change.
function ThemeProvider(props: ThemeProviderProps): React.Element;
<ThemeProvider defaultTheme={defaultDarkTheme} onThemeChange={handleChange}> {app}</ThemeProvider>With theme supplied the provider is controlled and setTheme only calls onThemeChange. It
defaults to defaultLightTheme.
| Prop | Type | Description |
|---|---|---|
| theme | Theme | Controlled theme. When set, ThemeProvider does not own the value. |
| defaultTheme | Theme | Initial theme for uncontrolled usage. Defaults to defaultLightTheme. |
| onThemeChange | (nextTheme: Theme) => void | Called whenever the theme changes via setTheme. |
| children | React.ReactNode | The subtree that should read this theme. |
useTheme
function useTheme(): ThemeContextValue;
const { theme, setTheme } = useTheme();Returns the current ThemeContextValue — the active theme and a setTheme. It uses a strict
context and throws outside a ThemeProvider.
useThemeValue
function useThemeValue<T>(selector: (theme: Theme) => T): T;
const accent = useThemeValue((theme) => theme.colors.accent);Selects and memoizes a derived value from the theme, recomputing only when the theme or selector changes.
createTheme
function createTheme(partialTheme?: PartialTheme): Theme;
const brand = createTheme({ colors: { accent: Color3.fromRGB(120, 80, 255) } });Builds a complete Theme by deep-merging a PartialTheme over defaultLightTheme, group by group.
Omitted fields fall back to the light defaults.
defaultLightTheme / defaultDarkTheme
const defaultLightTheme: Theme;const defaultDarkTheme: Theme;The two built-in themes. They share the same space, radius and typography scales and differ
only in colors.
createRecipe
A variant-driven styling helper. Given base styles, named variants, default selections and compound variants, it returns a resolver. That resolver turns a variant selection plus a theme into one merged props object.
function createRecipe<Props, Variants>( config: RecipeConfig<Props, Variants>,): (selection: RecipeSelection<Variants> | undefined, theme: Theme) => Partial<Props>;
const button = createRecipe({ base: (theme) => ({ BackgroundColor3: theme.colors.surface }), variants: { tone: { accent: (theme) => ({ BackgroundColor3: theme.colors.accent }), danger: (theme) => ({ BackgroundColor3: theme.colors.danger }), }, }, defaultVariants: { tone: "accent" },});
const props = button({ tone: "danger" }, theme);Resolution order is base, then each selected variant’s sx, then any matching compoundVariants,
each merged with mergeGuiProps — so later layers win and handler tables compose.
RecipeConfig fields:
| Prop | Type | Description |
|---|---|---|
| base | Sx<Props> | Styles applied before any variant. |
| variants | Record<string, Record<string, Sx<Props>>> | Named variant groups mapping each value to an sx. |
| defaultVariants | RecipeSelection<Variants> | Selection used when the caller omits a variant. |
| compoundVariants | Array<{ variants; sx }> | Extra sx applied when a combination of variant values matches. |
mergeSx
function mergeSx<Props>(...sxList: Array<Sx<Props>>): Sx<Props>;
const combined = mergeSx(baseSx, (theme) => ({ TextColor3: theme.colors.textPrimary }), overrideSx);Combines several sx values into one theme-resolving function, folding them with mergeGuiProps left
to right so later entries win.
resolveSx
function resolveSx<Props>(sx: Sx<Props>, theme: Theme): Partial<Props>;Resolves a single sx value: returned directly if it is a props object, called if it is a function,
empty if undefined.
mergeGuiProps
function mergeGuiProps<Props>( base?: Partial<Props>, variant?: Partial<Props>, user?: Partial<Props>,): Partial<Props>;The low-level merge used throughout. It shallow-merges up to three prop objects, base, variant
and user, with the last winning. It composes the Event and Change handler tables rather
than overwriting them, so two layers binding the same event both run.
react-lua passes Roblox event connections through Event and Change, so plain shallow-spreading would drop a base layer’s handlers when a later layer binds the same signal. This helper chains them.
Types
| Prop | Type | Description |
|---|---|---|
| Sx<Props> | Partial<Props> | ((theme: Theme) => Partial<Props>) | undefined | A static or theme-derived set of host props. |
| Theme | object | The full token set: colors, space, radius, typography. |
| PartialTheme | object | A deep-partial Theme accepted by createTheme. |
| ThemeColors | object | Color tokens: background, surface, surfaceElevated, border, textPrimary, textSecondary, accent, accentContrast, danger, dangerContrast, overlay. |
| ThemeSpace | object | Spacing scale keyed by pixel step (0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 32). |
| ThemeRadius | object | Corner radius tokens: none, sm, md, lg, xl, full. |
| ThemeTypography | object | Typography styles: labelSm, bodyMd, titleMd. |
| ThemeTypographyStyle | { font: Enum.Font; textSize: number } | A single typography entry. |
| ThemeContextValue | { theme: Theme; setTheme: (next: Theme) => void } | The value returned by useTheme. |
| ThemeProviderProps | object | Props for ThemeProvider. |
| RecipeConfig | object | Configuration object passed to createRecipe. |
| RecipeVariants | Record<string, Record<string, Sx<Props>>> | The shape of a recipe's variant groups. |
| RecipeSelection | Partial<Record<keyof Variants, string>> | A chosen value per variant group. |