Slots

A slot is a named opening a component leaves for content it does not own. The consumer, or the AI, fills it. This page is the contract for doing that reliably.

Some components are surfaces, not content. A Card is a frame with a radius, a padding, and an elevation. A Sheet is a rounded panel with a grabber and a home indicator. Neither ships a title or a body of its own. What they ship is a slot: a named opening where content goes. In Figma these are drawn as a solid violet box named [Slot:Name]; on this site they render as that same violet placeholder, so the docs never pretend a component owns content it does not.

Why slots exist

Slots keep the system composable. The component owns what should stay consistent (the surface, the spacing, the elevation, the tokens); the slot owns what changes every time (the content). That split is why one Card can hold a balance, a chart, a list, or a form without becoming four components. It is the naming rule in practice: a slot is a difference in content, not in anatomy or behavior, so it is a property of one component, never a new one.

How to find a slot (from data, not prose)

Every slot is a real entry in the component’s registry record. In components/registry.json, scan a component’s properties: any entry with type: "slot" is a fill target, and its name is the slot key you fill. Both Card and Sheet expose exactly one:

// layout/card  and  surface/sheet
properties: [
  { "name": "CardBody", "type": "slot", "default": "Slot" }
]
// → the slot key is "CardBody"

A component with no type: "slot"property has no slot: its content is fixed and you do not inject into it. The slot also appears in the component’s Anatomy as a part named Slot, so it is both queryable (data) and visible (diagram).

The fill contract

A filled component is a tree. Each node names a component by its registry id, sets any of its documented props, and (if it has one) fills its slot with an ordered array of children. A child is either another component node or a raw text node.

type Node = {
  component: string          // the registry id verbatim, e.g. "layout/card"
  props?: Record<string, unknown>   // only props on that component. Use the EXACT
                                     // strings from its properties[].values
                                     // (e.g. Badge intent is "success", not "Success").
  slots?: {                  // one key per slot property (type:"slot")
    [slotName: string]: Child[]     // ORDERED list; renders top to bottom
  }
}
// A child is a component node OR a text node. A text node inherits
// color.content.primary + the body.md type role by default; set color / typeRole
// only to override.
type Child = Node | { text: string; color?: string; typeRole?: string }

Rules: slot children are an ordered list rendered top to bottom. You may only use component ids and prop names that exist in the registry. You may nest another slot-bearing component inside a slot, but do not nest a surface in its own kind (no Card directly inside a Card’s slot, no Sheet inside a Sheet). When a component has a built-in region driven by its own props (for example Sheet’s hasHeader with its title and badge), put that chrome in props; the slot is for the body content below it.

A worked example

Brief: a compact account-summary Card with a status badge, a balance, and a primary action. Filled with real registry components (feedback/badge, layout/separator, actions/button) plus a text node for the balance:

{
  "component": "layout/card",
  "props": { "variant": "primary" },
  "slots": {
    "CardBody": [
      { "component": "feedback/badge",
        "props": { "variant": "primary", "intent": "success", "labelText": "Active" } },
      { "text": "$12,480.55" },
      { "component": "layout/separator", "props": { "orientation": "horizontal" } },
      { "component": "actions/button",
        "props": { "variant": "primary", "size": "md", "labelText": "Transfer" } }
    ]
  }
}

Every component and prop above is a real registry entry. When a brief needs an element the library does not have yet (here, a dedicated balance/amount component), use a text node and flag it, rather than inventing a component id.

What a slot inherits

The surface applies its own tokens to the region: the Card’s padding wraps the whole slot, its radius clips it, and its elevation sits under it. Spacing betweenslot children is the surface’s content gap where one is defined. What the surface does not set is the content itself: each child carries its own tokens. A component child brings its own fill and type; a text child inherits color.content.primary and the body.md type role by default, and may set color or typeRole to override. In short: the surface owns the frame and the outer spacing; each child owns its own styling.

How the AI fills a slot

This is where slots pay off for the atlas pipeline. When the AI builds a screen it does not redraw a Card. It reads the Card’s slot from the registry, generates a child array that fits the brief using only real component ids and props, and emits the tree above. The component supplies the frame and the tokens; the AI supplies the content. Because the surface is fixed and only the slot varies, the AI can never break the Card’s radius or elevation, only choose what goes inside. That is why we document the contract instead of a fixed example: an example teaches one layout, the contract teaches how to build any of them.

Slots in these docs

Any component that owns a slot renders it as the violet placeholder rather than stand-in content, and lists it as a part in its Anatomy. Today that is Card and Sheet. When you see the violet box, read it as “your content goes here,” and check the component’s registry record for the slot’s name.