Frame

Boxes a region once and divides it by rules.

Styles

  • wh.orders_daily

    96 partitions
    2026-10-19148,201 rows
    2026-10-18148,201 rows
    2026-10-17148,201 rows
    Updated 2m ago

    framedefault

  • wh.orders_daily

    96 partitions
    2026-10-19148,201 rows
    2026-10-18148,201 rows
    2026-10-17148,201 rows
    Updated 2m ago

    panel

Header

  • orders-daily

    Job failed02:14 UTC

    The source dropped a column. The next run starts at 03:00.

    2026-10-19148,201 rows
    2026-10-18148,201 rows
    2026-10-17148,201 rows

    state

Usage

When to use it
Use
A region of a page: a table, a log, a pipeline's stages, a group of settings.
Use
A raised summary standing on its own beside the main region (variant="panel").
Not for
Parts inside a region; divide them with rule-row and rule-col. Use Collapsible.
Not for
Something floating over the page. Use Popover.

Rules

7 rules
Never
Put a Frame or a panel inside a Frame. Because rules carry Sodium's layout, and a box in a box draws two edges where one closes the region.
Must
Put the thing's name in FrameTitle as written, then its state in FrameStatus and its meta in FrameValue meta; never a kind word before the name. Because a product header reads as the name and then how it is, and a KIND prefix in capitals reads like a spec sheet and says what the body already shows.
Should
Give each Frame an accessible name, with aria-label or aria-labelledby on its FrameTitle, when a view holds more than one. Because a section with no name is not a landmark, so a screen reader cannot tell the regions apart or jump between them.
Must
Hold at most one primary Button in a frame, header and footer together. Because a frame is one group of actions, and the slab marks the one thing to act on there.
Should
Use padding="none" for rows and tables, so they run to the frame's edge. Because a row's rule should meet the frame's edge, not stop 8px short of it.
Must
Start a body with its data, or one FrameDescription sentence, never a headline; divide a second part with a second FrameBody. Because the header already names the thing and its state, so a 24px headline under it repeats them in a marketing voice, and one rule between parts is what makes two regions read alike where a hand-set border drifts.
Should
Use 26px buttons in the header and footer: icon-sm ghost buttons named with aria-label in the header, size="sm" in the footer. Because the header is a 38px row (--head), where a 26px ghost leaves 6px clear and its glyph lands 12px from the edge, and the footer's 12px inset puts a 26px button's box as far from the rule as from the side.

Accessibility

3 checks
A section; give it an accessible name with aria-labelledby on the FrameTitle's id when the view has several.
The edge is --line, the same rule a field, a menu and a secondary button draw; it divides, it is not a cue.
The header's name is text-1 on the panel at 17.6:1 and 14.9:1; its meta text-2.

Edge cases

4 edge cases
Long title
Truncates before the actions move.
Phone, 390 wide
The frame fills the width; footer actions wrap under the meta.
Dark theme
Rules step up from the near-black page; the panel variant is a step lighter.
Forced colors
The edge draws in CanvasText.

API

7 props
Frame variant
'frame' | 'panel' default 'frame'
FrameBody padding
'default' | 'none' default 'default'
FrameDescription
p
FrameHeader, FrameTitle, FrameActions
header, h2, div
FrameStatus
span
FrameValue meta
boolean default false
FrameFooter, FrameMeta
footer, span

Tokens

13 tokens
--page
#ffffff
--panel
#fafaf7
--line
#e2dfd5
--line-faint
#efede6
--line-width
1px
--head
38px
--row
40px
--inset
12px
--type-code
400 13px/20px var(--font-mono)
--type-body-sm
400 14px/20px var(--font-sans)
--type-code-sm
400 12px/16px var(--font-mono)
--text-1
#161512
--text-2
#55524a

Install

For your coding agent

GitHub

Please star me pookie

198

A new design system every week

Leave your email before you copy the prompt.