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. Higher-level packages like @lattice-ui/react-system and the styled components build directly on it.

The core idea is the sx value — a partial set of host props, or a function from the current Theme to host props. Everything in this package either produces, resolves, or merges sx values, then hands the result to a host instance.

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 that renders a frame and applies an sx value resolved against the current theme. Supports asChild to merge 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 are merged with mergeGuiProps(rest, sx), so when both set the same property the sx value wins; Event/Change handler tables are composed instead of replaced. When asChild is set, Box requires exactly one valid child element and clones its props onto it (via Slot) instead of rendering a frame.

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 instead of a frame. Same sx, asChild, and host-prop merging behavior.

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. Supports controlled (theme) and uncontrolled (defaultTheme) usage, with onThemeChange notified on every change.

function ThemeProvider(props: ThemeProviderProps): React.Element;
<ThemeProvider defaultTheme={defaultDarkTheme} onThemeChange={handleChange}>
{app}
</ThemeProvider>

When theme is supplied the provider is controlled and setTheme only calls onThemeChange; otherwise it owns the theme internally. Defaults to defaultLightTheme when neither prop is given.

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 function. Must be called under a ThemeProvider (it uses a strict context and throws otherwise).

useThemeValue

function useThemeValue<T>(selector: (theme: Theme) => T): T;
const accent = useThemeValue((theme) => theme.colors.accent);

Selects and memoizes a derived value from the current theme, recomputing only when the theme or selector changes. Useful for reading a single token without re-running on unrelated context updates.

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 (colors, space, radius, typography). Omitted fields fall back to the light defaults.

defaultLightTheme / defaultDarkTheme

const defaultLightTheme: Theme;
const defaultDarkTheme: Theme;

The two built-in themes. Both share the same space, radius, and typography scales and differ only in colors. Use them directly or as the base for createTheme overrides.

createRecipe

A variant-driven styling helper. Given a config of base styles, named variants, default selections, and compound variants, it returns a resolver that turns a variant selection plus a theme into a single 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 (selection merged over defaultVariants), then any matching compoundVariants. Each layer is merged with mergeGuiProps, so later layers win and Event/Change handler tables are composed rather than replaced.

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 a single theme-resolving function. At resolve time each entry is resolved against the theme and folded together with mergeGuiProps, left to right, so later entries override earlier ones.

resolveSx

function resolveSx<Props>(sx: Sx<Props>, theme: Theme): Partial<Props>;

Resolves a single sx value against a theme: returns it directly if it is a props object, calls it if it is a function, and returns an empty object if it is undefined.

mergeGuiProps

function mergeGuiProps<Props>(
base?: Partial<Props>,
variant?: Partial<Props>,
user?: Partial<Props>,
): Partial<Props>;

The low-level merge used everywhere in this package. It shallow-merges up to three prop objects (base, then variant, then user, last wins) and, crucially, composes the Event and Change handler tables rather than overwriting them — when two layers bind the same event, both handlers run in order.

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.