Skip to main content

Theming Guide

Theming controls ImGui's global style — every window background, widget color, spacing value, and rounding radius across your entire application. This is different from per-widget styling where you set colors and vars on individual components.

XFrames ships with three built-in themes and supports runtime switching via patchStyle.

Built-in themes​

ThemeStyleAccent colorBase
Dark (theme2)Dark backgrounds, light textGreen #75f986Black #0A0B0D
Light (theme1)Light backgrounds, dark textCoral #ff6e59White #fff
Ocean (theme3)Deep navy backgroundsTeal #56d6d6Navy #1a1b2e

Each theme defines a color palette and maps it onto ImGui's 53 color slots:

import { XFramesStyleForPatching, ImGuiCol } from "@xframes/common";

const oceanColors = {
deepNavy: "#1a1b2e",
navy: "#252641",
midNavy: "#2f3055",
slate: "#4a4b6a",
lightSlate: "#7a7b9a",
silver: "#b8bbd0",
offWhite: "#e8eaf6",
teal: "#56d6d6",
hoverTeal: "#7aeaea",
};

const oceanTheme: XFramesStyleForPatching = {
colors: {
[ImGuiCol.Text]: [oceanColors.offWhite, 1],
[ImGuiCol.WindowBg]: [oceanColors.deepNavy, 1],
[ImGuiCol.Button]: [oceanColors.midNavy, 1],
[ImGuiCol.ButtonHovered]: [oceanColors.slate, 1],
[ImGuiCol.ButtonActive]: [oceanColors.teal, 1],
// ... all 53 ImGuiCol entries
},
};

Theme object structure​

Themes use the XFramesStyleForPatching type — a partial object where you only specify what you want to override:

type XFramesStyleForPatching = Partial<Omit<XFramesStyle, "colors">> & {
colors?: {
[k in ImGuiCol]?: HEXA;
};
};

type HEXA = [string, number]; // [hexColor, alpha 0.0–1.0]

The colors property maps ImGuiCol enum values to HEXA tuples. You can also override numeric style properties:

const theme: XFramesStyleForPatching = {
// Numeric style vars
windowRounding: 8,
frameRounding: 4,
framePadding: [6, 4], // [x, y]
itemSpacing: [8, 6],

// Colors
colors: {
[ImGuiCol.Text]: ["#ffffff", 1],
[ImGuiCol.WindowBg]: ["#1a1a2e", 1],
[ImGuiCol.FrameBg]: ["#16213e", 0.9], // 90% opaque
},
};

Setting the initial theme​

Pass your theme as the 4th argument to render():

import { render } from "./lib/render";
import { theme2 } from "./themes";

render(App, assetsBasePath, fontDefs, theme2);

The theme is serialized to JSON and sent to the C++ core during initialization.

Runtime theme switching​

Call patchStyle on the native module to change the theme at runtime. It lives directly on the native module — not on WidgetRegistrationService:

import { ComboChangeEvent, useWidgetRegistrationService } from "@xframes/common";
import { theme1, theme2, theme3 } from "../themes";

const themeList = [
{ name: "Dark", theme: theme2 },
{ name: "Light", theme: theme1 },
{ name: "Ocean", theme: theme3 },
];

function ThemeSwitcher() {
const widgetRegistrationService = useWidgetRegistrationService();

const handleThemeChange = useCallback((event: ComboChangeEvent) => {
const index = event.nativeEvent.value;
(widgetRegistrationService as any).wasmModule.patchStyle(
JSON.stringify(themeList[index].theme),
);
}, [widgetRegistrationService]);

return (
<XFrames.Combo
options={themeList.map((t) => t.name)}
initialSelectedIndex={0}
onChange={handleThemeChange}
/>
);
}

The style update takes effect on the next render frame — there is no flicker or delay.

Creating a custom theme​

  1. Define your color palette as a plain object with descriptive names:
const myColors = {
bg: "#2b2d42",
surface: "#3a3d5c",
text: "#edf2f4",
accent: "#ef233c",
accentHover: "#ff4757",
muted: "#8d99ae",
};
  1. Create the theme object, mapping palette colors to ImGui color slots:
import { XFramesStyleForPatching, ImGuiCol } from "@xframes/common";

const myTheme: XFramesStyleForPatching = {
// Optional: override style vars
windowRounding: 6,
frameRounding: 4,
grabRounding: 4,

colors: {
// Text
[ImGuiCol.Text]: [myColors.text, 1],
[ImGuiCol.TextDisabled]: [myColors.muted, 1],

// Backgrounds
[ImGuiCol.WindowBg]: [myColors.bg, 1],
[ImGuiCol.ChildBg]: [myColors.bg, 1],
[ImGuiCol.PopupBg]: [myColors.surface, 1],

// Borders
[ImGuiCol.Border]: [myColors.muted, 1],
[ImGuiCol.BorderShadow]: [myColors.bg, 1],

// Frames (inputs, checkboxes, sliders)
[ImGuiCol.FrameBg]: [myColors.surface, 1],
[ImGuiCol.FrameBgHovered]: [myColors.muted, 0.4],
[ImGuiCol.FrameBgActive]: [myColors.muted, 0.6],

// Buttons
[ImGuiCol.Button]: [myColors.accent, 1],
[ImGuiCol.ButtonHovered]: [myColors.accentHover, 1],
[ImGuiCol.ButtonActive]: [myColors.bg, 1],

// Headers (collapsing headers, tree nodes, table headers)
[ImGuiCol.Header]: [myColors.surface, 1],
[ImGuiCol.HeaderHovered]: [myColors.muted, 0.5],
[ImGuiCol.HeaderActive]: [myColors.accent, 1],

// Tabs
[ImGuiCol.Tab]: [myColors.surface, 1],
[ImGuiCol.TabHovered]: [myColors.muted, 0.5],
[ImGuiCol.TabActive]: [myColors.accent, 1],
[ImGuiCol.TabUnfocused]: [myColors.surface, 1],
[ImGuiCol.TabUnfocusedActive]: [myColors.muted, 1],

// Tables
[ImGuiCol.TableHeaderBg]: [myColors.surface, 1],
[ImGuiCol.TableBorderStrong]: [myColors.muted, 1],
[ImGuiCol.TableBorderLight]: [myColors.surface, 1],
[ImGuiCol.TableRowBg]: [myColors.bg, 1],
[ImGuiCol.TableRowBgAlt]: [myColors.surface, 1],

// Interactive elements
[ImGuiCol.CheckMark]: [myColors.accent, 1],
[ImGuiCol.SliderGrab]: [myColors.accent, 1],
[ImGuiCol.SliderGrabActive]: [myColors.accentHover, 1],

// Scrollbars
[ImGuiCol.ScrollbarBg]: [myColors.bg, 1],
[ImGuiCol.ScrollbarGrab]: [myColors.muted, 1],
[ImGuiCol.ScrollbarGrabHovered]: [myColors.text, 0.5],
[ImGuiCol.ScrollbarGrabActive]: [myColors.text, 0.8],

// Misc
[ImGuiCol.Separator]: [myColors.muted, 1],
[ImGuiCol.TextSelectedBg]: [myColors.accent, 0.4],
[ImGuiCol.ModalWindowDimBg]: [myColors.bg, 0.7],
},
};
  1. Use it as the initial theme or switch to it at runtime:
// As initial theme
render(App, assetsBasePath, fontDefs, myTheme);

// Or switch at runtime
(widgetRegistrationService as any).wasmModule.patchStyle(
JSON.stringify(myTheme),
);
note

You don't have to set all 53 color slots. patchStyle only overrides the colors you specify — the rest keep their current values. However, for a complete theme (used as the initial theme), you should define all slots for a consistent look.

ImGuiCol reference​

All 53 color IDs available in the ImGuiCol enum:

Text & backgrounds​

EnumValueControls
Text0Default text color
TextDisabled1Disabled/greyed-out text
WindowBg2Window backgrounds
ChildBg3Child window backgrounds
PopupBg4Popup and tooltip backgrounds

Borders​

EnumValueControls
Border5Border lines
BorderShadow6Border shadow (usually transparent)

Frames (inputs, checkboxes, sliders)​

EnumValueControls
FrameBg7Input field background
FrameBgHovered8Input field background on hover
FrameBgActive9Input field background when active

Title bars​

EnumValueControls
TitleBg10Unfocused title bar
TitleBgActive11Focused title bar
TitleBgCollapsed12Collapsed title bar
MenuBarBg13Menu bar background

Scrollbars​

EnumValueControls
ScrollbarBg14Scrollbar track
ScrollbarGrab15Scrollbar thumb
ScrollbarGrabHovered16Scrollbar thumb on hover
ScrollbarGrabActive17Scrollbar thumb when dragging

Interactive elements​

EnumValueControls
CheckMark18Checkbox check mark
SliderGrab19Slider handle
SliderGrabActive20Slider handle when dragging
Button21Button background
ButtonHovered22Button background on hover
ButtonActive23Button background when clicked

Headers & separators​

EnumValueControls
Header24Collapsing header, tree node, table header
HeaderHovered25Header on hover
HeaderActive26Header when clicked
Separator27Separator line
SeparatorHovered28Separator on hover
SeparatorActive29Separator when dragging

Resize grips​

EnumValueControls
ResizeGrip30Resize grip (bottom-right of windows)
ResizeGripHovered31Resize grip on hover
ResizeGripActive32Resize grip when dragging

Tabs​

EnumValueControls
Tab33Unselected tab
TabHovered34Tab on hover
TabActive35Selected/active tab
TabUnfocused36Unselected tab in unfocused window
TabUnfocusedActive37Selected tab in unfocused window

Plots​

EnumValueControls
PlotLines38Line plot default color
PlotLinesHovered39Line plot hover highlight
PlotHistogram40Histogram bar default color
PlotHistogramHovered41Histogram bar hover highlight

Tables​

EnumValueControls
TableHeaderBg42Table header row background
TableBorderStrong43Outer and header borders
TableBorderLight44Inner column/row borders
TableRowBg45Row background (even rows)
TableRowBgAlt46Alternating row background (odd rows)

Selection & navigation​

EnumValueControls
TextSelectedBg47Text selection highlight
DragDropTarget48Drag-and-drop target highlight
NavHighlight49Keyboard navigation highlight
NavWindowingHighlight50Window switching highlight
NavWindowingDimBg51Background dim during window switching
ModalWindowDimBg52Background dim behind modal windows

Next steps​