# The UI (components, and the five layers under them)

> Every screen in an Osy# app is a `component` — the one archetype. A page is a component with a route on it, a layout is a component, a reusable widget is a component; `[Page]`, `[Composable]` and the rest are attributes on that one thing, not separate kinds. State is its fields, `live var` is the field that keeps up with the database, and `render { }` is the tree. This is the largest area in the language, so the page below it is a map: which of the five layers your question is in, and which of the sixty pages answers it.

<!-- id: ui-index · area: ui · stability: stable · html: https://osysharp.com/reference/ui/index/ -->

## Summary        {#summary}
**There is one archetype: the `component`.** A bounded reactive unit with typed props, reactive members, and a
declarative render tree. A page, a layout, a dialog, a reusable card — all the same declaration, told apart by
attributes on it:

```osy title="one archetype — a page, a composable and a plain component are the same declaration" syntax
component Card(string title) { … }                 // a reusable piece
[Page("/orders/{id}")] component Order(Guid id) { … }   // …the same thing, with a route
[Composable] component Badge(string text) { … }         // …composable into a public page
```

Inside one, the four kinds of member cover everything a screen does — and there is **no `state` keyword**:

| You want | You write |
|---|---|
| a value the component owns | a **field** — `int count = 0;` |
| a value that follows the database, or another value | **`live var`** — `live var rows = Order.Where(o => o.Open);` |
| something that happens when the user acts | **`action`** — `action Add() { count = count + 1; }` |
| a pure helper the tree may call | **`method`** — and `render` may call it ([Calling helpers from render](https://osysharp.com/reference/ui/render-calls/)) |
| what is on the screen | **`render { }`** — the tree |

And **five layers** sit under that, which is the other half of the model: almost every question about the UI is
really a question about which layer you are in.

| Layer | What it decides | You write |
|---|---|---|
| **Theme** | the app's design *values* — colour, spacing, radius, type | `theme T { Colors { … } }` |
| **Style** | what one box *looks like*, in a closed vocabulary of props | `variants { base { Bg = Surface; } }` |
| **Structure** | what is *on the screen* and how it is arranged | `render { Stack { Text("Hi"); } }` |
| **Behaviour** | what changes, and when | `live var`, `action`, `data` |
| **Route** | which component *is* a page, and who may see it | `[Page("/orders")] [Authorize(…)]` |

They stack in one direction: a **route** shows a **component**, whose **structure** is built from atoms, each styled
by **style props**, whose values come from **theme** tokens. Nothing skips a layer, and that is the whole design —
change a colour in the theme and every box that named it moves, with no recompile of anything else.

All five layers in one small app — read it top to bottom, and each section below tells you more about the layer you
just passed:

```osy title="every layer, once — theme, motion, style, structure, behaviour, route" test app=ui-index
// 1. THEME — the values, named once. Every token lowers to a CSS custom property.
theme Studio {
  Colors { Bg = "#F6F7F9"; OnBg = "#16181D"; Surface = "#FFFFFF"; Border = "#E3E6EA"; Accent = "#4F46E5"; }
  Radius { Card = "10px"; }
  FontSize { Body = "15px"; Section = "17px"; }
  FontWeight { Medium = "600"; }
}

// 1b. MOTION — looping, with no destination state, so it is an `animation` and not a `Transition`.
animation Pulse {
  Duration = "2s";
  Easing = EaseInOut;
  Repeat = Infinite;
  0%   { Opacity = 1; }
  50%  { Opacity = 0.5; }
  100% { Opacity = 1; }
}

// 2 + 3. STYLE + STRUCTURE — a `variants` recipe (static CSS) around a render tree built from atoms.
[AllowAnonymous]
component Card(string title) {
  variants {
    // A pseudo-state NESTS inside a variant value — a top-level block here would be a variant DIMENSION, which
    // has to match a parameter.
    base { Bg = Colors.Surface; Rounded = Radius.Card; P = 4; BorderW = 1; Border = Colors.Border; Hover { Border = Colors.Accent; } }
  }
  render {
    Stack(gap: 2) {
      Text(title, fontSize: FontSize.Section, fontWeight: FontWeight.Medium);
      Slot;
    }
  }
}

// 4 + 5. BEHAVIOUR + ROUTE — state an action moves, on a component the router can reach.
[Page("/")]
[AllowAnonymous]
component Home() {
  int count = 0;
  action Add() { count = count + 1; }
  meta { title = "Overview"; }
  render {
    Stack(gap: 4, p: 6, maxW: "640px", mx: "auto") {
      Card("Counter") {
        Row(gap: 3, align: Align.Center) {
          Text("Clicked " + count + " times", fontSize: FontSize.Body);
          Button("Add", onPress: Add);
          Box(w: "10px", h: "10px", rounded: Radius.Card, bg: Colors.Accent, animation: Pulse);
        }
      }
    }
  }
}
```

## Description    {#description}

### 1. Theme names the values, once    {#theme}
A **[theme tokens](https://osysharp.com/reference/ui/theming/)** block declares design tokens: `Colors`, `Space`, `Radius`, `FontSize`, `Shadow`, `Motion`,
`ZIndex`, `Length`, `Breakpoints`. Each token is a single value, and each lowers to one CSS custom property — which is
why re-theming needs no recompile of your components, and why a token can carry a **per-mode value**
(`Modes.Of(light: …, dark: …)`) that follows the OS dark-mode preference with no flash.

A token can be a whole colour **ramp** rather than one value — see **[color palettes](https://osysharp.com/reference/ui/palette/)**. Fonts the app ships are
**[web fonts — shipping a typeface with your app](https://osysharp.com/reference/ui/web-fonts/)**.

⚠ **A token is a scalar.** Anything with internal structure is not a token: that is why keyframes live in
**[animation — looping motion with no destination state](https://osysharp.com/reference/ui/animation/)** rather than in the theme, beside `entity` and `component` at the top level.

⚑ **Ask the compiler, do not guess:** `osy docs ui-theming` is the token vocabulary in full.

### 2. Style is one closed vocabulary    {#style}
**[style props](https://osysharp.com/reference/ui/styling/)** is the fixed list of **style props** — `Bg`, `P`, `Rounded`, `Position`, `Shrink`, `Cursor`, … —
each mapping to CSS. The vocabulary is **closed and compile-checked**: a misspelled `Backgroud = Surface` is an error,
not a line that silently styles nothing.

You apply them two ways, and they are the same vocabulary either way:
- **`variants { }`** on a component — the recipe. Compiles to static CSS classes, so it costs nothing at runtime and
  can carry pseudo-states (`Hover { }`) and responsive overrides (`Cozy { }`).
- **inline on an atom** — `Text("x", fontSize: FontSize.Body, color: Colors.Subtle)`, for a one-off that does not deserve a component.

Values are a **number** (a step on a scale — `P = 4` is `1rem`), a **keyword** from that prop's closed set
(`Display = Display.Flex`), a **theme token** by name, or a **literal string** for props that pass a raw CSS value through.

⚑ **Reach for a token, not a literal, whenever the value is part of the design.** `osy lint` flags a raw value written
three or more times (`ui-raw-style-literal-repeated`) and names the token to declare — a design decision living in N
places is exactly what the theme exists to prevent.

**[layout primitives](https://osysharp.com/reference/ui/layout/)** is deliberately separate: `gap`, `align` and `justify` are *arrangement*, not appearance, and take
their own path. If you are asking "how do these sit next to each other", that is the layout page, not this one.

⚑ **Do not derive the vocabulary by reading the renderer.** `osy docs ui-styling` prints every style prop in tables by
group; `osy docs ui-layout` prints `gap`/`align`/`justify`. A prop you invented because it seemed plausible is a
compile error at best and a silently ignored line at worst.

### 3. Structure is atoms, components and controls    {#structure}
Three kinds of thing render, and knowing which you want answers most "how do I build X" questions:

- **Atoms** — the **16** primitives the renderer itself owns: `Stack`, `Row`, `Box`, `Text`, `Button`, `Pressable`,
  `Input`, `TextArea`, `Link`, `Image`, `Icon`, `Svg`, `Path`, `Canvas`, `Markdown`, `Upload`. There is deliberately no
  `Card`, no `Modal`, no `Grid` atom. A grid is `Box(display: Display.Grid, cols: …)`; a card is a component you
  write. **The platform widens the style vocabulary rather than shipping components** — that is the standing rule,
  and it is why the atom list stays this short.
- **Kit controls** — **[Osysharp.Ui (the UI kit)](https://osysharp.com/reference/ui/kit/)**: the ready-made styled ones (`Field`, `Button`, `Table`, …), in scope for every
  app with no `using` and no `use`, forkable by declaring a component of the same name. **This is what you build a
  page out of**; a bare `Input` atom has no label, and `Field("Email", value: email)` is the labelled input with the
  spacing and the accessibility already in it. **[Pinning a kit version (using Ui@2)](https://osysharp.com/reference/ui/kit-versioning/)** pins a version.
- **Components** — **[component](https://osysharp.com/reference/ui/component/)**, what you write. Parameters are props; **[Slot (child content)](https://osysharp.com/reference/ui/slots/)** takes children;
  **[[Composable] — presentational components in public pages](https://osysharp.com/reference/ui/composable/)** governs reuse from a public page; **[generic component](https://osysharp.com/reference/ui/generic-component/)** covers `Dropdown<T>`;
  **[Calling helpers from render](https://osysharp.com/reference/ui/render-calls/)** is the call syntax; **[Visitor](https://osysharp.com/reference/ui/visitor/)** handles a heterogeneous tree.
- **Controls** — **[control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/)**: a foreign widget (chart, data grid, map) implemented in JavaScript and described
  to the compiler by a `control` block, so its call sites type-check like anything else. Its styling knobs are
  **[styles — a control's own look knobs](https://osysharp.com/reference/ui/control-styles/)**, its imperative verbs **[commands — the verbs a control accepts](https://osysharp.com/reference/ui/control-commands/)**, its lazy assets
  **[chunks — assets a control loads on demand](https://osysharp.com/reference/ui/control-chunks/)**, what it reports about itself **[probe — what a control says about itself](https://osysharp.com/reference/ui/control-probe/)**.

App-shipped **[icons](https://osysharp.com/reference/ui/icons/)**, **[SVG assets](https://osysharp.com/reference/ui/svg-assets/)**, **[textures](https://osysharp.com/reference/ui/textures/)** and **[sound](https://osysharp.com/reference/ui/sound/)** are referenced by name
and checked at compile time.

⚑ **`osy kit` lists every bundled control with its signature and a worked example** (`osy kit <Control>` prints its
whole source, which is also how you fork it). `osy kit --atoms` lists the 16 primitives, each marked container or
not — i.e. whether `gap`/`align`/`justify` apply to it at all.

### 4. Behaviour is what changes, and when    {#behaviour}
**[The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/)** is the core: `var` is a snapshot, **`live var` subscribes** — a distinction worth knowing before
you write anything, because a list that never refreshes is almost always a missing `live`. **[on mount / on unmount](https://osysharp.com/reference/ui/lifecycle/)** covers
mount/unmount, **[Pending](https://osysharp.com/reference/ui/pending/)** the in-flight state, **[skeleton](https://osysharp.com/reference/ui/skeleton/)** what stands in before the first result
lands, and **[A failing query](https://osysharp.com/reference/ui/query-failure/)** what happens when a read fails.

**Writing data is [creating & saving data](https://osysharp.com/reference/ui/data-mutation/)**, and it is the one place UI differs from a server function: an edit applies
**instantly and stays visible while the user keeps working**, and **`UnitOfWork.Commit()`** is what sends the
accumulated edits to the server atomically. A form commits once, on Save; a page that saves per action (ticking a
to-do *is* the save) commits in each verb. Both are correct — what is never correct is a page with no Save and no
`UnitOfWork.Commit()`, where the write is discarded with no error. A read written **inside** a body is
**[reading data inside an action](https://osysharp.com/reference/ui/read-in-a-body/)**; **[Dialog.Open / Dialog.Ask / Dialog.Confirm / Dialog.Discard](https://osysharp.com/reference/ui/dialogs/)** is where the unit of work is a choice you make (`Inherit` vs `Root`).

Input handling: **[on change](https://osysharp.com/reference/ui/on-change/)**, **[onEnter](https://osysharp.com/reference/ui/on-enter/)**, **[onEscape](https://osysharp.com/reference/ui/on-escape/)**, **[debounce](https://osysharp.com/reference/ui/debounce/)**,
**[Validation](https://osysharp.com/reference/ui/validation/)**, **[Clipboard](https://osysharp.com/reference/ui/clipboard/)**, **[pointer](https://osysharp.com/reference/ui/pointer/)**, **[keys](https://osysharp.com/reference/ui/keys/)**, **[drag](https://osysharp.com/reference/ui/drag/)**.
**[Connection](https://osysharp.com/reference/ui/connection/)** is the app's own surface for "the server dropped".

### 5. A route makes it a page, and decides who may see it    {#route}
**[routes and pages](https://osysharp.com/reference/ui/routing/)** makes a component a page and gives it a URL: `[Page("/catalog/{slug}")]` captures the segment as a
parameter, `[Layout(AppShell)]` wraps it, and `[Render(CSR)]` / `[Render(SSR)]` chooses whether the first response
already carries the content. **[Navigation](https://osysharp.com/reference/ui/navigation/)** moves between pages.

**[page authorization (policies)](https://osysharp.com/reference/ui/authorize/)** is the access gate — and the default is the important part: a routed component **requires auth
unless it says `[AllowAnonymous]`**. Never the other way round. **[canPress / canEdit / canSee](https://osysharp.com/reference/ui/policy-controls/)** reflects a policy into the
UI (a button that disables itself because the rule says so, rather than because someone remembered to check).
**[Session.CurrentUser](https://osysharp.com/reference/ui/current-user/)** is who is being shown the page; **[Visitor](https://osysharp.com/reference/ui/visitor/)** is the opaque browser id for someone who
has not signed in — a name, never a credential.

### Proving a screen works    {#testing}
A UI test is an ordinary `[Test]`: **[Ui — drive the app's UI from a test](https://osysharp.com/reference/testing/ui/)** — `Ui.Visit` opens a route, `Ui.Click` presses what a person
would press, `Ui.Fill` types, and the same `Assert.*` verbs ask the questions, with your real security rules on.
`within:` is how you address one row when several read alike. `osy docs testing-ui` is the page; `osy docs testing`
is the model underneath it.

### Where things are NOT    {#not-here}
The questions that most often send people to the wrong page:

- **"How do I space these out?"** → `gap`/`align`/`justify` are **[layout primitives](https://osysharp.com/reference/ui/layout/)**, not style props.
- **"How do I make this move?"** → an A→B state change is `Transition` (a **[theme tokens](https://osysharp.com/reference/ui/theming/)** motion token, applied as a
  style prop). Looping motion with no end state is **[animation — looping motion with no destination state](https://osysharp.com/reference/ui/animation/)**.
- **"Why is my list stale?"** → **[The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/)**. It is `var` where you wanted `live var`, far more often than
  it is anything else.
- **"Where do I put the save?"** → **[creating & saving data](https://osysharp.com/reference/ui/data-mutation/)**. There is no `Save()` on a row; there is
  `UnitOfWork.Commit()` on the unit of work.
- **"Why is my third-party script blocked?"** → **[What an app page is allowed to load](https://osysharp.com/reference/ui/content-security-policy/)**. A control must ship what it needs
  rather than fetch it at run time.

### Read these four first    {#reading-order}
If you are starting cold, four pages get you productive and the rest are reference:

1. **[component](https://osysharp.com/reference/ui/component/)** — how to declare one and render it.
2. **[layout primitives](https://osysharp.com/reference/ui/layout/)** — how boxes sit next to each other.
3. **[theme tokens](https://osysharp.com/reference/ui/theming/)** then **[style props](https://osysharp.com/reference/ui/styling/)** — in that order: tokens first, then the props that name them.
4. **[The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/)** — the `var` / `live var` distinction.

Then **[routes and pages](https://osysharp.com/reference/ui/routing/)** + **[page authorization (policies)](https://osysharp.com/reference/ui/authorize/)** when you want a real page, **[creating & saving data](https://osysharp.com/reference/ui/data-mutation/)** the moment it has
to save, and **[control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/)** when you need something the atoms cannot express. **[Writing a component — what differs from C#](https://osysharp.com/reference/ui/csharp-differences/)** is
worth a skim if you are coming from C#.

## The pages      {#the-pages}
The whole area, grouped by the question that sends you to it.

**The unit itself**
- [component](https://osysharp.com/reference/ui/component/) — the one archetype; props, members, render
- [Writing a component — what differs from C#](https://osysharp.com/reference/ui/csharp-differences/) — what a component body does *not* do the way C# does
- [The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/) — `var` vs `live var`, and how a change re-renders only what read it
- [on mount / on unmount](https://osysharp.com/reference/ui/lifecycle/) — `on mount` / `on unmount`
- [generic component](https://osysharp.com/reference/ui/generic-component/) — a component with type parameters
- [[Composable] — presentational components in public pages](https://osysharp.com/reference/ui/composable/) — `[Composable]`, for a presentational piece a public page composes

**Building the tree**
- [layout primitives](https://osysharp.com/reference/ui/layout/) — `Stack` / `Row` / `Box`, and `gap` / `align` / `justify`
- [Slot (child content)](https://osysharp.com/reference/ui/slots/) · [Slot(item) — let the caller decide what each row looks like](https://osysharp.com/reference/ui/slot-template/) · [Cell template (your own content in a control's cell)](https://osysharp.com/reference/ui/cell-template/) — taking children, per-item templates, a control's cells
- [Naming a value in render](https://osysharp.com/reference/ui/render-binding/) — naming a value inside `render`
- [Calling helpers from render](https://osysharp.com/reference/ui/render-calls/) — calling a pure helper from a render expression
- [Visitor](https://osysharp.com/reference/ui/visitor/) — rendering a heterogeneous tree
- [Markdown — rendering markdown text](https://osysharp.com/reference/ui/markdown/) — rendering stored markdown as content
- [Canvas](https://osysharp.com/reference/ui/canvas/) — a drawing surface and the `Draw.*` verbs
- [Canvas 3D](https://osysharp.com/reference/ui/canvas-3d/) — a lit, shadowed 3D scene on the same canvas: cameras, lights, fog and meshes
- [App shells](https://osysharp.com/reference/ui/shell/) — the app shell: one `AppChrome` declaration, four arrangements of it

**Look**
- [style props](https://osysharp.com/reference/ui/styling/) — the style-prop vocabulary
- [theme tokens](https://osysharp.com/reference/ui/theming/) — tokens; [color palettes](https://osysharp.com/reference/ui/palette/) — colour ramps
- [animation — looping motion with no destination state](https://osysharp.com/reference/ui/animation/) — looping motion
- [web fonts — shipping a typeface with your app](https://osysharp.com/reference/ui/web-fonts/) · [icons](https://osysharp.com/reference/ui/icons/) · [SVG assets](https://osysharp.com/reference/ui/svg-assets/) · [textures](https://osysharp.com/reference/ui/textures/) · [sound](https://osysharp.com/reference/ui/sound/) — the assets an app ships
- [accessibility](https://osysharp.com/reference/ui/accessibility/) — `role:`, `label:`, and the state props

**Ready-made**
- [Osysharp.Ui (the UI kit)](https://osysharp.com/reference/ui/kit/) — the bundled controls; [Pinning a kit version (using Ui@2)](https://osysharp.com/reference/ui/kit-versioning/) — pinning one
- [control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/) — declaring a foreign widget
- [styles — a control's own look knobs](https://osysharp.com/reference/ui/control-styles/) · [commands — the verbs a control accepts](https://osysharp.com/reference/ui/control-commands/) · [chunks — assets a control loads on demand](https://osysharp.com/reference/ui/control-chunks/) · [probe — what a control says about itself](https://osysharp.com/reference/ui/control-probe/) — a control's four blocks
- [The markdown editor kit — a rich editor you opt into](https://osysharp.com/reference/ui/markdown-editor-kit/) — the optional rich markdown editor
- [The chart kit — line, column, bar, scatter and candle, with no JavaScript](https://osysharp.com/reference/ui/chart-kit/) — the optional charts, with no JavaScript at all
- [The barcode kit — a QR and barcode scanner you opt into](https://osysharp.com/reference/ui/barcode-kit/) — the optional QR and barcode scanner

**Data**
- [creating & saving data](https://osysharp.com/reference/ui/data-mutation/) — creating, editing, deleting, and `UnitOfWork.Commit()`
- [reading data inside an action](https://osysharp.com/reference/ui/read-in-a-body/) — a read written inside an action or hook
- [Pending](https://osysharp.com/reference/ui/pending/) · [skeleton](https://osysharp.com/reference/ui/skeleton/) · [A failing query](https://osysharp.com/reference/ui/query-failure/) — in-flight, stand-in, and failed
- [Sorting by a column the user picks](https://osysharp.com/reference/ui/sort-by-column/) — sorting by a column the user picks
- [Validation](https://osysharp.com/reference/ui/validation/) — the entity's rules, shown beside the field

**Input and events**
- [on change](https://osysharp.com/reference/ui/on-change/) · [onEnter](https://osysharp.com/reference/ui/on-enter/) · [onEscape](https://osysharp.com/reference/ui/on-escape/) · [debounce](https://osysharp.com/reference/ui/debounce/) — the everyday handlers
- [pointer](https://osysharp.com/reference/ui/pointer/) · [keys](https://osysharp.com/reference/ui/keys/) · [drag](https://osysharp.com/reference/ui/drag/) — pointer, held keys, drag-to-a-number
- [on every](https://osysharp.com/reference/ui/cadence/) — `on every`; [on settled — run something once, when a stream finishes](https://osysharp.com/reference/ui/on-settled/) — once, when a stream finishes
- [Clipboard](https://osysharp.com/reference/ui/clipboard/) — writing to the system clipboard
- [sound](https://osysharp.com/reference/ui/sound/) — playing the audio an app ships
- [camera and microphone](https://osysharp.com/reference/ui/capture/) — the camera and the microphone: photograph, record, and what a refusal looks like
- [upload](https://osysharp.com/reference/ui/upload/) — the file picker, and the `UploadedFile` both it and the camera answer
- [Func<T, R>](https://osysharp.com/reference/ui/function-value/) — `Func<T, R>`, so a component can be told *how* to get a value

**Route, access, session**
- [routes and pages](https://osysharp.com/reference/ui/routing/) — `[Page]`, params, `[Layout]`, `[Render(CSR)]`
- [Navigation](https://osysharp.com/reference/ui/navigation/) — moving between pages
- [page authorization (policies)](https://osysharp.com/reference/ui/authorize/) — the secure-by-default gate; [canPress / canEdit / canSee](https://osysharp.com/reference/ui/policy-controls/) — `canPress` / `canEdit` / `canSee`
- [Session.CurrentUser](https://osysharp.com/reference/ui/current-user/) — `Session.CurrentUser`; [Visitor](https://osysharp.com/reference/ui/visitor/) — the pre-sign-in browser id
- [Dialog.Open / Dialog.Ask / Dialog.Confirm / Dialog.Discard](https://osysharp.com/reference/ui/dialogs/) — an overlay, and which unit of work it edits in
- [Connection](https://osysharp.com/reference/ui/connection/) — the app's connection-loss surface
- [What an app page is allowed to load](https://osysharp.com/reference/ui/content-security-policy/) — what a page is allowed to load

**Measuring the real box**
- [Layout.ScrollHeight and Layout.ScrollWidth](https://osysharp.com/reference/ui/scroll-extent/) — `Layout.ScrollHeight` vs `Layout.Height`
- [Layout.TextWidth](https://osysharp.com/reference/ui/text-measurement/) — how wide a string will actually paint

## See also   {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — the unit everything else hangs off.
- [style props](https://osysharp.com/reference/ui/styling/) — the full style-prop vocabulary, group by group.
- [theme tokens](https://osysharp.com/reference/ui/theming/) — the token system the style props draw from.
- [layout primitives](https://osysharp.com/reference/ui/layout/) — arrangement, which is deliberately not styling.
- [creating & saving data](https://osysharp.com/reference/ui/data-mutation/) — how a screen writes, and where the commit is.
- [Ui — drive the app's UI from a test](https://osysharp.com/reference/testing/ui/) — driving the screen from a `[Test]`, with the security rules on.
