Complete integration of Holly/The Catalyst into brand identity: DESIGN BIBLE UPDATED (v2.0): - Added Holly's Arcane palette (purple family) - Primary: #A855F7 (vibrant purple) - Secondary: #9D4EDD (bright violet) - Accent: #C77DFF (light amethyst) - Dark: #7F00FF (deep purple) - Updated core philosophy: Fire + Frost + Foundation - Added Trinity gradients (Fire+Frost+Arcane combinations) - Updated brand identity to reflect The Trinity - Replaced outdated color palettes with current system HOLLY'S COLOR PALETTE DEFINED: - Arcane Primary: #A855F7 (main branding) - Arcane Secondary: #9D4EDD (highlights) - Arcane Accent: #C77DFF (soft touches) - Arcane Dark: #7F00FF (text/borders) - Arcane Gradient: linear-gradient(135deg, #A855F7 0%, #C77DFF 100%) - Arcane Storm: linear-gradient(135deg, #7F00FF 0%, #A855F7 50%, #C77DFF 100%) GHOST PAGE BUILDER ENHANCED: - Added complete Arcane color system to CSS - New Trinity Gradient: Fire+Arcane+Frost (3-element gradient) - Updated templates to showcase all three elements: - Trinity Layout (3 columns: Fire/Arcane/Frost) - Trinity Card Grid (showcasing all three founders) - Added Arcane utility classes (.arcane-text, .arcane-gradient, .arcane-border) - Updated header gradient to include purple (Fire→Arcane→Frost) - Updated footer color reference to show all trinity colors - Tool now in git: tools/ghost-page-builder/ THE TRINITY NOW COMPLETE: - 🔥 Fire (Meg/Orange): Passion, community, warmth - ⚡ Arcane (Holly/Purple): Creative transformation, building, foundation - ❄️ Frost (Michael/Teal): Precision, technical excellence, strategy FILES MODIFIED: - docs/planning/design-bible.md (v1.2 → v2.0) - Updated philosophy, color palettes, brand identity - Added comprehensive Trinity color system - Documented all gradients and usage patterns FILES CREATED: - tools/ghost-page-builder/ghost-page-builder.jsx (350+ lines) - React artifact with complete Trinity CSS - Sample templates showcasing all three elements - Color reference footer with hex codes - tools/ghost-page-builder/README.md - Complete usage documentation - Trinity CSS class reference - Technical specifications ACCESSIBILITY: - All color combinations maintain WCAG contrast ratios - Trinity gradient provides smooth visual transitions - Each element color distinct and recognizable NEXT STEPS: 1. Test Ghost Page Builder with new Trinity templates 2. Use Trinity colors across all Ghost pages 3. Update social media graphics with Trinity branding 4. Holly's build showcases use Arcane colors Signed-off-by: Chronicler #39 <claude@firefrostgaming.com>
451 lines
14 KiB
JavaScript
451 lines
14 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
|
||
// Fire/Frost/Arcane CSS definitions (injected into preview)
|
||
const FIRE_FROST_CSS = `
|
||
:root {
|
||
/* Fire Colors (Meg/The Emissary) */
|
||
--fire-primary: #FF6B35;
|
||
--fire-secondary: #F7931E;
|
||
--fire-accent: #FFA500;
|
||
|
||
/* Frost Colors (Michael/The Wizard) */
|
||
--frost-primary: #4ECDC4;
|
||
--frost-secondary: #45B7D1;
|
||
--frost-accent: #00CED1;
|
||
|
||
/* Arcane Colors (Holly/The Catalyst) */
|
||
--arcane-primary: #A855F7;
|
||
--arcane-secondary: #9D4EDD;
|
||
--arcane-accent: #C77DFF;
|
||
--arcane-dark: #7F00FF;
|
||
|
||
/* Neutral Colors */
|
||
--neutral-dark: #1a1a1a;
|
||
--neutral-gray: #6b7280;
|
||
--neutral-light: #f3f4f6;
|
||
}
|
||
|
||
/* Fire/Frost/Arcane Gradient Utilities */
|
||
.fire-frost-gradient {
|
||
background: linear-gradient(135deg, #FF6B35 0%, #4ECDC4 100%);
|
||
}
|
||
|
||
.fire-gradient {
|
||
background: linear-gradient(135deg, #FF6B35 0%, #FFA500 100%);
|
||
}
|
||
|
||
.frost-gradient {
|
||
background: linear-gradient(135deg, #4ECDC4 0%, #00CED1 100%);
|
||
}
|
||
|
||
.arcane-gradient {
|
||
background: linear-gradient(135deg, #A855F7 0%, #C77DFF 100%);
|
||
}
|
||
|
||
.trinity-gradient {
|
||
background: linear-gradient(135deg, #FF6B35 0%, #A855F7 50%, #4ECDC4 100%);
|
||
}
|
||
|
||
.arcane-storm-gradient {
|
||
background: linear-gradient(135deg, #7F00FF 0%, #A855F7 50%, #C77DFF 100%);
|
||
}
|
||
|
||
/* Typography Colors */
|
||
.fire-text {
|
||
color: var(--fire-primary);
|
||
}
|
||
|
||
.frost-text {
|
||
color: var(--frost-primary);
|
||
}
|
||
|
||
.arcane-text {
|
||
color: var(--arcane-primary);
|
||
}
|
||
|
||
/* Border Utilities */
|
||
.fire-border {
|
||
border-color: var(--fire-primary);
|
||
}
|
||
|
||
.frost-border {
|
||
border-color: var(--frost-primary);
|
||
}
|
||
|
||
.arcane-border {
|
||
border-color: var(--arcane-primary);
|
||
}
|
||
|
||
/* Common Ghost page styles */
|
||
.gh-content {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
.gh-content h1 {
|
||
font-size: 3rem;
|
||
font-weight: 700;
|
||
margin-bottom: 1.5rem;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.gh-content h2 {
|
||
font-size: 2rem;
|
||
font-weight: 600;
|
||
margin: 2rem 0 1rem;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.gh-content p {
|
||
font-size: 1.125rem;
|
||
line-height: 1.75;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.gh-content a {
|
||
color: var(--fire-primary);
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.gh-content a:hover {
|
||
color: var(--frost-primary);
|
||
}
|
||
`;
|
||
|
||
// Sample templates with Ghost wrapper classes
|
||
const SAMPLE_TEMPLATES = {
|
||
blank: {
|
||
name: 'Blank Page',
|
||
html: '<h1>Page Title</h1>\n<p>Start writing your content here...</p>'
|
||
},
|
||
simple: {
|
||
name: 'Simple Page',
|
||
html: `<h1>Welcome to Firefrost Gaming</h1>
|
||
<p>This is a simple page template with basic structure.</p>
|
||
|
||
<h2>Section Heading</h2>
|
||
<p>Add your content here. This template includes proper Ghost content classes for consistent styling.</p>
|
||
|
||
<p>You can add more paragraphs, headings, and content as needed.</p>`
|
||
},
|
||
twoColumn: {
|
||
name: 'Trinity Layout (3 Columns)',
|
||
html: `<h1>The Trinity</h1>
|
||
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 2rem; margin: 2rem 0;">
|
||
<div>
|
||
<h2 class="fire-text">🔥 Fire Path</h2>
|
||
<p><strong>Meg - The Emissary</strong></p>
|
||
<p>Community-focused content goes here. Passionate, warm, welcoming.</p>
|
||
<ul>
|
||
<li>Community events</li>
|
||
<li>Player stories</li>
|
||
<li>Social activities</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div>
|
||
<h2 class="arcane-text">⚡ Arcane Path</h2>
|
||
<p><strong>Holly - The Catalyst</strong></p>
|
||
<p>Creative, building-focused content. Innovation, transformation, foundation.</p>
|
||
<ul>
|
||
<li>Build showcases</li>
|
||
<li>Creative projects</li>
|
||
<li>World design</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div>
|
||
<h2 class="frost-text">❄️ Frost Path</h2>
|
||
<p><strong>Michael - The Wizard</strong></p>
|
||
<p>Technical, precise content goes here. Cool, calculated, systematic.</p>
|
||
<ul>
|
||
<li>Server specifications</li>
|
||
<li>Technical guides</li>
|
||
<li>Performance metrics</li>
|
||
</ul>
|
||
</div>
|
||
</div>`
|
||
},
|
||
cardGrid: {
|
||
name: 'Trinity Card Grid',
|
||
html: `<h1>The Trinity - Fire + Arcane + Frost</h1>
|
||
<p>Showcase the three elemental forces.</p>
|
||
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; margin: 2rem 0;">
|
||
<div style="border: 2px solid var(--fire-primary); border-radius: 8px; padding: 1.5rem;">
|
||
<h3 class="fire-text">🔥 Fire - The Emissary</h3>
|
||
<p>Passion, community, warmth. Meg brings the heart and the people.</p>
|
||
</div>
|
||
|
||
<div style="border: 2px solid var(--arcane-primary); border-radius: 8px; padding: 1.5rem;">
|
||
<h3 class="arcane-text">⚡ Arcane - The Catalyst</h3>
|
||
<p>Creative transformation, building, innovation. Holly brings the foundation.</p>
|
||
</div>
|
||
|
||
<div style="border: 2px solid var(--frost-primary); border-radius: 8px; padding: 1.5rem;">
|
||
<h3 class="frost-text">❄️ Frost - The Wizard</h3>
|
||
<p>Precision, technical excellence, strategy. Michael brings the architecture.</p>
|
||
</div>
|
||
</div>`
|
||
}
|
||
};
|
||
|
||
// Helper function to generate preview HTML for iframe srcdoc
|
||
const generatePreviewHtml = (htmlContent) => {
|
||
return `<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Ghost Preview</title>
|
||
|
||
<!-- Ghost Theme CSS (Source v1.5.2) -->
|
||
<link rel="stylesheet" href="https://firefrostgaming.com/assets/built/screen.css">
|
||
|
||
<!-- Fire/Frost Custom CSS -->
|
||
<style>${FIRE_FROST_CSS}</style>
|
||
</head>
|
||
<body class="gh-body" style="margin: 0; padding: 0; background: white;">
|
||
<div class="gh-content">
|
||
${htmlContent}
|
||
</div>
|
||
</body>
|
||
</html>`;
|
||
};
|
||
|
||
export default function GhostPageBuilder() {
|
||
// Two-state pattern: instant input + debounced preview
|
||
const [htmlInput, setHtmlInput] = useState('');
|
||
const [debouncedHtml, setDebouncedHtml] = useState('');
|
||
const [viewport, setViewport] = useState('desktop');
|
||
const [copied, setCopied] = useState(false);
|
||
|
||
// Load saved draft from localStorage on mount
|
||
useEffect(() => {
|
||
const saved = localStorage.getItem('firefrost_ghost_draft');
|
||
if (saved) {
|
||
setHtmlInput(saved);
|
||
setDebouncedHtml(saved);
|
||
} else {
|
||
// Start with simple template if no saved draft
|
||
setHtmlInput(SAMPLE_TEMPLATES.simple.html);
|
||
setDebouncedHtml(SAMPLE_TEMPLATES.simple.html);
|
||
}
|
||
}, []);
|
||
|
||
// Debounce preview updates and auto-save to localStorage
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => {
|
||
setDebouncedHtml(htmlInput);
|
||
localStorage.setItem('firefrost_ghost_draft', htmlInput);
|
||
}, 500);
|
||
|
||
return () => clearTimeout(timer);
|
||
}, [htmlInput]);
|
||
|
||
// Tab key handler for textarea
|
||
const handleKeyDown = (e) => {
|
||
if (e.key === 'Tab') {
|
||
e.preventDefault();
|
||
|
||
const start = e.target.selectionStart;
|
||
const end = e.target.selectionEnd;
|
||
|
||
// Insert two spaces at cursor position
|
||
const newValue = htmlInput.substring(0, start) + " " + htmlInput.substring(end);
|
||
|
||
setHtmlInput(newValue);
|
||
|
||
// Move cursor after inserted spaces (wait for React state update)
|
||
setTimeout(() => {
|
||
e.target.selectionStart = e.target.selectionEnd = start + 2;
|
||
}, 0);
|
||
}
|
||
};
|
||
|
||
// Copy HTML to clipboard
|
||
const handleCopy = async () => {
|
||
try {
|
||
await navigator.clipboard.writeText(htmlInput);
|
||
setCopied(true);
|
||
setTimeout(() => setCopied(false), 2000);
|
||
} catch (err) {
|
||
console.error('Failed to copy:', err);
|
||
}
|
||
};
|
||
|
||
// Load sample template
|
||
const loadTemplate = (templateKey) => {
|
||
setHtmlInput(SAMPLE_TEMPLATES[templateKey].html);
|
||
};
|
||
|
||
// Clear editor
|
||
const handleReset = () => {
|
||
if (confirm('Clear the editor? Your current work will be lost.')) {
|
||
setHtmlInput('');
|
||
localStorage.removeItem('firefrost_ghost_draft');
|
||
}
|
||
};
|
||
|
||
// Viewport dimensions
|
||
const viewportWidths = {
|
||
desktop: '100%',
|
||
tablet: '768px',
|
||
mobile: '375px'
|
||
};
|
||
|
||
return (
|
||
<div className="flex flex-col h-screen bg-gray-50">
|
||
{/* Header */}
|
||
<div className="bg-gradient-to-r from-orange-500 via-purple-500 to-teal-400 text-white p-4 shadow-lg">
|
||
<h1 className="text-2xl font-bold">🔥⚡❄️ Ghost Page Builder</h1>
|
||
<p className="text-sm opacity-90">Live preview with Fire + Arcane + Frost CSS</p>
|
||
</div>
|
||
|
||
{/* Toolbar */}
|
||
<div className="bg-white border-b border-gray-200 p-3 flex items-center gap-4 flex-wrap">
|
||
{/* Sample Templates Dropdown */}
|
||
<div className="flex items-center gap-2">
|
||
<label className="text-sm font-medium text-gray-700">Template:</label>
|
||
<select
|
||
onChange={(e) => loadTemplate(e.target.value)}
|
||
className="px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-orange-500"
|
||
>
|
||
<option value="">-- Select Template --</option>
|
||
{Object.entries(SAMPLE_TEMPLATES).map(([key, template]) => (
|
||
<option key={key} value={key}>{template.name}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{/* Viewport Toggle */}
|
||
<div className="flex items-center gap-2">
|
||
<label className="text-sm font-medium text-gray-700">Viewport:</label>
|
||
<div className="flex gap-1">
|
||
<button
|
||
onClick={() => setViewport('desktop')}
|
||
className={`px-3 py-1.5 text-sm rounded ${
|
||
viewport === 'desktop'
|
||
? 'bg-orange-500 text-white'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
Desktop
|
||
</button>
|
||
<button
|
||
onClick={() => setViewport('tablet')}
|
||
className={`px-3 py-1.5 text-sm rounded ${
|
||
viewport === 'tablet'
|
||
? 'bg-orange-500 text-white'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
Tablet
|
||
</button>
|
||
<button
|
||
onClick={() => setViewport('mobile')}
|
||
className={`px-3 py-1.5 text-sm rounded ${
|
||
viewport === 'mobile'
|
||
? 'bg-orange-500 text-white'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
Mobile
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Spacer */}
|
||
<div className="flex-1"></div>
|
||
|
||
{/* Action Buttons */}
|
||
<button
|
||
onClick={handleCopy}
|
||
className="px-4 py-1.5 bg-teal-500 text-white rounded hover:bg-teal-600 text-sm font-medium transition-colors"
|
||
>
|
||
{copied ? '✓ Copied!' : 'Copy HTML'}
|
||
</button>
|
||
|
||
<button
|
||
onClick={handleReset}
|
||
className="px-4 py-1.5 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 text-sm font-medium transition-colors"
|
||
>
|
||
Reset
|
||
</button>
|
||
</div>
|
||
|
||
{/* Main Content: Split Pane */}
|
||
<div className="flex-1 flex overflow-hidden">
|
||
{/* Left: Editor */}
|
||
<div className="w-1/2 flex flex-col border-r border-gray-200">
|
||
<div className="bg-gray-800 text-white px-4 py-2 text-sm font-medium">
|
||
HTML Editor
|
||
</div>
|
||
<textarea
|
||
value={htmlInput}
|
||
onChange={(e) => setHtmlInput(e.target.value)}
|
||
onKeyDown={handleKeyDown}
|
||
className="flex-1 p-4 font-mono text-sm bg-gray-900 text-gray-100 resize-none outline-none"
|
||
spellCheck="false"
|
||
placeholder="Paste or write your HTML here..."
|
||
/>
|
||
</div>
|
||
|
||
{/* Right: Preview */}
|
||
<div className="w-1/2 flex flex-col bg-gray-100">
|
||
<div className="bg-gray-800 text-white px-4 py-2 text-sm font-medium flex items-center justify-between">
|
||
<span>Live Preview</span>
|
||
<span className="text-xs opacity-75">
|
||
{viewport === 'desktop' ? 'Full Width' : viewportWidths[viewport]}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="flex-1 overflow-auto p-4 flex justify-center">
|
||
{/* Viewport Wrapper */}
|
||
<div
|
||
className="transition-all duration-300 ease-in-out shadow-lg"
|
||
style={{
|
||
width: viewportWidths[viewport],
|
||
height: '100%',
|
||
maxHeight: '100%'
|
||
}}
|
||
>
|
||
<iframe
|
||
title="Ghost Preview"
|
||
srcDoc={generatePreviewHtml(debouncedHtml)}
|
||
className="w-full h-full border-none bg-white rounded"
|
||
sandbox="allow-same-origin allow-scripts"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Footer with Color Reference */}
|
||
<div className="bg-white border-t border-gray-200 p-3">
|
||
<div className="flex items-center gap-4 text-xs text-gray-600 flex-wrap">
|
||
<span className="font-medium">Trinity Colors:</span>
|
||
<div className="flex items-center gap-1">
|
||
<span className="w-4 h-4 rounded" style={{background: '#FF6B35'}}></span>
|
||
<span>Fire #FF6B35</span>
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
<span className="w-4 h-4 rounded" style={{background: '#A855F7'}}></span>
|
||
<span>Arcane #A855F7</span>
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
<span className="w-4 h-4 rounded" style={{background: '#4ECDC4'}}></span>
|
||
<span>Frost #4ECDC4</span>
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
<span className="w-4 h-4 rounded" style={{background: 'linear-gradient(135deg, #FF6B35 0%, #A855F7 50%, #4ECDC4 100%)'}}></span>
|
||
<span>Trinity Gradient</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|