Skip to content

TomMoore515/TypeUSS

Repository files navigation

TypeUSS

Typesafe C# authoring for Unity UI Toolkit Stylesheets

Write your UI Toolkit styles in C# with full IntelliSense and compile-time safety, then generate standard .uss files automatically. Get the best of both worlds: the developer experience of C# and the runtime performance of USS.

TypeUSS Unity C# USS

Note

The code contained in this package is from my personal Unity project Starfoundry, while we are using it in production, that does not mean it is perfect or stable - this package should be considered experimental and the API may change without warning.

Overview

Unity UI Toolkit forces you to choose between:

Approach Type Safety IntelliSense Performance
Inline C# ❌ Poor (per-element allocation)
USS files ✅ High (native UI Toolkit)

TypeUSS lets you author styles in C# and generates USS automatically:

// ❌ Before: No IntelliSense, typos compile fine, painful to maintain
.chat-container {
    widht: 400px;  /* Typo - no error until runtime */
    background-color: rgba(0, 0, 0, 0.7);
}
// ✅ After: Full type safety, autocomplete, can share files with C# based component definitions
[GenerateUSS("UI/Generated/Chat.uss")]
public static class ChatStyles
{
    public static readonly Selector Container = Sel.Class("chat-container");
    
    public static readonly TypeStyle ContainerStyle = Container.Style(s => s
        .Width(400)                              // Autocomplete works!
        .BackgroundColor(new Color(0, 0, 0, 0.7f)));
}

// Usage - typesafe class names
container.AddToClassList(ChatStyles.Container);  // No magic strings

Generates standard USS files that Unity loads normally. Zero runtime overhead.

Installation

Manual Installation (recommended)

Copy the TypeUSS folder into your project's Assets/Scripts/ directory.

We recommend manual installation so you can easily add additional custom property wrappers as needed. TypeUSS doesn't wrap every USS property yet, just the most common ones. If you add new properties please consider contributing them!

Quick Start

1. Define your styles

using UnityEngine;
using TypeUSS;

[GenerateUSS("UI/Generated/Button.uss")]
public static class ButtonStyles
{
    // Define selectors (class names)
    public static readonly Selector Button = Sel.Class("btn");
    
    // Define rules (selector + properties)
    public static readonly TypeStyle ButtonBase = Button.Style(s => s
        .Padding(8, 16)
        .BorderRadius(4)
        .BackgroundColor(new Color(0.3f, 0.3f, 0.3f))
        .Color(Color.white));
    
    // Hover state
    public static readonly TypeStyle ButtonHover = Button.Hover().Style(s => s
        .BackgroundColor(new Color(0.4f, 0.4f, 0.4f)));
}

2. USS is generated automatically

When the Unity project recompiles, TypeUSS generates the '.uss' file in the location specified in their [GenerateUSS] attribute. You can also manually regenerate via TypeUSS → Regenerate All USS in the menu bar.

/* Auto-generated by TypeUSS - Do not edit manually */

.btn {
    padding: 8px 16px;
    border-radius: 4px;
    background-color: rgb(77, 77, 77);
    color: rgb(255, 255, 255);
}

.btn:hover {
    background-color: rgb(102, 102, 102);
}

3. Use in your UI toolkit elements

var button = new Button { text = "Click me" };
button.AddToClassList(ButtonStyles.Button);        // Typesafe class name

Features

Selectors

// Class selector: .my-class
Sel.Class("my-class")

// ID selector: #my-id
Sel.Id("my-id")

// Type selector: Button
Sel.Type<Button>()
Sel.Type("Button")

// Universal selector: *
Sel.All

// Escape hatch for complex selectors
Sel.Raw("#unity-text-input")

Pseudo-classes

Button.Hover()      // .btn:hover
Button.Active()     // .btn:active
Button.Focus()      // .btn:focus
Button.Enabled()    // .btn:enabled
Button.Disabled()   // .btn:disabled
Button.Checked()    // .btn:checked

// Chain them
Button.Hover().Focus()  // .btn:hover:focus

Combinators

// Child combinator: .parent > .child
Parent > Child

// Descendant combinator: .ancestor .descendant
Ancestor.Descendant(Child)

// Adjacent sibling: .a + .b
A + B

// Combine without space: Button.my-class
Sel.Type<Button>().And(MyClass)

Properties

TypeUSS wraps the most common USS properties with typesafe methods:

.Width(100)                    // 100px
.Width(50.Percent())           // 50%
.Width(Length.Auto)            // auto

.Height(200)
.MinWidth(100)
.MaxHeight(500)

.FlexGrow(1)
.FlexDirection(FlexDirection.Row)
.JustifyContent(Justify.Center)
.AlignItems(Align.FlexStart)

.Padding(10)                   // all sides
.Padding(10, 20)               // vertical, horizontal
.Padding(10, 20, 10, 20)       // top, right, bottom, left
.Margin(10)

.Position(Position.Absolute)
.Top(0)
.Left(0)

.BackgroundColor(Color.black)
.Color(Color.white)
.BorderColor(Color.gray)

.BorderWidth(1)
.BorderRadius(4)

.Display(DisplayStyle.Flex)
.Visibility(Visibility.Hidden)
.Overflow(Overflow.Hidden)
.Opacity(0.5f)

Escape Hatch

Don't stop coding to add a new property wrapper. Use .Prop() for anything not yet wrapped:

public static readonly TypeStyle HeaderStyle = Header.Style(s => s
    .Width(100)                              // Wrapped
    .BackgroundColor(Color.black)            // Wrapped
    .Prop("-unity-font-style", "bold")       // Escape hatch!
    .Prop("transition-duration", "0.2s"));   // Escape hatch!

When you notice you're using the same .Prop() call repeatedly, that's a signal to add a proper wrapper method.

Unity Internal Elements

Target Unity's internal element structure using Sel.Raw():

public static readonly Selector Input = Sel.Class("my-input");

// Style the internal text input element
public static readonly TypeStyle InputInner = (Input > Sel.Raw("#unity-text-input")).Style(s => s
    .BackgroundColor(Color.black)
    .Color(Color.white)
    .Padding(0, 5));

Automatic StyleSheet Registration

TypeUSS automatically finds MonoBehaviours in the open scene with a StyleSheet[] field named styleSheets and populates it with all generated stylesheets.

To automatically apply generated stylesheets to your UI, add a field to your UI controller:

public class UIManager : MonoBehaviour
{
    [SerializeField] private UIDocument document;
    [SerializeField] private StyleSheet[] styleSheets; // Auto-populated by TypeUSS
    
    void OnEnable()
    {
        foreach (var sheet in styleSheets)
            document.rootVisualElement.styleSheets.Add(sheet);
    }
}

Unity Version Compatibility

TypeUSS was developed and tested with Unity 6.2 - it may work with earlier versions but I have not taken the time to verify compatibility.

License

MIT License - see LICENSE for details.

Contributing

Contributions welcome! Please feel free to submit issues and pull requests.

Adding a new property wrapper:

  1. Add the method to StyleBuilder.cs
  2. Add any necessary enum mappings to EnumExtensions
  3. Submit a PR

About

Typesafe C# authoring for Unity UI Toolkit Stylesheets

Resources

License

Stars

Watchers

Forks

Languages