Box
Generic container chrome for arbitrary content, with no content slots of its own.
Arbitrary content, no header, title, or footer slots.
Usage
import { Box } from '@epilot/spark-ui/box'<Box variant="surface">
<p>Arbitrary content, no header, title, or footer slots.</p>
<Button size="small">Action</Button>
</Box>Box is the same chrome recipe Card uses (variant, color, radius), with none of Card's header/icon/title/description/footer slots. Reach for it when the content wrapped is arbitrary, rather than a title+description shape. Use Box for arbitrary content that just needs chrome, Card for the header/icon/title/description/footer slots, Accordion when the content collapses, and PageContainer for page width and gap, which is not chrome at all.
The chrome token family is named --spark-card-* for historical reasons. Box reads the exact same tokens and the same app-level <Theme cardVariant cardColor> default as Card, so both stay in lockstep with a single knob.
Examples
Variants
Box takes a self-scoped variant (soft, surface, outline, ghost, solid) and color (neutral, accent), identical to Card's, since they share one chrome recipe.
color=neutral
soft
surface
outline
ghost
color=accent
soft
surface
outline
ghost
Padding
padding (none, sm, md, lg) controls inset on all sides. md (default) follows the variant's own inset: chromed variants pad, ghost sits flush. none strips padding entirely, useful for a flush, clipped frame around content that provides its own inset (an image, a chart).
none
sm
md
lg
Radius
none
md
2xl
full
Interactive
Pass render with an <a href/> or <button/> to make the whole box the click target: it gains a hover tint, focus ring, and disabled styles. Don't nest other interactive controls inside an interactive box.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'soft' | 'surface' | 'outline' | 'ghost' | 'solid' | knob/soft | Container chrome, scoped to the box (never cascades into child controls). Unset follows the app-level data-card-variant. |
color | 'neutral' | 'accent' | 'neutral' | Chrome palette. neutral tracks the app theme's neutral color; accent tints the chrome with the brand accent. |
radius | 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full' | '2xl' | Corner radius. Each step maps to the matching Tailwind rounded-* utility, so it still scales with the page-wide Roundness knob. |
padding | 'none' | 'sm' | 'md' | 'lg' | 'md' | Inset on all sides. md follows the variant's own default inset; none strips it entirely. |
render | ReactElement | — | Base UI polymorphism. Pass <a href/> or <button/> to make the box interactive (hover tint, focus ring, disabled styles). |
Set app-wide defaults with the Theme provider's cardVariant / cardColor props, the same knob Card reads; a per-box prop overrides it.
Accessibility
- An interactive
Box(render={<a>}orrender={<button>}) is one click target. Don't nest other interactive controls inside it. render={<button>}needs its owntype="button"(ortype="submit"where relevant);Boxdoesn't set one for you.- Chrome never cascades into child controls: a
Boxstyledvariant="outline"doesn't outline the buttons inside it.