reckfellENGINE

UI themes: inheritance and local overrides

A working shared theme, lookup order and an isolated StyleBox for one button.

On this page

Store appearance in one place

Theme groups colors, constants, fonts, font sizes, textures, StyleBoxes and sounds by item name and Control type. For example, font_color for Button and font_color for Label are separate entries. Names and case must match the control API. Its API page lists available theme items.

Assign a shared theme to the UI root. Child Controls and Windows can use it and add their own themes. A local Theme Override suits one exception; a named type variation suits a reusable button variant. Do not copy the whole theme for one color.

Run the example

Add UiSampleStyle.cs and ThemeBasicsDemo.cs to a C# project. Attach ThemeBasicsDemo to a separate level's Control root, build and run with F6.

Changing the shared background changes the first two buttons' StyleBox. Separate first background copies the resource and changes only the first. Removing the override restores shared styling. Move the pointer away to inspect normal: hover and pressed have separate entries.

ThemeBasicsDemo.cs
C#
using Reckfell;

public partial class ThemeBasicsDemo : Control
{
    public Button Left { get; private set; } = null!;
    public Button Right { get; private set; } = null!;
    public StyleBoxFlat SharedNormal { get; private set; } = null!;
    public override void _Ready()
    {
        UiSampleStyle.Prepare(this);
        Theme.SetColor("font_color", "Button", Colors.White);
        SharedNormal = (StyleBoxFlat)Theme.GetStylebox("normal", "Button");
        var margin = new MarginContainer(); AddChild(margin); margin.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect, margin: 24);
        var column = new VBoxContainer(); margin.AddChild(column);
        column.AddChild(new Label { Text = UiSampleStyle.Text("Общая тема и локальное изменение", "Shared theme and local override") });
        Left = new Button { Text = UiSampleStyle.Text("Первая кнопка", "First button") };
        Right = new Button { Text = UiSampleStyle.Text("Вторая кнопка", "Second button") };
        column.AddChild(Left); column.AddChild(Right);
        var shared = new Button { Text = UiSampleStyle.Text("Изменить общий фон", "Change shared background") }; column.AddChild(shared);
        shared.Pressed += () => SharedNormal.BgColor = new Color(.08f, .16f, .35f);
        var local = new Button { Text = UiSampleStyle.Text("Отдельный фон первой", "Separate first background") }; column.AddChild(local);
        local.Pressed += SeparateLeft;
        var reset = new Button { Text = UiSampleStyle.Text("Убрать локальное изменение", "Remove local override") }; column.AddChild(reset);
        reset.Pressed += () => Left.RemoveThemeStyleboxOverride("normal");
    }

    public void SeparateLeft()
    {
        var own = (StyleBoxFlat)Left.GetThemeStylebox("normal").Duplicate();
        own.BgColor = new Color(.12f, .28f, .7f);
        Left.AddThemeStyleboxOverride("normal", own);
    }
}

Resolve a value

A normal Control query first checks its local override. It then searches branch themes from the nearest owner toward parents, considering variation/type dependencies within each theme. Project/default themes and fallbacks follow the branch.

The theme owner here is a Control or Window carrying Theme in the UI chain, separate from the Entity.Owner property used when saving a hierarchy. An ordinary Entity breaks inheritance: Control(Theme) → Entity → Button does not pass the theme to the button. Group UI with Control/Container, or explicitly assign Theme to the child UI root. Changing Entity.Owner does not restore inheritance.

This is nearest-theme-first lookup, not a global rule that a variation always overrides Button everywhere. A partial local theme can replace one setting and inherit others. Control.GetThemeColor/GetThemeStylebox return the value resolved for that control. Theme.GetColor/GetStylebox read the resource itself without walking Control ancestry.

ChangeScope
AddThemeColorOverride on one buttonThat Control; RemoveThemeColorOverride removes the exception
SetColor on a shared ThemeUsers of that entry unless a closer value overrides it
Mutate a shared StyleBoxFlatEvery control sharing the resource
Duplicate the StyleBox and override locallyThe separate copy

A shallow Duplicate suffices for the shown StyleBoxFlat. For resources containing mutable subresources, check what remains shared; deep duplication has its own resource rules. GetThemeStylebox does not make an independent copy.

Distinguish three diagnostic questions:

QueryMeaning
Theme.HasColor("font_color", "Button")Whether this exact resource has the entry
button.HasThemeColorOverride("font_color")Whether this button has a local exception
button.GetThemeColor("font_color")The value resolved through theme lookup

Tune in a practical order

Set typography, content margins and ordinary states first. Then define hover, pressed, disabled and visible focus. StyleBox borders/margins can change required size; check layout with long translations. Modulate changes the whole rendered result rather than one specific theme color.

BeginBulkThemeOverride/EndBulkThemeOverride can group notifications for several local changes. End the block in finally if exceptions are possible: an unfinished bulk block leaves notifications deferred. This is a flag rather than a nesting counter; do not nest these blocks. Remove obsolete overrides when moving to a shared theme or they continue hiding the new design. Project-wide options include gui/theme/custom and gui/theme/custom_font; these settings require the corresponding restart.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.