Skip to main content
This example demonstrates building a production-ready component library with multiple button variants, hover states, sizing options, and design tokens.

Example overview

We’ll create a button system with:
  • Design tokens for colors, spacing, and typography
  • Multiple button variants (primary, secondary, danger)
  • Size variants (small, medium, large)
  • Hover and active states
  • Automatic sizing with padding
1

Write the CSS

Create a comprehensive button library:
buttons.css
/* Design tokens */
:root {
  /* Colors */
  --primary: #335fff;
  --primary-hover: #2d50dd;
  --secondary: #6c757d;
  --secondary-hover: #5a6268;
  --danger: #dc3545;
  --danger-hover: #c82333;
  --white: #ffffff;
  
  /* Spacing */
  --spacing-sm: 8px;
  --spacing-md: 12px;
  --spacing-lg: 16px;
  
  /* Typography */
  --font-sm: 12px;
  --font-md: 14px;
  --font-lg: 16px;
  
  /* Radius */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
}

/* Base button styles */
button {
  font-family: "GothamSSm";
  font-weight: 600;
  text-align: center;
  width: auto;
  height: auto;
  border: none;
}

button:hover {
  opacity: 0.9;
}

/* Primary variant */
button.primary {
  background-color: var(--primary);
  color: var(--white);
  font-size: var(--font-md);
  padding: var(--spacing-md) var(--spacing-lg);
  border-radius: var(--radius-md);
}

button.primary:hover {
  background-color: var(--primary-hover);
}

/* Secondary variant */
button.secondary {
  background-color: var(--secondary);
  color: var(--white);
  font-size: var(--font-md);
  padding: var(--spacing-md) var(--spacing-lg);
  border-radius: var(--radius-md);
}

button.secondary:hover {
  background-color: var(--secondary-hover);
}

/* Danger variant */
button.danger {
  background-color: var(--danger);
  color: var(--white);
  font-size: var(--font-md);
  padding: var(--spacing-md) var(--spacing-lg);
  border-radius: var(--radius-md);
}

button.danger:hover {
  background-color: var(--danger-hover);
}

/* Size variants */
button.small {
  font-size: var(--font-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-sm);
}

button.large {
  font-size: var(--font-lg);
  padding: var(--spacing-lg) calc(var(--spacing-lg) + var(--spacing-sm));
  border-radius: var(--radius-lg);
}

/* Outline variant */
button.outline {
  background-color: transparent;
  color: var(--primary);
  border: 2px solid var(--primary);
  font-size: var(--font-md);
  padding: calc(var(--spacing-md) - 2px) calc(var(--spacing-lg) - 2px);
  border-radius: var(--radius-md);
}

button.outline:hover {
  background-color: var(--primary);
  color: var(--white);
}

/* Full width variant */
button.full-width {
  width: 100%;
}

/* Icon button */
button.icon {
  aspect-ratio: 1;
  padding: var(--spacing-md);
  border-radius: var(--radius-md);
}
2

Compile to Luau

rbx-css compile buttons.css -o ButtonStyles.luau
3

Review key output sections

The compiler generates comprehensive StyleRules with token references:
ButtonStyles.luau
-- Auto-generated by rbx-css
-- Source: buttons.css

local function createStyleSheet()
  local sheet = Instance.new("StyleSheet")
  sheet.Name = "StyleSheet"

  -- Design tokens
  sheet:SetAttribute("primary", Color3.fromRGB(51, 95, 255))
  sheet:SetAttribute("primary-hover", Color3.fromRGB(45, 80, 221))
  sheet:SetAttribute("secondary", Color3.fromRGB(108, 117, 125))
  sheet:SetAttribute("secondary-hover", Color3.fromRGB(90, 98, 104))
  sheet:SetAttribute("danger", Color3.fromRGB(220, 53, 69))
  sheet:SetAttribute("danger-hover", Color3.fromRGB(200, 35, 51))
  sheet:SetAttribute("white", Color3.fromRGB(255, 255, 255))
  sheet:SetAttribute("spacing-sm", UDim.new(0, 8))
  sheet:SetAttribute("spacing-md", UDim.new(0, 12))
  sheet:SetAttribute("spacing-lg", UDim.new(0, 16))
  sheet:SetAttribute("font-sm", 12)
  sheet:SetAttribute("font-md", 14)
  sheet:SetAttribute("font-lg", 16)
  sheet:SetAttribute("radius-sm", UDim.new(0, 4))
  sheet:SetAttribute("radius-md", UDim.new(0, 6))
  sheet:SetAttribute("radius-lg", UDim.new(0, 8))

  -- Base button (TextButton)
  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton"
    rule.Parent = sheet
    rule:SetProperties({
      FontFace = Font.new(
        "rbxasset://fonts/families/GothamSSm.json",
        Enum.FontWeight.SemiBold
      ),
      TextXAlignment = Enum.TextXAlignment.Center,
      AutomaticSize = Enum.AutomaticSize.XY,
    })
  end

  -- Primary button
  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.primary"
    rule.Parent = sheet
    rule:SetProperties({
      BackgroundColor3 = "$primary",
      BackgroundTransparency = 0,
      TextColor3 = "$white",
      TextSize = "$font-md",
    })
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.primary::UICorner"
    rule.Parent = sheet
    rule:SetProperty("CornerRadius", "$radius-md")
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.primary::UIPadding"
    rule.Parent = sheet
    rule:SetProperties({
      PaddingTop = "$spacing-md",
      PaddingBottom = "$spacing-md",
      PaddingLeft = "$spacing-lg",
      PaddingRight = "$spacing-lg",
    })
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.primary:Hover"
    rule.Parent = sheet
    rule:SetProperties({
      BackgroundColor3 = "$primary-hover",
    })
  end

  -- Small size variant
  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.small"
    rule.Parent = sheet
    rule:SetProperties({
      TextSize = "$font-sm",
    })
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.small::UICorner"
    rule.Parent = sheet
    rule:SetProperty("CornerRadius", "$radius-sm")
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.small::UIPadding"
    rule.Parent = sheet
    rule:SetProperties({
      PaddingTop = "$spacing-sm",
      PaddingBottom = "$spacing-sm",
      PaddingLeft = "$spacing-md",
      PaddingRight = "$spacing-md",
    })
  end

  -- Outline variant
  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.outline"
    rule.Parent = sheet
    rule:SetProperties({
      BackgroundTransparency = 1,
      TextColor3 = "$primary",
      TextSize = "$font-md",
    })
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.outline::UIStroke"
    rule.Parent = sheet
    rule:SetProperties({
      Thickness = 2,
      Color = "$primary",
      ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
    })
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.outline:Hover"
    rule.Parent = sheet
    rule:SetProperties({
      BackgroundColor3 = "$primary",
      BackgroundTransparency = 0,
      TextColor3 = "$white",
    })
  end

  -- Icon button with aspect ratio
  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.icon::UIAspectRatioConstraint"
    rule.Parent = sheet
    rule:SetProperty("AspectRatio", 1)
  end

  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.icon::UIPadding"
    rule.Parent = sheet
    rule:SetProperties({
      PaddingTop = "$spacing-md",
      PaddingBottom = "$spacing-md",
      PaddingLeft = "$spacing-md",
      PaddingRight = "$spacing-md",
    })
  end

  -- Full width
  do
    local rule = Instance.new("StyleRule")
    rule.Selector = "TextButton.full-width"
    rule.Parent = sheet
    rule:SetProperties({
      Size = UDim2.new(1, 0, 0, 0),
      AutomaticSize = Enum.AutomaticSize.Y,
    })
  end

  return sheet
end

return createStyleSheet
4

Apply to UI components

Use CollectionService tags to apply styles:
local CollectionService = game:GetService("CollectionService")
local createStyleSheet = require(ReplicatedStorage.ButtonStyles)

local sheet = createStyleSheet()
sheet.Parent = game.Players.LocalPlayer.PlayerGui.MyGui

-- Create buttons with class tags
local primaryBtn = Instance.new("TextButton")
primaryBtn.Text = "Primary Button"
CollectionService:AddTag(primaryBtn, "primary")

local smallSecondaryBtn = Instance.new("TextButton")
smallSecondaryBtn.Text = "Small Secondary"
CollectionService:AddTag(smallSecondaryBtn, "secondary")
CollectionService:AddTag(smallSecondaryBtn, "small")

local dangerBtn = Instance.new("TextButton")
dangerBtn.Text = "Delete"
CollectionService:AddTag(dangerBtn, "danger")
CollectionService:AddTag(dangerBtn, "large")

Component design patterns

Token organization

Group tokens by category for maintainability:
:root {
  /* Colors */
  --primary: #335fff;
  --secondary: #6c757d;
  
  /* Spacing scale */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 12px;
  --spacing-lg: 16px;
  --spacing-xl: 24px;
  
  /* Typography scale */
  --font-xs: 10px;
  --font-sm: 12px;
  --font-md: 14px;
  --font-lg: 16px;
  --font-xl: 20px;
}

Base + variant pattern

Define base styles, then override specific properties in variants:
/* Base */
button {
  font-family: "GothamSSm";
  font-weight: 600;
  width: auto;
  height: auto;
}

/* Variants override specific properties */
button.primary {
  background-color: var(--primary);
  color: white;
}

button.secondary {
  background-color: var(--secondary);
  color: white;
}

Modifier classes

Use additional classes for size and state modifiers:
/* Size modifiers */
button.small { font-size: 12px; }
button.large { font-size: 18px; }

/* State modifiers */
button.disabled { opacity: 0.5; }
button.loading { opacity: 0.7; }
These can be combined: <button class="primary large">Big Button</button>

Automatic sizing

Use width: auto and height: auto with padding for content-based sizing:
button {
  width: auto;  /* → AutomaticSize.X */
  height: auto; /* → AutomaticSize.Y */
  padding: 12px 24px;
}
The button automatically sizes to fit its text content plus padding.

Hover states

Use :hover for interactive feedback:
button:hover {
  opacity: 0.9;
}

button.primary:hover {
  background-color: var(--primary-hover);
}
Maps to :Hover GuiState in Roblox.

Advanced techniques

Gradient buttons

button.gradient {
  background: linear-gradient(90deg, #ff0099, #ffcc00);
  color: white;
}
Compiles to ::UIGradient pseudo-instance.

Icon buttons with aspect ratio

button.icon {
  aspect-ratio: 1; /* Square */
  padding: 12px;
}
Generates ::UIAspectRatioConstraint to maintain 1:1 ratio.

Border animations

Use separate hover states for border color:
button.outline {
  border: 2px solid var(--primary);
}

button.outline:hover {
  border-color: var(--primary-hover);
}

Multiple file workflow

For larger libraries, split into multiple files:
rbx-css compile tokens.css base.css buttons.css cards.css -o ComponentLib.luau
All files merge into a single StyleSheet with combined tokens and rules.

Build docs developers (and LLMs) love