> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/ui/uidoc-quick-start.md).

# UIDoc Quick Start

***

UIDoc lets you describe game UI with HTML and CSS, then supply its changing data and handle its events in CSL. It is useful for menus, inventories, skill trees, and other structured screen-space interfaces.

UIDoc is browser-like, but it is not an embedded web browser. Use the supported subset described in [UIDoc HTML and CSS Support](/ui/uidoc-html-css-support.md).

## Create a UIDoc asset

Create a folder under your game's `res` directory. The folder name must end in `.uidoc` and contain `index.html` and `index.css`:

```
res/
└── UI/
    └── settings.uidoc/
        ├── index.html
        └── index.css
```

Asset paths used by CSL are relative to `res`, so this document is loaded as `UI/settings.uidoc`.

## Generate it from Figma

The All Out UIDoc Figma plugin can generate the UIDoc folder and its CSL binding code for you. Use a freeform 16:9 Component as the screen root, native Text/Boolean component properties for dynamic fields, interactive component instances for actions, and native Slot properties for lists. Figma constraints, Auto Layout, and aspect-ratio locks become responsive UIDoc layout directly.

For a generated asset such as `ui/shop.uidoc`, the editor also maintains `scripts/generated/uidoc/ui/shop.csl`. Its typed wrapper supplies `default_data`, `decode_event`, and `draw`, so game code does not need to repeat the low-level bindings shown later in this guide:

```csl
data := Ui_Shop_UIDoc.default_data();
data.title = "Store";
Ui_Shop_UIDoc.draw(data, player, shop_event);
```

The generated code uses the same public `UI.uidoc_*` calls documented below; it does not add a separate runtime system. Select the generated UIDoc asset in the editor to preview Figma defaults and Slot samples, then resize the scene view to test responsiveness.

## Write the document

In `index.html`:

```html
<div class="screen">
  <div class="panel">
    <span class="title">Settings</span>
    <span class="message">{{message}}</span>
    <button class="close" data-on-click="event:close">Close</button>
  </div>
</div>
```

`{{message}}` is a text binding. `data-on-click` sends a `UIDoc_Event` to CSL.

In `index.css`:

```css
.screen {
  position: fixed;
  inset: env(safe-area-inset-top) env(safe-area-inset-right)
         env(safe-area-inset-bottom) env(safe-area-inset-left);
  display: flex;
  align-items: center;
  justify-content: center;
}

.panel {
  display: flex;
  flex-direction: column;
  width: 420px;
  padding: 24px;
  gap: 16px;
  color: white;
  background: #172033;
  border: 2px solid #52627d;
  border-radius: 12px;
  box-shadow: 0 12px 28px 0 #00000066;
}

.title {
  font-size: 32px;
  text-align: center;
}

.close {
  height: 48px;
  background: #3559a8;
  border-radius: 8px;
}

.close:hover {
  background: #4770ca;
}

.close:pressed {
  background: #29447f;
}
```

Safe-area insets keep full-screen UI clear of device cutouts and the game top-bar area.

## Bind and draw it from CSL

Draw player UI from that player's `ao_late_update` call stack. Clear and rebuild the UIDoc bindings before drawing the document each frame:

```csl
settings_uidoc_event :: proc(event: UIDoc_Event, userdata: Object) {
    player := userdata.(Player);
    if player == null return;

    if event.handler == "event:close" {
        player.settings_open = false;
    }
}

draw_settings :: proc(player: Player) {
    UI.uidoc_clear_bindings();
    UI.uidoc_bind_text("message", "Changes are saved automatically.");

    document := get_asset(UIDoc_Asset, "UI/settings.uidoc");
    if document == null return;

    UI.uidoc(document, false, player, settings_uidoc_event);
}

Player :: class : Player_Base {
    settings_open: bool;

    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() && this.settings_open {
            draw_settings(this);
        }
    }
}
```

The callback receives the exact handler string from `data-on-click`. An optional `data-key` is returned as `event.key`, which is useful when many repeated buttons share one handler.

Available top-level bindings are:

```csl
UI.uidoc_bind_bool("visible", true);
UI.uidoc_bind_text("name", player.name);
UI.uidoc_bind_float("health", player.health);
```

## Conditions, classes, and lists

Use `data-if` to include a node only while a boolean binding is true. Class attributes can also contain bindings:

```html
<div class="notice {{noticeClass}}" data-if="showNotice">
  {{noticeText}}
</div>
```

Use `data-for` for repeated data:

```html
<div class="inventory">
  <button
    class="item {{item.rarityClass}}"
    data-for="item in items"
    data-for-key="{{item.id}}"
    data-key="{{item.id}}"
    data-on-click="event:item">
    {{item.name}}
  </button>
</div>
```

Build that list in CSL before calling `UI.uidoc`:

```csl
UI.uidoc_begin_list("items");
for item: player.inventory {
    UI.uidoc_list_item(item.id);
    UI.uidoc_list_bind_text("id", item.id);
    UI.uidoc_list_bind_text("name", item.name);
    UI.uidoc_list_bind_text("rarityClass", item.rarity_class);
}
UI.uidoc_end_list();
```

Use stable, unique list-item keys. They preserve the identity of interaction, input, and scroll state when a list changes.

## Inputs

Bind an input with `data-bind-value`:

```html
<input
  id="pet-name"
  class="name-input"
  placeholder="Pet name"
  data-bind-value="petName"
  data-on-click="event:name-input">
```

Read its current value after the document has been drawn:

```csl
UI.uidoc_bind_text("petName", player.pet_name);
UI.uidoc(document, false, player, settings_uidoc_event);
player.pet_name = UI.uidoc_text_value(document, "pet-name", player.pet_name);
```

`UI.uidoc_text_value` looks up the input by its `id` or `data-key`, then returns the current value of its `data-bind-value` binding.

## Scrolling and zooming

Scrolling is enabled with CSS overflow. Both axes can be enabled on the same viewport:

```html
<div class="viewport" data-scroll-zoom="zoom">
  <div class="canvas">
    <button
      class="node"
      data-for="node in nodes"
      data-key="{{node.id}}"
      data-style-transform-x="node.x"
      data-style-transform-y="node.y"
      data-on-click="event:node">
      {{node.name}}
    </button>
  </div>
</div>
```

```css
.viewport {
  width: 100%;
  height: 100%;
  overflow-x: auto;
  overflow-y: auto;
}

.canvas {
  position: relative;
  width: 1600px;
  height: 1000px;
}

.node {
  position: absolute;
  width: 160px;
  height: 64px;
}
```

Bind `zoom` with `UI.uidoc_bind_float`. `data-scroll-zoom` scales the content's positions, sizes, text, images, hit regions, and scroll extents around the viewport center. Keep fixed zoom controls outside the zoomed viewport.

{% hint style="warning" %}
Numeric style attributes use a direct binding expression, such as `data-style-transform-x="node.x"` or `data-scroll-zoom="zoom"`. Do not put those expressions inside `{{...}}`.
{% endhint %}

For a normal scroll panel, omit `data-scroll-zoom`. Dragging pans every enabled axis; the wheel scrolls vertically, or horizontally when only horizontal overflow is enabled.

## Common mistakes

* Load the document using its path relative to `res`, including the `.uidoc` suffix.
* Draw it from local-player UI code under `is_local_or_server()`.
* Call `UI.uidoc_clear_bindings()` and provide the current bindings before each draw.
* Give repeated interactive nodes stable `data-for-key` and `data-key` values.
* Give scroll content a real size. Transforms alone should not be used as its only layout size.
* Use UIDoc's own responsive layout and zoom behavior instead of applying a second manual UI scale in CSL.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.allout.game/ui/uidoc-quick-start.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
