# The language

## Files

Events can also run inline scripts. File-level `scriptLanguage: "lua"` selects Lua; JavaScript is the default when omitted. See [Inline scripts](inline-scripts.md).

A file may contain any number of these definitions in any order:

| Top level | |
|---|---|
| `Dialog Name { ... }` | A dialog with its own size (`size`); the application opens its frame |
| `Screen Name { ... }` | The contents of a page; the application supplies the page and its geometry |
| `component Name : Basistyp { ... }` | A reusable element |
| `record Name { typ feld ... }` | An application data type, for example a list row |

- **Comments:** `//` and `/* */`.
- **Separators:** Statements end at a newline or `;`. Line breaks are allowed inside `()`, `[]` and `? :`.
- **Visibility:** Components and records are shared by all files in the same `aura_add_ui` invocation (or the same loader directory, including subdirectories). File names must be unique even across directories, because generated C++ files are named after them.

## Elements

```qml
Typ {
    id: elementName
    property: wert
    Kind { ... }
}
```

- **Type:** A built-in type (table below), an application type (`aura-elements.json`, see below) or a component.
- **`id`:** The name other expressions use to access the element, and its field name in the application's struct. Without `name`, the ID also becomes its `objectName`. Reserved names are `root`, `parent`, `theme`, `screen`, `props` and `self`.
- **Children** are drawn above earlier siblings.

`id: button` names an element; it is optional and uses an unquoted identifier. The older child syntax `Rect button { ... }` remains supported. Definition names stay explicit: `Dialog Hello`, `Screen Foo` and `component Foo : Rect` name definitions, independently of an optional root ID.

```qml
Dialog Hello {
    id: helloDialog
    property int count
    Text {
        text: str(helloDialog.count) // explicit access to the dialog property
    }
    Rect {
        width: 42
        Text { text: str(parent.width) + ": " + str(helloDialog.count) }
    }
}
```

`parent` always refers to the immediate containing element. It does not search ancestors for a property. `parent.count` works only when the immediate parent declares `count` (for example, a dialog or component root); use `helloDialog.count` when nested children need the dialog property.

| Type | aura-ui | Type-specific properties |
|---|---|---|
| `Item` | `Node` | – |
| `Rect` | `RectNode` | `color`, `radius`, `corners: topLeft, topRight, bottomRight, bottomLeft`, `border: width, color`, `gradient: top, bottom` |
| `Text` | `TextNode` | `text`, `font: family, size[, weight]`, `weight`, `color`, `align: left\|center\|right[, top\|center\|bottom]`, `wrap: width[, alignment]`, `fit: true\|minimumSize`, `lineHeight` (QML factor), `letterSpacing` (px), `pixelSize`/`minimumPixelSize` (px, bypassing `Context::fontSize`), `underline` |
| `Image` | `ImageNode` | `source: "name.png"` (`Context::image`) or `image: value` (an application image of type `image`), `fill: stretch\|crop\|fit\|pad`, `radius`, `corners` |
| `Svg` | `SvgNode` | `source: "name.svg"`, `fill: color` |
| `Scroll` | `ScrollNode` | `contentHeight`, `contentY`; horizontal with `direction: horizontal`: `contentWidth`, `contentX`. When a horizontal scroll is nested inside a vertical one, the direction of the finger decides which scrolls |
| `QrCode` | `QrCodeNode` | `text` (contents), `color` |
| `Html` | `HtmlNode` | `text` (HTML like QML RichText), `font: family, size`, `color`, `lineHeight`, `margin` (documentMargin); without `height`, its height follows the text at its given width |
| `Column` | `ColumnNode` | `spacing`, `padding`, `align: left\|center\|right`, `justify`; stacks visible children from the top and grows to their combined height (see [Layouts](#layouts-column-row-grid)) |
| `Row` | `RowNode` | `spacing`, `padding`, `align: top\|center\|bottom`, `justify`, `wrap: true`, `direction: leftToRight\|rightToLeft`; arranges visible children from the left and grows to their combined width, or arranges them from its right edge |
| `Grid` | `GridNode` | `columns`, `spacing`, `padding`, `cell: width, height`; cells take the size of the first visible child (or `cell`); height follows the rows |
| `Popover` | `PopoverNode` | `color`, `markerPos`, `unfold` (visible fraction of the height, 0–1; `enter: unfold 0 300 linear` unfolds it); a speech bubble with a marker at the top |
| `Ripple` | `RippleNode` | `color` (gray), `opacity` (0.4), `size` (200 × 200); feedback for its parent: appears under the finger when pressed and pulses (1 ↔ 1.1, 500 ms each way), grows to four times its size over 500 ms after a tap, then runs the parent's `onTap` (immediately without an animator). Place it last to draw it on top; the parent clips its children (`clip`) |
| `TextField` | `TextFieldNode` (`aura/ui/text_input.h`) | `text` (initial value, see [Text input](#text-input)), `font`, `weight`, `color`, `align`, `letterSpacing`, `pixelSize`, `placeholder`, `placeholderColor` (#cccccc), `masked` (`*` per character), `maxLength` (characters), `characters` (allowed characters, e.g. `"0123456789*#"`), `focus: true`, `onChange: signal`, `onAccept: signal`; a single line with a caret, clips and scrolls to keep the caret visible |
| `Keyboard` | `KeyboardNode` (`aura/ui/text_input.h`) | `layout: "DE"` (CZ, DE, DK, EN, FI, FR, NL, NO, PL, SE, SI, SK, also lowercase; `"alphanum"`: 800 × 240, digits and uppercase letters, Backspace, OK, Cancel), `color`, `radius`, `border`, `keyColor`, `textColor`, `accentColor` (modifier keys), `okColor`, `cancelColor`, `cancelText`, `okKey: false`, `onOk: signal`, `onCancel: signal`; an on-screen keyboard, 960 × 300, scaled to its own size |
| `Button` | `ButtonNode` (`aura/ui/controls.h`) | `text`, `font`, `weight`, `textColor` (white), `color` (#404040), `pressedColor` (otherwise a darker `color`), `radius` (8), `border`; a button with centered text, default size 200 × 60 (see [Controls](#controls)) |
| `CheckBox` | `CheckBoxNode` | `text`, `font`, `textColor`, `color` (box), `radius`, `checked`, `onToggle: signal`; tapping toggles it |
| `Switch` | `SwitchNode` | Like `CheckBox`, plus `trackColor` (off); a sliding switch, `color` (on) |
| `RadioButton` | `RadioButtonNode` | Like `CheckBox`, plus `group`; tapping selects it and clears the others in the same group (without `group`: its siblings). Only the newly selected button emits `onToggle` |
| `ComboBox` | `ComboBoxNode` | `options` (list of strings), `index` (-1: none), `placeholder`, `font`, `textColor`, `color`, `radius`, `border`, `listColor`, `listTextColor`, `selectedColor`, `visibleRows` (6), `onSelect: signal`; tapping opens a list below it, above the entire scene (or above the control if there is no room below); tapping outside closes it |
| `Slider` | `SliderNode` | `value`, `from` (0), `to` (1), `step`, `color`, `trackColor`, `onChange: signal`; drag or tap the track |
| `ProgressBar` | `ProgressBarNode` | `value`, `from` (0), `to` (1), `color`, `trackColor`, `radius` (half its height) |
| `Rive` | `RiveNode` | `source: "goodbye.riv"` (`Context::file`); loops the default state machine of the default artboard (otherwise its first animation) through the animator, fitted like `PreserveAspectFit` |
| `WebView` | `aura::webview::WebViewNode` (libaura-webview) | `url`, `script` (JavaScript, run in each page before its own scripts); Chromium through CEF, touch input, WebChannel to C++ (`aura.call`, `aura.on`). Only in applications with `AURA_WITH_WEBVIEW`; see [aura-webview.md](aura-webview.md) |

**Application element types:** An `aura-elements.json` beside or above the `.aura` files that use them describes elements drawn by the application:

The following example describes an application-provided `BadgeNode`, not a built-in AURA element.

```json
{
	"Badge": {
		"type": "BadgeNode",
		"header": "widgets/badge_node.h",
		"properties": {
			"color": {"type": "color"},
			"label": {"type": "string", "set": "setLabel()"}
		}
	}
}
```

- **Properties:** `type` is `number`, `string`, `bool` or `color`. Generated code sets the member of the same name, or the one specified by `set`; if `set` ends in `()`, it calls the function.
- **Consumers:** `aurac` (the nearest file above each `.aura` file, or `--elements`), `aura_add_ui` (as a dependency), the loader (all files in its directory), `aura-view` and the VS Code extension (validation, completion).
- **Dynamic mode:** This tells the loader about the properties, but it can create and configure the type only after the application registers it (`aura::ui::registerElementType`). Otherwise it reports “not part of this program” and uses the compiled version.

All elements also have:

| Property | Meaning |
|---|---|
| `name` | `objectName` (remote control, logs); defaults to the ID |
| `x`, `y`, `width`, `height`, `size: w, h` | Geometry |
| `place: ...` | Anchors; see below |
| `visible`, `opacity`, `rotation`, `scale`, `clip`, `glow` | As in aura-ui (`clip` clips to a rectangle, like Qt Quick; `glow` glows white, e.g. `glow: root.pressed`) |
| `onTap: signal`, `onTap: signal(args)`, `onTap: ignore` | Emits a signal or consumes the tap |
| `enabled: false` | Disables input for the element and its children (QML `enabled`); controls dim themselves |
| `grow: weight` | In a `Column`/`Row`: a share of the free space along the layout direction (QML `Layout.fillHeight`/`fillWidth`) |
| `stretch: true` | In a `Column`/`Row`: fills across the layout direction (width or height); fills its cell in a `Grid` |
| `minWidth`, `maxWidth`, `minHeight`, `maxHeight` | Limits for `grow` and `stretch` (0: none) |
| `onSettled: signal` | An element animation (`animate:`) reached its target; see [Animations](state-bindings.html#animations-animate-enter) |
| `pressSink: offset, ms` | Sinks when pressed |
| `spin: ms` | Spins clockwise indefinitely, one revolution per `ms` (service menu gear) |
| `loop: property from to ms [easing] [pause ms], …` | Oscillates indefinitely between two values; see [Animations](state-bindings.html#animations-animate-enter) |

**Text in detail:**
- **Font family:** Without `font`, uses `aura::ui::TextNode::defaultFamily`, set by the application at startup; if empty, uses the font collection's default.
- **Font size:** `font` and `fit: N` pass through the application's `Context::fontSize()` (default: unchanged). `pixelSize` sets pixels directly, bypassing this function.
- **Natural size:** A `Text` without `width`/`height`/`size` takes its content's size. With `wrap: 400`, its width is 400 and its height follows the text.
- **Rich text** like QML StyledText: `<b>`, `<br />` and `<font color="#rrggbb">`.

## Anchors (`place`)

`place: left 44, below title 30, centerY -20` becomes `aura::ui::place(node).left(44).below(*title, 30).centerY(-20)` (see [graphics.md](graphics.md)). Order matters.

| Directive | Meaning |
|---|---|
| `left`, `right`, `top`, `bottom` [gap] | Relative to the parent's edge |
| `centerX`, `centerY` [offset], `center` | Centered in the parent, rounded |
| `below`, `above`, `rightOf`, `leftOf` element [gap] | Beside an element |
| `alignLeft`, `alignTop`, `alignBottom` element [offset] | Aligned to an element |
| `centerXOn`, `centerYOn` element | Relative to an element's center |

Valid elements are IDs defined **earlier** in the source (siblings or ancestors), `parent`, and `id[index]` for repeated elements, e.g. `centerYOn field[0]`.

## Layouts: `Column`, `Row`, `Grid`

Layouts arrange their visible children when a child is added. If a child changes later (through a binding or rebuilding), they rearrange. Their own size follows the content (`Column`: height; left-to-right `Row`: width; `Grid`: height), so other elements can refer to it:

```qml
Scroll {
    id: list
    size: parent.width - 4, min(len(lines), 5) * 76
    contentHeight: productRows.height   // nach dem Bau der Zeilen
    Column {
        id: productRows
        width: parent.width
        repeat line, i in lines { CartRow { index: i; line: line } }
    }
}
// Header: rechtsbündig
Row { id: buttons; direction: rightToLeft; spacing: 16; size: parent.width, 60 }
// Grid layout
Grid { id: cells; columns: columns; spacing: 10 }
```

Invisible children take no space. Children set their own size; the layout assigns their position along its direction (`Column`: `y`; `Row`: `x`; `Grid`: both).

**Flexible:** These properties let a layout distribute space instead of just stacking:

```qml
Column {
    size: 400, parent.height
    padding: 20                     // 1 Wert, 2 (oben/unten, links/rechts) oder 4 (oben, rechts, unten, links)
    spacing: 10
    align: center                   // quer: left, center, right (Row: top, center, bottom)

    Text { text: tr("Title", "Titel") }
    Scroll { grow: 1 }              // nimmt die restliche Höhe
    Row {
        stretch: true               // so breit wie die Column (ohne padding)
        height: 60
        justify: spaceBetween       // start, center, end, spaceBetween, spaceAround, spaceEvenly
        DialogButton { text: "Zurück" }
        DialogButton { text: "Weiter"; grow: 1; maxWidth: 300 }
    }
}
Row { id: tags; width: 500; wrap: true; spacing: 8 }    // bricht in Zeilen um (QML Flow), wird so hoch wie sie
```

- **Length:** If a child has `grow` or `justify` is set, the layout keeps its height (`Column`) or width (`Row`) and distributes it: children without `grow` first get their sizes and the gaps; growing children share the remainder by weight (limited by `min…`/`max…`). Otherwise it grows to its content, as before.
- **Across:** Without `align`, `stretch` and `padding` on this axis, children keep their `x` (`Column`) or `y` (`Row`), as before. Otherwise the layout sets it; a layout without a width (height) then takes that of its widest (tallest) child.
- **Content follows:** If a layout resizes an element with `grow` or `stretch` (or a component instance), its descendants' properties run again (`size: parent.width - 4`, `place: right`), then nested layouts rearrange. In C++, `Node::resize()` sets the size and calls `onResized`.
- **`wrap`** (`Row` with a width only): Places children in lines, with `spacing` between lines too; `align` and `justify` apply to each line.

## Expressions

| | |
|---|---|
| Values | Numbers (internally `double`, as in QML), `"Text"` (with `\n`, `\t`, `\b`), colors `#rrggbb`/`#aarrggbb`, `true`/`false`, lists `[a, b]` |
| Operators | `+ - * / %`, `== != < > <= >=`, `&& \|\| !`, `a ? b : c`; `+` also concatenates strings |
| Names | IDs (defined earlier), `parent`, own geometry (`x`, `y`, `width`, `height`, `implicitWidth`, `implicitHeight`), loop variables, dialog or component `property` declarations (also as `root.<name>`) |
| Element values | `id.x`, `.y`, `.width`, `.height`, `.opacity`, `.rotation`, `.scale`, `.pressed`, `.implicitWidth`/`.implicitHeight` (Text; Image: image size; Html: height), `.contentHeight` (Scroll) |
| Records | `zeile.feld`, `liste[i].feld`, `root.zeile.feld` |
| Lists | `liste[i]`, `len(liste)`, e.g. `["", "ABC", "DEF"][i]` |
| Context | `theme.<name>`, `screen.width`, `screen.height`, `props.bool("ID")`, `props.int("ID")`, `props.string("ID")` |
| Functions | `tr("id", "Deutsch")`, `str(zahl)`, `replace(text, alt, neu)`, `lower(text)`, `upper(text)`, `len(liste\|text)`, `fontSize(px)` (`Context::fontSize`), `ceil`, `floor`, `round`, `abs`, `min`, `max`, `range(n)` |

Types are checked: a string where a number is required, or a field absent from a record, causes the build to fail.

## Repetition and conditions

```qml
repeat key, index in ["1", "2", "\b"] {
    Item { id: keyItem; x: index % 3 * 94; onTap: keyPressed(key) }
}
repeat i in range(6) { Rect { id: field; x: digitsX + i * 54 } }
repeat line, i in lines { CartRow { id: row; line: line } }

if key == "\b" { Svg { ... } } else { Text { ... } }
```

- **List:** A list in square brackets, `range(n)`, a `strings` property or a list of records.
- **IDs in a repetition** become vectors in the struct (`std::vector<aura::ui::RectNode*> field`). Outside the loop, access them with `field[0]`.
- **IDs under `if`** are `nullptr` in the struct while the branch is not built. Properties that wait for their own children (see below) must not reference children under `if`.
- **State:** If the list or condition depends on state, changing it rebuilds the block (see [Rebuilding](state-bindings.html#rebuilding-repeat-and-if)).
- **`lazy`:** `repeat p, i in photos lazy { … }` builds only visible entries, for long lists (thousands of photos on a phone):

  ```qml
  Scroll {
      id: grid
      size: parent.width, parent.height
      contentHeight: cells.height
      Grid {
          id: cells
          columns: 4
          spacing: 10
          cell: 240, 180                    // Pflicht: jede Zelle gleich groß
          width: parent.width
          repeat p, i in photos lazy {
              PhotoCell { id: cell; photo: p; stretch: true; onTap: pick(i) }
          }
      }
  }
  ```

  - **Where:** Directly in a `Grid` with `cell: width, height` (`columns: 1` for a list), inside a vertical `Scroll`; one element per entry, not inside another repetition.
  - **What gets built:** The rows in the visible area of the `Scroll`, plus one row before and after. Scrolling builds new entries and removes the others (deleted before the next frame; their bindings end). The `Grid` places each entry in the cell for its index and has the height of all rows, so `contentHeight: cells.height` remains correct.
  - **IDs:** `ui->cell` has one slot per list entry, `nullptr` for unbuilt entries. The application attaches additions (images) to newly built cells in `ui->onRebuilt` (cells without their additions), and discards pointers to removed cells.
  - **State:** As with any repetition: changing the list retains equal entries; other entries are rebuilt when visible.

## Property order

Each element follows this order, just like the earlier handwritten code:
1. Content (text, colors, font, …),
2. natural text size,
3. geometry (`x`, `y`, `width`, `height`, `size`, `place`, `contentHeight`) in source order.

This is why `height: back.y - 30 - y` works after `place: left 44, below editBox 30`.

**References to own children:** A property referring to an ID in its own subtree is applied only once that child is built, immediately afterwards. Later children already see the value:

```qml
component PriceRow : Rect {
    height: topicText.height + 12       // nach topicText
    Text { id: topicText; wrap: 290 }
    Text { place: right 15, centerY }   // sieht die Zeilenhöhe
}
```

## Pressed state

A property referring to `id.pressed` is reevaluated whenever that state changes. All descendants of `id` are reevaluated too, so anchors follow:

```qml
Item {
    id: keyItem
    onTap: keyPressed(key)
    Rect {
        id: face
        x: keyItem.pressed ? 5 : 0
        width: parent.width - (keyItem.pressed ? 8 : 0)
        Text { size: parent.width, parent.height }   // folgt dem Rect
    }
}
```

`id` itself is not rearranged, since that would reset a `pressSink`, for example. Components can also use `root.pressed` (key, footer button).

## Text input

```qml
TextField {
    id: code
    font: theme.textFont, 48
    placeholder: tr("Voucher_Code", "Gutscheincode")
    maxLength: 20
    focus: true
    size: 400, 60
    onChange: codeChanged           // signal codeChanged(string text)
    onAccept: redeem                // Enter
}
Rect { id: ok; onTap: redeem(code.text) }
Keyboard {
    layout: props.string("CurrentLanguage")          // "de", "EN", ...; unbekannt: DE
    place: centerX, bottom 20
    onOk: redeem(code.text)
    onCancel: close
}
```

- **Focus:** Keys go to the focused element, otherwise to `Application::onKey` as before. Tapping a `TextField` focuses it and places the caret at the tapped position. Tapping elsewhere (for example an on-screen keyboard key) leaves focus where it is. `focus: true` gives focus during construction; in C++, use `Node::focus()` / `Node::clearFocus()`. If the element is invisible or deleted, keys return to `onKey`.
- **Keys:** Characters insert at the caret (up to `maxLength`), Backspace and Delete remove characters, ←/→ move the caret, and Enter triggers `onAccept`. The field does not consume Escape, Tab, ↑/↓; they pass through to `onKey`. Input comes from the window keyboard, remote control (`Application::injectKey`) and `Keyboard` (`aura::ui::sendKey`).
- **`text`:** The initial value. If the binding reevaluates (another property on the element depends on state), typed text is retained; only a *changed* value replaces it (`bindValue`). The application reads text with `ui->code->value()` and sets it with `setValue()` (without `onChange`).
- **`id.text`** returns the current text of a `TextField` (or `Text`) in expressions, for example as a signal argument. Properties referring to a `TextField`'s `feld.text` run again after each keystroke (`visible: len(code.text) > 0`), as with a state property.
- **Signals:** `onChange` (after each typed change) and `onAccept` pass the text to a signal with a `string` parameter; a parameterless signal receives nothing. `onChange: changed(code.text)` also works. `Keyboard` has `onOk` and `onCancel`.
- **Keyboard:** Four rows of character keys with up to eight mappings (normal, Shift, Ctrl = `*#+@`, Alt = `éàó` and combinations). Shift, Ctrl and Alt remain active until tapped again. No images are needed: the keyboard draws its own symbols (Backspace, Enter, Shift, Space).
- **Blink:** The caret blinks every 500 ms (without an animation; the main loop redraws every 500 ms while idle anyway). For screenshots and tests, `aura::ui::TextFieldNode::blink = false` keeps it permanently visible.

## Controls

```qml
Column {
    padding: 24
    spacing: 16
    CheckBox { id: agb; text: tr("Agb", "AGB gelesen"); checked: accepted; onToggle: accept }    // signal accept(bool on)
    Slider { from: 0; to: 100; step: 5; value: volume; onChange: setVolume }                // signal setVolume(real v)
    ProgressBar { value: uploaded; to: total; stretch: true }
    Button { text: tr("Next", "Weiter"); color: theme.mainColor; enabled: agb.checked; onTap: next }
}
```

- **Selection:** `RadioButton { group: "format"; checked: format == "a"; onToggle: chooseA }` for a few visible choices; `ComboBox { options: formats; index: chosen; onSelect: choose }` (signal with `int`) for many.
- **State:** Like `text` on `TextField`, `checked`, `index` and `value` are initial values: the user's setting remains until the bound value changes (`bindChecked`, `bindValue`). `id.checked`, `id.index` (ComboBox) and `id.value` return the current setting in expressions; properties referring to them follow each user change (`enabled: agb.checked`, `text: str(volume.value)`).
- **Signals:** `onToggle` passes the new state to a signal with a `bool` parameter; a `Slider`'s `onChange` passes its value to a `real` parameter (only when changed, including dragging); parameterless signals receive nothing. `Button` uses the common `onTap`.
- **Appearance:** Neutral gray; applications style controls through properties or a component, e.g. `component MainButton : Button { color: theme.mainColor; font: theme.textFont, 24 }`. `Ripple`, `pressSink` and `glow` work as on any element.
- **`enabled`** affects every element and all its children: no taps or dragging.
- **How changes propagate:** `TextField`, `CheckBox`, `Switch` and `Slider` with an ID report every user change (`Node::onEdited`) under the binding key `@id`, before their own signal. This applies to individual elements, not IDs inside a `repeat`.

## Dialog and screen

```qml
Dialog OrderStatusDialog {
    property strings digits             // Argument der App (Zustand)
    const real digitsX: 140          // Konstante
    signal keyPressed(string key)
    size: 860, 600
    ...
}
```

- **`size: w, h`** (dialog only): The frame size. The application queries it using `Name::size(ctx, args)`; arguments may appear in it.
- **Screen:** Has no geometry. It can set its page's appearance (`color`, `border`, `radius`, `gradient`, `opacity`), e.g. `color: theme.white`.
- **`signal name` / `signal name(string key, int index)`:** Application events. Parameter types are `string`, `real`, `int`, `bool` and `color`; `real` and `int` arrive in C++ as `double`.
- **`property type name[: expression]`:** An application argument and mutable state with a setter, with an optional default value. It appears in `NameArgs`, passed to `size()` and `build()`.
- **`const type name: expression`:** A constant available throughout the dialog, with no setter or instance argument (e.g. `const int limit: 5`). This also applies to screens and components.
- **Defaults:** A `property` default is evaluated once during construction when no argument was supplied; it is not a persistent binding. Explicit values such as `0`, `false`, or `""` override it. Later changes use the setter.

Property types: `real`, `int`, `bool`, `string`, `color`, `image` (an application image, `sk_sp<SkImage>` in C++; may be empty), `strings` (a list of strings, `std::vector<std::string>` in C++), a record and `Datensatz[]` (a list).

## Components

```qml
component DialogButton : Rect {
    property string text        // Argument der Instanz (": wert": Standard)
    color: theme.mainColor
    radius: 4
    pressSink: 4, 250
    Text {
        text: root.text
        fit: true
        align: center, center
        size: parent.width, parent.height
    }
}

component LanguageButton : Rect {
    property string language
    signal select                       // eigenes Signal der Komponente
    onTap: select
    ...
}
```

- **Base type:** One of the built-in types.
- **`property`:** An instance argument, optionally with a default, and component state (setter in the struct).
- **Forwarding signals:** `onToggle: toggleContrast` on an instance emits the enclosing dialog's `toggleContrast` signal when the component emits `toggle`, with the same parameters. `onToggle: picked("x")` or `onEdit: edit(i)` passes custom arguments.
- **Instance order** (`DialogButton { id: noButton; ... }`): The component root's properties apply first, then the instance's (which win); only then are the component's children built. Thus a child's `size: parent.width, ...` uses the instance's size.
- **Children:** An instance has no children. Application images are passed through an `image` property; custom application drawing (cropped photos) goes in a named `Item` inside the component.
