Layout
Layout primitives
Box, Flex, Stack, Grid, Container and Section: spacing and alignment as token props, rendered as data attributes with no inline style.
<script>
import { Stack, Flex, Grid, Box, Container, Section } from "@codeatlasdev/ui";
</script>
<Container size="md">
<Section>
<Stack as="ul" gap="4">…</Stack>
<Grid columns={{ base: 1, md: 3 }} gap="4">…</Grid>
</Section>
</Container>
Every primitive renders any element (as) and forwards class and other attributes. Box, Flex, Stack and Grid are typed on the tag: as="form" takes the form’s own attributes and handlers (novalidate, action, onsubmit with currentTarget: HTMLFormElement), as="a" takes href, as="label" takes for, and an attribute of another tag is a type error. A token prop never becomes inline CSS: it is a data-* attribute (<div data-gap="4">) styled by one stylesheet in @layer ca.utilities, so the markup is cache-friendly, passes a strict CSP, and an app can override any step with a plain class.
| Primitive | Renders | Props |
|---|---|---|
Box |
div, padding |
p, px, py |
Flex |
display: flex |
gap, p, px, py, align, justify, direction, wrap |
Stack |
a Flex column | gap, p, px, py, align, justify |
Grid |
display: grid, equal tracks |
columns, gap, p, px, py, align |
Container |
centered, width-capped, fluid gutter | size (sm md lg xl), px, py |
Section |
a <section> with the page rhythm |
size (1 2 3), px, py |
Spacing values are steps of the spacing scale as strings ("4" is --ca-space-4, 16px at the default density; "0.5" is 2px). The scale is 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 6 7 8 10 11 12 16 20 24. A value off the scale logs a [ca-ui] warning in development and renders unstyled: for a one-off value add a class.
align is start | center | end | stretch | baseline, justify is start | center | end | between | around | evenly, direction is row | column | row-reverse | column-reverse, and columns is 1 to 12 equal tracks. For a template like 2fr 1fr or repeat(auto-fill, minmax(200px, 1fr)) write grid-template-columns in a class.
Responsive values
Pass an object instead of a value. base applies everywhere, and each breakpoint applies from its width up, on the shared scale: sm 600, md 900, lg 1200, xl 1536 px. The object becomes data-gap="2" data-md-gap="4".
Flex, Stack and Box
Reference #182 · Pick in the gallery
Code
Responsive Grid
Resize the window: 1, 2, then 4 columns, and the gap grows at `md`.
Reference #181 · Pick in the gallery
Code
Container and Section
Starter
Team
Reference #180 · Pick in the gallery
Code
Shared props
| Prop | Type | Default | Description |
|---|---|---|---|
as | keyof SvelteHTMLElements | per component | Element to render (`div`, `section`, `ul`, `form`...). Box, Flex, Stack and Grid also accept that element's own attributes and event types. |
gap | Space | responsive | undefined | Gap between children. |
p, px, py | Space | responsive | undefined | Padding on every side, horizontal, vertical. |
align | 'start' | 'center' | 'end' | 'stretch' | 'baseline' | responsive | undefined | `align-items`. |
justify | 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly' | responsive | undefined | `justify-content`. |
direction | 'row' | 'column' | 'row-reverse' | 'column-reverse' | responsive | 'row' | Flex only. |
wrap | boolean | responsive | false | Flex only: `flex-wrap: wrap`. |
columns | 1–12 | responsive | undefined | Grid only: equal tracks. |
Migrating from Stack, Grid and Row
Stack’s gap used to be pixels (gap={16}) and Grid took cols. They now take spacing steps (gap="4") and columns, and Row is deprecated in favor of Flex. Run the codemod, which converts every static usage and leaves the rest for you:
bunx @codeatlasdev/ui migrate --write src
| Before | After |
|---|---|
<Stack gap={16}> |
<Stack gap="4"> |
<Stack align="flex-end" justify="space-between"> |
<Stack align="end" justify="between"> |
<Row gap={8} wrap> |
<Flex gap="2" wrap> |
<Grid cols={3} gap={8}> |
<Grid columns={3} gap="2"> |
<Grid cols="2fr 1fr" gap={20}> |
<Grid cols="2fr 1fr" gap="5"> and a class with grid-template-columns: 2fr 1fr (cols is reported, the gap is still converted) |
What the codemod cannot map is left as it was and listed at the end with file, line, reason and recipe, for example src/T.svelte:5 Grid: cols is a template string (write grid-template-columns in a class) or src/T.svelte:6 Stack: dynamic gap (use a step string: px / 4, e.g. gap={16} is gap="4"). A gap={gap} left untouched is not harmless: it is now a step, so 16 means 64px. Add --strict to exit with code 1 when anything was left (CI, scripts).