ThinkForge themes can be created through the Theme Editor in the desktop app, or by writing JSON files directly. This document covers both approaches and explains the structure of a theme file.
Using the Theme Editor
The Theme Editor is a visual tool for building and modifying presets. Open it from Settings, then Appearance.
The editor lets you set the four global colors, typography, spacing, and individual element overrides. A preview panel shows changes in real time. Click Apply to App to push the theme live, or Save to write it to disk as a custom theme.
To modify a built-in preset, clone it first. Built-in presets are read-only. The clone gets a new ID and is saved to the custom themes folder.
Theme File Structure
A theme is a single JSON file. Custom themes are stored in Themes\custom\ under your data root, and the file name comes from the theme's Name field.
Required fields
Every theme needs four hex color strings. These are the global drivers the rest of the theme derives from when element overrides are not specified.
PrimaryColor — the accent or brand color, used for buttons, selected states, focus borders, progress fills, and interactive highlights. Example: "#0078D4".
BackgroundColor — the main application background. This is the most impactful field, because it determines whether the app runs in Light or Dark mode. Example: "#1E1E1E" for dark, "#FAFAFA" for light.
TextColor — primary text foreground. Example: "#FFFFFF" for dark themes, "#1A1A1A" for light themes.
BorderColor — default border color for panels, cards, and inputs. Example: "#3F3F3F".
Colors must be hex format with a # prefix, either 6 digits (#RRGGBB) or 8 digits (#AARRGGBB).
Metadata
Name — display name shown in the preset picker.
Description — optional.
Author — defaults to "User".
Version — defaults to "1.0".
Id — a unique ID string. Generated automatically when creating through the editor or importing. If you are writing JSON by hand, generate a unique value.
IsBuiltIn — must be false for custom themes. This is enforced on save.
Typography
FontFamily — font name string. Default: "Segoe UI".
BaseFontSize — body text size in pixels. Default: 13.0.
HeadingFontSize — heading text size in pixels. Default: 18.0.
Spacing and borders
CornerRadius — global corner radius in pixels. Default: 4.0.
BorderThickness — global border thickness in pixels. Default: 1.0.
ControlPadding — global control padding in pixels. Default: 12.0.
Effects
EnableShadows — whether shadow effects are active. Default: true.
ShadowBlur, ShadowOffsetX, ShadowOffsetY, ShadowOpacity, ShadowColor — global shadow parameters.
EnableGlow — whether glow effects are active. Default: false.
GlowBlur, GlowColor — global glow parameters.
Element Overrides
The Elements dictionary is where fine-grained control happens. Each entry targets a specific surface and can either derive its color from a global source or specify a custom hex value.
How derivation works
Each element has a GlobalSource field and a UseGlobal flag. When UseGlobal is true, the element derives its color from the named global source. When UseGlobal is false, it uses CustomColor instead.
Available global sources:
PrimaryColor — the global primary
BackgroundColor — the global background
TextColor — the global text
BorderColor — the global border
PrimaryLighter — primary lightened by 20%
PrimaryDarker — primary darkened by 20%
BackgroundLighter — background lightened by 10%
BorderLighter — border lightened by 15%
TextSecondary — text at approximately 70% opacity
Available elements
The theme system defines 66 elements across these groups:
Buttons — ButtonBackground, ButtonText, ButtonBorder, ButtonHover
Cards — CardBackground, CardBorder, CardText, CardShadow
Panels — PanelBackground, PanelBorder, PanelText, PanelShadow
Text — PrimaryText, SecondaryText
Inputs — InputBackground, InputBorder, InputText, InputFocusBorder
Progress — ProgressBarBackground, ProgressBarFill, ProgressBarBorder, ProgressBarText
CheckBox — CheckBoxBackground, CheckBoxBorder, CheckBoxCheck, CheckBoxText
RadioButton — RadioButtonBackground, RadioButtonBorder, RadioButtonDot, RadioButtonText
Slider — SliderTrack, SliderFill, SliderThumb, SliderTick, SliderBorder
ListBox — ListBoxBackground, ListBoxBorder, ListBoxSelected, ListBoxText, ListBoxSelectedText
ComboBox — ComboBoxBackground, ComboBoxBorder, ComboBoxPopup, ComboBoxText, ComboBoxArrow
Tabs — TabStripBackground, TabSelectedBackground, TabUnselectedBackground, TabSelectedBorder, TabSelectedText, TabUnselectedText
Sidebar — SidebarBackground, SidebarBorder, SidebarText, SidebarHover
Toolbar — ToolbarBackground, ToolbarBorder, ToolbarText
Status Bar — StatusBarBackground, StatusBarBorder, StatusBarText
Dialogs — DialogBackground, DialogBorder, DialogOverlay, DialogText, DialogShadow
Each element can also override CustomCornerRadius, CustomBorderThickness, and CustomPadding independently of the global values. Set these to null to inherit the global setting.
Per-element effect overrides
Elements can override shadow and glow settings independently. Set CustomEnableShadow, CustomShadowBlur, CustomShadowColor, and similar fields to override the global effect for that surface. Leave them null to use the global settings.
Example: Minimal Custom Theme
{
"Id": "my-custom-theme-001",
"Name": "Midnight Blue",
"Description": "A dark theme with blue accents",
"IsBuiltIn": false,
"Author": "User",
"Version": "1.0",
"PrimaryColor": "#4A90D9",
"BackgroundColor": "#0D1117",
"TextColor": "#E6EDF3",
"BorderColor": "#30363D",
"CornerRadius": 6.0,
"BorderThickness": 1.0,
"ControlPadding": 12.0,
"FontFamily": "Segoe UI",
"BaseFontSize": 13.0,
"HeadingFontSize": 18.0,
"EnableShadows": true,
"Elements": {}
}
With an empty Elements dictionary, all surface colors are derived automatically from the four globals. This is the simplest way to create a cohesive theme — set four colors and let the system compute the rest.
To override a specific surface, add an entry:
{
"Elements": {
"CardBackground": {
"Name": "Card Background",
"GlobalSource": "BackgroundLighter",
"UseGlobal": false,
"CustomColor": "#161B22"
}
}
}
Importing and Exporting
Themes can be shared as JSON files. Use the Theme Editor's import function to load a .json file from anywhere on disk. Importing assigns a new unique ID and saves the theme to the custom folder.
Export writes the full theme JSON to any location you choose. The exported file includes all element overrides and can be imported on another machine.
Storage Locations
Built-in presets — Themes\built-in\ under your data root. Read-only, written on first launch.
Custom presets — Themes\custom\ under your data root.
In a release build the data root is Documents\Think Forge.
Tips
Start with the built-in preset closest to what you want, clone it, and adjust from there. The built-in presets were designed as complete palettes with carefully tuned element overrides, so they make good starting points.
If you only need to change the accent color, set PrimaryColor and leave Elements empty or with UseGlobal: true on all entries. The derived colors update automatically.
For light themes, make sure BackgroundColor is genuinely bright — roughly #D0D0D0 or lighter — so the app correctly switches to Light mode. Light mode affects all standard controls and system popups.