返回 Skills 目录
unity-technologies/skills已通过检查

SKILL DETAIL

ui-uitk

unity-technologies/skills/ui-uitk

该技能面向 Unity 6.0 及以上版本,专注于 Unity UI Toolkit。它能够理解现有的 UI Toolkit 代码,进行有针对性的编辑,并生成新的 UXML 和 USS 文件,以及自定义 UI 元素、操作器(Manipulators)和运行时数据绑定。适用于涉及 .uxml、.uss、UI Toolkit、UIElements、UIDocument、UI 运行时绑定、自定义 UI 元素、操作器或 PanelSettings 的请求。 技能遵循项目约定,优先复用现有资源,并确保生成的 UI 符合 Unity 的 USS 限制(如避免使用不支持的 CSS 属性)。它提供详细的参考指南,涵盖 USS 模式、SVG 图标生成、常见问题、运行时绑定、Painter2D 自定义绘制、操作器模式以及自定义元素创建。工作流程包括分析需求、搜索现有文件、编写完整的 UXML/USS 文件,并指导用户在 Unity 编辑器中验证结果。

安装量 · 162查看来源

Installation

npx skills add https://github.com/unity-technologies/skills --skill ui-uitk

技能文件

SKILL.md

最近同步 · 2026年8月27日

references/common-issues.md
# Common USS/UXML Issues

## Transitions on :hover

Transitions must be defined on the base class, not the hover state:

```uss
/* WRONG - won't animate on hover-out */
.button:hover {
  background-color: blue;
  transition-duration: 0.2s;
}

/* CORRECT */
.button {
  background-color: white;
  transition-duration: 0.2s;
}
.button:hover {
  background-color: blue;
}
```

## Hardcoded Percentage Widths

Avoid hardcoded percentages for flexible layouts:

```uss
/* AVOID */
.column {
  width: 33%;
}

/* PREFER */
.column {
  flex-grow: 1;
}
```

## Unclosed Brackets

Always ensure brackets are properly closed:

```uss
/* WRONG - missing closing bracket */
.panel {
  padding: 16px;

.button {
  color: white;
}

/* CORRECT */
.panel {
  padding: 16px;
}

.button {
  color: white;
}
```

## Referencing Unity Theme

**Do NOT reference `UnityDefaultRuntimeTheme.tss`** or built-in theme icons.

Create custom styles or reuse project assets instead.

## Performance Issues

- **Inline styles** cause per-element memory overhead
- **`:hover` on parents** with many children invalidates entire hierarchies
- **Many classes per element** decreases selector performance linearly
- **Large hierarchies** are the main performance factor
references/custom-elements.md
# Custom VisualElements in UI Toolkit

Guide for creating custom VisualElements using Unity 6+ `[UxmlElement]` and `[UxmlAttribute]` attributes.

## Table of Contents

- [Overview](#overview)
- [Requirements](#requirements)
- [Supported Property Types](#supported-property-types)
- [Advanced Patterns](#advanced-patterns)
- [Best Practices](#best-practices)
- [UXML Namespace Declaration](#uxml-namespace-declaration)
- [UI Builder Integration](#ui-builder-integration)
- [Summary](#summary)

## Overview

Unity 6+ uses attribute-based custom elements. The old factory pattern (`IUxmlFactory`, `UxmlTraits`) is **deprecated**.

### ⚠️ CRITICAL: Namespace Declaration

**Never include assembly names in UXML namespace declarations:**

```xml
❌ WRONG: xmlns:custom="Game.UI.Custom, Assembly-CSharp"
✅ CORRECT: xmlns:custom="Game.UI.Custom"
```

**Format**: `xmlns:prefix="Namespace.Path"` (namespace only, no assembly)

### Basic Pattern

```csharp
[UxmlElement]
public partial class MyElement : VisualElement
{
    [UxmlAttribute]
    public string myValue { get; set; }
}
```

```xml
<ui:UXML xmlns:ui="UnityEngine.UIElements"
         xmlns:custom="Game.UI.Custom">
    <custom:MyElement my-value="Hello" />
</ui:UXML>
```

## Requirements

1. **[UxmlElement]** attribute on the class
2. **partial** keyword required
3. Inherit from **VisualElement** or subclass
4. **[UxmlAttribute]** on exposed properties

### Property Naming

C# camelCase → UXML kebab-case:
- `myStringValue` → `my-string-value`
- `maxHealth` → `max-health`

### Example

```csharp
[UxmlElement]
public partial class CustomLabel : Label
{
    [UxmlAttribute]
    public Color textColor { get; set; } = Color.white;

    [UxmlAttribute]
    public int fontSize { get; set; } = 14;

    public CustomLabel()
    {
        RegisterCallback<GeometryChangedEvent>(evt => {
            style.color = textColor;
            style.fontSize = fontSize;
        });
    }
}
```

```xml
<custom:CustomLabel text="Hello" text-color="rgb(255,215,0)" font-size="24" />
```

## Supported Property Types

**Basic**: `string`, `int`, `float`, `bool`, `Color`
**Unity**: `Texture2D`, `Sprite`, `Font`
**Enums**: Any enum type
**Collections**: `List<T>`, `T[]`

### Image Handling

**Static images** (don't change) → Use USS:
```css
.icon { background-image: url('project://Assets/UI/Icons/fireball.png'); }
```

**Dynamic images** (change per instance) → Use `[UxmlAttribute]`:
```csharp
[UxmlAttribute]
public Sprite portrait { get; set; }  // Different per character
```

## Advanced Patterns

### Validation Attributes

```csharp
[UxmlAttribute]
[Range(0, 100)]
[Tooltip("Current health value")]
public float health { get; set; } = 100f;
```

Improves UI Builder inspector experience.

### Custom Attribute Names

```csharp
[UxmlAttribute("hp")]
public float health { get; set; }  // Use "hp" in UXML
```

### Custom Type Converters

Use `UxmlAttributeConverter<T>` when a property's type is not natively supported by UXML (e.g. structs, complex data objects). An example below implements `FromString` to parse the UXML attribute string into the specified type, then register it with `[UxmlAttributeConverter]` on the property.

```csharp
public class HealthDataConverter : UxmlAttributeConverter<HealthData>
{
    public override HealthData FromString(string value)
    {
        var parts = value.Split(',');
        return new HealthData { current = float.Parse(parts[0]), max = float.Parse(parts[1]) };
    }
}

[UxmlAttribute]
[UxmlAttributeConverter(typeof(HealthDataConverter))]
public HealthData healthData { get; set; }
```

### Custom Property Drawers

Create custom UI Builder inspector controls for your attributes:

```csharp
// 1. Custom attribute
public class SliderDrawerAttribute : PropertyAttribute { }

// 2. Property drawer
[CustomPropertyDrawer(typeof(SliderDrawerAttribute))]
public class SliderDrawerPropertyDrawer : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        var field = new SliderInt(0, 100) { label = property.displayName };
        field.BindProperty(property);
        return field;
    }
}

// 3. Usage
[UxmlElement]
public partial class StyledButton : Button
{
    [UxmlAttribute]
    [SliderDrawer]
    public int intensity { get; set; } = 50;
}
```

**Override existing properties** with custom drawers:

```csharp
[UxmlElement]
public partial class CustomIntField : IntegerField
{
    // Override base 'value' property to use slider drawer in UI Builder
    [UxmlAttribute("value"), SliderDrawer]
    internal int myValue
    {
        get => this.value;
        set => this.value = value;
    }
}
```

This customizes how properties appear in the UI Builder inspector.


## Best Practices

1. **Always use `partial`** - Required for [UxmlElement]
2. **Provide defaults** - `public float radius { get; set; } = 30f;`
3. **Update on changes** - Use property setters to call `UpdateVisuals()` or `MarkDirtyRepaint()`
4. **Use backing fields** - When validation or change detection needed
5. **USS for styling** - Add class names and style via USS, not inline styles
6. **Static images → USS, Dynamic → C#** - Use USS `background-image` for fixed images
7. **Document elements** - Add XML comments for better developer experience

## UXML Namespace Declaration

### Rules

1. **Namespace only** - No assembly name, no commas
2. **Exact match** - Must match C# namespace exactly (case-sensitive)
3. **Format**: `xmlns:prefix="Namespace.Path"`

### Examples

```xml
<!-- ✅ Single namespace -->
<ui:UXML xmlns:ui="UnityEngine.UIElements"
         xmlns:custom="Game.UI.Custom">
    <custom:RadialProgress />
</ui:UXML>

<!-- ✅ Multiple namespaces -->
<ui:UXML xmlns:ui="UnityEngine.UIElements"
         xmlns:hud="Game.UI.HUD"
         xmlns:menu="Game.UI.Menu">
    <hud:HealthBar />
    <menu:SettingsPanel />
</ui:UXML>
```

### Common Mistakes

```xml
❌ xmlns:custom="Game.UI.Custom, Assembly-CSharp"  (assembly name)
❌ xmlns:custom="game.ui.custom"                    (wrong case)
❌ xmlns:custom="Custom"                            (incomplete)
❌ xmlns:custom="Game.UI.Custom.RadialProgress"     (class name)

✅ xmlns:custom="Game.UI.Custom"                    (correct)
```
references/painter2d.md
# Custom Visuals with Painter2D

## Table of Contents

- [The Pattern](#the-pattern)
- [Canvas-to-Painter2D Mapping](#canvas-to-painter2d-mapping)
- [Gradient Fills](#gradient-fills)
- [GradientElement — Full Example](#gradientelement--full-example)
- [Other Use Cases](#other-use-cases)
- [Rules](#rules)

USS cannot draw gradients, arbitrary shapes, arcs, or procedural patterns. For these, use the **Painter2D** API via the `generateVisualContent` callback.

Painter2D is modeled on the **HTML Canvas 2D** context — `BeginPath`, `MoveTo`, `LineTo`, `Arc`, `BezierCurveTo`, `Fill`, `Stroke` all map directly. Key differences from Canvas: text is drawn via `ctx.DrawText()` on the `MeshGenerationContext` (not on Painter2D itself), no `drawImage()` (use `fillTexture` or child elements with USS `background-image`), angles use `Angle.Degrees()` / `Angle.Turns()` structs, arc direction is an enum (`ArcDirection.Clockwise` / `.CounterClockwise`), and coordinates are local to the element's content rect.

**Drawing text:** Use `ctx.DrawText(string text, Vector2 pos, float fontSize, Color color, FontAsset font)` on the `MeshGenerationContext` directly. Pass `null` for `font` to use the element's USS font. This is useful when text must be positioned precisely within custom-drawn visuals — for simpler cases, child `Label` elements are easier.

## The Pattern

Every custom-drawn element: extend `VisualElement` directly (never `Label`, `Button`, etc. — Painter2D won't render correctly on those), subscribe to `generateVisualContent`, draw with `ctx.painter2D`, call `MarkDirtyRepaint()` when properties change. For animations, call `MarkDirtyRepaint()` every frame from an update loop.

```csharp
[UxmlElement]
public partial class MyCustomVisual : VisualElement
{
    float m_Value = 0.5f;

    [UxmlAttribute]
    public float Value
    {
        get => m_Value;
        set { m_Value = value; MarkDirtyRepaint(); }
    }

    public MyCustomVisual()
    {
        generateVisualContent += OnGenerateVisualContent;
    }

    void OnGenerateVisualContent(MeshGenerationContext ctx)
    {
        float w = contentRect.width;
        float h = contentRect.height;
        if (w < 1f || h < 1f) return;

        var painter = ctx.painter2D;
        // ... drawing commands
    }
}
```

Name classes to match their purpose — `GradientCard`, `RadialProgress`, `WaveformDisplay`, etc.

## Canvas-to-Painter2D Mapping

| HTML Canvas 2D | Unity Painter2D |
|----------------|-----------------|
| `beginPath()` | `BeginPath()` |
| `moveTo(x, y)` | `MoveTo(new Vector2(x, y))` |
| `lineTo(x, y)` | `LineTo(new Vector2(x, y))` |
| `arc(cx, cy, r, start, end)` | `Arc(Vector2 center, float radius, Angle start, Angle end, ArcDirection dir)` |
| `arcTo(x1, y1, x2, y2, r)` | `ArcTo(Vector2 p1, Vector2 p2, float radius)` |
| `bezierCurveTo(...)` | `BezierCurveTo(Vector2 ctrl1, Vector2 ctrl2, Vector2 end)` |
| `quadraticCurveTo(...)` | `QuadraticCurveTo(Vector2 ctrl, Vector2 end)` |
| `closePath()` | `ClosePath()` |
| `rect(x, y, w, h)` | **No equivalent** — trace manually with `MoveTo`/`LineTo`/`ClosePath` |
| `fill()` | `Fill(FillRule rule = NonZero)` — use `OddEven` for holes/cutouts |
| `stroke()` | `Stroke()` |
| `lineWidth` | `lineWidth` |
| `strokeStyle` | `strokeColor` / `strokeGradient` / `strokeFillGradient` |
| `fillStyle` | `fillColor` / `fillGradient` / `fillTexture` |
| `lineCap` | `lineCap` — `LineCap.Butt` (default), `.Round`, `.Square` |
| `lineJoin` | `lineJoin` — `LineJoin.Miter` (default), `.Bevel`, `.Round` |
| `setLineDash([...])` | `SetDashPattern(float[])` |
| `lineDashOffset` | `dashOffset` |

Both `Fill()` and `Stroke()` can be called on the same path.

Angle helpers: `Angle.Degrees(float)`, `Angle.Radians(float)`, `Angle.Turns(float)`.

## Gradient Fills

USS has no `linear-gradient()` or `radial-gradient()`. Use `FillGradient`:

```csharp
// Linear — two-color shorthand
FillGradient.MakeLinearGradient(Color startColor, Color endColor, Vector2 start, Vector2 end, AddressMode mode)
// Linear — multi-stop via Gradient object
FillGradient.MakeLinearGradient(Gradient gradient, Vector2 start, Vector2 end, AddressMode mode)

// Radial — two-color shorthand
FillGradient.MakeRadialGradient(Color startColor, Color endColor, Vector2 center, float radius, Vector2 focus, AddressMode mode)
// Radial — multi-stop via Gradient object
FillGradient.MakeRadialGradient(Gradient gradient, Vector2 center, float radius, Vector2 focus, AddressMode mode)
```

`AddressMode`: `Clamp` (extend edge color), `Repeat` (tile), `Mirror` (reflect).

**Linear gradient direction** — controlled by start/end points:

| Direction | Start | End |
|-----------|-------|-----|
| Top → Bottom | `(0, 0)` | `(0, height)` |
| Left → Right | `(0, 0)` | `(width, 0)` |
| Diagonal | `(0, 0)` | `(width, height)` |

**Radial gradient** — set `focus` off-center to shift the bright spot.

## GradientElement — Full Example

A custom element rendering a linear gradient with rounded corners and optional border stroke. All properties exposed as UXML attributes.

```csharp
using UnityEngine;
using UnityEngine.UIElements;

[UxmlElement]
public partial class GradientElement : VisualElement
{
    Color m_StartColor = new Color(0.13f, 0.59f, 0.95f);
    Color m_EndColor = new Color(0.61f, 0.15f, 0.69f);
    Color m_BorderColor = Color.white;
    float m_BorderWidth = 2f;
    float m_CornerRadius = 8f;
    float m_GradientAlpha = 1f;

    [UxmlAttribute]
    public Color StartColor
    {
        get => m_StartColor;
        set { m_StartColor = value; MarkDirtyRepaint(); }
    }

    [UxmlAttribute]
    public Color EndColor
    {
        get => m_EndColor;
        set { m_EndColor = value; MarkDirtyRepaint(); }
    }

    [UxmlAttribute]
    public Color BorderColor
    {
        get => m_BorderColor;
        set { m_BorderColor = value; MarkDirtyRepaint(); }
    }

    [UxmlAttribute]
    public float BorderWidth
    {
        get => m_BorderWidth;
        set { m_BorderWidth = value; MarkDirtyRepaint(); }
    }

    [UxmlAttribute]
    public float CornerRadius
    {
        get => m_CornerRadius;
        set { m_CornerRadius = value; MarkDirtyRepaint(); }
    }

    [UxmlAttribute]
    public float GradientAlpha
    {
        get => m_GradientAlpha;
        set { m_GradientAlpha = Mathf.Clamp01(value); MarkDirtyRepaint(); }
    }

    public GradientElement()
    {
        generateVisualContent += OnGenerateVisualContent;
    }

    void OnGenerateVisualContent(MeshGenerationContext ctx)
    {
        float w = contentRect.width;
        float h = contentRect.height;
        if (w < 1f || h < 1f)
            return;

        DrawGradientBackground(
            ctx.painter2D, w, h,
            m_StartColor, m_EndColor, m_GradientAlpha,
            m_CornerRadius,
            m_BorderColor, m_BorderWidth);
    }

    static void DrawGradientBackground(
        Painter2D painter,
        float width, float height,
        Color startColor, Color endColor, float alpha,
        float cornerRadius,
        Color borderColor, float borderWidth)
    {
        float r = Mathf.Min(cornerRadius, Mathf.Min(width, height) * 0.5f);

        var start = startColor;
        var end = endColor;
        start.a *= alpha;
        end.a *= alpha;

        painter.fillGradient = FillGradient.MakeLinearGradient(
            BuildGradient(start, end),
            new Vector2(0f, 0f),
            new Vector2(0f, height),
            AddressMode.Clamp);

        painter.BeginPath();
        TraceRoundedRect(painter, 0f, 0f, width, height, r);
        painter.Fill();

        if (borderWidth <= 0f)
            return;

        float half = borderWidth * 0.5f;
        painter.strokeColor = borderColor;
        painter.lineWidth = borderWidth;
        painter.lineJoin = LineJoin.Round;

        painter.BeginPath();
        TraceRoundedRect(
            painter, half, half,
            width - borderWidth, height - borderWidth,
            Mathf.Max(0f, r - half));
        painter.Stroke();
    }

    static Gradient BuildGradient(Color start, Color end)
    {
        var gradient = new Gradient();
        gradient.SetKeys(
            new[] { new GradientColorKey(start, 0f), new GradientColorKey(end, 1f) },
            new[] { new GradientAlphaKey(start.a, 0f), new GradientAlphaKey(end.a, 1f) });
        return gradient;
    }

    static void TraceRoundedRect(Painter2D p, float x, float y, float w, float h, float r)
    {
        p.MoveTo(new Vector2(x + r, y));
        p.LineTo(new Vector2(x + w - r, y));
        p.ArcTo(new Vector2(x + w, y), new Vector2(x + w, y + r), r);
        p.LineTo(new Vector2(x + w, y + h - r));
        p.ArcTo(new Vector2(x + w, y + h), new Vector2(x + w - r, y + h), r);
        p.LineTo(new Vector2(x + r, y + h));
        p.ArcTo(new Vector2(x, y + h), new Vector2(x, y + h - r), r);
        p.LineTo(new Vector2(x, y + r));
        p.ArcTo(new Vector2(x, y), new Vector2(x + r, y), r);
        p.ClosePath();
    }
}
```

### Usage in UXML

```uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
  <ui:Style src="Screen.uss" />
  <GradientElement class="gradient-card"
      start-color="#2196F3" end-color="#9C27B0"
      gradient-alpha="0.9" corner-radius="12"
      border-color="#FFFFFF" border-width="1">
    <ui:Label text="Card Title" class="card-title" />
    <ui:Label text="Description text goes here" class="card-desc" />
  </GradientElement>
</ui:UXML>
```

The element participates in flexbox, accepts children, and can be styled with USS for sizing, padding, and margin. The gradient draws behind child content.

## Other Use Cases

Painter2D handles any visual USS cannot express — progress rings (`Arc()` with dynamic `endAngle`), custom shapes (polygons, stars, badges), charts (bar fills, pie segments, sparklines), decorative elements (wave patterns, bezier flourishes), and animated visuals (drive properties from C#, call `MarkDirtyRepaint()` each frame).

## Rules

- **Extend `VisualElement` directly** — never `Label`, `Button`, etc.
- **Guard zero dimensions** — `if (contentRect.width < 1f || contentRect.height < 1f) return;`
- **`BeginPath()` before every path** — omitting it causes silent failures
- **No `Rect()` method** — Painter2D has no rectangle convenience method. Trace rectangles manually with `MoveTo`/`LineTo`/`ClosePath` (see `TraceRoundedRect` in the gradient example)
- **Set style properties before `BeginPath()`** — `lineWidth`, `strokeColor`, `fillColor`, etc.
- **Never mutate the element inside `generateVisualContent`** — no style changes, no adding children, no `MarkDirtyRepaint()` from within the callback
- **`LineCap.Butt` for precise arc endpoints** — `Round` extends past the endpoint by half the line width
references/pointermanipulator-guide.md
# UI Toolkit Manipulator Reference

Pointer Manipulators handle pointer interactions like drag and drop, click, hover, and gestures in Unity UI Toolkit.

## What is a Manipulator?

A `Manipulator` is an event handler class attached to `VisualElement`s to add interactive behavior. They encapsulate interaction logic and can be reused across multiple elements.

## Base Pattern

Pointer Manipulators inherit from `PointerManipulator` and override:
- `RegisterCallbacksOnTarget()` — Subscribe to events when attached
- `UnregisterCallbacksFromTarget()` — Unsubscribe when detached

Attach with: `element.AddManipulator(new YourManipulator())`

## Drag and Drop Approach

### Drag and Drop Code Design
1. **PointerDown** — Capture pointer, store pointer start position, store dragged element reference, mark as dragging, set to Aboslute positioning, and BringToFront

```csharp
_isDragging = true;
target.style.position = Position.Absolute;
target.BringToFront();
target.usageHints = UsageHints.DynamicTransform;
```

2. **PointerMove** — Update dragged element position relative to the pointer, check if pointer is over valid drop target. ALWAYS use the StyleTranslate API for drag and drop and updating element position unless otherwise specified.

Example of using StyleTranslate API:
```csharp
target.style.translate = new StyleTranslate(newWorldPosition);
```

3. **PointerUp** — If over drop target or find nearest overlapping target, execute drop logic (move element, trigger callback), release pointer
4. **PointerCaptureOut** — Finalize drop and raise event

### Drop Target Detection
Use `VisualElement.panel.Pick(position)` or check bounds with `worldBound.Contains(position)` to find elements under the pointer.

### Visual Feedback
Add/remove USS classes on drag start/end for hover states, drag shadows, or drop zone highlights:
```csharp
target.AddToClassList("dragging");
dropZone.AddToClassList("drop-zone-active");
```

## Best Practices
- Use USS classes for visual states instead of inline `element.style.*` properties
- Validate drop targets before executing drop logic
- Use `evt.StopPropagation();` to prevent dragged items from behaving like buttons or interacting unexpectedly
- To ensure dragged item is always on top use `BringToFront()`
- Decide the `pickingMode` for the dragged item to ensure the slot underneath is detected and revert to proper state after dropping

## Common Enhancements

- **Constrain to bounds** — Clamp position to parent or screen bounds
- **StyleTranslation API** - Use `target.style.translate = new StyleTranslate(newWorldPosition)` to avoid style pass updates
- **Performance** - Use Usage Hints and `UsageHints.DynamicTransform;` for better performance
- **Drop validation** — Check if drop target accepts this element type
- **Revert on invalid drop** — Animate back to start position if dropped outside valid zone

## Inventory and Crafting Systems

When users request inventory or crafting systems, first determine requirements:

### Ask Users First
- "Should players be able to drag and drop items between slots?"
- "Should items snap to grid positions or specific equipment slots?"
- "Do items stack? Do they have quantities?"

If drag-and-drop is NOT needed, create static visual layout only (UXML/USS).

### When to Use Drag-Drop

**Inventory systems need drag-drop when:**
- Players move items between slots (inventory, equipment, hotbar)
- Items can be equipped to specific slots (head, chest, weapon)
- Players organize or sort their inventory

**Crafting systems need drag-drop when:**
- Players place ingredients into crafting slots
- Players combine items by dragging them together
- Recipe slots require specific item types

**Implementation Checklist**
When implementing drag-drop for inventory/crafting:
- Created manipulator and callback methods for draggable UI element
- Created UI element or slots as a drop zone
- Added USS classes for dragging and drop states
- Stored item data separately from visual elements (use data binding or C# dictionaries)
- Validated drop targets before executing drop
- Added visual feedback: drag shadows, slot highlighting, hover states
references/svg-icons.md
# SVG Icon Generation (Unity 6.3+)

## Table of Contents

- [When to Use SVG](#when-to-use-svg)
- [Priority Order](#priority-order)
- [SVG Format](#svg-format)
- [Common Icon Examples](#common-icon-examples)
- [Usage in Unity](#usage-in-unity)
- [Tips](#tips)

## When to Use SVG

**Prefer SVG for:**
- Simple icons (arrows, chevrons, checkmarks)
- Geometric shapes
- UI symbols (close, menu, settings)
- Any icon that can be drawn with paths

**Use image generators for:**
- Complex illustrations
- Photorealistic content
- Detailed artwork
- Gradients with many stops

## Priority Order

1. **Reuse existing project icons** — always search first
2. **Generate SVG** — fast, resolution-independent, low cost
3. **Image generators** — last resort, slower and higher cost

## SVG Format

Unity imports SVG as VectorImage assets. Use standard SVG markup:

```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="..." stroke="currentColor" stroke-width="2" fill="none"/>
</svg>
```

## Common Icon Examples

### Arrow Right
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M8 4l8 8-8 8" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
```

### Arrow Left
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M16 4l-8 8 8 8" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
```

### Chevron Down
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M4 8l8 8 8-8" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
```

### Checkmark
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M4 12l6 6L20 6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
```

### Close (X)
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
```

### Plus
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M12 4v16M4 12h16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
```

### Minus
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M4 12h16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
```

### Menu (Hamburger)
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
```

### Settings (Gear)
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" fill="none"/>
  <path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
```

### Search (Magnifying Glass)
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <circle cx="10" cy="10" r="6" stroke="currentColor" stroke-width="2" fill="none"/>
  <path d="M14.5 14.5L20 20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
```

## Usage in Unity

1. Save SVG file to project (e.g., `Assets/UI/Icons/arrow-right.svg`)
2. Unity auto-imports as VectorImage
3. Set "Generated Asset Type" to "UI Toolkit Vector Image" in Inspector
4. Reference in USS:

```uss
.icon-arrow {
  background-image: url("project://database/Assets/UI/Icons/arrow-right.svg");
  width: 24px;
  height: 24px;
}
```

## Tips

- Use `viewBox="0 0 24 24"` for consistent sizing
- Use `stroke="currentColor"` for tintable icons
- Use `fill="none"` for outline-style icons
- Keep paths simple — complex SVGs may not render correctly
references/ui-runtime-binding.md
# Runtime Data Binding Reference

## Table of Contents

- [Core Concepts](#core-concepts)
- [Data Source Setup](#data-source-setup)
- [CRITICAL: Always Use nameof()](#critical-always-use-nameof)
- [Explicit Binding Element](#explicit-binding-element)
- [C# SetBinding (Programmatic)](#c-setbinding-programmatic)
- [PanelRenderer with Bindings](#panelrenderer-with-bindings)

Unity UI Toolkit runtime data binding for efficient UI updates.

## Core Concepts

**Use binding when:**
- Connecting game state to UI (health, score, ammo)
- Multiple UI elements display the same data
- Data changes frequently but predictably

**Avoid binding when:**
- One-time UI updates
- Per-frame updates (use direct property sets)
- Simple direct property assignment is clearer

## Data Source Setup

### Required: [CreateProperty] Attribute

```csharp
using Unity.Properties;
using UnityEngine;

public class PlayerData
{
    [CreateProperty]
    public int Health { get; set; }

    // the private serialized field here helps the user see the property in the editor but do not create the property on the private member unless requested
    [SerializeField, DontCreateProperty]
    private float m_Speed;

    // the public member gets a property created out of it so we bind to this property
    [CreateProperty]
    public float Speed
    {
        get => m_Speed;
        set => m_Speed = value;
    }
}
```


## CRITICAL: Always Use nameof()
```csharp
element.SetBinding("value", new DataBinding
{
    dataSourcePath = new PropertyPath(nameof(HealthData.HealthPercentage))
});
```

**Why nameof():**
- Compile-time safety (no typos)
- Refactoring support
- IntelliSense/autocomplete
- No capitalization errors

**Nested properties:**
```csharp
dataSourcePath = new PropertyPath($"{nameof(PlayerData)}.{nameof(PlayerData.Health)}.{nameof(HealthData.Current)}")
```

### Explicit Binding Element

```xml
<UXML xmlns:ui="UnityEngine.UIElements" xmlns:engine="UnityEngine.UIElements">
    <Slider name="volume-slider">
        <Bindings>
            <engine:DataBinding
                property="value"
                data-source-path="MasterVolume"
                binding-mode="TwoWay"/>
        </Bindings>
    </Slider>
</UXML>
```

The datasource can be set in the root VisualElement like:
```xml
    <ui:VisualElement name="root" data-source="project://database/Assets/Scripts/PlayerData.asset?fileID=11400000&amp;guid=976f7b99fc1424923aee5b5657723366&amp;type=2#PlayerData" class="root">
```
This is so that UIBuilder can also read the datasource and preview the binding and control.

```csharp
private void OnEnable()
{
    var root = GetComponent<UIDocument>().rootVisualElement;
    root.dataSource = m_Settings;
}
```

## C# SetBinding (Programmatic)

### Basic Pattern

```csharp
using UnityEngine;
using UnityEngine.UIElements;

[RequireComponent(typeof(UIDocument))]
public class HealthBarController : MonoBehaviour
{
    [SerializeField] private HealthData m_HealthData;
    private Label m_HealthLabel;
    private ProgressBar m_HealthBar;

    private void OnEnable()
    {
        var root = GetComponent<UIDocument>().rootVisualElement;
        m_HealthLabel = root.Q<Label>("health-label");
        m_HealthBar = root.Q<ProgressBar>("health-bar");

        m_HealthLabel.SetBinding("text", new DataBinding
        {
            dataSourcePath = new PropertyPath(nameof(HealthData.HealthText))
        });

        m_HealthBar.SetBinding("value", new DataBinding
        {
            dataSourcePath = new PropertyPath(nameof(HealthData.HealthPercentage))
        });

        m_HealthLabel.dataSource = m_HealthData;
        m_HealthBar.dataSource = m_HealthData;
    }

    private void OnDisable()
    {
        if (m_HealthLabel?.HasBinding("text") == true)
            m_HealthLabel.ClearBinding("text");
        if (m_HealthBar?.HasBinding("value") == true)
            m_HealthBar.ClearBinding("value");
    }
}
```

### Binding Modes

```csharp
element.SetBinding("text", new DataBinding
{
    dataSourcePath = new PropertyPath(nameof(Data.Score)),
    bindingMode = BindingMode.ToTarget
});

slider.SetBinding("value", new DataBinding
{
    dataSourcePath = new PropertyPath(nameof(Settings.MasterVolume)),
    bindingMode = BindingMode.TwoWay
});
```

**Modes:**
- `ToTarget` - Data → UI (read-only, default)
- `ToSource` - UI → Data (write-only, rare)
- `TwoWay` - Data ↔ UI (input fields)

### Runtime-Created Elements

```csharp
var label = new Label();
label.SetBinding("text", new DataBinding
{
    dataSourcePath = new PropertyPath(nameof(PlayerData.Name))
});
label.dataSource = m_PlayerData;
parentElement.Add(label);
```

## PanelRenderer with Bindings

**Unity 6.6+ only** - `PanelRenderer` is a new UI Toolkit runtime component.

**Use `PanelRenderer` instead of `UIDocument` for runtime UI with bindings.**

`PanelRenderer` provides `RegisterUIReloadCallback` which ensures bindings are properly re-established if the UI reloads.

### PanelRenderer Controller

```csharp
using Unity.Properties;
using UnityEngine;
using UnityEngine.UIElements;

public class StatsController : MonoBehaviour
{
    [SerializeField] private MyStats m_Stats;
    private ScriptableObject m_LoadedData;

    private void Awake()
    {
        m_LoadedData = ScriptableObject.Instantiate(m_Stats);
    }

    private void OnEnable()
    {
        GetComponent<PanelRenderer>().RegisterUIReloadCallback(OnUIReload);
    }

    private void OnUIReload(PanelRenderer panelRenderer, VisualElement rootElement)
    {
        var hpLabel = rootElement.Q("hpLabel");
        var mpLabel = rootElement.Q("mpLabel");

        rootElement.dataSource = m_LoadedData;

        hpLabel.SetBinding("text", new DataBinding
        {
            dataSourcePath = new PropertyPath(nameof(MyStats.HP))
        });

        mpLabel.SetBinding("text", new DataBinding
        {
            dataSourcePath = new PropertyPath(nameof(MyStats.MP))
        });
    }
}
```

**Key benefits:**
- Callback handles UI reload events automatically
- Bindings re-established if UI is reloaded
- Cleaner than manual OnEnable setup
- Works with ScriptableObject data sources
references/uss-guide.md
# USS Patterns and Examples

## Table of Contents

- [Design Tokens](#design-tokens)
- [Transitions](#transitions)
- [Pseudo-State Tinting](#pseudo-state-tinting)
- [Text Wrapping](#text-wrapping)
- [9-Slice Backgrounds](#9-slice-backgrounds)
- [Child vs Descendant Selectors](#child-vs-descendant-selectors)
- [Specificity](#specificity)

## Design Tokens

Use `:root` variables for repeated values:

```uss
:root {
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --color-primary: #4da3ff;
  --color-bg-dark: #1a1a1a;
  --color-text: #ffffff;
}

.container {
  padding: var(--spacing-md);
  background-color: var(--color-bg-dark);
  color: var(--color-text);
}

.button {
  padding: var(--spacing-sm) var(--spacing-md);
  background-color: var(--color-primary);
}
```

## Transitions

Define transition properties on the **base class**, not on `:hover`. Otherwise hover-out won't animate.

```uss
/* CORRECT */
.button {
  background-color: #4da3ff;
  transition-duration: 0.2s;
}
.button:hover {
  background-color: #6db3ff;
}

/* WRONG - transition on :hover won't animate out */
.button {
  background-color: #4da3ff;
}
.button:hover {
  background-color: #6db3ff;
  transition-duration: 0.2s;
}
```

## Pseudo-State Tinting

Prefer tinting one image instead of creating multiple image variants:

```uss
.button {
  background-image: url("project://database/Assets/UI/Textures/button-bg.png");
}

.button:hover {
  -unity-background-image-tint-color: rgba(255, 255, 255, 0.15);
}

.button:active {
  -unity-background-image-tint-color: rgba(0, 0, 0, 0.2);
}

.button:disabled {
  -unity-background-image-tint-color: rgba(128, 128, 128, 0.5);
}
```

## Text Wrapping

Labels don't wrap by default. Enable wrapping explicitly:

```uss
.description-text {
  white-space: normal;
  overflow: visible;
}
```

## 9-Slice Backgrounds

For scalable backgrounds that stretch without distorting edges:

```uss
.panel-background {
  background-image: url("project://database/Assets/UI/Textures/panel-bg.png");
  -unity-slice-left: 12;
  -unity-slice-top: 12;
  -unity-slice-right: 12;
  -unity-slice-bottom: 12;
  -unity-slice-scale: 1;
}
```

Slice values define the non-stretched border regions in pixels.

## Child vs Descendant Selectors

Prefer child selectors for performance:

```uss
/* BETTER - child selector */
.panel > .header > .title { }

/* AVOID - descendant selector (slower) */
.panel .header .title { }
```

## Specificity

More specific selectors override less specific ones. If your styles aren't applying, check for conflicting selectors:

```uss
/* Less specific */
.button { color: white; }

/* More specific - wins */
.panel .button { color: black; }

/* Even more specific - wins */
.panel > .content > .button { color: red; }
```
SKILL.md
---
name: ui-uitk
description: Unity UI Toolkit expert for Unity 6.0+. Understands, edits, and generates UXML and USS files with flex-based layouts. Use for requests involving .uxml, .uss, UI Toolkit, UIElements, UIDocument, UI runtime binding, Custom UI Elements, Manipulators or PanelSettings.
allowed-tools:
  - Read
  - Write
  - Edit
  - Glob
  - Grep
---

Understand existing Unity UI Toolkit code, make targeted edits, generate new UXML/USS files, Manipulators, and handle UI runtime binding.

## References

Read these as needed:
- `references/uss-guide.md` — USS patterns and examples
- `references/svg-icons.md` — SVG icon generation (only when generating icons)
- `references/common-issues.md` — Common mistakes to avoid
- `references/ui-runtime-binding.md` — Patterns and guide to bind data to UI at runtime (only when requested or when bindings are involved)
- `references/painter2d.md` — Painter2D API for custom visuals: gradients, shapes, arcs, procedural drawing (read this whenever gradients, custom shapes, progress rings, procedural drawing, or any visual beyond what USS can express is needed)
- `references/pointermanipulator-guide.md` — Patterns and guide to create and use Manipulators (only when requested or when manipulators are involved). This helps with setting up drag and drop features or simple event handling for a Visual Element.
- `references/custom-elements.md` — Custom UI Element patterns and guide to create reusable components with UXML, USS, and C#. This helps with creating complex UI components for reuse across the project.

Paths are relative to this skill's folder — read `references/uss-guide.md` directly.

## Understanding

When explaining UI structure, use this format:
```
[ElementType] name="elementName" class="class1 class2"
├── [ChildType] name="childName"
│   └── [GrandchildType]
└── [ChildType] class="another-class"
```

## Editing

**Common edit requests:**

| Request | Action |
|---------|--------|
| "Change button color" | Edit USS selector for that button |
| "Add a label here" | Add element to UXML at specified location |
| "Make this bigger" | Edit width/height in USS |
| "Hide this element" | Add `display: none` to USS or remove from UXML |
| "Rename this element" | Update `name` attribute in UXML |

**Don't over-edit:**
- Change only what's requested
- Preserve formatting and structure
- Don't "improve" unrelated code
- Don't add comments unless asked
- For targeted changes, prefer modifying specific elements or selectors over rewriting entire files — but use judgment; if a change touches most of the file, a rewrite may be cleaner
- Be careful not to accidentally drop existing elements, styles, or references when making edits
- **When editing USS**, focus on the properties and selectors relevant to the request — avoid unnecessary reorganization, but restructure if the change genuinely requires it

## Validation

There is no way to validate UXML or USS from outside the Editor — Unity parses these
files on import and reports problems in the Console. Write the files, then have the
user check the result.

**Workflow:**

1. Write the complete file to its target path in the project. Do not write partial or
   draft content — a half-written UXML file is a parse error the moment the Editor
   picks it up.
2. Ask the user to focus the Unity Editor. That triggers a reimport of the changed
   assets.
3. Ask them to report anything in the Console. UXML parse errors name the file and
   line; USS problems appear as warnings about unknown properties or selectors.
4. Fix what they report and repeat from step 1.

**Because feedback costs a user round-trip, get it right the first time:**

- Finish all files before asking the user to check, so one reimport covers everything
  rather than one per file.
- Re-read `references/uss-guide.md` and `references/common-issues.md` before writing,
  rather than after an error comes back.
- Watch for the mistakes that survive a parse but render wrong — those will not appear
  in the Console at all, so the user has to eyeball the UI. `references/common-issues.md`
  lists them.

## Generation

When creating new UI:

**Generate only what is requested:**

| Request | Output |
|---------|--------|
| USS only | `.uss` file only |
| UXML only | `.uxml` file only |
| UI screen / menu / panel | `.uss` + `.uxml` only |
| "with code" / "with logic" / "functional" | `.uss` + `.uxml` + `.cs` |

**These do NOT imply C#:**
- "proper buttons" → well-styled Button elements
- "currency display" → a Label element
- "working UI" → valid UXML/USS that renders
- "inventory screen" → visual layout only
- "inventory system" / "equipment system" / "crafting system" → Ask: "Should items be draggable?" If yes, see `references/pointermanipulator-guide.md` for patterns

**Generation workflow:**
1. **Analyze** — Determine exactly what files are needed. No extras.
2. **Search** — Find existing USS, UXML, assets. Don't assume paths.
3. **Follow project patterns** — Match folder structure and naming conventions.
4. **Reuse** — Check for shared stylesheets. Reuse if appropriate.
5. **Write USS first** — Verify against restrictions below.
6. **Write UXML** — Reference the USS, verify structure.
7. **Write the files out complete** — never partial content; see Validation above for
   how errors come back and why one round of files beats several.
8. **Scene setup** — Assign PanelSettings if adding UI to scene.
9. **Data binding** — If requested, add C# script with runtime data binding patterns (see `references/ui-runtime-binding.md`). Generate the scriptable object asset if needed. Assign the asset to the UI element root in UXML or via datasource in C#.

**Color, visibility, and specification rules:**
- **Ensure text is readable by default:** When choosing colors, ensure text contrasts with its background — but respect intentional low-contrast uses (disabled states, placeholder text, decorative elements). When using design tokens, check that text and background variables provide adequate contrast.
- **Honor exact values:** User-specified hex colors, pixel dimensions, spacing — use exactly as given. Do not approximate or substitute.

**Styling / Theme**
When styling UI or adjusting theme make sure to not only apply to the elements directly in the current UXML but also to the core elements of UI Toolkit which are composed of several child elements usually.

## Conventions

**Follow project patterns first.** Search existing files before applying defaults.

| Type | Convention | Good | Bad |
|------|------------|------|-----|
| `name` attribute | camelCase | `submitButton` | `submit-button` |
| `class` attribute / USS | kebab-case | `.submit-button` | `.submitButton` |
| File paths | Feature folders | `Assets/UI/Inventory/` | `Assets/Scripts/UI/` |

**Output format:**
```uxml Filename.uxml
<ui:UXML>...</ui:UXML>
```
```uss Filename.uss
.class { ... }
```

## USS Restrictions

Unity's USS is a subset of CSS. These properties do NOT exist — NEVER use them:

| NEVER Use | Use Instead |
|-----------|-------------|
| `border` shorthand | `border-width`, `border-color` separately |
| `gap` | `margin` on children |
| `z-index` | DOM order or parent nesting |
| `pointer-events` | `picking-mode` UXML attribute |
| `filter` | Not supported |
| `outline` | `border-*` properties |
| `box-shadow` | Nested elements or background image |
| `:first-child`, `:last-child`, `:nth-child` | Explicit classes |
| `[attribute]` selectors | Explicit classes |
| `transition-property: <value>` | Omit entirely, or `none`/`initial`/`inherit` only |
| `linear-gradient()`, `radial-gradient()` | Custom `VisualElement` with Painter2D (see `references/painter2d.md`) |

**Inline styles:** NEVER use `style="..."` in UXML. All styling in USS only.

**External URLs:** NEVER use `url()` with external paths. Only `url("project://database/Assets/...")`.

**Prefer flexible layouts over hardcoded sizes:**
- Use `flex-grow`, `flex-shrink`, or `%` instead of fixed `width`/`height` values
- Let elements flow naturally and be constrained by their parent container
- Set explicit pixel sizes only on root containers or when a fixed size is truly required
- Child elements should adapt to available space rather than define their own dimensions

## USS Brevity

- No default values (`flex-direction: column` is default)
- No default fonts
- No redundant constraints (`width: 100px` doesn't need `min-width`/`max-width`)
- No overlapping properties (`flex: 1` already sets grow/shrink)
- Simplest selector that works
- Never duplicate selectors

## UXML

Every file must:
1. Declare namespace: `<ui:UXML xmlns:ui="UnityEngine.UIElements">`
2. Link stylesheet(s): `<ui:Style src="Screen.uss" />`
3. Have exactly one top-level container
4. **No `style="..."` attributes** — use USS only

```uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
  <ui:Style src="Panel.uss" />
  <ui:VisualElement name="root" class="panel">
    <!-- content -->
  </ui:VisualElement>
</ui:UXML>
```

## Events and Interactivity
- Use Pointer Manipulators for event handling and interactivity on a VisualElement (see `references/pointermanipulator-guide.md`)
- If drag and drop is requested then write a pointer Manipulator and attach it to the relevant Visual Element in UXML or via C#.
- For simple click events, you can use the `clickable` manipulator in UXML
- For more advanced interactions, create use more traditional event callbacks in C# and attach them to elements as needed

**For inventory and crafting systems:**
- When users request an "inventory system", "equipment system", or "crafting system", ask explicitly: "Should players be able to drag and drop items?"
- If yes, read `references/pointermanipulator-guide.md` for inventory/crafting-specific patterns
- If no or unclear, create static layout only

## Assets

**Do NOT reference `UnityDefaultRuntimeTheme.tss`** or Unity's built-in theme icons.

**Icon priority:**
1. Reuse existing project icons
2. Generate SVG (see `references/svg-icons.md`)
3. Image generators (last resort)

**Reference format:**
```uss
background-image: url("project://database/Assets/UI/Textures/icon.png");
```

## Scene Setup

**PanelSettings is required** — UI won't render without it.

1. Search for existing PanelSettings asset
2. If none, create generic: `Assets/UI/PanelSettings.asset`
3. Assign to UIDocument's `Panel Settings` field

Skip for Editor UI (EditorWindow, PropertyDrawer).

## C# (Only When Requested)

- Style via USS classes (`AddToClassList()`) — never use `element.style.*` as inline styles have higher specificity than USS selectors, making them impossible to override via stylesheets, and add per-element memory overhead
- UITK uses TextCore text assets — use `FontAsset`, `TextStyleSheet`, and `TextSettings`, not their TextMeshPro equivalents (`TMP_FontAsset`, etc.)
- Place scripts in same folder as UXML/USS