Unity Game UI Toolkit Design Skill

SkillMedia

Game UI design using Unity's UI Toolkit (USS/UXML/Flexbox). Includes game UI elements like HUD, health bars, inventory, skill bars, PanelSettings scaling, and Safe Area support. Use when: game UI design, HUD creation, USS/UXML styling, Flexbox layout, PanelSettings configuration

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Unity Game UI Toolkit Design Skill skill

What this skill tells your AI

The instructions your AI receives, as published by akiojin/unity-mcp-server in .claude-plugin/plugins/unity-mcp-server/skills/unity-game-ui-toolkit-design/SKILL.md and read by ahel’s review.

A skill for game UI design using Unity's UI Toolkit (USS/UXML/Flexbox). This comprehensive game UI design guide covers implementation patterns for game UI elements like HUD, health bars, inventory, dialogs, screen scaling with PanelSettings, and Safe Area support.

Overview

UI Toolkit is Unity's next-generation UI system that builds UIs with an approach similar to web technologies (HTML/CSS).

FeatureDetails
Layout EngineYoga (CSS Flexbox subset implementation)
StylingUSS (Unity Style Sheets, CSS-like)
MarkupUXML (HTML-like)
Supported VersionUnity 2021.2+ (Unity 6.0+ recommended)
Use CasesGame UI, Editor extensions

Game UI Types

Game UIs are classified into 4 types based on purpose and presentation method. Before starting implementation, clarify which type your UI belongs to.

1. Diegetic

UI that physically exists within the game world. Characters can also perceive it.

ExampleGame
HP bar on suit's backDead Space
Car dashboardRacing Games
Ammo count on weaponMetro Exodus
Handheld mapFar Cry 2
<!-- UXML - Diegetic UI (placed in 3D space) -->
<ui:VisualElement class="diegetic-display">
    <ui:VisualElement class="diegetic-display__screen">
        <ui:Label class="diegetic-display__value" text="87" />
        <ui:Label class="diegetic-display__unit" text="%" />
    </ui:VisualElement>
</ui:VisualElement>

2. Non-Diegetic

Screen overlay. Pure player-facing information that characters cannot perceive.

ExamplePlacement
HP barTop-left
MinimapTop-right
Skill barBottom-center
Quest objectivesRight side
<!-- UXML - Non-Diegetic HUD structure -->
<ui:VisualElement class="hud">
    <!-- Top-left: Player status -->
    <ui:VisualElement class="hud__top-left">
        <ui:VisualElement class="health-bar" />
        <ui:VisualElement class="mana-bar" />
    </ui:VisualElement>

    <!-- Top-right: Minimap -->
    <ui:VisualElement class="hud__top-right">
        <ui:VisualElement class="minimap" />
    </ui:VisualElement>

    <!-- Bottom-center: Action bar -->
    <ui:VisualElement class="hud__bottom-center">
        <ui:VisualElement class="action-bar" />
    </ui:VisualElement>
</ui:VisualElement>

3. Spatial

UI that exists within the game world but characters cannot perceive.

Example
HP bar above enemy's head
NPC name display
Interactable object icons
Path guide lines

4. Meta

Expresses game state through screen effects. Not direct UI elements.

ExampleRepresentation
DamageRed vignette at screen edges
Low HPRed pulse across entire screen
Status effectsScreen distortion/color changes
UnderwaterBlue overlay
/* USS - Meta effect */
.meta-overlay {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(255, 0, 0, 0);
    transition-duration: 0.3s;
}

.meta-overlay--damage {
    background-color: rgba(255, 0, 0, 0.3);
}

.meta-overlay--low-health {
    /* Pulse animation */
}

HUD Screen Layout

Game HUDs have established placement conventions. Players unconsciously expect this layout.

┌─────────────────────────────────────────────────────┐
│ [HP/MP/Status]                    [Minimap/Compass] │
│ [Buff/Debuff icons]               [Quest Objectives]│
│                                                     │
│                     Game Screen                     │
│                    (Focus Area)                     │
│                                                     │
│ [Chat]                                              │
│ [Log/Notifications]  [Skill Bar/Items] [Quick Slots]│
└─────────────────────────────────────────────────────┘

Placement Principles

AreaElementsReason
Top-leftHP, MP, StaminaMost important status, gaze naturally goes there
Top-rightMinimap, CompassNavigation info, frequently checked
Bottom-centerSkill bar, Action barFeels close to hands, corresponds to keyboard layout
Bottom-rightInventory, Quick slotsSecondary info, affinity with mouse operation
Bottom-leftChat, LogText info, social elements
Right side verticalQuest objectives, NotificationsAdditional info, temporary display
Screen centerAvoidDon't obstruct gameplay visibility
/* USS - HUD grid layout */
.hud {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
}

.hud__top-left {
    position: absolute;
    left: 16px;
    top: 16px;
}

.hud__top-right {
    position: absolute;
    right: 16px;
    top: 16px;
}

.hud__bottom-center {
    position: absolute;
    left: 50%;
    bottom: 16px;
    translate: -50% 0;
}

.hud__bottom-left {
    position: absolute;
    left: 16px;
    bottom: 16px;
}

.hud__bottom-right {
    position: absolute;
    right: 16px;
    bottom: 80px; /* Above action bar */
}

.hud__right-side {
    position: absolute;
    right: 16px;
    top: 200px;
    width: 250px;
}

Quick Start

Basic UIDocument Setup

// 1. Create GameObject with UIDocument
mcp__unity-mcp-server__create_gameobject({
  name: "UIManager"
})

// 2. Add UIDocument component
mcp__unity-mcp-server__add_component({
  gameObjectPath: "/UIManager",
  componentType: "UIDocument"
})

// 3. Configure PanelSettings
mcp__unity-mcp-server__set_component_field({
  gameObjectPath: "/UIManager",
  componentType: "UIDocument",
  fieldPath: "panelSettings",
  valueType: "objectReference",
  objectReference: {
    assetPath: "Assets/UI/PanelSettings.asset"
  }
})

Core Concepts

1. PanelSettings (Screen Scaling)

PanelSettings is equivalent to uGUI's Canvas Scaler and controls UI scaling based on screen size.

Scale Mode List
ModeUse CaseFeatures
Constant Pixel SizeDesktop1:1 pixel correspondence (default)
Constant Physical SizeMulti-DPIDPI independent, constant physical size
Scale With Screen SizeMobileScales relative to reference resolution
Scale With Screen Size Settings
// PanelSettings configuration example
[CreateAssetMenu(menuName = "UI/Panel Settings")]
public class ResponsivePanelSettings : ScriptableObject
{
    // Create PanelSettings asset and configure:
    // Scale Mode: Scale With Screen Size
    // Reference Resolution: 1080 x 1920 (Portrait) or 1920 x 1080 (Landscape)
    // Screen Match Mode: Match Width Or Height
    // Match: 0 (Width priority for Portrait) / 1 (Height priority for Landscape)
}
Runtime Match Switching
// OrientationScaleHandler.cs
using UnityEngine;
using UnityEngine.UIElements;

public class OrientationScaleHandler : MonoBehaviour
{
    [SerializeField] private PanelSettings panelSettings;
    private ScreenOrientation lastOrientation;

    void Update()
    {
        if (Screen.orientation != lastOrientation)
        {
            bool isPortrait = Screen.height > Screen.width;
            panelSettings.match = isPortrait ? 0f : 1f;
            lastOrientation = Screen.orientation;
        }
    }
}

2. Flexbox Layout

UI Toolkit uses the Yoga (Flexbox) layout engine. Web developers will find this CSS layout model familiar.

flex-direction
/* USS - Vertical layout (default) */
.vertical-container {
    flex-direction: column;
}

/* USS - Horizontal layout */
.horizontal-container {
    flex-direction: row;
}
flex-grow / flex-shrink / flex-basis
/* USS - Equal distribution */
.equal-child {
    flex-grow: 1;
    flex-basis: 0;  /* Ignore content size for equal distribution */
}

/* USS - Fixed size + flexible */
.fixed-header {
    flex-grow: 0;
    flex-shrink: 0;
    height: 60px;
}

.flexible-content {
    flex-grow: 1;
    flex-shrink: 1;
}
Percentage-based Sizing
/* USS - Responsive sizing */
.responsive-panel {
    width: 80%;
    height: 100%;
    max-width: 600px;
}

.half-width {
    width: 50%;
}
align-items / justify-content
/* USS - Center alignment */
.center-container {
    align-items: center;      /* Cross-axis center */
    justify-content: center;  /* Main-axis center */
}

/* USS - Space between + equal spacing */
.space-between-container {
    justify-content: space-between;
}

/* USS - End alignment */
.end-aligned {
    align-items: flex-end;
    justify-content: flex-end;
}

3. USS (Unity Style Sheets)

Define styles with CSS-like syntax.

Basic Syntax
/* USS - Selector types */
.class-selector { }      /* Class selector */
#name-selector { }       /* Name selector */
Button { }               /* Type selector */
.parent > .child { }     /* Direct child selector */
.parent .descendant { }  /* Descendant selector */
.element:hover { }       /* Pseudo-class */
BEM Naming Convention
/* Block */
.menu { }

/* Element */
.menu__item { }
.menu__title { }

/* Modifier */
.menu--horizontal { }
.menu__item--active { }
.menu__item--disabled { }

4. UXML (UI Markup Language)

Define UI structure with HTML-like syntax.

<?xml version="1.0" encoding="utf-8"?>
<ui:UXML xmlns:ui="UnityEngine.UIElements">
    <ui:VisualElement class="root">
        <ui:VisualElement class="header">
            <ui:Label text="Title" class="header__title" />
        </ui:VisualElement>

        <ui:VisualElement class="content">
            <ui:ScrollView class="content__scroll">
                <ui:VisualElement class="content__list">
                    <!-- Dynamic items -->
                </ui:VisualElement>
            </ui:ScrollView>
        </ui:VisualElement>

        <ui:VisualElement class="footer">
            <ui:Button text="Action" class="footer__button" />
        </ui:VisualElement>
    </ui:VisualElement>
</ui:UXML>

Mobile Responsive Design

Responsive Layout Structure

/* USS - Mobile responsive basic structure */
.root {
    flex-grow: 1;
    flex-direction: column;
}

.header {
    flex-shrink: 0;
    height: 60px;
    flex-direction: row;
    align-items: center;
    padding: 0 16px;
}

.content {
    flex-grow: 1;
    flex-shrink: 1;
}

.footer {
    flex-shrink: 0;
    height: 80px;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    padding: 0 16px;
}

Safe Area Support

UI Toolkit requires coordinate system conversion (Screen.safeArea uses bottom-left origin, UI Toolkit uses top-left origin).

// SafeAreaController.cs
using UnityEngine;
using UnityEngine.UIElements;

public class SafeAreaController : MonoBehaviour
{
    private UIDocument uiDocument;
    private VisualElement safeAreaContainer;
    private Rect lastSafeArea;

    void Start()
    {
        uiDocument = GetComponent<UIDocument>();
        safeAreaContainer = uiDocument.rootVisualElement.Q<VisualElement>("safe-area");
        ApplySafeArea();
    }

    void Update()
    {
        if (Screen.safeArea != lastSafeArea)
        {
            ApplySafeArea();
        }
    }

    void ApplySafeArea()
    {
        Rect safeArea = Screen.safeArea;
        lastSafeArea = safeArea;

        // Convert to UI Toolkit coordinate system (top-left origin)
        float left = safeArea.x;
        float right = Screen.width - (safeArea.x + safeArea.width);
        float top = Screen.height - (safeArea.y + safeArea.height);
        float bottom = safeArea.y;

        // Consider PanelSettings scale
        var panelSettings = uiDocument.panelSettings;
        float scale = GetCurrentScale(panelSettings);

        safeAreaContainer.style.paddingLeft = left / scale;
        safeAreaContainer.style.paddingRight = right / scale;
        safeAreaContainer.style.paddingTop = top / scale;
        safeAreaContainer.style.paddingBottom = bottom / scale;
    }

    float GetCurrentScale(PanelSettings settings)
    {
        // Calculate scale for Scale With Screen Size
        if (settings.scaleMode == PanelScaleMode.ScaleWithScreenSize)
        {
            var refRes = settings.referenceResolution;
            float widthRatio = Screen.width / refRes.x;
            float heightRatio = Screen.height / refRes.y;
            return Mathf.Lerp(widthRatio, heightRatio, settings.match);
        }
        return 1f;
    }
}
/* USS - Safe Area container */
#safe-area {
    flex-grow: 1;
    /* padding is set dynamically from C# */
}

Dynamic Layout Switching (Media Query Alternative)

UI Toolkit doesn't support CSS @media queries, so switch styles dynamically with C#.

// ResponsiveLayoutController.cs
using UnityEngine;
using UnityEngine.UIElements;

public class ResponsiveLayoutController : MonoBehaviour
{
    private UIDocument uiDocument;
    private VisualElement root;
    private bool wasPortrait;

    void Start()
    {
        uiDocument = GetComponent<UIDocument>();
        root = uiDocument.rootVisualElement;
        ApplyOrientationLayout();
    }

    void Update()
    {
        bool isPortrait = Screen.height > Screen.width;
        if (isPortrait != wasPortrait)
        {
            ApplyOrientationLayout();
            wasPortrait = isPortrait;
        }
    }

    void ApplyOrientationLayout()
    {
        bool isPortrait = Screen.height > Screen.width;

        root.RemoveFromClassList("landscape");
        root.RemoveFromClassList("portrait");
        root.AddToClassList(isPortrait ? "portrait" : "landscape");

        // Layout based on screen width
        float screenWidth = Screen.width;
        root.RemoveFromClassList("narrow");
        root.RemoveFromClassList("wide");

        if (screenWidth < 600)
        {
            root.AddToClassList("narrow");
        }
        else
        {
            root.AddToClassList("wide");
        }
    }
}
/* USS - Orientation-based styles */
.portrait .sidebar {
    display: none;
}

.landscape .sidebar {
    display: flex;
    width: 250px;
}

/* USS - Screen width-based styles */
.narrow .content__grid {
    flex-direction: column;
}

.wide .content__grid {
    flex-direction: row;
    flex-wrap: wrap;
}

.narrow .card {
    width: 100%;
}

.wide .card {
    width: 48%;
    margin: 1%;
}

Game UI Elements Implementation

1. Health Bar / Resource Bar

<!-- UXML - Health bar -->
<ui:VisualElement class="resource-bar health-bar">
    <ui:VisualElement class="resource-bar__background">
        <ui:VisualElement class="resource-bar__fill" name="health-fill" />
        <ui:VisualElement class="resource-bar__delayed-fill" name="health-delayed" />
    </ui:VisualElement>
    <ui:Label class="resource-bar__text" name="health-text" text="100/100" />
</ui:VisualElement>
/* USS - Resource bar */
.resource-bar {
    width: 200px;
    height: 24px;
    flex-direction: row;
    align-items: center;
}

.resource-bar__background {
    flex-grow: 1;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    overflow: hidden;
}

.resource-bar__fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 100%;
    background-color: #e74c3c;
    transition-duration: 0.2s;
}

.resource-bar__delayed-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 100%;
    background-color: #c0392b;
    transition-duration: 0.5s;
    transition-delay: 0.3s;
}

.resource-bar__text {
    position: absolute;
    left: 0;
    right: 0;
    -unity-text-align: middle-center;
    color: white;
    font-size: 12px;
    text-shadow: 1px 1px 2px black;
}

/* Variations */
.mana-bar .resource-bar__fill {
    background-color: #3498db;
}

.stamina-bar .resource-bar__fill {
    background-color: #2ecc71;
}

.xp-bar .resource-bar__fill {
    background-color: #9b59b6;
}
// ResourceBarController.cs
public class ResourceBarController
{
    private VisualElement fill;
    private VisualElement delayedFill;
    private Label text;

    public void SetValue(float current, float max)
    {
        float percent = current / max;
        fill.style.width = Length.Percent(percent * 100);
        delayedFill.style.width = Length.Percent(percent * 100);
        text.text = $"{(int)current}/{(int)max}";
    }
}

2. Skill Cooldown

<!-- UXML - Skill slot -->
<ui:VisualElement class="skill-slot">
    <ui:VisualElement class="skill-slot__icon" />
    <ui:VisualElement class="skill-slot__cooldown-overlay" name="cooldown-overlay" />
    <ui:Label class="skill-slot__cooldown-text" name="cooldown-text" />
    <ui:Label class="skill-slot__keybind" text="Q" />
</ui:VisualElement>
/* USS - Skill slot */
.skill-slot {
    width: 64px;
    height: 64px;
    margin: 4px;
    background-color: rgba(0, 0, 0, 0.6);
    border-width: 2px;
    border-color: #555;
    border-radius: 8px;
}

.skill-slot__icon {
    position: absolute;
    left: 4px;
    top: 4px;
    right: 4px;
    bottom: 4px;
    background-image: resource("Icons/skill_placeholder");
}

.skill-slot__cooldown-overlay {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.7);
    /* Circular mask implemented with shader */
}

.skill-slot__cooldown-text {
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    translate: 0 -50%;
    -unity-text-align: middle-center;
    font-size: 20px;
    color: white;
    -unity-font-style: bold;
}

.skill-slot__keybind {
    position: absolute;
    right: 2px;
    bottom: 2px;
    font-size: 12px;
    color: #aaa;
    background-color: rgba(0, 0, 0, 0.5);
    padding: 2px 4px;
    border-radius: 2px;
}

.skill-slot--ready {
    border-color: #f39c12;
}

.skill-slot--active {
    border-color: #2ecc71;
    border-width: 3px;
}

3. Inventory Grid

<!-- UXML - Inventory -->
<ui:VisualElement class="inventory-panel">
    <ui:VisualElement class="inventory-panel__header">
        <ui:Label text="Inventory" class="inventory-panel__title" />
        <ui:Button class="inventory-panel__close" text="×" />
    </ui:VisualElement>
    <ui:VisualElement class="inventory-panel__grid" name="inventory-grid">
        <!-- Dynamically generated -->
    </ui:VisualElement>
    <ui:VisualElement class="inventory-panel__footer">
        <ui:Label name="gold-label" text="Gold: 0" />
        <ui:Label name="weight-label" text="Weight: 0/100" />
    </ui:VisualElement>
</ui:VisualElement>
/* USS - Inventory */
.inventory-panel {
    width: 400px;
    background-color: rgba(20, 20, 30, 0.95);
    border-width: 2px;
    border-color: #444;
    border-radius: 8px;
}

.inventory-panel__header {
    height: 40px;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: 0 12px;
    background-color: rgba(0, 0, 0, 0.3);
    border-bottom-width: 1px;
    border-bottom-color: #333;
}

.inventory-panel__grid {
    flex-direction: row;
    flex-wrap: wrap;
    padding: 8px;
}

.inventory-slot {
    width: 48px;
    height: 48px;
    margin: 2px;
    background-color: rgba(0, 0, 0, 0.4);
    border-width: 1px;
    border-color: #333;
    border-radius: 4px;
}

.inventory-slot:hover {
    border-color: #666;
    background-color: rgba(255, 255, 255, 0.1);
}

.inventory-slot--selected {
    border-color: #f39c12;
    border-width: 2px;
}

.inventory-slot__icon {
    position: absolute;
    left: 4px;
    top: 4px;
    right: 4px;
    bottom: 12px;
}

.inventory-slot__count {
    position: absolute;
    right: 2px;
    bottom: 2px;
    font-size: 10px;
    color: white;
    background-color: rgba(0, 0, 0, 0.6);
    padding: 1px 3px;
    border-radius: 2px;
}

/* Item rarity */
.inventory-slot--common { border-color: #aaa; }
.inventory-slot--uncommon { border-color: #2ecc71; }
.inventory-slot--rare { border-color: #3498db; }
.inventory-slot--epic { border-color: #9b59b6; }
.inventory-slot--legendary { border-color: #f39c12; }

4. Damage Numbers (Floating Text)

// DamageNumberController.cs
using UnityEngine;
using UnityEngine.UIElements;

public class DamageNumberController : MonoBehaviour
{
    [SerializeField] private UIDocument uiDocument;
    [SerializeField] private VisualTreeAsset damageNumberTemplate;

    public void SpawnDamageNumber(Vector3 worldPos, int damage, DamageType type)
    {
        var root = uiDocument.rootVisualElement;
        var damageLabel = damageNumberTemplate.Instantiate();

        var label = damageLabel.Q<Label>("damage-text");
        label.text = damage.ToString();

        // Style based on damage type
        switch (type)
        {
            case DamageType.Critical:
                label.AddToClassList("damage-number--critical");
                label.text = damage.ToString() + "!";
                break;
            case DamageType.Heal:
                label.AddToClassList("damage-number--heal");
                label.text = "+" + damage.ToString();
                break;
        }

        // Convert world coordinates to screen coordinates
        Vector2 screenPos = Camera.main.WorldToScreenPoint(worldPos);
        // Convert to UI Toolkit coordinate system (Y-axis inverted)
        float uiY = Screen.height - screenPos.y;

        damageLabel.style.position = Position.Absolute;
        damageLabel.style.left = screenPos.x;
        damageLabel.style.top = uiY;

        root.Add(damageLabel);

        // Remove after animation
        damageLabel.schedule.Execute(() => {
            damageLabel.RemoveFromHierarchy();
        }).ExecuteLater(1000);
    }
}
/* USS - Damage numbers */
.damage-number {
    position: absolute;
    font-size: 24px;
    color: white;
    -unity-font-style: bold;
    text-shadow: 2px 2px 4px black;
    transition-property: translate, opacity;
    transition-duration: 1s;
    translate: 0 0;
    opacity: 1;
}

.damage-number--animate {
    translate: 0 -50px;
    opacity: 0;
}

.damage-number--critical {
    font-size: 36px;
    color: #e74c3c;
}

.damage-number--heal {
    color: #2ecc71;
}

.damage-number--miss {
    color: #888;
    font-size: 18px;
}

5. Minimap

<!-- UXML - Minimap -->
<ui:VisualElement class="minimap">
    <ui:VisualElement class="minimap__frame">
        <ui:VisualElement class="minimap__content" name="minimap-content">
            <!-- RenderTexture set as background -->
        </ui:VisualElement>
        <ui:VisualElement class="minimap__player-icon" />
        <ui:VisualElement class="minimap__markers" name="minimap-markers">
            <!-- Dynamic markers -->
        </ui:VisualElement>
    </ui:VisualElement>
    <ui:VisualElement class="minimap__compass">
        <ui:Label text="N" class="minimap__compass-label" />
    </ui:VisualElement>
</ui:VisualElement>
/* USS - Minimap */
.minimap {
    width: 180px;
    height: 180px;
}

.minimap__frame {
    width: 100%;
    height: 100%;
    border-radius: 90px; /* Circular */
    border-width: 3px;
    border-color: rgba(0, 0, 0, 0.8);
    overflow: hidden;
}

.minimap__content {
    width: 100%;
    height: 100%;
    /* RenderTexture set from C# */
}

.minimap__player-icon {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 12px;
    translate: -50% -50%;
    background-color: #3498db;
    border-radius: 6px;
    border-width: 2px;
    border-color: white;
}

.minimap__marker {
    position: absolute;
    width: 8px;
    height: 8px;
    border-radius: 4px;
}

.minimap__marker--enemy {
    background-color: #e74c3c;
}

.minimap__marker--quest {
    background-color: #f39c12;
}

.minimap__marker--poi {
    background-color: #2ecc71;
}

6. Dialog System

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
34
Forks
7
Last commit
Sep 2026
Advanced
Item type
skill
Key
unity-game-ui-toolkit-design
Source
github.com/akiojin/unity-mcp-server