# 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` / `dark` themes that map *roles* (`color_bg`, `color_primary`, `color_text`, status colors…) onto palette primitives. Widgets read everything through `mod.streem_theme.s_themes.*`. * **Localization — `streem_i18n/`** * `mod.rs` — `en` / `sw` string tables; the active language is `mod.streem_i18n.s_i18n` (same swap pattern as themes). * **Host — `src/main.rs`** * Tracks the selected theme/language indices, points `s_themes` / `s_i18n` at the selection, reloads the script tree, and drives a live status label. 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 `--release` > for 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/ / 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) ```bash cargo check # type-check cargo test # runs the unit tests in src/main.rs cargo build --release ``` ### Running the UI (via Studio) 1. Start the Studio remote bridge once. 2. Add `theming` to the Cargo workspace and `makepad.splash` so Studio exposes it as a runnable item. 3. Launch it with `RunItem` (the runnable item name shown in Studio). 4. After editing UI/runtime code, `ClearBuild` the old build and re-run before trusting screenshots or widget dumps. --- ## What changed vs. the original See [`CHANGES.md`](./CHANGES.md) for the full list. Highlights: * Introduced the 3-tier token system and an app-owned `mod.streem_design.*` namespace (was `mod.widgets.*`, which collides conceptually with the framework's own widget namespace). * Themes now reference palette primitives instead of inlining hex. * Fixed the unused `color_text` role 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::LiveEdit` so a hot-reload re-applies the active selection instead of silently reverting to script defaults.