# Guides
How-tos that go beyond the reference. Each builds on the
[Concepts](./concepts.md) — especially the [control vs field
split](./concepts.md#control-vs-field--the-key-distinction).
**Using the library:**
- [Composing a section body](#composing-a-section-body)
- [Custom content and custom CSS](#custom-content-and-custom-css)
- [Reactive values and change events](#reactive-values-and-change-events)
- [Choosing a button](#choosing-a-button)
- [A toolbar with a fluid search](#a-toolbar-with-a-fluid-search)
- [Designing dock sections](#designing-dock-sections)
- [App-wide shortcuts and saved state](#app-wide-shortcuts-and-saved-state)
**Extending it:**
- [Building a custom control](#building-a-custom-control)
- [Building a custom field](#building-a-custom-field)
- [Validation & tooltips](#validation--tooltips)
- [Building a custom search or filter control](#building-a-custom-search-or-filter-control)
- [Deep-linking to a field](#deep-linking-to-a-field)
---
## Composing a section body
A `Section` body has padding and nothing else. It doesn't arrange what you put in it,
which is what makes custom content possible. So the structure is yours to choose:
| You're adding | Use | Why |
|---|---|---|
| One field | The field on its own | Nothing to stack |
| Several fields | `FieldGroup` | Rows have no gap of their own; the group provides it |
| A titled block of fields | `Fieldset` | A group with a heading |
| A block that collapses | A nested `Section` | |
| A value only the app changes | `ReadoutField` | A caption and selectable text, with no input to imply it's editable |
| A sentence or two | `Label layout="expanded"` | A compact `Label` truncates to one line |
| A warning or status message | `Label level="warn"` | Same row, colored by severity |
| Richer prose, a table, a tree | Your own markup | See [Custom content and custom CSS](#custom-content-and-custom-css) |
**Every `*Field` and `Label` is a row, and a row has no margin.** Rows placed directly in
a section or modal body touch, with no error or warning, and it only looks slightly
wrong. So wrap any stack of two or more:
```tsx
```
A group's direct children are its rows. A wrapper around one row is fine, but a wrapper
around several takes them out of the group's gap.
Bare content is deliberately supported, so a section holding a table or a canvas needs no
group. The rule is about stacks of rows, not about sections.
### Read-only or readout?
Both show a value a user can't change right now, and the difference is whether they ever
could. A field that's editable in some states uses `readonly` on its normal field, so it
keeps its shape when it locks. A value that only the app ever changes, like a resolved
path or a frame count, uses `ReadoutField`, which can also be selected and copied.
### Spacing between blocks
Spacing between two blocks in a body, like a `FieldGroup` and the buttons after it, is
yours. The library adds none, so that bare content stays free of surprises. A margin on
your own class is the intended answer, not a workaround:
```tsx
…
```
```css
.my-actions { margin-top: 8px; }
```
### Modal bodies and footers
A modal is one dismissable `Section`, so all of the above applies to its body. For a
footer with a primary action and a quieter one, set `variant` on the quieter option:
```tsx
```
Use `kind: 'static'` for non-button content such as a readout.
To widen a content-heavy modal, pass a class: `stageModal.open(content, { class: 'my-wide-modal' })`.
---
## Custom content and custom CSS
Everything inside `.stage-root` gets the tier's font size and the consumer's font family.
Beyond that, custom markup gets no library styling, so read the tokens rather than
repeating their values. A theme override then reaches your markup as well.
```css
.my-tree-row {
padding: 2px 6px;
color: var(--stage-color-text);
border-radius: var(--stage-border-radius);
}
.my-tree-row:hover { background: var(--stage-color-control-hover-bg); }
.my-tree-row.selected {
background: color-mix(in srgb, var(--stage-color-accent) 25%, var(--stage-color-panel-bg));
}
```
| You want | Token |
|---|---|
| Text | `--stage-color-text` |
| Disabled text | `--stage-color-text-disabled` |
| Hover fill | `--stage-color-control-hover-bg` |
| Borders | `--stage-color-border`, `--stage-color-border-strong` |
| Accent | `--stage-color-accent` |
| Text size, row height | `--stage-font-size`, `--stage-control-height` |
| Layering a custom popover | `--stage-z-popover` |
- **Skip `var()` fallbacks.** Every token is always defined inside `.stage-root`, so a
fallback does nothing except hide a misspelled or invented name.
- **`--stage-color-text-muted` doesn't dim by default.** It equals `--stage-color-text`
until a theme overrides it. Use `-text-disabled`, or mix your own, when you need a
dimmer tone.
- **Keyboard access is yours.** A `