Retained-mode UI at §113a's MVP tier. Part of fourJS.
Implements the MVP tier of §73–75 in docs/SPECIFICATION.md; shipped in Phase 11. Widgets are scene nodes; widgets do not draw themselves — the dependency matrix gives this package core, math, scene, input, and text (no render, materials, or geometry), so a widget owns hierarchy, size, hit area, and state, and the application supplies the pixels through the WidgetSkin seam.
UIWidget — the base class: layout box, insets (Insets / applyInsets), state snapshots (WidgetStateSnapshot, WidgetStateChangeEvent), focus (focusedWidget, UIFocusEvent; one focused widget per scene root), accessibility data (WidgetAccessibility — tabIndex drives the traversal; role/label/description are projected by the DOM mirror), convenience label / role accessors, and the UI_LAYOUT_AUTHORITY transform authority.Panel — container with absolute, stack, and flex layout (PanelLayout, LayoutDirection / LayoutAlign / LayoutJustify); §74's anchor mode ships as the per-child anchor/pivot/offset triple honored by absolute layout.Label — text measured with @fourjs/text's glyph atlas.Button — §72 click → uiactivate (WidgetActivateEvent), and §75's Enter/Space activation on the focused button (source: "keyboard"); activate() stays public for programmatic use, and willActivate() is the hook a control whose activation means something overrides.Toggle / Checkbox / RadioButton (2026-08-07, A-12) — checkable controls over CheckableWidget: activation flips (or, for a radio, sets) checked, which rides WidgetStateSnapshot.checked and uistatechange. A radio group is a name (group), scoped to the tree and resolved by a walk (collectRadioGroup, checkedRadio); arrow keys move and check within it.Slider (A-12) — a range (min/max/step) and a value with fraction for the skin; §72 press-and-drag through the hit point (transformed by the widget's own world matrix) and §75 arrow/Home/End keys. Emits uivaluechange.ProgressIndicator (A-12) — an output, not a control: clamped value, fraction, indeterminate; not interactive, not focusable.ImageWidget (A-12) — a box, a §79 logical source key, and a supplied natural size that becomes §74's intrinsic image size. Named with the suffix because Image is a browser global; its document type is ui:image.CanvasViewWidget (RFC 0004, 2026-08-29) — §73's canvas view, skin-drawn: the widget owns its box, a supplied device-pixel resolution (so pixelWidth/pixelHeight name the backing size in texels), and a monotonic contentVersion bumped by invalidate(); the WidgetSkin owns the §77a CanvasTexture and repaints through onContentChange. Document type ui:canvas-view — box and resolution only, painted pixels are never serialized (§77a).collectPickables — bridges a widget tree into @fourjs/input's §71 picking.installKeyboardTraversal (Tab / Shift-Tab over the widget tree), collectFocusOrder (scene order, sorted by accessibility.tabIndex; negative opts out), and keyboardFocusTarget — the resolver @fourjs/input's KeyboardInput takes, which is how keys reach the focused widget without input ever depending on ui.installAccessibilityMirror (opt-in; duck-typed DocumentLike so tests inject a fake and browsers pass window.document). Projects role, aria-label, aria-disabled / disabled, tabIndex, and slider/progress aria-value*. High contrast writes data-high-contrast; fontScale writes font-size on the mirror, not the canvas; reducedMotion is accepted (prefersReducedMotion() is the hook for widgets that later animate). mirror.dispose() removes the container. Tree-shakes when unused.WidgetSkin — five optional hooks: onAttach, onLayout, onStateChange (any §75 flag, checkedness included), onContentChange (a value, an indeterminate flag, an image source — content with no layout or state transition), onDetach.UI_STAGED — the frozen, dated list of everything §73–75 names that is not implemented; read it before assuming a control exists.UI_STAGED for reasons)Unit tests are colocated in tests/ per §92.
Workspace name @fourjs/ui; publishes as @danielsimonjr/fourjs-ui.