Theme Manifest
Themes require two files: manifest.json for marketplace metadata and theme.config.json for the runtime contract.
manifest.json
This file is read at install time by the marketplace.
Required Fields
| Field | Required | Purpose |
|---|---|---|
name | Yes | Theme name shown in the marketplace |
version | Yes | Semver version string, e.g. 1.0.0 |
entry | Yes | Runtime entry file inside the bundle |
type | Recommended | Set to "theme" |
privacy | Strongly recommended | Third-party and consent declaration |
Full Example
{
"name": "My Theme",
"version": "1.0.0",
"entry": "index.html",
"type": "theme",
"privacy": {
"collects_pii": false,
"collects_visitor_data": false,
"third_party_transfers": [],
"third_party_embeds": []
}
}
If your theme loads external fonts, analytics, or any third-party resource, declare those origins in the privacy block exactly as you would for an app. Read App Manifest — privacy for the full privacy field reference.
theme.config.json
This is the runtime contract Shuuka reads to configure the theme at load time and in the live preview.
Top-Level Fields
| Field | Required | Purpose |
|---|---|---|
type | Yes | Runtime type: "html" or "react" |
entry | Yes | Runtime entry file path inside the bundle |
mountSelector | No | DOM selector Shuuka mounts into. Default #app |
supportsThemeSettings | No | true if the theme exposes dashboard settings |
meta | Strongly recommended | Theme metadata object |
meta.name | Recommended | Theme display name |
meta.version | Recommended | Theme version |
meta.color_scheme | Strongly recommended | "dark" or "light" |
config | Strongly recommended | Card and link visual defaults |
config.iframe | Strongly recommended | App card visual defaults |
config.links | Strongly recommended | Link icon grid defaults |
defaults | No | Theme-owned default config values |
overridableKeys | No | Keys the profile owner may change from the dashboard |
html | No | HTML-only runtime options |
html.placeholders | No | Named placeholder definitions for HTML helpers |
react | No | React-only runtime options |
react.hydrateSelector | No | React hydration selector |
meta
{
"meta": {
"name": "My Theme",
"version": "1.0.0",
"color_scheme": "dark"
}
}
color_scheme propagates to every installed app iframe. When set to "dark", the platform adds <html class="dark"> to each app document. Apps that respect the .dark class will render correctly in your dark theme.
If color_scheme is missing, all app iframes default to light mode regardless of how dark your theme background is.
config.iframe
Controls the default visual appearance of app cards on the profile.
| Key | Type | Description |
|---|---|---|
background | string | CSS background value, e.g. "rgba(0,0,0,0.5)" or "transparent" |
border_radius | number | Border radius in pixels, e.g. 16 |
shadow | string | Shadow preset key ("none", "sm", "md", "lg", "xl") or CSS shadow value |
border_style | string | CSS border style: "solid", "dashed", "none" |
border_width | number | Border width in pixels, e.g. 1 |
border_color | string | CSS color for the border, e.g. "rgba(255,255,255,0.12)" |
padding | number | Inner padding in pixels, e.g. 0 |
text_color | string | Fallback text color inside app cards |
Why this matters:
When a theme is loaded in dev preview mode (?dev_theme={id}), the platform replaces config.iframe in the merged payload with the dev theme's own iframe section. Without this section, no inline card CSS variables are emitted and only your SCSS fallbacks apply.
Example:
{
"config": {
"iframe": {
"background": "rgba(10, 10, 20, 0.70)",
"border_radius": 18,
"shadow": "lg",
"border_style": "solid",
"border_width": 1,
"border_color": "rgba(255, 255, 255, 0.10)",
"padding": 0,
"text_color": "#ffffff"
}
}
}
config.links
Controls the default appearance of the social link icon grid.
| Key | Type | Description |
|---|---|---|
visible | boolean | Whether the links section is visible by default |
alignment | string | Grid alignment: "center", "left", "right" |
wrap | boolean | Whether icons wrap to multiple rows |
item_size | number | Icon size in pixels, e.g. 48 |
spacing | number | Gap between icons in pixels, e.g. 8 |
Example:
{
"config": {
"links": {
"visible": true,
"alignment": "center",
"wrap": true,
"item_size": 52,
"spacing": 10
}
}
}
defaults
defaults defines the theme's starting visual configuration. These values are merged as the theme layer in the settings resolution chain:
platform defaults → theme defaults → user overrides
The keys in defaults should mirror the settings schema the theme exposes.
Example:
{
"defaults": {
"background": {
"mode": "solid",
"color": "#0a0a0f"
},
"layout": {
"structure": "centered",
"max_width": 560
},
"header": {
"show_image": false
}
}
}
overridableKeys
overridableKeys lists the keys the profile owner is allowed to change from the theme settings panel. If a key is not listed here, the user cannot override it.
{
"overridableKeys": [
"background.color",
"background.mode",
"layout.structure"
]
}
html.placeholders
Optional. Defines named placeholder sections in the HTML template for use with the SDK render helpers.
{
"html": {
"placeholders": {
"header": "#theme-header",
"footer": "#theme-footer"
}
}
}
Runtime Rules
type | entry must be |
|---|---|
html | An .html file |
react | A .js, .mjs, .jsx, .ts, or .tsx file |
Complete theme.config.json Example
This is the minimum viable config for a properly working theme that supports dev preview and app-card broadcasting:
{
"type": "html",
"entry": "index.html",
"supportsThemeSettings": true,
"meta": {
"name": "My Theme",
"version": "1.0.0",
"color_scheme": "dark"
},
"config": {
"iframe": {
"background": "rgba(255, 255, 255, 0.05)",
"border_radius": 16,
"shadow": "md",
"border_style": "solid",
"border_width": 1,
"border_color": "rgba(255, 255, 255, 0.10)",
"padding": 0,
"text_color": "#ffffff"
},
"links": {
"visible": true,
"alignment": "center",
"wrap": true,
"item_size": 48,
"spacing": 8
}
},
"defaults": {
"background": {
"mode": "solid",
"color": "#0a0a0f"
}
},
"overridableKeys": [
"background.color",
"background.mode"
]
}
Settings Strategy
defaultsdefines the theme's intended look out-of-the-box.overridableKeysdefines what the profile owner can personalize.- Platform defaults cover any missing values.
Do not blur these layers. A theme that overrides everything in defaults but lists nothing in overridableKeys gives the user zero customization power. A theme that lists keys in overridableKeys but has no defaults risks missing values breaking the layout.
Next Step
Read Theme Platform Values for all template variables, Theme App-Card Integration for the card-style broadcast protocol, and HTML Theme Guide for a complete working example.