Theme Settings
Themes can expose a settings panel to profile owners so they can personalize background, layout, and card appearance without touching theme code.
How Theme Settings Work
- Your
theme.config.jsonsets"supportsThemeSettings": true. - You declare the available settings in
defaultsandoverridableKeys. - The platform renders the settings panel in the profile owner's dashboard.
- When the owner changes a setting, the platform fires a
shuuka:ctxpostMessage with the updated config. - Your
src/index.jsapplies the new values as CSS custom properties and rebroadcasts the card style.
theme.config.json Settings Fields
| Field | Purpose |
|---|---|
supportsThemeSettings | Enable the settings panel in the dashboard |
defaults | Initial values for every configurable key |
overridableKeys | Which keys the profile owner can change |
Example
{
"supportsThemeSettings": true,
"defaults": {
"background": {
"mode": "solid",
"color": "#0a0a0f",
"gradient": "linear-gradient(135deg, #0a0a0f 0%, #1a0a2e 100%)"
},
"layout": {
"structure": "centered",
"max_width": 560
}
},
"overridableKeys": [
"background.color",
"background.gradient",
"background.mode",
"layout.structure",
"layout.max_width"
]
}
Settings Merge Order
platform defaults → theme defaults → user overrides
defaults defines how your theme looks out-of-the-box.
overridableKeys defines what the profile owner is allowed to change.
A key that is not in overridableKeys cannot be changed by the owner even if it exists in defaults.
Reading Settings Changes in src/index.js
You don't have to manually parse shuuka:ctx messages or write your own update loop. The platform SDK's createThemeBridge() automatically intercepts live dashboard changes, converts the visual settings into CSS custom properties on your :root, and instantly broadcasts them to all open app iframes.
import { createThemeBridge } from '@shuuka';
createThemeBridge({
card: { ... },
// Opts your theme into automated background and layout variable injection
backgroundFallback: '#0a0a0f'
});
When you pass backgroundFallback, the SDK bridge automatically understands background and layout entries inside the user's theme.config.json and exposes these CSS variables on your page:
Injected Layout CSS Variables
--shk-wallpaper-background: The fully resolved background (either a solid color or a CSS gradient string from the dashboard settings).--theme-accent: The dashboard-configured layout accent color.--theme-text: The dashboard-configured primary text color.--theme-muted: The dashboard-configured secondary text color.
SCSS Application
You only need to consume those variables in your theme.scss:
.shk-wallpaper {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
// The SDK bridge handles solid vs gradient resolution automatically
background: var(--shk-wallpaper-background, #0a0a0f);
}
.shk-profile {
color: var(--theme-text, #ffffff);
}
Hot-Reloading Link Icon Sizing (Optional)
createThemeBridge covers everything about app containers, background layers, and typography. If you want the social icons to visually hot-reload inside the live dashboard preview immediately as the user drags the spacing slider, add a simple message fallback for the links configuration:
window.addEventListener('message', (event) => {
const cfg = event.data?.context?.config || event.data?.ctx?.config;
if (!cfg?.links) return;
const root = document.documentElement;
if (cfg.links.item_size !== undefined) root.style.setProperty('--links-icon-size', cfg.links.item_size + 'px');
if (cfg.links.spacing !== undefined) root.style.setProperty('--links-spacing', cfg.links.spacing + 'px');
});
Live Preview in the Dashboard
The settings panel in the dashboard sends shuuka:ctx messages in real time as the owner drags sliders or picks colors. Your applyIframeCssVars, applyBackgroundVars, and applyLinksCssVars functions must update CSS properties immediately without a page reload.
For the live preview to work correctly:
- All visual values must be controlled via CSS custom properties (not hardcoded in class names).
- Your message handler must call
broadcastCardStyle()after updatingresolvedCardStyleso open app iframes receive the updated style immediately. - SCSS fallback values must match your
defaultsexactly so initial render before the firstshuuka:ctxmessage looks correct.
SCSS Fallback Rule
All CSS custom properties in your SCSS must have fallback values that match your theme.config.json defaults:
// ✅ Correct — fallback matches theme.config.json default
.shk-theme__app-card {
border-radius: var(--app-card-radius, 16px); // matches "border_radius": 16 in config.iframe
}
// ❌ Wrong — no fallback, breaks during Vite dev proxy when CSS vars aren't injected
.shk-theme__app-card {
border-radius: var(--app-card-radius);
}
In the Vite dev proxy, your src/index.js does not load (because the /src/ path 404s on the proxy server). Only the built dist/index.js loads. SCSS fallbacks are the only styling during this phase.
Summary
| File | What to configure |
|---|---|
theme.config.json | supportsThemeSettings: true, defaults, overridableKeys |
src/index.js | applyIframeCssVars(), applyLinksCssVars(), applyBackgroundVars(), applyLayoutVars() |
src/theme.scss | All CSS custom properties with var(--name, fallback) |