For the complete documentation index, see llms.txt. This page is also available as Markdown.

UIDoc Quick Start

Create browser-like game UI with UIDoc HTML and CSS assets, CSL bindings, lists, and events.


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.

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.

Edit visually and generate CSL

Select a UIDoc asset in the All Out editor to open its visual authoring view. You can select and drag elements on the artboard, resize them, reorder or reparent them in Layers, and edit common HTML and CSS properties in the inspector. The preview is rendered by the UIDoc runtime itself, so it uses the same supported layout and rendering behavior as the game.

The Interface inspector infers dynamic fields, repeated lists, inputs, and actions from the document markup. Saving an asset such as ui/shop.uidoc also maintains scripts/generated/uidoc/ui/shop.csl. Its typed wrapper supplies default_data, decode_event, input readers, and draw, so game code does not need to repeat the low-level bindings shown later in this guide:

data := UiShop_UIDoc.default_data();
data.title = "Store";
UiShop_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 or change the UIDoc runtime API.

When using the automation tools, uidoc_create_asset returns its template version and the exact generated index.html/index.css contents. UIDoc assets do not hot reload in a running game, so restart the game after changing those files. Use uidoc_diagnostics for compile and viewport checks, then uidoc_runtime_inspect to inspect live nodes, bindings, styles, click payloads, and rectangles.

Write the document

In index.html:

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

In index.css:

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:

The callback receives the exact handler string from data-on-click. A non-repeated control can supply a static data-key for event.key; repeated controls use their resolved data-for-key or list-item key.

userdata is passed only to that callback; it does not identify the document. Each UIDoc asset may be drawn once per simulation submission. When a document stops being drawn, its current activation closes; drawing it again automatically assigns a new activation generation so stale replicated layouts cannot attach to the reopened document. At most four different UIDoc assets may be active at once.

Available top-level bindings are:

Conditions, paint bindings, and lists

Use data-if to include a node only while a top-level boolean binding is true. An optional leading ! inverts it. data-if does not resolve list-local expressions such as item.visible; repeated visual state should use a list-local paint binding instead, while repeated structural or interactive visibility should be decided when building the CSL list. For other dynamic visual state, keep classes static and bind a supported color or opacity:

Bind noticeColor with UI.uidoc_bind_text using a supported CSS color string. Class attributes are compiled as static class tokens and do not support {{...}} interpolation.

Use data-for for repeated data:

Build that list in CSL before calling UI.uidoc:

Use a stable, unique direct expression such as data-for-key="item.id" for each repeated item. It is returned as event.key. Give UI.uidoc_list_item(...) the same stable value so interaction, input, and scroll identity survive list changes. A static data-key names the control's role; UIDoc combines that role with each enclosing list item's bound key and current index for its registered runtime identity.

For the example above, a live test name contains a suffix such as inventory-item#potion-42:7/__widget. Inspect the exact name in client_ui_tree, then target that instance with the full name or a sufficiently precise suffix such as Test.click_button("inventory-item#potion-42:7"). A role-only lookup such as Test.click_button("inventory-item") is ambiguous when several rows are visible. data-on-click and visible text are not test selectors. Nested lists append one #key:index pair per loop.

Inputs

Bind an input with data-bind-value:

Read its current value after the document has been drawn:

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:

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.

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.

  • Draw each UIDoc asset once per update. Use userdata only as callback data.

  • Use data-if only with top-level booleans. Bind list-local visual state through data-style-opacity/data-style-color, or omit structural/interactive rows from the bound list.

  • Give repeated nodes a stable direct data-for-key expression; keep data-key static when naming a control role.

  • Treat CSS support as property/value specific. auto and none are accepted only where the generated reference lists them, side borders such as border-bottom are unsupported, and calc(...) is limited to documented length fields with simple addition/subtraction.

  • 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.

Last updated