LuauCSS: StyleSheets that you author like CSS 
Intro
Roblox StyleSheets are great!! Wiring them up by hand is… a lot of Instance.news. I’m used to CSS so this makes my head spin when looking at it. Lets take this amalgamation of some samples from the docs site!
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local coreSheet = ReplicatedStorage:FindFirstChild("CoreSheet")
-- Create a tokens style sheet
local tokensSheet = Instance.new("StyleSheet")
tokensSheet.Name = "Tokens"
tokensSheet.Parent = ReplicatedStorage
-- Set tokens (attributes) on tokens sheet
tokensSheet:SetAttribute("LightGray", Color3.new(0.9, 0.9, 0.9))
tokensSheet:SetAttribute("DarkGray", Color3.new(0.2, 0.2, 0.2))
-- Create theme style sheets
local lightThemeSheet = Instance.new("StyleSheet")
lightThemeSheet.Name = "LightTheme"
lightThemeSheet:SetAttribute("Background", "$LightGray")
lightThemeSheet.Parent = ReplicatedStorage
local darkThemeSheet = Instance.new("StyleSheet")
darkThemeSheet.Name = "DarkTheme"
darkThemeSheet:SetAttribute("Background", "$DarkGray")
darkThemeSheet.Parent = ReplicatedStorage
-- Set theme sheets to derive from tokens sheet
lightThemeSheet:SetDerives({ tokensSheet })
darkThemeSheet:SetDerives({ tokensSheet })
local themeDerive = Instance.new("StyleDerive")
themeDerive.Parent = coreSheet
themeDerive.StyleSheet = lightThemeSheet
-- Function to dynamically change the derived theme for the core sheet
local function changeTheme()
if themeDerive.StyleSheet == lightThemeSheet then
themeDerive.StyleSheet = darkThemeSheet
elseif themeDerive.StyleSheet == darkThemeSheet then
themeDerive.StyleSheet = lightThemeSheet
end
end
-- Add rules
local rule = Instance.new("StyleRule")
rule.Parent = coreSheet
-- Class selector
rule.Selector = "TextButton"
-- Set rule properties
rule:SetProperties({
["BackgroundColor3"] = Color3.fromHex("335FFF"),
["TextColor3"] = Color3.fromHex("E1E1E1"),
["Size"] = UDim2.new(0.15, 0, 0, 40),
["BorderSizePixel"] = 0,
})
-- And a pseudoelement
local cornerRule = Instance.new("StyleRule")
cornerRule.Parent = coreSheet
-- UI component selector
cornerRule.Selector = "Frame::UICorner"
-- Set rule property
cornerRule:SetProperty("CornerRadius", UDim.new(0, 20))
-- And transitions!
tokenSheet:SetAttribute("LinearTransition", TweenInfo.new(1, Enum.EasingStyle.Linear))
tokenSheet:SetAttribute("QuadTransition", TweenInfo.new(0.25, Enum.EasingStyle.Quad))
-- Make the rule
local frameRule = Instance.new("StyleRule")
frameRule.Parent = coreSheet
rule.Selector = "Frame"
-- Set transitions for multiple properties at once
frameRule:SetPropertyTransitions({
["BackgroundColor3"] = "$LinearTransition",
["BackgroundTransparency"] = "$QuadTransition",
})
LuauCSS is a simple ModuleScript that turns tokens, rules, gradients, and hover states into something a bit more readable thanks to some delicious syntax sugar in Luau ![]()
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LuauCSS = require(ReplicatedStorage:WaitForChild("LuauCSS"))
local Styling = LuauCSS.new {
Tokens = {
LightGray = Color3.new(0.9, 0.9, 0.9),
DarkGray = Color3.new(0.2, 0.2, 0.2),
LinearTransition = TweenInfo.new(1, Enum.EasingStyle.Linear),
QuadTransition = TweenInfo.new(0.25, Enum.EasingStyle.Quad),
},
Themes = {
LightTheme = { Background = "$LightGray" },
DarkTheme = { Background = "$DarkGray" },
},
Default = "LightTheme",
}
Styling
"TextButton" {
BackgroundColor3 = Color3.fromHex("335FFF"),
TextColor3 = Color3.fromHex("E1E1E1"),
Size = UDim2.new(0.15, 0, 0, 40),
BorderSizePixel = 0,
}
"Frame::UICorner" {
CornerRadius = UDim.new(0, 20),
}
"Frame" {
Transition = {
BackgroundColor3 = "$LinearTransition",
BackgroundTransparency = "$QuadTransition",
},
}
local coreSheet = Styling:GetStyleSheet()
coreSheet.Name = "CoreSheet"
coreSheet.Parent = ReplicatedStorage
local currentTheme = "LightTheme"
local function changeTheme()
currentTheme = if currentTheme == "LightTheme" then "DarkTheme" else "LightTheme"
Styling:useTheme(currentTheme)
end
Get it:
Creator Store – automatically updated from github! thanks rojo 
GitHub – copy the single script into your project.
Setup
- Grab LuauCSS from the Creator Store or copy
LuauCSS.luafrom GitHub intoReplicatedStorageas aModuleScript - Create your styling instance with tokens and/or themes
- Set up your rules —
.Button,.Button:Hover,.Card::UIGradient, etc. - Parent the sheet to
ReplicatedStorage, then link with aStyleLink
local LuauCSS = require(ReplicatedStorage.LuauCSS)
local Styling = LuauCSS.new {
Tokens = {
Primary = Color3.fromHex("FF0099"),
Accent = Color3.fromHex("0093F0"),
},
}
Styling
".Button" {
BackgroundColor3 = "$Primary",
Transition = { Default = TweenInfo.new(0.15) },
}
".Button:Hover" { BackgroundColor3 = "$Accent" }
".Button::UICorner" { CornerRadius = UDim.new(0, 8) }
local sheet = Styling:GetStyleSheet()
sheet.Parent = ReplicatedStorage
local link = Instance.new("StyleLink")
link.StyleSheet = sheet
link.Parent = screenGui
That’s it! Tag your instances and press Play, or use a great plugin like Flipbook or UILabs to preview scripted UI without having to do that ![]()
For more detailed API documentation please visit the github repo and read the readme ![]()
Before / Afters 
Please appreciate these animations I spent too much time putting together that are probably objectively less useful then just showing the code snippets but like i said i spent too much time on them.
Also there’s something useful, i took tailwind’s default theme (theme.css) and turned it in to a fairly readable stylesheet (uploaded as an example).
I had my good friend cursor whip up something to create the header for this devforum post as well, thats an example too! I’ll work on other examples at some point.
What it doesn’t do (yet…) 
StyleDerive.Priority/ multiple derives — one derive per instance
Auto StyleLink— you still link the sheet yourself
StyleRule.Priorityworks fine viaPriority = Nin a rule
Feedback 
Feature requests welcome in the comments! Bug reports too though the script is pretty simple. And you can open up a PR on the github repo if i set it up right. And if i didn’t set it up right file an issue if i set THAT up right.
Happy styling ~ Bitwise (@BitwiseAndrea)

