Osy#betaRun it on Osyrin: osyrin.com

Reference / UI

Placing and naming a component — m:, w:, grow:, alignSelf:, animation:, label: on a component call

Badge("New", m: 2, alignSelf: AlignSelf.Center, animationKey: count) — placement, spacing, motion and label: land on the component's root element

A caller may PLACE, SPACE and ANIMATE a component it calls — `m*`, `w`/`h` and their min/max, `grow`, `shrink`, `basis`, `alignSelf`, `order`, `colSpan`/`rowSpan`/`gridArea`, `animation`, `animationDelay`, `exitAnimation` and `animationKey` — and they apply to the component's root element, winning over its own. What PAINTS it (colour, border, type) stays the component's own vocabulary, such as `tone:` and `size:`. A caller may also NAME it — `label:` becomes the root's accessible name, and what a test's `within:` finds it by.

stable2 examples compiled by CIuistylinglayoutauthoring

Summary#

Where a component sits, how much room it gets and how it arrives are decisions about the page around it, so the caller writes them on the call:

[Route("/orders")] [AllowAnonymous]
component OrdersPage() {
  int fresh = 3;
  render {
    Row(gap: 2, align: Align.Center) {
      Text("Orders", grow: 1);
      Badge($"{fresh} new", tone: Tone.Primary, ml: 2, animationKey: fresh);
      Button("Refresh", onPress: Refresh, alignSelf: AlignSelf.End);
    }
  }
  action Refresh() { fresh = fresh + 1; }
}

Signature#

<Component>(<args>…, <placement prop>: <value>)
// m mx my mt mb ml mr · w h minW maxW minH maxH · grow shrink basis alignSelf order colSpan rowSpan gridArea
// animation animationDelay exitAnimation animationKey · label

The values are the ones the same prop takes on an atom (style props): spacing steps, lengths, enum members, declared animations.

Description#

Which element do they land on?#

The component's ROOT — the element its render writes at the top level. A component whose root is another component passes them on, so Badge(…) inside your own [Composable] component Status takes the m: written on Status(…, m: 2). Each arm of a top-level if is the root while it shows.

Who wins?#

The caller. The value is written after the component's own on that element, and an inline value beats a variant's class — so a Badge whose own recipe sets a width takes w: from the call.

Naming one — label:#

A component used twice on one screen needs two names, and it cannot know them — so the caller gives it one. label: lands on the root as its accessible name (aria-label), which is also what Assert.Visible(…, within: "…") scopes to in a test. It REPLACES a name the component gives its root itself; an element has one name.

[Composable]
component Panel(string title) {
  render { Stack(gap: 2, p: 3, label: title) { Text(title, fontWeight: FontWeight.Medium); Slot; } }
}

[Route("/lines")] [AllowAnonymous]
component LinesPage() {
  render {
    Row(gap: 3) {
      Panel("Open", label: "Open on next") { Text("4 slices"); }
      Panel("Open", label: "Open on maint-4.2") { Text("1 slice"); }
    }
  }
}

A component that declares a label parameter itself — the kit's Badge does — receives the argument instead, as below.

What is refused?#

  • A prop that paints — bg, color, border, fontSize, rounded, shadow, … A component's look is its own vocabulary (Badge(…, tone: Tone.Success)), so twenty screens cannot drift apart; to paint around it, wrap it in a Box and style the Box. The refusal names the component's own parameters.
  • A component with no one root element — two elements side by side at its top level, a foreach (one root per row), the caller's own content (Slot) or a foreign control. The refusal says which, and writes the wrap.
Badge("Late", bg: Colors.Danger);              // ✗ bg PAINTS — use Badge's own vocabulary:
Badge("Late", tone: Tone.Danger);              // ✓
Box(bg: Colors.Muted, p: 2) { Badge("Late"); } // ✓ or paint a wrapper around it

What if the component has a parameter with that name?#

Then it is that parameter, as a named argument is in C#: Chart(rows, h: 240) passes 240 to a Chart that declares int h, and the chart uses it where it chooses. Only a name the component does not declare is a placement prop.

Why not a wrapper Box?#

A wrapper is an extra element, and it changes layout by being there: in a Row, the flex item is now the Box, not the control, so grow: and alignSelf: on the control inside it no longer mean anything. Written on the call, they land on the element the parent actually lays out.

See also#

Related

style props

Inside a `variants` block, each `Name = value` is a style prop from a fixed vocabulary the renderer maps to CSS — paint…

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

animationKey — play an animation again when a value changes; a line that draws itself

`animationKey: value` on any element makes its animation play each time the value CHANGES — and not when the element…

layout primitives

The built-in layout primitives and how they arrange children. `Stack` stacks children in a column, `Row` lays them in a…