4.9 KiB
theming
A small Makepad example that demonstrates runtime theme and language
swapping using the new script_mod! / Splash DSL (Makepad 2.0).
Click Toggle theme to cycle light/dark; click Language to cycle English/Kiswahili. The whole UI re-skins and re-localizes at runtime by swapping the active design token set and reloading the script tree — no per-widget edits required.
Token architecture (3 tiers)
This example follows a layered design-token system so that a single change propagates predictably across the whole UI:
Tier 1 — Primitives Tier 2 — Semantic Tier 3 — Widgets
(raw values, no meaning) (roles bound per theme) (read active theme)
mod.streem_design.palette → mod.streem_theme.light → draw_bg.color: color_bg
mod.streem_design.shapes → mod.streem_theme.dark ↘ draw_text.text_style: h1
mod.streem_design.typography mod.streem_theme.s_themes ← (the active theme)
- Tier 1 —
streem_design/palette.rs— the only place raw hex colors live (amber_600,gray_900…).shapes.rs— radii, control heights, a spacing scale, border widths.typography.rs— named text styles (h1,body1,overline…).
- Tier 2 —
streem_theme/mod.rs—light/darkthemes that map roles (color_bg,color_primary,color_text, status colors…) onto palette primitives. Widgets read everything throughmod.streem_theme.s_themes.*.
- Localization —
streem_i18n/mod.rs—en/swstring tables; the active language ismod.streem_i18n.s_i18n(same swap pattern as themes).
- Host —
src/main.rs- Tracks the selected theme/language indices, points
s_themes/s_i18nat the selection, reloads the script tree, and drives a live status label.
- Tracks the selected theme/language indices, points
Why this matters: because semantic themes reference palette tokens, you can
re-brand the entire app by editing one entry in palette.rs. Because widgets
only ever read s_themes/s_i18n, the host can re-skin/re-localize the tree
generically.
Project layout
theming/
├── Cargo.toml
└── src/
├── main.rs # host App: toggles + reload path
├── streem_design/
│ ├── mod.rs # registers tier-1 modules
│ ├── palette.rs # tier 1: raw colors
│ ├── shapes.rs # tier 1: radii / spacing / sizes
│ └── typography.rs # tier 1: text styles
├── streem_theme/
│ └── mod.rs # tier 2: semantic light/dark themes
└── streem_i18n/
└── mod.rs # localized strings (en/sw)
Building & running
Studio-first workflow. Per the project runbook, UI programs should be launched and inspected through the Makepad Studio remote protocol via a runnable item, not via raw
cargo run. Use the shell only for non-UI tasks (cargo check,cargo test, file/search ops), and prefer--releasefor any performance-sensitive run.
Dependency setup
Cargo.toml uses a local path dependency on makepad-widgets, which
assumes this crate sits as a sibling of your Makepad checkout:
makepad/ # your makepad checkout (the new DSL lives on `dev`)
widgets/
<projects>/
theming/ # this crate → path = "../../../../makepad/widgets"
Adjust the number of ../ segments in Cargo.toml to match where you place
this crate. If you are not inside a Makepad checkout, uncomment the git
dependency in Cargo.toml instead (pinned to branch = "dev").
Non-UI checks (shell is fine)
cargo check # type-check
cargo test # runs the unit tests in src/main.rs
cargo build --release
Running the UI (via Studio)
- Start the Studio remote bridge once.
- Add
themingto the Cargo workspace andmakepad.splashso Studio exposes it as a runnable item. - Launch it with
RunItem(the runnable item name shown in Studio). - After editing UI/runtime code,
ClearBuildthe old build and re-run before trusting screenshots or widget dumps.
What changed vs. the original
See CHANGES.md for the full list. Highlights:
- Introduced the 3-tier token system and an app-owned
mod.streem_design.*namespace (wasmod.widgets.*, which collides conceptually with the framework's own widget namespace). - Themes now reference palette primitives instead of inlining hex.
- Fixed the unused
color_textrole and removed dead/duplicated token files. - Status text is now driven entirely by live Rust state (no baked-in "light | reapply: 0" string in the DSL).
- Added a generic, index-based theme/language cycle (replacing the rigid 2-state enum) plus runtime language switching wired through one reload path.
- Hardened
Event::LiveEditso a hot-reload re-applies the active selection instead of silently reverting to script defaults.