Files
WooNooW/woonoow-page-editor-v4-canvas-ui.md

1065 lines
33 KiB
Markdown

# WooNooW Page Editor - Enhanced UI/UX Brief (v4)
## Overview
Redesign the Page Editor from a **card-list-based interface** to a **live-canvas-based interface** that shows actual rendered content in real-time. Leverage JSON-based rendering (no iframe/MutationObserver overhead) for faster, simpler implementation.
---
## Core Philosophy
**One Mental Model: What You See Is What You Get (WYSIWYG)**
- Canvas shows the **actual rendered output** merchants will see on the front-end
- No iframe isolation → Direct component rendering → Instant visual feedback
- Edit sections on the canvas → Settings panel updates → Preview updates in real-time
- No context switching between "list view" and "edit mode"
---
## New Layout Structure
### Full-Screen Layout (No iframe, Direct React Rendering)
```
┌──────────────────────────────────────────────────────────────────┐
│ [☰] WooNooW Appearance > Pages > [About Us] [D/M] [Save] │
├─────────────────┬────────────────────────┬──────────────────────┤
│ │ │ │
│ LEFT SIDEBAR │ MAIN CANVAS │ RIGHT INSPECTOR │
│ (180px) │ (Dynamic, ~60-70%) │ (Collapse ◄) │
│ │ │ (200-280px) │
│ 📄 PAGES │ ╔════════════════════╗ │ │
│ ├─ Home │ ║ LIVE PAGE RENDER ║ │ ┌─────────────────┐ │
│ ├─ About ✓ │ ║ ║ │ │ Section Config │ │
│ └─ Contact │ ║ ┌──────────────┐ ║ │ └─────────────────┘ │
│ │ ║ │ HERO │ ║ │ │
│ 📋 TEMPLATES │ ║ │ Welcome │←──╋─┼─→ [Hero section selected]
│ ├─ Post │ ║ │ [Image] │ ║ │ │
│ └─ Portfolio │ ║ │ Join us │ ║ │ Layout: │
│ │ ║ └──────────────┘ ║ │ [Hero-Left ▼] │
│ [+ Create] │ ║ ║ │ │
│ │ ║ ┌──────────────┐ ║ │ Title: │
│ │ ║ │ IMAGE+TEXT │ ║ │ [Welcome______] │
│ │ ║ │ [IMG] [TEXT] │ ║ │ │
│ │ ║ └──────────────┘ ║ │ Image: │
│ │ ║ ║ │ [Choose Image ▼] │
│ │ ║ ┌──────────────┐ ║ │ │
│ │ ║ │ CTA │ ║ │ [Delete Section] │
│ │ ║ │ "Shop Now" │ ║ │ │
│ │ ║ └──────────────┘ ║ │ [Hide Inspector] │
│ │ ║ ║ │ │
│ │ ║ [+ Add section] ║ │ │
│ │ ╚════════════════════╝ │ │
│ │ │ │
│ │ Responsive toggle: │ │
│ │ [Desktop] [Mobile] │ │
│ │ │ │
└─────────────────┴────────────────────────┴──────────────────────┘
│ [Discard Changes] [Save Changes] [Preview in New Tab] │
└──────────────────────────────────────────────────────────────────┘
```
### Key Differences from v3
| Aspect | v3 (Card List) | v4 (Live Canvas) |
|--------|---|---|
| **Main Column** | Card list (read-only) | Live rendered sections |
| **Rendering** | Screenshot/preview | Actual React components |
| **Interactivity** | Click card → settings | Click section → settings |
| **Visual Feedback** | Static cards | Actual page appearance |
| **Preview Size** | Small box on right | Full mobile/desktop |
| **Edit Speed** | Click → settings → change | See change instantly |
| **Context** | List + Settings + Preview | One coherent interface |
---
## Canvas Rendering System (JSON → React Components)
### Architecture: Zero-Iframe, Direct Component Rendering
```
┌──────────────────────────────────────────┐
│ Page Editor State (Redux/Zustand) │
│ { │
│ sections: [ │
│ { type: "hero", props: {...} }, │
│ { type: "content", props: {...} } │
│ ], │
│ selectedSectionId: "sec-1" │
│ } │
└──────────────────────────────────────────┘
↓ (Real-time)
┌──────────────────────────────────────────┐
│ CanvasRenderer Component │
│ (Renders JSON → React components) │
│ │
│ { sections.map((sec) => │
│ <SectionComponent │
│ data={sec} │
│ isSelected={...} │
│ onSelect={...} │
│ /> │
│ )} │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ Live Canvas Preview │
│ (Shows exact front-end appearance) │
│ │
│ ╔═══════════════════════════════════╗ │
│ ║ HERO SECTION ║ │
│ ║ (Selected, blue border) ║ │
│ ╚═══════════════════════════════════╝ │
│ │
│ ┌───────────────────────────────────┐ │
│ │ IMAGE + TEXT │ │
│ │ (Not selected, gray border) │ │
│ └───────────────────────────────────┘ │
└──────────────────────────────────────────┘
```
### Why This Is Simpler Than Page Builder Plugins
**Page Builder Plugins (e.g., Elementor, Beaver):**
- Use iframes to sandbox editing environment
- Must observe DOM mutations to track changes
- Heavy JavaScript overhead
- Slow preview updates
**WooNooW Approach:**
- ✅ JSON state → React components (unidirectional)
- ✅ No iframe = no sandbox overhead
- ✅ No mutation observers = simple state management
- ✅ Change state → re-render → instant preview
- ✅ Single component tree (not isolated)
---
## Canvas Interaction Model
### Section States
**At Rest (Not Selected):**
```
┌─────────────────────────────────┐
│ Hero Section │ ← Subtle border (gray)
│ [Actual rendered content] │ ← Hover shows subtle highlight
│ "Welcome to our store" │ ← Cursor change on hover
│ [Featured image] │
└─────────────────────────────────┘
```
**On Hover:**
```
╔════════════════════════════════╗
║ Hero Section ║ ← Blue border appears
║ [Actual rendered content] ║ ← Handles show: [↑][↓][◆]
║ "Welcome to our store" ║ ← "Click to edit" hint
║ [Featured image] ║
║ Drag handle: [:::] ║ ← Reorder handle visible
╚════════════════════════════════╝
```
**On Click (Selected):**
```
╔════════════════════════════════╗
║ Hero Section ║ ← Bold blue border
║ [Actual rendered content] ║ ← Selection handles
║ "Welcome to our store" ║ ← Full edit mode
║ [Featured image] ║ ← Right panel populated
║ [≣ Move ↑ ↓] [⊕ Duplicate] [✕]║ ← Context menu
╚════════════════════════════════╝
↓ Right panel updates:
Section Config
├─ Layout: [Hero-Left ▼]
├─ Color: [Primary ▼]
├─ Title: [Welcome__________]
└─ Image: [Choose Image ▼]
```
### Interaction Handlers
```typescript
// Canvas Section Component
<CanvasSection
section={section}
isSelected={selectedId === section.id}
// Selection
onClick={() => setSelectedSection(section.id)}
onMouseEnter={() => setHoveredId(section.id)}
onMouseLeave={() => setHoveredId(null)}
// Reordering
onDragStart={() => initDragReorder(section.id)}
onDragOver={(e) => e.preventDefault()}
onDrop={() => performDragReorder(section.id)}
// Quick actions
onDuplicate={() => duplicateSection(section.id)}
onDelete={() => deleteSection(section.id)}
onMoveUp={() => moveSection(section.id, 'up')}
onMoveDown={() => moveSection(section.id, 'down')}
/>
```
---
## Inspector Panel (Right Side)
### Inspector Behavior
**Collapsible:**
```
┌──────────────────┐
│ [Inspector] [◄] │ ← Click to collapse (inspector slides out)
│ │
│ Section Config │
│ ├─ Layout: [▼] │
│ ├─ Title: [____] │
│ └─ Image: [▼] │
└──────────────────┘
When collapsed:
[Canvas takes 70% width instead of 60%]
```
**Responsive to Selection:**
```
No section selected:
┌──────────────────┐
│ Page Settings │
├─ Title: [About] │
├─ Slug: [/about] │
└─ SEO: [Manage→] │
└──────────────────┘
Hero section selected:
┌──────────────────┐
│ Section Config │
│ Type: Hero │
├─ Layout: [▼] │
├─ Title: [____] │
└─ Image: [▼] │
└──────────────────┘
Content section selected:
┌──────────────────┐
│ Section Config │
│ Type: Content │
├─ Source: [▼] │ ← Dynamic: post_content
│ ○ Static text │
│ ◉ Post Body │
└─ [Dynamic ◆] │
└──────────────────┘
```
### Inspector Field Types
**For Static Fields:**
```
Title Field
[Text input____________]
Description
[Large textarea___
_________________
_________________]
Image
[Choose from Media] or [Upload new]
[Preview thumbnail]
```
**For Dynamic Fields (CPT Templates):**
```
Title Source
○ Static text: [__________]
◉ Dynamic: [Post Title ▼]
• Post Title
• Post Excerpt
• Custom Field X
[◆ Dynamic] indicator shown
```
**Layout Controls:**
```
Layout Variant
[Hero-Left ▼]
├─ Hero-Left
├─ Hero-Right
├─ Hero-Center
└─ Hero-Full
Color Scheme
[Primary ▼]
├─ Primary
├─ Secondary
├─ Accent
└─ Dark
```
---
## Canvas UI Features
### Section Card Appearance
**In Canvas:**
```
Rendered like the actual front-end:
╔═══════════════════════════════════╗
║ HERO SECTION ║ ← Looks like real page
║ ║
║ Welcome to our store ║ ← Actual text
║ ┌─────────────────────────────┐ ║
║ │ [Featured Image] │ ║ ← Actual image
║ │ (500x300px) │ ║
║ └─────────────────────────────┘ ║
║ ║
║ Join our community today ║ ← Button
║ [SHOP NOW →] ║
║ ║
╚═══════════════════════════════════╝
```
NOT like current cards:
```
❌ WRONG (Current):
┌─────────────────────────┐
│ Hero [default] │
│ [↑] [↓] [x] │
│ ─────────────────────── │
```
### Add Section UI
**Floating Button Below Sections:**
```
Canvas shows sections:
╔═══════════════════════════════════╗
║ HERO SECTION ║
╚═══════════════════════════════════╝
[+ Add section] ← Click to open section picker
├─ Hero
├─ Image + Text
├─ Feature Grid
├─ Related Items
├─ CTA Banner
└─ Content
```
Or inline:
```
╔═══════════════════════════════════╗
║ CTA BANNER ║
╚═══════════════════════════════════╝
[+ Add section below] ← Click here
```
### Responsive Preview Toggle
**Canvas shows device toggle:**
```
[Desktop] [Mobile] ← Toggle between previews
Desktop (full width, e.g., 1024px)
╔═══════════════════════════════════╗
║ Page at desktop width ║
╚═══════════════════════════════════╝
Mobile (narrow width, e.g., 375px)
╔════════════╗
║ Page at ║
║ mobile ║
║ width ║
╚════════════╝
```
---
## Data Flow & State Management
### Single Source of Truth
```
┌─────────────────────────────────┐
│ Editor State (Zustand/Redux) │
│ │
│ { │
│ pageId: "about", │
│ pageType: "page", │
│ title: "About Us", │
│ slug: "about", │
│ sections: [ │
│ { │
│ id: "sec-1", │
│ type: "hero", │
│ layoutVariant: "left", │
│ props: { │
│ title: { │
│ type: "static", │
│ value: "Welcome" │
│ }, │
│ image: { │
│ type: "static", │
│ value: "/img.jpg" │
│ } │
│ } │
│ } │
│ ], │
│ selectedSectionId: "sec-1", │
│ hasUnsavedChanges: true │
│ } │
└─────────────────────────────────┘
↓ (Consumed by):
├─ CanvasRenderer
│ └─ Renders sections to UI
├─ InspectorPanel
│ └─ Shows/edits selected section
├─ PreviewToggle
│ └─ Desktop/Mobile responsive
└─ SaveButton
└─ POST to /wp-json/woonoow/v1/pages/{slug}
```
### Real-Time Updates
```
User changes field in Inspector:
[Title input] → onChange event
Store update:
sections[0].props.title.value = "New Title"
React re-renders:
<CanvasRenderer sections={sections} />
Canvas shows updated title instantly
(No save needed, live preview)
```
---
## Component Structure
### Main Components
```
<PageEditor> ← Top container
├─ <Sidebar> ← Left navigation
│ ├─ <PagesList> ← Structural pages
│ └─ <TemplatesList> ← CPT templates
├─ <Canvas> ← Main workspace
│ ├─ <ResponsiveToggle> ← Desktop/Mobile
│ └─ <CanvasRenderer> ← Renders sections
│ ├─ <CanvasSection> ← Each section
│ │ ├─ <HeroRenderer> ← Section-specific
│ │ ├─ <ContentRenderer>
│ │ ├─ <FeatureGridRenderer>
│ │ └─ ...
│ └─ <AddSectionButton>
├─ <Inspector> ← Right panel
│ ├─ <PageSettings> ← When no section selected
│ ├─ <SectionSettings> ← When section selected
│ └─ <PreviewToggle> ← Collapse button
└─ <BottomBar> ← Actions
├─ <DiscardButton>
├─ <SaveButton>
└─ <PreviewButton>
```
### Canvas Section Component Pattern
```tsx
// Each section type follows same pattern:
function HeroSectionRenderer({ section, isSelected, onSelect }) {
return (
<CanvasSection
isSelected={isSelected}
onClick={onSelect}
className="section-hero"
>
{/* Renders actual content */}
<h1>{section.props.title.value}</h1>
<img src={section.props.image.value} />
<p>{section.props.subtitle.value}</p>
{/* Selection UI overlays */}
{isSelected && (
<SelectionOverlay>
<DragHandle />
<ContextMenu />
</SelectionOverlay>
)}
</CanvasSection>
);
}
// Same for Content, FeatureGrid, CTA, etc.
```
---
## Selection & Interaction Feedback
### Visual Selection Indicators
**Unselected:**
```
┌─────────────────────────────┐
│ Hero Section │ ← Gray border (1px)
│ [Content] │ ← Normal opacity
└─────────────────────────────┘
```
**Hovered:**
```
┌─────────────────────────────┐
│ Hero Section │ ← Light blue border (1px)
│ [Content] │ ← Subtle shadow
│ [↑ ↓] │ ← Move handles appear
└─────────────────────────────┘
```
**Selected:**
```
╔═════════════════════════════╗
║ Hero Section ║ ← Bold blue border (2px)
║ [Content] ║ ← Highlight background
║ ║
║ [≣] [↑↓] [⊕] [✕] ║ ← Full action menu
╚═════════════════════════════╝
```
### Drag & Drop Reordering
```
// User drags section up:
Before:
╔═══════════════════╗
║ HERO (drag me) ║ ← Dragging
║ [↑ ↓] ║
╚═══════════════════╝
╔═══════════════════╗
║ IMAGE+TEXT ║ ← Drop zone appears
║ [↑ ↓] ║
╚═══════════════════╝
After drop:
╔═══════════════════╗
║ HERO ║
╚═══════════════════╝
╔═══════════════════╗
║ IMAGE+TEXT ║
╚═══════════════════╝
```
---
## Save & Persistence Flow
### Auto-Save vs Manual Save
**Current approach: Manual Save**
```
User edits → Real-time canvas update (no server call)
[Save Changes] button → POST to API → Save to wp_options/postmeta
[Discard Changes] button → Reset to last saved state
```
**Optional: Auto-Save (Future)**
```
User edits → Real-time canvas update
[Unsaved changes indicator] appears
Auto-save after 3 seconds of inactivity
Or manual save with [Save] button
```
### Save Endpoints
```
POST /wp-json/woonoow/v1/pages/about
{
"sections": [...]
}
→ Saves to wp_postmeta (_wn_page_structure)
POST /wp-json/woonoow/v1/templates/post
{
"sections": [...]
}
→ Saves to wp_options (wn_template_post)
```
---
## Responsive Canvas Behavior
### Desktop View (Default)
```
Full-width canvas showing page at 1024px width
┌─────────────────────────────────┐
│ [Desktop] [Mobile] │
│ │
│ Canvas width: 1024px │
│ Shows full page layout │
└─────────────────────────────────┘
```
### Mobile View
```
Canvas showing page at 375px width
┌──────────┐
│[D][M] │
│ │
│ Canvas │
│ width: │
│ 375px │
│ │
│ Shows │
│ mobile │
│ layout │
│ │
└──────────┘
```
### Scroll Behavior
- Canvas is scrollable (overflow-y: auto)
- Sections stack vertically
- Inspector panel stays fixed (top-right)
- Bottom action bar always visible
---
## Merchant Experience Flow
### Typical Edit Session
1. **Select page from sidebar**
```
Sidebar: [About Us] ← Click
Canvas loads page structure
Inspector shows page settings
```
2. **Click section to edit**
```
Canvas: [Hero section] ← Click
Section highlights (blue border)
Inspector updates: Section Config for Hero
```
3. **Change fields in Inspector**
```
Inspector: Title = [Welcome_____] ← Type "Welcome to us"
Canvas updates instantly
No server call needed yet
```
4. **Reorder sections (optional)**
```
Canvas: Drag [Hero] above [Image+Text]
Sections reorder instantly
```
5. **Add new section**
```
Canvas: [+ Add section] ← Click
Section picker popup
Select "CTA Banner"
New section appears at bottom
Inspector shows CTA settings
```
6. **Save all changes**
```
Bottom bar: [Save Changes] ← Click
POST to /wp-json/woonoow/v1/pages/about
Sections saved to database
"Page saved" toast notification
```
---
## Implementation Phases (Enhanced)
### Phase 1: Canvas Rendering System (Priority)
- [ ] Refactor state from Redux/Zustand (unified store)
- [ ] Build `CanvasRenderer` component
- Maps sections array to React components
- Zero-iframe approach (direct rendering)
- Responsive device simulation (CSS media queries)
- [ ] Build section renderers (Hero, Content, Feature Grid, CTA, Related)
- Render actual content, not placeholder cards
- Show dynamic placeholders with [◆] badges
- [ ] Add responsive toggle (Desktop/Mobile)
- [ ] Test real-time re-rendering on state changes
### Phase 2: Canvas Interaction (Click, Hover, Select)
- [ ] Click handler → Set selected section in state
- [ ] Hover handler → Show selection handles
- [ ] Visual feedback (borders, shadows, highlights)
- [ ] Context menu (Move up, Down, Duplicate, Delete)
- [ ] Add section button (opens section picker)
### Phase 3: Inspector Panel (Settings)
- [ ] Collapse/expand button
- [ ] Page settings vs Section settings
- [ ] Dynamic field type detection
- Static: text input, textarea, image picker
- Dynamic: dropdown with available sources
- [ ] Real-time sync (change field → canvas updates)
### Phase 4: Drag & Drop Reordering
- [ ] Drag start/over/drop handlers
- [ ] Visual drag feedback (opacity, shadow)
- [ ] Reorder state updates
- [ ] Test with multiple sections
### Phase 5: Polish & Launch
- [ ] Save/Discard buttons
- [ ] Unsaved changes warning
- [ ] Toast notifications (saved, error)
- [ ] Keyboard shortcuts (ESC to deselect, etc.)
- [ ] Mobile-friendly inspector (for tablet editing)
- [ ] Performance optimization (memoization, lazy rendering)
---
## Code Example: Canvas Component Structure
```tsx
// pages/editor/PageEditor.tsx
export function PageEditor() {
const [pageState, dispatch] = useReducer(pageReducer, initialState);
const [selectedSectionId, setSelectedSectionId] = useState(null);
const [deviceMode, setDeviceMode] = useState('desktop');
const handleSectionSelect = (sectionId) => {
setSelectedSectionId(sectionId);
};
const handleSectionDelete = (sectionId) => {
dispatch({
type: 'DELETE_SECTION',
payload: { sectionId }
});
};
const handleSectionDuplicate = (sectionId) => {
dispatch({
type: 'DUPLICATE_SECTION',
payload: { sectionId }
});
};
const handleFieldChange = (sectionId, fieldPath, value) => {
dispatch({
type: 'UPDATE_SECTION_PROP',
payload: { sectionId, fieldPath, value }
});
};
const handleSave = async () => {
const response = await fetch(`/wp-json/woonoow/v1/pages/${pageState.slug}`, {
method: 'POST',
body: JSON.stringify({ sections: pageState.sections })
});
// Show toast notification
};
return (
<div className="page-editor">
<Sidebar
pages={pageState.pages}
selectedPageId={pageState.id}
/>
<Canvas
sections={pageState.sections}
selectedSectionId={selectedSectionId}
deviceMode={deviceMode}
onSelectSection={handleSectionSelect}
onDeleteSection={handleSectionDelete}
onDuplicateSection={handleSectionDuplicate}
onFieldChange={handleFieldChange}
/>
<Inspector
pageState={pageState}
selectedSectionId={selectedSectionId}
onFieldChange={handleFieldChange}
onDeviceChange={setDeviceMode}
/>
<BottomBar
onSave={handleSave}
onDiscard={() => location.reload()}
/>
</div>
);
}
// components/Canvas.tsx
export function Canvas({
sections,
selectedSectionId,
deviceMode,
onSelectSection,
onFieldChange,
...handlers
}) {
return (
<div className="canvas-container">
<DeviceToggle mode={deviceMode} onChange={setDeviceMode} />
<div className={`canvas-viewport canvas-${deviceMode}`}>
{sections.map((section) => (
<CanvasSection
key={section.id}
section={section}
isSelected={selectedSectionId === section.id}
onClick={() => onSelectSection(section.id)}
{...handlers}
>
<SectionRenderer
section={section}
onFieldChange={(field, value) =>
onFieldChange(section.id, field, value)
}
/>
</CanvasSection>
))}
<AddSectionButton onClick={openSectionPicker} />
</div>
</div>
);
}
// components/CanvasSection.tsx
export function CanvasSection({ section, isSelected, children, ...handlers }) {
return (
<div
className={`canvas-section ${isSelected ? 'selected' : ''}`}
onClick={handlers.onClick}
onDragStart={handlers.onDragStart}
onDragOver={handlers.onDragOver}
onDrop={handlers.onDrop}
>
{/* Actual rendered content */}
{children}
{/* Selection overlay */}
{isSelected && (
<SelectionOverlay>
<DragHandle />
<ContextMenu
onDuplicate={() => handlers.onDuplicate(section.id)}
onDelete={() => handlers.onDelete(section.id)}
/>
</SelectionOverlay>
)}
</div>
);
}
// components/Inspector.tsx
export function Inspector({ pageState, selectedSectionId, onFieldChange }) {
if (!selectedSectionId) {
return <PageSettings pageState={pageState} />;
}
const section = pageState.sections.find(s => s.id === selectedSectionId);
return (
<div className="inspector-panel">
<div className="section-config">
<h3>{section.type}</h3>
{Object.entries(section.props).map(([fieldName, fieldData]) => (
<InspectorField
key={fieldName}
fieldName={fieldName}
fieldData={fieldData}
onChange={(value) =>
onFieldChange(selectedSectionId, `props.${fieldName}.value`, value)
}
/>
))}
<button onClick={() => handleDelete(selectedSectionId)}>
Delete Section
</button>
</div>
<Canvas preview />
</div>
);
}
// components/InspectorField.tsx
export function InspectorField({ fieldName, fieldData, onChange }) {
if (fieldData.type === 'static') {
return (
<div className="inspector-field">
<label>{fieldName}</label>
<input
type="text"
value={fieldData.value}
onChange={(e) => onChange(e.target.value)}
/>
</div>
);
}
if (fieldData.type === 'dynamic') {
return (
<div className="inspector-field">
<label>{fieldName} <span className="dynamic-badge">[◆]</span></label>
<select value={fieldData.source} onChange={(e) => onChange(e.target.value)}>
<option value="post_title">Post Title</option>
<option value="post_content">Post Content</option>
<option value="post_featured_image">Featured Image</option>
{/* More options */}
</select>
</div>
);
}
}
```
---
## CSS Utilities for Canvas
```css
/* Canvas container */
.canvas-container {
flex: 1;
background: #f5f5f5;
overflow-y: auto;
padding: 20px;
}
/* Device modes */
.canvas-viewport {
background: white;
margin: 0 auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.canvas-desktop {
max-width: 1024px;
}
.canvas-mobile {
max-width: 375px;
}
/* Sections */
.canvas-section {
position: relative;
padding: 0;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-bottom: 16px;
transition: all 0.2s ease;
}
.canvas-section:hover {
border-color: #90caf9;
box-shadow: 0 2px 8px rgba(144, 202, 249, 0.3);
}
.canvas-section.selected {
border-color: #1976d2;
border-width: 2px;
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
}
/* Selection overlay */
.selection-overlay {
position: absolute;
top: 0;
right: 0;
display: flex;
gap: 8px;
padding: 8px;
background: rgba(25, 118, 210, 0.05);
border-radius: 4px;
opacity: 0;
}
.canvas-section.selected .selection-overlay {
opacity: 1;
}
/* Dynamic badge */
.dynamic-badge {
display: inline-block;
width: 14px;
height: 14px;
background: #ff9800;
color: white;
border-radius: 50%;
text-align: center;
font-size: 10px;
line-height: 14px;
margin-left: 4px;
}
```
---
## Benefits of This Approach
| Benefit | Explanation |
|---------|---|
| **WYSIWYG** | See actual page appearance while editing |
| **No iframe overhead** | Direct component rendering = faster updates |
| **No MutationObserver** | JSON → React = clean state management |
| **Instant feedback** | Change field → canvas updates in milliseconds |
| **Simpler code** | No iframe communication, no sandbox complexity |
| **Better mobile edit** | Responsive canvas shows mobile view directly |
| **Easier debugging** | Single React component tree, standard dev tools |
| **Faster iteration** | Less complexity = faster implementation |
| **Merchant friendly** | See exactly what customers will see |
---
## Next Steps
1. ✅ Refactor state management (unified store)
2. ✅ Build CanvasRenderer component
3. ✅ Implement section click/hover interactions
4. ✅ Connect Inspector panel to canvas
5. ✅ Add drag-and-drop reordering
6. ✅ Test responsive device modes
7. ✅ Implement save/discard flow
8. ✅ Polish UX (animations, feedback)