godot-ui-theming

$npx mdskill add thedivergentai/GD-Agentic-Skills/godot-ui-theming

Creates consistent UI themes using Theme resources, StyleBoxes, and fonts.

  • Solves inconsistent UI styling across scenes and multiple theme support.
  • Depends on Theme resources, StyleBoxFlat/Texture, and custom fonts.
  • Uses theme inheritance, overrides, and dynamic switching for visual consistency.
  • Delivers scripts for theme management, dark mode, and runtime style changes.

SKILL.md

.github/skills/godot-ui-themingView on GitHub ↗
---
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.

## Godot 4.7: UI Theming

- **Control offset transform** for inspector-driven visual tweaks without relayout.
- `ResourceImporterDynamicFont.hinting` default changed to **3** — verify font crispness on target DPI.
- **GradientTexture2D** supports **conic** gradients.

## 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.

More from thedivergentai/GD-Agentic-Skills

SkillDescription
godot-2d-animationExpert patterns for 2D animation in Godot using AnimatedSprite2D and skeletal cutout rigs. Use when implementing sprite frame animations, procedural animation (squash/stretch), cutout bone hierarchies, or frame-perfect timing systems. Trigger keywords: AnimatedSprite2D, SpriteFrames, animation_finished, animation_looped, frame_changed, frame_progress, set_frame_and_progress, cutout animation, skeletal 2D, Bone2D, procedural animation, animation state machine, advance(0).
godot-2d-physicsExpert patterns for Godot 2D physics including collision layers/masks, Area2D triggers, raycasting, and PhysicsDirectSpaceState2D queries. Use when implementing collision detection, trigger zones, line-of-sight systems, or manual physics queries. Trigger keywords: CollisionShape2D, CollisionPolygon2D, collision_layer, collision_mask, set_collision_layer_value, set_collision_mask_value, Area2D, body_entered, body_exited, RayCast2D, force_raycast_update, PhysicsPointQueryParameters2D, PhysicsShapeQueryParameters2D, direct_space_state, move_and_collide, move_and_slide.
godot-3d-lightingExpert patterns for Godot 3D lighting including DirectionalLight3D shadow cascades, OmniLight3D attenuation, SpotLight3D projectors, VoxelGI vs SDFGI, and LightmapGI baking. Use when implementing realistic 3D lighting, shadow optimization, global illumination, or light probes. Trigger keywords: DirectionalLight3D, OmniLight3D, SpotLight3D, shadow_enabled, directional_shadow_mode, directional_shadow_split, omni_range, omni_attenuation, spot_range, spot_angle, VoxelGI, SDFGI, LightmapGI, ReflectionProbe, Environment, WorldEnvironment.
godot-3d-materialsExpert patterns for Godot 3D PBR materials using StandardMaterial3D including albedo, metallic/roughness workflows, normal maps, ORM texture packing, transparency modes, and shader conversion. Use when creating realistic 3D surfaces, PBR workflows, or material optimization. Trigger keywords: StandardMaterial3D, BaseMaterial3D, albedo_texture, metallic, metallic_texture, roughness, roughness_texture, normal_texture, normal_enabled, orm_texture, transparency, alpha_scissor, alpha_hash, cull_mode, ShaderMaterial, shader parameters.
godot-3d-world-buildingExpert patterns for 3D level design using GridMap with MeshLibrary, CSG constructive solid geometry, occlusion, and runtime GridMap builders. Use when building 3D levels, modular tilesets, or BSP-style geometry. For sky/fog/Environment recipes, route to godot-3d-lighting. Trigger keywords: GridMap, MeshLibrary, set_cell_item, get_cell_item, map_to_local, local_to_map, CSGCombiner3D, CSGBox3D, CSGSphere3D, CSGPolygon3D, OccluderInstance3D, bake CSG.
godot-ability-systemExpert patterns for RPG/action ability systems including cooldown strategies, combo systems, ability chaining, skill trees with prerequisites, upgrade paths, and resource management. Use when implementing unlockable abilities, character progression, or complex skill systems. Trigger keywords: PlayerAbility, AbilityManager, cooldown, SkillTree, SkillNode, prerequisites, can_use, execute, ComboSystem, ability_chain, global_cooldown, charge_system, upgrade_path.
godot-adapt-2d-to-3dExpert patterns for migrating 2D games to 3D including node type conversions, camera systems (third-person, first-person, orbit), physics layer migration, sprite-to-model art pipeline, and control scheme adaptations. Use when porting 2D projects to 3D or adding 3D elements. Trigger keywords: CharacterBody2D to CharacterBody3D, Area2D to Area3D, Camera2D to Camera3D, Vector2 to Vector3, collision_layer migration, sprite to MeshInstance3D, 2D to 3D conversion.
godot-adapt-3d-to-2dExpert patterns for simplifying 3D games to 2D including dimension reduction strategies, 2.5D fake-depth, isometric ports, camera flattening, physics conversion, 3D-to-sprite art pipeline, and control simplification. Use when porting 3D to 2D, building 2.5D / isometric / fake-depth gameplay, creating 2D versions for mobile, or prototyping. Trigger keywords: CharacterBody3D to CharacterBody2D, Camera3D to Camera2D, Vector3 to Vector2, flatten Z-axis, 2.5D, isometric, fake depth, Y-sort, simulated Z, orthogonal projection, 3D to sprite conversion, performance optimization.
godot-adapt-desktop-to-mobileExpert patterns for porting desktop games to mobile including touch control schemes (virtual joystick, gesture detection), UI scaling for small screens, performance optimization for mobile GPUs, battery life management, and platform-specific features. Use when creating mobile ports or cross-platform mobile builds. Trigger keywords: TouchScreenButton, virtual_joystick, gesture_detector, InputEventScreenTouch, InputEventScreenDrag, mobile_optimization, battery_saving, adaptive_performance, MOBILE_ENABLED.
godot-adapt-mobile-to-desktopExpert patterns for scaling mobile games to desktop including mouse/keyboard controls, increased resolution and graphical fidelity, expanded UI layouts, settings menus, window management, and platform-specific features. Use when creating desktop ports or cross-platform releases. Trigger keywords: mouse_controls, keyboard_shortcuts, resolution_scaling, graphics_settings, fullscreen_toggle, window_modes, Steam_integration, desktop_optimization.