unity-ui-development
Unity provides two UI systems: UGUI (the established GameObject-based system) and UI Toolkit (the newer retained-mode system inspired by web technologies). This skill covers both systems, when to use each, and implementation patterns.
Works with
Agent Skills format with YAML frontmatter. Claude Code reads it as-is.
---
name: "unity-ui-development"
description: "Unity provides two UI systems: UGUI (the established GameObject-based system) and UI Toolkit (the newer retained-mode system inspired by web technologies). This skill covers both systems, when to use each, and implementation patterns."
license: "MIT"
---
# Unity UI Development
## Overview
Unity provides two UI systems: UGUI (the established GameObject-based system) and UI Toolkit (the newer retained-mode system inspired by web technologies). This skill covers both systems, when to use each, and implementation patterns.
## Choosing a UI System
| Factor | UGUI | UI Toolkit |
|--------|------|------------|
| Runtime UI | Mature, full-featured | Supported (Unity 2023+) |
| Editor UI | Not supported | Primary system for editor |
| World-space UI | Excellent (World Space Canvas) | Limited |
| Animation/Tweening | DOTween, Animator, LeanTween | USS transitions, C# manipulation |
| Styling | Per-element, manual | USS stylesheets (CSS-like) |
| Performance | Draw call heavy with many canvases | Retained mode, lighter draw calls |
| Learning curve | Lower for Unity devs | Higher (web-like paradigm) |
| VR/AR | Well-supported | Limited world-space support |
**Recommendation:** Use UGUI for world-space UI, VR/AR, and projects needing maximum asset store compatibility. Use UI Toolkit for editor extensions, complex data-driven UIs (lists, trees), and projects on Unity 2023+.
## UGUI (Canvas-Based UI)
### Canvas Setup
| Render Mode | Use For | Notes |
|-------------|---------|-------|
| Screen Space - Overlay | HUD, menus | Always on top, no camera needed |
| Screen Space - Camera | Post-processing on UI | Assign render camera |
| World Space | In-game signs, health bars | Set event camera for interaction |
**Performance rule:** Separate static and dynamic UI into different Canvases. A Canvas rebuilds ALL children when any child changes.
### RectTransform Anchoring
```bash
Anchor presets control how elements resize with parent:
- Stretch-Stretch: Element fills parent (full-screen backgrounds)
- Center-Center: Fixed size at center (popup dialogs)
- Bottom-Left: Fixed corner position (minimap)
- Top-Stretch: Stretches horizontally, fixed at top (nav bar)
```
Set anchors via the RectTransform anchor preset widget (hold Alt+Shift to also set pivot and position). Use `anchorMin`, `anchorMax`, `offsetMin`, `offsetMax` in code.
### Layout Components
| Component | Purpose |
|-----------|---------|
| `HorizontalLayoutGroup` | Arrange children left-to-right |
| `VerticalLayoutGroup` | Arrange children top-to-bottom |
| `GridLayoutGroup` | Grid arrangement (inventory slots) |
| `LayoutElement` | Override min/preferred/flexible size |
| `ContentSizeFitter` | Auto-resize to content |
| `AspectRatioFitter` | Maintain aspect ratio |
Disable `Layout.childForceExpandWidth/Height` to prevent unwanted stretching.
### Event Handling
```csharp
using UnityEngine.UI;
using TMPro;
[SerializeField] Button startButton;
[SerializeField] TMP_InputField nameField;
[SerializeField] Slider volumeSlider;
void OnEnable()
{
startButton.onClick.AddListener(OnStartClicked);
nameField.onEndEdit.AddListener(OnNameChanged);
volumeSlider.onValueChanged.AddListener(OnVolumeChanged);
}
void OnDisable()
{
startButton.onClick.RemoveListener(OnStartClicked);
nameField.onEndEdit.RemoveListener(OnNameChanged);
volumeSlider.onValueChanged.RemoveListener(OnVolumeChanged);
}
```
Always `RemoveListener` in `OnDisable` to prevent leaks and ghost references.
### TextMeshPro
Always use TextMeshPro (`TMP_Text`, `TextMeshProUGUI`) over legacy `Text`. Import TMP Essentials when prompted. Use rich text tags: `<color=#FF0000>`, `<b>`, `<size=24>`, `<sprite=0>` for inline icons.
## UI Toolkit (USS/UXML)
### Architecture
```text
UI Toolkit Stack:
UXML -- Structure (like HTML)
USS -- Styling (like CSS)
C# -- Logic (like JavaScript)
```
### UXML Structure
```xml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement class="container">
<ui:Label text="Player Stats" class="title" />
<ui:ProgressBar name="health-bar" title="HP" high-value="100" />
<ui:Button name="attack-btn" text="Attack" class="action-btn" />
<ui:ListView name="inventory-list" />
</ui:VisualElement>
</ui:UXML>
```
### USS Styling
```css
.container {
flex-direction: column;
padding: 10px;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 8px;
}
.title {
font-size: 24px;
color: white;
-unity-font-style: bold;
margin-bottom: 10px;
}
.action-btn {
height: 40px;
background-color: #4CAF50;
color: white;
border-radius: 4px;
transition-duration: 0.2s;
}
.action-btn:hover {
background-color: #66BB6A;
scale: 1.05 1.05;
}
```
Key USS differences from CSS: use `-unity-` prefix for Unity-specific properties. Flexbox is the layout model (default `flex-direction: column`). Use `transition-duration`, `transition-property` for animations.
### C# Integration
```csharp
[RequireComponent(typeof(UIDocument))]
public class StatsUI : MonoBehaviour
{
void OnEnable()
{
var root = GetComponent<UIDocument>().rootVisualElement;
var healthBar = root.Q<ProgressBar>("health-bar");
var attackBtn = root.Q<Button>("attack-btn");
var inventory = root.Q<ListView>("inventory-list");
attackBtn.RegisterCallback<ClickEvent>(OnAttack);
healthBar.value = player.Health;
// ListView binding
inventory.makeItem = () => new Label();
inventory.bindItem = (element, index) =>
((Label)element).text = items[index].Name;
inventory.itemsSource = items;
}
}
```
Query elements with `Q<T>("name")` or `Q<T>(className: "class")`. Register callbacks with `RegisterCallback<EventType>`. Always query from `rootVisualElement`.
### Data Binding (Unity 2023.2+)
```csharp
// Runtime data binding with [CreateProperty] and INotifyBindablePropertyChanged
public class PlayerData : INotifyBindablePropertyChanged
{
public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
private int _health;
[CreateProperty]
public int Health
{
get => _health;
set { _health = value; Notify(); }
}
void Notify([CallerMemberName] string prop = "")
=> propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(prop));
}
```
Bind in UXML with `binding-path="Health"` or in C# with `element.SetBinding("value", new DataBinding { dataSourcePath = ... })`.
## Common UI Patterns
| Pattern | UGUI Approach | UI Toolkit Approach |
|---------|---------------|---------------------|
| Popup dialog | Enable/disable child panel | Add/remove from visual tree |
| Scroll list | ScrollRect + VerticalLayoutGroup | ListView (virtualized) |
| Drag-and-drop | IBeginDragHandler, IDragHandler, IEndDragHandler | PointerManipulator |
| Tab system | Toggle group + panels | RadioButtonGroup + display toggling |
| Tooltip | Follow cursor panel | Manipulator + VisualElement positioning |
| Screen fade | CanvasGroup.alpha tween | USS opacity transition |
## Additional Resources
### Reference Files
- **`references/ugui-patterns.md`** -- Advanced UGUI patterns: scroll optimization, dynamic layouts, world-space interaction, localization, safe area handling, screen adaptation
- **`references/ui-toolkit-advanced.md`** -- Custom controls, ListView/TreeView mastery, custom manipulators, editor UI integration, theming, responsive layoutsMore General & Other skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
grill-with-docs
mattpocock/skills
A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.

