# System

> Density scaling, layout primitives, and surface tones layered on top of the style foundation, wired together by a single SystemProvider.

Source: https://docs.astra-void.xyz/lattice-ui/reference/system/

`@lattice-ui/react-system` · Stable direction · depends on `runtime`, `style`

System sits one level above `@lattice-ui/react-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

```ts
import { SystemProvider, useSystemTheme } from "@lattice-ui/react-system";
import { DensityProvider, useDensity, applyDensity, density } from "@lattice-ui/react-system";
import { Stack, Row, Grid } from "@lattice-ui/react-system";
import { Surface, surface } from "@lattice-ui/react-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/react-style`'s `ThemeProvider`, so any `useTheme` consumer below sees the density-scaled theme automatically.

```tsx
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

```ts
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`.

```tsx
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

```ts
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

```ts
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

```ts
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.

```tsx
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

```tsx
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.

```tsx
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.

```tsx
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

```ts
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. |

> **Density is a theme transform, not a layout engine**
>
> `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.

## Related links

- [Style](https://docs.astra-void.xyz/lattice-ui/reference/style.md)
- [Runtime](https://docs.astra-void.xyz/lattice-ui/reference/runtime.md)
- [Roblox UI constraints](https://docs.astra-void.xyz/lattice-ui/guides/roblox-ui-constraints.md)
