Structure
The pieces that give a modal its skeleton: grouped settings, footers, and scroll areas. (The modal shell — title, backdrop, sizing, close — comes from ctx.ui.showModal and is not yours to build.)
Section
A labeled group — the uppercase NAME / FOLDERS / FORMATTING headers seen throughout Silo's modals and settings pages. Use it to label one field too, not only a group: a modal form is a stack of Sections, each wrapping a single Input / Select (plus its hint). This is the only sanctioned way to put a label above a field — never a bare <label> or an ad-hoc class.
import { Section, SettingRow, Switch } from "@silo-code/sdk";
<Section label="Formatting">
<SettingRow
label="Format on save"
hint="Run Format Document before writing to disk."
>
<Switch
checked={formatOnSave}
onChange={setFormatOnSave}
aria-label="Format on save"
/>
</SettingRow>
</Section>;| Prop | Type | Notes |
|---|---|---|
label | string | rendered uppercase, semibold, letter-spaced, chrome−1, text-lo |
accessory | ReactNode? | right-aligned — a count badge, an add affordance |
SettingRow
The label + hint + control row every settings surface uses: title and dim description on the left, the control pinned right. Rows in a group are separated by hairline dividers.
| Prop | Type | Notes |
|---|---|---|
label | string | base / text-hi |
hint | string? | sm / text-lo, wraps under the label |
enabled | boolean? | gates dependent; a plain boolean unrelated to whatever children is |
dependent | ReactNode? | sub-settings below the hint, gated by enabled — see below |
| children | ReactNode | the control — Switch, Select, Input, a number field, or a small status recipe |
Inline status is a recipe, not a component
The "✓ Authenticated" pattern is an icon + --silo-color-ok text dropped into a SettingRow's control slot — deliberately not its own component.
Dependent sub-settings
A row can nest a sub-setting that only makes sense while its own control is on — a cutoff field, a secondary checkbox. The row's control stays centered against just the label+hint above it, not the taller combined block.
<SettingRow
label="Collapse older agents"
hint="Agents drop off into a collapsed section once they are older than the cutoff period."
enabled={staleDoneEnabled}
dependent={
<>
Cutoff period <Input type="number" value={hours} onChange={...} /> hours
</>
}
>
<Switch checked={staleDoneEnabled} onChange={setStaleDoneEnabled} aria-label="Collapse older agents" />
</SettingRow>dependent renders inside a native <fieldset> — when enabled is false every focusable descendant is disabled automatically and the whole block dims, with no disabled prop to thread into each child by hand.
ModalActions
The footer: actions right-aligned, with an optional start slot pinned left for meta text or a secondary action. Every modal footer is a ModalActions — never hand-roll a flex footer.
// standard form footer
<ModalActions>
<Button onClick={close}>Cancel</Button>
<Button variant="primary" onClick={save}>Create</Button>
</ModalActions>
// with the start slot (meta text…)
<ModalActions start="6 sessions · 6 procs">
<Button>Go to Terminal</Button>
<Button variant="danger">End Task</Button>
</ModalActions>
// …or a secondary action
<ModalActions start={<Button size="sm" onClick={create}>+ Create branch</Button>}>
<Button onClick={fetch}>Fetch</Button>
</ModalActions>Ordering rule: the primary action is rightmost; neutral actions sit to its left; start holds anything that belongs to the modal rather than to confirming/cancelling it.
Scroll areas (.silo-scroll)
Any scrollable region inside a modal gets the silo-scroll class — an 8px overlay-style scrollbar with a transparent track and a pill thumb derived from the theme's text color (28% opacity, 45% on hover), so it stays visible in every theme. Never style scrollbars yourself.
<div className="silo-scroll" style={{ maxHeight: 300 }}>
<List aria-label="Branches">…</List>
</div>