16 KiB
Sandbox theme reference
res://sandbox_theme.json is the hand-editable presentation configuration for
the Sandbox Stage (res://scenes/stage/sandbox_stage.tscn). It is loaded by
SandboxStage during _ready(), before the code-built UI is created. The
loader is res://scripts/stage/sandbox_theme.gd (class_name SandboxTheme).
The committed file uses schema "2.0". The theme controls presentation
defaults only; it does not save a scene, placement, or action data.
Load, merge, and validation rules
The load path is:
res://sandbox_theme.json
│
▼
SandboxTheme.DEFAULT_CONFIG
│ deep-merge file values
▼
SandboxStage._build_ui() and visual overlays
- Missing, unreadable, malformed, or non-object JSON uses the built-in default configuration and emits one warning. The stage continues to run.
- Missing nested keys inherit the built-in value. Unknown keys are retained in the raw dictionary but have no effect until a consumer is added.
stylebox(component)resolves styles in this order:global→<section>.style_defaults→ the component object. For example,paletteis resolved fromglobal, thenpalette.style_defaults, thenpalette.- Colors must be strings in
#RRGGBBor#RRGGBBAAform. Invalid colors use the requested fallback and emit a warning. - Numeric accessors accept finite integers/floats and clamp them to the range requested by the consumer. Invalid or non-finite numbers use that consumer's fallback. Size arrays are accepted when they contain two numeric values; individual controls apply their own minimum sizes.
- Font paths are
res://resource paths. A missing resource, or a resource that is not aFont, usesThemeDB.fallback_fontand emits a warning. - The theme is loaded once per Sandbox Stage instance. Restart the scene (F6) to see edits; there is no live file watcher or reload button.
The live grid size, snap toggle, and grid visibility are stored in
user://sandbox_settings.json. grid.snap_size supplies the initial grid-size
default; it does not overwrite a value already saved in user settings.
Schema overview
Every current top-level block is listed below. The values shown are the
defaults in the committed sandbox_theme.json (not necessarily the fallback
used by an older caller).
Current consumer status
The schema intentionally includes forward-looking values for the reusable widget and speech work. The current Sandbox runtime applies these groups:
globalstyle values used by resolved styleboxes and the selector control theme (background/border/radius/padding);global.text_colorandglobal.muted_text_colorare available presentation values but are not applied to every existing label automatically.fonts.ui_font,emoji_font, bold/italic variants, the nestedsizesentries for action popups, tooltips, status pills, queue/rule/action editors, panel rows/titles,file_menu,file_menu_popup, andmode_switcher, plus the relatedflags.mode_colors.edit,direct,play, andguide;grid.snap_sizeas the first-run grid default; dialog sizes/fonts;chrome.mode_switcherwidth; palette width/card size/drag threshold/style; selector cell and thumbnail sizes; and alloverlaysvalues.
These declared values currently remain reserved or require a legacy adapter:
chrome.top_bar.height,status_bar.height,mode_frame,mode_badge, andtooltipdescribe the intended chrome contract, but the corresponding layout/style constants are still authored inSandboxStage.popups.action_menu.font_sizeandpopups.asset_selector.backdrop_alphaare reserved component overrides. The active action-menu size comes fromfonts.sizes.action_popup; selectorcell_sizeandthumbnail_sizeare active.palette.icon_sizeandpalette.selected_border_widthare reserved for the palette card renderer; the current card has its own glyph/border values.fonts.sizes.assignment_badge,rule_label,asset_label, andasset_badgeare part of the resolved schema, but the current direct call intoStageDirectorVisualsstill reads the legacy flat badge/label names. Use the legacy names documented below when changing those director visuals.fonts.presetsand everywidgetslist are exposed through reusable controls, but the current Sandbox Speak action does not instantiate those controls yet. They are future speech-customization data.
version
| Key | Format | Current value | Notes |
|---|---|---|---|
version |
string | "2.0" |
Use "2.0" when using the nested schema below. |
global
Global style values are inherited by SandboxTheme.stylebox() and the default
control theme.
| Key | Format | Default | Purpose |
|---|---|---|---|
font_preset |
string | "ui" |
Named base-font selection for future/general UI use. |
font_size |
integer | 18 |
Default Control font size. |
text_color, muted_text_color |
color string | #F5F7FA, #AAB2C0 |
Primary and secondary text colors. |
bg_color, surface_color |
color string | #171B24F2, #252B38FA |
Global and raised-surface backgrounds. |
border_color |
color string | #FFFFFF33 |
Default border color. |
border_width |
integer | 1 |
Default border width. |
corner_radius |
integer | 10 |
Default corner radius. |
padding, spacing |
integer | 8, 8 |
Default style padding and layout spacing. |
fonts
Font paths may be empty ("") to use the engine fallback. presets is an
object of arbitrary preset IDs; each entry has a display label and a path.
The committed file provides ui, comic, pixel, and scary entries.
| Key | Format | Default | Consumer |
|---|---|---|---|
ui_font, emoji_font |
res:// path or empty |
"" |
Base UI and emoji-capable stage/director controls. |
ui_font_bold, ui_font_italic |
res:// path or empty |
"" |
Optional style variants; bold can be generated from the base font when no dedicated path is supplied. |
presets |
object {id:{label,path}} |
see JSON | font_presets() and future font selectors. |
sizes.action_popup |
integer | 24 |
Director/action popup text. |
sizes.action_popup_emoji |
integer | 22 |
Effective popup size when a popup font is active (Godot popup menus have one font size). |
sizes.assignment_badge |
integer | 20 |
Reserved resolved-theme value; current director visuals use legacy fonts.assignment_badge_font_size. |
sizes.rule_label |
integer | 18 |
Reserved resolved-theme value; current director visuals use legacy fonts.rule_label_font_size. |
sizes.status_pill |
integer | 18 |
Mode badge/pill. |
sizes.tooltip |
integer | 18 |
Cursor-attached targeting tooltip. |
sizes.file_menu |
integer | 22 |
File button in the top bar. |
sizes.file_menu_popup |
integer | 26 |
File menu and Open Recent popup rows. |
sizes.mode_switcher |
integer | 24 |
Edit/Direct/Play buttons. |
sizes.queue_panel, sizes.rule_panel |
integer | 22, 22 |
Queue and rule editor panels. |
sizes.action_editor, sizes.rule_editor |
integer | 22, 22 |
Action and rule editor controls. |
sizes.panel_row, sizes.panel_title |
integer | 20, 24 |
Editor row text and panel titles. |
sizes.asset_label, sizes.asset_badge |
integer | 18, 14 |
Reserved asset-card text sizing. |
flags.panel_title_bold |
boolean | true |
Bold queue/rule panel titles. |
flags.rule_label_bold |
boolean | false |
Bold director rule labels. |
flags.badge_bold |
boolean | true |
Bold director badges. |
flags.action_popup_bold, flags.action_popup_italic |
boolean | false, false |
Popup style flags. |
mode_colors
Each value is a color string. These accents are used by the mode badge/frame, active mode controls, targeting feedback, and related stage chrome.
| Key | Default | Purpose |
|---|---|---|
edit |
#22C6FF |
Edit-mode accent. |
direct |
#FFB300 |
Direct-mode accent. |
play |
#33DD77 |
Play-mode accent. |
guide |
#22C6FF |
Terrain painting guide line. |
grid
| Key | Format | Default | Purpose |
|---|---|---|---|
snap_size |
finite number | 15.0 |
Initial grid size, clamped to the stage's supported range. |
chrome
style_defaults is the optional common style object for chrome components. Its
style keys follow the global style formats (bg_color, border_color,
border_width, corner_radius, padding).
| Key | Format | Default | Purpose |
|---|---|---|---|
style_defaults |
style object | { "bg_color":"#171B24F2", "corner_radius":10 } |
Shared chrome style base. |
top_bar.height |
number | 64 |
Top navigation bar height. |
status_bar.height |
number | 38 |
Bottom status bar height. |
mode_switcher.segment_min_width |
number | 124 |
Minimum width of each Edit/Direct/Play segment. |
mode_frame.border_width |
integer | 3 |
Colored stage frame width. |
mode_badge.corner_radius |
integer | 12 |
Mode badge corner radius. |
mode_badge.padding |
number | 10 |
Mode badge padding. |
tooltip.bg_color |
color | #101010E6 |
Targeting tooltip background. |
tooltip.border_width |
integer | 1 |
Targeting tooltip border. |
dialogs.queue_size |
[width,height] |
[680,520] |
Queue editor window minimum size. |
dialogs.rule_size |
[width,height] |
[720,560] |
Rule editor window minimum size. |
dialogs.action_size |
[width,height] |
[560,390] |
Action editor window minimum size. |
dialogs.confirm_size |
[width,height] |
[620,300] |
Rename, unsaved, missing-assets, and other confirmation/error windows. |
dialogs.font_size |
integer | 22 |
Dialog controls/buttons. |
dialogs.message_font_size |
integer | 26 |
Centered AcceptDialog message text. |
popups
style_defaults accepts the normal style keys and is inherited by popup
components.
| Key | Format | Default | Purpose |
|---|---|---|---|
style_defaults |
style object | bg #202633FA, border #FFFFFF33, radius 12, padding 12 |
Common popup surface. |
action_menu.font_size |
integer | 24 |
Reserved action-menu component override; use fonts.sizes.action_popup for the active menu. |
dialog |
object | {} |
Reserved per-dialog popup overrides. |
asset_selector.backdrop_alpha |
finite number | 0.55 |
Reserved selector backdrop opacity. |
asset_selector.cell_size |
[width,height] |
[190,220] |
Prop/stickman selector cell size. |
asset_selector.thumbnail_size |
[width,height] |
[156,156] |
Prop/stickman selector thumbnail size. |
palette
style_defaults uses normal style keys and styles the stage placement palette.
| Key | Format | Default | Purpose |
|---|---|---|---|
style_defaults |
style object | bg #252B38FA, radius 10, padding 6 |
Palette surface/card base. |
dock_width |
number | 116 |
Palette width in Edit mode and stage-frame inset. |
card_size |
[width,height] |
[96,94] |
Placement-card minimum size; the card keeps room for its label. |
icon_size |
number | 30 |
Reserved palette icon/glyph size. |
drag_threshold |
number | 8 |
Pointer distance before a card becomes a drag. |
selected_border_width |
integer | 3 |
Reserved selected palette card border. |
overlays
style_defaults controls dashed overlay geometry in world pixels; colors are
RGBA strings.
| Key | Format | Default | Purpose |
|---|---|---|---|
style_defaults.line_width |
number | 2 |
Dashed trajectory/guide width. |
style_defaults.dash_length |
number | 8 |
Dash length. |
style_defaults.dash_gap |
number | 5 |
Gap length. |
terrain_guide.color |
color | #22C6FF |
Terrain paint guide. |
valid.color |
color | #4DE66680 |
Valid director target/placement feedback. |
same_type.color |
color | #FFFFFF26 |
Same-type placement feedback. |
invalid.color |
color | #FF4D4D80 |
Invalid target/placement feedback. |
widgets
These lists feed reusable controls (EmojiQuickGrid, FontPresetPicker, and
PresetSizeToggle). They are data sources, not speech-action persistence.
| Key | Format | Default | Purpose |
|---|---|---|---|
swatches |
array of color strings | white, black, cyan, amber, green, red | Color selector entries. |
size_presets |
array of {id,label,value} objects |
Small/Medium/HUGE (20/28/40) |
Size selector entries; value is an integer-like size. |
emoji |
array of strings | eight emoji entries | Emoji quick-grid entries. |
Practical edits
Make the top navigation easier to read:
{
"fonts": {
"sizes": {
"file_menu": 26,
"file_menu_popup": 30,
"mode_switcher": 28
}
}
}
Change the mode frame and make the stage colors more distinct:
{
"mode_colors": {
"edit": "#4DD9FF",
"direct": "#FF8A3D",
"play": "#65E68A",
"guide": "#4DD9FF"
},
"chrome": { "mode_frame": { "border_width": 4 } }
}
Give selectors larger cards and thumbnails:
{
"popups": {
"asset_selector": {
"cell_size": [220, 260],
"thumbnail_size": [190, 190]
}
}
}
Make kid-facing editors and confirmations larger:
{
"chrome": {
"dialogs": {
"queue_size": [760, 600],
"rule_size": [800, 640],
"font_size": 26,
"message_font_size": 30
}
},
"fonts": {
"sizes": {
"queue_panel": 24,
"rule_panel": 24,
"action_editor": 24,
"rule_editor": 24,
"panel_row": 22,
"panel_title": 28
}
}
}
Legacy v1 files
Older Phase 4b/3c theme files used flat keys such as
fonts.action_popup_font_size, fonts.queue_panel_font_size,
fonts.panel_title_font_size, and mode keys such as
mode_colors.edit_accent and mode_colors.guide_line. They remain accepted.
The loader aliases the legacy mode names and selected flat font-size names into
the nested structure, while SandboxStage retains the old precedence for a
file that does not declare version "2.0".
The complete legacy names still read by the current runtime are:
fonts.action_popup_font_size fonts.action_popup_emoji_size
fonts.assignment_badge_font_size fonts.assignment_badge_radius
fonts.rule_label_font_size fonts.status_pill_font_size
fonts.tooltip_font_size fonts.queue_panel_font_size
fonts.rule_panel_font_size fonts.action_editor_font_size
fonts.rule_editor_font_size fonts.panel_row_font_size
fonts.panel_title_font_size fonts.panel_title_bold
fonts.rule_label_bold fonts.badge_bold
fonts.action_popup = { size, bold, italic }
mode_colors.edit_accent mode_colors.direct_accent
mode_colors.play_accent mode_colors.guide_line
For a migration, copy the values into the nested keys in this document and set
"version": "2.0". Keep only one spelling of each setting so the result is
easy to maintain.
Fonts, emoji, and troubleshooting
- Use project-relative paths such as
res://fonts/kids_font.ttf; do not use a machine-specific absolute path. - If text falls back to the Godot default, check the path, import status, and
that the resource is actually a
Font/font file. Restart the Sandbox after correcting it. - Emoji availability depends on the selected font and platform glyph support.
A missing
emoji_fontis non-fatal; the normal UI fallback remains usable. - If a color or number is ignored, check JSON commas/quotes, use a supported color form, and keep numbers finite. The warning identifies the affected key.
- If a selector or dialog looks cramped, increase its size array and its related font size together; restart F6 to reload the file.
Speech customization status
fonts.presets (ui, comic, pixel, scary), widgets.size_presets,
widgets.swatches, and widgets.emoji are the reusable data contract for a
future speech-customization UI. They are intentionally available now so a
future Speak dialog can consume the same theme.
The current Sandbox speech action still stores and renders text through the
existing StickmanSpeechBubble; these theme lists do not yet change saved
speech actions, bubble shape, bubble colors, or bubble font at runtime. Do not
assume that changing a preset changes an existing speech action. Treat
speech-specific fields under popups.dialog or future bubble sections as
reserved until that feature is implemented.