UI componentsdocs/ui/components/surfaces.md

Surfaces

Containers with a visual boundary that group related content.

← Component reference

Card #

Content card (Bulma/Mantine/Bootstrap).

Project cover
marketing-site

Deployed 4 minutes ago from main.

Interact with the example: callbacks show up here.
const cardImage = "/images/cover.jpg";

<Card style={{ "max-width": "22rem" }}>
    <CardImage>
        <img src={cardImage} alt="Project cover" width="640" height="280" />
    </CardImage>
    <CardContent>
        <strong>marketing-site</strong>
        <Text muted>Deployed 4 minutes ago from main.</Text>
    </CardContent>
    <CardFooter>
        <CardFooterItem onClick={() => {}}>Visit</CardFooterItem>
        <CardFooterItem onClick={() => {}}>Logs</CardFooterItem>
    </CardFooter>
</Card>
PropTypeRequiredDefaultDescription
childrencontentCardHeader, CardImage, CardContent, CardFooter, or any content.

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

Parts

CardHeader #

Card header row.

Billing

Pro plan · renews Oct 1
<Card>
    <CardHeader>
        <CardHeaderTitle>Billing</CardHeaderTitle>
    </CardHeader>
    <CardContent>Pro plan · renews Oct 1</CardContent>
</Card>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

CardHeaderTitle #

Title text inside a card header.

Billing

<Card>
    <CardHeader>
        <CardHeaderTitle>Billing</CardHeaderTitle>
    </CardHeader>
</Card>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

CardImage #

Full-bleed media at the top of a card.

Project cover
const cardImage = "/images/cover.jpg";

<Card style={{ "max-width": "22rem" }}>
    <CardImage>
        <img src={cardImage} alt="Project cover" width="640" height="280" />
    </CardImage>
</Card>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

CardContent #

Padded body of a card.

Card content is padded and flows like body text.
<Card>
    <CardContent>Card content is padded and flows like body text.</CardContent>
</Card>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

CardFooter #

Card footer row of actions.

Delete this project?
Interact with the example: callbacks show up here.
<Card>
    <CardContent>Delete this project?</CardContent>
    <CardFooter>
        <CardFooterItem onClick={() => {}}>Cancel</CardFooterItem>
        <CardFooterItem onClick={() => {}}>Delete</CardFooterItem>
    </CardFooter>
</Card>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

CardFooterItem #

One action cell in a card footer (a button when onClick is set).

Interact with the example: callbacks show up here.
<Card>
    <CardFooter>
        <CardFooterItem onClick={() => {}}>
            Save
        </CardFooterItem>
        <CardFooterItem onClick={() => {}}>Cancel</CardFooterItem>
    </CardFooter>
</Card>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.
onClick(e: MouseEvent) => voidMakes the item a button; called when pressed.

Box #

Bordered surface for grouping content.

Boxes group related content on a raised surface.
<Box>Boxes group related content on a raised surface.</Box>
PropTypeRequiredDefaultDescription
childrencontentBox content.

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

Panel #

Side panel / filter panel (Bulma).

function Example() {
    const repos = [
        { name: "arachne", visibility: "public" },
        { name: "marketing-site", visibility: "private" },
        { name: "design-tokens", visibility: "public" },
    ];
    const tab = signal("all");
    const active = signal("arachne");
    const shown = () => repos.filter((r) => tab() === "all" || r.visibility === tab());
    return (
        <Panel label="Repositories" style={{ "max-width": "22rem" }}>
            <PanelHeading>Repositories</PanelHeading>
            <PanelTabs>
                <For each={["all", "public", "private"]}>
                    {(id) => (
                        <PanelTab active={tab() === id} onClick={() => tab.set(id)}>
                            {id[0]?.toUpperCase() + id.slice(1)}
                        </PanelTab>
                    )}
                </For>
            </PanelTabs>
            <For each={shown()}>
                {(repo) => (
                    <PanelBlock active={active() === repo.name} onClick={() => active.set(repo.name)}>
                        {repo.name}
                    </PanelBlock>
                )}
            </For>
        </Panel>
    );
}
PropTypeRequiredDefaultDescription
childrencontentPanelHeading, PanelTabs and PanelBlocks.
labelstringAccessible name of the panel's <nav> landmark; set it when a page has several panels.

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

Parts

PanelHeading #

Heading row of a panel.

<Panel label="Projects">
    <PanelHeading>Repositories</PanelHeading>
    <PanelBlock>arachne</PanelBlock>
</Panel>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

PanelTabs #

Tab row inside a panel.

function Example() {
    const tab = signal("all");
    return (
        <Panel label="Filter tabs">
            <PanelTabs>
                <PanelTab active={tab() === "all"} onClick={() => tab.set("all")}>
                    All
                </PanelTab>
                <PanelTab active={tab() === "forks"} onClick={() => tab.set("forks")}>
                    Forks
                </PanelTab>
            </PanelTabs>
        </Panel>
    );
}
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

PanelTab #

One tab in a panel's tab row.

function Example() {
    const tab = signal("all");
    return (
        <Panel label="Sources">
            <PanelTabs>
                <PanelTab active={tab() === "all"} onClick={() => tab.set("all")}>
                    All
                </PanelTab>
                <PanelTab active={tab() === "forks"} onClick={() => tab.set("forks")}>
                    Forks
                </PanelTab>
            </PanelTabs>
        </Panel>
    );
}
PropTypeRequiredDefaultDescription
activebooleanMarks the selected tab.
childrencontentTab label.
onClick(e: MouseEvent) => voidCalled when the tab is pressed.

PanelBlock #

Panel row; renders a <button> when onClick is set.

function Example() {
    const active = signal("marketing-site");
    return (
        <Panel label="Recent projects">
            <PanelBlock
                active={active() === "marketing-site"}
                onClick={() => active.set("marketing-site")}
            >
                marketing-site
            </PanelBlock>
            <PanelBlock active={active() === "docs"} onClick={() => active.set("docs")}>
                docs
            </PanelBlock>
        </Panel>
    );
}
PropTypeRequiredDefaultDescription
activebooleanMarks the selected row.
childrencontentRow content.
onClick(e: MouseEvent) => voidMakes the row a button; called when pressed.

Paper & tiles #

Plain surfaces and tile layouts.

Paper #

Surface paper (Mantine).

Paper is the plainest surface: padding, radius, optional border and shadow.
<Paper padding="lg" shadow withBorder>
    Paper is the plainest surface: padding, radius, optional border and shadow.
</Paper>
PropTypeRequiredDefaultDescription
childrencontentPaper content.
padding"sm" | "md" | "lg"Inner padding.
shadowbooleanAdds a soft shadow.
withBorderbooleanAdds a border.

Tile #

Nestable tile grid (Bulma).

Wide tile
Narrow
<Tile ancestor>
    <Tile parent size={8}>
        <Tile child>
            <Box>Wide tile</Box>
        </Tile>
    </Tile>
    <Tile parent>
        <Tile child>
            <Box>Narrow</Box>
        </Tile>
    </Tile>
</Tile>
PropTypeRequiredDefaultDescription
ancestorbooleanOutermost tile of a tile layout.
childbooleanA leaf tile holding content.
childrencontentNested tiles or content.
parentbooleanA tile that contains child tiles.
size1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12Width in twelfths.
verticalbooleanStack the inner tiles vertically.

Block #

Vertical spacing block (Bulma).

A block adds the standard bottom margin between siblings.
Like this second one.
<div>
    <Block>A block adds the standard bottom margin between siblings.</Block>
    <Block>Like this second one.</Block>
</div>
PropTypeRequiredDefaultDescription
childrencontentBlock content.

Inset #

Recessed panel (canvas background, border) for secondary content inside a surface.

Recessed area for secondary content, like a settings preview.

<Inset>
    <Text muted>Recessed area for secondary content, like a settings preview.</Text>
</Inset>
PropTypeRequiredDefaultDescription
childrencontentRecessed content.

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