Skip to content

Theming

Void Video uses a JSON-driven theming engine for the on-screen UI. Themes control colors, fonts, widget styles, and per-layer transparency — everything rendered by the Nuklear-based menu and OSD system.

Theme Files

Themes are loaded from themes/<name>.json relative to the application directory. The active theme is set in your config:

json
{
  "ui": {
    "theme_name": "void_dark"
  }
}

If the JSON file can't be found, the built-in void_dark defaults are used.

File Format

Theme files are JSON with support for // single-line comments. The top-level structure:

json
{
  "name": "my_theme",
  "fonts": { ... },
  "base_colors": { ... },
  "semantic_colors": { ... },
  "style_overrides": { ... },
  "widget_styles": { ... },
  "layer_overrides": { ... }
}

All sections are optional — omitted sections use the built-in defaults.

Fonts

Custom TTF fonts can be loaded from the theme directory. Each font has a name, a path to a .ttf file (relative to the theme JSON), and a pixel size.

json
"fonts": {
  "body":    { "path": "fonts/DMSans.ttf",                "size": 18 },
  "heading": { "path": "fonts/DMSans.ttf",                "size": 24 },
  "mono":    { "path": "fonts/JetBrainsMono-Regular.ttf", "size": 14 }
}

The font named body is used as the default UI font. If no body font is defined, the first font in the list is used. If no fonts are defined at all, Nuklear's built-in default font is used.

Fonts are baked into a texture atlas on the GPU. All defined fonts share a single atlas, so adding many fonts or very large sizes will increase GPU memory usage.

TIP

Place your .ttf files in a fonts/ subdirectory next to your theme JSON for clean organization.

Base Colors

The 28 base colors that Nuklear derives its entire widget style tree from. Each is an RGBA array [r, g, b, a] with values 0–255.

json
"base_colors": {
  "text":                    [240, 242, 248, 255],
  "window":                  [12,  14,  20,  200],
  "header":                  [16,  18,  26,  220],
  "border":                  [255, 255, 255, 15],
  "button":                  [20,  22,  30,  255],
  "button_hover":            [30,  42,  60,  255],
  "button_active":           [25,  60,  90,  255],
  "toggle":                  [20,  22,  30,  255],
  "toggle_hover":            [30,  42,  60,  255],
  "toggle_cursor":           [79,  195, 247, 255],
  "select":                  [12,  14,  20,  255],
  "select_active":           [20,  40,  60,  180],
  "slider":                  [20,  22,  30,  255],
  "slider_cursor":           [79,  195, 247, 200],
  "slider_cursor_hover":     [128, 220, 255, 220],
  "slider_cursor_active":    [192, 238, 255, 255],
  "property":                [16,  18,  26,  255],
  "edit":                    [16,  18,  26,  255],
  "edit_cursor":             [79,  195, 247, 255],
  "combo":                   [20,  22,  30,  255],
  "chart":                   [20,  22,  30,  255],
  "chart_color":             [79,  195, 247, 180],
  "chart_color_highlight":   [124, 77,  255, 255],
  "scrollbar":               [12,  14,  20,  255],
  "scrollbar_cursor":        [40,  44,  58,  255],
  "scrollbar_cursor_hover":  [60,  68,  90,  255],
  "scrollbar_cursor_active": [79,  195, 247, 180],
  "tab_header":              [16,  18,  26,  220]
}

Semantic Colors

Named colors used by the application for specific UI elements. These are independent of Nuklear's widget system and accessed in code via theme->semantic(SemanticColor::X).

KeyPurposeDefault (void_dark)
osd_section_headerOSD section headingsCyan #4fc3f7
osd_latencyLatency displayGold #f4a842
osd_warningWarning messagesGold #f4a842
osd_errorError messagesRed
osd_exposure_headerExposure section headingGreen #80e0a0
ev_positivePositive EV valuesGreen #80e0a0
ev_negativeNegative EV valuesGold #f4a842
ev_graph_lineEV graph line colorCyan #4fc3f7
scene_change_spikeScene change spike markerPurple #7c4dff
scene_change_textScene change labelPurple dimmed
menu_selected_bgSelected menu item backgroundCyan glow
menu_selected_textSelected menu item textWhite
menu_active_bgActive (current value) backgroundSubtle cyan
menu_active_textActive (current value) textCyan #4fc3f7
histogram_barHistogram bar colorCyan #4fc3f7
no_signalNo signal indicatorRed
json
"semantic_colors": {
  "osd_section_header":  [79,  195, 247, 255],
  "menu_selected_bg":    [79,  195, 247, 40],
  "menu_selected_text":  [240, 242, 248, 255],
  "scene_change_spike":  [124, 77,  255, 255]
}

Style Overrides

Numeric properties for widget geometry — border width, rounding, padding, and spacing.

json
"style_overrides": {
  "window": {
    "border": 1.0,
    "rounding": 0.0,
    "padding": [10, 10],
    "group_padding": [6, 6],
    "group_border": 1.0,
    "spacing": [4, 4],
    "scrollbar_size": [8, 8]
  },
  "button": {
    "border": 1.0,
    "rounding": 2.0,
    "padding": [6, 4]
  },
  "selectable": {
    "rounding": 0.0,
    "padding": [6, 3]
  },
  "chart": {
    "border": 0.0,
    "rounding": 0.0,
    "padding": [4, 4]
  },
  "scrollbar": {
    "border": 0.0,
    "rounding": 3.0,
    "rounding_cursor": 3.0
  }
}

Widget Styles

Per-widget background style items for each interaction state (normal, hover, active). Each state supports multiple types:

Color

Flat solid color:

json
{ "type": "color", "value": [79, 195, 247, 40] }

Gradient

Vertical gradient rendered as a GPU texture:

json
{ "type": "gradient", "top": [20, 22, 30, 255], "bottom": [12, 14, 20, 255] }

Image

PNG or JPEG texture loaded from a file path (relative to the theme JSON):

json
{ "type": "image", "path": "button_bg.png" }

Nine-Slice

Stretchable image with fixed border regions:

json
{ "type": "nine_slice", "path": "panel.png", "l": 8, "t": 8, "r": 8, "b": 8 }

Hidden

Fully transparent / invisible:

json
{ "type": "hidden" }

Widget Paths

The widget_styles section uses dot-separated paths to target specific widgets:

PathWidgetStates
buttonStandard buttonnormal, hover, active
contextual_buttonContext menu buttonnormal, hover, active
menu_buttonMenu bar buttonnormal, hover, active
selectableSelectable itemnormal, hover, pressed
selectable.activeSelectable (active)normal_active, hover_active, pressed_active
toggle / checkboxCheckboxnormal, hover, active
checkbox.cursorCheckbox check markcursor_normal, cursor_hover
optionRadio buttonnormal, hover, active
sliderSlider tracknormal, hover, active
slider.cursorSlider handlecursor_normal, cursor_hover, cursor_active
progressProgress bar tracknormal, hover, active
progress.cursorProgress bar fillcursor_normal, cursor_hover, cursor_active
scrollbarScrollbar tracknormal, hover, active
scrollbar.cursorScrollbar thumbcursor_normal, cursor_hover, cursor_active
editText edit fieldnormal, hover, active
propertyProperty fieldnormal, hover, active
comboCombo boxnormal, hover, active
tabTab headernormal
windowWindow backgroundnormal
window.headerWindow title barnormal, hover, active
window.scalerWindow resize handlenormal

Example:

json
"widget_styles": {
  "selectable": {
    "normal":  { "type": "color", "value": [12, 14, 20, 0] },
    "hover":   { "type": "color", "value": [79, 195, 247, 25] },
    "pressed": { "type": "color", "value": [79, 195, 247, 40] }
  },
  "slider.cursor": {
    "cursor_normal": { "type": "color", "value": [79, 195, 247, 200] },
    "cursor_hover":  { "type": "color", "value": [128, 220, 255, 230] },
    "cursor_active": { "type": "color", "value": [192, 238, 255, 255] }
  }
}

Layer Overrides

Different UI layers (OSD, Menu, Status bar) can have independent alpha values for window backgrounds and borders. This lets the OSD be more transparent than the menu system.

json
"layer_overrides": {
  "OSD":    { "window_alpha": 180, "border_alpha": 100 },
  "Menu":   { "window_alpha": 210, "border_alpha": 150 },
  "Status": { "window_alpha": 180, "border_alpha": 120 }
}

Values are 0–255, where 255 is fully opaque.

Creating a Custom Theme

  1. Copy themes/void_dark.json as a starting point
  2. Rename and edit the colors to match your preference
  3. Set theme_name in your config to your new theme name (without .json)
  4. Restart Void Video

TIP

You only need to include the sections you want to change. Omitted sections inherit from the built-in defaults.