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_layoutmeans frequently changing text can change an intrinsically sized box and invalidate cached layout. Adddata-text-reservewith 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_driftmeans an image has independent width and height withobject-fit: fill. Usecontain,cover, oraspect-ratiofor 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:
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.
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, andbox-sizing.margin,padding,gap,row-gap, andcolumn-gap.flex,flex-direction,flex-wrap,flex-grow,flex-shrink, andflex-basis.align-items,align-self,align-content, andjustify-content.position: static,relative,absolute, orfixed, withinset,top,right,bottom, andleft.overflow,overflow-x, andoverflow-yusingvisible,hidden,auto, orscroll.
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:
autois valid only for the property rows that list it, such as sizes, flex basis, offsets,align-self, and overflow.noneis valid only where listed, such asdisplay, supported paint/filter resets,pointer-events, andimage-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(), andclamp()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 conicbackground-imagegradient.border,border-width,border-color, and a single uniformborder-radiusvalue. Side-specific borders such asborder-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-shadowortext-shadowlayers; box shadows may be inset.One
filter: blur(...)orfilter: drop-shadow(X Y [blur] [color]), plusbackdrop-filter: blur(...).opacity,z-index, andpointer-events.font-family,font-size,font-style,font-weight,line-height,letter-spacing,text-align,white-space,overflow-wrap, andword-break.Text
outline-colorandoutline-width.object-fit: cover,contain, orfillfor images.image-tint,image-grayscale,image-fill-amount, andimage-fill-direction.translate,scale, and thetranslate(...),translateX(...),translateY(...), andscale(...)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
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