Latticereference

Style

Theme tokens, recipe and host-prop helpers, and the Box/Text primitives that every styled Lattice UI surface builds on.

@lattice-ui/react-styleStable directiondepends on runtime

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

PropTypeDescription
sxSx<StyleProps>Host props, or a function of the theme returning host props, applied to the frame.
asChildbooleanMerge resolved props onto the single child element instead of rendering a frame.
childrenReact.ReactNodeFrame children, or the single element to clone when asChild is set.
...restGuiObject propsAny 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 })}
/>
PropTypeDescription
sxSx<StyleProps>Host props, or a function of the theme returning host props, applied to the textlabel.
asChildbooleanMerge resolved props onto the single child element instead of rendering a textlabel.
childrenReact.ReactNodeChildren, or the single element to clone when asChild is set.
...restGuiObject propsAny 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.

PropTypeDescription
themeThemeControlled theme. When set, ThemeProvider does not own the value.
defaultThemeThemeInitial theme for uncontrolled usage. Defaults to defaultLightTheme.
onThemeChange(nextTheme: Theme) => voidCalled whenever the theme changes via setTheme.
childrenReact.ReactNodeThe 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:

PropTypeDescription
baseSx<Props>Styles applied before any variant.
variantsRecord<string, Record<string, Sx<Props>>>Named variant groups mapping each value to an sx.
defaultVariantsRecipeSelection<Variants>Selection used when the caller omits a variant.
compoundVariantsArray<{ 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.

Types

PropTypeDescription
Sx<Props>Partial<Props> | ((theme: Theme) => Partial<Props>) | undefinedA static or theme-derived set of host props.
ThemeobjectThe full token set: colors, space, radius, typography.
PartialThemeobjectA deep-partial Theme accepted by createTheme.
ThemeColorsobjectColor tokens: background, surface, surfaceElevated, border, textPrimary, textSecondary, accent, accentContrast, danger, dangerContrast, overlay.
ThemeSpaceobjectSpacing scale keyed by pixel step (0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 32).
ThemeRadiusobjectCorner radius tokens: none, sm, md, lg, xl, full.
ThemeTypographyobjectTypography 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.
ThemeProviderPropsobjectProps for ThemeProvider.
RecipeConfigobjectConfiguration object passed to createRecipe.
RecipeVariantsRecord<string, Record<string, Sx<Props>>>The shape of a recipe's variant groups.
RecipeSelectionPartial<Record<keyof Variants, string>>A chosen value per variant group.