# WooNooW Customer SPA Theme System
## ๐จ Design Philosophy
**SaaS Approach:** Curated options over infinite flexibility
- โ
4 master layouts (not infinite themes)
- Classic, Modern, Boutique (multi-product stores)
- Launch (single product funnels) ๐
- โ
Design tokens (not custom CSS)
- โ
Preset combinations (not freestyle design)
- โ
Accessibility built-in (WCAG 2.1 AA)
- โ
Performance optimized (Core Web Vitals)
---
## ๐๏ธ Theme Architecture
### Design Token System
All styling is controlled via CSS custom properties (design tokens):
```css
:root {
/* Colors */
--color-primary: #3B82F6;
--color-secondary: #8B5CF6;
--color-accent: #10B981;
--color-background: #FFFFFF;
--color-text: #1F2937;
/* Typography */
--font-heading: 'Inter', sans-serif;
--font-body: 'Lora', serif;
--font-size-base: 16px;
--line-height-base: 1.5;
/* Spacing (8px grid) */
--space-1: 0.5rem; /* 8px */
--space-2: 1rem; /* 16px */
--space-3: 1.5rem; /* 24px */
--space-4: 2rem; /* 32px */
--space-6: 3rem; /* 48px */
--space-8: 4rem; /* 64px */
/* Border Radius */
--radius-sm: 0.25rem; /* 4px */
--radius-md: 0.5rem; /* 8px */
--radius-lg: 1rem; /* 16px */
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 250ms ease;
--transition-slow: 350ms ease;
}
```
---
## ๐ Master Layouts
### 1. Classic Layout
**Target Audience:** Traditional ecommerce, B2B
**Characteristics:**
- Header: Logo left, menu right, search bar
- Shop: Sidebar filters (left), product grid (right)
- Product: Image gallery left, details right
- Footer: 4-column widget areas
**File:** `customer-spa/src/layouts/ClassicLayout.tsx`
```typescript
export function ClassicLayout({ children }) {
return (
{children}
);
}
```
**CSS:**
```css
.classic-layout {
--header-height: 80px;
--sidebar-width: 280px;
}
.classic-main {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr;
gap: var(--space-6);
max-width: 1280px;
margin: 0 auto;
padding: var(--space-6);
}
@media (max-width: 768px) {
.classic-main {
grid-template-columns: 1fr;
}
}
```
### 2. Modern Layout (Default)
**Target Audience:** Fashion, lifestyle, modern brands
**Characteristics:**
- Header: Centered logo, minimal menu
- Shop: Top filters (no sidebar), large product cards
- Product: Full-width gallery, sticky details
- Footer: Minimal, centered
**File:** `customer-spa/src/layouts/ModernLayout.tsx`
```typescript
export function ModernLayout({ children }) {
return (
{children}
);
}
```
**CSS:**
```css
.modern-layout {
--header-height: 100px;
--content-max-width: 1440px;
}
.modern-main {
max-width: var(--content-max-width);
margin: 0 auto;
padding: var(--space-8) var(--space-4);
}
.modern-layout .product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: var(--space-6);
}
```
### 3. Boutique Layout
**Target Audience:** Luxury, high-end fashion
**Characteristics:**
- Header: Full-width, transparent overlay
- Shop: Masonry grid, elegant typography
- Product: Minimal UI, focus on imagery
- Footer: Elegant, serif typography
**File:** `customer-spa/src/layouts/BoutiqueLayout.tsx`
```typescript
export function BoutiqueLayout({ children }) {
return (
{children}
);
}
```
**CSS:**
```css
.boutique-layout {
--header-height: 120px;
--content-max-width: 1600px;
font-family: var(--font-heading);
}
.boutique-main {
max-width: var(--content-max-width);
margin: 0 auto;
}
.boutique-layout .product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
gap: var(--space-8);
}
```
### 4. Launch Layout ๐ (Single Product Funnel)
**Target Audience:** Single product sellers, course creators, SaaS, product launchers
**Important:** Landing page is **fully custom** (user builds with their page builder). WooNooW SPA only takes over **from checkout onwards** after CTA button is clicked.
**Characteristics:**
- **Landing page:** User's custom design (Elementor, Divi, etc.) - NOT controlled by WooNooW
- **Checkout onwards:** WooNooW SPA takes full control
- **No traditional header/footer** on SPA pages (distraction-free)
- **Streamlined checkout** (one-page, minimal fields, no cart)
- **Upsell opportunity** on thank you page
- **Direct access** to product in My Account
**Page Flow:**
```
Landing Page (Custom - User's Page Builder)
โ
[CTA Button Click] โ User directs to /checkout
โ
Checkout (WooNooW SPA - Full screen, no distractions)
โ
Thank You (WooNooW SPA - Upsell/downsell opportunity)
โ
My Account (WooNooW SPA - Access product/download)
```
**Technical Note:**
- Landing page URL: Any (/, /landing, /offer, etc.)
- CTA button links to: `/checkout` or `/checkout?add-to-cart=123`
- WooNooW SPA activates only on checkout, thank you, and account pages
- This is essentially **Checkout-Only mode** with optimized funnel design
**File:** `customer-spa/src/layouts/LaunchLayout.tsx`
```typescript
export function LaunchLayout({ children }) {
const location = useLocation();
const isLandingPage = location.pathname === '/' || location.pathname === '/shop';
return (
{/* Minimal header only on non-landing pages */}
{!isLandingPage && }
{children}
{/* No footer on landing page */}
{!isLandingPage && }
);
}
```
**CSS:**
```css
.launch-layout {
--content-max-width: 1200px;
min-height: 100vh;
}
.launch-main {
max-width: var(--content-max-width);
margin: 0 auto;
}
/* Landing page: full-screen hero */
.launch-landing {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: var(--space-8);
}
.launch-landing .hero-title {
font-size: var(--text-5xl);
font-weight: 700;
margin-bottom: var(--space-4);
}
.launch-landing .hero-subtitle {
font-size: var(--text-xl);
margin-bottom: var(--space-8);
opacity: 0.8;
}
.launch-landing .cta-button {
font-size: var(--text-xl);
padding: var(--space-4) var(--space-8);
min-width: 300px;
}
/* Checkout: streamlined, no distractions */
.launch-checkout {
max-width: 600px;
margin: var(--space-8) auto;
padding: var(--space-6);
}
/* Thank you: upsell opportunity */
.launch-thankyou {
max-width: 800px;
margin: var(--space-8) auto;
text-align: center;
}
.launch-thankyou .upsell-section {
margin-top: var(--space-8);
padding: var(--space-6);
border: 2px solid var(--color-primary);
border-radius: var(--radius-lg);
}
```
**Perfect For:**
- Digital products (courses, ebooks, software)
- SaaS trial โ paid conversions
- Webinar funnels
- High-ticket consulting
- Limited-time offers
- Crowdfunding campaigns
- Product launches
**Competitive Advantage:**
Replaces expensive tools like CartFlows ($297-997/year) with built-in, optimized funnel.
---
## ๐จ Color System
### Color Palette Generation
When user sets primary color, we auto-generate shades:
```typescript
function generateColorShades(baseColor: string) {
return {
50: lighten(baseColor, 0.95),
100: lighten(baseColor, 0.90),
200: lighten(baseColor, 0.75),
300: lighten(baseColor, 0.60),
400: lighten(baseColor, 0.40),
500: baseColor, // Base color
600: darken(baseColor, 0.10),
700: darken(baseColor, 0.20),
800: darken(baseColor, 0.30),
900: darken(baseColor, 0.40),
};
}
```
### Contrast Checking
Ensure WCAG AA compliance:
```typescript
function ensureContrast(textColor: string, bgColor: string) {
const contrast = getContrastRatio(textColor, bgColor);
if (contrast < 4.5) {
// Adjust text color for better contrast
return adjustColorForContrast(textColor, bgColor, 4.5);
}
return textColor;
}
```
### Dark Mode Support
```css
@media (prefers-color-scheme: dark) {
:root {
--color-background: #1F2937;
--color-text: #F9FAFB;
/* Invert shades */
}
}
```
---
## ๐ Typography System
### Typography Presets
#### Professional
```css
:root {
--font-heading: 'Inter', -apple-system, sans-serif;
--font-body: 'Lora', Georgia, serif;
--font-weight-heading: 700;
--font-weight-body: 400;
}
```
#### Modern
```css
:root {
--font-heading: 'Poppins', -apple-system, sans-serif;
--font-body: 'Roboto', -apple-system, sans-serif;
--font-weight-heading: 600;
--font-weight-body: 400;
}
```
#### Elegant
```css
:root {
--font-heading: 'Playfair Display', Georgia, serif;
--font-body: 'Source Sans Pro', -apple-system, sans-serif;
--font-weight-heading: 700;
--font-weight-body: 400;
}
```
#### Tech
```css
:root {
--font-heading: 'Space Grotesk', monospace;
--font-body: 'IBM Plex Mono', monospace;
--font-weight-heading: 700;
--font-weight-body: 400;
}
```
### Type Scale
```css
:root {
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
}
```
---
## ๐งฉ Component Theming
### Button Component
```typescript
// components/ui/button.tsx
export function Button({ variant = 'primary', ...props }) {
return (
);
}
```
```css
.btn {
font-family: var(--font-heading);
font-weight: 600;
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-md);
transition: all var(--transition-base);
}
.btn-primary {
background: var(--color-primary);
color: white;
}
.btn-primary:hover {
background: var(--color-primary-600);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
```
### Product Card Component
```typescript
// components/ProductCard.tsx
export function ProductCard({ product, layout }) {
const theme = useTheme();
return (
{product.name}
{product.price}
);
}
```
```css
.product-card {
background: var(--color-background);
border-radius: var(--radius-lg);
overflow: hidden;
transition: all var(--transition-base);
}
.product-card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-4px);
}
.product-card-modern {
/* Modern layout specific styles */
padding: var(--space-4);
}
.product-card-boutique {
/* Boutique layout specific styles */
padding: 0;
}
```
---
## ๐ญ Theme Provider (React Context)
### Implementation
**File:** `customer-spa/src/contexts/ThemeContext.tsx`
```typescript
import { createContext, useContext, useEffect, ReactNode } from 'react';
interface ThemeConfig {
layout: 'classic' | 'modern' | 'boutique';
colors: {
primary: string;
secondary: string;
accent: string;
background: string;
text: string;
};
typography: {
preset: string;
customFonts?: {
heading: string;
body: string;
};
};
}
const ThemeContext = createContext(null);
export function ThemeProvider({
config,
children
}: {
config: ThemeConfig;
children: ReactNode;
}) {
useEffect(() => {
// Inject CSS variables
const root = document.documentElement;
// Colors
root.style.setProperty('--color-primary', config.colors.primary);
root.style.setProperty('--color-secondary', config.colors.secondary);
root.style.setProperty('--color-accent', config.colors.accent);
root.style.setProperty('--color-background', config.colors.background);
root.style.setProperty('--color-text', config.colors.text);
// Typography
loadTypographyPreset(config.typography.preset);
// Add layout class to body
document.body.className = `layout-${config.layout}`;
}, [config]);
return (
{children}
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
```
### Loading Google Fonts
```typescript
function loadTypographyPreset(preset: string) {
const fontMap = {
professional: ['Inter:400,600,700', 'Lora:400,700'],
modern: ['Poppins:400,600,700', 'Roboto:400,700'],
elegant: ['Playfair+Display:400,700', 'Source+Sans+Pro:400,700'],
tech: ['Space+Grotesk:400,700', 'IBM+Plex+Mono:400,700'],
};
const fonts = fontMap[preset];
if (!fonts) return;
const link = document.createElement('link');
link.href = `https://fonts.googleapis.com/css2?family=${fonts.join('&family=')}&display=swap`;
link.rel = 'stylesheet';
document.head.appendChild(link);
}
```
---
## ๐ฑ Responsive Design
### Breakpoints
```css
:root {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
```
### Mobile-First Approach
```css
/* Mobile (default) */
.product-grid {
grid-template-columns: 1fr;
gap: var(--space-4);
}
/* Tablet */
@media (min-width: 768px) {
.product-grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--space-6);
}
}
/* Desktop */
@media (min-width: 1024px) {
.product-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Large Desktop */
@media (min-width: 1280px) {
.product-grid {
grid-template-columns: repeat(4, 1fr);
}
}
```
---
## โฟ Accessibility
### Focus States
```css
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
button:focus-visible {
box-shadow: 0 0 0 3px var(--color-primary-200);
}
```
### Screen Reader Support
```typescript
```
### Color Contrast
All text must meet WCAG AA standards (4.5:1 for normal text, 3:1 for large text).
---
## ๐ Performance Optimization
### CSS-in-JS vs CSS Variables
We use **CSS variables** instead of CSS-in-JS for better performance:
- โ
No runtime overhead
- โ
Instant theme switching
- โ
Better browser caching
- โ
Smaller bundle size
### Critical CSS
Inline critical CSS in ``:
```php
```
### Font Loading Strategy
```html
```
---
## ๐งช Testing
### Visual Regression Testing
```typescript
describe('Theme System', () => {
it('should apply modern layout correctly', () => {
cy.visit('/shop?theme=modern');
cy.matchImageSnapshot('shop-modern-layout');
});
it('should apply custom colors', () => {
cy.setTheme({ colors: { primary: '#FF0000' } });
cy.get('.btn-primary').should('have.css', 'background-color', 'rgb(255, 0, 0)');
});
});
```
### Accessibility Testing
```typescript
it('should meet WCAG AA standards', () => {
cy.visit('/shop');
cy.injectAxe();
cy.checkA11y();
});
```
---
## ๐ Related Documentation
- [Customer SPA Architecture](./CUSTOMER_SPA_ARCHITECTURE.md)
- [Customer SPA Settings](./CUSTOMER_SPA_SETTINGS.md)
- [Component Library](./COMPONENT_LIBRARY.md)