Theming
Customize every aspect of your widget to match your brand đ¨
Theming & Customization
Make the widget truly yours with 50+ customization options. From colors to animations, control every detail.
Quick Example
import { WhatIf } from '@twenty1tools/whatif';
const whatif = new WhatIf('your-app-id', '/api', {
colors: {
primary: '#3b82f6', // Your brand color
},
typography: {
fontFamily: 'Inter, sans-serif', // Your font
},
border: {
radius: '16px', // Rounded corners
},
position: 'bottom-right', // Widget position
});Theme Configuration
Color Palette
Customize all 12 color properties:
colors: {
// Primary colors
primary: '#3b82f6', // Button background
primaryHover: '#2563eb', // Button hover state
// Background & text
background: '#ffffff', // Widget background
textPrimary: '#1f2937', // Main text color
textSecondary: '#6b7280', // Secondary text
// Borders
border: '#e5e7eb', // Default borders
borderHover: '#3b82f6', // Focus/hover borders
// Interactive states
hoverBackground: '#f3f4f6', // Hover backgrounds
closeButton: '#9ca3af', // Close X color
closeButtonHover: '#1f2937', // Close X hover
// Ratings
ratingFilled: '#fbbf24', // Filled stars
ratingEmpty: '#d1d5db', // Empty stars
}Interactive Theme Showcase
See how different themes transform the same widget. Click the preview buttons to try them out! Each example showcases different dark mode settings, positions, animations, and styling options.
đ Light Mode Professional
const lightModeTheme = {
darkMode: 'light', // Forced light mode
position: 'top-right', // Top-right position
colors: {
primary: '#059669',
primaryHover: '#047857',
background: '#ffffff',
textPrimary: '#111827',
textSecondary: '#6b7280',
border: '#e5e7eb'
},
typography: {
fontFamily: 'system-ui, sans-serif',
questionSize: '18px',
questionWeight: '600'
},
spacing: {
padding: '24px',
gap: '16px',
offset: '20px'
},
border: { radius: '12px' },
shadow: { container: '0 10px 25px rgba(0,0,0,0.1)' },
animation: { duration: 300, easing: 'ease-out' }
};đ Dark Mode Minimalist
const darkModeTheme = {
darkMode: 'dark', // Forced dark mode
position: 'bottom-left', // Bottom-left position
colors: {
primary: '#f59e0b',
primaryHover: '#d97706',
background: '#1f2937',
textPrimary: '#f9fafb',
textSecondary: '#d1d5db',
border: '#374151',
hoverBackground: '#374151'
},
typography: {
fontFamily: '"JetBrains Mono", monospace',
questionSize: '16px',
inputSize: '14px'
},
spacing: {
padding: '16px',
gap: '12px',
offset: '16px'
},
border: {
radius: '4px',
buttonRadius: '2px',
width: '1px'
},
shadow: { enabled: false },
animation: { enabled: false }
};đ Funky Bounce Auto Dark Mode
const funkyTheme = {
darkMode: 'auto', // Auto-detect system preference
position: 'bottom-right', // Default position
colors: {
primary: '#8b5cf6', // Electric purple
primaryHover: '#7c3aed', // Deeper purple on hover
background: 'rgba(15, 23, 42, 0.95)', // Dark blue-tinted glass
textPrimary: '#f1f5f9', // Light slate text
textSecondary: '#cbd5e1', // Muted slate
border: '#334155', // Slate border
hoverBackground: '#1e293b', // Darker hover state
ratingFilled: '#06b6d4', // Cyan stars
ratingEmpty: '#475569' // Muted empty stars
},
typography: {
fontFamily: '"Fredoka One", cursive', // Funky rounded font
questionSize: '22px',
questionWeight: '400',
buttonSize: '16px',
buttonWeight: '600'
},
spacing: {
padding: '28px',
gap: '18px',
offset: '24px'
},
border: {
radius: '20px',
buttonRadius: '16px',
inputRadius: '12px'
},
shadow: {
container: '0 25px 50px rgba(139, 92, 246, 0.25)' // Purple-tinted glow
},
animation: {
duration: 800,
easing: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)' // Super bouncy
}
};đ Fast Top-Left Compact
const fastCompactTheme = {
darkMode: 'auto',
position: 'top-left', // Top-left position
colors: {
primary: '#7c3aed',
primaryHover: '#6d28d9',
background: '#fefefe',
textPrimary: '#1f2937',
textSecondary: '#6b7280'
},
typography: {
fontFamily: 'Inter, sans-serif',
questionSize: '14px',
buttonSize: '12px'
},
spacing: {
padding: '12px',
gap: '8px',
offset: '12px'
},
border: { radius: '6px' },
shadow: { container: '0 2px 8px rgba(0,0,0,0.1)' },
animation: { duration: 150, easing: 'ease-in' } // Super fast
};đ¨ High Contrast Bold
const highContrastTheme = {
darkMode: 'auto',
position: 'bottom-right',
colors: {
primary: '#dc2626',
primaryHover: '#b91c1c',
background: '#000000',
textPrimary: '#ffffff',
textSecondary: '#cccccc',
border: '#ffffff',
hoverBackground: '#333333'
},
typography: {
fontFamily: 'Impact, sans-serif',
questionSize: '24px',
questionWeight: '900',
buttonSize: '16px',
buttonWeight: 'bold'
},
spacing: {
padding: '20px',
gap: '16px',
offset: '20px'
},
border: {
radius: '0px', // Sharp corners
buttonRadius: '0px',
width: '3px' // Thick borders
},
shadow: {
container: '0 0 20px rgba(220, 38, 38, 0.5)' // Glowing red shadow
},
animation: {
duration: 1000,
easing: 'ease-in-out'
}
};Typography
Control all text styling:
typography: {
fontFamily: 'Inter, system-ui, sans-serif',
// Question text
questionSize: '20px',
questionWeight: '700',
// Form elements
inputSize: '16px',
// Button text
buttonSize: '16px',
buttonWeight: '600',
}Font Examples
// Modern sans-serif
fontFamily: 'Inter, system-ui, sans-serif'
// Classic serif
fontFamily: 'Georgia, serif'
// Monospace
fontFamily: 'Fira Code, monospace'
// Your custom font
fontFamily: '"Your Font", sans-serif'Make sure your custom fonts are loaded before initializing the widget!
Spacing
Fine-tune the layout:
spacing: {
padding: '28px', // Internal padding
gap: '20px', // Space between elements
offset: '24px', // Distance from screen edge
}Spacing Presets
// Compact
spacing: { padding: '16px', gap: '12px', offset: '16px' }
// Default
spacing: { padding: '20px', gap: '16px', offset: '20px' }
// Spacious
spacing: { padding: '32px', gap: '24px', offset: '32px' }Borders
Control roundness and thickness:
border: {
radius: '16px', // Widget corners
buttonRadius: '8px', // Button corners
inputRadius: '8px', // Input corners
width: '1px', // Border thickness
}Border Style Presets
// Sharp (minimal)
border: { radius: '4px', buttonRadius: '2px', width: '1px' }
// Rounded (default)
border: { radius: '12px', buttonRadius: '6px', width: '1px' }
// Pill (modern)
border: { radius: '24px', buttonRadius: '24px', width: '1px' }
// Bold
border: { radius: '8px', buttonRadius: '6px', width: '2px' }Shadow
Control depth and elevation:
shadow: {
enabled: true,
container: '0 20px 50px rgba(0,0,0,0.15)',
}Shadow Presets
// Subtle
shadow: {
enabled: true,
container: '0 4px 12px rgba(0,0,0,0.08)'
}
// Default
shadow: {
enabled: true,
container: '0 8px 24px rgba(0,0,0,0.12)'
}
// Dramatic
shadow: {
enabled: true,
container: '0 20px 50px rgba(0,0,0,0.2)'
}
// Flat (no shadow)
shadow: { enabled: false }Animation
Control motion and timing:
animation: {
enabled: true,
duration: 400, // Milliseconds
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
}// Disable animations
animation: { enabled: false }
// Fast
animation: { duration: 200 }
// Smooth
animation: { duration: 500, easing: 'ease-in-out' }
// Bouncy
animation: {
duration: 600,
easing: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'
}Widget Position
Place the widget in any corner:
position: 'bottom-right' // Default
position: 'bottom-left'
position: 'top-right'
position: 'top-left'Complete Theme Example
Here's a fully customized theme:
import { WhatIf } from '@twenty1tools/whatif';
const whatif = new WhatIf('your-app-id', '/api', {
// Position
position: 'bottom-right',
// Colors
colors: {
primary: '#3b82f6',
primaryHover: '#2563eb',
background: '#ffffff',
textPrimary: '#1f2937',
textSecondary: '#6b7280',
border: '#e5e7eb',
borderHover: '#3b82f6',
hoverBackground: '#f3f4f6',
closeButton: '#9ca3af',
closeButtonHover: '#1f2937',
ratingFilled: '#fbbf24',
ratingEmpty: '#d1d5db',
},
// Typography
typography: {
fontFamily: 'Inter, system-ui, sans-serif',
questionSize: '20px',
questionWeight: '700',
inputSize: '16px',
buttonSize: '16px',
buttonWeight: '600',
},
// Spacing
spacing: {
padding: '28px',
gap: '20px',
offset: '24px',
},
// Borders
border: {
radius: '16px',
buttonRadius: '8px',
inputRadius: '8px',
width: '1px',
},
// Shadow
shadow: {
enabled: true,
container: '0 20px 50px rgba(0,0,0,0.15)',
},
// Animation
animation: {
enabled: true,
duration: 400,
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
},
});Pre-built Themes
Stripe-Inspired
{
colors: {
primary: '#635bff',
background: '#ffffff',
textPrimary: '#0a2540',
},
typography: {
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto',
},
border: { radius: '8px' },
}Minimal
{
colors: {
primary: '#000000',
primaryHover: '#333333',
},
border: {
radius: '4px',
buttonRadius: '2px',
},
shadow: { enabled: false },
}E-commerce
{
colors: {
primary: '#ff6b35',
ratingFilled: '#ffd700',
},
typography: {
questionSize: '22px',
questionWeight: '800',
},
spacing: { padding: '32px' },
}Advanced: Custom CSS
For complete control, inject custom CSS:
{
customCss: `
/* Glassmorphism effect */
.widget-container {
background: rgba(255, 255, 255, 0.7) !important;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
/* Custom button hover */
button[type="submit"]:hover {
transform: scale(1.05);
}
/* Gradient border */
.widget-container {
border: 2px solid transparent;
background: linear-gradient(white, white) padding-box,
linear-gradient(45deg, #3b82f6, #8b5cf6) border-box;
}
`
}Dynamic Theming
Update the theme at runtime:
const whatif = new WhatIf('app-id');
// Later, update theme
whatif.setTheme({
colors: { primary: '#10b981' } // Change to green
});React Hook
const { setTheme } = useWhatIf();
<button onClick={() => setTheme({
colors: { primary: '#8b5cf6' }
})}>
Change Theme
</button>TypeScript Support
Full type safety for theme configuration:
import { WhatIf, type ThemeConfig, type ColorPalette } from '@twenty1tools/whatif';
const colors: ColorPalette = {
primary: '#3b82f6',
// TypeScript will autocomplete and validate
};
const theme: ThemeConfig = {
colors,
// Full IntelliSense support
};