Theme Quickstart
Themes control how a Shuuka profile is presented. A custom theme takes full visual ownership over the page layout, backgrounds, fonts, link icons, and app card appearance.
Why Build a Theme?
While Shuuka ships with built-in themes, a custom SDK theme lets you:
- Encode a precise design system (brand-specific aesthetic, dark glass, minimalist, editorial, etc.)
- Control every aspect of profile layout — header, bio, links, apps, wallpaper
- Define card container constraints for all installed apps
- Guarantee visual consistency across the entire profile ecosystem
Color Schemes
Every theme must declare whether it is fundamentally dark or light via meta.color_scheme in theme.config.json.
When this flag is set, the platform automatically tags every installed app iframe with <html class="dark"> (or removes it for light themes). Apps that support the .dark class will adapt their contrast, text, and backgrounds automatically. One setting, full ecosystem adaptation.
Theme Bundle Structure
my-theme.zip
├── manifest.json ← marketplace metadata
├── theme.config.json ← runtime contract
├── index.html ← theme HTML entry
├── icon.svg ← theme icon (marketplace)
├── thumbnail.jpg ← marketplace preview image
├── header.jpg ← marketplace header image (1000×263)
└── dist/
├── theme.css ← compiled styles
└── index.js ← compiled theme runtime
Required Files
| File | Purpose |
|---|---|
manifest.json | Marketplace metadata (name, version, privacy) |
theme.config.json | Runtime contract (type, entry, card defaults) |
icon.svg | Theme icon |
thumbnail.jpg | Marketplace preview image |
header.jpg | Marketplace header image (exactly 1000×263) |
index.html or runtime entry | Theme shell |
Runtime Types
| Type | When to use |
|---|---|
html | HTML-first themes with [[template]] placeholders and a minimal JS runtime |
react | Component-based themes with a full JS/React entry |
Most themes start as html type. The HTML template handles layout and slotting of platform content; a small src/index.js handles CSS var injection and postMessage handling.
Minimal Working HTML Theme
This is the smallest possible working theme. Copy it, run it through your build tool, and upload.
index.html
<!DOCTYPE html>
<html lang="[[user.locale]]">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
[[layout.inline_styles]]
<link rel="stylesheet" href="./dist/theme.css">
</head>
<body>
<article class="shk-profile [[layout.article_class]]">
<header class="profile-header">
[[IF settings.show_avatar]]
<img class="avatar" src="[[user.avatar_url]]" alt="[[user.name]]" loading="lazy">
[[/IF]]
[[IF settings.show_title]]
<h1 class="profile-name">[[user.name]]</h1>
[[/IF]]
[[IF settings.show_nickname]]
<p class="profile-handle">@[[user.nickname]]</p>
[[/IF]]
[[IF settings.show_description]]
<p class="profile-bio">[[user.description]]</p>
[[/IF]]
</header>
[[IF settings.show_links]]
<nav class="links-section">
[[links.markup]]
</nav>
[[/IF]]
[[IF settings.show_apps]]
<section class="apps-section">
[[apps.rows_markup]]
</section>
[[/IF]]
</article>
[[layout.after_markup]]
[[layout.outside_markup]]
<script src="./dist/index.js"></script>
</body>
</html>
src/index.js (minimal)
import { createThemeBridge } from '@shuuka';
createThemeBridge({
card: {
backgroundColor: 'rgba(255, 255, 255, 0.06)',
borderColor: 'rgba(255, 255, 255, 0.12)',
borderStyle: 'solid',
borderWidth: '1px',
borderRadius: '16px',
boxShadow: '0 4px 24px rgba(0,0,0,0.20)',
},
backgroundFallback: '#0a0a0f'
});
// Optional: Hot-reload link icons when editing in the dashboard
window.addEventListener('message', (event) => {
const snap = event.data?.snapshot;
const cfg = snap?.theme?.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');
});
src/theme.scss (minimal)
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: var(--wallpaper-color, #0a0a0f);
color: #ffffff;
min-height: 100vh;
}
.shk-profile {
max-width: 560px;
margin: 0 auto;
padding: 40px 16px 80px;
display: flex;
flex-direction: column;
gap: 24px;
}
.profile-header {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
text-align: center;
}
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
}
.profile-name {
font-size: 1.25rem;
font-weight: 700;
margin: 0;
}
.profile-handle {
font-size: 0.9rem;
opacity: 0.6;
margin: 0;
}
.profile-bio {
font-size: 0.875rem;
opacity: 0.75;
margin: 0;
line-height: 1.5;
}
// App card — visual surface on the wrapper, NOT the iframe container
.shk-theme__app-card {
background: var(--app-card-bg, rgba(255, 255, 255, 0.06));
border: 1px solid var(--app-card-border-color, rgba(255, 255, 255, 0.10));
border-radius: var(--app-card-radius, 16px);
box-shadow: var(--app-card-shadow, 0 4px 24px rgba(0, 0, 0, 0.20));
overflow: visible;
transition: transform 0.28s ease, box-shadow 0.35s ease;
}
// Clip container — sizing and overflow clipping ONLY
.shk-theme__app-card .billboard-app-container {
background: transparent;
border: none;
box-shadow: none !important;
border-radius: var(--app-card-radius, 16px);
overflow: hidden !important;
}
Runtime Lifecycle
- Shuuka loads the theme entry file (
index.html). - The platform replaces all
[[...]]placeholders with live user data server-side. - The page renders with static profile data immediately (no JavaScript required for initial paint).
- Your
src/index.jsruntime loads and connects to the platform viapostMessage. - The
shuuka:contextSnapshotorshuuka:ctxmessage fires with the full live config. - Your runtime calls
applyIframeCssVars()andapplyLinksCssVars()to sync CSS vars. - Live preview changes from the dashboard re-fire the context message — your runtime updates CSS vars and rebroadcasts the card style.
Settings merge order
platform defaults → theme defaults → user overrides
Testing Your Theme Locally
Before uploading, use the SDK theme dev preview to test against a real profile:
- Register your local dev server at My Apps → Developer Mode.
- Note the numeric project ID shown.
- Open any profile URL with
?dev_theme={id}, e.g.https://shuuka.com/username?dev_theme=2. - The platform proxies your
index.htmlwith live user data. Only you see the change.
Read Theme Dev Preview for full dev preview requirements and path resolution rules.
Recommended Reading Order
- Theme Manifest — full
theme.config.jsonreference - Theme Platform Values — all
[[...]]template variables and CSS custom properties - HTML Theme Guide — step-by-step walkthrough with CSS architecture
- Theme App-Card Integration — card-style and button-style broadcast protocol
- Theme Dev Preview — dev preview path resolution and SCSS fallback rules