
# tecs.ui


Build retained interfaces from ordinary Tecs entities.

`tecs.ui` supplies responsive layout, intrinsic sizing, clipping, scrolling,
hit testing, keyboard focus, and semantic interaction events for HUDs, menus,
inspectors, inventories, and in-world panels. A UI tree is an ECS hierarchy:
spawn entities, relate them with `ChildOf`, describe their boxes with
[`Style`](/modules/ui/#tecs.ui.Style), and let the plugin update only the layout and
GPU instance data that changed. Screen roots follow window resizing and pixel
density automatically.

<img src="/images/ui-compose.png" alt="The Compose demo scene combining an intrinsic image, a stretched rectangle, a fixed circle, and text" />

Use the module when several visual entities must size and position one another,
when content must scroll or clip, or when pointer and keyboard input should
target the same retained boxes that were drawn. It is not a DOM or a separate
widget renderer. Existing rectangles, circles, sprites, images, and text remain
the visible pieces, with their current materials, shaders, batching, and dirty
tracking. `tecs.ui` composes those pieces and gives them layout and interaction.

[`Style`](/modules/ui/#tecs.ui.Style) describes layout rather than appearance. Put a
drawing component on the same entity or on a child, and add `Paint(true)` when
its transform should stretch to the computed box. Drawing remains in the
existing instanced producers and shaders.

The [complete interface guide](/ui/) contains 37 copyable recipes for layout,
visual composition, intrinsic sizing, images, scrolling, interaction, focus,
and dirty-aware updates. The examples below provide a complete starting point
without leaving this API reference.

The plugin retains one native layout tree keyed by ECS entity ID. `ChildOf` is the
layout hierarchy as well as the transform hierarchy. The layout system writes
`Layout` only when the box changes and writes `RelativeTransform2D` only when
layout or scrolling changes its result. Query membership observers retain
structural invalidation, the ECS dirty-archetype set gates value updates, and
the native tree computes only dirty roots before returning a compact list of
changed boxes.

`px`, `%`, and `auto` dimension strings are parsed only when a `Style` enters or dirties the
retained tree. Unchanged frames skip layout computation, layout export, clip
reconstruction, and hit-rectangle reconstruction. A screen root installed
through `plugin(app)` follows the application's logical window size and pixel
density automatically. The plugin observes viewport changes on the world's
platform event bus, so unchanged frames do not poll the window.

[`Intrinsic`](/modules/ui/#tecs.ui.Intrinsic) copies cached text, sprite-region, or
custom leaf metrics into Taffy when the source component dirties. Wrapping text
uses a bounded retained convergence pass: Taffy chooses its width, SDL_ttf
measures that width exactly, and no native layout callback enters Lua.

`Paint(true)` centers and stretches a drawing leaf over its box. Keep
containers at unit scale and put their rectangle, circle, or image on an
absolute child with `Paint`: transform scale composes through `ChildOf`, so a
stretched container would also scale its descendants.

## Flex sizing and wrapping

<img src="/images/ui-flex.png" alt="The Flex demo scene comparing a fixed-width child with flex-grow children and a wrapped grid" />

The first row compares a fixed `88px` child with `flexGrow` weights one and
two. The cards below use `flexWrap = "wrap"`, so the screenshot shows both
remaining-space distribution and line wrapping against one retained parent.

## Absolute positioning and depth

<img src="/images/ui-overlay.png" alt="The Overlay demo scene with four absolutely positioned corner cards and a centered higher-depth circle" />

Absolute children anchor to their retained parent without consuming flex
space. Renderer depth remains a separate concern, which is why the center
circle and its label can sit above the four anchored cards.

## Scrolling and clipping

[`Scroll`](/modules/ui/#tecs.ui.Scroll) turns its entity's layout box into a
viewport. Its `x` and `y` move descendants, not the viewport entity itself.
Every nested viewport is intersected before the plugin assigns the existing
`tecs.gfx.Clip` component, so each drawable still carries one clip index in the
ordinary GPU instance. `firstClip` and `lastClip` reserve which of the
renderer’s 255 clip rectangles the UI owns.

Content extent includes negative, absolute, and nested descendants. Wheel
distance that a deepest viewport cannot consume passes to its ancestors.
`reveal` exposes a descendant programmatically, and `Scrollbar` derives a
composed thumb transform without adding a renderer primitive. Snapshots retain
scroll offsets and derive content extent again after load.

<img src="/images/ui-scroll.png" alt="The Scroll demo scene with a fixed clipped viewport, overflowing rows, and a composed block scrollbar" />

Screen roots use logical UI coordinates and multiply clips by `pixelDensity`.
World roots project viewport corners through the renderer camera. Clip
rectangles stay axis-aligned; rotating a scroll viewport therefore does not
rotate its clip.

## Interaction and navigation

Passing the application to [`plugin`](/modules/ui/#tecs.ui.plugin) supplies its
renderer and input, enabling clip-aware hit testing. Manual options remain
available to tests and tools. [`Interaction`](/modules/ui/#tecs.ui.Interaction) opts
in a layout box, and [`InteractionState`](/modules/ui/#tecs.ui.InteractionState)
reports hover, per-pointer capture, drag, and focus without replacing
application state.

The plugin emits one bubbling [`Event`](/modules/ui/#tecs.ui.Event) at entity
addresses. Mouse and touch identities capture independently; releasing over a
captured target emits `"click"` and then `"activate"`, while draggable targets
emit semantic drag events. Tab and Shift-Tab traverse visible focusable
controls and repeat while held, and Return or Space emits `"activate"` at the
focused control.
Programmatic focus, focus reveal, and modal focus scopes use the same state. A
`"wheel"` event begins at the deepest viewport under the pointer; setting
`event.consumed = true` prevents both ancestor delivery and default scrolling.

Hit priority uses the renderer's layer-depth calculation. A depth tie prefers
the deeper UI node, explicit `Interaction.order`, and then stable retained
insertion order. `Style.style.order` independently orders layout siblings.
Give overlapping controls explicit orders and distinct renderer depth when
both their input and drawing order are meaningful.

## Complete application

Keep entity composition separate from plugin setup. This first block creates
a full-window root, a centered panel, an ordinary GPU-rendered background, and
intrinsically sized text:

```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local ChildOf <const> = tecs.ecs.ChildOf
local RelativeTransform2D <const> = tecs.ecs.RelativeTransform2D
local Tint <const> = tecs.gfx.Tint
local Material <const> = tecs.gfx.Material
local Renderable2D <const> = tecs.gfx.Renderable2D

local function spawnInterface(
    world: tecs.World, app: tecs.Application, font: tecs.gfx.Font
)
    local root <const> = world:spawn(
        ui.Style({
            width = "100%",
            height = "100%",
            justifyContent = "center",
            alignItems = "center",
        }),
        ui.Root("screen"),
        tecs.Transform2D(0, 0, 0, 16)
    )

    local panel <const> = world:spawn(
        ui.Style({
            width = 420,
            height = 240,
            flexDirection = "column",
            padding = 20,
            gap = 12,
        }),
        RelativeTransform2D(),
        ChildOf(root)
    )

    world:spawn(
        ui.Style({position = "absolute", inset = 0}),
        ui.Paint(true),
        RelativeTransform2D(0, 0, 0),
        Tint(0.035, 0.055, 0.09, 0.96),
        Material(tecs.gfx.materials.id("rounded"), 0.05),
        Renderable2D(),
        ChildOf(panel)
    )

    world:spawn(
        ui.Style({maxWidth = "100%"}),
        ui.Intrinsic("text", {wrap = true}),
        RelativeTransform2D(0, 0, 1),
        Tint(0.94, 0.97, 1.0, 1.0),
        tecs.gfx.Text.new({
            text = "This text and panel are ordinary ECS entities.",
            font = font,
            size = 16,
        }),
        ChildOf(panel)
    )
end
```

The application plugin installs text and UI before a `Startup` system spawns
the retained entities. Passing `app` to `ui.plugin` supplies the renderer,
input, window size, and pixel density:

```teal
local function gamePlugin(world: tecs.World, app: tecs.Application)
    tecs.gfx.layers.configure(
        16,
        {
            sort = "z",
            screenSpace = true,
            unlit = true,
        }
    )
    world:addPlugin(tecs.gfx.textPlugin({renderer = app.renderer}))
    world:addPlugin(ui.plugin(app))
    world:addSystem({
        name = "game.SpawnInterface",
        phase = tecs.ecs.phases.Startup,
        run = function()
            local font <const> = tecs.gfx.newTTF({
                source = "fonts/JetBrainsMono-ExtraBold.ttf",
                name = "game-ui-16",
                size = 16,
                raster = "alpha",
            }):wait().value
            spawnInterface(world, app, font)
        end,
    })
end

return tecs.newApplication({plugin = gamePlugin})
```

The standalone [`docs/examples/ui.tl`](https://github.com/tecs-dev/tecs/blob/main/docs/examples/ui.tl)
application expands that setup into a scrollable, keyboard-navigable panel:

<img src="/images/ui-example.png" alt="The standalone retained UI example with a centered panel and scrollable controls" />

## Common recipes

### Stretch an ordinary rectangle over a layout box

Put the drawing component on an absolute child. `Paint(true)` copies the
computed width and height into its relative transform without scaling the
container's descendants:

```teal
local card <const> = world:spawn(
    ui.Style({width = 280, height = 120}),
    RelativeTransform2D(),
    ChildOf(panel)
)

world:spawn(
    ui.Style({position = "absolute", inset = 0}),
    ui.Paint(true),
    RelativeTransform2D(0, 0, 0),
    Tint(0.10, 0.17, 0.27, 1.0),
    Material(tecs.gfx.materials.id("rounded"), 0.08),
    Renderable2D(),
    ChildOf(card)
)
```

### Load an image and preserve its aspect ratio

The asset loader decodes pixels asynchronously. The renderer registers them
and returns the ordinary sprite consumed by `Intrinsic("image")`:

```teal
tecs.assets.loadImage(
    tecs.io.files.assetPath("images/portrait.png")
):map(function(image: tecs.assets.Image): tecs.gfx.Sprite
    return app.renderer.sprites:registerImage(image)
end):onSettle(function(loaded: tecs.Future<tecs.gfx.Sprite>)
    if loaded.status == "ready" then
        world:spawn(
            ui.Style({height = 64}),
            ui.Intrinsic("image"),
            ui.Paint(true),
            RelativeTransform2D(0, 0, 2),
            loaded.value,
            Tint(1, 1, 1, 1),
            Renderable2D(),
            ChildOf(panel)
        )
    end
end)
```

### Create a clipped scrolling list

`Scroll` belongs on the fixed viewport. Its larger child creates overflow,
and the scrollbar is an ordinary composed drawing entity rather than a Taffy
or renderer primitive:

```teal
local viewport <const> = world:spawn(
    ui.Style({width = "100%", height = 240}),
    ui.Scroll(),
    RelativeTransform2D(),
    ChildOf(panel)
)

local content <const> = world:spawn(
    ui.Style({
        width = "100%",
        height = 600,
        flexDirection = "column",
        gap = 8,
    }),
    RelativeTransform2D(),
    ChildOf(viewport)
)

world:spawn(
    ui.Scrollbar("vertical", 12, 4, 28),
    ui.Interaction({focusable = false, draggable = true, order = 100}),
    RelativeTransform2D(0, 0, 10),
    Tint(0.32, 0.82, 1.0, 1.0),
    Material(tecs.gfx.materials.id("rounded"), 0),
    Renderable2D(),
    ChildOf(viewport)
)
```

### Observe pointer and keyboard activation

`Interaction` makes the computed box selectable but draws nothing. Events
bubble through `ChildOf`, and Return or Space produces the same `activate`
event as a click:

```teal
local type uiTypes = require("tecs.ui")
local type UiEvent = uiTypes.Event

local button <const> = world:spawn(
    ui.Style({width = 180, height = 44}),
    ui.Interaction({tabIndex = 1}),
    RelativeTransform2D(),
    ChildOf(content)
)

world:observe(
    button,
    ui.Event,
    function(event: UiEvent)
        if event.kind == "activate" then
            print("activated via", event.source)
        end
    end
)
```

### Change a retained style

Use `getMut` only when a property actually changes. This dirties the retained
style once; unchanged frames do not parse the string or recompute the tree:

```teal
local style <const> = world:getMut(panel, ui.Style)
style.style.width = "100%"
style.style.maxWidth = "480px"
```

### Focus and reveal a control

Programmatic focus uses the same state and navigation order as Tab. Focusing a
clipped descendant reveals it through every ancestor viewport:

```teal
ui.focus(world, button)
ui.reveal(world, button, "center")

local focused <const> = ui.focused(world)
if focused == button then
    ui.blur(world)
end
```


## Module contents

### Types

| Type | Kind | Description |
| --- | --- | --- |
| [`Event`](/modules/ui/#tecs.ui.Event) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Event reports one semantic interaction at an entity address. |
| [`EventKind`](/modules/ui/#tecs.ui.EventKind) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Identifies one semantic UI interaction. |
| [`FocusScope`](/modules/ui/#tecs.ui.FocusScope) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | FocusScope marks an entity that may become the active navigation boundary. |
| [`Interaction`](/modules/ui/#tecs.ui.Interaction) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Interaction makes one computed layout box a UI input target. |
| [`InteractionSource`](/modules/ui/#tecs.ui.InteractionSource) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Identifies what initiated a semantic UI event. |
| [`InteractionState`](/modules/ui/#tecs.ui.InteractionState) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | InteractionState reports transient state derived by the UI plugin. |
| [`Intrinsic`](/modules/ui/#tecs.ui.Intrinsic) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Intrinsic supplies cached leaf metrics to layout without a Lua callback. |
| [`IntrinsicSource`](/modules/ui/#tecs.ui.IntrinsicSource) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Selects which leaf component owns natural dimensions. |
| [`Layout`](/modules/ui/#tecs.ui.Layout) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Layout reports the last box computed by retained layout. |
| [`Options`](/modules/ui/#tecs.ui.Options) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Options configures rendering, input, scrolling, and the owned clip-index range. |
| [`Overrides`](/modules/ui/#tecs.ui.Overrides) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Overrides changes scrolling and clip allocation without repeating an application's renderer and input. |
| [`Paint`](/modules/ui/#tecs.ui.Paint) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Paint controls how a drawing component consumes its layout box. |
| [`PointerType`](/modules/ui/#tecs.ui.PointerType) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Identifies the pointer device associated with an event. |
| [`RevealAlign`](/modules/ui/#tecs.ui.RevealAlign) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Selects where reveal places a descendant in each viewport. |
| [`Root`](/modules/ui/#tecs.ui.Root) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Root supplies the available space and coordinate mapping for one tree. |
| [`RootSizing`](/modules/ui/#tecs.ui.RootSizing) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Selects where a root receives its available dimensions. |
| [`RootSpace`](/modules/ui/#tecs.ui.RootSpace) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Selects whether a root uses screen or world coordinates. |
| [`Scroll`](/modules/ui/#tecs.ui.Scroll) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Scroll offsets descendants and clips them to this entity's layout box. |
| [`ScrollAxis`](/modules/ui/#tecs.ui.ScrollAxis) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | Selects the dimension controlled by a scrollbar thumb. |
| [`Scrollbar`](/modules/ui/#tecs.ui.Scrollbar) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Scrollbar derives one composed thumb transform from an ancestor viewport. |
| [`Style`](/modules/ui/#tecs.ui.Style) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Style stores the layout properties that place one UI entity. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`blur`](/modules/ui/#tecs.ui.blur) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Clears the world's focused interaction. |
| [`focus`](/modules/ui/#tecs.ui.focus) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Moves focus to one enabled, focusable interaction. |
| [`focused`](/modules/ui/#tecs.ui.focused) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the world's focused interaction. |
| [`plugin`](/modules/ui/#tecs.ui.plugin) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns a plugin that derives layout, transforms, clipping, and optional interaction from retained layout nodes. |
| [`popFocusScope`](/modules/ui/#tecs.ui.popFocusScope) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Pops the active navigation boundary and restores its saved focus. |
| [`pushFocusScope`](/modules/ui/#tecs.ui.pushFocusScope) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Pushes one modal navigation boundary and remembers the current focus. |
| [`reveal`](/modules/ui/#tecs.ui.reveal) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Scrolls every ancestor viewport enough to expose one descendant. |

### Values

| Value | Type | Description |
| --- | --- | --- |
| [`Node`](/modules/ui/#tecs.ui.Node) | [`Component`](/modules/ecs/#tecs.ecs.Component) | Read-only. Exposes the tag that marks an entity as participating in UI layout. |

## Types

<a id="tecs.ui.Event"></a>
### tecs.ui.Event <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Event` reports one semantic interaction at an entity address.
Read-only. Exposes the event type delivered to an interaction target
and its `ChildOf` ancestors.


```teal
record tecs.ui.Event is events.Event
    kind: EventKind
    target: integer
    currentTarget: integer
    x: number
    y: number
    button: integer
    pointerId: string
    pointerType: PointerType
    deltaX: number
    deltaY: number
    source: InteractionSource
    consumed: boolean

    init: function(
        event: Event,
        kind: EventKind,
        target: integer,
        x: number,
        y: number,
        button: integer,
        pointerId: string,
        pointerType: PointerType,
        deltaX: number,
        deltaY: number,
        source: InteractionSource
    )
end
```

#### Interfaces

| Interface |
| --- |
| [`events.Event`](/modules/events/#tecs.events.Event) |

#### Examples


Handles pointer and keyboard activation through the same observer.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local type uiTypes = require("tecs.ui")
local type UiEvent = uiTypes.Event
local world <const> = tecs.ecs.newWorld()
local button <const> = world:spawn(ui.Interaction({tabIndex = 1}))

world:observe(
    button,
    ui.Event,
    function(event: UiEvent)
        if event.kind == "activate" then
            print("activated via", event.source)
        end
    end
)
```

<a id="tecs.ui.Event.kind"></a>
#### tecs.ui.Event.kind <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports `"pointerEnter"`, `"pointerLeave"`,
`"pointerDown"`, `"pointerMove"`, `"pointerUp"`, `"click"`, `"wheel"`,
`"focus"`, `"blur"`, `"activate"`, `"dragStart"`, `"dragMove"`,
`"dragEnd"`, or `"dragCancel"`.


```teal
tecs.ui.Event.kind: EventKind
```

<a id="tecs.ui.Event.target"></a>
#### tecs.ui.Event.target <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Identifies the entity where dispatch began.


```teal
tecs.ui.Event.target: integer
```

<a id="tecs.ui.Event.currentTarget"></a>
#### tecs.ui.Event.currentTarget <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Identifies the entity whose observers are currently
receiving this bubbling event.


```teal
tecs.ui.Event.currentTarget: integer
```

<a id="tecs.ui.Event.x"></a>
#### tecs.ui.Event.x <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the pointer's horizontal window coordinate.


```teal
tecs.ui.Event.x: number
```

<a id="tecs.ui.Event.y"></a>
#### tecs.ui.Event.y <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the pointer's vertical window coordinate.


```teal
tecs.ui.Event.y: number
```

<a id="tecs.ui.Event.button"></a>
#### tecs.ui.Event.button <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the mouse button, or zero for touch and events that
have no button.


```teal
tecs.ui.Event.button: integer
```

<a id="tecs.ui.Event.pointerId"></a>
#### tecs.ui.Event.pointerId <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Identifies `"mouse"` or one opaque touch identity. Keyboard,
controller, and programmatic events use the empty string.


```teal
tecs.ui.Event.pointerId: string
```

<a id="tecs.ui.Event.pointerType"></a>
#### tecs.ui.Event.pointerType <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports `"mouse"`, `"touch"`, `"pen"`, or `"none"`.


```teal
tecs.ui.Event.pointerType: PointerType
```

<a id="tecs.ui.Event.deltaX"></a>
#### tecs.ui.Event.deltaX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports horizontal movement for pointer-move, drag, and
wheel events, or zero for other events.


```teal
tecs.ui.Event.deltaX: number
```

<a id="tecs.ui.Event.deltaY"></a>
#### tecs.ui.Event.deltaY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports vertical movement for pointer-move, drag, and wheel
events, or zero for other events.


```teal
tecs.ui.Event.deltaY: number
```

<a id="tecs.ui.Event.source"></a>
#### tecs.ui.Event.source <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports `"pointer"`, `"keyboard"`, `"controller"`, or
`"programmatic"` as the activation source.


```teal
tecs.ui.Event.source: InteractionSource
```

<a id="tecs.ui.Event.consumed"></a>
#### tecs.ui.Event.consumed <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. An observer may set this field to stop bubbling and
suppress the default wheel scroll.


```teal
tecs.ui.Event.consumed: boolean
```

<a id="tecs.ui.Event.init"></a>
#### tecs.ui.Event.init <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

```teal
function tecs.ui.Event.init(
    event: Event,
    kind: EventKind,
    target: integer,
    x: number,
    y: number,
    button: integer,
    pointerId: string,
    pointerType: PointerType,
    deltaX: number,
    deltaY: number,
    source: InteractionSource
)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `event` | [`Event`](/modules/ui/#tecs.ui.Event) |  |
| `kind` | [`EventKind`](/modules/ui/#tecs.ui.EventKind) |  |
| `target` | `integer` |  |
| `x` | `number` |  |
| `y` | `number` |  |
| `button` | `integer` |  |
| `pointerId` | `string` |  |
| `pointerType` | [`PointerType`](/modules/ui/#tecs.ui.PointerType) |  |
| `deltaX` | `number` |  |
| `deltaY` | `number` |  |
| `source` | [`InteractionSource`](/modules/ui/#tecs.ui.InteractionSource) |  |

##### Returns

None.

<a id="tecs.ui.Event.$meta"></a>

<a id="tecs.ui.EventKind"></a>
### tecs.ui.EventKind <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Identifies one semantic UI interaction.


```teal
enum tecs.ui.EventKind
    "activate"
    "blur"
    "click"
    "dragCancel"
    "dragEnd"
    "dragMove"
    "dragStart"
    "focus"
    "pointerDown"
    "pointerEnter"
    "pointerLeave"
    "pointerMove"
    "pointerUp"
    "wheel"
end
```

<a id="tecs.ui.FocusScope"></a>
### tecs.ui.FocusScope <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`FocusScope` marks an entity that may become the active navigation boundary.
Read-only. Exposes the marker used by `pushFocusScope`.


```teal
record tecs.ui.FocusScope is Component
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Marks a dialog as a boundary for modal pointer and keyboard navigation.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local root <const> = world:spawn(ui.Style({width = 1280, height = 720}))

local dialog <const> = world:spawn(
    ui.Style({width = 440, height = 260}),
    ui.FocusScope(),
    tecs.ecs.RelativeTransform2D(),
    tecs.ecs.ChildOf(root)
)
```

<a id="tecs.ui.Interaction"></a>
### tecs.ui.Interaction <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Interaction` makes one computed layout box a UI input target.
Read-only. Exposes the component that admits a layout box to hit
testing and keyboard navigation.


```teal
record tecs.ui.Interaction is Component
    enabled: boolean
    focusable: boolean
    tabIndex: integer
    order: integer
    draggable: boolean
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Creates a focusable control with an explicit Tab position.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local panel <const> = world:spawn(ui.Style({width = 480, height = 360}))

local button <const> = world:spawn(
    ui.Style({width = 180, height = 44}),
    ui.Interaction({tabIndex = 1, draggable = false}),
    tecs.ecs.RelativeTransform2D(),
    tecs.ecs.ChildOf(panel)
)
```

<a id="tecs.ui.Interaction.enabled"></a>
#### tecs.ui.Interaction.enabled <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller controls whether hit testing and navigation
can select this entity. The default is true.


```teal
tecs.ui.Interaction.enabled: boolean
```

<a id="tecs.ui.Interaction.focusable"></a>
#### tecs.ui.Interaction.focusable <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller controls whether clicking or keyboard
navigation can focus this entity. The default is true.


```teal
tecs.ui.Interaction.focusable: boolean
```

<a id="tecs.ui.Interaction.tabIndex"></a>
#### tecs.ui.Interaction.tabIndex <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller orders keyboard focus. Positive values come
first in ascending order, zero values follow in authorial order, and
negative values skip keyboard navigation.


```teal
tecs.ui.Interaction.tabIndex: integer
```

<a id="tecs.ui.Interaction.order"></a>
#### tecs.ui.Interaction.order <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies an authorial tie-breaker for hit
testing and focus when controls share renderer depth and tab index.
Higher values hit later; lower values focus first.


```teal
tecs.ui.Interaction.order: integer
```

<a id="tecs.ui.Interaction.draggable"></a>
#### tecs.ui.Interaction.draggable <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller enables semantic drag events after pointer
movement crosses the plugin's drag threshold.


```teal
tecs.ui.Interaction.draggable: boolean
```

<a id="tecs.ui.InteractionSource"></a>
### tecs.ui.InteractionSource <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Identifies what initiated a semantic UI event.


```teal
enum tecs.ui.InteractionSource
    "controller"
    "keyboard"
    "pointer"
    "programmatic"
end
```

<a id="tecs.ui.InteractionState"></a>
### tecs.ui.InteractionState <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`InteractionState` reports transient state derived by the UI plugin.
Read-only. Exposes transient hover, press, and focus state.


```teal
record tecs.ui.InteractionState is Component
    hovered: boolean
    pressed: boolean
    focused: boolean
    dragging: boolean
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Reads transient focus state without dirtying the retained control.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local button <const> = world:spawn(ui.Interaction({tabIndex = 1}))

local state <const> = world:get(button, ui.InteractionState)
if state ~= nil and state.focused then
    print("button has keyboard focus")
end
```

<a id="tecs.ui.InteractionState.hovered"></a>
#### tecs.ui.InteractionState.hovered <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports whether the pointer is over the
entity's visible, clipped box. Ordinary game code may read this field
to choose its visual state and must not write it.


```teal
tecs.ui.InteractionState.hovered: boolean
```

<a id="tecs.ui.InteractionState.pressed"></a>
#### tecs.ui.InteractionState.pressed <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports whether at least one pointer was
pressed on the entity and remains captured. Ordinary game code may read
this field and must not write it.


```teal
tecs.ui.InteractionState.pressed: boolean
```

<a id="tecs.ui.InteractionState.focused"></a>
#### tecs.ui.InteractionState.focused <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports whether keyboard activation currently
targets the entity. Ordinary game code may read this field and must not
write it.


```teal
tecs.ui.InteractionState.focused: boolean
```

<a id="tecs.ui.InteractionState.dragging"></a>
#### tecs.ui.InteractionState.dragging <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports whether at least one captured pointer
is dragging this entity. Ordinary game code may read this field and
must not write it.


```teal
tecs.ui.InteractionState.dragging: boolean
```

<a id="tecs.ui.Intrinsic"></a>
### tecs.ui.Intrinsic <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Intrinsic` supplies cached leaf metrics to layout without a Lua callback.
Read-only. Exposes cached custom, text, or image leaf measurement.


```teal
record tecs.ui.Intrinsic is Component
    source: IntrinsicSource
    width: number
    height: number
    minWidth: number
    scale: number
    wrap: boolean
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Selects natural text, image, or custom leaf measurements.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui

local wrappedText <const> = ui.Intrinsic("text", {wrap = true})
local imageSize <const> = ui.Intrinsic("image")
local customSize <const> = ui.Intrinsic(
    "custom", {width = 96, height = 28, minWidth = 48}
)
```

<a id="tecs.ui.Intrinsic.source"></a>
#### tecs.ui.Intrinsic.source <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller selects `"custom"`, `"text"`, or
`"image"`. Text reads `tecs.gfx.text.Text`; image reads
`tecs.components.Sprite`; custom reads the dimensions below.


```teal
tecs.ui.Intrinsic.source: IntrinsicSource
```

<a id="tecs.ui.Intrinsic.width"></a>
#### tecs.ui.Intrinsic.width <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the preferred custom width in
logical units. Text and image sources ignore this field.


```teal
tecs.ui.Intrinsic.width: number
```

<a id="tecs.ui.Intrinsic.height"></a>
#### tecs.ui.Intrinsic.height <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the preferred custom height in
logical units. Text and image sources ignore this field.


```teal
tecs.ui.Intrinsic.height: number
```

<a id="tecs.ui.Intrinsic.minWidth"></a>
#### tecs.ui.Intrinsic.minWidth <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the custom minimum-content width.
Zero uses `width`. Text derives its longest unbroken run and image
sources preserve their aspect ratio instead.


```teal
tecs.ui.Intrinsic.minWidth: number
```

<a id="tecs.ui.Intrinsic.scale"></a>
#### tecs.ui.Intrinsic.scale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller scales text or source-image units into UI
units. The default is one.


```teal
tecs.ui.Intrinsic.scale: number
```

<a id="tecs.ui.Intrinsic.wrap"></a>
#### tecs.ui.Intrinsic.wrap <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller lets Taffy choose a text line width and
lets the plugin write that result to `Text.wrapWidth`. Other sources
ignore this field.


```teal
tecs.ui.Intrinsic.wrap: boolean
```

<a id="tecs.ui.IntrinsicSource"></a>
### tecs.ui.IntrinsicSource <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Selects which leaf component owns natural dimensions.


```teal
enum tecs.ui.IntrinsicSource
    "custom"
    "image"
    "text"
end
```

<a id="tecs.ui.Layout"></a>
### tecs.ui.Layout <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Layout` reports the last box computed by retained layout.
Read-only. Exposes the component that reports a computed box without
becoming a drawing primitive.


```teal
record tecs.ui.Layout is Component
    x: number
    y: number
    width: number
    height: number
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Reads the final logical size after the UI layout phase.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local panel <const> = world:spawn(ui.Style({width = 480, height = 320}))

local box <const> = world:get(panel, ui.Layout)
if box ~= nil then
    print(("%.0f x %.0f"):format(box.width, box.height))
end
```

<a id="tecs.ui.Layout.x"></a>
#### tecs.ui.Layout.x <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. The engine reports the box's X offset from its parent.


```teal
tecs.ui.Layout.x: number
```

<a id="tecs.ui.Layout.y"></a>
#### tecs.ui.Layout.y <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. The engine reports the box's Y offset from its parent.


```teal
tecs.ui.Layout.y: number
```

<a id="tecs.ui.Layout.width"></a>
#### tecs.ui.Layout.width <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. The engine reports the box width in logical units.


```teal
tecs.ui.Layout.width: number
```

<a id="tecs.ui.Layout.height"></a>
#### tecs.ui.Layout.height <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. The engine reports the box height in logical units.


```teal
tecs.ui.Layout.height: number
```

<a id="tecs.ui.Options"></a>
### tecs.ui.Options <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Options` configures rendering, input, scrolling, and the owned
clip-index range.


```teal
record tecs.ui.Options
    renderer: Renderer
    firstClip: integer
    lastClip: integer
    input: any
    layer: any
    wheelStep: number
    dragThreshold: number
end
```

<a id="tecs.ui.Options.renderer"></a>
#### tecs.ui.Options.renderer <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the renderer whose camera and GPU
clip table the UI uses.


```teal
tecs.ui.Options.renderer: Renderer
```

<a id="tecs.ui.Options.firstClip"></a>
#### tecs.ui.Options.firstClip <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller reserves the first UI-owned clip index.
The default is one.


```teal
tecs.ui.Options.firstClip: integer
```

<a id="tecs.ui.Options.lastClip"></a>
#### tecs.ui.Options.lastClip <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller reserves the last UI-owned clip index.
The default is 255.


```teal
tecs.ui.Options.lastClip: integer
```

<a id="tecs.ui.Options.input"></a>
#### tecs.ui.Options.input <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the application's folded input to
enable hit testing, scrolling, focus, and activation. Omitting it
installs layout and clipping only.


```teal
tecs.ui.Options.input: any
```

<a id="tecs.ui.Options.layer"></a>
#### tecs.ui.Options.layer <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the input layer the UI may read.
Omitting it reads the base layer.


```teal
tecs.ui.Options.layer: any
```

<a id="tecs.ui.Options.wheelStep"></a>
#### tecs.ui.Options.wheelStep <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller sets logical scroll units per wheel unit.
The default is 40.


```teal
tecs.ui.Options.wheelStep: number
```

<a id="tecs.ui.Options.dragThreshold"></a>
#### tecs.ui.Options.dragThreshold <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller sets pointer movement in logical units
before a draggable capture emits `"dragStart"`. The default is four.


```teal
tecs.ui.Options.dragThreshold: number
```

<a id="tecs.ui.Overrides"></a>
### tecs.ui.Overrides <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Overrides` changes scrolling and clip allocation without repeating an
application's renderer and input.


```teal
record tecs.ui.Overrides
    firstClip: integer
    lastClip: integer
    layer: any
    wheelStep: number
    dragThreshold: number
end
```

<a id="tecs.ui.Overrides.firstClip"></a>
#### tecs.ui.Overrides.firstClip <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller reserves the first UI-owned clip index.
The default is one.


```teal
tecs.ui.Overrides.firstClip: integer
```

<a id="tecs.ui.Overrides.lastClip"></a>
#### tecs.ui.Overrides.lastClip <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller reserves the last UI-owned clip index.
The default is 255.


```teal
tecs.ui.Overrides.lastClip: integer
```

<a id="tecs.ui.Overrides.layer"></a>
#### tecs.ui.Overrides.layer <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the input layer the UI may read.
Omitting it reads the base layer.


```teal
tecs.ui.Overrides.layer: any
```

<a id="tecs.ui.Overrides.wheelStep"></a>
#### tecs.ui.Overrides.wheelStep <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller sets logical scroll units per wheel unit.
The default is 40.


```teal
tecs.ui.Overrides.wheelStep: number
```

<a id="tecs.ui.Overrides.dragThreshold"></a>
#### tecs.ui.Overrides.dragThreshold <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller sets pointer movement in logical units
before a draggable capture emits `"dragStart"`. The default is four.


```teal
tecs.ui.Overrides.dragThreshold: number
```

<a id="tecs.ui.Paint"></a>
### tecs.ui.Paint <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Paint` controls how a drawing component consumes its layout box.
Read-only. Exposes the component that optionally stretches an existing
primitive to its computed box.


```teal
record tecs.ui.Paint is Component
    stretch: boolean
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Stretches an ordinary rounded rectangle over its retained layout box.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local panel <const> = world:spawn(ui.Style({width = 480, height = 320}))

world:spawn(
    ui.Style({position = "absolute", inset = 0}),
    ui.Paint(true),
    tecs.ecs.RelativeTransform2D(0, 0, 1),
    tecs.gfx.Tint(0.1, 0.2, 0.3, 1),
    tecs.gfx.Material(tecs.gfx.materials.id("rounded"), 0.1),
    tecs.gfx.Renderable2D(),
    tecs.ecs.ChildOf(panel)
)
```

<a id="tecs.ui.Paint.stretch"></a>
#### tecs.ui.Paint.stretch <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller enables `stretch` to copy the layout width
and height into `RelativeTransform2D.scaleX` and `scaleY`.


```teal
tecs.ui.Paint.stretch: boolean
```

<a id="tecs.ui.PointerType"></a>
### tecs.ui.PointerType <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Identifies the pointer device associated with an event.


```teal
enum tecs.ui.PointerType
    "mouse"
    "none"
    "pen"
    "touch"
end
```

<a id="tecs.ui.RevealAlign"></a>
### tecs.ui.RevealAlign <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Selects where `reveal` places a descendant in each viewport.


```teal
enum tecs.ui.RevealAlign
    "center"
    "end"
    "nearest"
    "start"
end
```

<a id="tecs.ui.Root"></a>
### tecs.ui.Root <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Root` supplies the available space and coordinate mapping for one tree.
Read-only. Exposes the component that gives a retained UI tree its
available size and coordinate space.


```teal
record tecs.ui.Root is Component
    space: RootSpace
    width: number
    height: number
    pixelDensity: number
    sizing: RootSizing
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Creates a screen root that follows the application window automatically.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()

local root <const> = world:spawn(
    ui.Style({width = "100%", height = "100%"}),
    ui.Root("screen"),
    tecs.Transform2D(0, 0, 0, 16)
)
```

<a id="tecs.ui.Root.space"></a>
#### tecs.ui.Root.space <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller chooses `"screen"` for logical screen
coordinates or `"world"` for coordinates projected by the camera.


```teal
tecs.ui.Root.space: RootSpace
```

<a id="tecs.ui.Root.width"></a>
#### tecs.ui.Root.width <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the layout root width. An
application-backed screen root with automatic sizing follows the
logical window width; a camera-sized world root derives world units
from the physical viewport and camera zoom.


```teal
tecs.ui.Root.width: number
```

<a id="tecs.ui.Root.height"></a>
#### tecs.ui.Root.height <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the layout root height. An
application-backed screen root with automatic sizing follows the
logical window height; a camera-sized world root derives world units
from the physical viewport and camera zoom.


```teal
tecs.ui.Root.height: number
```

<a id="tecs.ui.Root.pixelDensity"></a>
#### tecs.ui.Root.pixelDensity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies target pixels per logical point.
The default is one. Automatic screen and camera-sized world roots
follow the window's current pixel density.


```teal
tecs.ui.Root.pixelDensity: number
```

<a id="tecs.ui.Root.sizing"></a>
#### tecs.ui.Root.sizing <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller selects `"auto"` for the standard behavior,
`"manual"` to preserve every authored field, or `"camera"` on a world
root to derive its extent and transform from the active camera.
`"auto"` follows the window for screen roots and is manual for world
roots.


```teal
tecs.ui.Root.sizing: RootSizing
```

<a id="tecs.ui.RootSizing"></a>
### tecs.ui.RootSizing <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Selects where a root receives its available dimensions.


```teal
enum tecs.ui.RootSizing
    "auto"
    "camera"
    "manual"
end
```

<a id="tecs.ui.RootSpace"></a>
### tecs.ui.RootSpace <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Selects whether a root uses screen or world coordinates.


```teal
enum tecs.ui.RootSpace
    "screen"
    "world"
end
```

<a id="tecs.ui.Scroll"></a>
### tecs.ui.Scroll <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Scroll` offsets descendants and clips them to this entity's layout box.
Read-only. Exposes the component that offsets descendants and clips
their existing GPU instances.


```teal
record tecs.ui.Scroll is Component
    x: number
    y: number
    contentWidth: number
    contentHeight: number
    contentX: number
    contentY: number
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Creates a fixed viewport and changes its retained vertical offset.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local panel <const> = world:spawn(ui.Style({width = 480, height = 360}))

local viewport <const> = world:spawn(
    ui.Style({width = "100%", height = 240}),
    ui.Scroll(),
    tecs.ecs.RelativeTransform2D(),
    tecs.ecs.ChildOf(panel)
)

local scroll <const> = world:getMut(viewport, ui.Scroll)
scroll.y = scroll.y + 80
```

<a id="tecs.ui.Scroll.x"></a>
#### tecs.ui.Scroll.x <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the horizontal scroll offset in
logical units.


```teal
tecs.ui.Scroll.x: number
```

<a id="tecs.ui.Scroll.y"></a>
#### tecs.ui.Scroll.y <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies the vertical scroll offset in
logical units.


```teal
tecs.ui.Scroll.y: number
```

<a id="tecs.ui.Scroll.contentWidth"></a>
#### tecs.ui.Scroll.contentWidth <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports the horizontal extent of retained
content, including nested and absolute descendants. Ordinary game code
should ignore this field.


```teal
tecs.ui.Scroll.contentWidth: number
```

<a id="tecs.ui.Scroll.contentHeight"></a>
#### tecs.ui.Scroll.contentHeight <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports the vertical extent of retained
content, including nested and absolute descendants. Ordinary game code
should ignore this field.


```teal
tecs.ui.Scroll.contentHeight: number
```

<a id="tecs.ui.Scroll.contentX"></a>
#### tecs.ui.Scroll.contentX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports the smallest horizontal content
coordinate, including negative-positioned descendants. Ordinary game
code should ignore this field.


```teal
tecs.ui.Scroll.contentX: number
```

<a id="tecs.ui.Scroll.contentY"></a>
#### tecs.ui.Scroll.contentY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. The engine reports the smallest vertical content
coordinate, including negative-positioned descendants. Ordinary game
code should ignore this field.


```teal
tecs.ui.Scroll.contentY: number
```

<a id="tecs.ui.ScrollAxis"></a>
### tecs.ui.ScrollAxis <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

Selects the dimension controlled by a scrollbar thumb.


```teal
enum tecs.ui.ScrollAxis
    "horizontal"
    "vertical"
end
```

<a id="tecs.ui.Scrollbar"></a>
### tecs.ui.Scrollbar <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Scrollbar` derives one composed thumb transform from an ancestor viewport.
Read-only. Exposes a composed scrollbar thumb driven by its parent
viewport.


```teal
record tecs.ui.Scrollbar is Component
    axis: ScrollAxis
    thickness: number
    inset: number
    minLength: number
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Composes a square vertical thumb from ordinary renderer components.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local viewport <const> = world:spawn(
    ui.Style({width = 480, height = 240}), ui.Scroll()
)

world:spawn(
    ui.Scrollbar("vertical", 12, 4, 28),
    ui.Interaction({focusable = false, draggable = true}),
    tecs.ecs.RelativeTransform2D(0, 0, 10),
    tecs.gfx.Tint(0.32, 0.82, 1, 1),
    tecs.gfx.Material(tecs.gfx.materials.id("rounded"), 0),
    tecs.gfx.Renderable2D(),
    tecs.ecs.ChildOf(viewport)
)
```

<a id="tecs.ui.Scrollbar.axis"></a>
#### tecs.ui.Scrollbar.axis <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller selects `"horizontal"` or `"vertical"`.


```teal
tecs.ui.Scrollbar.axis: ScrollAxis
```

<a id="tecs.ui.Scrollbar.thickness"></a>
#### tecs.ui.Scrollbar.thickness <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller sets the thumb thickness in logical units.


```teal
tecs.ui.Scrollbar.thickness: number
```

<a id="tecs.ui.Scrollbar.inset"></a>
#### tecs.ui.Scrollbar.inset <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller leaves this much room inside each end of
the viewport track.


```teal
tecs.ui.Scrollbar.inset: number
```

<a id="tecs.ui.Scrollbar.minLength"></a>
#### tecs.ui.Scrollbar.minLength <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller sets the smallest thumb length in logical
units.


```teal
tecs.ui.Scrollbar.minLength: number
```

<a id="tecs.ui.Style"></a>
### tecs.ui.Style <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Style` stores the layout properties that place one UI entity.
Read-only. Exposes the component that supplies retained layout properties
and adds `Layout` and `tecs.gfx.Clip`.


```teal
record tecs.ui.Style is Component
    style: {string: any}
end
```

#### Interfaces

| Interface |
| --- |
| [`Component`](/modules/ecs/#tecs.ecs.Component) |

#### Examples


Builds a column whose width follows its parent up to 480 logical pixels.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local root <const> = world:spawn(ui.Style({width = 1280, height = 720}))

local panel <const> = world:spawn(
    ui.Style({
        width = "100%",
        maxWidth = 480,
        flexDirection = "column",
        padding = 20,
        gap = 12,
    }),
    tecs.ecs.RelativeTransform2D(),
    tecs.ecs.ChildOf(root)
)
```

<a id="tecs.ui.Style.style"></a>
#### tecs.ui.Style.style <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. The caller supplies a table and marks `Style` dirty
after mutating it. Numbers and `"24px"` strings use logical UI pixels,
`"50%"` is parent-relative, and `"auto"` selects automatic sizing.
Invalid strings raise when the plugin synchronizes the style. The
older `{value=number, unit="percent"}` and `unit="points"` table forms
remain supported.

Supported keys are `display`, `position`, `flexDirection`, `flexWrap`,
`justifyContent`, `alignItems`, `alignContent`, `flexGrow`,
`flexShrink`, `flexBasis`, `width`, `height`, `minWidth`, `minHeight`,
`maxWidth`, `maxHeight`, `margin`, `padding`, `border`, `gap`,
`rowGap`, `inset`, and the Tecs-only integer `order` used to retain
authorial sibling order. Edge values accept one dimension or a table
with `left`, `right`, `top`, and `bottom`.


```teal
tecs.ui.Style.style: {string: any}
```

## Functions

<a id="tecs.ui.blur"></a>
### tecs.ui.blur <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Clears the world's focused interaction.


```teal
function tecs.ui.blur(world: World): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | The caller supplies a world with the UI plugin. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns false when the plugin is missing. |

#### Examples


Clears the current keyboard focus when one exists.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()

if ui.focused(world) ~= nil then
    ui.blur(world)
end
```

<a id="tecs.ui.focus"></a>
### tecs.ui.focus <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Moves focus to one enabled, focusable interaction.


```teal
function tecs.ui.focus(
    world: World, entity: integer
): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | The caller supplies a world with the UI plugin. |
| `entity` | `integer` | The caller supplies the interaction to focus. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns false when the plugin is missing or the entity cannot receive focus. |

#### Examples


Moves keyboard focus to an enabled, focusable control.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local saveButton <const> = world:spawn(ui.Interaction({tabIndex = 1}))

if not ui.focus(world, saveButton) then
    print("save button cannot receive focus")
end
```

<a id="tecs.ui.focused"></a>
### tecs.ui.focused <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the world's focused interaction.


```teal
function tecs.ui.focused(world: World): integer
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | The caller supplies a world with or without the UI plugin. |

#### Returns

| Type | Description |
| --- | --- |
| `integer` | Returns the focused entity, or nil when nothing is focused. |

#### Examples


Finds the interaction that currently owns keyboard focus.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()

local current <const> = ui.focused(world)
if current ~= nil then
    print("focused entity", current)
end
```

<a id="tecs.ui.plugin"></a>
### tecs.ui.plugin <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns a plugin that derives layout, transforms, clipping, and
optional interaction from retained layout nodes.

The plugin owns every renderer clip index from `firstClip` through
`lastClip`, inclusive. Reserve a smaller range when another system also
calls `renderer.sprites:setClipRegion`. Exhausting the range raises rather than
drawing unclipped content.

Passing an [`Application`](/modules/Application/) supplies its
renderer, input, and window. The plugin samples the viewport initially,
then observes platform resize, pixel-size, display, and display-scale
events at world address zero. It coalesces them and refreshes automatic
roots before layout without polling the window on unchanged frames. The
optional second argument overrides clip allocation, the input layer,
and wheel distance. Passing `Options` directly preserves manual wiring
for tests, tools, and worlds not owned by an application. When input is
present, the plugin reads its configured layer in `PreUpdate`. It uses
the hit geometry derived after the previous frame's layout and
clipping, which is the geometry the player was shown when the input
arrived.



```teal
function tecs.ui.plugin(
    source: any, overrides: Overrides
): function(World)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `source` | `any` | The caller supplies an application or manually wired options containing a renderer. |
| `overrides` | [`Overrides`](/modules/ui/#tecs.ui.Overrides) | The caller may override source defaults without repeating its renderer and input. |

#### Returns

| Type | Description |
| --- | --- |
| `function(`[`World`](/modules/ecs/#tecs.World)`)` | Returns a plugin for `world:addPlugin`. Each world receives a separate retained tree and clip allocator. |

#### Examples


Installs UI with explicit dependencies in a test or manually constructed world.


```teal
local tecs <const> = require("tecs")
local type Renderer = require("tecs.Renderer")
local type inputTypes = require("tecs.input")

local function installManualUi(
    world: tecs.World, renderer: Renderer, input: inputTypes.Input
)
    world:addPlugin(tecs.ui.plugin({
        renderer = renderer,
        input = input,
        firstClip = 32,
        lastClip = 63,
    }))
end
```


Installs UI from an application so roots follow its window and input.


```teal
local tecs <const> = require("tecs")

local function gamePlugin(world: tecs.World, app: tecs.Application)
    world:addPlugin(tecs.ui.plugin(
        app,
        {
            wheelStep = 36,
            dragThreshold = 4,
        }
    ))
end
```

<a id="tecs.ui.popFocusScope"></a>
### tecs.ui.popFocusScope <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Pops the active navigation boundary and restores its saved focus.


```teal
function tecs.ui.popFocusScope(world: World, scope: integer): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | The caller supplies a world with the UI plugin. |
| `scope` | `integer` | The caller may require a particular active scope. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns false when the plugin is missing or the requested scope is not active. |

#### Examples


Closes the active modal boundary and restores its saved focus.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local dialog <const> = world:spawn(ui.FocusScope())

if not ui.popFocusScope(world, dialog) then
    print("dialog is not the active focus scope")
end
```

<a id="tecs.ui.pushFocusScope"></a>
### tecs.ui.pushFocusScope <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Pushes one modal navigation boundary and remembers the current focus.


```teal
function tecs.ui.pushFocusScope(world: World, scope: integer): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | The caller supplies a world with the UI plugin. |
| `scope` | `integer` | The caller supplies an entity carrying `FocusScope`. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns false when the plugin or scope is missing. |

#### Examples


Activates a modal scope and focuses its first control.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local dialog <const> = world:spawn(ui.FocusScope())
local firstDialogControl <const> = world:spawn(
    ui.Interaction({tabIndex = 1}), tecs.ecs.ChildOf(dialog)
)

if ui.pushFocusScope(world, dialog) then
    ui.focus(world, firstDialogControl)
end
```

<a id="tecs.ui.reveal"></a>
### tecs.ui.reveal <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Scrolls every ancestor viewport enough to expose one descendant.


```teal
function tecs.ui.reveal(
    world: World, entity: integer, align: RevealAlign
): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | The caller supplies a world with the UI plugin. |
| `entity` | `integer` | The caller supplies a retained UI descendant. |
| `align` | [`RevealAlign`](/modules/ui/#tecs.ui.RevealAlign) | The caller selects `"nearest"`, `"start"`, `"center"`, or `"end"`; omission selects `"nearest"`. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns false when the plugin or layout is missing. |


#### Examples


Centers a selected row through every retained ancestor viewport.


```teal
local tecs <const> = require("tecs")
local ui <const> = tecs.ui
local world <const> = tecs.ecs.newWorld()
local viewport <const> = world:spawn(
    ui.Style({height = 240}), ui.Scroll()
)
local selectedRow <const> = world:spawn(
    ui.Style({height = 44}),
    tecs.ecs.RelativeTransform2D(),
    tecs.ecs.ChildOf(viewport)
)

if not ui.reveal(world, selectedRow, "center") then
    print("selected row has no retained layout")
end
```

## Values

<a id="tecs.ui.Node"></a>
### tecs.ui.Node <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Exposes the tag that marks an entity as participating in UI
layout. `Style`, `Root`, and `Scroll` add it automatically.


```teal
tecs.ui.Node: Component
```