Custom themes
Registering your own palettes, sharing them as JSON, and the built-in theme editor.
Registering one#
A theme is a table of role names to Color3. Supply only the roles you want to
change; every role you leave out takes its colour from Dark.
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 appears in Ember.ThemeNames() like a built-in, so a
theme dropdown picks it up with no extra work. It will not appear if
Themes.Only is set and does not list
it.
The full API#
| Function | What it does |
|---|---|
Ember.RegisterTheme(name, palette) | Adds or replaces a custom theme, and writes it to disk. Returns true, name, or false, reason. A built-in name is rejected. |
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. false if the write failed. |
Ember.LoadThemes() | Reads them back; returns how many were loaded |
Ember.ExportTheme(name) | Returns the theme as a JSON string, or nil |
Ember.ImportTheme(source, name?) | Takes JSON or an http(s) URL. Returns the registered name, or nil, reason. |
Ember.OnThemeApplied(fn) | Runs fn(name) when a named theme is applied. Returns a subscription with :Disconnect(). |
Ember.OnThemesChanged(fn) | Runs fn(names) when a theme is added or removed. Same subscription. |
Persisting your themes#
RegisterTheme writes to disk on its own. Read them back on startup:
Ember.LoadThemes() --> number of themes restoredEmber.Configure({ Storage = … }) calls LoadThemes for you, so a script that
configures storage already has its custom themes back. Ember.SaveThemes() is
there for when you edit a palette table in place and want that written out.
Sharing one#
ExportTheme returns the palette as JSON:
local json = Ember.ExportTheme("Sunset")
setclipboard(json)Importing validates before it applies, so a malformed string changes nothing:
local name, err = Ember.ImportTheme(json)
if not name then
warn("that is not a theme:", err)
endThe built-in editor#
One call builds a whole section for editing themes:
win:ThemeEditor({ Name = "Theme Editor", Icon = "droplet" })The section holds a base-theme picker, a swatch grid for every role, a colour picker for the selected role, a name box, and buttons for apply, save, delete, export and import. Export copies JSON to the clipboard. Import reads pasted JSON or fetches a URL.
Name and Icon set the section's tab, defaulting to "Themes" and droplet.
Colours apply as you drag. Pass LivePreview = false to hold them back until
the user hits Apply or Save.
The base theme follows the current one
Opening the editor starts from whatever theme is active, and changing the base applies it immediately, so you are always editing the theme you can see.
Designing a palette that works#
Start from a built-in that is close and change three things:
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:
accentInkmust contrast withaccent. It is text sitting on the accent colour. A pale accent needs a dark ink, and vice versa.bg→panel→panel2→panel3should get progressively lighter (or progressively darker on a light theme). Without those steps the window has no visible layers.faintstill has to be readable. It paints descriptions and placeholders. If it disappears intopanel2, every hint in your UI vanishes.