Skip to main content

Theme App-Card Integration

Themes are responsible for presenting installed apps cleanly.

Visual Ownership Rule

In custom SDK themes, visual styling belongs on the outer app card wrapper, not on the iframe container.

ElementResponsibility
.shk-theme__app-cardvisual surface: background, border, radius, shadow
.billboard-app-containerclip mask and sizing only
iframe bodyapp content only

Do / Don’t

DoDon’t
style .shk-theme__app-cardput shadows on .billboard-app-container
use --app-card-* variablesduplicate card chrome inside multiple layers
let the theme own cosmetic defaultsoverride every app visually by brute force

Why This Matters

If both the theme wrapper and the iframe container own shadows or backgrounds, users see a double-card effect. That breaks the visual system immediately.

Minimal Example

.shk-theme__app-card {
background: var(--app-card-bg, var(--shk-default-card-bg, #ffffff));
border-radius: var(--app-card-radius, 14px);
box-shadow: var(--app-card-shadow, 0 1px 4px rgba(0, 0, 0, 0.06));
overflow: visible;
}

.shk-theme__app-card .billboard-app-container {
background: transparent;
border: none;
box-shadow: none;
}

Broadcasting Your Card Style to App Iframes

Apps can opt into inheriting your card's exact visual style — background, border, radius, shadow, hover lift, etc. — by relying on the Platform SDK bridge.

The Shuuka SDK provides a createThemeBridge utility that completely handles the iframe postMessage protocol, live dashboard CSS variable syncing, and fallback resolution automatically.

Integrating the Theme Bridge

Your only responsibility in src/index.js is to provide your compiled SCSS defaults. This ensures apps always receive valid sizes and colours even before the dashboard makes live style modifications over the bridge.

import { createThemeBridge } from '@shuuka';

createThemeBridge({
card: {
backgroundColor: 'rgba(255, 255, 255, 0.06)', // compiled SCSS fallback
borderColor: 'rgba(255, 255, 255, 0.12)',
borderStyle: 'solid',
borderWidth: '1px',
borderRadius: '16px',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.18)',
backdropFilter: 'blur(12px)', // Arbitrary CSS works natively
},
btn: {
primary: {
backgroundColor: '#222222', // light mode default
color: '#ffffff',
borderRadius: '10px',
border: 'none',
},
secondary: {
backgroundColor: '#eaeaea', // light mode default
color: '#000000',
border: 'none',
}
}
});

backdropFilter default

If you omit backdropFilter, the SDK applies backdrop-filter: blur(12px) automatically. This means all themes get a glass blur by default. For solid-card or flat themes, explicitly pass backdropFilter: 'none' to opt out:

createThemeBridge({
card: {
backgroundColor: '#1a1a1a',
borderRadius: '16px',
backdropFilter: 'none', // disable the default blur
},
});

Pseudo-selectors and hover states

You can pass pseudo-selector keys directly inside card as objects. The SDK renders them as separate CSS rules:

createThemeBridge({
card: {
borderRadius: '18px',
boxShadow: '0 8px 32px rgba(0,0,0,0.60)',
'&:hover': {
transform: 'translateY(-3px)',
borderColor: 'rgba(255,255,255,0.28)',
},
'&:active': { transform: 'translateY(0)' },
'&::before': {
content: '""',
position: 'absolute',
inset: 0,
background: 'radial-gradient(ellipse at 50% -5%, rgba(255,255,255,0.08) 0%, transparent 70%)',
pointerEvents: 'none',
borderRadius: 'inherit',
},
},
});

component Parameter

The component parameter styles platform UI elements that apps use inside their iframes — titles, subtitles, list rows, paragraphs, and labels. Apps that use .shk-host-title, .shk-host-subtitle, .shk-host-list-container, etc. will inherit these styles automatically.

createThemeBridge({
card: { /* ... */ },
btn: { /* ... */ },
component: {
title: {
color: '#e8dfc8',
},
subtitle: {
color: 'rgba(232, 223, 200, 0.52)',
opacity: '0.8',
},
row: {
backgroundColor: 'rgba(255, 255, 255, 0.03)',
borderColor: 'rgba(255, 255, 255, 0.08)',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.07)',
borderColor: 'rgba(255, 255, 255, 0.18)',
},
},
},
});
KeyTargetsAccepts
title.shk-host-titlecolor, any CSS property
subtitle.shk-host-subtitlecolor, opacity, any CSS property
row.shk-host-list-container rowsbackgroundColor, borderColor, &:hover object
paragraphparagraph text blockscolor, any CSS property
labellabel text (uppercase)color, any CSS property

If you omit component, the SDK applies neutral defaults (color: inherit, background: transparent).


For themes with dark mode, pass dark-aware values that reflect your compiled SCSS defaults:

const isDark = document.documentElement.classList.contains('dark');

createThemeBridge({
card: { /* ... */ },
btn: {
primary: {
backgroundColor: isDark ? '#e8e8e8' : '#222222',
color: isDark ? '#111111' : '#ffffff',
borderRadius: '10px',
border: 'none',
},
secondary: {
backgroundColor: isDark ? 'rgba(255,255,255,0.10)' : '#eaeaea',
color: isDark ? 'rgba(255,255,255,0.85)' : '#000000',
border: 'none',
}
}
});

That's it.

When the user changes card settings in the dashboard, the SDK intercepts the configuration, sets the --app-card-* CSS variables on the root <html> element dynamically, rebuilds the internal stylesheet, and broadcasts that updated CSS safely to every app iframe running on the page.


Dark Mode and Transparent App Iframes

How the platform manages transparency (platform-owned — no theme action required)

When your theme is dark, the platform passes ?transparent=1&theme_mode=dark to every app sub-iframe. The platform then automatically injects a <style data-platform-canvas-guard> tag as the very first element inside each iframe's <head>, before any other CSS or script parses.

/* Injected by the platform — you will see this in DevTools */
html, body { background: transparent !important; }
body { color-scheme: dark !important; }

You do not need to do anything. This is entirely platform-managed.

Why this guard exists

There is a CSS spec behavior (Color Adjust L3) that affects transparent iframes in dark mode:

When :root's computed background-color is transparent, the browser paints the viewport canvas — the layer below the DOM — with the UA system Canvas color. When color-scheme: dark is active on :root (via <meta> or CSS), that Canvas color is the system dark background (#1c1c1e on macOS Chrome).

Result: even with html, body { background: transparent }, the dark canvas shows through the transparent iframe body, producing a solid dark background on the app card.

The guard prevents this by:

  1. Setting html and body background to var(--shk-host-card-bg, transparent) synchronously before UA styles apply:
    • Solid-card dark themes (e.g. shuuka-dark with rgba(28,39,59,0.92)) fill the iframe body with the theme's card color, preventing the white UA light-mode canvas from showing through.
    • Glass themes (e.g. Aria, Nebula with near-zero opacity) remain effectively transparent, preserving the frosted-glass compositing effect.
    • The transparent fallback applies to themes that don't supply a card background.
  2. Scoping color-scheme: dark to body only — this keeps UA form elements and scrollbars dark without darkening the canvas through :root
  3. The platform also suppresses the <meta name="color-scheme" content="dark"> tag for transparent dark iframes (server-side), since the meta has the identical canvas-darkening effect

What theme developers must not do in app sub-iframe CSS

Don'tWhy
Set color-scheme on :root or html in app CSSFights the platform guard and re-darkens the canvas
Set an explicit background-color (other than transparent) on :root in app CSSOverrides the transparent lock
Add <meta name="color-scheme" content="dark"> manually in app HTMLThe platform suppresses the meta server-side; adding it back undoes that

html.dark class selectors in your theme SCSS and in app CSS are safe — they target content inside <body> and do not affect the canvas.

Debugging

Open DevTools inside any app sub-iframe on a dark theme. The first tag in <head> should be:

<style data-platform-canvas-guard>html,body{background:transparent!important;}body{color-scheme:dark!important;}</style>

If it is missing, the iframe was not served with ?transparent=1 — check that your theme has meta.color_scheme: dark in theme.config.json and that templateUsesWrapperStyles is true for your theme type.


App Manifest Interaction

Apps may declare card_settings that intentionally lock some card behavior. Theme authors should assume:

  • structural app constraints may win
  • cosmetic defaults should still come from the theme whenever possible

card_settings Keys Themes Should Respect

KeyPossible valuesTheme impact
card_styletrue, falseWhether the app expects Shuuka card chrome
styletrue, falseLegacy alias for card_style
backgroundCSS color, gradient, or transparentApp locks the card background. Omit to inherit the platform's theme-aware default (light/dark mode).
border_radiusCSS length such as 0, 12px, 999pxApp locks the outer card radius
box_shadowtrue, false, or CSS shadow valueApp locks the outer card shadow behavior. Omit to inherit the platform default.
full_widthtrue, falseWhether the app should stretch edge-to-edge
disable_scroll_overlaytrue, falseHost compatibility flag for overlay behavior
size.width / size.heightpixel value, "100%", "auto"Embed sizing hints

If an app declares background, border_radius, or box_shadow, treat those as explicit app-level visual locks and avoid reintroducing a second conflicting card layer in the theme.

When a visitor has not consented to an app's third-party data transfers, the Shuuka platform renders a .shk-consent-placeholder element inside the app card in place of the iframe content.

This element is owned entirely by the platform — not the theme.

ElementOwnerWhat to do
.shk-theme__app-cardThemeStyle freely with card tokens
.shk-consent-placeholderPlatformDo not style. The platform injects a self-contained white card with fixed brand styling.

The placeholder renders a white card (with shadow and border-radius) that is always readable regardless of the theme's background color. Text and button labels are supplied by the platform in the visitor's active locale — themes have no control over this content.

Do not do this

/* WRONG — consent placeholder is platform-owned */
.shk-consent-placeholder {
color: var(--theme-text-color);
background: var(--app-card-bg);
}

What the platform renders

  • White self-contained card with box-shadow
  • Red icon for disabled state, green icon for pending state
  • Title, description, and action button in the visitor's locale
  • Translations provided automatically by the platform — no theme action required

The theme card wrapper (.shk-theme__app-card) still applies its own background, border, and radius around the placeholder, so the overall card frame continues to match the theme.