Skip to main content

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

  1. Your theme.config.json sets "supportsThemeSettings": true.
  2. You declare the available settings in defaults and overridableKeys.
  3. The platform renders the settings panel in the profile owner's dashboard.
  4. When the owner changes a setting, the platform fires a shuuka:ctx postMessage with the updated config.
  5. Your src/index.js applies the new values as CSS custom properties and rebroadcasts the card style.

theme.config.json Settings Fields

FieldPurpose
supportsThemeSettingsEnable the settings panel in the dashboard
defaultsInitial values for every configurable key
overridableKeysWhich 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);
}

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:

  1. All visual values must be controlled via CSS custom properties (not hardcoded in class names).
  2. Your message handler must call broadcastCardStyle() after updating resolvedCardStyle so open app iframes receive the updated style immediately.
  3. SCSS fallback values must match your defaults exactly so initial render before the first shuuka:ctx message 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

FileWhat to configure
theme.config.jsonsupportsThemeSettings: true, defaults, overridableKeys
src/index.jsapplyIframeCssVars(), applyLinksCssVars(), applyBackgroundVars(), applyLayoutVars()
src/theme.scssAll CSS custom properties with var(--name, fallback)