UI componentsdocs/ui/components/layout.md

Layout

Arrange things on the page: stacks, grids, containers and page chrome.

← Component reference

Stack #

Flex stack; gap maps to --a-stack-gap.

First

Second

Third

<Stack gap="0.5rem">
    <Text>First</Text>
    <Text>Second</Text>
    <Text>Third</Text>
</Stack>
PropTypeRequiredDefaultDescription
childrencontentItems to stack.
direction"column" | "row""column"Stack vertically (column) or horizontally (row).
gapstringSpace between children (any CSS length).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Group #

Horizontal flex group (Mantine Group / Bootstrap btn-group row).

Interact with the example: callbacks show up here.
<Group gap="0.5rem">
    <Button variant="ghost" onClick={() => {}}>
        Cancel
    </Button>
    <Button onClick={() => {}}>Save</Button>
</Group>
PropTypeRequiredDefaultDescription
align"start" | "center" | "end" | "stretch"Vertical alignment of the items.
childrencontentItems in a row.
gapstringSpace between items (any CSS length).
growbooleanItems share the row's width equally.
justify"start" | "center" | "end" | "between" | "around"Horizontal distribution of the items.
wrapbooleanLet items wrap onto new lines.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Flex #

Flexbox layout primitive (direction, gap, align, justify, wrap).

Invoices

Interact with the example: callbacks show up here.
<Flex justify="space-between" align="center">
    <Text>Invoices</Text>
    <Button size="sm" onClick={() => {}}>
        Export
    </Button>
</Flex>
PropTypeRequiredDefaultDescription
alignstringCSS align-items.
childrencontentFlex items.
direction"column" | "row"Main axis.
gapstringSpace between items (any CSS length).
justifystringCSS justify-content.
wrapbooleanLet items wrap.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Grid #

CSS grid layout (Bulma-style 2D grid).

1
2
3
4
5
6
<Grid cols={3} gap="0.75rem">
    <Box>1</Box>
    <Box>2</Box>
    <Box>3</Box>
    <Box>4</Box>
    <Box>5</Box>
    <Box>6</Box>
</Grid>
PropTypeRequiredDefaultDescription
childrencontentGrid cells (plain elements or GridItems).
colsnumber12Column count (CSS grid). Default 12.
gapstringSpace between cells (any CSS length).
minstringMin track width for auto-fit dense grids, e.g. 12rem. Overrides cols when set.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Parts

GridItem #

Item of a CSS grid, placed with span / start.

Spans two columns
One
<Grid cols={3} gap="0.75rem">
    <GridItem span={2}>
        <Box>Spans two columns</Box>
    </GridItem>
    <Box>One</Box>
</Grid>
PropTypeRequiredDefaultDescription
childrencontentCell content.
spannumberNumber of columns the cell covers.
startnumberColumn the cell starts at (1-based).

Columns #

Flexbox columns container (Bulma columns).

Auto
Half
Auto
<Columns gap="1rem">
    <Column>
        <Box>Auto</Box>
    </Column>
    <Column size={6}>
        <Box>Half</Box>
    </Column>
    <Column>
        <Box>Auto</Box>
    </Column>
</Columns>
PropTypeRequiredDefaultDescription
centeredbooleanCentre the columns horizontally.
childrencontentColumns.
gapstringSpace between columns (any CSS length).
mobilebooleanKeep columns side-by-side on mobile (default stacks under 768px).
multilinebooleanWrap columns onto new rows when they don't fit.
vcenteredbooleanCentre the columns vertically.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Parts

Column #

Single column (Bulma column).

One third
Rest
<Columns>
    <Column size={4}>
        <Box>One third</Box>
    </Column>
    <Column>
        <Box>Rest</Box>
    </Column>
</Columns>
PropTypeRequiredDefaultDescription
childrencontentColumn content.
narrowbooleanOnly as wide as its content.
offsetColumnSizeEmpty space before the column, in the same units as size.
sizeColumnSizeWidth in twelfths (1–12) or a named fraction (half, one-third, …); auto when unset.

ColumnSize

type ColumnSize = | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | "full" | "four-fifths" | "three-quarters" | "two-thirds" | "three-fifths" | "half" | "two-fifths" | "one-third" | "one-quarter" | "one-fifth" | "narrow" | "auto";

Container #

Horizontally centred content container with a max width.

Content constrained to the small container width.
<Container size="sm">
    <Box>Content constrained to the small container width.</Box>
</Container>
PropTypeRequiredDefaultDescription
childrencontentCentred, width-constrained content.
size"full" | "sm" | "md" | "lg"Maximum width.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Section #

Vertical page section with rhythm spacing.

Pricing

Sections add vertical rhythm between page regions.

<Section size="sm">
    <Title order={3}>Pricing</Title>
    <Text>Sections add vertical rhythm between page regions.</Text>
</Section>
PropTypeRequiredDefaultDescription
childrencontentSection content.
size"sm" | "md" | "lg"Vertical padding.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Level #

Horizontal level bar (Bulma level).

128 deploys
Interact with the example: callbacks show up here.
<Level>
    <LevelLeft>
        <LevelItem>
            <strong>128 deploys</strong>
        </LevelItem>
    </LevelLeft>
    <LevelRight>
        <LevelItem>
            <Button size="sm" onClick={() => {}}>
                New deploy
            </Button>
        </LevelItem>
    </LevelRight>
</Level>
PropTypeRequiredDefaultDescription
childrencontentLevelLeft, LevelRight and/or LevelItems.
mobilebooleanKeep the row horizontal on small screens (it stacks by default).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Parts

LevelLeft #

Left-aligned group of a level bar.

Left side
<Level>
    <LevelLeft>
        <LevelItem>Left side</LevelItem>
    </LevelLeft>
</Level>
PropTypeRequiredDefaultDescription
childrencontentItems on this side of the row.

LevelRight #

Right-aligned group of a level bar.

Right side
<Level>
    <LevelRight>
        <LevelItem>Right side</LevelItem>
    </LevelRight>
</Level>
PropTypeRequiredDefaultDescription
childrencontentItems on this side of the row.

LevelItem #

One centred item of a level bar.

Deploys

128
<Level>
    <LevelItem>
        <Text>Deploys</Text>
    </LevelItem>
    <LevelItem>
        <strong>128</strong>
    </LevelItem>
</Level>
PropTypeRequiredDefaultDescription
childrencontentItems on this side of the row.

Media #

Media object (Bulma / UIkit comment / Bootstrap media).

AL
Ada Lovelace

opened #421

2h

<Media>
    <MediaLeft>
        <Avatar name="Ada Lovelace" />
    </MediaLeft>
    <MediaContent>
        <strong>Ada Lovelace</strong> <Text muted>opened #421</Text>
    </MediaContent>
    <MediaRight>
        <Text muted>2h</Text>
    </MediaRight>
</Media>
PropTypeRequiredDefaultDescription
childrencontentMediaLeft, MediaContent and MediaRight.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Parts

MediaLeft #

Leading figure of a media object (avatar, thumbnail).

GH
Left slot holds the avatar or thumbnail.
<Media>
    <MediaLeft>
        <Avatar name="Grace Hopper" />
    </MediaLeft>
    <MediaContent>Left slot holds the avatar or thumbnail.</MediaContent>
</Media>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

MediaContent #

Main content of a media object.

GH
The content column grows to fill the row.
<Media>
    <MediaLeft>
        <Avatar name="Grace Hopper" />
    </MediaLeft>
    <MediaContent>The content column grows to fill the row.</MediaContent>
</Media>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

MediaRight #

Trailing content of a media object (actions).

Row content
Interact with the example: callbacks show up here.
<Media>
    <MediaContent>Row content</MediaContent>
    <MediaRight>
        <Button size="sm" variant="ghost" onClick={() => {}}>
            Reply
        </Button>
    </MediaRight>
</Media>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

Center & space #

Centre content or add fixed space.

Center #

Centres its content horizontally and vertically.

Centered on both axes

<Center style={{ "min-height": "6rem" }}>
    <Text muted>Centered on both axes</Text>
</Center>
PropTypeRequiredDefaultDescription
childrencontentContent to centre horizontally and vertically.
inlinebooleanShrink to the content (inline-flex) instead of filling the width.

Space #

Fixed spacer; h / w accept numbers (px) or CSS lengths.

Above

Below, 1.5rem later

<div>
    <Text>Above</Text>
    <Space h="1.5rem" />
    <Text>Below, 1.5rem later</Text>
</div>
PropTypeRequiredDefaultDescription
hstring | numberVertical space (pixels or any CSS length).
wstring | numberHorizontal space (pixels or any CSS length).

Divider #

Horizontal rule.

Account


Billing

<Stack gap="0.75rem">
    <Text>Account</Text>
    <Divider />
    <Text>Billing</Text>
</Stack>

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

AspectRatio #

Fixed-ratio box (--a-aspect).

16 by 9 placeholder
<AspectRatio style={{ "max-width": "20rem" }} ratio={16 / 9}>
    <img src="/images/16-9.jpg" alt="16 by 9 placeholder" />
</AspectRatio>

Slots: root inner

PropTypeRequiredDefaultDescription
childrencontentContent that fills the box (images and video are cropped to fit).
rationumber16 / 9Width divided by height, e.g. 16 / 9.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

ScrollArea #

Scrollable region with a max height.

Deploy #128

Deploy #127

Deploy #126

Deploy #125

Deploy #124

Deploy #123

<ScrollArea maxHeight="6rem">
    <Stack gap="0.35rem">
        <Text>Deploy #128</Text>
        <Text>Deploy #127</Text>
        <Text>Deploy #126</Text>
        <Text>Deploy #125</Text>
        <Text>Deploy #124</Text>
        <Text>Deploy #123</Text>
    </Stack>
</ScrollArea>
PropTypeRequiredDefaultDescription
childrencontentScrollable content.
maxHeightstringHeight after which the content scrolls (any CSS length).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Splitter #

Two resizable panes (pointer + ← → Home End).

Files

Editor

<Splitter
    style={{ height: "8rem" }}
    label="Resize panes"
    initial={40}
    left={<Text>Files</Text>}
    right={<Text>Editor</Text>}
/>

Slots: root handle pane

PropTypeRequiredDefaultDescription
initialnumber0.4Left pane ratio 0–1, default 0.4 (read once, at mount).
labelstring"Resize panes"Accessible name for the handle (default "Resize panes").
leftcontentFirst (left) pane content.
maxnumber0.8Largest size of the first pane, in percent.
minnumber0.2Smallest size of the first pane, in percent.
rightcontentSecond (right) pane content.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Masonry #

Masonry layout of variable-height items in columns.

Release 2.4

Dark theme, a component reference and interactive examples.

Standup

Ship the tree search.

Design review

Tighten the button group borders, fix the burger icon, make quote backgrounds optional and give the tree folder icons.

Bug

PIN input needed two Backspaces.

Idea

Masonry packs cards of different heights into columns without gaps.

Reading

WAI-ARIA tree pattern.

function Example() {
    const notes = [
        { title: "Release 2.4", body: "Dark theme, a component reference and interactive examples." },
        { title: "Standup", body: "Ship the tree search." },
        {
            title: "Design review",
            body: "Tighten the button group borders, fix the burger icon, make quote backgrounds optional and give the tree folder icons.",
        },
        { title: "Bug", body: "PIN input needed two Backspaces." },
        { title: "Idea", body: "Masonry packs cards of different heights into columns without gaps." },
        { title: "Reading", body: "WAI-ARIA tree pattern." },
    ];
    return (
        <Masonry columns={3}>
            <For each={notes}>
                {(note) => (
                    <Paper withBorder padding="md">
                        <strong>{note.title}</strong>
                        <Text muted>{note.body}</Text>
                    </Paper>
                )}
            </For>
        </Masonry>
    );
}
PropTypeRequiredDefaultDescription
childrencontentItems to pack; each keeps its own height.
columns2 | 3 | 43Number of columns.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Reel #

Horizontal scroll reel.

Slide 1Slide 2Slide 3Slide 4
<Reel>
    <img src="/images/1.jpg" alt="Slide 1" width="240" height="160" />
    <img src="/images/2.jpg" alt="Slide 2" width="240" height="160" />
    <img src="/images/3.jpg" alt="Slide 3" width="240" height="160" />
    <img src="/images/4.jpg" alt="Slide 4" width="240" height="160" />
</Reel>
PropTypeRequiredDefaultDescription
childrencontentItems in a horizontally scrolling row.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Bleed #

Negative horizontal margin to break out of padding.

Full-width strip that ignores its container's padding.

<Bleed x="1rem">
    <Text>Full-width strip that ignores its container's padding.</Text>
</Bleed>
PropTypeRequiredDefaultDescription
childrencontentContent to extend.
xstring"-1rem"How far to extend past the container on each side (any CSS length).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

AppShell #

Application chrome: optional sidebar + header around main content.

Acme Console

Deploys page

const sections = [
    { id: "overview", label: "Overview" },
    { id: "deploys", label: "Deploys" },
    { id: "settings", label: "Settings" },
];

function Example() {
    const page = signal("deploys");
    return (
        <AppShell
            contentAs="div"
            header={<strong>Acme Console</strong>}
            sidebar={<SidebarNav label="Main" items={sections} value={page()} onChange={page.set} />}
        >
            <Text>{sections.find((s) => s.id === page())?.label} page</Text>
        </AppShell>
    );
}

Slots: root content header main sidebar

PropTypeRequiredDefaultDescription
childrencontentMain content.
contentAs"main" | "div"Element for the content area (default "main"). Use "div" when the shell is nested in a page that already has a <main> landmark.
headercontentContent of the top bar.
sidebarcontentContent of the side column, e.g. a SidebarNav.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Page title block with breadcrumb, description and actions.

Deploys

Every push to a branch creates a deploy.

Interact with the example: callbacks show up here.
<PageHeader
    title="Deploys"
    description="Every push to a branch creates a deploy."
    actions={<Button onClick={() => {}}>New deploy</Button>}
/>

Slots: root actions crumb description row text title

PropTypeRequiredDefaultDescription
titlestringyesPage title (rendered as <h1>).
actionscontentButtons aligned to the end of the header.
breadcrumbcontentBreadcrumb above the title.
descriptioncontentText under the title.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Hero #

Full-bleed page hero (Bulma / Bootstrap jumbotron).

Ship faster with Arachne

Signals, SSR and 330+ accessible components.

<Hero tone="accent" size="sm">
    <HeroBody>
        <Title order={2} size={3}>
            Ship faster with Arachne
        </Title>
        <Text>Signals, SSR and 330+ accessible components.</Text>
    </HeroBody>
</Hero>
PropTypeRequiredDefaultDescription
childrencontentHeroHead, HeroBody and HeroFoot, or plain content.
sizeHeroSizeHeight: sm / md / lg, half screen or full screen.
toneHeroToneBackground colour scheme.

HeroSize

type HeroSize = "sm" | "md" | "lg" | "half" | "full";

HeroTone

type HeroTone = "default" | "accent" | "dark" | "light";

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Parts

HeroHead #

Top area of a hero (e.g. navigation).

Arachne
Hero head sits at the top, for a navbar or brand.
<Hero tone="dark" size="sm">
    <HeroHead>
        <strong>Arachne</strong>
    </HeroHead>
    <HeroBody>Hero head sits at the top, for a navbar or brand.</HeroBody>
</Hero>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

HeroBody #

Main content area of a hero.

The body grows to fill the hero and centres its content.
<Hero size="sm">
    <HeroBody>The body grows to fill the hero and centres its content.</HeroBody>
</Hero>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

HeroFoot #

Bottom area of a hero (e.g. tabs).

Hero with a footer row.

Trusted by 2,000 teams

<Hero tone="light" size="sm">
    <HeroBody>Hero with a footer row.</HeroBody>
    <HeroFoot>
        <Text muted>Trusted by 2,000 teams</Text>
    </HeroFoot>
</Hero>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

Page and site footers.

Page footer region.

© 2026 Acme Inc. · Privacy · Terms

<Footer>
    <Text muted>© 2026 Acme Inc. · Privacy · Terms</Text>
</Footer>
PropTypeRequiredDefaultDescription
childrencontentFooter content.

Marketing footer.

<SiteFooter
    brand="Arachne"
    columns={[
        {
            title: "Product",
            links: [
                { label: "Pricing", href: "#pricing" },
                { label: "Changelog", href: "#changelog" },
            ],
        },
        {
            title: "Company",
            links: [
                { label: "About", href: "#about" },
                { label: "Careers", href: "#careers" },
            ],
        },
    ]}
    meta="© 2026 Arachne"
/>

Slots: root brand column columnTitle grid link meta

PropTypeRequiredDefaultDescription
brandcontentLogo or name.
columns{ title: string; links: SiteFooterLink[]; }[]Link columns: each a title and its links.
metacontentBottom line, e.g. copyright.

SiteFooterLink

FieldTypeRequiredDescription
labelstringyesLink text.
hrefstringRenders a real link when set.
onClick() => voidClick handler (renders a button when there is no href).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Sticky & affix #

Keep content in view while scrolling.

Sticky #

Sticks its content to an edge of the scroll container.

Sticks 8px from the top while its container scrolls.
<Sticky offset={8}>
    <Paper withBorder>Sticks 8px from the top while its container scrolls.</Paper>
</Sticky>
PropTypeRequiredDefaultDescription
childrencontentContent to keep in view.
offsetnumber0Distance in pixels from the edge it sticks to.
position"top" | "bottom""top"Edge to stick to while scrolling.

Bar pinned to the top/bottom of its scroll container.

You have unsaved changes.

Interact with the example: callbacks show up here.
<StickyBar position="bottom">
    <Text>You have unsaved changes.</Text>
    <Button size="sm" onClick={() => {}}>
        Save
    </Button>
</StickyBar>
PropTypeRequiredDefaultDescription
childrencontentBar content, e.g. a message and a Save button.
position"top" | "bottom""bottom"Edge the bar sticks to.

Affix #

Viewport-pinned container.

Interact with the example: callbacks show up here.
<Affix position="bottom-right" offset="1rem">
    <Button size="sm" onClick={() => {}}>
        Feedback
    </Button>
</Affix>
PropTypeRequiredDefaultDescription
childrencontentPinned content, e.g. a feedback button.
offsetstringDistance from the corner (any CSS length).
position"top-left" | "top-right" | "bottom-left" | "bottom-right""bottom-right"Viewport corner to pin to.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.