Custom Theming
Make your Plaros embeds match your brand with custom colors, themes, and styling options.
Theme Override Options
Custom Colors
Override your tenant's default colors for specific embeds using URL parameters.
Primary Color
Controls buttons, links, and main interactive elements.
// URL parameter (without #)
?primaryColor=FF6B35
// JavaScript example
url.searchParams.set('primaryColor', '#FF6B35'.replace('#', ''));Secondary Color
Controls accents, highlights, and secondary interactive elements.
// URL parameter (without #)
?secondaryColor=004E89
// JavaScript example
url.searchParams.set('secondaryColor', '#004E89'.replace('#', ''));Theme Mode
Force your embed to use light or dark mode regardless of user preferences.
// Force dark mode
?themeMode=dark
// Force light mode
?themeMode=lightComplete Theming Example
const themedEmbedUrl = new URL('https://play.plaros.com/v1/embed/acme-corp/playbooks');
// Basic configuration
themedEmbedUrl.searchParams.set('zonePath', 'brand-training');
// Custom brand colors
themedEmbedUrl.searchParams.set('primaryColor', 'FF6B35'); // Orange
themedEmbedUrl.searchParams.set('secondaryColor', '004E89'); // Navy Blue
// Force dark theme
themedEmbedUrl.searchParams.set('themeMode', 'dark');
console.log(themedEmbedUrl.toString());
// https://play.plaros.com/v1/embed/acme-corp/playbooks?zonePath=brand-training&primaryColor=FF6B35&secondaryColor=004E89&themeMode=darkColor Picker Integration
React with Color Picker
import React, { useState } from 'react';
const ThemeCustomizer = ({ tenantSlug, zonePath, onUrlChange }) => {
const [primaryColor, setPrimaryColor] = useState('#2196F3');
const [secondaryColor, setSecondaryColor] = useState('#9C27B0');
const [themeMode, setThemeMode] = useState('light');
const generateUrl = () => {
const url = new URL(`https://play.plaros.com/v1/embed/${tenantSlug}/playbooks`);
url.searchParams.set('zonePath', zonePath);
url.searchParams.set('primaryColor', primaryColor.replace('#', ''));
url.searchParams.set('secondaryColor', secondaryColor.replace('#', ''));
url.searchParams.set('themeMode', themeMode);
onUrlChange(url.toString());
return url.toString();
};
return (
<div className="theme-customizer">
<h3>Customize Embed Theme</h3>
<div className="color-controls">
<label>
Primary Color:
<input
type="color"
value={primaryColor}
onChange={(e) => setPrimaryColor(e.target.value)}
/>
</label>
<label>
Secondary Color:
<input
type="color"
value={secondaryColor}
onChange={(e) => setSecondaryColor(e.target.value)}
/>
</label>
<label>
Theme Mode:
<select
value={themeMode}
onChange={(e) => setThemeMode(e.target.value)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label>
</div>
{/* Live Preview */}
<div className="color-preview">
<button
style={{
backgroundColor: primaryColor,
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
marginRight: '8px'
}}
>
Primary Button
</button>
<button
style={{
backgroundColor: 'transparent',
color: secondaryColor,
border: `2px solid ${secondaryColor}`,
padding: '6px 14px',
borderRadius: '4px',
marginRight: '8px'
}}
>
Secondary Button
</button>
<span
style={{
backgroundColor: secondaryColor,
color: 'white',
padding: '4px 8px',
borderRadius: '12px',
fontSize: '12px'
}}
>
Accent Badge
</span>
</div>
<button onClick={generateUrl}>
Update Embed URL
</button>
</div>
);
};Responsive Sizing
Customize how your embed appears on different screen sizes.
Standard Responsive Setup
.playbook-embed-container {
position: relative;
width: 100%;
max-width: 800px; /* Adjust maximum width */
margin: 0 auto;
}
.playbook-embed-wrapper {
position: relative;
padding-bottom: 75%; /* 4:3 aspect ratio */
height: 0;
overflow: hidden;
}
.playbook-embed-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
border-radius: 8px;
}
/* Mobile optimizations */
@media (max-width: 768px) {
.playbook-embed-container {
max-width: 100%;
padding: 0 16px;
}
.playbook-embed-wrapper {
padding-bottom: 177.78%; /* 16:9 for mobile */
}
}
/* Tablet optimizations */
@media (min-width: 769px) and (max-width: 1024px) {
.playbook-embed-container {
max-width: 600px;
}
.playbook-embed-wrapper {
padding-bottom: 133.33%; /* 3:4 for tablets */
}
}Custom Aspect Ratios
Different content types may benefit from different aspect ratios:
/* Square embed (1:1) */
.playbook-embed-square {
padding-bottom: 100%;
}
/* Widescreen embed (16:9) */
.playbook-embed-widescreen {
padding-bottom: 56.25%;
}
/* Portrait embed (3:4) */
.playbook-embed-portrait {
padding-bottom: 133.33%;
}
/* Tall embed (9:16 - mobile-first) */
.playbook-embed-tall {
padding-bottom: 177.78%;
}Advanced Styling
Custom Container Styling
.playbook-embed-container {
/* Shadow and border effects */
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
border: 1px solid #e0e0e0;
border-radius: 12px;
overflow: hidden;
/* Background for loading states */
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
/* Smooth transitions */
transition: box-shadow 0.3s ease;
}
.playbook-embed-container:hover {
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
}
/* Loading state */
.playbook-embed-container::before {
content: 'Loading interactive content...';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #666;
font-size: 14px;
z-index: 1;
}
.playbook-embed-container iframe {
/* Hide loading text once iframe loads */
position: relative;
z-index: 2;
}Brand Integration
.branded-embed {
/* Company colors */
--brand-primary: #FF6B35;
--brand-secondary: #004E89;
--brand-accent: #FFD23F;
background: var(--brand-primary);
padding: 20px;
border-radius: 16px;
}
.branded-embed::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg,
var(--brand-primary) 0%,
var(--brand-accent) 50%,
var(--brand-secondary) 100%
);
}
.branded-embed .playbook-embed-wrapper {
background: white;
border-radius: 8px;
overflow: hidden;
}CSS Variables for Dynamic Theming
Use CSS custom properties for runtime theme switching:
:root {
--embed-primary-color: #2196F3;
--embed-secondary-color: #9C27B0;
--embed-border-radius: 8px;
--embed-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] {
--embed-primary-color: #90CAF9;
--embed-secondary-color: #CE93D8;
--embed-shadow: 0 4px 20px rgba(255, 255, 255, 0.1);
}
.dynamic-themed-embed {
border-radius: var(--embed-border-radius);
box-shadow: var(--embed-shadow);
}// Switch themes dynamically
function switchEmbedTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
// Update embed URL if needed
const iframe = document.querySelector('.playbook-embed iframe');
const currentUrl = new URL(iframe.src);
currentUrl.searchParams.set('themeMode', theme);
iframe.src = currentUrl.toString();
}Color Accessibility
Ensure your custom colors meet accessibility standards:
// Check color contrast ratio
function getContrastRatio(color1, color2) {
// Implementation of WCAG contrast calculation
// Use libraries like 'color' or 'chroma-js' for production
}
// Validate color choices
function validateColors(primaryColor, secondaryColor) {
const contrastRatio = getContrastRatio(primaryColor, '#FFFFFF');
if (contrastRatio < 4.5) {
console.warn('Primary color may not meet WCAG AA standards');
}
return contrastRatio >= 3; // WCAG AA Large text minimum
}Theme Testing
Test your custom themes across different scenarios:
// Test configuration
const themeTestCases = [
{
name: 'Corporate Blue',
primaryColor: '0066CC',
secondaryColor: '6699FF',
themeMode: 'light'
},
{
name: 'Dark Mode Orange',
primaryColor: 'FF6B35',
secondaryColor: 'FFD23F',
themeMode: 'dark'
},
{
name: 'High Contrast',
primaryColor: '000000',
secondaryColor: 'FFFFFF',
themeMode: 'light'
}
];
// Generate test URLs
themeTestCases.forEach(theme => {
const url = new URL('https://play.plaros.com/v1/embed/test-tenant/playbooks');
url.searchParams.set('zonePath', 'theme-test');
url.searchParams.set('primaryColor', theme.primaryColor);
url.searchParams.set('secondaryColor', theme.secondaryColor);
url.searchParams.set('themeMode', theme.themeMode);
console.log(`${theme.name}: ${url.toString()}`);
});Next Steps
See Integration Examples for framework-specific theming implementations
Learn about Configuration Options for additional customization
Check Troubleshooting for common theming issues