TailBox CSS Compiler
A Tailwind CSS JIT compiler for s&box.
About
TailBox brings the power of
Tailwind CSS utility classes directly into the s&box UI engine. No Node.js, no CLI, no external build steps. You type the classes in your
.razor files, and TailBox compiles them instantly in C#.
Unlike other static generation tools (such as Sailwind), TailBox is a true Just-In-Time (JIT) compiler. It reads your UI at runtime, parses complex arbitrary values (e.g. w-[25px]), understands chained variants (e.g. dark:hover:bg-red-500/50), and translates them into optimal s&box styles natively.
Features
- Optimized JIT Engine: Designed to minimize string allocations in hot paths, reducing GC pressure and maintaining UI performance.
- Tailwind Syntax Support: Supports variants (
hover:, active:), arbitrary values (w-[25px], bg-[#ff0000]), and opacity modifiers (bg-red-500/50). - Hot-Reload Compatible: Works perfectly with s&box UI hot-reloading. CSS is generated incrementally in milliseconds.
- Smart Transitions & Animations: Automatically converts Tailwind durations and easings into s&box-compatible UI animations.
- Native Theming: Supports Light/Dark modes natively mapped to s&box UI, saving preferences automatically to Cookies.
Installation
Add TailBox CSS Compiler to your project dependencies via the s&box Library Manager (search for tailbox).
Usage
1. Global Variables (Theming)
TailBox relies on standard CSS variables for its colors (ex: bg-primary, text-muted-foreground).
Create a
globals.scss file in your
code/ folder to define your theme colors. You can use any
Shadcn UI Theme or generate your own stunning palettes using tools like
Tweakcn, just drop the CSS variables inside!
⚠️ CRITICAL: RGB Format Required
To support Tailwind's opacity modifiers (e.g. bg-primary/50), all your CSS color variables must be defined in rgb(r, g, b) format. Hex codes or HSL will not work properly with opacity.
/* globals.scss */
/* @tailbox-default-mode: dark */
:root {
--background: rgb(255, 255, 255);
--foreground: rgb(10, 10, 10);
--card: rgb(255, 255, 255);
--card-foreground: rgb(10, 10, 10);
--popover: rgb(255, 255, 255);
--popover-foreground: rgb(10, 10, 10);
--primary: rgb(23, 23, 23);
--primary-foreground: rgb(250, 250, 250);
--secondary: rgb(245, 245, 245);
--secondary-foreground: rgb(23, 23, 23);
--muted: rgb(245, 245, 245);
--muted-foreground: rgb(115, 115, 115);
--accent: rgb(245, 245, 245);
--accent-foreground: rgb(23, 23, 23);
--destructive: rgb(231, 0, 11);
--destructive-foreground: rgb(255, 255, 255);
--border: rgb(229, 229, 229);
--input: rgb(229, 229, 229);
--ring: rgb(161, 161, 161);
--radius: 0.625rem;
/* ... add your other specific variables ... */
}
.dark {
--background: rgb(10, 10, 10);
--foreground: rgb(250, 250, 250);
--card: rgb(23, 23, 23);
--card-foreground: rgb(250, 250, 250);
--popover: rgb(38, 38, 38);
--popover-foreground: rgb(250, 250, 250);
--primary: rgb(229, 229, 229);
--primary-foreground: rgb(23, 23, 23);
--secondary: rgb(38, 38, 38);
--secondary-foreground: rgb(250, 250, 250);
--muted: rgb(38, 38, 38);
--muted-foreground: rgb(161, 161, 161);
--accent: rgb(64, 64, 64);
--accent-foreground: rgb(250, 250, 250);
--destructive: rgb(255, 100, 103);
--destructive-foreground: rgb(250, 250, 250);
--border: rgb(40, 40, 40);
--input: rgb(52, 52, 52);
--ring: rgb(115, 115, 115);
--radius: 0.625rem;
/* ... */
}
2. Setup your Root Component (The Engine)
Instead of inheriting from Panel, simply inherit your main HUD or Menu from TailboxPanel. This only needs to be done once at the root level of your UI!
Important Setup Rule: Your root structure should use <root> for pointer events, and an inner <div> with bg-background to properly process the color theme changes.
@using Sandbox;
@using Sandbox.UI;
@using Tailbox.Compiler;
@inherits TailboxPanel
<root class="absolute inset-0 pointer-events-auto">
<!-- App Background -->
<div class="absolute inset-0 bg-background flex flex-col items-center justify-center p-8">
<!-- Main Elegant Card -->
<div class="flex flex-col bg-card border border-border rounded-2xl shadow-xl w-[600px] shrink-0 overflow-hidden transform transition-all duration-300">
<!-- Hero Header -->
<div class="flex flex-col p-10 bg-muted/40 border-b border-border/50 items-center justify-center">
<div class="w-[80px] h-[80px] bg-primary/20 rounded-full flex items-center justify-center border border-primary/30 shadow-sm mb-21">
<label class="text-3xl font-black text-primary font-mono select-none">TB</label>
</div>
<label class="text-[48px] font-black text-foreground font-sans tracking-tight mb-2">Hello TailBox!</label>
<label class="text-muted-foreground font-medium text-[18px] font-sans text-center">
A Tailbox CSS JIT Compiler for s&box.
</label>
</div>
<!-- Content Component -->
<div class="flex flex-col p-8 items-center gap-8 bg-card">
<NestedComponent />
<!-- Action Buttons -->
<div class="flex flex-row gap-4 mt-2 w-full justify-center">
<button class="flex items-center justify-center bg-primary hover:bg-primary/90 text-primary-foreground px-8 py-3.5 rounded-full font-bold shadow-md transition-all hover:scale-105 active:scale-95 shrink-0 text-base cursor-pointer" onclick=@ToggleTheme>
Toggle Theme Mode
</button>
</div>
</div>
</div>
</div>
</root>
@code {
protected override int BuildHash() => HashCode.Combine(TailboxRuntime.CurrentTheme);
void ToggleTheme() => TailboxRuntime.ToggleTheme();
}
3. Sub-components (Nested Components)
For any child component inside your project (or in sub-folders like UI/Components/), you should inherit from TailboxWidget instead of the standard Panel.
💡 Why TailboxWidget?
If you inherit from a standard Panel, creating new classes during Hot-Reload might not translate them until the parent updates. TailboxWidget isolates the child component, allowing it to translate its own new classes immediately upon Hot-Reload or state changes, ensuring a consistent development experience.
✨ Pro Tip: Global Usings
To avoid typing @using Tailbox.Compiler; at the top of every Razor file, create a GlobalUsings.cs file anywhere in your Code/ folder with this single line:global using System;
global using System.Collections.Generic;
global using System.Linq;
global using Tailbox.Compiler;
@namespace Sandbox.UI
@inherits TailboxWidget
<root class="flex p-4 bg-muted rounded-md text-foreground hover:bg-muted/80 transition-colors">
<label>I'm fully Hot-Reload compatible!</label>
</root>Theme System & Cookies
TailBox has a powerful built-in TailboxRuntime.SetTheme("dark") system.
- When the theme is changed, it is automatically saved as a persistent Cookie (
Tailbox_theme). - Next time the player launches the game, TailBox remembers their choice and applies the dark/light mode before the first frame.
- Changing a theme automatically forces s&box to restyle all components instantly.
Advanced: Variant Support
TailBox goes beyond simple classes by supporting variant chaining. All of these work out-of-the-box natively in s&box:
hover:bg-blue-500active:scale-95dark:bg-blackgroup-hover:text-primary
Acknowledgments
- Utility classes and naming conventions are inspired by Tailwind CSS.
- Open Source and custom-built from scratch for the s&box engine.
*Last reviewed: March 2026*