UI Toolkit Basics

You've seen UITK from the outside (What is UITK?) and traced Luna's theme chain (Quick Start). This page is the working vocabulary for writing UITK — USS selectors, classes, pseudo-classes, USS variables, the event system, and the C# query API. Skim if you're CSS-fluent; read carefully if you're coming from uGUI.

The three layers are the same as the web — structure, style, behavior:

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

Vocabulary quick-reference

You met UXML, USS, TSS, UIDocument, and Panel Settings in the previous pages. Here they are in one place for reference:

  • 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.

Everything from here on is new ground — flexbox layout, USS selectors and variables, the event system, and the C# query API.

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