Creating Themes

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:

ButtonsButtonBackground, ButtonText, ButtonBorder, ButtonHover

CardsCardBackground, CardBorder, CardText, CardShadow

PanelsPanelBackground, PanelBorder, PanelText, PanelShadow

TextPrimaryText, SecondaryText

InputsInputBackground, InputBorder, InputText, InputFocusBorder

ProgressProgressBarBackground, ProgressBarFill, ProgressBarBorder, ProgressBarText

CheckBoxCheckBoxBackground, CheckBoxBorder, CheckBoxCheck, CheckBoxText

RadioButtonRadioButtonBackground, RadioButtonBorder, RadioButtonDot, RadioButtonText

SliderSliderTrack, SliderFill, SliderThumb, SliderTick, SliderBorder

ListBoxListBoxBackground, ListBoxBorder, ListBoxSelected, ListBoxText, ListBoxSelectedText

ComboBoxComboBoxBackground, ComboBoxBorder, ComboBoxPopup, ComboBoxText, ComboBoxArrow

TabsTabStripBackground, TabSelectedBackground, TabUnselectedBackground, TabSelectedBorder, TabSelectedText, TabUnselectedText

SidebarSidebarBackground, SidebarBorder, SidebarText, SidebarHover

ToolbarToolbarBackground, ToolbarBorder, ToolbarText

Status BarStatusBarBackground, StatusBarBorder, StatusBarText

DialogsDialogBackground, 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 presetsThemes\built-in\ under your data root. Read-only, written on first launch.

Custom presetsThemes\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.