--- name: godot-ui-theming description: "Expert blueprint for UI themes using Theme resources, StyleBoxes, custom fonts, and theme overrides for consistent visual styling. Covers StyleBoxFlat/Texture, theme inheritance, dynamic theme switching, and font variations. Use when implementing consistent UI styling OR supporting multiple themes. Keywords Theme, StyleBox, StyleBoxFlat, add_theme_override, font, theme inheritance, dark mode." --- # UI Theming Theme resources, StyleBox styling, font management, and override system define consistent UI visual identity. ## Available Scripts ### [global_theme_manager.gd](scripts/global_theme_manager.gd) Expert theme manager with dynamic switching, theme variants, and fallback handling. ### [ui_scale_manager.gd](scripts/ui_scale_manager.gd) Runtime theme switching and DPI/Resolution scale management. ### [theme_swapper.gd](scripts/theme_swapper.gd) Dynamic Dark/Light mode implementation using cascading theme root propagation. ### [danger_button_assignment.gd](scripts/danger_button_assignment.gd) Expert use of `theme_type_variation` for semantic UI styling without scene duplication. ### [dynamic_stylebox_color.gd](scripts/dynamic_stylebox_color.gd) Safe runtime StyleBox modification. Demonstrates the critical `duplicate()` pattern for isolated overrides. ### [procedural_theme_safe.gd](scripts/procedural_theme_safe.gd) Reliable theming for generated UI elements using `NOTIFICATION_THEME_CHANGED`. ### [custom_chart_drawing.gd](scripts/custom_chart_drawing.gd) Pattern for reading active Theme properties (colors, fonts) in custom `_draw()` logic. ### [theme_isolation.gd](scripts/theme_isolation.gd) Ensuring HUD consistency by isolating nodes from parent themes and referencing Project Defaults. ### [pulsating_ui_theme.gd](scripts/pulsating_ui_theme.gd) Animating UI styles via Tweens. Targets StyleBox properties directly after duplication. ### [crisp_ui_scaler.gd](scripts/crisp_ui_scaler.gd) High-quality resolution-independent scaling using `content_scale_factor` to maintain font crispness. ### [memory_safe_custom_drawing.gd](scripts/memory_safe_custom_drawing.gd) Fixing the "disappearing stylebox" bug by caching resources at the class level for the RenderingServer. ### [rtl_theme_mirroring.gd](scripts/rtl_theme_mirroring.gd) Bi-directional (RTL/LTR) UI support. Swaps theme variants dynamically based on layout direction. ### [focus_prompt_icon_swapper.gd](scripts/focus_prompt_icon_swapper.gd) Controller/keyboard prompt icon bank swap + focus highlight panel. **MANDATORY** for accessibility prompt chrome. ## NEVER Do in UI Theming - **NEVER create StyleBox in `_ready()` for many nodes** — Instantiating `StyleBoxFlat.new()` 100 times creates 100 unique objects. Use a Theme resource for shared heritage. - **NEVER forget theme inheritance** — Parent themes are ignored if a child has its own theme. Apply themes at the root and use `theme_type_variation` for specific overrides. - **NEVER hardcode colors in StyleBox** — Use `theme.get_color()` to maintain a single source of truth for your palette. - **NEVER use `add_theme_override` for global styles** — This is brittle. Define styles in a Theme resource for automatic propagation across the project. - **NEVER modify theme resources during `_draw()` OR `_process()`** — Frequent layout recalculations will severely degrade performance. - **NEVER assign `StyleBoxEmpty` to focus styles without a fallback** — This invisibly breaks controller/keyboard navigation [1]. Always provide a visible alternative (e.g. scale change). - **NEVER use standard `set()` for theme properties** — Calling `node.set("font_color", red)` fails. You MUST use the dedicated `add_theme_color_override()` API [3]. - **NEVER use `expand_margin_*` to increase clickable area** — It only expands the VISUAL bounds. Use `content_margin_*` on the StyleBox or adjust the Control's size to ensure input works [5]. - **NEVER define StyleBoxes as local variables inside `_draw()`** — They will be garbage collected before the RenderingServer can finish drawing them [7]. Store at class level. - **NEVER duplicate scenes/themes just to change one color** — Use `theme_type_variation` to create lightweight derived styles (e.g. "DangerButton") within the same Theme [8]. - **NEVER skip `corner_radius_all` on StyleBoxFlat** — shorthand for uniform rounding; prefer it over four separate radius fields when all corners match. - **NEVER confuse Theme items with Control overrides** — `add_theme_*_override` beats Theme resource items on that node only; a child Control with its own `theme` still blocks parent cascade. Clear with `remove_theme_*_override` when swapping roots — do not leave stale overrides fighting the new Theme. ## Decision Tree — Theme Ownership | Goal | Choose | Notes / script | |------|--------|----------------| | App-wide look | Project Settings → **GUI → Theme** | Author in Theme editor — no per-node StyleBox tutorials here | | One Control differs | `add_theme_*_override` on that node | Local only; never for global styles | | Button/panel subtype | `theme_type_variation` | See [danger_button_assignment.gd](scripts/danger_button_assignment.gd) | | Runtime color tweak without mutating shared Theme | `stylebox.duplicate()` then override | See [dynamic_stylebox_color.gd](scripts/dynamic_stylebox_color.gd) | Fonts & StyleBoxes: edit via **Theme editor** / Project Theme. Runtime helpers: [global_theme_manager.gd](scripts/global_theme_manager.gd), [theme_swapper.gd](scripts/theme_swapper.gd), [procedural_theme_safe.gd](scripts/procedural_theme_safe.gd). ## Expert Theming Patterns ### 1. Shared-Color-Palette (The Static Pattern) Maintain a single source of truth for UI colors accessible to both the Theme Editor and GDScript. - **Theme Setup**: In your `.theme` file, create a custom type called `Palette` and add `Color` items (e.g., `primary`, `danger`, `accent`). - **Static Access**: Use a `SharedPalette` class with `static func get_primary() -> Color` that pulls from `ThemeDB.get_project_theme()`. This ensures UI scripts and the visual theme never drift. ### 2. Theme-Type-Variations Avoid duplicating button scenes or styleboxes for variants like "Danger" or "Ghost" styles. - **Implementation**: In the Theme Editor, create a new **Type Variation**. Set its **Base Type** to `Button`. - **Inheritance**: The variation inherits all properties from the base type. You only override what's different (e.g., set `font_color` to red for `DangerButton`). - **Usage**: Assign via code `node.theme_type_variation = &"DangerButton"` or via the Inspector dropdown. > **MANDATORY**: Read [danger_button_assignment.gd](scripts/danger_button_assignment.gd) — do not fork Button scenes for color variants. ### 3. Runtime StyleBox Color (duplicate first) When a single Control needs a runtime tint, **duplicate** the StyleBox before mutating — shared Theme StyleBoxes must stay immutable. > **MANDATORY**: Read [dynamic_stylebox_color.gd](scripts/dynamic_stylebox_color.gd) — never mutate a Theme StyleBox in place. ### 4. Runtime-Theme-Swapping (Accessibility) Efficiently switch the visual style of the entire game for Light, Dark, or High-Contrast modes. - **Cascading Updates**: Assign a new `Theme` resource to the **root** Control node. Godot propagates this to every descendant. - **Accessibility**: Use `NOTIFICATION_THEME_CHANGED` to update elements that don't support automatic theming (like custom `_draw()` logic or RichText effects). - **High-Contrast**: Ensure High-Contrast themes use pure black/white and thicker focus outlines for low-vision accessibility. > **MANDATORY**: Read [theme_swapper.gd](scripts/theme_swapper.gd) — swap at the theme root; do not walk every Control assigning themes. ### 5. RTL / LTR Theme Mirroring Bi-directional layouts need mirrored StyleBox / type-variation banks when direction flips — not hand-flipped anchors alone. > **MANDATORY**: Read [rtl_theme_mirroring.gd](scripts/rtl_theme_mirroring.gd) — swap theme variants from layout direction; do not hardcode LTR margins. ### 6. Themed-Asset-Loading (Seasonal Variants) Godot Themes support more than just colors and fonts—they can store textures. - **Setup**: Define UI icons as **Icon** items within separate Theme resources (e.g., `halloween.theme`, `christmas.theme`). - **Swapping**: Swapping the root theme resource instantly cascades the new icon textures across all buttons and panels without manual logic. ### 7. UI-Focus-Manager (Dynamic Controller Icons) Standard focus styles are static. For accessibility UX, swap prompt icons by device and tween a highlight panel to `get_global_rect()`. > **MANDATORY**: Read [focus_prompt_icon_swapper.gd](scripts/focus_prompt_icon_swapper.gd) — do not paste joypad icon paths into Control scripts. Pairs with Runtime-Theme-Swapping (Accessibility) above and [theme_swapper.gd](scripts/theme_swapper.gd) for High-Contrast roots. ### 8. Asset-Dependency-Audit (Draw-Call Reduction) Ensuring UI textures are optimized for rendering performance. - **Atlas Packing**: Use `AtlasTexture` to crop small UI elements from a singular large sheet. This reduces VRAM state changes and minimizes draw calls [14]. - **Compression Policy**: - **2D/Pixel Art**: Use **Lossless** compression to avoid blurry artifacts [15]. - **UI Backgrounds**: Use **Lossy** or **Basis Universal** for large illustrations to save disk space without decreasing VRAM usage [15]. - **Audit**: Use `ResourceLoader.get_dependencies(scene_path)` to ensure no uncompressed raw assets (e.g. `.png`) are leaking into the final export [19]. ## Deep recipes (on demand) > LLM-ignorance rule: if a general agent would not know it before reading, it lives here or in `scripts/` — never delete, only move. | Topic | Reference | |-------|-----------| | StyleBox / font setup | [theme-authoring-recipes.md](references/theme-authoring-recipes.md) | ## Reference > Progressive disclosure: open Official Documentation links only when researching a specific API; > load Related Skills when routing work to a peer domain — do not preload the whole lattice. ### Official Documentation - [GUI skinning](https://docs.godotengine.org/en/stable/tutorials/ui/gui_skinning.html) — Theme resources, StyleBoxes, and cascading skin ownership. - [Using the theme editor](https://docs.godotengine.org/en/stable/tutorials/ui/gui_using_theme_editor.html) — Authoring Theme assets without hand-editing every Control override. - [Theme type variations](https://docs.godotengine.org/en/stable/tutorials/ui/gui_theme_type_variations.html) — Variants for button/panel subtypes without duplicating whole themes. - [Using fonts](https://docs.godotengine.org/en/stable/tutorials/ui/gui_using_fonts.html) — DynamicFont / font size theming for UI readability. - [Custom GUI controls](https://docs.godotengine.org/en/stable/tutorials/ui/custom_gui_controls.html) — When themed `_draw()` needs theme item lookups. - [Size and anchors](https://docs.godotengine.org/en/stable/tutorials/ui/size_and_anchors.html) — Layout that survives theme scale and DPI changes. - [Theme](https://docs.godotengine.org/en/stable/classes/class_theme.html) — Runtime get/set for colors, constants, icons, StyleBoxes. - [ThemeDB](https://docs.godotengine.org/en/stable/classes/class_themedb.html) — Project default theme and fallback resolution. - [Control](https://docs.godotengine.org/en/stable/classes/class_control.html) — Theme overrides and NOTIFICATION_THEME_CHANGED. - [StyleBox](https://docs.godotengine.org/en/stable/classes/class_stylebox.html) — Panel/button chrome used by most Theme skins. - [AtlasTexture](https://docs.godotengine.org/en/stable/classes/class_atlastexture.html) — Pack UI icons to cut draw-call churn. - [Input](https://docs.godotengine.org/en/stable/classes/class_input.html) — Custom cursors and joypad-driven prompt icon swaps. ### Related Skills #### Prerequisites - [godot-project-foundations](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md) — Project layout and default Control/Theme placement before skin systems. - [godot-resource-data-patterns](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md) — Theme/StyleBox/icon banks as Resources instead of path-string sprawl. - [godot-ui-containers](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md) — Containers must be correct before theme polish hides layout bugs. - [godot-signal-architecture](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md) — Theme swap and accessibility mode changes should signal up, not poll. #### Complements - [godot-ui-rich-text](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-rich-text/SKILL.md) — BBCode/fonts that must stay coherent with Theme typefaces. - [godot-tweening](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md) — Focus highlight panels and seasonal theme transitions. - [godot-input-handling](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-input-handling/SKILL.md) — Controller-aware prompt icons and focus navigation. - [godot-autoload-architecture](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md) — Theme managers as Autoloads with clear ownership. - [godot-export-builds](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-export-builds/SKILL.md) — Atlas/compression policies that affect shipped UI VRAM. - [godot-performance-optimization](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md) — Draw-call and atlas packing for dense HUD skins. #### Downstream / consumers - [godot-theme-easter](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-theme-easter/SKILL.md) — Seasonal overlays that swap Theme/icon banks on top of this skill. - [godot-composition-apps](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-composition-apps/SKILL.md) — App UIs that live or die on Theme consistency. - [godot-genre-visual-novel](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-genre-visual-novel/SKILL.md) — Dialogue chrome heavily Theme-driven. - [godot-platform-mobile](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-platform-mobile/SKILL.md) — Touch-scale and high-contrast theme variants. #### Master - [godot-master](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-master/SKILL.md) — Library router and mirrored module entry for UI theming.