Documentation

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
};

Next Steps