IFrame Theming
Platforms that embed Closed Caption Creator in an iframe can customize the look of the editor so it feels native to their product. Theming covers UI chrome — the toolbar, panels, buttons, inputs, fonts, borders, and dropdowns — using a small set of design tokens that map onto Bootstrap CSS variables already used throughout the application.
The Konva timeline canvas, caption preview fonts, and QC style guides are not controlled by these APIs. Use setPreviewFontFamily / setPreviewFontSize for preview typography and loadStyleGuide for caption quality rules.
Approaches
There are two complementary ways to theme an iframe build:
- Build-time theme CSS — ship a customer stylesheet with the dedicated offline build. The file loads automatically based on the customer system slug. Best for hosted environments that always use one brand (for example a Grass Valley Framelight deployment).
- Runtime remote APIs — call
setTheme,loadCustomStyles, orclearThemefrom the parent page after the PenPal connection is ready. Best when the host needs to drive colors dynamically or share one build across multiple skins.
Most dedicated customer environments use build-time CSS for the baseline look, then optionally use the remote APIs for fine-tuning.
Build-Time Themes
Dedicated offline iframe environments are configured with a customer system slug (for example gv, tv2, or demo). When the system is not demo, the editor loads:
./assets/css/themes/{system}.css
Customer logos follow the same slug pattern:
| Asset | Path |
|---|---|
| Toolbar logo | ./assets/img/logos/{system}.png |
| Status modal icon | ./assets/img/logos/{system}-icon.png |
Creating a customer theme
- Choose a short system slug for the customer.
- Add the logo assets named above.
- Add a CSS file at
assets/css/themes/{system}.cssthat overrides Bootstrap variables under[data-bs-theme="dark"](and light if needed). - Prefer token overrides over pasting a host application's full stylesheet — large third-party CSS dumps conflict with Bootstrap and Closed Caption Creator component styles.
- Build and deploy the offline iframe package for that customer as usual.
Example theme snippet:
[data-bs-theme="dark"] {
--bs-primary: #408fe0;
--bs-primary-rgb: 64, 143, 224;
--bs-body-bg: #181c24;
--bs-body-color: rgba(255, 255, 255, 0.9);
--bs-secondary-color: rgba(255, 255, 255, 0.6);
--bs-border-color: rgba(255, 255, 255, 0.1);
--bs-light: #232a35;
--bs-link-color: #408fe0;
--bs-link-hover-color: #7cc4f5;
--bs-body-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
[data-bs-theme="dark"] .btn-primary {
--bs-btn-color: #fff;
--bs-btn-bg: #408fe0;
--bs-btn-border-color: #408fe0;
--bs-btn-hover-bg: #1a8de8;
--bs-btn-hover-border-color: #1a8de8;
}
Also override .btn-outline-primary --bs-btn-* variables when you change the accent color so outline buttons stay consistent.
Runtime Theme APIs
Call theme methods immediately after connection.promise resolves and before importing media when possible, so users do not see a flash of the default teal branding.
iframe.addEventListener('load', async () => {
const messenger = new Penpal.WindowMessenger({
remoteWindow: iframe.contentWindow,
allowedOrigins: ['https://your-iframe-host.example.com']
});
const connection = Penpal.connect({ messenger, methods: {} });
const remote = await connection.promise;
await remote.setTheme({
primary: '#408fe0',
bodyBg: '#181c24',
bodyColor: 'rgba(255, 255, 255, 0.9)',
borderColor: 'rgba(255, 255, 255, 0.1)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
});
await remote.setProjectName('My Project');
await remote.importMedia(mediaUrl);
await remote.importSubtitle(subtitleUrl, 'subRip');
});
setTheme(tokens)
Applies structured chrome overrides as CSS variables on the document root. Tokens may use short aliases or raw --bs-* variable names. When primary is a hex color and primaryRgb is omitted, the RGB triplet is derived automatically.
Returns { success: boolean, message: string }.
loadCustomStyles(cssText)
Injects or replaces a runtime <style> block with the provided CSS string. Use this for advanced chrome tweaks that go beyond simple token values. Prefer setTheme for color and font overrides.
Returns { success: boolean, message: string }.
clearTheme()
Removes runtime styles and token overrides applied by setTheme / loadCustomStyles. The build-time assets/css/themes/{system}.css file (if present) remains loaded.
Returns { success: boolean, message: string }.
Theme Token Contract
Alias (setTheme) | CSS variable | Typical use |
|---|---|---|
primary | --bs-primary | Accent color, active states |
primaryRgb | --bs-primary-rgb | r, g, b for rgba() helpers |
bodyBg | --bs-body-bg | Main workspace background |
bodyBgRgb | --bs-body-bg-rgb | Background RGB triplet |
bodyColor | --bs-body-color | Primary text |
bodyColorRgb | --bs-body-color-rgb | Text RGB triplet |
secondaryColor | --bs-secondary-color | Muted labels |
borderColor | --bs-border-color | Dividers and panel edges |
fontFamily | --bs-body-font-family | UI font stack |
linkColor | --bs-link-color | Links |
linkHoverColor | --bs-link-hover-color | Link hover |
light | --bs-light | Panel / surface fill |
danger | --bs-danger | Error / destructive accents |
warning | --bs-warning | Warning accents |
success | --bs-success | Success accents |
info | --bs-info | Informational accents |
You can also pass raw property names such as "--bs-primary" in the setTheme object. Unknown keys are ignored with a console warning.
Scope and Limitations
- Included: navbar, panels, modals, buttons, form controls, dropdowns, fonts, borders, and related chrome that consume Bootstrap variables.
- Not included: Konva timeline track/waveform colors, caption burn-in / preview fonts (use display APIs), and QC style guides (use
loadStyleGuide). - Light / dark: Customer themes should define tokens under
[data-bs-theme="dark"]and optionally light. Iframe builds commonly default to dark. - Expectation: A perfect pixel match to every host UI is not guaranteed. Matching accent color, surfaces, borders, and typography is usually enough for a cohesive embed.