Custom themes

Register your own palette, save it, share it, and use the built-in editor.

Registering one#

A theme is a table of role names to Color3. You only have to supply the roles you want to change — the rest fall back to the current theme.

Luau
Ember.RegisterTheme("Sunset", {
    bg      = Color3.fromRGB(24, 16, 22),
    panel   = Color3.fromRGB(32, 22, 30),
    panel2  = Color3.fromRGB(41, 28, 38),
    accent  = Color3.fromRGB(255, 122, 47),
    accentInk = Color3.fromRGB(28, 12, 4),
})
 
Ember.SetTheme("Sunset")

Once registered the name behaves like a built-in: it appears in Ember.ThemeNames(), so a theme dropdown picks it up with no extra work.

The full API#

FunctionWhat it does
Ember.RegisterTheme(name, palette)Adds or replaces a theme
Ember.RemoveTheme(name)Deletes a custom theme. Built-ins are protected.
Ember.SetTheme(name)Applies it, repainting everything on screen
Ember.ThemeNames()Every selectable theme name
Ember.IsBuiltInTheme(name)Whether it shipped with the library
Ember.SaveThemes()Writes your custom themes to disk
Ember.LoadThemes()Reads them back; returns how many were loaded
Ember.ExportTheme(name)Returns the theme as a JSON string
Ember.ImportTheme(json)Parses one back. Returns nil on rubbish.
Ember.OnThemeApplied(fn)Runs fn every time the theme changes
Ember.OnThemesChanged(fn)Runs fn when a theme is added or removed

Persisting your themes#

Registering a theme lasts as long as the session. To keep it:

Luau
Ember.RegisterTheme("Sunset", palette)
Ember.SaveThemes()

And on startup:

Luau
Ember.LoadThemes()   --> number of themes restored

Sharing one#

ExportTheme gives you a JSON string safe to put on a clipboard or in a message:

Luau
local json = Ember.ExportTheme("Sunset")
setclipboard(json)

Importing validates before it applies, so a malformed string cannot corrupt anything:

Luau
local name, err = Ember.ImportTheme(json)
if not name then
    warn("that is not a theme:", err)
end

The built-in editor#

Ember ships a whole theme editor. One call adds it to a section:

Luau
local settings = win:Section("Settings", "settings")
win:ThemeEditor(settings)

It gives the user a colour picker per role, a live preview, a base theme to start from, and import/export buttons — all wired up, nothing for you to build.

The base theme follows the current one

Opening the editor starts you from whatever theme is active, and changing the base applies it immediately. So the thing you are editing is always the thing you are looking at.

Designing a palette that works#

Start from a built-in that is close and change three things:

Luau
local base = Ember.Themes.Dark
 
local mine = {}
for role, colour in pairs(base) do mine[role] = colour end
 
mine.accent = Color3.fromRGB(255, 122, 47)
mine.accentInk = Color3.fromRGB(28, 12, 4)
mine.hover = Color3.fromRGB(38, 28, 24)
 
Ember.RegisterTheme("Mine", mine)

Three rules that stop a custom theme looking broken:

  1. accentInk must contrast with accent. It is text sitting on the accent colour. A pale accent needs a dark ink, and vice versa.
  2. bgpanelpanel2panel3 should get progressively lighter (or progressively darker on a light theme). That gradient is what makes the window read as layered rather than flat.
  3. faint still has to be readable. It paints descriptions and placeholders. If it disappears into panel2, every hint in your UI vanishes.