Every example in this guide came out of one project: a dense simulation game with roughly twenty screens, all of them built in C# against a single USS file, shipped as a public build.
I am writing it because the official documentation does not have a concept guide, and because most UI Toolkit questions I searched for while building had no accepted answer. What follows is not a tutorial. It is the list of things that cost me an evening each.
What USS does not have
The pattern is consistent: USS covers layout and colour well, and stops at anything that paints a shape or reacts to position in a list.
| What you want | Status | What to use instead |
|---|---|---|
linear-gradient() |
Not available | Bake it into a 256×1 texture and stretch it as a background image, or paint it with Painter2D |
| Arcs, rings, circles | Not available | Painter2D in generateVisualContent. A gradient ring means stroking it in short segments, because Painter2D strokes in one colour |
clip-path, masks |
Not available | Bake the shape into the alpha channel of the image itself |
@keyframes |
Not available | transition for simple cases, or swap classes on a schedule from C# |
:last-child, :first-child, :nth-child |
Not available | Negative margin on the container, or clear the last child's margin from C# |
Grid layout, gap |
Not available | Flexbox with margins on the children |
The first four are inconvenient and obvious: you find out immediately, because nothing appears. The fifth is the dangerous one.
The rules that parse and do nothing
Positional selectors fail quietly, in the player, forever
A rule like .pack:last-child { margin-right: 0; } is accepted by the
parser, dropped, and logged as Unknown pseudo class "last-child" in the
player log at every launch. Nothing in the editor tells you. The rule simply never
applies.
In the build I shipped, five such rules had been dead since the day they were written. I found them by reading the log of the compiled player, not from any test, because a test never reads a log.
If you want a row of items with no trailing gap, there are two honest options. Give the container a negative right margin equal to the child gap, so the last item's margin is absorbed. Or clear it from C# once the row is filled:
The failure that deletes half your screen
This is the single most expensive UI Toolkit mistake I have made, and it has no error message at all.
A class named from C# that was never written into the stylesheet
AddToClassList("creator-column") compiles. It runs. The element takes
default flex, which in a column usually means it shrinks to nothing. In my case an
entire panel of readouts and notes was invisible on a screen I looked at every day,
and 244 automated tests were green throughout.
Two defences. State flex-shrink: 0 on your panel class so a missing rule
degrades into overflow rather than disappearance. And write a test that reads the
stylesheet, collects every string passed to AddToClassList and
EnableInClassList across your source, and fails when one has no rule.
That guard has caught the same class of fault on this project more times than any other test I own, including twice while writing the article you are reading.
The runtime theme is styling your controls already
Sliders, toggles, text fields and dropdowns arrive pre-styled by Unity's runtime theme, in a light editor idiom. On a dark game screen they show up as pale grey slabs.
The instinct is to reach into the theme's own element names and override them. That instinct cost me four wrong fixes in a row.
Do not fight the theme's internals
Targeting .unity-toggle__checkmark to remove a white slab also removed
the checkmark, so the control became a box that could not show its own state.
Targeting unity-base-slider__dragger-border produced four attempts that
each fixed the symptom somewhere else.
The reliable answer is to stop overriding and start drawing. A tick is a button, a drawn box and a filled block. It is fifteen lines, it cannot be broken by a theme update, and it looks like the rest of your game.
Watch for global caps as well. A rule like .unity-base-slider { max-width: 560px }
anywhere in your sheet applies to every slider in the project. On a 1370 pixel row my
sliders capped at 560 and the overlay marking their locked portion lined up with the
groove only by coincidence.
Four things that look right and are not
A Button with a height does not centre its own label
It draws the text at the top left of the box. State
-unity-text-align: middle-center on any button you give an explicit
height to.
The runtime theme puts a margin on every Button
Four cards sized to fit a 1340 pixel page measured 1352 because each inherited a
theme margin. The fourth wrapped to a row of its own, and it read as a deliberate
three-across layout rather than as an overflow. State margin-left and
margin-top explicitly on any styled Button.
Number formatting follows the machine, not the game
A raw $"{value:N2}" prints 20,00 on a machine with a Polish
locale. This is not a UI Toolkit fault, but it surfaces in UI Toolkit strings more
than anywhere else because that is where numbers meet the screen. Route every figure
through one formatter that uses InvariantCulture. I have been caught by
this three separate times on the same project.
A PanelSettings reference can vanish on a scene rebuild
Assigning document.panelSettings from editor tooling did not survive
being written to disk for the second scene in a build run, every time,
deterministically. The interface was constructed into no panel and drawn nowhere: the
screen was the camera's clear colour and nothing else, which looks exactly like a
hang. Assign serialized fields through SerializedObject in editor
tooling, and read the serialized field back rather than the property you just set.
How to see any of this
None of the failures above throw. Most of them are invisible to a test suite that never opens a window. The only reliable method I have found is to render the interface to a texture and look at the picture.
A PlayMode test that loads the scene, points the panel at a RenderTexture,
walks every screen and writes each frame to a PNG costs about eighty lines. One pass over
the resulting contact sheet found four faults that 683 green tests had not: every
styled button drawing its label at the top left, eleven tiles with no art because the
files were never made, a difference bar scaled by the wrong value, and money printed as
$20,00.
Assert two things about the frame so a picture cannot lie to you: that it is not one flat colour, because a render texture nobody drew into is indistinguishable from a dark screen that worked, and that nothing extends past the right-hand edge.
Is UI Toolkit worth it anyway?
For this project, yes, and I would choose it again. A dense interface built in C# against one stylesheet is far easier to keep consistent than the same interface assembled by hand in a scene, and every screen can be built by a function that a test can call.
But go in knowing the trade. The documentation is thin, a large share of forum questions have no answer, the theme styles your controls before you do, and the missing USS features are the ones you reach for on day one. None of that is fatal. All of it is easier when somebody has written the list down first.
Everything here came out of Scaling Laws, an AI company tycoon built in Unity and released as a free public build. The source is open, including the stylesheet and the guard tests described above.
Questions people ask about this
Does USS support gradients?
No. Bake the gradient into a 256×1 texture and stretch it, or paint it with Painter2D. The texture is cheaper and is the right answer for a horizontal bar.
Does USS support :last-child?
No, and neither does any other positional pseudo-class. The rule parses, is dropped, and warns in the player log at every launch while styling nothing.
Why does my USS class do nothing?
Most often because it is named from C# and was never written into the stylesheet. The element takes default flex and collapses, silently. Write a test that compares the two lists.
Why is my button text not centred?
A Button with an explicit height does not centre its own label. Set
-unity-text-align: middle-center.
UI Toolkit or uGUI in 2026?
UI Toolkit for a dense, data-heavy interface built in code, and it is Unity's forward path. uGUI is still simpler for a handful of straightforward screens.