@lattice-ui/system Stable direction
depends on core , style System sits one level above @lattice-ui/style. Where Style owns the raw theme and host-prop helpers, System adds the app-facing layer: a density system that scales spacing, radius, and type globally; layout primitives (Stack, Row, Grid) that drive Roblox layout instances from theme spacing tokens; and surface tones for consistent backgrounds and borders. SystemProvider ties the theme and density together and feeds a density-resolved Theme down through ThemeProvider.
Import
import { SystemProvider, useSystemTheme } from "@lattice-ui/system";import { DensityProvider, useDensity, applyDensity, density } from "@lattice-ui/system";import { Stack, Row, Grid } from "@lattice-ui/system";import { Surface, surface } from "@lattice-ui/system";API reference
SystemProvider
The top-level provider. It owns the raw (base) theme, wraps a DensityProvider, and exposes both through a system context. Density transforms are applied internally and the resolved theme is passed down via @lattice-ui/style’s ThemeProvider, so any useTheme consumer below sees the density-scaled theme automatically.
function SystemProvider(props: SystemProviderProps): React.Element;
<SystemProvider defaultTheme={defaultDarkTheme} defaultDensity="comfortable"> {app}</SystemProvider>Theme and density are each independently controllable (theme/defaultTheme, density/defaultDensity). Writes always target the base theme; the resolved theme is derived from base theme plus current density.
| Prop | Type | Description |
|---|---|---|
| theme | Theme | Controlled base theme. When set, SystemProvider does not own theme state. |
| defaultTheme | Theme | Initial base theme for uncontrolled usage. Defaults to defaultLightTheme. |
| onThemeChange | (nextTheme: Theme) => void | Called when the base theme changes. |
| density | DensityToken | Controlled density. "compact", "comfortable", or "spacious". |
| defaultDensity | DensityToken | Initial density for uncontrolled usage. Defaults to "comfortable". |
| onDensityChange | (next: DensityToken) => void | Called when density changes. |
| children | React.ReactNode | The subtree governed by this system. |
useSystemTheme
function useSystemTheme(): SystemThemeContextValue;
const { theme, baseTheme, density, setBaseTheme, setDensity } = useSystemTheme();Returns the system context: the density-resolved theme for reading, the baseTheme before density transforms, the current density, and setters. Writes go through setBaseTheme (raw theme) and setDensity. Must be called under a SystemProvider.
DensityProvider
The density layer used internally by SystemProvider, also usable on its own. It reads the base theme from the surrounding system base-theme context, resolves a density-scaled theme, and republishes it through ThemeProvider. Density is controllable via density/defaultDensity.
function DensityProvider(props: DensityProviderProps): React.Element;
<DensityProvider density="compact">{children}</DensityProvider>| Prop | Type | Description |
|---|---|---|
| density | DensityToken | Controlled density value. |
| defaultDensity | DensityToken | Initial density for uncontrolled usage. Defaults to "comfortable". |
| onDensityChange | (next: DensityToken) => void | Called when density changes. |
| children | React.ReactNode | The subtree receiving the density-resolved theme. |
useDensity
function useDensity(): DensityContextValue;
const { density, setDensity } = useDensity();Returns the current density token and a setDensity setter. Must be called under a DensityProvider (or SystemProvider).
applyDensity
function applyDensity(theme: Theme, token: DensityToken): Theme;
const compact = applyDensity(defaultLightTheme, "compact");Pure theme transformer. Returns a new Theme with space, radius, and typography scaled by the token’s factors and colors carried through unchanged. Spacing and radius are rounded and clamped to a non-negative minimum; text sizes clamp to a minimum of 10. Density does not create layout or child instances — it only reshapes token values.
density
function density(token: DensityToken): (theme: Theme) => Theme;
const compact = density("compact");const scaled = compact(theme);A curried form of applyDensity — returns a theme transformer bound to a token, convenient where a (theme) => Theme function is expected.
Stack
Vertical-by-default flex layout primitive. Renders a transparent frame with a uilistlayout, mapping align/justify to Roblox alignment enums and resolving gap and padding through theme spacing tokens.
function Stack(props: StackProps): React.Element;
<Stack gap={8} align="center" padding={16}> {children}</Stack>align controls the cross axis and justify the main axis (their effect swaps with direction). gap and every padding field accept a SpaceToken (resolved through the theme) or a raw pixel number. autoSize maps to AutomaticSize; sx and any direct host props are merged on top of the transparent base. asChild is not supported and throws if passed.
| Prop | Type | Description |
|---|---|---|
| direction | LayoutDirection | "vertical" (default) or "horizontal". |
| gap | SpaceValue | Spacing between children: a space token or pixel number. Defaults to 0. |
| align | StackAlign | Cross-axis alignment: "start", "center", or "end". Defaults to "start". |
| justify | StackJustify | Main-axis alignment: "start", "center", or "end". Defaults to "start". |
| autoSize | StackAutoSize | boolean | "x" | "y" | "xy". Maps to AutomaticSize; true picks the main axis. |
| padding... | SpaceValue | padding, paddingX/Y, and paddingTop/Right/Bottom/Left, resolved via theme spacing. |
| sx | Sx<StyleProps> | Extra host props, merged over the transparent base. |
| children | React.ReactNode | Laid-out children. |
Row
function Row(props: RowProps): React.Element;
<Row gap={6}>{children}</Row>A Stack with direction fixed to "horizontal". Accepts every StackProps field except direction (RowProps = Omit<StackProps, "direction">).
Grid
Responsive grid primitive backed by a uigridlayout. It measures its own AbsoluteSize and resolves the column count and cell width on layout changes — either honoring a fixed columns or fitting as many minColumnWidth cells as the container allows.
function Grid(props: GridProps): React.Element;
<Grid minColumnWidth={120} gap={8} cellHeight={48}> {children}</Grid>gap sets both axes; rowGap/columnGap override per axis. All spacing accepts tokens or pixels. cellHeight defaults to 32. Like Stack, asChild is not supported and throws.
| Prop | Type | Description |
|---|---|---|
| columns | number | Fixed column count. When omitted, columns are derived from minColumnWidth and container width. |
| minColumnWidth | SpaceValue | Minimum cell width used to compute responsive column count. |
| cellHeight | SpaceValue | Height of each cell. Defaults to 32. |
| gap | SpaceValue | Spacing for both axes. Defaults to 0. |
| rowGap | SpaceValue | Vertical spacing override. Defaults to gap. |
| columnGap | SpaceValue | Horizontal spacing override. Defaults to gap. |
| autoSize | StackAutoSize | Maps to AutomaticSize; true resolves to the Y axis. Defaults to false. |
| padding... | SpaceValue | Same padding fields as Stack, resolved via theme spacing. |
| sx | Sx<StyleProps> | Extra host props, merged over the transparent base. |
| children | React.ReactNode | Grid cells. |
Surface
Decorated surface host primitive. Renders a frame styled with the chosen tone and, for non-overlay tones, adds a uicorner and uistroke for rounded corners and a themed border.
function Surface(props: SurfaceProps): React.Element;
<Surface tone="elevated" Size={UDim2.fromOffset(240, 120)}> {children}</Surface>For decorated tones the host BorderSizePixel is forced to 0 since the border is drawn by uistroke; the overlay tone renders a translucent fill with no decoration. sx and direct host props merge on top. asChild is not supported and throws.
| Prop | Type | Description |
|---|---|---|
| tone | SurfaceToken | "surface" (default), "elevated", "sunken", or "overlay". |
| sx | Sx<StyleProps> | Extra host props, merged over the tone. |
| children | React.ReactNode | Surface contents. |
surface
function surface<Props>(token: SurfaceToken): Sx<Props>;
const elevatedSx = surface("elevated");Props-only counterpart to the Surface primitive. Returns an Sx value that resolves to background, border color, and border-size host props for the given tone — no child instances (no uicorner/uistroke). Use it when you only want the tone’s host props on an existing element. The overlay tone resolves to a translucent overlay fill with no border.
Types
| Prop | Type | Description |
|---|---|---|
| DensityToken | "compact" | "comfortable" | "spacious" | The three density levels. |
| DensityContextValue | { density; setDensity } | Value returned by useDensity. |
| DensityProviderProps | object | Props for DensityProvider. |
| SystemProviderProps | object | Props for SystemProvider. |
| SystemThemeContextValue | object | theme, baseTheme, density, setBaseTheme, setDensity from useSystemTheme. |
| LayoutDirection | "vertical" | "horizontal" | Stack fill direction. |
| StackAlign | "start" | "center" | "end" | Cross-axis alignment. |
| StackJustify | "start" | "center" | "end" | Main-axis alignment. |
| StackAutoSize | boolean | "x" | "y" | "xy" | AutomaticSize selector. |
| StackPadding | object | The padding/paddingX/paddingTop... field group. |
| SpaceToken | keyof Theme["space"] | A key of the theme spacing scale. |
| SpaceValue | SpaceToken | number | A space token or a raw pixel value. |
| StackProps | object | Props for Stack. |
| RowProps | Omit<StackProps, "direction"> | Props for Row. |
| GridProps | object | Props for Grid. |
| SurfaceToken | "surface" | "elevated" | "sunken" | "overlay" | The four surface tones. |
| SurfaceProps | object | Props for the Surface primitive. |
applyDensity and density only reshape token values — they never create or modify Roblox instance graphs. Spacing and radius are rounded and clamped to non-negative values, and text sizes never drop below 10 so labels stay legible at compact density. Layout itself is produced by Stack, Row, Grid, and Surface reading the resolved tokens.