> 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-html-css-support.md).

# UIDoc HTML and CSS Support

***

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](/ui/uidoc-quick-start.md).

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.

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:

```
UI/board.uidoc:seed-cell#seed-42:7/__widget
```

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:

```css
env(safe-area-inset-top)
env(safe-area-inset-right)
env(safe-area-inset-bottom)
env(safe-area-inset-left)
```

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.
