UI Toolkit Basics

The minimum UI Toolkit knowledge needed to read Luna code and USS files. Already know UITK? Skip to Why UI Toolkit? for the comparison, or Theme Stack for Luna's theme model.

UI Toolkit is Unity's newer UI system, modeled on web tech (HTML/CSS/DOM). Three layers split structure, style, and behavior — same as the web:

UXML → structure (the visual tree) USS → style (selectors + properties; cascades like CSS) C# → behavior (event callbacks, dynamic state)

Building blocks

  • UXML — XML markup describing the visual tree (≈ HTML). Hand-write it or design in UI Builder.
  • USS — Unity Style Sheets. CSS-like rules with .class, #name, type, and pseudo-class selectors. Inspired by CSS with Unity-specific overrides (-unity-… properties, image scale modes).
  • TSS — Theme StyleSheet. A USS file used as the root theme, assigned to Panel Settings. Use @import to layer in further USS. One TSS per project.
  • VisualElement — the runtime node (≈ DOM element). Has style, layout, children, classes, callbacks.
  • UIDocument — MonoBehaviour that hosts a UXML tree in a scene. Points at a UXML source and a Panel Settings asset.
  • Panel Settings — asset configuring how Unity renders the panel: active TSS, scale mode, text settings, target texture (world-space).

Composition: a UIDocument loads a .uxml; its Panel Settings resolves the .tss; the TSS imports .uss files; selectors target elements in the visual tree.

Layout — flexbox, not anchors

Layout is flexbox. Direction (flex-direction: row | column), grow (flex-grow), wrap, justify, align — same axes and rules as CSS flexbox. No RectTransform anchors. Coming from uGUI, expect a brief re-learn: parents distribute space to children via flex rules instead of children pinning to parent edges via anchors.

Reaching into the tree from C#

Load a UXML clone, then query elements by name or class:

csharp
var root = uiDocument.rootVisualElement; var btn = root.Q<Button>("submit-btn"); // single element by name var rows = root.Query<VisualElement>(className: "row").ToList();

Q<T>() is the workhorse. Luna sample code uses it everywhere.

State via class manipulation

Don't mutate inline style at runtime if you can avoid it. Toggle a CSS class instead, and let the stylesheet describe what that state looks like:

csharp
element.AddToClassList("active"); element.RemoveFromClassList("active"); element.EnableInClassList("disabled", isDisabled); // conditional element.ClassListContains("active");
css
.button { background-color: var(--color-primary-500); } .button.active { background-color: var(--color-primary-700); }

Luna components use this pattern pervasively — responsive breakpoints (.breakpoint-lg), locale switches (.locale-ja), drag state, etc.

Pseudo-classes

:hover, :active, :focus, :checked, :disabled, :root. Style states declaratively in USS instead of writing hover/leave callbacks.

USS variables (--…)

Custom properties (CSS variables) declared on a selector (commonly :root for globals) and referenced with var(--name). Inherit down the tree:

css
:root { --color-primary-500: #a855f7; --radius-md: 8px; } .button { background-color: var(--color-primary-500); border-radius: var(--radius-md); }

Luna's whole theming model is USS variables — every color, every spacing, every font is a var(--…) reference into LunaUIDemoTheme.tss. Override the variables in your own USS to retheme without touching component selectors.

Events — RegisterCallback, not UnityEvent

UI Toolkit has its own event system. It is not Button.onClick or UnityEvent — those are uGUI / Inspector wiring. Use RegisterCallback<T>:

csharp
btn.RegisterCallback<ClickEvent>(evt => Debug.Log("clicked")); slider.RegisterValueChangedCallback(evt => Debug.Log(evt.newValue)); input.RegisterCallback<KeyDownEvent>(evt => { /* … */ });

Events propagate in three phases: trickle-down (root → target), target, bubble-up (target → root). Same model as DOM events. Stop propagation with evt.StopPropagation().

Text — SDF fonts + TextSettings

UITK uses SDF (signed distance field) fonts for crisp scaling at any size. The Panel Settings → Text Settings asset configures the font fallback chain — what to render when a glyph isn't in the primary font (Japanese, emoji, missing symbols). Luna's UITextSettings.asset in Essentials wires the Luna font set; extend it instead of replacing it when you add a language.

Backgrounds — sprites and 9-slicing

background-image: url("..."); accepts both Texture2D and Sprite. Sprites with import-settings borders 9-slice automatically via -unity-slice-{left,right,top,bottom} properties. Luna uses this for buttons, panels, progress bars.

Authoring tools (mentioned, not covered here)

  • UI Builder — visual editor for UXML/USS (Window > UI Toolkit > UI Builder).
  • UI Debugger — Inspector-like inspector for the running visual tree (Window > UI Toolkit > Debugger).

Both are worth opening once; neither is required.

Further reading (Unity manual)

Next: Luna's theme on top of UITK

Now that you know how UITK works in general, see how Luna layers a theme on top: Theme Stack explains Panel Settings configuration, the @import chain from Unity's defaults through Luna's main theme to your overrides, and how to customise colors / fonts without forking the package.

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL