Skip to main content

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

FilePurpose
manifest.jsonMarketplace metadata (name, version, privacy)
theme.config.jsonRuntime contract (type, entry, card defaults)
icon.svgTheme icon
thumbnail.jpgMarketplace preview image
header.jpgMarketplace header image (exactly 1000×263)
index.html or runtime entryTheme shell

Runtime Types

TypeWhen to use
htmlHTML-first themes with [[template]] placeholders and a minimal JS runtime
reactComponent-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

  1. Shuuka loads the theme entry file (index.html).
  2. The platform replaces all [[...]] placeholders with live user data server-side.
  3. The page renders with static profile data immediately (no JavaScript required for initial paint).
  4. Your src/index.js runtime loads and connects to the platform via postMessage.
  5. The shuuka:contextSnapshot or shuuka:ctx message fires with the full live config.
  6. Your runtime calls applyIframeCssVars() and applyLinksCssVars() to sync CSS vars.
  7. 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:

  1. Register your local dev server at My Apps → Developer Mode.
  2. Note the numeric project ID shown.
  3. Open any profile URL with ?dev_theme={id}, e.g. https://shuuka.com/username?dev_theme=2.
  4. The platform proxies your index.html with live user data. Only you see the change.

Read Theme Dev Preview for full dev preview requirements and path resolution rules.


  1. Theme Manifest — full theme.config.json reference
  2. Theme Platform Values — all [[...]] template variables and CSS custom properties
  3. HTML Theme Guide — step-by-step walkthrough with CSS architecture
  4. Theme App-Card Integration — card-style and button-style broadcast protocol
  5. Theme Dev Preview — dev preview path resolution and SCSS fallback rules