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.
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.
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 stringsGenerates standard USS files that Unity loads normally. Zero runtime overhead.
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!
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)));
}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);
}var button = new Button { text = "Click me" };
button.AddToClassList(ButtonStyles.Button); // Typesafe class name// 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")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// 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)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)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.
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));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);
}
}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.
MIT License - see LICENSE for details.
Contributions welcome! Please feel free to submit issues and pull requests.
Adding a new property wrapper:
- Add the method to
StyleBuilder.cs - Add any necessary enum mappings to
EnumExtensions - Submit a PR