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

UIDoc HTML and CSS Support

The HTML, CSS, responsive layout, interaction, scrolling, and binding features supported by UIDoc, plus important browser features it omits.


UIDoc intentionally implements a practical browser-like subset for game UI. It compiles assets ahead of time and renders them through the engine; it does not run a browser, JavaScript, or a live DOM.

For a working first screen, start with UIDoc Quick Start.

The generated repository reference, docs/uidoc_supported_css.md, is the exhaustive property/value list. It is emitted by UIDoc::debug_supported_css_reference() and checked by the UIDoc tests. This page explains that contract at an authoring level.

Unknown declarations, rejected values, unsupported selectors, malformed HTML, and unsupported media queries are compile errors. The parser may continue to collect diagnostics, but the asset is invalid until the errors are fixed. Unsupported CSS block at-rules are silently skipped; do not rely on browser at-rule behavior.

UIDoc HTML and CSS do not hot reload in a running game. Restart the game after asset changes. For runtime debugging, uidoc_runtime_inspect exposes live DOM nodes, current bindings, computed styles, event handler/key payloads, and rectangles. Its screen_rect, client_ui_tree, and client_click all use bottom-left-origin engine viewport coordinates; screenshot pixels use a top-left origin.

The compile tool includes structured UIDoc diagnostics inline when asset preprocessing fails. uidoc_diagnostics with viewportMatrix: true returns aggregate pass counts and groups the same issue category for the same source node across profiles. unique_issue_count is that grouped node/category count; issue_occurrence_count is the raw total across all viewport/scale entries. Set viewportMatrixVerbose: true only when every failing entry is useful. Distinct source nodes are not coalesced even when they share a selector or CSS source line, so a shared-rule problem may still appear as several groups; fix the rule once and rerun diagnostics.

Two common authoring warnings need context:

  • dynamic_text_intrinsic_layout means frequently changing text can change an intrinsically sized box and invalidate cached layout. Add data-text-reserve with the widest expected, localization-aware sample, or give the element a definite width and height. It is not a duplicate-text or rendering error.

  • image_aspect_drift means an image has independent width and height with object-fit: fill. Use contain, cover, or aspect-ratio for ordinary art. A purpose-built progress fill that is deliberately stretched into its track is a valid exception; verify that case visually.

HTML and UIDoc attributes

Element names are accepted as generic layout nodes. These elements have specialized runtime behavior:

Element
Behavior

span

Inline text content

img

Engine asset image loaded from src

button

Pointer interaction and click events

input

Editable text bound with data-bind-value

Non-empty text content creates text nodes. Use {{name}} for a top-level text binding and {{item.name}} inside a repeated list. Image src values also support this interpolation. Class names and data-key values are static.

Attribute
Purpose

id, class, style

CSS matching and inline style

src, width, height

Image source and dimensions

value, placeholder, disabled

Input and control state

data-if="binding"

Include a subtree while a top-level boolean binding is true; optional ! inverts it

data-for="item in items"

Repeat a subtree for a CSL-bound list

data-for-key="item.id"

Resolve the repeated click's event.key from a direct expression

data-key="role"

Give a node a static interaction/input role

data-bind-value="name"

Connect an input to a UIDoc text value

data-text-reserve="00:00"

Measure a stable literal sample while painting live bound text

data-on-click="event:name"

Send a click event to the CSL callback; event.handler receives the exact full value, including event:

data-style-transform-x/y

Bind numeric translation without relayout

data-style-transform-scale

Bind numeric visual scale

data-style-opacity

Bind numeric opacity

data-style-color

Bind a supported CSS color string without changing structure

data-style-image-fill-amount

Bind an image fill amount

data-scroll-zoom

Bind complete content zoom on a scroll viewport

Binding-expression attributes contain direct expressions, for example data-for-key="item.id", data-style-opacity="item.opacity", and data-scroll-zoom="zoom". Do not put those expressions inside {{...}}. Mustache interpolation is limited to text and image src values.

data-if is narrower than the other list-aware binding attributes: it looks up a top-level boolean and does not resolve a repeated alias such as item.visible. For non-interactive visual state in a repeated subtree, bind item.opacity or item.color. Opacity does not disable hit testing, so filter structural or interactive rows while building the CSL list rather than leaving an invisible button in place.

Repeated control identity and tests

data-for-key determines the repeated click's event.key. The runtime control identity is separate: it starts with static data-key (then HTML id, then an anonymous tag fallback) and appends #key:index for every enclosing loop. Here key is the value supplied to UI.uidoc_list_item(...); normally it should be the same stable ID resolved by data-for-key. A repeated button can therefore appear in client_ui_tree as:

Use client_ui_tree to copy the exact live identity. Test.click_button accepts the full name or a suffix and treats the final /__widget as optional, so Test.click_button("seed-cell#seed-42:7") selects that instance when the suffix is unique. A role-only query can choose an arbitrary closest repeated instance. Nested lists append multiple #key:index pairs. Handler strings and visible labels are not identity selectors.

Raw style and script blocks in the HTML are compile errors. Put authored CSS in index.css; there is no JavaScript runtime.

CSS selectors and cascade

Supported selectors include:

  • Element names, .class, #id, and *.

  • Descendant and direct-child (>) combinators.

  • Comma-separated selector lists.

  • :hover, :focus, :active, :pressed, :mouse-down, :dragging, :scroll-state, and :disabled.

UIDoc uses normal id/class/type specificity and source order as the tie-breaker. Inline style is applied last. Paint order uses z-index, then document order.

Layout-affecting declarations inside interaction pseudo-state rules are ignored and diagnosed. Hovering a button can safely change its color or opacity, but should not change its size or surrounding layout.

Unsupported selector forms include sibling combinators, attribute selectors, pseudo-elements, :not(), :nth-*, :is(), :where(), and :has().

CSS layout

UIDoc supports display: block, display: flex, and display: none. Other display values are diagnosed and fall back to block. Its layout properties are:

  • width, height, min-*, max-*, aspect-ratio, and box-sizing.

  • margin, padding, gap, row-gap, and column-gap.

  • flex, flex-direction, flex-wrap, flex-grow, flex-shrink, and flex-basis.

  • align-items, align-self, align-content, and justify-content.

  • position: static, relative, absolute, or fixed, with inset, top, right, bottom, and left.

  • overflow, overflow-x, and overflow-y using visible, hidden, auto, or scroll.

Lengths support the relevant combinations of unitless pixels, px, rem, em, %, vw, vh, safe-area env(...), and additive or subtractive calc(...) with up to eight terms. Percent padding and margin are diagnosed and ignored. Percent and viewport units are also invalid for gaps, but they are supported by sizes, flex basis, and the individual top/right/bottom/left offsets.

padding, margin, and inset accept the standard one-, two-, three-, and four-value forms. Percent values are unsupported in the inset shorthand; use the individual offset properties when percentages are required.

CSS keywords and math are checked per property, not accepted globally because a browser would accept them somewhere. In particular:

  • auto is valid only for the property rows that list it, such as sizes, flex basis, offsets, align-self, and overflow.

  • none is valid only where listed, such as display, supported paint/filter resets, pointer-events, and image-fill-direction.

  • calc(...) is available only on documented length fields. It permits addition and subtraction of at most eight simple length terms. Multiplication, division, nested math, min(), max(), and clamp() are unsupported, and percent terms require a definite containing block.

When in doubt, check the exact property row in docs/uidoc_supported_css.md; another property's accepted values are not evidence that the same token works here.

Text, images, and paint

Supported presentation features include:

  • color, background, background-color, and one linear, radial, or conic background-image gradient.

  • border, border-width, border-color, and a single uniform border-radius value. Side-specific borders such as border-bottom, per-corner radii, and percentage radii are unsupported. Use an explicit child divider with a fixed height/width and background color when only one edge is needed.

  • Up to eight comma-separated box-shadow or text-shadow layers; box shadows may be inset.

  • One filter: blur(...) or filter: drop-shadow(X Y [blur] [color]), plus backdrop-filter: blur(...).

  • opacity, z-index, and pointer-events.

  • font-family, font-size, font-style, font-weight, line-height, letter-spacing, text-align, white-space, overflow-wrap, and word-break.

  • Text outline-color and outline-width.

  • object-fit: cover, contain, or fill for images.

  • image-tint, image-grayscale, image-fill-amount, and image-fill-direction.

  • translate, scale, and the translate(...), translateX(...), translateY(...), and scale(...) transform subset. Translate percentages resolve against the element's own border box; viewport translate units remain unsupported.

Colors support common CSS forms including hex, rgb()/rgba(), hsl()/hsla(), named colors, transparent, and currentColor where applicable.

Gradients support up to eight color stops, CSS directions/angles and positions, currentColor, and in oklab or in srgb; Oklab is the default. Multiple background layers and CSS image URLs are not supported. Use an img with an engine asset path for bitmap backgrounds.

box-shadow follows the element's rectangular border box. filter: drop-shadow(X Y [blur] [color]) follows the filtered element's painted alpha, including transparent image corners. Its omitted color defaults to the element's currentColor. Put it on the actual img for an irregular or nine-sliced bitmap; putting it on a container also includes the container's painted descendants in the shadow silhouette.

Progress image fills

For a continuous progress bar, place a full-size img inside a fixed-size track, bind a normalized 0..1 value with data-style-image-fill-amount, and set image-fill-direction: right plus object-fit: fill. Use a rectangular or gradient texture with no unintended transparent gaps across the bar interior.

Do not reuse an icon or other decorative transparent image for the fill. image-tint is multiplicative and preserves the source alpha; it does not turn transparent pixels opaque. object-fit: cover also crops square art aggressively when the track is thin. Together those choices can produce empty leading space or diagonal wedges even when UIDoc's fill clipping is working correctly.

Diagnostics do not currently inspect texture alpha or flag object-fit: cover on progress fills. Verify progress bars visually at 0, 0.25, 0.5, and 1. The Game UI kit's paired fill_*.png assets are purpose-built for their corresponding backings, and .kit-progress-fill applies the intended fit.

UIDoc defaults to the built-in AllIn family, with real 400, 700, and 900 faces. @font-face can declare project TTF or OTF Font assets by asset ID. Style and weight select the nearest declared face; missing bold or slant may be synthesized. Fonts load asynchronously, so UIDoc uses the next family or AllIn until a requested face is ready and then invalidates text layout.

Omit line-height for the default line height that scales with font size. Explicit line-height: normal is not accepted. Unitless values are multipliers; length values are fixed inherited lengths.

letter-spacing is inherited and accepts normal, unitless pixels, px, rem, or em. normal resolves to zero.

The transform subset does not include rotation, skew, 3D transforms, or multiple-token scale values. A filter value contains one blur(...) or one drop-shadow(...); filter chains and other filter functions are unsupported. Filter and backdrop-filter compositor scopes can nest up to four levels.

Responsive styles and safe areas

Media queries are supported, but only for this explicit condition allowlist:

  • (min-width: N) and (max-width: N).

  • Combined minimum and maximum width queries.

  • Tailwind-style (width >= N) and (width <= N).

  • (hover: hover).

Width values accept unitless pixels, px, and rem. Unsupported media blocks are dropped with a diagnostic.

Use these values where safe-area lengths are accepted:

UIDoc's top safe-area inset also reserves the game's top-bar band.

Scrolling and zooming

overflow-x and overflow-y are independent, so one viewport can scroll horizontally, vertically, or on both axes. A two-axis viewport pans both axes when dragged. The mouse wheel scrolls vertically when vertical scrolling is enabled; a horizontal-only viewport maps the wheel to horizontal scrolling.

The runtime currently draws a vertical scrollbar thumb. Horizontal content is still reachable by dragging even though a horizontal thumb is not drawn.

data-scroll-zoom="zoomBinding" adds browser-like content zoom to an interactive scroll viewport. It scales descendant geometry, text, images, transforms, hit testing, and scroll extents while the viewport and its siblings remain fixed. When the binding changes, the engine preserves the viewed area around the viewport center and reclamps the scroll position. The engine safety range is 0.05 through 20; applications should normally use narrower limits.

Tailwind-style classes

UIDoc assets can use supported Tailwind-style utility classes. Utilities are resolved and lowered into UIDoc CSS during asset processing; Tailwind is not running in the game. Covered areas include display, position, flex, sizing, spacing, inset, alignment, text, color, border, radius, overflow, pointer events, opacity, z-index, aspect ratio, shadows, transforms, and arbitrary image-effect properties.

The lowering step supports hover:, focus:, active:, disabled:, responsive sm:/md:/lg:/xl:/2xl:, bracket values such as w-[320px], and CSS @apply. Tailwind preflight/base reset is not included. Output that cannot be represented by UIDoc fails asset baking.

Notable browser features not supported

Browser feature
UIDoc approach

JavaScript and DOM APIs

Drive state with CSL bindings, data-if, data-for, and event callbacks.

CSS Grid

Use flexbox, block layout, or explicit positioning.

CSS variables and custom properties

Bind values from CSL or use ordinary shared classes.

Transitions, keyframes, and CSS animations

Animate CSL bindings such as opacity, translation, scale, or scroll zoom.

Pseudo-elements and advanced selectors

Add explicit elements and classes to the document.

Container queries and most media features

Use the supported viewport-width and hover queries.

CSS URL backgrounds and multiple background layers

Use a supported gradient or an img engine asset.

Per-corner radii and more than eight shadows

Use the supported single-radius/eight-shadow limits or explicit nested elements.

min(), max(), and clamp() CSS math functions

Combine width/height with the supported min-* and max-* properties.

Rotate, skew, 3D transforms, filter chains, and other filter functions

Prepare the visual as an asset or use supported translate/scale/blur/drop-shadow effects.

Web forms, navigation, fetch, iframes, canvas, SVG DOM, audio, and video

Use CSL and the corresponding engine systems.

Browser semantic and accessibility behavior

UIDoc elements are game UI nodes; HTML tag names do not imply browser behavior.

UIDoc aims to make common game UI easy to author, not to reproduce every HTML and CSS feature. Keep layout within this subset so assets bake predictably and client/server UI simulation stays deterministic.

Last updated