Godot UI / Control nodes (4.x)
Lay out responsive UI with Control anchors and Container nodes, style it with a
Theme, and make it navigable by keyboard and gamepad. Targets Godot 4.7.
When to use
- Use when building HUDs, menus, inventories, dialog boxes, or settings screens with
Control-derived nodes; arranging UI that adapts to window size; theming; or wiring
focus navigation for controller/keyboard.
When not to use: in-world 2D nodes (Node2D/sprites) → godot-nodes-scenes;
animating UI transitions → godot-animation (Tween); genre UIs like card hands →
card-game/visual-novel. For full input rebinding → input-systems.
Core workflow
- Use
Control nodes for UI, not Node2D. Controls have a rect (position + size),
anchors, and participate in focus/theming.
- Anchor for responsiveness. Anchors are fractions (0–1) of the parent rect that the
Control's edges stick to. Use the editor's Layout presets (Top-Left, Full Rect,
Center, etc.) instead of hand-placing pixels.
- Let Containers position children. Put children in a
VBoxContainer,
HBoxContainer, GridContainer, MarginContainer, etc. — the container sets their
position/size; you control flow with size_flags. Don't set child anchors inside a
container (it overrides them).
- Style with a
Theme. Assign a Theme resource on a top Control; children inherit
it. Override per-node with theme overrides only when necessary.
- Wire focus so gamepad/keyboard can move between buttons; set a default focused
control and define neighbors or rely on auto-neighbor.
- Connect signals (
pressed, toggled, text_submitted, value_changed).
Patterns
1. Responsive layout with anchors (code form)
extends Control
func _ready() -> void:
# Stretch this panel to fill its parent (equivalent to the "Full Rect" preset).
anchors_preset = Control.PRESET_FULL_RECT
# Or set anchors manually: all four edges at the parent's far corners.
# anchor_left = 0; anchor_top = 0; anchor_right = 1; anchor_bottom = 1
2. A menu built from containers + button signals
extends VBoxContainer # children stack vertically, auto-sized
func _ready() -> void:
for child in get_children():
if child is Button:
child.pressed.connect(_on_button_pressed.bind(child.name))
# Give the first button focus so a gamepad can navigate immediately.
if get_child_count() > 0:
(get_child(0) as Control).grab_focus()
func _on_button_pressed(which: StringName) -> void:
match which:
"PlayButton": get_tree().change_scene_to_file("res://game.tscn")
"QuitButton": get_tree().quit()
3. Size flags: make one child expand to fill leftover space
# In a HBoxContainer: a label on the left, a spacer that eats remaining width.
func _ready() -> void:
$Label.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
$Spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL # grows to fill
4. Theme override for one node (without a full Theme resource)
func _ready() -> void:
# Per-node overrides: use add_theme_* (type-specific setters).
$Title.add_theme_font_size_override("font_size", 32)
$Title.add_theme_color_override("font_color", Color.GOLD)
$Panel.add_theme_stylebox_override("panel", preload("res://ui/panel.stylebox.tres"))
Pitfalls
- Mixing manual position with Containers. A child of a
Container cannot set its own
position/anchors — the container owns layout. To free-place, take the node out of the
container or use a plain Control/PanelContainer wrapper.
- Anchors vs offsets. Anchors are fractions of the parent; offsets are pixel deltas
from the anchored point. Set anchors via presets, then nudge with offsets. Setting only
position while anchors are at 0 makes UI not scale with the window.
Node2D for UI. Buttons/labels parented under a Node2D won't theme or take focus
correctly. Keep UI under a CanvasLayer/Control subtree.
- Focus lost on gamepad. If nothing is focused, directional input does nothing. Call
grab_focus() on an initial control and ensure focus_mode is not FOCUS_NONE.
- Theme vs theme override. A
Theme resource styles a whole subtree; add_theme_*
overrides one node. Overusing per-node overrides defeats centralized theming.
rect_* properties are renamed. Godot 3's rect_size/rect_position/rect_min_size
are now size/position/custom_minimum_size in 4.x.
mouse_filter on a full-rect Control can swallow clicks meant for nodes beneath it;
set MOUSE_FILTER_IGNORE on purely decorative panels.
References
- For the anchor/offset math, every Container type, building/extending Theme and StyleBox
resources, focus neighbor wiring, and
CanvasLayer for HUDs, read
references/layout-and-theming.md.
Related skills
game-ui-ux — cross-engine UI/UX: responsive scaling, safe areas, focus navigation, screen flow.
godot-animation — Tween-based UI transitions and juicing.
godot-signals-groups — connecting UI events to game logic.
input-systems — rebindable input and multi-device focus.
card-game / visual-novel — UI-heavy genre templates.
1---2name: godot-ui-control3description: Build Godot 4.7 user interfaces with Control nodes: anchors and offsets for responsive layout, Container nodes (VBox/HBox/Grid/Margin) for automatic arrangement, Theme resources for consistent styling, and keyboard/gamepad focus navigation. Use when laying out a HUD, menu, or UI in a Godot project, working with Control/Container nodes, anchors, themes, or focus in a .tscn.4---5
6# Godot UI / Control nodes (4.x)
7
8Lay out responsive UI with `Control` anchors and `Container` nodes, style it with a
9`Theme`, and make it navigable by keyboard and gamepad. Targets **Godot 4.7**.
10
11## When to use
12
13- Use when building HUDs, menus, inventories, dialog boxes, or settings screens with
14 `Control`-derived nodes; arranging UI that adapts to window size; theming; or wiring
15 focus navigation for controller/keyboard.
16
17**When *not* to use:** in-world 2D nodes (`Node2D`/sprites) → `godot-nodes-scenes`;
18animating UI transitions → `godot-animation` (Tween); genre UIs like card hands →
19`card-game`/`visual-novel`. For full input rebinding → `input-systems`.
20
21## Core workflow
22
231. **Use `Control` nodes for UI**, not `Node2D`. Controls have a rect (position + size),
24 anchors, and participate in focus/theming.
252. **Anchor for responsiveness.** Anchors are fractions (0–1) of the parent rect that the
26 Control's edges stick to. Use the editor's **Layout** presets (Top-Left, Full Rect,
27 Center, etc.) instead of hand-placing pixels.
283. **Let Containers position children.** Put children in a `VBoxContainer`,
29 `HBoxContainer`, `GridContainer`, `MarginContainer`, etc. — the container sets their
30 position/size; you control flow with `size_flags`. Don't set child anchors inside a
31 container (it overrides them).
324. **Style with a `Theme`.** Assign a `Theme` resource on a top Control; children inherit
33 it. Override per-node with theme overrides only when necessary.
345. **Wire focus** so gamepad/keyboard can move between buttons; set a default focused
35 control and define neighbors or rely on auto-neighbor.
366. **Connect signals** (`pressed`, `toggled`, `text_submitted`, `value_changed`).
37
38## Patterns
39
40### 1. Responsive layout with anchors (code form)
41
42```gdscript
43extends Control
44
45func _ready() -> void:
46 # Stretch this panel to fill its parent (equivalent to the "Full Rect" preset).
47 anchors_preset = Control.PRESET_FULL_RECT
48 # Or set anchors manually: all four edges at the parent's far corners.
49 # anchor_left = 0; anchor_top = 0; anchor_right = 1; anchor_bottom = 1
50```
51
52### 2. A menu built from containers + button signals
53
54```gdscript
55extends VBoxContainer # children stack vertically, auto-sized
56
57func _ready() -> void:
58 for child in get_children():
59 if child is Button:
60 child.pressed.connect(_on_button_pressed.bind(child.name))
61 # Give the first button focus so a gamepad can navigate immediately.
62 if get_child_count() > 0:
63 (get_child(0) as Control).grab_focus()
64
65func _on_button_pressed(which: StringName) -> void:
66 match which:
67 "PlayButton": get_tree().change_scene_to_file("res://game.tscn")
68 "QuitButton": get_tree().quit()
69```
70
71### 3. Size flags: make one child expand to fill leftover space
72
73```gdscript
74# In a HBoxContainer: a label on the left, a spacer that eats remaining width.
75func _ready() -> void:
76 $Label.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
77 $Spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL # grows to fill
78```
79
80### 4. Theme override for one node (without a full Theme resource)
81
82```gdscript
83func _ready() -> void:
84 # Per-node overrides: use add_theme_* (type-specific setters).
85 $Title.add_theme_font_size_override("font_size", 32)
86 $Title.add_theme_color_override("font_color", Color.GOLD)
87 $Panel.add_theme_stylebox_override("panel", preload("res://ui/panel.stylebox.tres"))
88```
89
90## Pitfalls
91
92- **Mixing manual position with Containers.** A child of a `Container` cannot set its own
93 position/anchors — the container owns layout. To free-place, take the node out of the
94 container or use a plain `Control`/`PanelContainer` wrapper.
95- **Anchors vs offsets.** Anchors are fractions of the parent; offsets are pixel deltas
96 from the anchored point. Set anchors via presets, then nudge with offsets. Setting only
97 position while anchors are at 0 makes UI not scale with the window.
98- **`Node2D` for UI.** Buttons/labels parented under a `Node2D` won't theme or take focus
99 correctly. Keep UI under a `CanvasLayer`/`Control` subtree.
100- **Focus lost on gamepad.** If nothing is focused, directional input does nothing. Call
101 `grab_focus()` on an initial control and ensure `focus_mode` is not `FOCUS_NONE`.
102- **Theme vs theme override.** A `Theme` resource styles a whole subtree; `add_theme_*`
103 overrides one node. Overusing per-node overrides defeats centralized theming.
104- **`rect_*` properties are renamed.** Godot 3's `rect_size`/`rect_position`/`rect_min_size`
105 are now `size`/`position`/`custom_minimum_size` in 4.x.
106- **`mouse_filter`** on a full-rect Control can swallow clicks meant for nodes beneath it;
107 set `MOUSE_FILTER_IGNORE` on purely decorative panels.
108
109## References
110
111- For the anchor/offset math, every Container type, building/extending Theme and StyleBox
112 resources, focus neighbor wiring, and `CanvasLayer` for HUDs, read
113 `references/layout-and-theming.md`.
114
115## Related skills
116
117- `game-ui-ux` — cross-engine UI/UX: responsive scaling, safe areas, focus navigation, screen flow.
118- `godot-animation` — Tween-based UI transitions and juicing.
119- `godot-signals-groups` — connecting UI events to game logic.
120- `input-systems` — rebindable input and multi-device focus.
121- `card-game` / `visual-novel` — UI-heavy genre templates.