feat: port all template categories to JSON format

- Ported Minimalist templates to JSON (Swiss Grid, Brutalist, etc.)
- Ported Tech templates to JSON (SaaS, Terminal, Cyberpunk, etc.)
- Ported Creative templates to JSON (Art Gallery, Zine, Pop Art, etc.)
- Ported Industrial templates to JSON (Blueprint, Factory, Schematic, etc.)
- Ported Nature templates to JSON (Botanical, Ocean, Mountain, etc.)
- Ported Lifestyle templates to JSON (Cookbook, Travel, Coffee House, etc.)
- Ported Vintage templates to JSON (Art Deco, Medieval, Retro 80s, etc.)
- Updated README.md to reflect the new JSON-based style system (example configuration and contribution workflow)
- Completed migration of over 150 styles to the new architecture
This commit is contained in:
TypoGenie
2026-02-01 18:51:43 +02:00
parent da335734d3
commit a6f664088c
405 changed files with 69134 additions and 5936 deletions

View File

@@ -1,41 +1,145 @@
import React, { useState } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { AppState, PaperSize } from './types';
import { FileUpload } from './components/FileUpload';
import { StyleSelector } from './components/StyleSelector';
import { Preview } from './components/Preview';
import { ZoomControl } from './components/ZoomControl';
import { useSettings } from './hooks/useSettings';
import { useTemplates } from './hooks/useTemplates';
// @ts-ignore
import { parse } from 'marked';
import { Sparkles, Loader2, FileType } from 'lucide-react';
import { Sparkles, Loader2, FileType, Keyboard, X, RefreshCw } from 'lucide-react';
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
// Keyboard shortcuts help component
const KeyboardShortcutsHelp: React.FC<{ onClose: () => void }> = ({ onClose }) => {
const shortcuts = [
{ key: '↑ / ↓', description: 'Navigate styles' },
{ key: '← / →', description: 'Navigate categories (when focused)' },
{ key: 'Enter', description: 'Select style or category' },
{ key: 'Home / End', description: 'First/last item' },
{ key: 'PgUp / PgDn', description: 'Jump 5 items' },
{ key: 'Tab', description: 'Switch between sections' },
{ key: 'Ctrl + Enter', description: 'Generate document' },
{ key: 'Escape', description: 'Go back / Close' },
];
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={onClose}
>
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
className="bg-zinc-900 border border-zinc-700 rounded-2xl p-6 max-w-md w-full shadow-2xl"
onClick={e => e.stopPropagation()}
>
<div className="flex justify-between items-center mb-6">
<div className="flex items-center gap-3">
<div className="p-2 bg-indigo-500/10 rounded-lg text-indigo-400">
<Keyboard size={20} />
</div>
<h2 className="text-xl font-bold text-white">Keyboard Shortcuts</h2>
</div>
<button
onClick={onClose}
className="p-1 hover:bg-zinc-800 rounded-lg text-zinc-400 hover:text-white transition-colors"
>
<X size={20} />
</button>
</div>
<div className="space-y-3">
{shortcuts.map((shortcut, index) => (
<motion.div
key={shortcut.key}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05 }}
className="flex justify-between items-center py-2 border-b border-zinc-800 last:border-0"
>
<span className="text-zinc-400">{shortcut.description}</span>
<kbd className="px-2 py-1 bg-zinc-800 rounded text-sm font-mono text-zinc-300 border border-zinc-700">
{shortcut.key}
</kbd>
</motion.div>
))}
</div>
<p className="mt-6 text-xs text-zinc-500 text-center">
Press Escape or click outside to close
</p>
</motion.div>
</motion.div>
);
};
const App: React.FC = () => {
const [appState, setAppState] = useState<AppState>(AppState.UPLOAD);
const [content, setContent] = useState<string>('');
const [inputFileName, setInputFileName] = useState<string>('');
const [selectedStyle, setSelectedStyle] = useState<string | null>(null);
const [paperSize, setPaperSize] = useState<PaperSize>('Letter');
const [generatedHtml, setGeneratedHtml] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const [showShortcuts, setShowShortcuts] = useState(false);
const handleFileLoaded = (text: string) => {
const { uiZoom, setUiZoom, isLoaded } = useSettings();
const { templates, categories, isLoading: templatesLoading, error: templatesError, refresh, openFolder } = useTemplates();
// Global keyboard shortcut: Toggle help with ? or /
useKeyboardNavigation({
onEnter: undefined,
}, []);
// Note: Native file drop is handled by the FileUpload component
// The Tauri file drop is disabled to allow the webview to handle drag events
// This preserves the drag hover animations in the FileUpload component
// Global keydown listener for shortcuts help
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === '?' || e.key === '/') {
e.preventDefault();
setShowShortcuts(prev => !prev);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const handleFileLoaded = (text: string, fileName: string = '') => {
setContent(text);
setInputFileName(fileName);
setAppState(AppState.CONFIG);
};
const handleGenerate = async () => {
const handleGenerate = useCallback(async () => {
if (!selectedStyle || !content) return;
setAppState(AppState.GENERATING);
setError(null);
try {
// Small artificial delay to show the "Processing" state for better UX,
// otherwise it flickers too fast since local parsing is instant.
await new Promise(resolve => setTimeout(resolve, 800));
// Parse markdown to HTML using the local 'marked' library
const html = await parse(content);
if (!html) throw new Error("No content generated");
console.log('--- STAGE 1: MARKDOWN GENERATION ---');
console.log('First 500 chars of HTML:', html.substring(0, 500));
console.log('Contains h1?', html.includes('<h1'));
console.log('Contains h3?', html.includes('<h3'));
console.log('Contains table?', html.includes('<table'));
setGeneratedHtml(html);
setAppState(AppState.PREVIEW);
} catch (err) {
@@ -43,123 +147,298 @@ const App: React.FC = () => {
setError("Failed to process the document. Please check your file format and try again.");
setAppState(AppState.CONFIG);
}
};
}, [selectedStyle, content]);
const handleReset = () => {
setAppState(AppState.UPLOAD);
setContent('');
setGeneratedHtml('');
setSelectedStyle(null);
setInputFileName('');
};
const handleBackToConfig = () => {
setAppState(AppState.CONFIG);
};
// Render Logic
if (!isLoaded) {
return null;
}
if (appState === AppState.PREVIEW) {
// Pass selectedStyleId to Preview so it can lookup font config
// We add the prop via spread or explicit
return (
// @ts-ignore - Adding prop dynamically if interface not fully updated in previous file change block (it was)
<Preview
htmlContent={generatedHtml}
onBack={handleBackToConfig}
<div
className="h-screen w-screen overflow-hidden"
style={{ fontSize: `${uiZoom}%` }}
>
<div className="h-full w-full flex flex-col">
<Preview
htmlContent={generatedHtml}
onBack={handleBackToConfig}
paperSize={paperSize}
// @ts-ignore
selectedStyleId={selectedStyle}
/>
selectedStyleId={selectedStyle}
inputFileName={inputFileName}
uiZoom={uiZoom}
onZoomChange={setUiZoom}
templates={templates}
/>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-zinc-950 text-zinc-100 flex flex-col font-sans selection:bg-indigo-500/30">
{/* Header */}
<header className="border-b border-zinc-800 bg-zinc-950/50 backdrop-blur-sm sticky top-0 z-40">
<div
className="h-screen bg-zinc-950 text-zinc-100 flex flex-col font-sans selection:bg-indigo-500/30 overflow-hidden"
style={{ fontSize: `${uiZoom}%` }}
>
{/* Keyboard Shortcuts Modal */}
<AnimatePresence>
{showShortcuts && (
<KeyboardShortcutsHelp onClose={() => setShowShortcuts(false)} />
)}
</AnimatePresence>
{/* Header - Fixed height */}
<motion.header
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
className="flex-none border-b border-zinc-800 bg-zinc-950/50 backdrop-blur-sm z-40"
>
<div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<div className="flex items-center gap-2 cursor-pointer" onClick={handleReset}>
<div className="bg-gradient-to-br from-indigo-500 to-violet-600 p-2 rounded-lg">
<motion.div
className="flex items-center gap-2 cursor-pointer"
onClick={handleReset}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<motion.div
className="bg-gradient-to-br from-indigo-500 to-violet-600 p-2 rounded-lg"
whileHover={{ rotate: [0, -10, 10, 0], transition: { duration: 0.5 } }}
>
<FileType className="text-white" size={20} />
</div>
</motion.div>
<h1 className="text-xl font-bold tracking-tight text-white">TypoGenie</h1>
</div>
{appState !== AppState.UPLOAD && (
<div className="flex items-center gap-4 text-sm text-zinc-500">
<span className={appState === AppState.CONFIG ? "text-indigo-400 font-medium" : ""}>Configure</span>
<span>/</span>
<span className={appState === AppState.GENERATING ? "text-indigo-400 font-medium" : ""}>Generate</span>
<span>/</span>
<span>Preview</span>
</div>
)}
</div>
</header>
</motion.div>
{/* Main Content */}
<main className="flex-grow flex flex-col relative">
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div className="absolute -top-[20%] -left-[10%] w-[50%] h-[50%] bg-indigo-900/10 rounded-full blur-3xl"></div>
<div className="absolute top-[20%] -right-[10%] w-[40%] h-[40%] bg-violet-900/10 rounded-full blur-3xl"></div>
</div>
<div className="flex items-center gap-4">
{/* UI Zoom Control */}
<ZoomControl zoom={uiZoom} onZoomChange={setUiZoom} />
<div className="relative z-10 w-full max-w-7xl mx-auto px-6 py-12 flex-grow flex flex-col">
{appState === AppState.UPLOAD && (
<div className="flex flex-col items-center justify-center flex-grow space-y-8 animate-in fade-in zoom-in-95 duration-500">
<div className="text-center space-y-4 max-w-2xl">
<h2 className="text-4xl md:text-5xl font-extrabold tracking-tight text-white">
Turn Markdown into <br/>
<span className="text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-violet-400">
Professional Word Docs.
</span>
</h2>
<p className="text-lg text-zinc-400 leading-relaxed">
Upload your raw text. Select a style.
Get a formatted DOCX file ready for Microsoft Word.
</p>
</div>
<FileUpload onFileLoaded={handleFileLoaded} />
</div>
)}
{/* Refresh templates button */}
{appState === AppState.CONFIG && (
<motion.button
onClick={refresh}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="hidden sm:flex items-center gap-2 px-3 py-1.5 text-xs text-zinc-500 hover:text-zinc-300 bg-zinc-900 hover:bg-zinc-800 rounded-lg transition-colors border border-zinc-800"
title="Reload templates from disk"
>
<RefreshCw size={14} />
<span>Refresh</span>
</motion.button>
)}
{appState === AppState.CONFIG && (
<div className="animate-in fade-in slide-in-from-bottom-4 duration-500">
<StyleSelector
selectedStyle={selectedStyle}
onSelectStyle={setSelectedStyle}
selectedPaperSize={paperSize}
onSelectPaperSize={setPaperSize}
onGenerate={handleGenerate}
/>
{error && (
<div className="mt-6 p-4 bg-red-900/20 border border-red-800 rounded-xl text-center text-red-300">
{error}
</div>
{/* Keyboard shortcuts hint */}
<motion.button
onClick={() => setShowShortcuts(true)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="hidden sm:flex items-center gap-2 px-3 py-1.5 text-xs text-zinc-500 hover:text-zinc-300 bg-zinc-900 hover:bg-zinc-800 rounded-lg transition-colors border border-zinc-800"
title="Show keyboard shortcuts"
>
<Keyboard size={14} />
<span>Shortcuts</span>
<kbd className="px-1.5 py-0.5 bg-zinc-800 rounded text-zinc-400 font-mono">?</kbd>
</motion.button>
<AnimatePresence mode="wait">
{appState !== AppState.UPLOAD && (
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
className="flex items-center gap-4 text-sm text-zinc-500"
>
<span className={appState === AppState.CONFIG ? "text-indigo-400 font-medium" : ""}>Configure</span>
<span>/</span>
<span className={appState === AppState.GENERATING ? "text-indigo-400 font-medium" : ""}>Generate</span>
<span>/</span>
<span>Preview</span>
</motion.div>
)}
</div>
)}
</AnimatePresence>
</div>
</div>
</motion.header>
{appState === AppState.GENERATING && (
<div className="flex flex-col items-center justify-center flex-grow text-center animate-in fade-in duration-700">
<div className="relative">
<div className="absolute inset-0 bg-indigo-500 blur-xl opacity-20 animate-pulse"></div>
<Loader2 size={64} className="text-indigo-400 animate-spin relative z-10" />
</div>
<h3 className="mt-8 text-2xl font-bold text-white">Formatting Document</h3>
<p className="mt-2 text-zinc-400 max-w-md">
Applying typographic rules and preparing print-ready layout...
</p>
</div>
)}
{/* Main Content - Takes remaining space */}
<main className="flex-1 relative overflow-hidden">
{/* Animated background blobs */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<motion.div
className="absolute -top-[20%] -left-[10%] w-[50%] h-[50%] bg-indigo-900/10 rounded-full blur-3xl"
animate={{
x: [0, 30, 0],
y: [0, -20, 0],
scale: [1, 1.1, 1]
}}
transition={{ duration: 8, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.div
className="absolute top-[20%] -right-[10%] w-[40%] h-[40%] bg-violet-900/10 rounded-full blur-3xl"
animate={{
x: [0, -20, 0],
y: [0, 30, 0],
scale: [1, 1.15, 1]
}}
transition={{ duration: 10, repeat: Infinity, ease: "easeInOut" }}
/>
</div>
<div className="relative z-10 w-full h-full max-w-7xl mx-auto px-6 py-6">
<AnimatePresence mode="wait">
{appState === AppState.UPLOAD && (
<motion.div
key="upload"
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -20, scale: 1.05 }}
transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }}
className="h-full flex flex-col items-center justify-center space-y-8"
>
<motion.div
className="text-center space-y-4 max-w-2xl"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1, duration: 0.5 }}
>
<motion.h2
className="text-4xl md:text-5xl font-extrabold tracking-tight text-white"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
>
Turn Markdown into <br />
<motion.span
className="text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-violet-400"
animate={{
backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"]
}}
transition={{ duration: 5, repeat: Infinity, ease: "linear" }}
style={{ backgroundSize: "200% 200%" }}
>
Professional Word Docs.
</motion.span>
</motion.h2>
<motion.p
className="text-lg text-zinc-400 leading-relaxed"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4, duration: 0.5 }}
>
Upload your raw text. Select a style.
Get a formatted DOCX file ready for Microsoft Word.
</motion.p>
</motion.div>
<FileUpload onFileLoaded={handleFileLoaded} />
</motion.div>
)}
{appState === AppState.CONFIG && (
<motion.div
key="config"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }}
className="h-full"
>
<StyleSelector
templates={templates}
categories={categories}
selectedStyle={selectedStyle}
onSelectStyle={setSelectedStyle}
selectedPaperSize={paperSize}
onSelectPaperSize={setPaperSize}
onGenerate={handleGenerate}
isLoading={templatesLoading}
error={templatesError}
onOpenTemplatesFolder={openFolder}
/>
<AnimatePresence>
{error && (
<motion.div
initial={{ opacity: 0, y: -10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -10, scale: 0.95 }}
className="mt-4 p-4 bg-red-900/20 border border-red-800 rounded-xl text-center text-red-300"
role="alert"
aria-live="polite"
>
{error}
</motion.div>
)}
</AnimatePresence>
</motion.div>
)}
{appState === AppState.GENERATING && (
<motion.div
key="generating"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 1.1 }}
transition={{ duration: 0.3 }}
className="h-full flex flex-col items-center justify-center text-center"
>
<motion.div
className="relative"
animate={{ rotate: 360 }}
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
>
<motion.div
className="absolute inset-0 bg-indigo-500 blur-xl opacity-20"
animate={{ scale: [1, 1.2, 1], opacity: [0.2, 0.4, 0.2] }}
transition={{ duration: 1.5, repeat: Infinity }}
/>
<Loader2 size={64} className="text-indigo-400 relative z-10" />
</motion.div>
<motion.h3
className="mt-8 text-2xl font-bold text-white"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
>
Formatting Document
</motion.h3>
<motion.p
className="mt-2 text-zinc-400 max-w-md"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
>
Applying typographic rules and preparing print-ready layout...
</motion.p>
</motion.div>
)}
</AnimatePresence>
</div>
</main>
<footer className="py-6 text-center text-zinc-600 text-sm border-t border-zinc-900 print:hidden">
{/* Footer - Fixed height, always visible */}
<motion.footer
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
className="flex-none py-4 text-center text-zinc-600 text-sm border-t border-zinc-900 bg-zinc-950"
>
<p>TypoGenie - Professional Typesetting Engine</p>
</footer>
</motion.footer>
</div>
);
};
export default App;
export default App;

View File

@@ -0,0 +1,272 @@
import React, { useRef, useEffect, useState, useCallback } from 'react';
interface CustomScrollbarProps {
children: React.ReactNode;
className?: string;
horizontal?: boolean;
}
export const CustomScrollbar: React.FC<CustomScrollbarProps> = ({
children,
className = '',
horizontal = false
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const thumbRef = useRef<HTMLDivElement>(null);
const [isHoveringContainer, setIsHoveringContainer] = useState(false);
const [isHoveringTrack, setIsHoveringTrack] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const [thumbSize, setThumbSize] = useState(40);
// Drag state
const dragState = useRef({
isActive: false,
startMouse: 0,
startScroll: 0,
trackSize: 0,
maxScroll: 0
});
// Update thumb size
const updateMetrics = useCallback(() => {
const container = containerRef.current;
if (!container) return;
if (horizontal) {
const ratio = container.clientWidth / Math.max(container.scrollWidth, 1);
setThumbSize(Math.max(40, ratio * container.clientWidth));
} else {
const ratio = container.clientHeight / Math.max(container.scrollHeight, 1);
setThumbSize(Math.max(40, ratio * container.clientHeight));
}
updateThumbVisual();
}, [horizontal]);
// Update thumb position from scroll
const updateThumbVisual = useCallback(() => {
const container = containerRef.current;
const thumb = thumbRef.current;
if (!container || !thumb) return;
if (dragState.current.isActive) return;
if (horizontal) {
const maxScroll = container.scrollWidth - container.clientWidth;
const trackSize = container.clientWidth - thumbSize;
if (maxScroll <= 0 || trackSize <= 0) {
thumb.style.transform = 'translateX(0px)';
return;
}
const ratio = container.scrollLeft / maxScroll;
thumb.style.transform = `translateX(${ratio * trackSize}px)`;
} else {
const maxScroll = container.scrollHeight - container.clientHeight;
const trackSize = container.clientHeight - thumbSize;
if (maxScroll <= 0 || trackSize <= 0) {
thumb.style.transform = 'translateY(0px)';
return;
}
const ratio = container.scrollTop / maxScroll;
thumb.style.transform = `translateY(${ratio * trackSize}px)`;
}
}, [horizontal, thumbSize]);
// Setup
useEffect(() => {
updateMetrics();
const handleResize = () => updateMetrics();
window.addEventListener('resize', handleResize);
const observer = new MutationObserver(() => updateMetrics());
if (containerRef.current) {
observer.observe(containerRef.current, { childList: true, subtree: true });
}
return () => {
window.removeEventListener('resize', handleResize);
observer.disconnect();
};
}, [updateMetrics]);
// Scroll listener
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const onScroll = () => {
if (!dragState.current.isActive) {
updateThumbVisual();
}
};
container.addEventListener('scroll', onScroll, { passive: true });
return () => container.removeEventListener('scroll', onScroll);
}, [updateThumbVisual]);
// Drag handling - DIRECT synchronous updates
useEffect(() => {
if (!isDragging) {
// Restore transition when not dragging
if (thumbRef.current) {
thumbRef.current.style.transition = 'opacity 0.15s, width 0.15s, height 0.15s';
}
return;
}
const container = containerRef.current;
const thumb = thumbRef.current;
if (!container || !thumb) return;
const state = dragState.current;
state.isActive = true;
// REMOVE transition during drag for instant response
thumb.style.transition = 'none';
const handleMouseMove = (e: MouseEvent) => {
const mousePos = horizontal ? e.clientX : e.clientY;
const deltaMouse = mousePos - state.startMouse;
const scrollRatio = deltaMouse / state.trackSize;
const newScroll = state.startScroll + (scrollRatio * state.maxScroll);
const clampedScroll = Math.max(0, Math.min(newScroll, state.maxScroll));
if (horizontal) {
container.scrollLeft = clampedScroll;
const visualRatio = state.maxScroll > 0 ? clampedScroll / state.maxScroll : 0;
thumb.style.transform = `translateX(${visualRatio * state.trackSize}px)`;
} else {
container.scrollTop = clampedScroll;
const visualRatio = state.maxScroll > 0 ? clampedScroll / state.maxScroll : 0;
thumb.style.transform = `translateY(${visualRatio * state.trackSize}px)`;
}
};
const handleMouseUp = () => {
state.isActive = false;
setIsDragging(false);
// Transition will be restored by effect cleanup
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
state.isActive = false;
};
}, [isDragging, horizontal]);
const handleThumbMouseDown = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
const container = containerRef.current;
const thumb = thumbRef.current;
if (!container || !thumb) return;
const state = dragState.current;
if (horizontal) {
state.startMouse = e.clientX;
state.startScroll = container.scrollLeft;
state.trackSize = container.clientWidth - thumbSize;
state.maxScroll = container.scrollWidth - container.clientWidth;
} else {
state.startMouse = e.clientY;
state.startScroll = container.scrollTop;
state.trackSize = container.clientHeight - thumbSize;
state.maxScroll = container.scrollHeight - container.clientHeight;
}
// Remove transition immediately on mouse down
thumb.style.transition = 'none';
setIsDragging(true);
};
const handleTrackClick = (e: React.MouseEvent) => {
if (e.target === thumbRef.current) return;
const container = containerRef.current;
if (!container) return;
const rect = container.getBoundingClientRect();
if (horizontal) {
const clickX = e.clientX - rect.left;
const percentage = clickX / rect.width;
container.scrollTo({
left: percentage * (container.scrollWidth - container.clientWidth),
behavior: 'smooth'
});
} else {
const clickY = e.clientY - rect.top;
const percentage = clickY / rect.height;
container.scrollTo({
top: percentage * (container.scrollHeight - container.clientHeight),
behavior: 'smooth'
});
}
};
const isActive = isHoveringTrack || isDragging;
const opacity = isActive ? 0.6 : (isHoveringContainer ? 0.2 : 0);
const size = isActive ? '6px' : '2px';
return (
<div
className={`relative ${className}`}
onMouseEnter={() => setIsHoveringContainer(true)}
onMouseLeave={() => {
setIsHoveringContainer(false);
if (!isDragging) setIsHoveringTrack(false);
}}
>
<div
ref={containerRef}
className="h-full w-full overflow-auto scrollbar-hide"
style={{
scrollbarWidth: 'none',
msOverflowStyle: 'none',
overflowX: horizontal ? 'auto' : 'hidden',
overflowY: horizontal ? 'hidden' : 'auto'
}}
>
{children}
</div>
<div
className={`absolute cursor-pointer z-10 ${horizontal ? 'bottom-0 left-0 right-0 h-4' : 'top-0 right-0 bottom-0 w-4'}`}
style={{ opacity }}
onMouseEnter={() => setIsHoveringTrack(true)}
onMouseLeave={() => { if (!isDragging) setIsHoveringTrack(false); }}
onClick={handleTrackClick}
>
<div
ref={thumbRef}
className="absolute rounded-full bg-zinc-400 cursor-grab active:cursor-grabbing"
style={{
[horizontal ? 'left' : 'top']: 0,
[horizontal ? 'bottom' : 'right']: '4px',
[horizontal ? 'width' : 'height']: thumbSize,
[horizontal ? 'height' : 'width']: size,
opacity: isActive ? 0.9 : 0.5,
transition: 'opacity 0.15s, width 0.15s, height 0.15s',
transform: horizontal ? 'translateX(0px)' : 'translateY(0px)',
willChange: 'transform'
}}
onMouseDown={handleThumbMouseDown}
/>
</div>
<style>{`
.scrollbar-hide::-webkit-scrollbar {
display: none !important;
}
`}</style>
</div>
);
};

View File

@@ -0,0 +1,145 @@
import { X } from 'lucide-react';
import { useState } from 'react';
interface ExportOptionsModalProps {
isOpen: boolean;
onClose: () => void;
onExport: (useTableHeaders: boolean) => void;
}
export default function ExportOptionsModal({ isOpen, onClose, onExport }: ExportOptionsModalProps) {
const [selectedMode, setSelectedMode] = useState<'table' | 'semantic'>('semantic');
if (!isOpen) return null;
const handleExport = () => {
onExport(selectedMode === 'table');
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-zinc-950/80 backdrop-blur-sm">
<div className="relative w-full max-w-2xl bg-zinc-900 rounded-2xl shadow-2xl m-4 overflow-hidden border border-zinc-700">
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-zinc-800 bg-zinc-900/50">
<h2 className="text-xl font-semibold text-white">Export Options</h2>
<button
onClick={onClose}
className="p-1 text-zinc-400 hover:text-white transition-colors rounded-md hover:bg-zinc-800"
aria-label="Close"
>
<X size={20} />
</button>
</div>
{/* Content */}
<div className="px-6 py-4 space-y-4 bg-zinc-900">
<p className="text-sm text-zinc-400 mb-6">
Choose how headers should be rendered in your Word document:
</p>
{/* Option 1: High-Fidelity */}
<label
className={`block p-4 border-2 rounded-lg cursor-pointer transition-all ${selectedMode === 'table'
? 'border-indigo-500 bg-indigo-500/10'
: 'border-zinc-700 hover:border-zinc-600 bg-zinc-800/50'
}`}
>
<div className="flex items-start">
<input
type="radio"
name="exportMode"
value="table"
checked={selectedMode === 'table'}
onChange={() => setSelectedMode('table')}
className="mt-1 mr-3 text-indigo-600 focus:ring-indigo-500 bg-zinc-700 border-zinc-600"
/>
<div className="flex-1">
<div className="font-semibold text-white mb-2">High-Fidelity Layout</div>
<div className="space-y-1 text-sm">
<div className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-zinc-300">Perfect padding and border alignment</span>
</div>
<div className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-zinc-300">Backgrounds contained precisely</span>
</div>
<div className="flex items-start">
<span className="text-red-500 mr-2"></span>
<span className="text-zinc-300">No automatic Table of Contents</span>
</div>
<div className="flex items-start">
<span className="text-red-500 mr-2"></span>
<span className="text-zinc-300">Document outline/navigation disabled</span>
</div>
</div>
<div className="mt-3 text-xs text-zinc-500 italic">
Best for: Portfolios, brochures, print-ready designs
</div>
</div>
</div>
</label>
{/* Option 2: Semantic */}
<label
className={`block p-4 border-2 rounded-lg cursor-pointer transition-all ${selectedMode === 'semantic'
? 'border-indigo-500 bg-indigo-500/10'
: 'border-zinc-700 hover:border-zinc-600 bg-zinc-800/50'
}`}
>
<div className="flex items-start">
<input
type="radio"
name="exportMode"
value="semantic"
checked={selectedMode === 'semantic'}
onChange={() => setSelectedMode('semantic')}
className="mt-1 mr-3 text-indigo-600 focus:ring-indigo-500 bg-zinc-700 border-zinc-600"
/>
<div className="flex-1">
<div className="font-semibold text-white mb-2">Semantic Structure</div>
<div className="space-y-1 text-sm">
<div className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-zinc-300">Auto-generated Table of Contents</span>
</div>
<div className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-zinc-300">Document navigation panel works</span>
</div>
<div className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-zinc-300">Screen reader accessible</span>
</div>
<div className="flex items-start">
<span className="text-yellow-500 mr-2"></span>
<span className="text-zinc-300">Minor padding/border alignment issues</span>
</div>
</div>
<div className="mt-3 text-xs text-zinc-500 italic">
Best for: Academic papers, reports, accessible documents
</div>
</div>
</div>
</label>
</div>
{/* Footer */}
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-zinc-800 bg-zinc-900">
<button
onClick={onClose}
className="px-4 py-2 text-sm font-medium text-zinc-300 bg-zinc-800 border border-zinc-700 rounded-md hover:bg-zinc-700 transition-colors"
>
Cancel
</button>
<button
onClick={handleExport}
className="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-500 transition-colors"
>
Export to Word
</button>
</div>
</div>
</div>
);
}

View File

@@ -1,13 +1,19 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useState, useRef, useEffect } from 'react';
import { motion } from 'motion/react';
import { Upload, FileText, AlertCircle } from 'lucide-react';
import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
interface FileUploadProps {
onFileLoaded: (content: string) => void;
onFileLoaded: (content: string, fileName: string) => void;
}
export const FileUpload: React.FC<FileUploadProps> = ({ onFileLoaded }) => {
const [dragActive, setDragActive] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const dropzoneRef = useRef<HTMLDivElement>(null);
const dragCounter = useRef(0);
const handleFile = (file: File) => {
setError(null);
@@ -20,7 +26,9 @@ export const FileUpload: React.FC<FileUploadProps> = ({ onFileLoaded }) => {
reader.onload = (e) => {
const text = e.target?.result;
if (typeof text === 'string') {
onFileLoaded(text);
// Extract filename without extension
const fileName = file.name.replace(/\.[^/.]+$/, '');
onFileLoaded(text, fileName);
}
};
reader.onerror = () => setError('Error reading file.');
@@ -30,16 +38,24 @@ export const FileUpload: React.FC<FileUploadProps> = ({ onFileLoaded }) => {
const handleDrag = useCallback((e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
if (e.type === 'dragenter' || e.type === 'dragover') {
if (e.type === 'dragenter') {
dragCounter.current += 1;
setDragActive(true);
} else if (e.type === 'dragleave') {
setDragActive(false);
dragCounter.current -= 1;
if (dragCounter.current === 0) {
setDragActive(false);
}
} else if (e.type === 'dragover') {
// Keep drag active during dragover
setDragActive(true);
}
}, []);
const handleDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
dragCounter.current = 0;
setDragActive(false);
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
handleFile(e.dataTransfer.files[0]);
@@ -53,40 +69,158 @@ export const FileUpload: React.FC<FileUploadProps> = ({ onFileLoaded }) => {
}
};
const openFilePicker = useCallback(() => {
inputRef.current?.click();
}, []);
// Keyboard navigation for the dropzone
useKeyboardNavigation({
onEnter: () => {
if (isFocused) {
openFilePicker();
}
},
onSpace: () => {
if (isFocused) {
openFilePicker();
}
},
}, [isFocused]);
// Clear error after 5 seconds
useEffect(() => {
if (error) {
const timer = setTimeout(() => setError(null), 5000);
return () => clearTimeout(timer);
}
}, [error]);
return (
<div className="w-full max-w-xl mx-auto">
<div
className={`relative group flex flex-col items-center justify-center w-full h-64 border-2 border-dashed rounded-2xl transition-all duration-300 ease-in-out cursor-pointer overflow-hidden
${dragActive ? 'border-indigo-500 bg-indigo-500/10' : 'border-zinc-700 bg-zinc-900/50 hover:border-zinc-500 hover:bg-zinc-800/50'}`}
<motion.div
className="w-full max-w-xl mx-auto"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5, duration: 0.5 }}
role="region"
aria-label="File upload"
>
<motion.div
ref={dropzoneRef}
role="button"
tabIndex={0}
aria-label="Drop zone. Click or press Enter to select a file."
className={`relative group flex flex-col items-center justify-center w-full h-64 border-2 border-dashed rounded-2xl cursor-pointer overflow-hidden outline-none transition-all
${dragActive ? 'border-indigo-500 bg-indigo-500/10' : 'border-zinc-700 bg-zinc-900/50'}
${isFocused ? 'ring-2 ring-indigo-500 ring-offset-2 ring-offset-zinc-950 border-indigo-400' : ''}
`}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
onClick={openFilePicker}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openFilePicker();
}
}}
whileHover={{
scale: 1.02,
borderColor: '#6366f1',
backgroundColor: 'rgba(99, 102, 241, 0.05)'
}}
whileTap={{ scale: 0.98 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<input
ref={inputRef}
type="file"
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10"
className="hidden"
onChange={handleChange}
accept=".md,.txt,.markdown"
aria-label="Select file"
/>
<div className="flex flex-col items-center justify-center pt-5 pb-6 text-zinc-400 group-hover:text-zinc-200 transition-colors">
<div className={`p-4 rounded-full mb-4 ${dragActive ? 'bg-indigo-500/20 text-indigo-400' : 'bg-zinc-800 text-zinc-500'}`}>
<motion.div
className="flex flex-col items-center justify-center pt-5 pb-6 text-zinc-400 group-hover:text-zinc-200 transition-colors"
animate={dragActive ? { y: [0, -5, 0] } : {}}
transition={{ duration: 0.3 }}
>
<motion.div
className={`p-4 rounded-full mb-4 ${dragActive ? 'bg-indigo-500/20 text-indigo-400' : 'bg-zinc-800 text-zinc-500'}`}
animate={dragActive ? {
scale: [1, 1.2, 1],
rotate: [0, 10, -10, 0]
} : {}}
transition={{ duration: 0.5 }}
whileHover={{ scale: 1.1, rotate: 5 }}
>
<Upload size={32} />
</div>
<p className="mb-2 text-lg font-medium">
<span className="font-semibold text-indigo-400">Click to upload</span> or drag and drop
</p>
<p className="text-sm text-zinc-500">Markdown or Plain Text files</p>
</div>
</div>
</motion.div>
<motion.p
className="mb-2 text-lg font-medium"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
>
<motion.span
className="font-semibold text-indigo-400"
whileHover={{ color: '#818cf8' }}
>
Click to upload
</motion.span>{' '}
or drag and drop
</motion.p>
<motion.p
className="text-sm text-zinc-500"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
>
Markdown or Plain Text files
</motion.p>
<motion.p
className="text-xs text-zinc-600 mt-2"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.8 }}
>
Press Enter to browse
</motion.p>
</motion.div>
{/* Animated border gradient on hover */}
<motion.div
className="absolute inset-0 rounded-2xl pointer-events-none"
style={{
background: 'linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.1), transparent)',
}}
initial={{ x: '-100%' }}
whileHover={{ x: '100%' }}
transition={{ duration: 0.6, ease: 'easeInOut' }}
/>
</motion.div>
{error && (
<div className="mt-4 p-4 bg-red-900/20 border border-red-800 rounded-lg flex items-center gap-3 text-red-200 animate-in fade-in slide-in-from-top-2">
<AlertCircle size={20} />
<motion.div
initial={{ opacity: 0, y: -10, height: 0 }}
animate={{ opacity: 1, y: 0, height: 'auto' }}
exit={{ opacity: 0, y: -10, height: 0 }}
className="mt-4 p-4 bg-red-900/20 border border-red-800 rounded-lg flex items-center gap-3 text-red-200"
role="alert"
aria-live="polite"
>
<motion.div
animate={{ rotate: [0, 10, -10, 0] }}
transition={{ duration: 0.5 }}
>
<AlertCircle size={20} />
</motion.div>
<span>{error}</span>
</div>
</motion.div>
)}
</div>
</motion.div>
);
};

View File

@@ -1,374 +1,434 @@
import React, { useEffect, useRef, useState } from 'react';
import { ArrowLeft, Download, FileText, CheckCircle2, ExternalLink } from 'lucide-react';
import { PaperSize, DocxStyleConfig, DocxBorder } from '../types';
import { TYPOGRAPHY_STYLES } from '../constants';
// @ts-ignore
import * as docx from 'docx';
import { motion, AnimatePresence } from 'motion/react';
import { ArrowLeft, Download, FileText, CheckCircle2, ExternalLink, Loader2, ZoomIn, ZoomOut } from 'lucide-react';
import { PaperSize } from '../types';
import { StyleOption } from '../types';
import { getPreviewCss } from '../services/templateRenderer';
import { generateDocxDocument } from '../utils/docxConverter';
import ExportOptionsModal from './ExportOptionsModal';
import { open } from '@tauri-apps/plugin-shell';
import { save } from '@tauri-apps/plugin-dialog';
import { writeFile } from '@tauri-apps/plugin-fs';
import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
interface PreviewProps {
htmlContent: string;
onBack: () => void;
paperSize: PaperSize;
selectedStyleId?: string | null;
inputFileName?: string;
uiZoom: number;
onZoomChange: (zoom: number) => void;
templates: StyleOption[];
}
export const Preview: React.FC<PreviewProps> = ({ htmlContent, onBack, paperSize, selectedStyleId }) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [isExporting, setIsExporting] = useState(false);
const [successMsg, setSuccessMsg] = useState(false);
// Zoom Control Component
const ZoomControl: React.FC<{ zoom: number; onZoomChange: (zoom: number) => void }> = ({ zoom, onZoomChange }) => {
const decreaseZoom = () => onZoomChange(Math.max(50, zoom - 10));
const increaseZoom = () => onZoomChange(Math.min(200, zoom + 10));
// Get current style
const style = TYPOGRAPHY_STYLES.find(s => s.id === selectedStyleId) || TYPOGRAPHY_STYLES[0];
return (
<div className="flex items-center gap-2 bg-zinc-900/80 rounded-lg border border-zinc-800 px-2 py-1">
<motion.button
onClick={decreaseZoom}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-1 text-zinc-400 hover:text-white transition-colors"
aria-label="Zoom out"
>
<ZoomOut size={16} />
</motion.button>
<span className="text-xs font-medium text-zinc-300 min-w-[3rem] text-center">{zoom}%</span>
<motion.button
onClick={increaseZoom}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-1 text-zinc-400 hover:text-white transition-colors"
aria-label="Zoom in"
>
<ZoomIn size={16} />
</motion.button>
</div>
);
};
// Extract unique fonts for display
const usedFonts = Array.from(new Set([
style.wordConfig.heading1.font,
style.wordConfig.heading2.font,
style.wordConfig.body.font
])).filter(Boolean);
// Font download component
const FontList: React.FC<{ fonts: string[] }> = ({ fonts }) => {
const [downloadingFont, setDownloadingFont] = useState<string | null>(null);
const [downloadStatus, setDownloadStatus] = useState<Record<string, string>>({});
// Helper to convert Points to Half-Points (docx standard)
const pt = (points: number) => points * 2;
// Helper to convert Inches/MM to Twips
const inchesToTwips = (inches: number) => Math.round(inches * 1440);
const mmToTwips = (mm: number) => Math.round(mm * (1440 / 25.4));
// Helper to map Border config to Docx Border
const mapBorder = (b?: DocxBorder) => {
if (!b) return undefined;
let style = docx.BorderStyle.SINGLE;
if (b.style === 'double') style = docx.BorderStyle.DOUBLE;
if (b.style === 'dotted') style = docx.BorderStyle.DOTTED;
if (b.style === 'dashed') style = docx.BorderStyle.DASHED;
return {
color: b.color,
space: b.space,
style: style,
size: b.size
};
const downloadFont = async (fontName: string) => {
setDownloadingFont(fontName);
setDownloadStatus(prev => ({ ...prev, [fontName]: 'Downloading...' }));
try {
const encodedName = encodeURIComponent(fontName);
const downloadUrl = `https://fonts.google.com/download?family=${encodedName}`;
await open(downloadUrl);
setDownloadStatus(prev => ({ ...prev, [fontName]: 'Opened in browser' }));
setTimeout(() => {
setDownloadStatus(prev => { const newStatus = { ...prev }; delete newStatus[fontName]; return newStatus; });
}, 3000);
} catch (err) {
setDownloadStatus(prev => ({ ...prev, [fontName]: 'Failed' }));
} finally {
setDownloadingFont(null);
}
};
const generateDocx = async (styleId: string) => {
const openGoogleFonts = async (fontName: string) => {
try {
const url = `https://fonts.google.com/specimen/${fontName.replace(/\s+/g, '+')}`;
await open(url);
} catch (err) {
window.open(`https://fonts.google.com/specimen/${fontName.replace(/\s+/g, '+')}`, '_blank');
}
};
return (
<div className="flex items-center gap-2 text-sm text-zinc-500">
<span className="hidden md:inline">Fonts:</span>
<div className="flex gap-2 flex-wrap">
{fonts.map((font, index) => (
<motion.div key={font} className="relative group" initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: index * 0.05 }}>
<div className="flex items-center gap-1">
<motion.button
onClick={() => downloadFont(font)}
disabled={downloadingFont === font}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="px-2 py-1 bg-zinc-900 border border-zinc-800 hover:border-zinc-600 rounded-l text-zinc-300 hover:text-white transition-all flex items-center gap-1.5 text-xs font-medium"
>
{downloadingFont === font ? <Loader2 size={10} className="animate-spin" /> : <Download size={10} />}
{font}
</motion.button>
<motion.button
onClick={() => openGoogleFonts(font)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="px-1.5 py-1 bg-zinc-900 border border-l-0 border-zinc-800 hover:border-zinc-600 rounded-r text-zinc-400 hover:text-white transition-all"
>
<ExternalLink size={10} />
</motion.button>
</div>
{downloadStatus[font] && (
<motion.div className="absolute top-full left-0 mt-1 px-2 py-1 bg-zinc-800 border border-zinc-700 rounded text-xs whitespace-nowrap z-50" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }}>
{downloadStatus[font]}
</motion.div>
)}
</motion.div>
))}
</div>
</div>
);
};
export const Preview: React.FC<PreviewProps> = ({
htmlContent,
onBack,
paperSize,
selectedStyleId,
inputFileName = 'document',
uiZoom,
onZoomChange,
templates
}) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const backButtonRef = useRef<HTMLButtonElement>(null);
const saveButtonRef = useRef<HTMLButtonElement>(null);
const [isExporting, setIsExporting] = useState(false);
const [successMsg, setSuccessMsg] = useState(false);
const [showExportModal, setShowExportModal] = useState(false);
const [focusedElement, setFocusedElement] = useState<'back' | 'fonts' | 'save'>('save');
// Get current style from templates
const style = templates.find(s => s.id === selectedStyleId) || templates[0] || null;
// Extract used fonts for display
const usedFonts = style ? Array.from(new Set([
style.typography?.fonts?.heading || style.wordConfig?.heading1?.font || 'Arial',
style.typography?.fonts?.body || style.wordConfig?.body?.font || 'Arial'
])).filter(Boolean) : [];
useKeyboardNavigation({
onEscape: () => onBack(),
onArrowLeft: () => { if (focusedElement === 'save') { setFocusedElement('back'); backButtonRef.current?.focus(); } },
onArrowRight: () => { if (focusedElement === 'back') { setFocusedElement('save'); saveButtonRef.current?.focus(); } },
onEnter: () => { if (focusedElement === 'back') onBack(); else if (focusedElement === 'save' && !isExporting) handleSave(); },
onCtrlEnter: () => { if (!isExporting) handleSave(); },
}, [focusedElement, isExporting, onBack]);
useEffect(() => {
setTimeout(() => saveButtonRef.current?.focus(), 100);
}, []);
const handleSave = async () => {
setShowExportModal(true);
};
const handleExportConfirm = async (useTableHeaders: boolean) => {
setShowExportModal(false);
const sid = selectedStyleId || 'swiss-grid';
await generateDocx(sid, useTableHeaders);
};
const generateDocx = async (styleId: string, useTableHeaders: boolean = false) => {
setIsExporting(true);
try {
const style = TYPOGRAPHY_STYLES.find(s => s.id === styleId) || TYPOGRAPHY_STYLES[0];
const cfg = style.wordConfig;
const template = templates.find(s => s.id === styleId) || templates[0];
// PARSE HTML
const parser = new DOMParser();
const doc = parser.parseFromString(htmlContent, 'text/html');
const nodes = Array.from(doc.body.childNodes);
// Get base config from wordConfig
const cfg = template.wordConfig || {
heading1: { font: 'Arial', size: 24, color: '000000', align: 'left' },
heading2: { font: 'Arial', size: 18, color: '333333', align: 'left' },
body: { font: 'Arial', size: 11, color: '000000', align: 'left' },
accentColor: '000000'
};
const docxChildren = [];
// Extract page background from template
const pageBackground = template.typography?.colors?.background;
for (const node of nodes) {
if (node.nodeType !== Node.ELEMENT_NODE) continue;
const el = node as HTMLElement;
const tagName = el.tagName.toLowerCase();
// --- Run Parser (Inline Styles) ---
const parseRuns = (element: HTMLElement, baseConfig: DocxStyleConfig) => {
const runs = [];
for (const child of Array.from(element.childNodes)) {
if (child.nodeType === Node.TEXT_NODE) {
runs.push(new docx.TextRun({
text: child.textContent || '',
font: baseConfig.font,
size: pt(baseConfig.size),
color: baseConfig.color,
underline: baseConfig.underline ? { type: docx.UnderlineType.SINGLE, color: baseConfig.color } : undefined,
}));
} else if (child.nodeType === Node.ELEMENT_NODE) {
const childEl = child as HTMLElement;
const isBold = childEl.tagName === 'STRONG' || childEl.tagName === 'B';
const isItalic = childEl.tagName === 'EM' || childEl.tagName === 'I';
runs.push(new docx.TextRun({
text: childEl.textContent || '',
bold: isBold,
italics: isItalic,
font: baseConfig.font,
size: pt(baseConfig.size),
color: baseConfig.color,
underline: baseConfig.underline ? { type: docx.UnderlineType.SINGLE, color: baseConfig.color } : undefined,
}));
}
}
return runs;
};
// Get fonts from template
const codeFont = template.typography?.fonts?.code || template.typography?.fonts?.body || 'Consolas';
// Detect dark theme
const isDarkTheme = pageBackground ? parseInt(pageBackground, 16) < 0x444444 : false;
// --- Block Parser ---
// 1. HEADINGS
if (tagName.match(/^h[1-6]$/)) {
const level = parseInt(tagName.replace('h', ''));
// Select config based on level (simplified to H1 or H2, fallback H2 for others)
const hCfg = level === 1 ? cfg.heading1 : cfg.heading2;
const headingLevel = level === 1 ? docx.HeadingLevel.HEADING_1 : docx.HeadingLevel.HEADING_2;
// DEBUG: Specific markdown constructs for DOCX
console.log('=== PREVIEW CALLING DOCX (h3, strong, table, ul/ol, li) ===');
console.log('wordConfig h3:', JSON.stringify({ font: cfg.heading2?.font, size: cfg.body?.size, color: template.elements?.h3?.color }));
console.log('elements h3:', JSON.stringify(template.elements?.h3));
console.log('elements strong:', JSON.stringify(template.elements?.strong));
console.log('elements table:', JSON.stringify(template.elements?.table));
console.log('elements th:', JSON.stringify(template.elements?.th));
console.log('elements td:', JSON.stringify(template.elements?.td));
console.log('elements ul:', JSON.stringify(template.elements?.ul));
console.log('elements ol:', JSON.stringify(template.elements?.ol));
console.log('elements li:', JSON.stringify(template.elements?.li));
console.log('useTableHeaders:', useTableHeaders);
console.log('=== END PREVIEW CALL ===');
// Border Mapping
const borderConfig: any = {};
if (hCfg.border) {
if (hCfg.border.top) borderConfig.top = mapBorder(hCfg.border.top);
if (hCfg.border.bottom) borderConfig.bottom = mapBorder(hCfg.border.bottom);
if (hCfg.border.left) borderConfig.left = mapBorder(hCfg.border.left);
if (hCfg.border.right) borderConfig.right = mapBorder(hCfg.border.right);
}
// Alignment Mapping
let align = docx.AlignmentType.LEFT;
if (hCfg.align === 'center') align = docx.AlignmentType.CENTER;
if (hCfg.align === 'right') align = docx.AlignmentType.RIGHT;
if (hCfg.align === 'both') align = docx.AlignmentType.BOTH;
docxChildren.push(new docx.Paragraph({
children: [
new docx.TextRun({
text: el.textContent || '',
font: hCfg.font,
bold: hCfg.bold,
italics: hCfg.italic,
underline: hCfg.underline ? { type: docx.UnderlineType.SINGLE, color: hCfg.color } : undefined,
size: pt(hCfg.size),
color: hCfg.color,
allCaps: hCfg.allCaps,
smallCaps: hCfg.smallCaps,
characterSpacing: hCfg.tracking
})
],
heading: headingLevel,
alignment: align,
spacing: {
before: hCfg.spacing?.before,
after: hCfg.spacing?.after,
line: hCfg.spacing?.line
},
border: borderConfig,
shading: hCfg.shading ? {
fill: hCfg.shading.fill,
color: hCfg.shading.color,
type: docx.ShadingType.CLEAR // usually clear to show fill
} : undefined,
keepNext: true,
keepLines: true
}));
}
// 2. PARAGRAPHS
else if (tagName === 'p') {
let align = docx.AlignmentType.LEFT;
if (cfg.body.align === 'center') align = docx.AlignmentType.CENTER;
if (cfg.body.align === 'right') align = docx.AlignmentType.RIGHT;
if (cfg.body.align === 'both') align = docx.AlignmentType.BOTH;
docxChildren.push(new docx.Paragraph({
children: parseRuns(el, cfg.body),
spacing: {
before: cfg.body.spacing?.before,
after: cfg.body.spacing?.after,
line: cfg.body.spacing?.line,
lineRule: docx.LineRuleType.AUTO
},
alignment: align
}));
}
// 3. BLOCKQUOTES
else if (tagName === 'blockquote') {
docxChildren.push(new docx.Paragraph({
children: parseRuns(el, { ...cfg.body, size: cfg.body.size + 1, color: cfg.accentColor, italic: true } as DocxStyleConfig),
indent: { left: 720 }, // 0.5 inch
border: { left: { color: cfg.accentColor, space: 10, style: docx.BorderStyle.SINGLE, size: 24 } },
shading: { fill: "F8F8F8", type: docx.ShadingType.CLEAR, color: "auto" }, // Default light grey background for quotes
spacing: { before: 200, after: 200, line: 300 }
}));
}
// 4. LISTS
else if (tagName === 'ul' || tagName === 'ol') {
const listItems = Array.from(el.children);
for (const li of listItems) {
docxChildren.push(new docx.Paragraph({
children: parseRuns(li as HTMLElement, cfg.body),
bullet: { level: 0 },
spacing: { before: 80, after: 80 }
}));
}
}
}
// Create Document
const docxFile = new docx.Document({
sections: [{
properties: {
page: {
size: {
width: paperSize === 'A4' ? mmToTwips(210) : inchesToTwips(8.5),
height: paperSize === 'A4' ? mmToTwips(297) : inchesToTwips(11),
},
margin: {
top: inchesToTwips(1),
right: inchesToTwips(1.2),
bottom: inchesToTwips(1),
left: inchesToTwips(1.2),
}
}
},
children: docxChildren
}]
const blob = await generateDocxDocument(htmlContent, {
paperSize,
heading1: cfg.heading1,
heading2: cfg.heading2,
body: cfg.body,
accentColor: cfg.accentColor,
pageBackground,
codeFont,
isDarkTheme,
elements: template.elements,
fonts: template.typography?.fonts,
palette: template.typography?.colors,
id: template.id,
page: template.page,
useTableHeaders
});
const blob = await docx.Packer.toBlob(docxFile);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `typogenie-${style.name.replace(/\s+/g, '-').toLowerCase()}.docx`;
a.click();
window.URL.revokeObjectURL(url);
setSuccessMsg(true);
setTimeout(() => setSuccessMsg(false), 3000);
const arrayBuffer = await blob.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const defaultName = `${inputFileName}.docx`;
const savePath = await save({
defaultPath: defaultName,
filters: [{ name: 'Word Document', extensions: ['docx'] }]
});
if (savePath) {
await writeFile(savePath, uint8Array);
setSuccessMsg(true);
setTimeout(() => setSuccessMsg(false), 3000);
}
} catch (e) {
console.error("Docx Gen Error", e);
alert("Failed to generate DOCX");
alert("Failed to generate DOCX: " + e);
} finally {
setIsExporting(false);
}
};
// Track blob URL for cleanup
const blobUrlRef = useRef<string | null>(null);
// Render preview whenever dependencies change
useEffect(() => {
if (!iframeRef.current) return;
// We already have 'style' from the component scope, but useEffect needs to be robust
const currentStyle = TYPOGRAPHY_STYLES.find(s => s.id === selectedStyleId) || TYPOGRAPHY_STYLES[0];
if (!iframeRef.current || !style) return;
const doc = iframeRef.current.contentDocument;
if (doc) {
const html = `
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="${currentStyle.googleFontsImport}" rel="stylesheet">
<style>
body {
background-color: #52525b;
display: flex;
justify-content: center;
padding: 40px;
margin: 0;
font-family: sans-serif;
}
.page {
background: white;
width: ${paperSize === 'A4' ? '210mm' : '8.5in'};
min-height: ${paperSize === 'A4' ? '297mm' : '11in'};
padding: 25mm;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4);
box-sizing: border-box;
/* Inject default body color to ensure visibility in dark themes */
color: #${currentStyle.wordConfig.body.color};
// DEBUG: Specific markdown constructs for Preview
console.log('=== PREVIEW DEBUG (h3, strong, table, ul/ol, li) ===');
console.log('h3:', JSON.stringify(style.elements?.h3));
console.log('strong:', JSON.stringify(style.elements?.strong));
console.log('table:', JSON.stringify(style.elements?.table));
console.log('th:', JSON.stringify(style.elements?.th));
console.log('td:', JSON.stringify(style.elements?.td));
console.log('ul:', JSON.stringify(style.elements?.ul));
console.log('ol:', JSON.stringify(style.elements?.ol));
console.log('li:', JSON.stringify(style.elements?.li));
console.log('=== END PREVIEW DEBUG ===');
/* User Selected Typography */
${currentStyle.previewCss}
}
.page * { box-sizing: border-box; }
.page img { max-width: 100%; }
.page table { width: 100%; border-collapse: collapse; margin-bottom: 1em; }
.page th, .page td { border: 1px solid #ddd; padding: 8px; text-align: left; }
</style>
</head>
<body>
<div class="page">
${htmlContent}
</div>
</body>
</html>
`;
doc.open();
doc.write(html);
doc.close();
// Cleanup old blob URL
if (blobUrlRef.current) {
URL.revokeObjectURL(blobUrlRef.current);
blobUrlRef.current = null;
}
}, [htmlContent, paperSize, selectedStyleId]);
// Get CSS from template - this generates from elements structure
const templateCss = getPreviewCss(style);
console.log('Generated preview CSS (first 500 chars):', templateCss.substring(0, 500));
// Get page background from template
const pageBg = style.typography?.colors?.background || 'ffffff';
// Build the complete CSS - template CSS now includes .page styles
const allCss = [
/* Reset styles first */
'::-webkit-scrollbar { width: 6px !important; height: 6px !important; }',
'::-webkit-scrollbar-track { background: transparent !important; }',
'::-webkit-scrollbar-thumb { background: #71717a !important; border-radius: 3px !important; }',
'* { scrollbar-width: thin; scrollbar-color: #71717a transparent; box-sizing: border-box; }',
'html, body { margin: 0; padding: 0; min-height: 100%; }',
/* Dark outer background */
'body { background-color: #18181b; padding: 40px 20px; }',
/* Template CSS - includes .page styles with fonts, colors, etc. */
templateCss,
/* Page dimensions (not included in template CSS) */
`.page {`,
` width: ${paperSize === 'A4' ? '210mm' : '8.5in'};`,
` min-height: ${paperSize === 'A4' ? '297mm' : '11in'};`,
` padding: 25mm;`,
` box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4);`,
` box-sizing: border-box;`,
` margin: 0 auto;`,
`}`,
/* Utilities */
'.page img { max-width: 100%; }'
].join('\n');
// Inject CSS directly as inline style tag
const html = `
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="${style.googleFontsImport || 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap'}" rel="stylesheet">
<style>
${allCss}
</style>
</head>
<body>
<div class="page">
${htmlContent}
<script>
console.log('--- STAGE 2: PREVIEW COMPUTED STYLES ---');
setTimeout(() => {
['h1', 'h2', 'h3', 'p', 'table', 'th', 'td', 'ul', 'ol', 'li', 'blockquote', 'code', 'pre'].forEach(tag => {
const el = document.querySelector('.page ' + tag);
if (el) {
const style = window.getComputedStyle(el);
// Get borders specifically
const borderTop = style.borderTopWidth + ' ' + style.borderTopStyle + ' ' + style.borderTopColor;
const borderBottom = style.borderBottomWidth + ' ' + style.borderBottomStyle + ' ' + style.borderBottomColor;
const borderLeft = style.borderLeftWidth + ' ' + style.borderLeftStyle + ' ' + style.borderLeftColor;
const borderRight = style.borderRightWidth + ' ' + style.borderRightStyle + ' ' + style.borderRightColor;
console.log(\`PREVIEW \${tag.toUpperCase()}: \`, {
fontFamily: style.fontFamily,
fontSize: style.fontSize,
color: style.color,
backgroundColor: style.backgroundColor,
fontWeight: style.fontWeight,
border: \`T:\${borderTop} R:\${borderRight} B:\${borderBottom} L:\${borderLeft}\`,
padding: style.padding,
margin: style.margin
});
} else {
console.log(\`PREVIEW \${tag.toUpperCase()}: Not found\`);
}
});
}, 500);
</script>
</div>
</body>
</html>
`;
// Create blob URL for CSP compliance
const blob = new Blob([html], { type: 'text/html' });
const blobUrl = URL.createObjectURL(blob);
blobUrlRef.current = blobUrl;
iframeRef.current.src = blobUrl;
// Cleanup on unmount
return () => {
if (blobUrlRef.current) {
URL.revokeObjectURL(blobUrlRef.current);
blobUrlRef.current = null;
}
};
}, [htmlContent, paperSize, selectedStyleId, templates, style]);
if (!style) {
return <div className="h-screen flex items-center justify-center text-white">Loading...</div>;
}
return (
<div className="h-screen flex flex-col bg-zinc-900">
{/* Toolbar */}
<div className="sticky top-0 z-50 bg-zinc-950/90 backdrop-blur-md border-b border-zinc-800 p-4">
<motion.div className="h-screen flex flex-col bg-zinc-950" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.3 }}>
<motion.div className="sticky top-0 z-50 bg-zinc-950/90 backdrop-blur-md border-b border-zinc-800 p-4" initial={{ y: -20, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }}>
<div className="max-w-7xl mx-auto flex flex-col sm:flex-row justify-between items-center gap-4">
<button
onClick={onBack}
className="flex items-center gap-2 text-zinc-400 hover:text-white transition-colors"
>
<motion.button ref={backButtonRef} onClick={onBack} onFocus={() => setFocusedElement('back')} whileHover={{ x: -3 }} whileTap={{ scale: 0.95 }} className="flex items-center gap-2 text-zinc-400 hover:text-white transition-colors outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-zinc-950 rounded-lg px-2 py-1">
<ArrowLeft size={20} />
<span>Back to Editor</span>
</button>
</motion.button>
<div className="flex flex-col sm:flex-row items-center gap-6">
{/* Font List */}
<div className="flex items-center gap-2 text-sm text-zinc-500">
<span className="hidden md:inline">Fonts:</span>
<div className="flex gap-2">
{usedFonts.map(font => (
<a
key={font}
href={`https://fonts.google.com/specimen/${font.replace(/\s+/g, '+')}`}
target="_blank"
rel="noopener noreferrer"
className="px-2 py-1 bg-zinc-900 border border-zinc-800 hover:border-zinc-600 rounded text-zinc-300 hover:text-white transition-all flex items-center gap-1.5 text-xs font-medium"
title={`Download ${font} from Google Fonts`}
>
{font}
<ExternalLink size={10} />
</a>
))}
</div>
</div>
<FontList fonts={usedFonts} />
<div className="h-4 w-px bg-zinc-800 hidden sm:block" />
<ZoomControl zoom={uiZoom} onZoomChange={onZoomChange} />
<div className="h-4 w-px bg-zinc-800 hidden sm:block" />
<div className="flex items-center gap-4">
<span className="text-zinc-500 text-sm hidden sm:inline">
Format: {paperSize}
</span>
<button
onClick={() => {
// Ensure we pass a string for styleId
const sid = selectedStyleId || 'swiss-grid';
generateDocx(sid);
}}
<span className="text-zinc-500 text-sm hidden sm:inline">Format: {paperSize}</span>
<motion.button
ref={saveButtonRef}
onClick={handleSave}
onFocus={() => setFocusedElement('save')}
disabled={isExporting}
className={`flex items-center gap-2 px-5 py-2.5 rounded-lg font-semibold transition-colors shadow-lg
${successMsg ? 'bg-emerald-600 text-white' : 'bg-blue-600 text-white hover:bg-blue-500'}
${isExporting ? 'opacity-50 cursor-wait' : ''}`}
whileHover={{ scale: 1.05, boxShadow: '0 0 20px rgba(59, 130, 246, 0.4)' }}
whileTap={{ scale: 0.95 }}
className={`flex items-center gap-2 px-5 py-2.5 rounded-lg font-semibold transition-colors shadow-lg ${successMsg ? 'bg-emerald-600 text-white' : 'bg-blue-600 text-white hover:bg-blue-500'} ${isExporting ? 'opacity-50 cursor-wait' : ''}`}
>
{successMsg ? (
<><CheckCircle2 size={18} /><span>Downloaded!</span></>
) : (
<><FileText size={18} /><span>{isExporting ? 'Generating...' : 'Download Word Doc'}</span></>
)}
</button>
<AnimatePresence mode="wait">
{successMsg ? (
<motion.div key="success" initial={{ scale: 0 }} animate={{ scale: 1 }} exit={{ scale: 0 }} className="flex items-center gap-2">
<CheckCircle2 size={18} />
<span>Saved!</span>
</motion.div>
) : (
<motion.div key="default" initial={{ scale: 0 }} animate={{ scale: 1 }} exit={{ scale: 0 }} className="flex items-center gap-2">
{isExporting ? <Loader2 size={18} className="animate-spin" /> : <FileText size={18} />}
<span>{isExporting ? 'Generating...' : 'Save Word Doc'}</span>
</motion.div>
)}
</AnimatePresence>
</motion.button>
</div>
</div>
</div>
</motion.div>
<div className="flex-grow relative bg-zinc-950 overflow-hidden preview-container">
<iframe ref={iframeRef} className="w-full h-full border-0 block" title="Report Preview" />
</div>
<div className="flex-grow relative bg-zinc-800 overflow-hidden">
<iframe
ref={iframeRef}
className="w-full h-full border-0 block"
title="Report Preview"
/>
</div>
</div>
<ExportOptionsModal
isOpen={showExportModal}
onClose={() => setShowExportModal(false)}
onExport={handleExportConfirm}
/>
</motion.div>
);
};
};

View File

@@ -1,111 +1,281 @@
import React, { useState, useMemo, useRef, useEffect } from 'react';
import { TYPOGRAPHY_STYLES } from '../constants';
import { StyleOption, PaperSize, StyleCategory } from '../types';
import { Check, Type, Printer, Search } from 'lucide-react';
import { PaperSize, StyleOption } from '../types';
import { Check, Type, Printer, Search, Heart } from 'lucide-react';
import { motion } from 'motion/react';
import { getPreviewCss } from '../services/templateRenderer';
interface StyleSelectorProps {
templates: StyleOption[];
categories: string[];
selectedStyle: string | null;
onSelectStyle: (id: string) => void;
selectedPaperSize: PaperSize;
onSelectPaperSize: (size: PaperSize) => void;
onGenerate: () => void;
isLoading?: boolean;
error?: string | null;
}
const SAMPLE_CONTENT = `
<h1>The Art of Typography</h1>
<p>Typography is the art and technique of arranging type to make written language legible, readable, and appealing when displayed. The arrangement of type involves selecting typefaces, point sizes, line lengths, line-spacing, and letter-spacing.</p>
<p>Typography is the art and technique of arranging type to make written language <strong>legible</strong>, <em>readable</em>, and <u>appealing</u> when displayed. The arrangement involves selecting typefaces, point sizes, line lengths, and letter-spacing.</p>
<h2>1. Visual Hierarchy</h2>
<p>Visual hierarchy enables the reader to understand the importance of different sections. By using size, weight, and color, we guide the eye through the document in a logical flow.</p>
<h3>Key Principles</h3>
<p>Effective typography follows timeless principles. Each element serves a purpose, creating harmony between form and function.</p>
<h4>Micro-Typography</h4>
<p>The smallest details matter most. Kerning, tracking, and leading work together to create <code>readable</code> text that flows naturally.</p>
<blockquote>
"Design is not just what it looks like and feels like. Design is how it works."
"Design is not just what it looks like and feels like. Design is how it works." — Steve Jobs
</blockquote>
<h2>2. Key Elements</h2>
<h2>2. Essential Elements</h2>
<h3>Unordered Principles</h3>
<ul>
<li><strong>Typeface:</strong> The design of the letters.</li>
<li><strong>Contrast:</strong> Distinguishing elements effectively.</li>
<li><strong>Consistency:</strong> Maintaining a coherent structure.</li>
<li><strong>Typeface:</strong> The personality of your document</li>
<li><strong>Contrast:</strong> Distinguishing elements effectively</li>
<li><strong>Consistency:</strong> Maintaining a coherent structure</li>
<li><strong>Whitespace:</strong> Breathing room for the eyes</li>
</ul>
<p>Good typography is invisible. It should not distract the reader but rather enhance the reading experience, ensuring the message is delivered clearly and effectively.</p>
<h3>Process Steps</h3>
<ol>
<li>Analyze your content and audience</li>
<li>Choose appropriate typefaces</li>
<li>Establish hierarchy and rhythm</li>
<li>Test and refine the layout</li>
</ol>
<hr>
<h2>3. Technical Implementation</h2>
<p>When implementing typography in code, precision is essential. Here's a basic example:</p>
<pre>function applyTypography(element) {
element.style.fontFamily = 'Georgia, serif';
element.style.lineHeight = '1.6';
element.style.letterSpacing = '0.02em';
}</pre>
<h3>Font Properties Comparison</h3>
<table>
<tr>
<th>Property</th>
<th>Serif</th>
<th>Sans-Serif</th>
</tr>
<tr>
<td>Readability</td>
<td>High (print)</td>
<td>High (screen)</td>
</tr>
<tr>
<td>Personality</td>
<td>Classic</td>
<td>Modern</td>
</tr>
<tr>
<td>Use Case</td>
<td>Body text</td>
<td>Headings</td>
</tr>
</table>
<p>Good typography is invisible. It should not distract the reader but enhance the experience. Visit <a href="https://typography.com">typography.com</a> to learn more about professional typesetting.</p>
<h5>Advanced Techniques</h5>
<p>Mastering typography requires understanding both the rules and when to break them creatively.</p>
<h6>Final Thoughts</h6>
<p>Typography transforms ordinary text into extraordinary communication. Every choice matters.</p>
`;
export const StyleSelector: React.FC<StyleSelectorProps> = ({
templates,
categories,
selectedStyle,
onSelectStyle,
selectedPaperSize,
onSelectPaperSize,
onGenerate
onGenerate,
isLoading,
error
}) => {
const [activeCategory, setActiveCategory] = useState<StyleCategory | 'All'>('All');
const [activeCategory, setActiveCategory] = useState<string>('All');
const [searchQuery, setSearchQuery] = useState('');
const [favorites, setFavorites] = useState<string[]>(() => {
try {
const saved = localStorage.getItem('favoriteTemplates');
return saved ? JSON.parse(saved) : [];
} catch (e) {
console.error('Failed to parse favorites', e);
return [];
}
});
const iframeRef = useRef<HTMLIFrameElement>(null);
// Dynamically extract categories from styles and sort them
const categories = useMemo(() => {
const cats = new Set(TYPOGRAPHY_STYLES.map(s => s.category));
return Array.from(cats).sort();
}, []);
// Persist favorites
useEffect(() => {
localStorage.setItem('favoriteTemplates', JSON.stringify(favorites));
}, [favorites]);
const toggleFavorite = (e: React.MouseEvent, id: string) => {
e.stopPropagation();
setFavorites(prev =>
prev.includes(id) ? prev.filter(fid => fid !== id) : [...prev, id]
);
};
const filteredStyles = useMemo(() => {
let styles = TYPOGRAPHY_STYLES;
if (activeCategory !== 'All') {
styles = TYPOGRAPHY_STYLES.filter(style => style.category === activeCategory);
let styles = templates;
// Filter by category
if (activeCategory === 'fav') {
styles = styles.filter(style => favorites.includes(style.id));
} else if (activeCategory !== 'All') {
styles = templates.filter(style => style.category === activeCategory);
}
// Filter by search
if (searchQuery.trim()) {
const query = searchQuery.toLowerCase();
styles = styles.filter(style =>
style.name.toLowerCase().includes(query) ||
style.description.toLowerCase().includes(query) ||
style.category.toLowerCase().includes(query)
);
}
// Always sort alphabetically by name
return [...styles].sort((a, b) => a.name.localeCompare(b.name));
}, [activeCategory]);
}, [templates, activeCategory, searchQuery, favorites]);
const currentStyleObj = useMemo(() =>
TYPOGRAPHY_STYLES.find(s => s.id === selectedStyle),
[selectedStyle]);
const currentStyleObj = useMemo(() =>
templates.find(s => s.id === selectedStyle),
[templates, selectedStyle]);
// Track blob URL for cleanup
const blobUrlRef = useRef<string | null>(null);
// Update preview when style changes
useEffect(() => {
if (!iframeRef.current || !currentStyleObj) return;
const doc = iframeRef.current.contentDocument;
if (doc) {
const html = `
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="${currentStyleObj.googleFontsImport}" rel="stylesheet">
<style>
body {
background-color: #ffffff;
margin: 0;
padding: 40px;
/* Inject default body color from config to ensure lists/quotes inherit correctly */
color: #${currentStyleObj.wordConfig.body.color};
font-family: sans-serif;
/* Apply Selected Style CSS */
${currentStyleObj.previewCss}
}
/* Scrollbar styling for the preview iframe */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #f1f1f1; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
</style>
</head>
<body>
${SAMPLE_CONTENT}
</body>
</html>
`;
doc.open();
doc.write(html);
doc.close();
// Cleanup old blob URL
if (blobUrlRef.current) {
URL.revokeObjectURL(blobUrlRef.current);
blobUrlRef.current = null;
}
}, [currentStyleObj]);
// Get CSS from template
const templateCss = getPreviewCss(currentStyleObj);
// DEBUG: Specific markdown constructs for StyleSelector
console.log('=== STYLESELECTOR DEBUG (h3, strong, table, ul/ol, li) ===');
console.log('h3:', JSON.stringify(currentStyleObj.elements?.h3));
console.log('strong:', JSON.stringify(currentStyleObj.elements?.strong));
console.log('table:', JSON.stringify(currentStyleObj.elements?.table));
console.log('th:', JSON.stringify(currentStyleObj.elements?.th));
console.log('td:', JSON.stringify(currentStyleObj.elements?.td));
console.log('ul:', JSON.stringify(currentStyleObj.elements?.ul));
console.log('ol:', JSON.stringify(currentStyleObj.elements?.ol));
console.log('li:', JSON.stringify(currentStyleObj.elements?.li));
console.log('=== END STYLESELECTOR DEBUG ===');
const html = `
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="${currentStyleObj.googleFontsImport}" rel="stylesheet">
<style>
/* Reset */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* App background (dark) */
body {
background-color: #18181b;
padding: 40px;
min-height: 100vh;
}
/* Template CSS - includes .page styles */
${templateCss}
/* Page dimensions (not included in template) */
.page {
width: ${selectedPaperSize === 'A4' ? '210mm' : '8.5in'};
min-height: ${selectedPaperSize === 'A4' ? '297mm' : '11in'};
margin: 0 auto;
padding: 25mm;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);
}
/* Scrollbar */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #71717a; border-radius: 4px; }
</style>
</head>
<body>
<div class="page">
${SAMPLE_CONTENT}
</div>
</body>
</html>
`;
// Create blob URL for CSP compliance
const blob = new Blob([html], { type: 'text/html' });
const blobUrl = URL.createObjectURL(blob);
blobUrlRef.current = blobUrl;
iframeRef.current.src = blobUrl;
// Cleanup on unmount
return () => {
if (blobUrlRef.current) {
URL.revokeObjectURL(blobUrlRef.current);
blobUrlRef.current = null;
}
};
}, [currentStyleObj, selectedPaperSize]);
if (isLoading) {
return (
<div className="w-full h-full flex items-center justify-center">
<motion.div className="text-center" initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<motion.div className="w-12 h-12 border-4 border-indigo-500/30 border-t-indigo-500 rounded-full mx-auto mb-4" animate={{ rotate: 360 }} transition={{ duration: 1, repeat: Infinity, ease: "linear" }} />
<p className="text-zinc-400">Loading templates...</p>
</motion.div>
</div>
);
}
if (error) {
return (
<div className="w-full h-full flex flex-col items-center justify-center p-8 text-center">
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} className="max-w-md">
<div className="w-16 h-16 bg-red-500/10 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl"></span>
</div>
<h3 className="text-xl font-semibold text-white mb-2">Template Error</h3>
<p className="text-zinc-400 mb-6">{error}</p>
</motion.div>
</div>
);
}
return (
<div className="w-full h-[calc(100vh-140px)] min-h-[600px] flex flex-col gap-6 max-w-[1600px] mx-auto">
<div className="w-full h-full min-h-[600px] flex flex-col gap-6 max-w-[1600px] mx-auto">
{/* Top Controls */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 px-1">
<div className="flex items-center gap-3">
@@ -113,26 +283,25 @@ export const StyleSelector: React.FC<StyleSelectorProps> = ({
<Type size={24} />
</div>
<div>
<h2 className="text-2xl font-bold text-zinc-100">Select Typography Style</h2>
<p className="text-zinc-400 text-sm">Browse {TYPOGRAPHY_STYLES.length} professional templates</p>
<h2 className="text-2xl font-bold text-zinc-100">Select Typography Style</h2>
<p className="text-zinc-400 text-sm">Browse {templates.length} professional templates</p>
</div>
</div>
<div className="flex items-center gap-4 bg-zinc-900/50 p-1.5 rounded-xl border border-zinc-800/50">
<div className="flex items-center gap-2 text-zinc-400 px-3">
<Printer size={16} />
<span className="text-xs font-semibold uppercase tracking-wider">Format</span>
</div>
<div className="flex gap-1">
<div className="flex items-center gap-2 text-zinc-400 px-3">
<Printer size={16} />
<span className="text-xs font-semibold uppercase tracking-wider">Format</span>
</div>
<div className="flex gap-1">
{(['Letter', 'A4'] as PaperSize[]).map((size) => (
<button
key={size}
onClick={() => onSelectPaperSize(size)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${
selectedPaperSize === size
? 'bg-zinc-700 text-white shadow-sm'
: 'text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800'
}`}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${selectedPaperSize === size
? 'bg-zinc-700 text-white shadow-sm'
: 'text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800'
}`}
>
{size}
</button>
@@ -143,59 +312,99 @@ export const StyleSelector: React.FC<StyleSelectorProps> = ({
{/* Main Split Layout */}
<div className="flex-1 grid grid-cols-1 lg:grid-cols-12 gap-6 min-h-0">
{/* LEFT COLUMN: Style List */}
<div className="lg:col-span-4 flex flex-col gap-4 min-h-0 bg-zinc-900/30 rounded-2xl border border-zinc-800/50 overflow-hidden">
{/* Category Filter Tabs */}
<div className="p-4 border-b border-zinc-800/50 overflow-x-auto no-scrollbar">
<div className="flex gap-2">
<button
onClick={() => setActiveCategory('All')}
className={`px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-all ${
activeCategory === 'All'
? 'bg-white text-black'
: 'bg-zinc-800 text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200'
}`}
>
All
</button>
{categories.map(cat => (
<div className="flex flex-col border-b border-zinc-800/50 bg-zinc-900/20">
<div className="p-4 overflow-x-auto no-scrollbar pb-2">
<div className="flex gap-2">
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={`px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-all ${
activeCategory === cat
? 'bg-indigo-500 text-white shadow-lg shadow-indigo-500/20'
: 'bg-zinc-800 text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200'
}`}
onClick={() => setActiveCategory('fav')}
className={`px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-all flex items-center gap-1.5 ${activeCategory === 'fav'
? 'bg-rose-500 text-white shadow-lg shadow-rose-500/20'
: 'bg-zinc-800 text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200'
}`}
>
{cat}
<Heart size={12} className={activeCategory === 'fav' ? 'fill-current' : ''} />
fav
</button>
))}
<div className="w-px h-6 bg-zinc-800 mx-1 self-center" />
<button
onClick={() => setActiveCategory('All')}
className={`px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-all ${activeCategory === 'All'
? 'bg-white text-black'
: 'bg-zinc-800 text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200'
}`}
>
All
</button>
{categories.map(cat => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={`px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-all ${activeCategory === cat
? 'bg-indigo-500 text-white shadow-lg shadow-indigo-500/20'
: 'bg-zinc-800 text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200'
}`}
>
{cat}
</button>
))}
</div>
</div>
{/* Search Input */}
<div className="px-4 pb-4">
<div className="relative group">
<Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-500 group-focus-within:text-indigo-400 transition-colors" />
<input
type="text"
placeholder="Search templates..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full bg-zinc-900 border border-zinc-700 rounded-xl py-2 pl-9 pr-4 text-sm text-zinc-200 placeholder:text-zinc-600 focus:outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/50 transition-all"
/>
</div>
</div>
</div>
{/* Scrollable List */}
<div className="flex-1 overflow-y-auto p-4 space-y-3 custom-scrollbar">
{filteredStyles.map((style) => (
{filteredStyles.length === 0 ? (
<div className="text-center py-8 text-zinc-500">
<p className="text-sm">No templates found</p>
</div>
) : filteredStyles.map((style) => (
<div
key={style.id}
onClick={() => onSelectStyle(style.id)}
className={`p-4 rounded-xl border transition-all cursor-pointer group relative
${selectedStyle === style.id
? 'border-indigo-500 bg-indigo-500/10 shadow-[0_0_15px_rgba(99,102,241,0.15)]'
${selectedStyle === style.id
? 'border-indigo-500 bg-indigo-500/10 shadow-[0_0_15px_rgba(99,102,241,0.15)]'
: 'border-zinc-800 bg-zinc-900/40 hover:border-zinc-700 hover:bg-zinc-800'}`}
>
<div className="flex justify-between items-start mb-1">
<h3 className={`font-bold text-sm ${selectedStyle === style.id ? 'text-white' : 'text-zinc-300'}`}>
{style.name}
</h3>
{selectedStyle === style.id && (
<div className="bg-indigo-500 rounded-full p-0.5">
<Check size={12} className="text-white" />
</div>
)}
<div className="flex justify-between items-start mb-1 gap-2">
<h3 className={`font-bold text-sm ${selectedStyle === style.id ? 'text-white' : 'text-zinc-300'}`}>
{style.name}
</h3>
<div className="flex items-center gap-2 shrink-0">
<button
onClick={(e) => toggleFavorite(e, style.id)}
className={`p-1.5 rounded-full transition-all ${favorites.includes(style.id)
? 'text-rose-400 bg-rose-500/10 hover:bg-rose-500/20'
: 'text-zinc-600 hover:text-zinc-400 hover:bg-zinc-700/50'
}`}
>
<Heart size={14} className={favorites.includes(style.id) ? 'fill-current' : ''} />
</button>
{selectedStyle === style.id && (
<div className="bg-indigo-500 rounded-full p-0.5">
<Check size={12} className="text-white" />
</div>
)}
</div>
</div>
<p className="text-xs text-zinc-500 line-clamp-2 leading-relaxed mb-2">{style.description}</p>
<div className="flex items-center gap-2">
@@ -211,7 +420,7 @@ export const StyleSelector: React.FC<StyleSelectorProps> = ({
{/* RIGHT COLUMN: Preview Window */}
<div className="lg:col-span-8 flex flex-col min-h-0 bg-zinc-950 rounded-2xl border border-zinc-800 shadow-2xl relative overflow-hidden">
{/* Preview Header */}
<div className="h-12 border-b border-zinc-800 bg-zinc-900/50 flex items-center px-4 justify-between">
<div className="flex items-center gap-2">
@@ -247,17 +456,17 @@ export const StyleSelector: React.FC<StyleSelectorProps> = ({
{/* Bottom Action Bar */}
<div className="p-4 border-t border-zinc-800 bg-zinc-900/50 flex justify-end items-center gap-4">
<button
onClick={onGenerate}
disabled={!selectedStyle}
className="flex items-center gap-2 px-6 py-3 bg-indigo-600 hover:bg-indigo-500 text-white font-bold rounded-xl transition-all shadow-lg hover:shadow-indigo-500/25 disabled:opacity-50 disabled:cursor-not-allowed"
>
<span>Apply Style & Convert</span>
<Printer size={18} />
</button>
<button
onClick={onGenerate}
disabled={!selectedStyle}
className="flex items-center gap-2 px-6 py-3 bg-indigo-600 hover:bg-indigo-500 text-white font-bold rounded-xl transition-all shadow-lg hover:shadow-indigo-500/25 disabled:opacity-50 disabled:cursor-not-allowed"
>
<span>Apply Style & Convert</span>
<Printer size={18} />
</button>
</div>
</div>
</div>
</div>
);
};
};

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { motion } from 'motion/react';
import { ZoomIn, ZoomOut } from 'lucide-react';
interface ZoomControlProps {
zoom: number;
onZoomChange: (zoom: number) => void;
}
export const ZoomControl: React.FC<ZoomControlProps> = ({ zoom, onZoomChange }) => {
const decreaseZoom = () => onZoomChange(Math.max(50, zoom - 10));
const increaseZoom = () => onZoomChange(Math.min(200, zoom + 10));
return (
<div className="flex items-center gap-2 bg-zinc-900/80 rounded-lg border border-zinc-800 px-2 py-1">
<motion.button
onClick={decreaseZoom}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-1 text-zinc-400 hover:text-white transition-colors"
aria-label="Zoom out"
>
<ZoomOut size={16} />
</motion.button>
<span className="text-xs font-medium text-zinc-300 min-w-[3rem] text-center">
{zoom}%
</span>
<motion.button
onClick={increaseZoom}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-1 text-zinc-400 hover:text-white transition-colors"
aria-label="Zoom in"
>
<ZoomIn size={16} />
</motion.button>
</div>
);
};

View File

@@ -1,3 +1,48 @@
import { ALL_STYLES } from './styles/index';
import React from 'react';
import { StyleOption } from './types';
import {
Minus, BookOpen, Building2, Cpu, Palette,
Sparkles, Heart, GraduationCap, Factory, Globe,
Zap, Coffee, Leaf
} from 'lucide-react';
export const TYPOGRAPHY_STYLES = ALL_STYLES;
// Category icons mapping
export const CATEGORY_ICONS: Record<string, React.ComponentType<{ size?: number }>> = {
'Minimalist': Minus,
'Editorial': BookOpen,
'Corporate': Building2,
'Tech': Cpu,
'Creative': Palette,
'Vintage': Sparkles,
'Academic': GraduationCap,
'Lifestyle': Heart,
'Industrial': Factory,
'Nature': Leaf,
'Core': Globe,
'Other': Zap
};
// Category display names mapping (for icons)
export const getCategoryIcon = (category: string) => {
// Try exact match first
if (CATEGORY_ICONS[category]) {
return CATEGORY_ICONS[category];
}
// Try common variations
const normalized = category.toLowerCase();
if (normalized.includes('minimal')) return Minus;
if (normalized.includes('edit')) return BookOpen;
if (normalized.includes('corp')) return Building2;
if (normalized.includes('tech')) return Cpu;
if (normalized.includes('creat')) return Palette;
if (normalized.includes('vint')) return Sparkles;
if (normalized.includes('acad')) return GraduationCap;
if (normalized.includes('life')) return Heart;
if (normalized.includes('indust')) return Factory;
return Zap;
};
// Templates are now loaded dynamically from TypoGenie-Data/templates/
// See README-TEMPLATES.md for documentation on creating custom templates

View File

@@ -0,0 +1,196 @@
import React, { useEffect, useCallback, useRef, useState } from 'react';
interface UseKeyboardNavOptions {
onArrowUp?: () => void;
onArrowDown?: () => void;
onArrowLeft?: () => void;
onArrowRight?: () => void;
onEnter?: () => void;
onEscape?: () => void;
onTab?: () => void;
onShiftTab?: () => void;
onSpace?: () => void;
onHome?: () => void;
onEnd?: () => void;
onPageUp?: () => void;
onPageDown?: () => void;
onCtrlEnter?: () => void;
disabled?: boolean;
}
export const useKeyboardNavigation = (options: UseKeyboardNavOptions, deps: React.DependencyList) => {
const optionsRef = useRef(options);
optionsRef.current = options;
useEffect(() => {
if (optionsRef.current.disabled) return;
const handleKeyDown = (e: KeyboardEvent) => {
const opts = optionsRef.current;
// Handle Ctrl+Enter separately
if (e.key === 'Enter' && e.ctrlKey) {
if (opts.onCtrlEnter) {
e.preventDefault();
opts.onCtrlEnter();
}
return;
}
switch (e.key) {
case 'ArrowUp':
if (opts.onArrowUp) {
e.preventDefault();
opts.onArrowUp();
}
break;
case 'ArrowDown':
if (opts.onArrowDown) {
e.preventDefault();
opts.onArrowDown();
}
break;
case 'ArrowLeft':
if (opts.onArrowLeft) {
e.preventDefault();
opts.onArrowLeft();
}
break;
case 'ArrowRight':
if (opts.onArrowRight) {
e.preventDefault();
opts.onArrowRight();
}
break;
case 'Enter':
if (opts.onEnter) {
e.preventDefault();
opts.onEnter();
}
break;
case 'Escape':
if (opts.onEscape) {
e.preventDefault();
opts.onEscape();
}
break;
case 'Tab':
if (e.shiftKey && opts.onShiftTab) {
e.preventDefault();
opts.onShiftTab();
} else if (!e.shiftKey && opts.onTab) {
e.preventDefault();
opts.onTab();
}
break;
case ' ':
if (opts.onSpace) {
e.preventDefault();
opts.onSpace();
}
break;
case 'Home':
if (opts.onHome) {
e.preventDefault();
opts.onHome();
}
break;
case 'End':
if (opts.onEnd) {
e.preventDefault();
opts.onEnd();
}
break;
case 'PageUp':
if (opts.onPageUp) {
e.preventDefault();
opts.onPageUp();
}
break;
case 'PageDown':
if (opts.onPageDown) {
e.preventDefault();
opts.onPageDown();
}
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, deps);
};
export const useFocusableList = <T extends HTMLElement>(
itemCount: number,
options: {
onSelect?: (index: number) => void;
onEscape?: () => void;
orientation?: 'horizontal' | 'vertical';
wrap?: boolean;
} = {}
) => {
const [focusedIndex, setFocusedIndex] = useState(-1);
const itemRefs = useRef<(T | null)[]>([]);
const { onSelect, onEscape, orientation = 'vertical', wrap = true } = options;
const focusItem = useCallback((index: number) => {
if (index < 0 || index >= itemCount) return;
setFocusedIndex(index);
itemRefs.current[index]?.focus();
itemRefs.current[index]?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, [itemCount]);
const handleArrowNext = useCallback(() => {
const nextIndex = focusedIndex + 1;
if (nextIndex < itemCount) {
focusItem(nextIndex);
} else if (wrap) {
focusItem(0);
}
}, [focusedIndex, itemCount, wrap, focusItem]);
const handleArrowPrev = useCallback(() => {
const prevIndex = focusedIndex - 1;
if (prevIndex >= 0) {
focusItem(prevIndex);
} else if (wrap) {
focusItem(itemCount - 1);
}
}, [focusedIndex, itemCount, wrap, focusItem]);
const handleEnter = useCallback(() => {
if (focusedIndex >= 0 && onSelect) {
onSelect(focusedIndex);
}
}, [focusedIndex, onSelect]);
const handleHome = useCallback(() => {
focusItem(0);
}, [focusItem]);
const handleEnd = useCallback(() => {
focusItem(itemCount - 1);
}, [focusItem, itemCount]);
const setItemRef = useCallback((index: number) => (el: T | null) => {
itemRefs.current[index] = el;
}, []);
useKeyboardNavigation({
[orientation === 'vertical' ? 'onArrowDown' : 'onArrowRight']: handleArrowNext,
[orientation === 'vertical' ? 'onArrowUp' : 'onArrowLeft']: handleArrowPrev,
onEnter: handleEnter,
onEscape,
onHome: handleHome,
onEnd: handleEnd,
}, [handleArrowNext, handleArrowPrev, handleEnter, onEscape, handleHome, handleEnd]);
return {
focusedIndex,
setFocusedIndex,
focusItem,
setItemRef,
itemRefs,
};
};

86
src/hooks/useSettings.ts Normal file
View File

@@ -0,0 +1,86 @@
import { useState, useEffect, useCallback } from 'react';
interface Settings {
uiZoom: number;
}
const DEFAULT_SETTINGS: Settings = {
uiZoom: 100,
};
export const useSettings = () => {
const [settings, setSettings] = useState<Settings>(DEFAULT_SETTINGS);
const [isLoaded, setIsLoaded] = useState(false);
// Load settings from localStorage
useEffect(() => {
const loadSettings = () => {
try {
const saved = localStorage.getItem('typogenie-settings');
if (saved) {
const parsed = JSON.parse(saved);
const merged = { ...DEFAULT_SETTINGS, ...parsed };
setSettings(merged);
// Apply zoom immediately on load
applyZoom(merged.uiZoom);
}
} catch (e) {
console.error('Failed to load settings:', e);
}
setIsLoaded(true);
};
loadSettings();
}, []);
// Apply zoom using font-size scaling on root element
// This is the most reliable way to scale UI while preserving scrollability
const applyZoom = (zoom: number) => {
const scale = zoom / 100;
const root = document.documentElement;
// Base font size is 16px, scale it
root.style.fontSize = `${16 * scale}px`;
// Store zoom value as CSS variable for other calculations
root.style.setProperty('--ui-zoom', String(scale));
root.style.setProperty('--ui-zoom-percent', `${zoom}%`);
// Adjust the app container
const appRoot = document.getElementById('root');
if (appRoot) {
// Remove any transform-based scaling
appRoot.style.transform = 'none';
appRoot.style.width = '100%';
appRoot.style.height = '100%';
}
};
// Save settings when they change
const saveSettings = useCallback((newSettings: Partial<Settings>) => {
const updated = { ...settings, ...newSettings };
setSettings(updated);
// Apply zoom immediately
if (newSettings.uiZoom !== undefined) {
applyZoom(newSettings.uiZoom);
}
try {
localStorage.setItem('typogenie-settings', JSON.stringify(updated));
} catch (e) {
console.error('Failed to save settings:', e);
}
}, [settings]);
const setUiZoom = useCallback((zoom: number) => {
const clamped = Math.max(50, Math.min(200, zoom));
saveSettings({ uiZoom: clamped });
}, [saveSettings]);
return {
settings,
isLoaded,
setUiZoom,
uiZoom: settings.uiZoom,
};
};

88
src/hooks/useTemplates.ts Normal file
View File

@@ -0,0 +1,88 @@
import { useState, useEffect, useCallback, useMemo } from 'react';
import { StyleOption } from '../types';
import { loadTemplates, ensureTemplatesFolder, openTemplatesFolder } from '../services/templateLoader';
interface UseTemplatesReturn {
templates: StyleOption[];
isLoading: boolean;
error: string | null;
refresh: () => Promise<void>;
openFolder: () => Promise<void>;
getTemplate: (id: string) => StyleOption | undefined;
categories: string[];
templatesByCategory: Map<string, StyleOption[]>;
}
export const useTemplates = (): UseTemplatesReturn => {
const [templates, setTemplates] = useState<StyleOption[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const refresh = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
const result = await loadTemplates();
setTemplates(result.templates);
if (result.error) {
setError(result.error);
} else if (result.templates.length === 0) {
setError('No templates found. Click "Open Templates Folder" to see where they should be.');
}
} catch (e) {
console.error('Failed to load templates:', e);
const errorMsg = e instanceof Error ? e.message : String(e);
setError(`Failed to load templates: ${errorMsg}`);
} finally {
setIsLoading(false);
}
}, []);
const openFolder = useCallback(async () => {
try {
const result = await openTemplatesFolder();
if (!result.success && result.error) {
setError(`Failed to open folder: ${result.error}`);
}
} catch (e) {
console.error('Failed to open templates folder:', e);
setError('Failed to open templates folder. Try navigating to the folder manually.');
}
}, []);
useEffect(() => {
refresh();
}, [refresh]);
const getTemplate = useCallback((id: string) => {
return templates.find(t => t.id === id);
}, [templates]);
const categories = useMemo(() => {
const cats = new Set<string>();
templates.forEach(t => cats.add(t.category));
return Array.from(cats).sort();
}, [templates]);
const templatesByCategory = useMemo(() => {
const map = new Map<string, StyleOption[]>();
templates.forEach(t => {
if (!map.has(t.category)) {
map.set(t.category, []);
}
map.get(t.category)!.push(t);
});
return map;
}, [templates]);
return {
templates,
isLoading,
error,
refresh,
openFolder,
getTemplate,
categories,
templatesByCategory,
};
};

110
src/index.css Normal file
View File

@@ -0,0 +1,110 @@
@import "tailwindcss";
/* Custom scrollbar - always visible at consistent size */
* {
scrollbar-width: 6px;
scrollbar-color: #52525b transparent;
}
*::-webkit-scrollbar {
width: 6px;
height: 6px;
background: transparent;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background: #52525b;
border-radius: 3px;
min-height: 40px;
}
*::-webkit-scrollbar-thumb:hover {
background: #71717a;
}
*::-webkit-scrollbar-corner {
background: transparent;
}
@layer base {
:root {
font-size: 16px;
}
body {
font-family: 'Inter', sans-serif;
background-color: #09090b;
color: #e4e4e7;
overflow: hidden;
margin: 0;
padding: 0;
}
#root {
width: 100vw;
height: 100vh;
overflow: hidden;
}
}
/* Force custom scrollbars everywhere */
html ::-webkit-scrollbar,
body ::-webkit-scrollbar,
* ::-webkit-scrollbar {
width: 6px !important;
height: 6px !important;
background: transparent !important;
}
html ::-webkit-scrollbar-track,
body ::-webkit-scrollbar-track,
* ::-webkit-scrollbar-track {
background: transparent !important;
}
html ::-webkit-scrollbar-thumb,
body ::-webkit-scrollbar-thumb,
* ::-webkit-scrollbar-thumb {
background: #52525b !important;
border-radius: 3px !important;
min-height: 40px !important;
}
html ::-webkit-scrollbar-thumb:hover,
body ::-webkit-scrollbar-thumb:hover,
* ::-webkit-scrollbar-thumb:hover {
background: #71717a !important;
}
/* Firefox */
*,
*::before,
*::after {
scrollbar-width: 6px !important;
scrollbar-color: #52525b transparent !important;
}
/* No scrollbar utility */
.no-scrollbar::-webkit-scrollbar {
display: none !important;
}
.no-scrollbar {
-ms-overflow-style: none !important;
scrollbar-width: none !important;
}
/* Focus ring spacing */
.focus-ring-spacing {
padding: 4px;
}
.focus-ring-spacing:focus-within {
outline: 2px solid rgba(99, 102, 241, 0.3);
outline-offset: -2px;
border-radius: 8px;
}

View File

@@ -1,6 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
const rootElement = document.getElementById('root');
if (!rootElement) {
@@ -12,4 +13,4 @@ root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
);

View File

@@ -0,0 +1,74 @@
// Default templates embedded in the app
// These are written to disk on first run if no templates exist
export const defaultTemplates: Record<string, string> = {
"academic/research-paper.json": JSON.stringify({
"id": "research-paper",
"name": "Research Paper",
"category": "Academic",
"description": "Formal academic formatting for research papers and scholarly articles.",
"vibe": "Scholarly, Formal, Precise",
"googleFontsImport": "https://fonts.googleapis.com/css2?family=Crimson+Text:ital,wght@0,400;0,600;0,700;1,400&display=swap",
"typography": {
"fonts": { "heading": "Crimson Text", "body": "Crimson Text", "code": "Consolas, Monaco, monospace" },
"colors": { "text": "000000", "textSecondary": "444444", "background": "FFFFFF", "accent": "2F5496", "border": "CCCCCC", "codeBg": "F5F5F5", "blockquoteBorder": "2F5496" }
},
"elements": {
"h1": { "font": "heading", "size": 28, "color": "text", "bold": true, "align": "center", "spacing": { "before": 24, "after": 18, "line": 1.2 } },
"h2": { "font": "heading", "size": 14, "color": "text", "bold": true, "align": "left", "spacing": { "before": 18, "after": 8, "line": 1.2 } },
"h3": { "font": "heading", "size": 12, "color": "text", "bold": true, "align": "left", "spacing": { "before": 14, "after": 6, "line": 1.2 } },
"h4": { "font": "heading", "size": 11, "color": "text", "bold": true, "italic": true, "align": "left", "spacing": { "before": 12, "after": 4, "line": 1.2 } },
"p": { "font": "body", "size": 11, "color": "text", "align": "justify", "spacing": { "before": 0, "after": 0, "line": 1.8 }, "indent": 24 },
"blockquote": { "font": "body", "size": 10.5, "color": "textSecondary", "italic": true, "spacing": { "before": 12, "after": 12, "line": 1.5 }, "indent": 36, "borderLeft": { "color": "accent", "width": 3, "style": "solid" } },
"code": { "font": "code", "size": 9.5, "color": "text", "background": "codeBg" },
"pre": { "font": "code", "size": 9, "color": "text", "background": "codeBg", "spacing": { "before": 12, "after": 12 }, "padding": 12, "border": { "color": "border", "width": 1, "style": "solid" } },
"ul": { "spacing": { "before": 8, "after": 8 }, "indent": 24, "bullet": "disc" },
"ol": { "spacing": { "before": 8, "after": 8 }, "indent": 24, "numbering": "decimal" },
"li": { "font": "body", "size": 11, "spacing": { "before": 2, "after": 2 } },
"strong": { "font": "body", "bold": true },
"em": { "font": "body", "italic": true },
"a": { "font": "body", "color": "accent", "underline": true },
"table": { "spacing": { "before": 12, "after": 12 }, "border": { "color": "border", "width": 1, "style": "solid" } },
"th": { "font": "heading", "size": 10, "bold": true, "background": "F0F0F0", "padding": 8 },
"td": { "font": "body", "size": 10, "padding": 8 },
"hr": { "border": { "color": "border", "width": 1, "style": "solid" }, "spacing": { "before": 18, "after": 18 } }
},
"page": { "margins": { "top": 72, "bottom": 72, "left": 72, "right": 72 } }
}, null, 2),
"core/default.json": JSON.stringify({
"id": "default",
"name": "Default Clean",
"category": "Core",
"description": "Clean, versatile default style that works for any document type.",
"vibe": "Clean, Versatile, Professional",
"googleFontsImport": "https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;600;700&display=swap",
"typography": {
"fonts": { "heading": "Source Sans 3", "body": "Source Sans 3", "code": "Source Sans 3" },
"colors": { "text": "1A1A1A", "textSecondary": "2D2D2D", "background": "FFFFFF", "accent": "2563EB", "border": "E5E5E5", "codeBg": "F5F5F5", "blockquoteBorder": "2563EB" }
},
"elements": {
"h1": { "font": "heading", "size": 28, "color": "text", "bold": true, "align": "left", "spacing": { "before": 20, "after": 10 } },
"h2": { "font": "heading", "size": 15, "color": "textSecondary", "bold": true, "align": "left", "spacing": { "before": 14, "after": 7 } },
"h3": { "font": "heading", "size": 13, "color": "textSecondary", "bold": true, "align": "left", "spacing": { "before": 12, "after": 6 } },
"p": { "font": "body", "size": 11, "color": "333333", "align": "left", "spacing": { "before": 0, "after": 9, "line": 1.6 } },
"blockquote": { "font": "body", "size": 11, "color": "textSecondary", "italic": true, "spacing": { "before": 12, "after": 12 }, "borderLeft": { "color": "accent", "width": 4, "style": "solid" } },
"code": { "font": "code", "size": 9.5, "background": "codeBg" },
"pre": { "font": "code", "size": 9, "background": "codeBg", "padding": 12 },
"ul": { "spacing": { "before": 8, "after": 8 }, "indent": 24 },
"ol": { "spacing": { "before": 8, "after": 8 }, "indent": 24 },
"li": { "font": "body", "size": 11 },
"strong": { "font": "body", "bold": true },
"em": { "font": "body", "italic": true },
"a": { "font": "body", "color": "accent", "underline": true },
"table": { "spacing": { "before": 12, "after": 12 } },
"th": { "font": "heading", "size": 10, "bold": true, "background": "F0F0F0" },
"td": { "font": "body", "size": 10 },
"hr": { "border": { "color": "border", "width": 1, "style": "solid" } }
},
"page": { "margins": { "top": 72, "bottom": 72, "left": 72, "right": 72 } }
}, null, 2)
};
// List of all template paths that should exist
export const templatePaths = Object.keys(defaultTemplates);

View File

@@ -0,0 +1,247 @@
import { invoke } from '@tauri-apps/api/core';
import { StyleOption, DocxStyleConfig, ElementStyle, FontConfig, ColorPalette } from '../types';
import { generatePreviewCss, generateWordConfig } from './templateRenderer';
interface RawTemplate {
id: string;
name: string;
category?: string;
description?: string;
vibe?: string;
googleFontsImport?: string;
typography?: {
fonts?: FontConfig;
colors?: ColorPalette;
};
elements?: Record<string, ElementStyle>;
page?: Record<string, any>;
wordConfig?: Record<string, any>;
previewCss?: string;
[key: string]: any;
}
interface TemplateFile {
name: string;
content: string;
category: string; // Folder-based category from Rust
}
/**
* Resolves a color reference or returns the color directly
*/
function resolveColor(color: string | undefined, palette: ColorPalette): string {
if (!color) return '#000000';
if (palette[color as keyof ColorPalette]) {
return '#' + palette[color as keyof ColorPalette];
}
return color.startsWith('#') ? color : '#' + color;
}
/**
* Resolves a font reference or returns the font directly
*/
function resolveFont(font: string | undefined, fonts: FontConfig): string {
if (!font) return 'serif';
if (fonts[font as keyof FontConfig]) {
return fonts[font as keyof FontConfig]!;
}
return font;
}
/**
* Generates preview CSS from unified template structure
*/
/**
/**
* Validate that a parsed object conforms to the StyleOption interface
*/
function validateTemplate(raw: RawTemplate, filename: string): StyleOption | null {
if (!raw.id || typeof raw.id !== 'string') {
console.error(`Invalid template in ${filename}: missing or invalid 'id'`);
return null;
}
if (!raw.name || typeof raw.name !== 'string') {
console.error(`Invalid template in ${filename}: missing or invalid 'name'`);
return null;
}
const hasUnifiedStructure = raw.elements && raw.typography;
let wordConfig: StyleOption['wordConfig'];
let previewCss: string;
if (hasUnifiedStructure) {
const fonts = raw.typography?.fonts || {};
const palette = raw.typography?.colors || {};
const elements = raw.elements || {};
// Only generate wordConfig if it's missing or incomplete in the raw template
// Prefer the raw wordConfig if it exists (allows manual overrides)
if (raw.wordConfig && Object.keys(raw.wordConfig).length > 0) {
wordConfig = raw.wordConfig as StyleOption['wordConfig'];
} else {
// Use the imported generator which now has better logic
wordConfig = generateWordConfig(elements, fonts, palette) as StyleOption['wordConfig'];
}
// Similarly for previewCss - prefer explicit override
if (raw.previewCss) {
previewCss = raw.previewCss;
} else {
previewCss = generatePreviewCss(elements, fonts, palette);
}
} else {
const defaultHeading1: DocxStyleConfig = {
font: 'Arial', size: 24, color: '000000', align: 'left', spacing: { before: 240, after: 120, line: 240 }
};
const defaultHeading2: DocxStyleConfig = {
font: 'Arial', size: 18, color: '333333', align: 'left', spacing: { before: 200, after: 100, line: 240 }
};
const defaultBody: DocxStyleConfig = {
font: 'Arial', size: 11, color: '333333', align: 'left', spacing: { before: 0, after: 120, line: 276 }
};
wordConfig = raw.wordConfig && raw.wordConfig.heading1 && raw.wordConfig.heading2 && raw.wordConfig.body
? {
heading1: { ...defaultHeading1, ...raw.wordConfig.heading1 },
heading2: { ...defaultHeading2, ...raw.wordConfig.heading2 },
body: { ...defaultBody, ...raw.wordConfig.body },
accentColor: raw.wordConfig.accentColor || '000000'
}
: {
heading1: defaultHeading1,
heading2: defaultHeading2,
body: defaultBody,
accentColor: '000000'
};
previewCss = raw.previewCss || 'font-family: Arial, sans-serif;';
}
const template: StyleOption = {
id: raw.id,
name: raw.name,
category: raw.category || 'Other',
description: raw.description || '',
vibe: raw.vibe || '',
googleFontsImport: raw.googleFontsImport || '',
wordConfig,
previewCss,
// Preserve unified template structure if present
...(raw.typography && { typography: raw.typography }),
...(raw.elements && { elements: raw.elements }),
...(raw.page && { page: raw.page })
};
const extraFields = Object.entries(raw).reduce((acc, [key, val]) => {
if (!['id', 'name', 'category', 'description', 'vibe', 'googleFontsImport', 'wordConfig', 'previewCss', 'typography', 'elements', 'page'].includes(key)) {
acc[key] = val;
}
return acc;
}, {} as Record<string, any>);
return { ...template, ...extraFields };
}
/**
* Debug function to check paths
*/
export async function debugPaths(): Promise<string> {
try {
return await invoke<string>('debug_paths');
} catch (e) {
return `Error: ${e}`;
}
}
/**
* Load all templates from the portable folder (next to EXE)
* Uses Rust backend for file operations since BaseDirectory.Executable doesn't work in JS
*/
export async function loadTemplates(): Promise<{ templates: StyleOption[]; error?: string }> {
const templates: StyleOption[] = [];
let error: string | undefined;
try {
// Use Rust command to read all templates
console.log('Calling read_templates command...');
const files = await invoke<TemplateFile[]>('read_templates');
console.log(`Received ${files.length} templates from Rust`);
for (const file of files) {
try {
const raw = JSON.parse(file.content) as RawTemplate;
const validated = validateTemplate(raw, file.name);
if (validated) {
// Use folder-based category from Rust, fallback to 'Other'
const category = file.category || 'Other';
templates.push({ ...validated, category });
}
} catch (e) {
console.error(`Failed to parse template ${file.name}:`, e);
}
}
if (templates.length === 0) {
error = 'No templates found.';
}
} catch (e) {
console.error('Error in loadTemplates:', e);
error = e instanceof Error ? e.message : String(e);
}
// Sort by category then name
templates.sort((a, b) => {
const catCompare = a.category.localeCompare(b.category);
if (catCompare !== 0) return catCompare;
return a.name.localeCompare(b.name);
});
// Deduplicate by ID (in case of duplicate files)
const seen = new Set<string>();
const deduplicated = templates.filter(t => {
if (seen.has(t.id)) {
console.warn(`Duplicate template ID found: ${t.id}, skipping duplicate`);
return false;
}
seen.add(t.id);
return true;
});
console.log(`Returning ${deduplicated.length} templates (deduplicated from ${templates.length}), error: ${error}`);
return { templates: deduplicated, error };
}
/**
* Open the templates folder in the system file explorer
*/
export async function openTemplatesFolder(): Promise<{ success: boolean; error?: string }> {
try {
await invoke('open_templates_folder');
return { success: true };
} catch (e) {
const errorMsg = e instanceof Error ? e.message : String(e);
return { success: false, error: errorMsg };
}
}
/**
* Load a single template by ID
*/
export async function loadTemplateById(id: string): Promise<StyleOption | null> {
const { templates } = await loadTemplates();
return templates.find(t => t.id === id) || null;
}
/**
* Get all available categories from templates
*/
export function getCategories(templates: StyleOption[]): string[] {
const categories = new Set<string>();
templates.forEach(t => categories.add(t.category));
return Array.from(categories).sort();
}

View File

@@ -0,0 +1,433 @@
import { StyleOption, ElementStyle, FontConfig, ColorPalette } from '../types';
/**
* Resolves a color reference (e.g., 'accent', 'background') or returns the color directly
*/
export function resolveColor(color: string | undefined, palette: ColorPalette): string {
if (!color) return '#000000';
// Check if it's a palette reference
if (palette[color as keyof ColorPalette]) {
return '#' + palette[color as keyof ColorPalette];
}
// Return as-is with # prefix if not already present
return color.startsWith('#') ? color : '#' + color;
}
/**
* Resolves a font reference (e.g., 'heading', 'body', 'code') to actual font name
*/
export function resolveFont(font: string | undefined, fonts: FontConfig): string {
if (!font) return 'serif';
// Check if it's a font reference
if (fonts[font as keyof FontConfig]) {
return fonts[font as keyof FontConfig]!;
}
// Return as-is (already an actual font name)
return font;
}
/**
* Converts element style to CSS string
*/
function elementToCss(
selector: string,
style: ElementStyle | undefined,
fonts: FontConfig,
palette: ColorPalette
): string {
if (!style) return '';
const css: string[] = [];
if (style.font) css.push(`font-family: ${resolveFont(style.font, fonts)}`);
if (style.size) css.push(`font-size: ${style.size}pt`);
if (style.color) css.push(`color: ${resolveColor(style.color, palette)}`);
if (style.background) css.push(`background: ${resolveColor(style.background, palette)}`);
if (style.bold) css.push('font-weight: 700');
if (style.italic) css.push('font-style: italic');
if (style.underline) css.push('text-decoration: underline');
if (style.allCaps) css.push('text-transform: uppercase');
if (style.align) css.push(`text-align: ${style.align === 'both' ? 'justify' : style.align}`);
if (style.spacing) {
if (style.spacing.before) css.push(`margin-top: ${style.spacing.before}pt`);
if (style.spacing.after) css.push(`margin-bottom: ${style.spacing.after}pt`);
if (style.spacing.line) css.push(`line-height: ${style.spacing.line}`);
}
if (style.indent) css.push(`text-indent: ${style.indent}pt`);
if (style.padding) css.push(`padding: ${style.padding}pt`);
if (style.border) {
css.push(`border: ${style.border.width}px ${style.border.style} ${resolveColor(style.border.color, palette)}`);
}
if (style.borderTop) {
css.push(`border-top: ${style.borderTop.width}px ${style.borderTop.style} ${resolveColor(style.borderTop.color, palette)}`);
}
if (style.borderBottom) {
css.push(`border-bottom: ${style.borderBottom.width}px ${style.borderBottom.style} ${resolveColor(style.borderBottom.color, palette)}`);
}
if (style.borderLeft) {
css.push(`border-left: ${style.borderLeft.width}px ${style.borderLeft.style} ${resolveColor(style.borderLeft.color, palette)}`);
}
if (style.borderRight) {
css.push(`border-right: ${style.borderRight.width}px ${style.borderRight.style} ${resolveColor(style.borderRight.color, palette)}`);
}
if (css.length === 0) return '';
return `${selector} { ${css.join('; ')} }`;
}
/**
* Generates preview CSS from unified template structure
* This is the SINGLE SOURCE OF TRUTH for both preview and export styling
* All selectors are scoped to .page to avoid affecting the app background
*/
export function generatePreviewCss(template: StyleOption): string {
const fonts = template.typography?.fonts || { heading: 'serif', body: 'serif', code: 'monospace' };
const palette = template.typography?.colors || { text: '000000', background: 'FFFFFF' };
const elements = template.elements || {};
const cssParts: string[] = [];
// .page base styles - scoped to avoid affecting app background
const baseStyles: string[] = [];
baseStyles.push(`font-family: ${resolveFont('body', fonts)}`);
baseStyles.push(`background: ${resolveColor('background', palette)}`);
baseStyles.push(`color: ${resolveColor('text', palette)}`);
baseStyles.push(`line-height: 1.5`);
cssParts.push(`.page { ${baseStyles.join('; ')}; min-height: 100%; }`);
cssParts.push(`.page blockquote, .page table, .page img { break-inside: avoid; }`);
// Headings - scoped to .page
cssParts.push(elementToCss('.page h1', elements.h1, fonts, palette));
cssParts.push(elementToCss('.page h2', elements.h2, fonts, palette));
cssParts.push(elementToCss('.page h3', elements.h3, fonts, palette));
cssParts.push(elementToCss('.page h4', elements.h4, fonts, palette));
cssParts.push(elementToCss('.page h5', elements.h5, fonts, palette));
cssParts.push(elementToCss('.page h6', elements.h6, fonts, palette));
// Paragraph - scoped to .page
cssParts.push(elementToCss('.page p', elements.p, fonts, palette));
// Blockquote - scoped to .page
cssParts.push(elementToCss('.page blockquote', elements.blockquote, fonts, palette));
// Code - scoped to .page
cssParts.push(elementToCss('.page code', elements.code, fonts, palette));
cssParts.push(elementToCss('.page pre', elements.pre, fonts, palette));
cssParts.push('.page pre code { background: transparent !important; padding: 0 !important }');
// Lists - scoped to .page
cssParts.push(elementToCss('.page ul', elements.ul, fonts, palette));
cssParts.push(elementToCss('.page ol', elements.ol, fonts, palette));
cssParts.push(elementToCss('.page li', elements.li, fonts, palette));
// Inline styles - scoped to .page
cssParts.push(elementToCss('.page strong', elements.strong, fonts, palette));
cssParts.push(elementToCss('.page em', elements.em, fonts, palette));
cssParts.push(elementToCss('.page a', elements.a, fonts, palette));
// Tables - scoped to .page
cssParts.push('.page table { border-collapse: collapse; border-spacing: 0; width: 100%; }');
cssParts.push(elementToCss('.page table', elements.table, fonts, palette));
cssParts.push(elementToCss('.page th', elements.th, fonts, palette));
cssParts.push(elementToCss('.page td', elements.td, fonts, palette));
// Horizontal rule - scoped to .page
if (elements.hr?.border) {
const borderColor = resolveColor(elements.hr.border.color, palette);
cssParts.push(`.page hr { border: none; border-top: ${elements.hr.border.width}px ${elements.hr.border.style} ${borderColor}; margin: ${elements.hr.spacing?.before || 12}pt 0 }`);
}
// Images - scoped to .page
if (elements.img?.align) {
cssParts.push(`.page img { display: block; text-align: ${elements.img.align}; margin: ${elements.img.spacing?.before || 12}pt auto }`);
}
// Remove empty rules and join
return cssParts.filter(part => part.trim()).join('\n');
}
/**
* Generates CSS from legacy wordConfig structure
* Used when unified elements/typography structure isn't available
* All selectors are scoped to .page to avoid affecting the app background
*/
function generateCssFromWordConfig(template: StyleOption): string {
const cfg = template.wordConfig;
if (!cfg) return '.page { font-family: serif; }';
const cssParts: string[] = [];
// .page base styles - scoped to avoid affecting app background
const pageStyles: string[] = [];
if (cfg.body?.font) pageStyles.push(`font-family: ${cfg.body.font}`);
if (cfg.body?.size) pageStyles.push(`font-size: ${cfg.body.size}pt`);
if (cfg.body?.color) pageStyles.push(`color: #${cfg.body.color}`);
if (cfg.body?.align) pageStyles.push(`text-align: ${cfg.body.align === 'both' ? 'justify' : cfg.body.align}`);
if (cfg.body?.spacing?.line) {
pageStyles.push(`line-height: ${cfg.body.spacing.line / 240}`);
}
// White background for the document page
pageStyles.push('background: #ffffff');
cssParts.push(`.page { ${pageStyles.join('; ')} }`);
// Headings - scoped to .page
if (cfg.heading1) {
const h1Styles: string[] = [];
if (cfg.heading1.font) h1Styles.push(`font-family: ${cfg.heading1.font}`);
if (cfg.heading1.size) h1Styles.push(`font-size: ${cfg.heading1.size}pt`);
if (cfg.heading1.color) h1Styles.push(`color: #${cfg.heading1.color}`);
if (cfg.heading1.bold) h1Styles.push('font-weight: 700');
if (cfg.heading1.italic) h1Styles.push('font-style: italic');
if (cfg.heading1.allCaps) h1Styles.push('text-transform: uppercase');
if (cfg.heading1.align) h1Styles.push(`text-align: ${cfg.heading1.align}`);
if (cfg.heading1.spacing?.before) {
h1Styles.push(`margin-top: ${cfg.heading1.spacing.before / 20}pt`);
}
if (cfg.heading1.spacing?.after) {
h1Styles.push(`margin-bottom: ${cfg.heading1.spacing.after / 20}pt`);
}
cssParts.push(`.page h1 { ${h1Styles.join('; ')} }`);
}
if (cfg.heading2) {
const h2Styles: string[] = [];
if (cfg.heading2.font) h2Styles.push(`font-family: ${cfg.heading2.font}`);
if (cfg.heading2.size) h2Styles.push(`font-size: ${cfg.heading2.size}pt`);
if (cfg.heading2.color) h2Styles.push(`color: #${cfg.heading2.color}`);
if (cfg.heading2.bold) h2Styles.push('font-weight: 700');
if (cfg.heading2.italic) h2Styles.push('font-style: italic');
if (cfg.heading2.allCaps) h2Styles.push('text-transform: uppercase');
if (cfg.heading2.align) h2Styles.push(`text-align: ${cfg.heading2.align}`);
if (cfg.heading2.spacing?.before) {
h2Styles.push(`margin-top: ${cfg.heading2.spacing.before / 20}pt`);
}
if (cfg.heading2.spacing?.after) {
h2Styles.push(`margin-bottom: ${cfg.heading2.spacing.after / 20}pt`);
}
cssParts.push(`.page h2 { ${h2Styles.join('; ')} }`);
}
// Paragraph - scoped to .page
if (cfg.body) {
const pStyles: string[] = [];
if (cfg.body.font) pStyles.push(`font-family: ${cfg.body.font}`);
if (cfg.body.size) pStyles.push(`font-size: ${cfg.body.size}pt`);
if (cfg.body.color) pStyles.push(`color: #${cfg.body.color}`);
if (cfg.body.align) pStyles.push(`text-align: ${cfg.body.align === 'both' ? 'justify' : cfg.body.align}`);
if (cfg.body.spacing?.line) {
pStyles.push(`line-height: ${cfg.body.spacing.line / 240}`);
}
if (cfg.body.spacing?.after) {
pStyles.push(`margin-bottom: ${cfg.body.spacing.after / 20}pt`);
}
cssParts.push(`.page p { ${pStyles.join('; ')} }`);
}
// All other elements scoped to .page
cssParts.push('.page blockquote { border-left: 3px solid #' + (cfg.accentColor || 'cccccc') + '; padding-left: 16px; margin: 16px 0; font-style: italic; }');
cssParts.push('.page ul, .page ol { margin: 16px 0; padding-left: 32px; }');
cssParts.push('.page li { margin-bottom: 8px; }');
cssParts.push('.page strong { font-weight: 700; }');
cssParts.push('.page em { font-style: italic; }');
cssParts.push('.page code { font-family: monospace; background: #f5f5f5; padding: 2px 4px; border-radius: 3px; }');
cssParts.push('.page pre { background: #f5f5f5; padding: 16px; border-radius: 4px; overflow-x: auto; }');
cssParts.push('.page table { border-collapse: collapse; width: 100%; margin: 16px 0; }');
cssParts.push('.page th, .page td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; }');
cssParts.push('.page th { background: #f5f5f5; font-weight: 700; }');
cssParts.push('.page hr { border: none; border-top: 1px solid #' + (cfg.accentColor || 'cccccc') + '; margin: 24px 0; }');
return cssParts.join('\n');
}
/**
* Gets complete CSS for preview
* Always generates fresh CSS from unified template structure
*/
export function getPreviewCss(template: StyleOption): string {
// Always generate CSS from unified structure if available
if (template.elements && template.typography) {
return generatePreviewCss(template);
}
// Generate CSS from legacy wordConfig structure
if (template.wordConfig) {
return generateCssFromWordConfig(template);
}
// Final fallback
return 'body { font-family: serif; color: #000000; background: #ffffff; }';
}
/**
* Generates Word-compatible wordConfig from unified template structure
*/
// Export this function to be used by templateLoader
export function generateWordConfig(
elements: Record<string, ElementStyle>,
fonts: FontConfig,
palette: ColorPalette
) {
const h1 = elements.h1;
const h2 = elements.h2;
const body = elements.p;
const toTwips = (pts?: number) => pts;
const toLineTwips = (line?: number) => line;
// Helper to ensure borders are preserved in the config
const mapBorder = (el?: ElementStyle, side?: 'top' | 'bottom' | 'left' | 'right') => {
// If specific side border exists
let sideBorder;
if (side === 'left') sideBorder = el?.borderLeft;
else if (side === 'top') sideBorder = el?.borderTop;
else if (side === 'bottom') sideBorder = el?.borderBottom;
else if (side === 'right') sideBorder = el?.borderRight;
if (sideBorder) {
return {
color: resolveColor(sideBorder.color, palette).replace('#', ''),
space: 24, // Standard space
style: sideBorder.style || 'single',
size: (sideBorder.width || 1) * 8
};
}
// If global border exists
if (el?.border) {
return {
color: resolveColor(el.border.color, palette).replace('#', ''),
space: 24, // Standard space
style: el.border.style || 'single',
size: (el.border.width || 1) * 8
};
}
return undefined;
};
return {
heading1: h1 ? {
font: resolveFont(h1.font, fonts),
size: h1.size || 24,
color: h1.color ? resolveColor(h1.color, palette).replace('#', '') : '000000',
bold: h1.bold,
italic: h1.italic,
allCaps: h1.allCaps,
align: h1.align,
spacing: h1.spacing ? {
before: toTwips(h1.spacing.before) || 0,
after: toTwips(h1.spacing.after) || 0,
line: toLineTwips(h1.spacing.line) || 240
} : undefined,
// Map borders specifically for H1 if they exist
border: {
left: mapBorder(h1, 'left'),
bottom: mapBorder(h1, 'bottom'),
top: mapBorder(h1, 'top'),
right: mapBorder(h1, 'right')
},
shading: h1.background ? {
fill: resolveColor(h1.background, palette).replace('#', ''),
type: 'clear', // Always use clear for shading to avoid black blocks
color: 'auto'
} : undefined
} : undefined,
heading2: h2 ? {
font: resolveFont(h2.font, fonts),
size: h2.size || 18,
color: h2.color ? resolveColor(h2.color, palette).replace('#', '') : '333333',
bold: h2.bold,
italic: h2.italic,
allCaps: h2.allCaps,
align: h2.align,
spacing: h2.spacing ? {
before: toTwips(h2.spacing.before) || 0,
after: toTwips(h2.spacing.after) || 0,
line: toLineTwips(h2.spacing.line) || 240
} : undefined,
shading: h2.background ? {
fill: resolveColor(h2.background, palette).replace('#', ''),
type: 'clear',
color: 'auto'
} : undefined
} : undefined,
body: body ? {
font: resolveFont(body.font, fonts),
size: body.size || 11,
color: body.color ? resolveColor(body.color, palette).replace('#', '') : '000000',
align: body.align,
spacing: body.spacing ? {
before: toTwips(body.spacing.before) || 0,
after: toTwips(body.spacing.after) || 0,
line: toLineTwips(body.spacing.line) || 1.2
} : undefined
} : undefined,
accentColor: palette.accent || '000000',
heading3: elements.h3 ? {
font: resolveFont(elements.h3.font, fonts),
size: elements.h3.size || 14,
color: elements.h3.color ? resolveColor(elements.h3.color, palette).replace('#', '') : '111111',
bold: elements.h3.bold,
italic: elements.h3.italic,
allCaps: elements.h3.allCaps,
align: elements.h3.align,
spacing: elements.h3.spacing ? {
before: toTwips(elements.h3.spacing.before) || 0,
after: toTwips(elements.h3.spacing.after) || 0,
line: toLineTwips(elements.h3.spacing.line) || 240
} : undefined
} : undefined,
heading4: elements.h4 ? {
font: resolveFont(elements.h4.font, fonts),
size: elements.h4.size || 12,
color: elements.h4.color ? resolveColor(elements.h4.color, palette).replace('#', '') : '111111',
bold: elements.h4.bold,
italic: elements.h4.italic,
align: elements.h4.align,
spacing: elements.h4.spacing ? {
before: toTwips(elements.h4.spacing.before) || 0,
after: toTwips(elements.h4.spacing.after) || 0,
line: toLineTwips(elements.h4.spacing.line) || 240
} : undefined
} : undefined,
heading5: elements.h5 ? {
font: resolveFont(elements.h5.font, fonts),
size: elements.h5.size || 11,
color: elements.h5.color ? resolveColor(elements.h5.color, palette).replace('#', '') : '111111',
bold: elements.h5.bold,
italic: elements.h5.italic,
align: elements.h5.align,
spacing: elements.h5.spacing ? {
before: toTwips(elements.h5.spacing.before) || 0,
after: toTwips(elements.h5.spacing.after) || 0,
line: toLineTwips(elements.h5.spacing.line) || 240
} : undefined
} : undefined,
heading6: elements.h6 ? {
font: resolveFont(elements.h6.font, fonts),
size: elements.h6.size || 10,
color: elements.h6.color ? resolveColor(elements.h6.color, palette).replace('#', '') : '111111',
bold: elements.h6.bold,
italic: elements.h6.italic,
align: elements.h6.align,
spacing: elements.h6.spacing ? {
before: toTwips(elements.h6.spacing.before) || 0,
after: toTwips(elements.h6.spacing.after) || 0,
line: toLineTwips(elements.h6.spacing.line) || 240
} : undefined
} : undefined
};
}
/**
* Gets complete wordConfig, preferring generated config over legacy
*/
export function getWordConfig(template: StyleOption) {
// If template has unified structure, generate config from it
if (template.elements && template.typography) {
const fonts = template.typography.fonts || {};
const palette = template.typography.colors || {};
const elements = template.elements;
return generateWordConfig(elements, fonts, palette);
}
// Fall back to legacy wordConfig
return template.wordConfig;
}

View File

@@ -1,407 +0,0 @@
import { StyleOption } from '../types';
export const academicStyles: StyleOption[] = [
{
id: 'academic-journal',
name: 'Academic Journal',
category: 'Academic',
description: 'Scholarly and rigorous design for academic papers and research publications. Traditional serif typography optimized for extended reading.',
vibe: 'Scholarly, Serious, Traditional',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&family=Source+Sans+3:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Libre Baskerville", size: 18, color: "000000", bold: true, align: 'center',
spacing: { before: 360, after: 240, line: 240 }
},
heading2: {
font: "Libre Baskerville", size: 13, color: "000000", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 }
},
body: {
font: "Libre Baskerville", size: 10, color: "1A1A1A", align: 'both',
spacing: { before: 0, after: 140, line: 300 }
},
accentColor: "800000"
},
previewCss: `
font-family: 'Libre Baskerville', serif;
h1 { font-size: 18pt; font-weight: 700; color: #000000; text-align: center; margin-bottom: 28px; }
h2 { font-size: 13pt; font-weight: 700; color: #000000; margin-top: 28px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.7; color: #1A1A1A; margin-bottom: 12px; text-align: justify; }
blockquote { padding-left: 40px; margin: 20px 0; font-size: 9pt; color: #333333; }
`
},
{
id: 'medical-professional',
name: 'Medical Professional',
category: 'Healthcare',
description: 'Clean, clinical design for healthcare and medical documentation. Emphasizes clarity and professionalism with calming blue accents.',
vibe: 'Clinical, Professional, Trustworthy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Open Sans", size: 20, color: "1565C0", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 240 }
},
heading2: {
font: "Open Sans", size: 13, color: "0D47A1", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 }
},
body: {
font: "Open Sans", size: 10, color: "37474F", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "1565C0"
},
previewCss: `
font-family: 'Open Sans', sans-serif;
h1 { font-size: 20pt; font-weight: 700; color: #1565C0; margin-bottom: 20px; }
h2 { font-size: 13pt; font-weight: 700; color: #0D47A1; margin-top: 28px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.6; color: #37474F; margin-bottom: 14px; }
blockquote { background: #E3F2FD; padding: 16px; border-left: 4px solid #1565C0; margin: 20px 0; }
`
},
{
id: 'education-friendly',
name: 'Education Friendly',
category: 'Education',
description: 'Approachable and clear design for educational materials. High readability with friendly colors suitable for learning environments.',
vibe: 'Friendly, Educational, Accessible',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Nunito", size: 24, color: "5E35B1", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "EDE7F6", color: "auto", style: "clear" }
},
heading2: {
font: "Nunito", size: 14, color: "7E57C2", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Nunito", size: 11, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "5E35B1"
},
previewCss: `
font-family: 'Nunito', sans-serif;
h1 { font-size: 24pt; font-weight: 700; color: #5E35B1; background: #EDE7F6; padding: 16px 20px; border-radius: 8px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #7E57C2; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 11pt; line-height: 1.7; color: #424242; margin-bottom: 14px; }
blockquote { background: #F3E5F5; padding: 16px; border-radius: 8px; border-left: 4px solid #7E57C2; margin: 20px 0; }
`
},
{
id: 'scientific-journal',
name: 'Scientific Journal',
category: 'Academic',
description: 'Precise design for scientific publications. Clear hierarchy optimized for data-heavy content and citations.',
vibe: 'Scientific, Precise, Academic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Literata:wght@400;500;700&family=Fira+Sans:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Literata", size: 18, color: "1A1A1A", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 260 }
},
heading2: {
font: "Fira Sans", size: 12, color: "1A1A1A", bold: true, align: 'left',
spacing: { before: 280, after: 120, line: 240 }
},
body: {
font: "Literata", size: 10, color: "262626", align: 'both',
spacing: { before: 0, after: 140, line: 280 }
},
accentColor: "0066CC"
},
previewCss: `
font-family: 'Literata', serif;
h1 { font-size: 18pt; font-weight: 700; color: #1A1A1A; margin-bottom: 24px; }
h2 { font-family: 'Fira Sans', sans-serif; font-size: 12pt; font-weight: 600; color: #1A1A1A; margin-top: 28px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #262626; margin-bottom: 12px; text-align: justify; }
blockquote { padding-left: 24px; border-left: 2px solid #0066CC; margin: 16px 0; font-size: 9pt; }
`
},
{
id: 'kids-education',
name: 'Kids Education',
category: 'Education',
description: 'Fun and engaging design for children\'s educational content. Rounded typography with bright, cheerful colors.',
vibe: 'Educational, Fun, Engaging',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Fredoka", size: 32, color: "FF6B6B", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "FFF9DB", color: "auto", style: "clear" }
},
heading2: {
font: "Fredoka", size: 18, color: "4ECDC4", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Fredoka", size: 12, color: "495057", align: 'left',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "FF6B6B"
},
previewCss: `
font-family: 'Fredoka', sans-serif;
h1 { font-size: 32pt; font-weight: 600; color: #FF6B6B; text-align: center; background: #FFF9DB; padding: 16px 20px; border-radius: 12px; margin-bottom: 28px; }
h2 { font-size: 18pt; font-weight: 500; color: #4ECDC4; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.8; color: #495057; margin-bottom: 16px; }
blockquote { background: #E3F9F8; padding: 16px; border-radius: 12px; border-left: 6px solid #4ECDC4; margin: 20px 0; }
`
},
{
id: 'dark-academia',
name: 'Dark Academia',
category: 'Academic',
description: 'Moody, literary aesthetic. Tweed textures, old libraries, and classic serif typography.',
vibe: 'Moody, Scholarly, Literary',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Alegreya:wght@400;700&family=Alegreya+SC:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Alegreya SC", size: 28, color: "3E2723", bold: true, align: 'center',
spacing: { before: 480, after: 280, line: 240 },
border: { bottom: { color: "5D4037", space: 6, style: "double", size: 12 } }
},
heading2: {
font: "Alegreya", size: 16, color: "4E342E", bold: true, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Alegreya", size: 11, color: "212121", align: 'both',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "5D4037"
},
previewCss: `
font-family: 'Alegreya', serif;
background: #F5F1E8;
h1 { font-family: 'Alegreya SC', serif; font-size: 28pt; font-weight: 700; color: #3E2723; text-align: center; border-bottom: 4px double #5D4037; padding-bottom: 16px; margin-bottom: 32px; }
h2 { font-size: 16pt; font-weight: 700; color: #4E342E; margin-top: 36px; margin-bottom: 18px; font-style: italic; }
p { font-size: 11pt; line-height: 1.8; color: #212121; margin-bottom: 16px; text-align: justify; }
blockquote { border-left: 2px solid #3E2723; padding-left: 20px; margin: 28px 0; font-style: italic; color: #4E342E; }
`
},
{
id: 'botanical-textbook',
name: 'Botanical Textbook',
category: 'Scientific',
description: 'Vintage science textbook aesthetic. Academic yet organic, like a 19th-century flora guide.',
vibe: 'Vintage, Scientific, Organic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Alice&family=Gentium+Book+Basic:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Alice", size: 28, color: "33691E", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "827717", space: 6, style: "single", size: 8 } }
},
heading2: {
font: "Gentium Book Basic", size: 14, color: "558B2F", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Gentium Book Basic", size: 11, color: "1B1B1B", align: 'both',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "827717"
},
previewCss: `
font-family: 'Gentium Book Basic', serif;
h1 { font-family: 'Alice', serif; font-size: 28pt; color: #33691E; border-bottom: 2px solid #827717; padding-bottom: 10px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #558B2F; margin-top: 30px; margin-bottom: 14px; font-style: italic; }
p { font-size: 11pt; line-height: 1.7; color: #1B1B1B; margin-bottom: 14px; text-align: justify; }
blockquote { background: #F9FBE7; padding: 16px; border-left: 4px solid #827717; margin: 20px 0; font-size: 10pt; }
`
},
{
id: 'chemistry-lab',
name: 'Chemistry Lab',
category: 'Scientific',
description: 'Clean, molecular design. Modern thin typography suitable for diagrams and formulas.',
vibe: 'Scientific, Modern, Molecular',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Advent+Pro:wght@400;600;700&family=Heebo:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Advent Pro", size: 32, color: "00BCD4", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Heebo", size: 16, color: "0097A7", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Heebo", size: 10, color: "37474F", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "00BCD4"
},
previewCss: `
font-family: 'Heebo', sans-serif;
h1 { font-family: 'Advent Pro', sans-serif; font-size: 32pt; font-weight: 700; color: #00BCD4; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #0097A7; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #37474F; margin-bottom: 14px; font-weight: 300; }
blockquote { border: 1px solid #00BCD4; padding: 16px; margin: 20px 0; border-radius: 8px; background: #E0F7FA; }
`
},
{
id: 'mathematics-paper',
name: 'Mathematics Paper',
category: 'Academic',
description: 'Inspired by LaTeX and Computer Modern. Serif-heavy, precise, and highly legible for academic rigor.',
vibe: 'Academic, Rigorous, Formal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Domine:wght@400;700&family=Noticia+Text:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Domine", size: 24, color: "000000", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Domine", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Noticia Text", size: 11, color: "212121", align: 'both',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Noticia Text', serif;
h1 { font-family: 'Domine', serif; font-size: 24pt; font-weight: 700; color: #000000; text-align: center; margin-bottom: 24px; }
h2 { font-family: 'Domine', serif; font-size: 14pt; font-weight: 700; color: #000000; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; text-align: justify; }
blockquote { border-left: 3px solid #000; padding-left: 20px; margin: 24px 0; font-style: italic; background: #F5F5F5; padding: 10px 20px; }
`
},
{
id: 'phd-thesis',
name: 'PhD Thesis',
category: 'Academic',
description: 'The pinnacle of academic formatting. Extremely legible serif body, double spacing, and rigorous margin structure.',
vibe: 'Formal, Scholarly, Prestigious',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Tinos:wght@400;700&family=Crimson+Text:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Tinos", size: 24, color: "000000", bold: true, align: 'center',
spacing: { before: 480, after: 240, line: 240 },
allCaps: true
},
heading2: {
font: "Tinos", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 }
},
body: {
font: "Crimson Text", size: 12, color: "000000", align: 'both',
spacing: { before: 0, after: 0, line: 480 } // Double spaced
},
accentColor: "000000"
},
previewCss: `
font-family: 'Crimson Text', serif;
line-height: 2.0;
h1 { font-family: 'Tinos', serif; font-size: 24pt; font-weight: 700; text-align: center; text-transform: uppercase; margin-bottom: 24px; }
h2 { font-family: 'Tinos', serif; font-size: 14pt; font-weight: 700; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 12pt; text-align: justify; margin-bottom: 0; }
blockquote { margin-left: 40px; font-style: italic; }
`
},
{
id: 'history-textbook',
name: 'History Textbook',
category: 'Academic',
description: 'Classic textbook design. Sepia tones, distinct serif headers, and wide margins for notes.',
vibe: 'Classic, Educational, Historical',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Goudy+Bookletter+1911&family=Sorts+Mill+Goudy&display=swap',
wordConfig: {
heading1: {
font: "Goudy Bookletter 1911", size: 28, color: "5D4037", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "8D6E63", space: 4, style: "single", size: 8 } }
},
heading2: {
font: "Sorts Mill Goudy", size: 16, color: "3E2723", bold: false, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Sorts Mill Goudy", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "8D6E63"
},
previewCss: `
font-family: 'Sorts Mill Goudy', serif;
background: #FFF8E1;
h1 { font-family: 'Goudy Bookletter 1911', serif; font-size: 28pt; color: #5D4037; border-bottom: 2px solid #8D6E63; padding-bottom: 10px; margin-bottom: 24px; }
h2 { font-size: 16pt; color: #3E2723; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 11pt; line-height: 1.7; color: #212121; margin-bottom: 14px; }
blockquote { background: #FFF; padding: 16px; border: 1px solid #8D6E63; margin: 20px 0; }
`
},
{
id: 'scantron-test',
name: 'Scantron Test',
category: 'Academic',
description: 'Standardized testing sheet. Salmon pinks and greens with bubble-filling aesthetics.',
vibe: 'Academic, Retro, Bureaucratic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Roboto Mono", size: 28, color: "B71C1C", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "EF5350", space: 4, style: "single", size: 12 } }
},
heading2: {
font: "Roboto Mono", size: 14, color: "1B5E20", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "E8F5E9", color: "auto", style: "clear" }
},
body: {
font: "Roboto Mono", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "EF5350"
},
previewCss: `
font-family: 'Roboto Mono', monospace;
background: #FFF3E0; /* Light salmonish tint */
h1 { font-size: 28pt; font-weight: 700; color: #B71C1C; border-bottom: 2px solid #EF5350; padding-bottom: 8px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #1B5E20; background: #C8E6C9; display: inline-block; padding: 4px 12px; margin-top: 32px; margin-bottom: 16px; border-radius: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 1px solid #B71C1C; padding: 16px; margin: 24px 0; border-radius: 8px; background: #FFEBEE; }
`
},
{
id: 'emergency-room',
name: 'Emergency Room',
category: 'Medical',
description: 'Hospital signage. Sterile white, urgent red, and clean blue.',
vibe: 'Sterile, Urgent, Clean',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Assistant:wght@400;700&family=Heebo:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Assistant", size: 36, color: "D50000", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "D50000", space: 4, style: "single", size: 12 } }
},
heading2: {
font: "Heebo", size: 18, color: "2962FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Heebo", size: 11, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D50000"
},
previewCss: `
font-family: 'Heebo', sans-serif;
h1 { font-family: 'Assistant', sans-serif; font-size: 36pt; font-weight: 700; color: #D50000; border-bottom: 4px solid #D50000; padding-bottom: 8px; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #2962FF; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { background: #FFEBEE; border-left: 6px solid #D50000; padding: 16px; margin: 24px 0; }
`
}
];

View File

@@ -1 +0,0 @@
// Styles have been moved to categorized files.

View File

@@ -1,430 +0,0 @@
import { StyleOption } from '../types';
export const corporateStyles: StyleOption[] = [
{
id: 'tech-memo',
name: 'Tech Memorandum',
category: 'Corporate',
description: 'Modern, functional, and authoritative. Uses shading for headers to create distinct sections.',
vibe: 'Corporate, Tech, Strategy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Roboto", size: 24, color: "0F172A", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "F1F5F9", color: "auto", style: "clear" },
border: { left: { color: "2563EB", space: 10, style: "single", size: 48 } }
},
heading2: {
font: "Roboto", size: 14, color: "2563EB", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Roboto", size: 10, color: "334155", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2563EB"
},
previewCss: `
font-family: 'Roboto', sans-serif;
h1 { font-size: 24pt; font-weight: 700; background: #F1F5F9; color: #0F172A; padding: 12px 16px; border-left: 8px solid #2563EB; margin-bottom: 20px; }
h2 { font-size: 14pt; font-weight: 700; color: #2563EB; margin-top: 30px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #334155; margin-bottom: 14px; }
blockquote { background: #EFF6FF; padding: 16px; border-radius: 4px; color: #1E40AF; border-left: 4px solid #2563EB; margin: 20px 0; }
`
},
{
id: 'annual-report',
name: 'Annual Report',
category: 'Corporate',
description: 'Trustworthy and substantial. Deep navy blues, clean sans-serifs, and grid-like precision.',
vibe: 'Financial, Serious, Trust',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@400;700;900&family=Merriweather:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Libre Franklin", size: 32, color: "0D2B46", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Libre Franklin", size: 16, color: "A0A0A0", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Merriweather", size: 10, color: "333333", align: 'both',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "0D2B46"
},
previewCss: `
font-family: 'Merriweather', serif;
h1 { font-family: 'Libre Franklin', sans-serif; font-size: 32pt; font-weight: 900; color: #0D2B46; margin-bottom: 24px; }
h2 { font-family: 'Libre Franklin', sans-serif; font-size: 16pt; font-weight: 700; color: #A0A0A0; text-transform: uppercase; margin-top: 32px; margin-bottom: 16px; border-bottom: 1px solid #CCC; padding-bottom: 4px; }
p { font-size: 10pt; line-height: 1.8; color: #333333; margin-bottom: 14px; text-align: justify; }
blockquote { border-left: 4px solid #0D2B46; padding-left: 16px; margin: 24px 0; color: #0D2B46; font-style: italic; }
`
},
{
id: 'legal-contract',
name: 'Legal Contract',
category: 'Corporate',
description: 'Binding agreement style. Serif fonts, justified text, and numbered section aesthetics.',
vibe: 'Legal, Binding, Formal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Tinos:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Tinos", size: 14, color: "000000", bold: true, align: 'center',
spacing: { before: 240, after: 240, line: 240 },
allCaps: true
},
heading2: {
font: "Tinos", size: 12, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 },
underline: true
},
body: {
font: "Tinos", size: 11, color: "000000", align: 'both',
spacing: { before: 0, after: 120, line: 240 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Tinos', serif;
h1 { font-size: 14pt; font-weight: 700; color: #000000; text-align: center; text-transform: uppercase; margin-bottom: 16px; }
h2 { font-size: 12pt; font-weight: 700; color: #000000; text-decoration: underline; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 11pt; line-height: 1.4; color: #000000; margin-bottom: 12px; text-align: justify; }
blockquote { margin: 20px 40px; border: 1px solid #000; padding: 10px; }
`
},
{
id: 'corporate-executive',
name: 'Corporate Executive',
category: 'Corporate',
description: 'Authoritative and professional. Designed for board reports, executive summaries, and high-stakes business communications.',
vibe: 'Executive, Formal, Trustworthy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Source+Serif+4:wght@400;600;700&family=Source+Sans+3:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Source Serif 4", size: 24, color: "1A237E", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Source Sans 3", size: 14, color: "303F9F", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Source Sans 3", size: 10, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "1A237E"
},
previewCss: `
font-family: 'Source Sans 3', sans-serif;
h1 { font-family: 'Source Serif 4', serif; font-size: 24pt; font-weight: 700; color: #1A237E; margin-bottom: 24px; border-bottom: 2px solid #1A237E; padding-bottom: 8px; }
h2 { font-size: 14pt; font-weight: 600; color: #303F9F; margin-top: 30px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { border-left: 4px solid #1A237E; padding-left: 16px; color: #1A237E; margin: 20px 0; font-style: italic; }
`
},
{
id: 'political-campaign',
name: 'Political Campaign',
category: 'Professional',
description: 'Bold, patriotic design for campaigns. Strong headers with red, white, and blue motifs.',
vibe: 'Patriotic, Bold, Official',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Francois+One&family=Cabin:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Francois One", size: 36, color: "0D47A1", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "D32F2F", space: 8, style: "single", size: 12 } }
},
heading2: {
font: "Cabin", size: 16, color: "D32F2F", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Cabin", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Cabin', sans-serif;
h1 { font-family: 'Francois One', sans-serif; font-size: 36pt; color: #0D47A1; border-bottom: 4px solid #D32F2F; padding-bottom: 12px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 16pt; font-weight: 700; color: #D32F2F; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { background: #E3F2FD; padding: 20px; border-left: 8px solid #0D47A1; margin: 24px 0; }
`
},
{
id: 'passport-official',
name: 'Passport Official',
category: 'Government',
description: 'International travel document aesthetic. Machine-readable fonts with secure, bureaucratic vibes.',
vibe: 'Official, Secure, Monospace',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Martian+Mono:wght@400;700&family=Courier+Prime&display=swap',
wordConfig: {
heading1: {
font: "Martian Mono", size: 22, color: "1A237E", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 240 }
},
heading2: {
font: "Martian Mono", size: 12, color: "B71C1C", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 },
allCaps: true
},
body: {
font: "Courier Prime", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 140, line: 260 }
},
accentColor: "B71C1C"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-family: 'Martian Mono', monospace; font-size: 22pt; font-weight: 700; color: #1A237E; margin-bottom: 24px; letter-spacing: -1px; }
h2 { font-family: 'Martian Mono', monospace; font-size: 12pt; font-weight: 700; color: #B71C1C; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.5; color: #212121; margin-bottom: 12px; }
blockquote { border: 1px dashed #1A237E; padding: 16px; margin: 20px 0; background: #E8EAF6; }
`
},
{
id: 'top-secret-redacted',
name: 'Top Secret Redacted',
category: 'Government',
description: 'Declassified document style. Typewriter fonts with "blacked out" highlight effects.',
vibe: 'Secret, Classified, Rough',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Special+Elite&family=Courier+Prime:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Special Elite", size: 28, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
border: {
top: { color: "000000", space: 6, style: "single", size: 24 },
bottom: { color: "000000", space: 6, style: "single", size: 24 }
}
},
heading2: {
font: "Courier Prime", size: 14, color: "B71C1C", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" } // Red text on black bar
},
body: {
font: "Courier Prime", size: 11, color: "000000", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
background: #F5F5F5;
h1 { font-family: 'Special Elite', cursive; font-size: 28pt; color: #000000; border-top: 4px solid #000; border-bottom: 4px solid #000; padding: 16px 0; text-align: center; margin-bottom: 28px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #B71C1C; background: #000000; display: inline-block; padding: 4px 12px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #000000; margin-bottom: 14px; }
blockquote { background: #000; color: #000; padding: 10px; margin: 20px 0; user-select: none; }
blockquote:hover { color: #FFF; } /* Easter egg for preview */
`
},
{
id: 'legal-pleading',
name: 'Legal Pleading',
category: 'Professional',
description: 'Courtroom document style. Rigid framing, double spacing, and traditional serif fonts.',
vibe: 'Legal, Strict, Formal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=EB+Garamond:wght@400;700&family=Courier+Prime&display=swap',
wordConfig: {
heading1: {
font: "EB Garamond", size: 14, color: "000000", bold: true, align: 'center',
spacing: { before: 240, after: 240, line: 240 },
allCaps: true,
// underline: true - using border bottom to simulate
border: { bottom: { color: "000000", space: 4, style: "single", size: 4 } }
},
heading2: {
font: "EB Garamond", size: 12, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 }
},
body: {
font: "Courier Prime", size: 12, color: "000000", align: 'both',
spacing: { before: 0, after: 0, line: 480 }, // Double Spacing
border: { left: { color: "BDBDBD", space: 12, style: "single", size: 4 } } // Simulates line number bar area
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
line-height: 2.0;
h1 { font-family: 'EB Garamond', serif; font-size: 14pt; font-weight: 700; color: #000000; text-align: center; text-decoration: underline; text-transform: uppercase; margin-bottom: 24px; }
h2 { font-family: 'EB Garamond', serif; font-size: 12pt; font-weight: 700; color: #000000; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 12pt; color: #000000; margin-bottom: 0px; text-align: justify; border-left: 1px solid #BDBDBD; padding-left: 15px; }
blockquote { margin-left: 40px; border-left: 1px solid #BDBDBD; padding-left: 15px; }
`
},
{
id: 'currency-bill',
name: 'Currency Bill',
category: 'Official',
description: 'Banknote aesthetic. Intricate guilloche-style borders and emerald green serif fonts.',
vibe: 'Financial, Secure, Green',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display+SC:wght@700&family=Noto+Serif:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display SC", size: 36, color: "1B5E20", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "2E7D32", space: 6, style: "double", size: 12 },
bottom: { color: "2E7D32", space: 6, style: "double", size: 12 }
}
},
heading2: {
font: "Noto Serif", size: 16, color: "388E3C", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Noto Serif", size: 11, color: "1B5E20", align: 'both',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2E7D32"
},
previewCss: `
font-family: 'Noto Serif', serif;
background: #E8F5E9;
h1 { font-family: 'Playfair Display SC', serif; font-size: 36pt; font-weight: 700; color: #1B5E20; text-align: center; border-top: 4px double #2E7D32; border-bottom: 4px double #2E7D32; padding: 12px 0; margin-bottom: 24px; letter-spacing: 2px; }
h2 { font-size: 16pt; font-weight: 700; color: #388E3C; text-align: center; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #1B5E20; margin-bottom: 14px; text-align: justify; }
blockquote { border: 1px solid #2E7D32; padding: 20px; margin: 24px 0; background: #C8E6C9; text-align: center; border-radius: 50%; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; margin: 0 auto; }
`
},
{
id: 'credit-card-platinum',
name: 'Credit Card Platinum',
category: 'Financial',
description: 'Premium card aesthetic. Silver/gradient shading with OCR-style raised numbering fonts.',
vibe: 'Premium, Silver, Metallic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Krub:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Krub", size: 32, color: "E0E0E0", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "424242", color: "auto", style: "clear" } // Dark grey metallic
},
heading2: {
font: "Share Tech Mono", size: 18, color: "C0C0C0", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true,
tracking: 100 // Wide tracking
},
body: {
font: "Krub", size: 11, color: "616161", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "C0C0C0"
},
previewCss: `
font-family: 'Krub', sans-serif;
h1 { font-size: 32pt; font-weight: 700; color: #E0E0E0; background: linear-gradient(135deg, #616161, #212121); padding: 16px; margin-bottom: 24px; border-radius: 12px; }
h2 { font-family: 'Share Tech Mono', monospace; font-size: 18pt; color: #757575; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 3px; text-shadow: 1px 1px 0 #FFF; }
p { font-size: 11pt; line-height: 1.6; color: #616161; margin-bottom: 14px; }
blockquote { border-left: 4px solid #C0C0C0; padding-left: 16px; margin: 24px 0; background: #F5F5F5; }
`
},
{
id: 'whiteboard-strategy',
name: 'Whiteboard Strategy',
category: 'Business',
description: 'Brainstorming session. Dry-erase marker fonts in standard office colors (Black, Blue, Red).',
vibe: 'Corporate, Brainstorm, Sketch',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Patrick+Hand&display=swap',
wordConfig: {
heading1: {
font: "Architects Daughter", size: 36, color: "D32F2F", bold: true, align: 'center', // Red marker
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "1976D2", space: 4, style: "single", size: 12 } } // Blue underline
},
heading2: {
font: "Patrick Hand", size: 20, color: "1976D2", bold: true, align: 'left', // Blue marker
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Patrick Hand", size: 14, color: "212121", align: 'left', // Black marker
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Patrick Hand', cursive;
background: #FFFFFF;
h1 { font-family: 'Architects Daughter', cursive; font-size: 36pt; font-weight: 700; color: #D32F2F; border-bottom: 3px solid #1976D2; padding-bottom: 10px; margin-bottom: 24px; text-align: center; }
h2 { font-size: 20pt; font-weight: 700; color: #1976D2; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { border: 2px solid #388E3C; padding: 16px; margin: 24px 0; border-radius: 16px; color: #388E3C; } /* Green marker box */
`
},
{
id: 'stock-ticker',
name: 'Stock Ticker',
category: 'Financial',
description: 'LED moving message sign. Dot matrix fonts with bright green/red on black.',
vibe: 'Financial, Digital, LED',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=DotGothic16&display=swap',
wordConfig: {
heading1: {
font: "DotGothic16", size: 32, color: "00E676", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
heading2: {
font: "DotGothic16", size: 18, color: "FF1744", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
body: {
font: "DotGothic16", size: 12, color: "E0E0E0", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "212121", color: "auto", style: "clear" }
},
accentColor: "00E676"
},
previewCss: `
font-family: 'DotGothic16', sans-serif;
background: #212121;
h1 { font-size: 32pt; color: #00E676; background: #000000; padding: 12px; margin-bottom: 24px; border-bottom: 2px solid #333; }
h2 { font-size: 18pt; color: #FF1744; background: #000000; padding: 8px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 12pt; line-height: 1.5; color: #E0E0E0; margin-bottom: 14px; }
blockquote { border-left: 4px solid #00E676; padding-left: 16px; margin: 24px 0; color: #B9F6CA; }
`
},
{
id: 'police-blotter',
name: 'Police Blotter',
category: 'Official',
description: 'Law enforcement paperwork. Carbon copy blue fonts on stark white forms.',
vibe: 'Official, Blue, Bureaucratic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Courier+Prime&family=Special+Elite&display=swap',
wordConfig: {
heading1: {
font: "Special Elite", size: 26, color: "0D47A1", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
allCaps: true
},
heading2: {
font: "Courier Prime", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "E3F2FD", color: "auto", style: "clear" }
},
body: {
font: "Courier Prime", size: 11, color: "1A237E", align: 'left', // Carbon copy blue
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0D47A1"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-family: 'Special Elite', cursive; font-size: 26pt; color: #0D47A1; margin-bottom: 24px; text-transform: uppercase; text-decoration: underline; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; background: #E3F2FD; display: block; padding: 4px; margin-top: 32px; margin-bottom: 16px; border: 1px solid #0D47A1; }
p { font-size: 11pt; line-height: 1.5; color: #1A237E; margin-bottom: 14px; }
blockquote { border: 1px solid #000; padding: 16px; margin: 24px 0; background: #FAFAFA; }
`
}
];

View File

@@ -1,364 +0,0 @@
import { StyleOption } from '../types';
export const creativeStyles: StyleOption[] = [
{
id: 'brutalist-mono',
name: 'Brutalist Lab',
category: 'Creative',
description: 'Raw, monospaced aesthetic. Boxes, thick outlines, and typewriter vibes.',
vibe: 'Code, Industrial, Raw',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Space Mono", size: 24, color: "000000", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "000000", space: 12, style: "single", size: 24 },
bottom: { color: "000000", space: 12, style: "single", size: 24 },
left: { color: "000000", space: 12, style: "single", size: 24 },
right: { color: "000000", space: 12, style: "single", size: 24 }
}
},
heading2: {
font: "Space Mono", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "E0E0E0", color: "auto", style: "clear" }
},
body: {
font: "Space Mono", size: 10, color: "111111", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Space Mono', monospace;
h1 { font-size: 24pt; font-weight: 700; color: #000000; border: 4px solid #000; padding: 16px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; background: #E0E0E0; padding: 8px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 10pt; line-height: 1.6; color: #111111; margin-bottom: 14px; }
blockquote { border-left: 6px solid #000; padding-left: 16px; margin: 24px 0; font-weight: 700; }
`
},
{
id: 'bauhaus-poster',
name: 'Bauhaus Poster',
category: 'Creative',
description: 'Geometric minimalism. Primary colors, angled text, and strong diagonal compositions.',
vibe: 'Artistic, Geometric, Modernist',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@400;700&family=Jost:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Josefin Sans", size: 36, color: "D50000", bold: true, align: 'right',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "000000", space: 8, style: "single", size: 36 } }
},
heading2: {
font: "Jost", size: 16, color: "2962FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" }
},
body: {
font: "Jost", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D50000"
},
previewCss: `
font-family: 'Jost', sans-serif;
h1 { font-family: 'Josefin Sans', sans-serif; font-size: 36pt; font-weight: 700; color: #D50000; text-align: right; border-bottom: 8px solid #000; padding-bottom: 10px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 16pt; font-weight: 700; color: #2962FF; background: #FFEB3B; padding: 8px 16px; margin-top: 32px; margin-bottom: 16px; display: inline-block; transform: rotate(-1deg); }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 10px solid #D50000; padding-left: 20px; margin: 24px 0; font-style: italic; }
`
},
{
id: 'glitch-art',
name: 'Glitch Art',
category: 'Creative',
description: 'Digital distortion aesthetic. Monospaced fonts with neon colors and "broken" styling.',
vibe: 'Digital, Glitch, Cyber',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Rubik+Glitch&family=Share+Tech+Mono&display=swap',
wordConfig: {
heading1: {
font: "Rubik Glitch", size: 40, color: "D500F9", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "121212", color: "auto", style: "clear" }
},
heading2: {
font: "Share Tech Mono", size: 18, color: "00E5FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Share Tech Mono", size: 11, color: "BDBDBD", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
accentColor: "00E5FF"
},
previewCss: `
font-family: 'Share Tech Mono', monospace;
background: #000000;
color: #BDBDBD;
h1 { font-family: 'Rubik Glitch', cursive; font-size: 40pt; color: #D500F9; background: #121212; padding: 10px; margin-bottom: 24px; text-shadow: 2px 0 #00E5FF; }
h2 { font-size: 18pt; font-weight: 700; color: #00E5FF; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 11pt; line-height: 1.5; color: #BDBDBD; margin-bottom: 14px; }
blockquote { border: 1px solid #D500F9; padding: 16px; margin: 24px 0; color: #D500F9; box-shadow: 4px 4px 0 #00E5FF; }
`
},
{
id: 'vaporwave-aesthetic',
name: 'Vaporwave Aesthetic',
category: 'Creative',
description: 'Nostalgic 80s/90s internet culture. Pink and cyan gradients, wide tracking, and ironic luxury.',
vibe: 'Nostalgic, Digital, Surreal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Varela+Round&family=Montserrat:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Montserrat", size: 32, color: "FF71CE", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
allCaps: true,
tracking: 100 // Wide spacing
},
heading2: {
font: "Varela Round", size: 16, color: "01CDFE", bold: false, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Montserrat", size: 10, color: "B967FF", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "01CDFE"
},
previewCss: `
font-family: 'Montserrat', sans-serif;
background: #F0F0FF;
h1 { font-size: 32pt; font-weight: 700; color: #FF71CE; text-align: center; margin-bottom: 24px; letter-spacing: 4px; text-shadow: 2px 2px #01CDFE; text-transform: uppercase; }
h2 { font-family: 'Varela Round', sans-serif; font-size: 16pt; color: #01CDFE; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #B967FF; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px solid #FF71CE; padding: 16px; margin: 24px 0; background: #E0FFFF; color: #01CDFE; }
`
},
{
id: 'pop-art-comic',
name: 'Pop Art Comic',
category: 'Creative',
description: 'Roy Lichtenstein inspired. Bold black outlines, halftone vibes, and primary colors.',
vibe: 'Comic, Bold, Pop',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Bangers", size: 42, color: "000000", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFFF00", color: "auto", style: "clear" } // Yellow back
},
heading2: {
font: "Bangers", size: 24, color: "FFFFFF", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FF0000", color: "auto", style: "clear" } // Red back
},
body: {
font: "Comic Neue", size: 12, color: "000000", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Comic Neue', sans-serif;
h1 { font-family: 'Bangers', cursive; font-size: 42pt; color: #000000; background: #FFFF00; padding: 10px; border: 4px solid #000; box-shadow: 5px 5px 0 #000; margin-bottom: 24px; display: inline-block; }
h2 { font-family: 'Bangers', cursive; font-size: 24pt; color: #FFFFFF; background: #FF0000; padding: 5px 15px; border: 3px solid #000; margin-top: 32px; margin-bottom: 16px; display: inline-block; transform: rotate(-2deg); }
p { font-size: 12pt; line-height: 1.5; color: #000000; margin-bottom: 14px; font-weight: 700; }
blockquote { background: #FFFFFF; border: 3px solid #000; padding: 20px; border-radius: 50% / 20%; margin: 24px 0; text-align: center; }
`
},
{
id: 'grunge-90s',
name: 'Grunge 90s',
category: 'Creative',
description: 'Distressed and edgy. David Carson inspired typography with overlapping elements and grit.',
vibe: 'Dirty, Edgy, Chaotic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Special+Elite&display=swap',
wordConfig: {
heading1: {
font: "Permanent Marker", size: 36, color: "333333", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Special Elite", size: 18, color: "8B0000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "dashed", size: 12 } }
},
body: {
font: "Special Elite", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "8B0000"
},
previewCss: `
font-family: 'Special Elite', cursive;
background-color: #F0F0F0;
h1 { font-family: 'Permanent Marker', cursive; font-size: 36pt; color: #333333; text-align: center; margin-bottom: 24px; transform: rotate(1deg); opacity: 0.9; }
h2 { font-size: 18pt; font-weight: 700; color: #8B0000; margin-top: 32px; margin-bottom: 16px; border-bottom: 2px dashed #000; display: inline-block; transform: skewX(-5deg); }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 3px solid #000; padding: 16px; margin: 24px 0; background: #D3D3D3; transform: rotate(-1deg); }
`
},
{
id: 'risograph-print',
name: 'Risograph Print',
category: 'Creative',
description: 'Independent publishing style. Grainy textures, misalignment, and vibrant spot colors (Pink/Teal).',
vibe: 'Artistic, Indie, Textured',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Eb+Garamond:ital,wght@0,400;0,700;1,400&family=Karla:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Eb Garamond", size: 42, color: "FF007F", bold: true, align: 'left', // Riso Pink
spacing: { before: 400, after: 200, line: 200 },
italic: true
},
heading2: {
font: "Karla", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "00A9FF", color: "auto", style: "clear" } // Riso Blue background
},
body: {
font: "Karla", size: 10, color: "1A1A1A", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF007F"
},
previewCss: `
font-family: 'Karla', sans-serif;
background: #FFFEF0;
h1 { font-family: 'Eb Garamond', serif; font-size: 42pt; font-weight: 700; font-style: italic; color: #FF007F; margin-bottom: 24px; mix-blend-mode: multiply; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; background: #00A9FF; display: inline-block; padding: 4px 10px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #1A1A1A; margin-bottom: 14px; }
blockquote { border-left: 6px solid #FF007F; padding-left: 16px; margin: 24px 0; color: #555; font-style: italic; }
`
},
{
id: 'blueprint-cyanotype',
name: 'Blueprint Cyanotype',
category: 'Creative',
description: 'Architectural blueprint style. White lines on a deep cyan blue background.',
vibe: 'Technical, Blue, Schematic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Roboto+Mono:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Architects Daughter", size: 32, color: "FFFFFF", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "FFFFFF", space: 4, style: "single", size: 12 } }
},
heading2: {
font: "Roboto Mono", size: 14, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Roboto Mono", size: 10, color: "E0F7FA", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFFFFF"
},
previewCss: `
font-family: 'Roboto Mono', monospace;
background: #0047AB; /* Cobalt/Blueprint Blue */
color: #FFFFFF;
h1 { font-family: 'Architects Daughter', cursive; font-size: 32pt; font-weight: 700; color: #FFFFFF; text-align: center; border-bottom: 2px solid #FFFFFF; margin-bottom: 24px; padding-bottom: 8px; }
h2 { font-size: 14pt; font-weight: 700; color: #FFFFFF; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; border: 1px solid #FFF; display: inline-block; padding: 4px; }
p { font-size: 10pt; line-height: 1.6; color: #E0F7FA; margin-bottom: 14px; }
blockquote { border: 1px dashed #FFFFFF; padding: 16px; margin: 24px 0; background: #003380; }
`
},
{
id: 'street-art-graffiti',
name: 'Street Art Graffiti',
category: 'Creative',
description: 'Urban street style. Spray paint fonts, dripping effects, and concrete vibes.',
vibe: 'Urban, Rebel, Spray',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Rock+Salt&display=swap',
wordConfig: {
heading1: {
font: "Permanent Marker", size: 48, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFFF00", color: "auto", style: "clear" } // Hazard Yellow
},
heading2: {
font: "Rock Salt", size: 18, color: "FF0000", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Permanent Marker", size: 12, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Permanent Marker', cursive;
background: #F0F0F0;
h1 { font-size: 48pt; color: #000000; text-align: center; background: #FFFF00; padding: 10px; transform: rotate(-2deg); margin-bottom: 24px; box-shadow: 4px 4px 0 #000; }
h2 { font-family: 'Rock Salt', cursive; font-size: 18pt; color: #FF0000; margin-top: 32px; margin-bottom: 16px; transform: rotate(1deg); }
p { font-size: 12pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { border-left: 5px solid #000; padding-left: 16px; margin: 24px 0; font-family: 'Rock Salt', cursive; font-size: 10pt; }
`
},
{
id: 'origami-paper',
name: 'Origami Paper',
category: 'Creative',
description: 'Folded paper aesthetic. Sharp angles, subtle shadows, and crisp typography.',
vibe: 'Delicate, Sharp, Paper',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cormorant+Unicase:wght@400;700&family=Muli:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Cormorant Unicase", size: 32, color: "37474F", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Cormorant Unicase", size: 16, color: "546E7A", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Muli", size: 10, color: "455A64", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "37474F"
},
previewCss: `
font-family: 'Muli', sans-serif;
background: #FAFAFA;
h1 { font-family: 'Cormorant Unicase', serif; font-size: 32pt; font-weight: 700; color: #37474F; text-align: center; margin-bottom: 24px; text-shadow: 1px 1px 0 #CCC; }
h2 { font-family: 'Cormorant Unicase', serif; font-size: 16pt; color: #546E7A; margin-top: 32px; margin-bottom: 16px; border-bottom: 1px solid #CFD8DC; display: inline-block; padding-bottom: 4px; }
p { font-size: 10pt; line-height: 1.7; color: #455A64; margin-bottom: 14px; }
blockquote { background: #FFF; padding: 20px; border: 1px solid #ECEFF1; box-shadow: 2px 2px 5px rgba(0,0,0,0.05); margin: 24px 0; transform: rotate(1deg); }
`
},
{
id: 'watercolor-wash',
name: 'Watercolor Wash',
category: 'Creative',
description: 'Artistic and fluid. Soft blended colors and painterly fonts.',
vibe: 'Artistic, Soft, Fluid',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Kalam:wght@400;700&family=Amatic+SC:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Kalam", size: 36, color: "7986CB", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Amatic SC", size: 24, color: "4DB6AC", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Kalam", size: 12, color: "616161", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "7986CB"
},
previewCss: `
font-family: 'Kalam', cursive;
h1 { font-size: 36pt; font-weight: 700; color: #7986CB; text-align: center; margin-bottom: 24px; text-shadow: 2px 2px 4px rgba(121, 134, 203, 0.3); }
h2 { font-family: 'Amatic SC', cursive; font-size: 24pt; font-weight: 700; color: #4DB6AC; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.6; color: #616161; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px solid #E0F2F1; background: #E8EAF6; padding: 20px; border-radius: 50% 20% / 10% 40%; margin: 24px 0; text-align: center; }
`
}
];

View File

@@ -1,284 +0,0 @@
import { StyleOption } from '../types';
export const editorialStyles: StyleOption[] = [
{
id: 'ny-editor',
name: 'The Editorial',
category: 'Editorial',
description: 'High-contrast serifs, centered headers, delicate borders. Feels like a premium magazine feature.',
vibe: 'Luxury, Fashion, Literature',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400;1,400&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display", size: 36, color: "111111", bold: true, italic: true, align: 'center',
spacing: { before: 600, after: 400, line: 240 }
},
heading2: {
font: "Playfair Display", size: 14, color: "444444", bold: true, allCaps: true, tracking: 100, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "DDDDDD", space: 8, style: "single", size: 4 } }
},
body: {
font: "Lora", size: 11, color: "333333", align: 'both',
spacing: { before: 0, after: 200, line: 320 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Lora', serif;
h1 { font-family: 'Playfair Display', serif; font-size: 36pt; font-weight: 700; font-style: italic; text-align: center; margin-bottom: 30px; }
h2 { font-family: 'Playfair Display', serif; font-size: 14pt; font-weight: 700; text-transform: uppercase; letter-spacing: 3px; text-align: center; border-bottom: 1px solid #ddd; padding-bottom: 10px; margin-top: 40px; color: #444; }
p { font-size: 11pt; line-height: 1.8; text-align: justify; margin-bottom: 20px; }
blockquote { font-family: 'Playfair Display', serif; font-size: 18pt; font-style: italic; text-align: center; color: #555; margin: 40px 20px; border: none; }
`
},
{
id: 'luxury-editorial',
name: 'Luxury Editorial',
category: 'Editorial',
description: 'High-fashion magazine inspired design. Elegant serifs with sophisticated spacing and premium feel for upscale content.',
vibe: 'Fashion, Premium, Sophisticated',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cormorant:wght@400;500;700&family=Montserrat:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Cormorant", size: 36, color: "1C1C1C", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Montserrat", size: 11, color: "1C1C1C", bold: true, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Montserrat", size: 10, color: "3A3A3A", align: 'left',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "B8860B"
},
previewCss: `
font-family: 'Montserrat', sans-serif;
h1 { font-family: 'Cormorant', serif; font-size: 36pt; font-weight: 500; color: #1C1C1C; text-align: center; margin-bottom: 32px; letter-spacing: 2px; }
h2 { font-size: 11pt; font-weight: 500; color: #1C1C1C; margin-top: 36px; margin-bottom: 18px; text-transform: uppercase; letter-spacing: 3px; }
p { font-size: 10pt; line-height: 1.7; color: #3A3A3A; margin-bottom: 16px; font-weight: 300; }
blockquote { border-top: 1px solid #B8860B; border-bottom: 1px solid #B8860B; padding: 24px 0; margin: 32px 0; text-align: center; font-family: 'Cormorant', serif; font-size: 14pt; font-style: italic; }
`
},
{
id: 'newspaper-classic',
name: 'Newspaper Classic',
category: 'Editorial',
description: 'Traditional newspaper layout with strong typographic hierarchy. Bold headlines, readable body text, and authoritative presence.',
vibe: 'News, Authority, Information',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=PT+Serif:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display", size: 36, color: "000000", bold: true, align: 'center',
spacing: { before: 240, after: 160, line: 220 }
},
heading2: {
font: "Playfair Display", size: 16, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 }
},
body: {
font: "PT Serif", size: 10, color: "1A1A1A", align: 'both',
spacing: { before: 0, after: 120, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'PT Serif', serif;
h1 { font-family: 'Playfair Display', serif; font-size: 36pt; font-weight: 900; color: #000000; text-align: center; border-bottom: 2px solid #000000; padding-bottom: 12px; margin-bottom: 20px; }
h2 { font-family: 'Playfair Display', serif; font-size: 16pt; font-weight: 700; color: #000000; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #1A1A1A; margin-bottom: 10px; text-align: justify; }
blockquote { font-size: 14pt; font-style: italic; text-align: center; margin: 24px 40px; }
`
},
{
id: 'newspaper-modern',
name: 'Newspaper Modern',
category: 'Editorial',
description: 'Contemporary newspaper design balancing tradition with modern sensibilities. Clean grid-based layout.',
vibe: 'Modern, Editorial, Informative',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=DM+Sans:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "DM Serif Display", size: 36, color: "1A1A1A", bold: false, align: 'left',
spacing: { before: 320, after: 200, line: 220 }
},
heading2: {
font: "DM Sans", size: 12, color: "1A1A1A", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 }
},
body: {
font: "DM Sans", size: 10, color: "333333", align: 'both',
spacing: { before: 0, after: 140, line: 280 }
},
accentColor: "DC2626"
},
previewCss: `
font-family: 'DM Sans', sans-serif;
h1 { font-family: 'DM Serif Display', serif; font-size: 36pt; color: #1A1A1A; margin-bottom: 24px; }
h2 { font-size: 12pt; font-weight: 700; color: #1A1A1A; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 1px; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 12px; text-align: justify; }
blockquote { font-size: 16pt; font-style: italic; font-family: 'DM Serif Display', serif; text-align: center; margin: 28px 20px; color: #DC2626; }
`
},
{
id: 'newspaper-tabloid',
name: 'Newspaper Tabloid',
category: 'Editorial',
description: 'Sensationalist news design. Condensed, heavy headers with urgent red accents.',
vibe: 'Urgent, Bold, Sensational',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Antonio:wght@700&family=Pathway+Extreme:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Antonio", size: 42, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 200 },
shading: { fill: "D50000", color: "auto", style: "clear" }
},
heading2: {
font: "Antonio", size: 20, color: "000000", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 220 }
},
body: {
font: "Pathway Extreme", size: 10, color: "111111", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D50000"
},
previewCss: `
font-family: 'Pathway Extreme', sans-serif;
h1 { font-family: 'Antonio', sans-serif; font-size: 42pt; font-weight: 700; color: #FFFFFF; background: #D50000; padding: 12px 16px; margin-bottom: 24px; text-transform: uppercase; line-height: 1; }
h2 { font-family: 'Antonio', sans-serif; font-size: 20pt; font-weight: 700; color: #000000; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.5; color: #111111; margin-bottom: 14px; }
blockquote { font-size: 14pt; font-weight: 700; font-style: italic; color: #000000; border-left: 6px solid #D50000; padding-left: 16px; margin: 24px 0; }
`
},
{
id: 'indie-zine',
name: 'Indie Zine',
category: 'Editorial',
description: 'DIY photocopier aesthetic. Typewriter fonts, cut-out look, and rebellious asymmetry.',
vibe: 'DIY, Rebellious, Rough',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Special+Elite&family=Rock+Salt&display=swap',
wordConfig: {
heading1: {
font: "Rock Salt", size: 28, color: "000000", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "D1C4E9", color: "auto", style: "clear" }
},
heading2: {
font: "Special Elite", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "single", size: 12 } }
},
body: {
font: "Special Elite", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Special Elite', cursive;
h1 { font-family: 'Rock Salt', cursive; font-size: 28pt; color: #000000; background: #D1C4E9; padding: 10px; transform: rotate(-2deg); margin-bottom: 24px; display: inline-block; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; border-bottom: 2px solid #000; margin-top: 32px; margin-bottom: 16px; width: fit-content; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 2px dashed #000; padding: 16px; margin: 24px 0; transform: rotate(1deg); }
`
},
{
id: 'literary-review',
name: 'Literary Review',
category: 'Editorial',
description: 'Classic bookish aesthetic. Dense serif text, elegant headers, perfect for long-form essays.',
vibe: 'Intellectual, Dense, Classic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,400;0,700;1,400&family=Sorts+Mill+Goudy&display=swap',
wordConfig: {
heading1: {
font: "Crimson Pro", size: 30, color: "4A148C", bold: true, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Crimson Pro", size: 14, color: "000000", bold: false, align: 'center',
spacing: { before: 360, after: 180, line: 240 },
smallCaps: true
},
body: {
font: "Sorts Mill Goudy", size: 11, color: "212121", align: 'both',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "4A148C"
},
previewCss: `
font-family: 'Sorts Mill Goudy', serif;
h1 { font-family: 'Crimson Pro', serif; font-size: 30pt; font-weight: 700; color: #4A148C; text-align: center; margin-bottom: 32px; }
h2 { font-family: 'Crimson Pro', serif; font-size: 14pt; color: #000000; text-align: center; margin-top: 36px; margin-bottom: 18px; font-variant: small-caps; }
p { font-size: 11pt; line-height: 1.8; color: #212121; margin-bottom: 16px; text-align: justify; }
blockquote { margin: 24px 40px; font-style: italic; color: #4A148C; }
`
},
{
id: 'fashion-magazine',
name: 'Fashion Magazine',
category: 'Editorial',
description: 'Trendy and bold. High-contrast typography with oversized lettering and pink accents.',
vibe: 'Trendy, Loud, Pink',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Abril+Fatface&family=Raleway:wght@300;700&display=swap',
wordConfig: {
heading1: {
font: "Abril Fatface", size: 48, color: "E91E63", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 200 }
},
heading2: {
font: "Raleway", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true,
tracking: 200
},
body: {
font: "Raleway", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "E91E63"
},
previewCss: `
font-family: 'Raleway', sans-serif;
h1 { font-family: 'Abril Fatface', cursive; font-size: 48pt; color: #E91E63; margin-bottom: 24px; line-height: 0.9; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; text-transform: uppercase; letter-spacing: 4px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; font-weight: 300; }
blockquote { border-left: 10px solid #E91E63; padding-left: 20px; margin: 24px 0; font-family: 'Abril Fatface', cursive; font-size: 18pt; }
`
},
{
id: 'film-script',
name: 'Film Script',
category: 'Editorial',
description: 'Hollywood screenplay format. Courier font, specific margins, and character name centering.',
vibe: 'Cinematic, Format, Draft',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Courier+Prime:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Courier Prime", size: 12, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 240, line: 240 },
allCaps: true,
underline: true
},
heading2: {
font: "Courier Prime", size: 12, color: "000000", bold: true, align: 'center',
spacing: { before: 480, after: 0, line: 240 },
allCaps: true
},
body: {
font: "Courier Prime", size: 12, color: "000000", align: 'left',
spacing: { before: 0, after: 0, line: 240 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-size: 12pt; font-weight: 700; color: #000000; text-decoration: underline; text-transform: uppercase; margin-bottom: 12px; }
h2 { font-size: 12pt; font-weight: 700; color: #000000; text-transform: uppercase; text-align: center; margin-top: 24px; margin-bottom: 0px; }
p { font-size: 12pt; line-height: 1; color: #000000; margin-bottom: 12px; }
blockquote { margin: 0 40px; text-align: center; }
`
}
];

View File

@@ -1,22 +1,8 @@
import { StyleOption } from '../types';
import { minimalistStyles } from './minimalist';
import { editorialStyles } from './editorial';
import { corporateStyles } from './corporate';
import { techStyles } from './tech';
import { creativeStyles } from './creative';
import { vintageStyles } from './vintage';
import { lifestyleStyles } from './lifestyle';
import { academicStyles } from './academic';
import { industrialStyles } from './industrial';
// Templates are now loaded dynamically from JSON files
// See src/services/templateLoader.ts for the loading mechanism
// Templates are bundled in src-tauri/templates/ and loaded at runtime
export const ALL_STYLES: StyleOption[] = [
...minimalistStyles,
...editorialStyles,
...corporateStyles,
...techStyles,
...creativeStyles,
...vintageStyles,
...lifestyleStyles,
...academicStyles,
...industrialStyles
];
import { StyleOption } from '../types';
// Export empty array - templates are loaded dynamically
export const ALL_STYLES: StyleOption[] = [];

View File

@@ -1,617 +0,0 @@
import { StyleOption } from '../types';
export const industrialStyles: StyleOption[] = [
{
id: 'construction-industrial',
name: 'Construction Industrial',
category: 'Industrial',
description: 'Bold and sturdy design for construction and industrial companies. Strong typography with safety-inspired colors.',
vibe: 'Strong, Industrial, Professional',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;700&family=Roboto:wght@400;500&display=swap',
wordConfig: {
heading1: {
font: "Oswald", size: 28, color: "212121", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFC107", color: "auto", style: "clear" }
},
heading2: {
font: "Oswald", size: 16, color: "FF8F00", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Roboto", size: 10, color: "37474F", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF8F00"
},
previewCss: `
font-family: 'Roboto', sans-serif;
h1 { font-family: 'Oswald', sans-serif; font-size: 28pt; font-weight: 700; color: #212121; background: #FFC107; padding: 16px 20px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-family: 'Oswald', sans-serif; font-size: 16pt; font-weight: 700; color: #FF8F00; margin-top: 30px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #37474F; margin-bottom: 14px; }
blockquote { background: #FFF8E1; padding: 16px; border-left: 6px solid #FF8F00; margin: 20px 0; }
`
},
{
id: 'automotive-bold',
name: 'Automotive Bold',
category: 'Automotive',
description: 'Powerful design for automotive and motorsport industries. Bold typography with dynamic energy and speed.',
vibe: 'Powerful, Dynamic, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Teko:wght@400;500;700&family=Barlow:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Teko", size: 40, color: "B71C1C", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 200 }
},
heading2: {
font: "Teko", size: 20, color: "212121", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 220 }
},
body: {
font: "Barlow", size: 10, color: "37474F", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "B71C1C"
},
previewCss: `
font-family: 'Barlow', sans-serif;
h1 { font-family: 'Teko', sans-serif; font-size: 40pt; font-weight: 700; color: #B71C1C; margin-bottom: 24px; text-transform: uppercase; letter-spacing: 2px; }
h2 { font-family: 'Teko', sans-serif; font-size: 20pt; font-weight: 500; color: #212121; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #37474F; margin-bottom: 14px; }
blockquote { background: #FFEBEE; padding: 16px; border-left: 6px solid #B71C1C; margin: 20px 0; }
`
},
{
id: 'real-estate-premium',
name: 'Real Estate Premium',
category: 'Real Estate',
description: 'Sophisticated design for luxury real estate and property marketing. Elegant typography with premium golden accents.',
vibe: 'Luxury, Premium, Sophisticated',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Lato:wght@300;400;700&display=swap',
wordConfig: {
heading1: {
font: "Cinzel", size: 28, color: "1A1A1A", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Cinzel", size: 14, color: "B8860B", bold: false, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Lato", size: 10, color: "4A4A4A", align: 'left',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "B8860B"
},
previewCss: `
font-family: 'Lato', sans-serif;
h1 { font-family: 'Cinzel', serif; font-size: 28pt; font-weight: 600; color: #1A1A1A; text-align: center; margin-bottom: 32px; letter-spacing: 4px; }
h2 { font-family: 'Cinzel', serif; font-size: 14pt; color: #B8860B; margin-top: 36px; margin-bottom: 18px; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 1.7; color: #4A4A4A; margin-bottom: 16px; font-weight: 300; }
blockquote { border-top: 1px solid #B8860B; border-bottom: 1px solid #B8860B; padding: 24px 0; margin: 32px 0; text-align: center; }
`
},
{
id: 'environmental-green',
name: 'Environmental Green',
category: 'Sustainability',
description: 'Nature-inspired design for environmental and sustainability communications. Organic feel with earthy green palette.',
vibe: 'Natural, Sustainable, Organic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bitter:wght@400;500;700&family=Karla:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Bitter", size: 26, color: "2E7D32", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Bitter", size: 14, color: "388E3C", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Karla", size: 10, color: "3E4A3D", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2E7D32"
},
previewCss: `
font-family: 'Karla', sans-serif;
h1 { font-family: 'Bitter', serif; font-size: 26pt; font-weight: 700; color: #2E7D32; margin-bottom: 24px; }
h2 { font-family: 'Bitter', serif; font-size: 14pt; font-weight: 700; color: #388E3C; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.6; color: #3E4A3D; margin-bottom: 14px; }
blockquote { background: #E8F5E9; padding: 16px; border-left: 4px solid #2E7D32; margin: 20px 0; }
`
},
{
id: 'aviation-classic',
name: 'Aviation Classic',
category: 'Transport',
description: 'Vintage aviation inspired design. Classic military typography with heritage color palette.',
vibe: 'Heritage, Classic, Aviation',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Stencil&family=Public+Sans:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Stencil", size: 28, color: "1B4D3E", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Public Sans", size: 13, color: "8B0000", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Public Sans", size: 10, color: "363636", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "8B0000"
},
previewCss: `
font-family: 'Public Sans', sans-serif;
h1 { font-family: 'Stencil', sans-serif; font-size: 28pt; color: #1B4D3E; margin-bottom: 24px; letter-spacing: 3px; }
h2 { font-size: 13pt; font-weight: 600; color: #8B0000; margin-top: 30px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #363636; margin-bottom: 14px; }
blockquote { background: #F5F5DC; padding: 16px; border-left: 4px solid #1B4D3E; margin: 20px 0; }
`
},
{
id: 'architecture-firm',
name: 'Architecture Firm',
category: 'Architecture',
description: 'Structural precision for architecture and design firms. Clean geometry with sophisticated restraint.',
vibe: 'Structural, Precise, Sophisticated',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Fahkwang:wght@400;500;600&family=Manrope:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Fahkwang", size: 28, color: "1A1A1A", bold: false, align: 'left',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Manrope", size: 12, color: "4A4A4A", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
body: {
font: "Manrope", size: 10, color: "525252", align: 'left',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "1A1A1A"
},
previewCss: `
font-family: 'Manrope', sans-serif;
h1 { font-family: 'Fahkwang', sans-serif; font-size: 28pt; font-weight: 500; color: #1A1A1A; margin-bottom: 32px; letter-spacing: 1px; }
h2 { font-size: 12pt; font-weight: 500; color: #4A4A4A; margin-top: 40px; margin-bottom: 20px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 1.7; color: #525252; margin-bottom: 16px; font-weight: 300; }
blockquote { border-left: 1px solid #1A1A1A; padding-left: 24px; margin: 32px 0; }
`
},
{
id: 'electric-vehicle',
name: 'Electric Vehicle',
category: 'Automotive',
description: 'Sustainable tech design for electric vehicle and clean energy content. Eco-modern with electric accents.',
vibe: 'Sustainable, Modern, Electric',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Electrolize&family=Mulish:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Electrolize", size: 28, color: "10B981", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Mulish", size: 14, color: "1E293B", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Mulish", size: 10, color: "475569", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "10B981"
},
previewCss: `
font-family: 'Mulish', sans-serif;
h1 { font-family: 'Electrolize', sans-serif; font-size: 28pt; color: #10B981; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #1E293B; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #475569; margin-bottom: 14px; }
blockquote { background: #ECFDF5; padding: 16px; border-left: 4px solid #10B981; margin: 20px 0; }
`
},
{
id: 'industrial-blueprint',
name: 'Industrial Blueprint',
category: 'Technical',
description: 'Engineering-inspired design reminiscent of technical blueprints. Precise typography with drafting aesthetics.',
vibe: 'Technical, Precise, Engineering',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Titillium+Web:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Titillium Web", size: 24, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "1A3A5C", color: "auto", style: "clear" }
},
heading2: {
font: "Titillium Web", size: 14, color: "1A3A5C", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Share Tech Mono", size: 9, color: "2C4A6B", align: 'left',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "1A3A5C"
},
previewCss: `
font-family: 'Share Tech Mono', monospace;
h1 { font-family: 'Titillium Web', sans-serif; font-size: 24pt; font-weight: 700; color: #FFFFFF; background: #1A3A5C; padding: 16px 20px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-family: 'Titillium Web', sans-serif; font-size: 14pt; font-weight: 700; color: #1A3A5C; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 9pt; line-height: 1.7; color: #2C4A6B; margin-bottom: 14px; }
blockquote { background: #E8F4FC; padding: 16px; border: 1px dashed #1A3A5C; margin: 20px 0; }
`
},
{
id: 'tactical-military',
name: 'Tactical Military',
category: 'Industrial',
description: 'Military-spec aesthetic. Stencil typography with olive drab and technical readouts.',
vibe: 'Tactical, Sturdy, Regulated',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Allerta+Stencil&family=Quantico:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Allerta Stencil", size: 28, color: "33691E", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "558B2F", space: 6, style: "single", size: 16 } }
},
heading2: {
font: "Quantico", size: 14, color: "558B2F", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Quantico", size: 10, color: "1B1B1B", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "33691E"
},
previewCss: `
font-family: 'Quantico', sans-serif;
background: #F1F8E9;
h1 { font-family: 'Allerta Stencil', sans-serif; font-size: 28pt; color: #33691E; border-bottom: 4px solid #558B2F; padding-bottom: 12px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #558B2F; margin-top: 30px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #1B1B1B; margin-bottom: 14px; }
blockquote { border: 2px solid #33691E; padding: 16px; margin: 20px 0; background: #DCEDC8; font-family: 'Quantico', sans-serif; }
`
},
{
id: 'logistics-freight',
name: 'Logistics Freight',
category: 'Industrial',
description: 'Shipping and cargo aesthetic. Stenciled, heavy typography with industrial orange and slate blue.',
vibe: 'Industrial, Heavy, Shipping',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Saira+Stencil+One&family=Saira:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Saira Stencil One", size: 36, color: "E65100", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Saira", size: 16, color: "263238", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Saira", size: 10, color: "455A64", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "E65100"
},
previewCss: `
font-family: 'Saira', sans-serif;
h1 { font-family: 'Saira Stencil One', sans-serif; font-size: 36pt; color: #E65100; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 600; color: #263238; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.5; color: #455A64; margin-bottom: 14px; }
blockquote { background: #FFF3E0; padding: 16px; border-left: 8px solid #263238; margin: 20px 0; }
`
},
{
id: 'public-transit',
name: 'Public Transit',
category: 'Urban',
description: 'Subway map and transit signage aesthetic. Clean, highly legible sans-serifs with color-coded lines.',
vibe: 'Urban, Functional, Color-coded',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Mukta:wght@400;700&family=Hanken+Grotesk:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Hanken Grotesk", size: 32, color: "000000", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "FFC107", space: 8, style: "single", size: 24 } }
},
heading2: {
font: "Mukta", size: 18, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "EEEEEE", color: "auto", style: "clear" }
},
body: {
font: "Mukta", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFC107"
},
previewCss: `
font-family: 'Mukta', sans-serif;
h1 { font-family: 'Hanken Grotesk', sans-serif; font-size: 32pt; font-weight: 700; color: #000000; border-bottom: 6px solid #FFC107; padding-bottom: 12px; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #000000; background: #EEEEEE; padding: 4px 12px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 6px solid #F44336; padding-left: 16px; margin: 24px 0; }
`
},
{
id: 'industrial-safety',
name: 'Industrial Safety',
category: 'Industrial',
description: 'Construction site signage. Heavy blacks and yellow caution stripes.',
vibe: 'Safety, Bold, Construction',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Anton&family=Roboto:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Anton", size: 48, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFD600", color: "auto", style: "clear" }
},
heading2: {
font: "Roboto", size: 16, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "thick", size: 24 } },
allCaps: true
},
body: {
font: "Roboto", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFD600"
},
previewCss: `
font-family: 'Roboto', sans-serif;
h1 { font-family: 'Anton', sans-serif; font-size: 48pt; color: #000000; background: repeating-linear-gradient(45deg, #FFD600, #FFD600 10px, #FFC400 10px, #FFC400 20px); text-align: center; padding: 20px; margin-bottom: 24px; text-transform: uppercase; border: 4px solid #000; }
h2 { font-size: 16pt; font-weight: 700; color: #000000; margin-top: 32px; margin-bottom: 16px; border-bottom: 4px solid #000; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { border: 2px solid #000; background: #FFF9C4; padding: 16px; margin: 24px 0; font-weight: 700; }
`
},
{
id: 'highway-interstate',
name: 'Highway Interstate',
category: 'Urban',
description: 'Road signage aesthetic. Reflective white text on deep highway green backgrounds.',
vibe: 'Functional, Travel, Signage',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Overpass:wght@400;700&family=Public+Sans:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Overpass", size: 36, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "00695C", color: "auto", style: "clear" },
border: {
bottom: { color: "FFFFFF", space: 4, style: "single", size: 12 },
top: { color: "FFFFFF", space: 4, style: "single", size: 12 }
}
},
heading2: {
font: "Public Sans", size: 18, color: "004D40", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Public Sans", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "00695C"
},
previewCss: `
font-family: 'Public Sans', sans-serif;
h1 { font-family: 'Overpass', sans-serif; font-size: 36pt; font-weight: 700; color: #FFFFFF; background: #00695C; padding: 16px; border-top: 2px solid #FFF; border-bottom: 2px solid #FFF; margin-bottom: 24px; border-radius: 8px; }
h2 { font-size: 18pt; font-weight: 700; color: #004D40; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { background: #FFEB3B; color: #000; padding: 16px; margin: 24px 0; border: 4px solid #000; transform: skewX(-10deg); width: fit-content; }
`
},
{
id: 'furniture-manual',
name: 'Furniture Manual',
category: 'Instructional',
description: 'Flat-pack assembly guide. Clean, heavy line art vibes with friendly sans-serifs.',
vibe: 'Clean, Instructional, Neutral',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Varela+Round&family=Hind:wght@300;600&display=swap',
wordConfig: {
heading1: {
font: "Varela Round", size: 32, color: "000000", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Varela Round", size: 16, color: "000000", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: {
bottom: { color: "000000", space: 4, style: "single", size: 12 },
left: { color: "000000", space: 4, style: "single", size: 12 }
},
shading: { fill: "F5F5F5", color: "auto", style: "clear" }
},
body: {
font: "Hind", size: 11, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Hind', sans-serif;
h1 { font-family: 'Varela Round', sans-serif; font-size: 32pt; color: #000000; margin-bottom: 24px; }
h2 { font-family: 'Varela Round', sans-serif; font-size: 16pt; color: #000000; background: #F5F5F5; padding: 8px; border-left: 4px solid #000; border-bottom: 2px solid #000; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { border: 2px solid #000; padding: 20px; margin: 24px 0; border-radius: 50%; width: 50px; height: 50px; line-height: 50px; text-align: center; font-weight: 700; font-size: 24pt; display: inline-block; }
`
},
{
id: 'night-vision',
name: 'Night Vision',
category: 'Tactical',
description: 'Military optics style. Grainy bright greens on dark green background.',
vibe: 'Tactical, Green, Grainy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Wallpoet&family=Roboto+Mono:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Wallpoet", size: 32, color: "76FF03", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "1B5E20", color: "auto", style: "clear" }
},
heading2: {
font: "Roboto Mono", size: 16, color: "CCFF90", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "76FF03", space: 4, style: "dotted", size: 12 } }
},
body: {
font: "Roboto Mono", size: 10, color: "B2FF59", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
accentColor: "76FF03"
},
previewCss: `
font-family: 'Roboto Mono', monospace;
background: #000000;
h1 { font-family: 'Wallpoet', cursive; font-size: 32pt; color: #76FF03; background: #1B5E20; padding: 16px; text-align: center; margin-bottom: 24px; text-shadow: 0 0 10px #76FF03; }
h2 { font-size: 16pt; font-weight: 700; color: #CCFF90; margin-top: 32px; margin-bottom: 16px; border-bottom: 2px dotted #76FF03; }
p { font-size: 10pt; line-height: 1.6; color: #B2FF59; margin-bottom: 14px; }
blockquote { border-left: 4px solid #76FF03; padding-left: 16px; margin: 24px 0; color: #64DD17; }
`
},
{
id: 'fire-station',
name: 'Fire Station',
category: 'Service',
description: 'Engine company aesthetic. Fire engine red, brass gold, and bold gothic numbers.',
vibe: 'Brave, Red, Gold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Anton&family=Rokkitt:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Anton", size: 40, color: "B71C1C", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "FFD600", space: 6, style: "thick", size: 24 },
bottom: { color: "FFD600", space: 6, style: "thick", size: 24 }
}
},
heading2: {
font: "Rokkitt", size: 18, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Rokkitt", size: 12, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "B71C1C"
},
previewCss: `
font-family: 'Rokkitt', serif;
h1 { font-family: 'Anton', sans-serif; font-size: 40pt; color: #B71C1C; text-align: center; border-top: 6px solid #FFD600; border-bottom: 6px solid #FFD600; padding: 16px 0; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 18pt; font-weight: 700; color: #000000; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { background: #FFEBEE; border: 2px solid #B71C1C; padding: 16px; margin: 24px 0; }
`
},
{
id: 'taxi-cab',
name: 'Taxi Cab',
category: 'Urban',
description: 'Yellow cab aesthetic. Checkerboard patterns (simulated) and bold black on yellow.',
vibe: 'Urban, Yellow, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Archivo+Black&family=Roboto:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Archivo Black", size: 36, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" },
border: {
top: { color: "000000", space: 4, style: "dashed", size: 24 }, // Simulates checkers
bottom: { color: "000000", space: 4, style: "dashed", size: 24 }
}
},
heading2: {
font: "Roboto", size: 18, color: "000000", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FFFFFF", color: "auto", style: "clear" }
},
body: {
font: "Roboto", size: 12, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFEB3B"
},
previewCss: `
font-family: 'Roboto', sans-serif;
background: #212121;
padding: 20px;
h1 { font-family: 'Archivo Black', sans-serif; font-size: 36pt; color: #000000; background: #FFEB3B; padding: 16px; margin-bottom: 24px; text-align: center; border-top: 6px dashed #000; border-bottom: 6px dashed #000; }
h2 { font-size: 18pt; font-weight: 700; color: #000000; background: #FFFFFF; padding: 8px; margin-top: 32px; margin-bottom: 16px; text-align: center; display: inline-block; }
p { font-size: 12pt; line-height: 1.5; color: #FFFFFF; margin-bottom: 14px; }
blockquote { background: #FFEB3B; color: #000; padding: 16px; margin: 24px 0; font-weight: 700; }
`
},
{
id: 'subway-tile',
name: 'Subway Tile',
category: 'Urban',
description: 'Classic station ceramics. Clean white backgrounds, heavy black text, and tile-like framing.',
vibe: 'Clean, Ceramic, Urban',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Work+Sans:wght@300;600&family=Lexend:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Lexend", size: 36, color: "000000", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "000000", space: 12, style: "single", size: 4 },
bottom: { color: "000000", space: 12, style: "single", size: 4 },
left: { color: "000000", space: 12, style: "single", size: 4 },
right: { color: "000000", space: 12, style: "single", size: 4 }
}
},
heading2: {
font: "Work Sans", size: 16, color: "212121", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Work Sans", size: 11, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Work Sans', sans-serif;
h1 { font-family: 'Lexend', sans-serif; font-size: 36pt; font-weight: 700; color: #000000; border: 4px solid #000000; padding: 16px; margin-bottom: 24px; text-align: center; background: #FFFFFF; }
h2 { font-size: 16pt; font-weight: 700; color: #212121; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { border-left: 8px solid #000; padding-left: 16px; margin: 24px 0; background: #F5F5F5; }
`
},
{
id: 'german-autobahn',
name: 'German Autobahn',
category: 'Transport',
description: 'Precision engineering aesthetic. DIN-style fonts and strict blue/white signage.',
vibe: 'German, Precision, Transport',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Barlow:wght@400;700&family=Roboto+Condensed:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Barlow", size: 36, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "1565C0", color: "auto", style: "clear" } // Autobahn Blue
},
heading2: {
font: "Roboto Condensed", size: 18, color: "0D47A1", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Roboto Condensed", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "1565C0"
},
previewCss: `
font-family: 'Roboto Condensed', sans-serif;
h1 { font-family: 'Barlow', sans-serif; font-size: 36pt; font-weight: 700; color: #FFFFFF; background: #1565C0; padding: 12px 20px; margin-bottom: 24px; border-radius: 4px; }
h2 { font-size: 18pt; font-weight: 700; color: #0D47A1; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 6px solid #1565C0; padding-left: 16px; margin: 24px 0; background: #E3F2FD; }
`
}
];

View File

@@ -1,768 +0,0 @@
import { StyleOption } from '../types';
export const lifestyleStyles: StyleOption[] = [
{
id: 'food-recipe',
name: 'Food & Recipe',
category: 'Lifestyle',
description: 'Warm and appetizing design for cookbooks and food content. Inviting typography with rustic charm.',
vibe: 'Warm, Appetizing, Homestyle',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&family=Lato:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display", size: 32, color: "6D4C41", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Playfair Display", size: 16, color: "8D6E63", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Lato", size: 10, color: "4E342E", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D84315"
},
previewCss: `
font-family: 'Lato', sans-serif;
h1 { font-family: 'Playfair Display', serif; font-size: 32pt; font-weight: 600; color: #6D4C41; text-align: center; margin-bottom: 28px; }
h2 { font-family: 'Playfair Display', serif; font-size: 16pt; color: #8D6E63; margin-top: 32px; margin-bottom: 16px; font-style: italic; }
p { font-size: 10pt; line-height: 1.6; color: #4E342E; margin-bottom: 14px; }
blockquote { background: #FFF3E0; padding: 16px; border-left: 4px solid #D84315; margin: 20px 0; }
`
},
{
id: 'wedding-elegant',
name: 'Wedding Elegant',
category: 'Events',
description: 'Romantic and elegant design for wedding stationery and formal events. Delicate typography with sophisticated flourishes.',
vibe: 'Romantic, Elegant, Celebratory',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Tangerine:wght@400;700&family=Cormorant+Garamond:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Tangerine", size: 48, color: "5D4037", bold: true, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Cormorant Garamond", size: 14, color: "795548", bold: false, align: 'center',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Cormorant Garamond", size: 11, color: "5D4037", align: 'center',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "D4AF37"
},
previewCss: `
font-family: 'Cormorant Garamond', serif;
h1 { font-family: 'Tangerine', cursive; font-size: 48pt; font-weight: 700; color: #5D4037; text-align: center; margin-bottom: 32px; }
h2 { font-size: 14pt; font-weight: 500; color: #795548; text-align: center; margin-top: 36px; margin-bottom: 18px; letter-spacing: 3px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.7; color: #5D4037; margin-bottom: 16px; text-align: center; font-weight: 300; }
blockquote { border-top: 1px solid #D4AF37; border-bottom: 1px solid #D4AF37; padding: 24px 40px; margin: 32px 0; text-align: center; font-style: italic; }
`
},
{
id: 'sports-dynamic',
name: 'Sports Dynamic',
category: 'Sports',
description: 'Energetic and bold design for sports and athletic content. Dynamic typography with high-impact colors.',
vibe: 'Energetic, Bold, Athletic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Rubik:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Bebas Neue", size: 36, color: "D32F2F", bold: false, align: 'left',
spacing: { before: 360, after: 200, line: 220 }
},
heading2: {
font: "Rubik", size: 14, color: "1A1A1A", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 }
},
body: {
font: "Rubik", size: 10, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Rubik', sans-serif;
h1 { font-family: 'Bebas Neue', sans-serif; font-size: 36pt; color: #D32F2F; margin-bottom: 24px; letter-spacing: 2px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #1A1A1A; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { background: #FFEBEE; padding: 16px; border-left: 6px solid #D32F2F; margin: 20px 0; font-weight: 500; }
`
},
{
id: 'childrens-playful',
name: 'Children\'s Playful',
category: 'Children',
description: 'Fun and engaging design for children\'s content. Rounded typography with bright, cheerful colors.',
vibe: 'Playful, Friendly, Colorful',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Baloo+2:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Baloo 2", size: 32, color: "6A1B9A", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Baloo 2", size: 18, color: "00897B", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Baloo 2", size: 11, color: "37474F", align: 'left',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "FF6B6B"
},
previewCss: `
font-family: 'Baloo 2', cursive;
h1 { font-size: 32pt; font-weight: 700; color: #6A1B9A; text-align: center; margin-bottom: 28px; }
h2 { font-size: 18pt; font-weight: 700; color: #00897B; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.7; color: #37474F; margin-bottom: 14px; }
blockquote { background: #FFF3E0; padding: 16px; border-radius: 16px; border-left: 6px solid #FF6F00; margin: 20px 0; }
`
},
{
id: 'travel-adventure',
name: 'Travel Adventure',
category: 'Travel',
description: 'Adventurous design for travel and exploration content. Bold typography with earthy, wanderlust-inspiring colors.',
vibe: 'Adventurous, Inspiring, Explorer',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Amatic+SC:wght@400;700&family=Josefin+Sans:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Amatic SC", size: 48, color: "3E2723", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 220 }
},
heading2: {
font: "Josefin Sans", size: 14, color: "5D4037", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Josefin Sans", size: 10, color: "4E342E", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF7043"
},
previewCss: `
font-family: 'Josefin Sans', sans-serif;
h1 { font-family: 'Amatic SC', cursive; font-size: 48pt; font-weight: 700; color: #3E2723; text-align: center; margin-bottom: 28px; letter-spacing: 4px; }
h2 { font-size: 14pt; font-weight: 600; color: #5D4037; margin-top: 30px; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 1.6; color: #4E342E; margin-bottom: 14px; }
blockquote { background: #FBE9E7; padding: 16px; border-left: 4px solid #FF7043; margin: 20px 0; }
`
},
{
id: 'hotel-hospitality',
name: 'Hotel Hospitality',
category: 'Hospitality',
description: 'Welcoming design for hotels and hospitality industry. Elegant yet approachable with warm sophistication.',
vibe: 'Welcoming, Elegant, Warm',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Tenor+Sans&family=Nunito+Sans:wght@300;400;600&display=swap',
wordConfig: {
heading1: {
font: "Tenor Sans", size: 28, color: "2C3E50", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Tenor Sans", size: 14, color: "8B7355", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Nunito Sans", size: 10, color: "4A4A4A", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "8B7355"
},
previewCss: `
font-family: 'Nunito Sans', sans-serif;
h1 { font-family: 'Tenor Sans', sans-serif; font-size: 28pt; color: #2C3E50; text-align: center; margin-bottom: 28px; letter-spacing: 2px; }
h2 { font-family: 'Tenor Sans', sans-serif; font-size: 14pt; color: #8B7355; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #4A4A4A; margin-bottom: 14px; font-weight: 300; }
blockquote { background: #F9F6F1; padding: 20px; border-left: 4px solid #8B7355; margin: 24px 0; }
`
},
{
id: 'spa-wellness',
name: 'Spa Wellness',
category: 'Wellness',
description: 'Serene design for spa and wellness industries. Calm typography with soothing, natural colors.',
vibe: 'Serene, Calming, Natural',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Philosopher:wght@400;700&family=Questrial&display=swap',
wordConfig: {
heading1: {
font: "Philosopher", size: 28, color: "4A6741", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Philosopher", size: 14, color: "6B8E63", bold: false, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Questrial", size: 10, color: "5D6D5A", align: 'left',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "6B8E63"
},
previewCss: `
font-family: 'Questrial', sans-serif;
h1 { font-family: 'Philosopher', serif; font-size: 28pt; color: #4A6741; text-align: center; margin-bottom: 32px; }
h2 { font-family: 'Philosopher', serif; font-size: 14pt; color: #6B8E63; margin-top: 36px; margin-bottom: 18px; }
p { font-size: 10pt; line-height: 1.7; color: #5D6D5A; margin-bottom: 16px; }
blockquote { background: #F1F8E9; padding: 20px; border-radius: 4px; margin: 28px 0; text-align: center; }
`
},
{
id: 'parisian-chic',
name: 'Parisian Chic',
category: 'Fashion',
description: 'Sophisticated French aesthetics. Elegant high-contrast serifs with plenty of white space.',
vibe: 'Sophisticated, Chic, Minimal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bodoni+Moda:ital,opsz,wght@0,6..96,400;0,6..96,700;1,6..96,400&family=Jost:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Bodoni Moda", size: 36, color: "000000", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Bodoni Moda", size: 16, color: "333333", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
italic: true
},
body: {
font: "Jost", size: 10, color: "1A1A1A", align: 'center',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Jost', sans-serif;
h1 { font-family: 'Bodoni Moda', serif; font-size: 36pt; font-weight: 700; color: #000000; text-align: center; margin-bottom: 32px; }
h2 { font-family: 'Bodoni Moda', serif; font-size: 16pt; font-weight: 700; color: #333333; text-align: center; margin-top: 36px; margin-bottom: 18px; font-style: italic; }
p { font-size: 10pt; line-height: 1.8; color: #1A1A1A; margin-bottom: 16px; text-align: center; font-weight: 300; }
blockquote { border-top: 1px solid #000000; border-bottom: 1px solid #000000; padding: 24px; margin: 32px 40px; text-align: center; font-family: 'Bodoni Moda', serif; font-style: italic; }
`
},
{
id: 'fine-dining-menu',
name: 'Fine Dining Menu',
category: 'Hospitality',
description: 'High-end restaurant aesthetic. Elegant scripts, centered layouts, and luxurious gold tones.',
vibe: 'Elegant, Expensive, Centered',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Great+Vibes&family=Cormorant+Garamond:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Great Vibes", size: 42, color: "C5A059", bold: false, align: 'center',
spacing: { before: 500, after: 300, line: 240 }
},
heading2: {
font: "Cormorant Garamond", size: 14, color: "212121", bold: true, align: 'center',
spacing: { before: 360, after: 180, line: 240 },
allCaps: true
},
body: {
font: "Cormorant Garamond", size: 12, color: "424242", align: 'center',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "C5A059"
},
previewCss: `
font-family: 'Cormorant Garamond', serif;
background: #FFFAF0;
h1 { font-family: 'Great Vibes', cursive; font-size: 42pt; color: #C5A059; text-align: center; margin-bottom: 30px; }
h2 { font-size: 14pt; font-weight: 600; color: #212121; text-align: center; margin-top: 36px; margin-bottom: 18px; text-transform: uppercase; letter-spacing: 3px; border-bottom: 1px solid #C5A059; display: inline-block; padding-bottom: 5px; }
p { font-size: 12pt; line-height: 1.8; color: #424242; margin-bottom: 16px; text-align: center; font-style: italic; }
blockquote { border: 1px solid #C5A059; padding: 24px; margin: 30px 60px; text-align: center; }
`
},
{
id: 'cottagecore-journal',
name: 'Cottagecore Journal',
category: 'Aesthetic',
description: 'Whimsical countryside diary. Handwritten fonts with soft sage greens and earthy browns.',
vibe: 'Whimsical, Nature, Handmade',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Amatic+SC:wght@400;700&family=Patrick+Hand&display=swap',
wordConfig: {
heading1: {
font: "Amatic SC", size: 40, color: "33691E", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Patrick Hand", size: 18, color: "5D4037", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Patrick Hand", size: 14, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "33691E"
},
previewCss: `
font-family: 'Patrick Hand', cursive;
background: #F1F8E9;
h1 { font-family: 'Amatic SC', cursive; font-size: 40pt; font-weight: 700; color: #33691E; text-align: center; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #5D4037; text-align: center; margin-top: 30px; margin-bottom: 14px; border-bottom: 1px dashed #8D6E63; display: inline-block; }
p { font-size: 14pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { background: #FFF; border: 1px dotted #33691E; padding: 20px; border-radius: 10px; margin: 24px 0; }
`
},
{
id: 'hipster-coffee',
name: 'Hipster Coffee',
category: 'Modern',
description: 'Artisan roaster branding. Clean slab serifs on kraft paper textures.',
vibe: 'Artisan, Slab, Kraft',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Zilla+Slab:wght@400;700&family=Lato:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Zilla Slab", size: 36, color: "3E2723", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Lato", size: 14, color: "000000", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true,
tracking: 100
},
body: {
font: "Lato", size: 10, color: "4E342E", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "3E2723"
},
previewCss: `
font-family: 'Lato', sans-serif;
background: #E0C097;
h1 { font-family: 'Zilla Slab', serif; font-size: 36pt; font-weight: 700; color: #3E2723; text-align: center; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; text-align: center; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 1.6; color: #4E342E; margin-bottom: 14px; }
blockquote { border-top: 1px solid #3E2723; border-bottom: 1px solid #3E2723; padding: 20px; margin: 24px 0; text-align: center; font-style: italic; }
`
},
{
id: 'kindergarten-art',
name: 'Kindergarten Art',
category: 'Playful',
description: 'Child-like crayon aesthetic. Primary colors, rounded fonts, and large friendly text.',
vibe: 'Childish, Fun, Primary',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Chewy&family=Balsamiq+Sans:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Chewy", size: 40, color: "2962FF", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Balsamiq Sans", size: 18, color: "D50000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Balsamiq Sans", size: 14, color: "111111", align: 'left',
spacing: { before: 0, after: 160, line: 320 }
},
accentColor: "FFD600"
},
previewCss: `
font-family: 'Balsamiq Sans', cursive;
h1 { font-family: 'Chewy', cursive; font-size: 40pt; color: #2962FF; text-align: center; margin-bottom: 24px; transform: rotate(-2deg); }
h2 { font-size: 18pt; font-weight: 700; color: #D50000; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.6; color: #111111; margin-bottom: 14px; }
blockquote { border: 4px dashed #FFD600; padding: 20px; margin: 24px 0; border-radius: 20px; background: #FFFDE7; }
`
},
{
id: 'surf-shop',
name: 'Surf Shop',
category: 'Lifestyle',
description: 'California cool. Relaxed hand-drawn fonts with sea foam blues and sunny yellows.',
vibe: 'Relaxed, Beach, Sunny',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Kalam:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Permanent Marker", size: 36, color: "0097A7", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Kalam", size: 18, color: "FBC02D", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Kalam", size: 12, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0097A7"
},
previewCss: `
font-family: 'Kalam', cursive;
h1 { font-family: 'Permanent Marker', cursive; font-size: 36pt; color: #0097A7; text-align: center; margin-bottom: 24px; transform: rotate(-1deg); }
h2 { font-size: 18pt; font-weight: 700; color: #FBC02D; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { background: #E0F7FA; border-radius: 16px; padding: 20px; margin: 24px 0; border: 2px solid #0097A7; }
`
},
{
id: 'winter-holiday',
name: 'Winter Holiday',
category: 'Seasonal',
description: 'Festive greeting card style. Elegant script, deep reds and forest greens.',
vibe: 'Festive, Cozy, Traditional',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Mountains+of+Christmas:wght@400;700&family=Merriweather:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Mountains of Christmas", size: 36, color: "B71C1C", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Merriweather", size: 16, color: "1B5E20", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Merriweather", size: 11, color: "212121", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "B71C1C"
},
previewCss: `
font-family: 'Merriweather', serif;
background: #FFEBEE;
h1 { font-family: 'Mountains of Christmas', cursive; font-size: 36pt; font-weight: 700; color: #B71C1C; text-align: center; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #1B5E20; text-align: center; margin-top: 32px; margin-bottom: 16px; border-bottom: 2px dotted #1B5E20; display: inline-block; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px solid #B71C1C; padding: 16px; margin: 24px 0; border-radius: 8px; background: #FFF; }
`
},
{
id: 'recipe-card',
name: 'Recipe Card',
category: 'Home',
description: 'Grandmas index card. Typewriter fonts with red header lines.',
vibe: 'Homey, Traditional, Cookery',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Courier+Prime&family=Homemade+Apple&display=swap',
wordConfig: {
heading1: {
font: "Courier Prime", size: 24, color: "000000", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 240 },
border: { bottom: { color: "F44336", space: 4, style: "single", size: 12 } }
},
heading2: {
font: "Courier Prime", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 },
allCaps: true
},
body: {
font: "Courier Prime", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "F44336"
},
previewCss: `
font-family: 'Courier Prime', monospace;
background: #FFF;
border: 1px solid #E0E0E0;
padding: 20px;
h1 { font-size: 24pt; font-weight: 700; color: #000000; border-bottom: 2px solid #F44336; padding-bottom: 8px; margin-bottom: 20px; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; margin-top: 24px; margin-bottom: 12px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.5; color: #212121; margin-bottom: 12px; }
blockquote { font-family: 'Homemade Apple', cursive; color: #D32F2F; font-size: 14pt; margin: 20px 0; }
`
},
{
id: 'national-park-signage',
name: 'National Park Signage',
category: 'Nature',
description: 'Routed wood sign aesthetic. Heavy rounded serifs with earthy brown and cream colors.',
vibe: 'Outdoors, Rustic, Official',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Crete+Round:ital@0;1&family=Lato:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Crete Round", size: 32, color: "FFFFFF", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "3E2723", color: "auto", style: "clear" } // Dark brown background
},
heading2: {
font: "Lato", size: 16, color: "3E2723", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Lato", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "3E2723"
},
previewCss: `
font-family: 'Lato', sans-serif;
h1 { font-family: 'Crete Round', serif; font-size: 32pt; color: #FFFFFF; background: #3E2723; padding: 16px; margin-bottom: 24px; border-radius: 8px; border: 2px solid #5D4037; }
h2 { font-size: 16pt; font-weight: 700; color: #3E2723; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { background: #EFEBE9; border-left: 6px solid #3E2723; padding: 16px; margin: 24px 0; }
`
},
{
id: 'supermarket-receipt',
name: 'Supermarket Receipt',
category: 'Everyday',
description: 'Thermal paper aesthetic. Monospaced, slightly faded grey text with narrow alignment.',
vibe: 'Disposable, Monospace, Grey',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Fira+Mono&family=Inconsolata:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Fira Mono", size: 24, color: "000000", bold: true, align: 'center',
spacing: { before: 300, after: 200, line: 240 },
allCaps: true
},
heading2: {
font: "Inconsolata", size: 14, color: "000000", bold: false, align: 'center',
spacing: { before: 240, after: 120, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "dashed", size: 12 } }
},
body: {
font: "Inconsolata", size: 11, color: "424242", align: 'left', // Dark grey for thermal look
spacing: { before: 0, after: 120, line: 240 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Inconsolata', monospace;
background: #FAFAFA;
padding: 20px;
width: 400px;
margin: 0 auto;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
h1 { font-family: 'Fira Mono', monospace; font-size: 24pt; font-weight: 700; color: #000000; text-align: center; margin-bottom: 20px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 400; color: #000000; text-align: center; margin-top: 24px; margin-bottom: 12px; border-bottom: 1px dashed #000; padding-bottom: 5px; }
p { font-size: 11pt; line-height: 1.4; color: #424242; margin-bottom: 10px; }
blockquote { text-align: center; margin: 20px 0; font-weight: 700; }
`
},
{
id: 'varsity-team',
name: 'Varsity Team',
category: 'Sport',
description: 'College sports jersey style. Block lettering with athletic gold and navy.',
vibe: 'Athletic, College, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Graduate&family=Saira:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Graduate", size: 36, color: "FDD835", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "1A237E", color: "auto", style: "clear" },
border: {
top: { color: "FDD835", space: 8, style: "single", size: 24 },
bottom: { color: "FDD835", space: 8, style: "single", size: 24 }
}
},
heading2: {
font: "Saira", size: 20, color: "1A237E", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Saira", size: 12, color: "212121", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FDD835"
},
previewCss: `
font-family: 'Saira', sans-serif;
h1 { font-family: 'Graduate', serif; font-size: 36pt; color: #FDD835; background: #1A237E; border-top: 4px solid #FDD835; border-bottom: 4px solid #FDD835; padding: 20px; text-align: center; margin-bottom: 24px; }
h2 { font-size: 20pt; font-weight: 700; color: #1A237E; text-align: center; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 12pt; line-height: 1.5; color: #212121; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px dashed #1A237E; padding: 16px; margin: 24px 0; background: #E8EAF6; }
`
},
{
id: 'sticky-note',
name: 'Sticky Note',
category: 'Casual',
description: 'Handwritten reminder aesthetic. Marker fonts on a canary yellow background.',
vibe: 'Casual, Handwritten, Yellow',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Kalam:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Permanent Marker", size: 32, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Kalam", size: 18, color: "D50000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Kalam", size: 14, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "FFF9C4", color: "auto", style: "clear" } // Yellow paper
},
accentColor: "D50000"
},
previewCss: `
font-family: 'Kalam', cursive;
background: #FFF9C4;
padding: 30px;
box-shadow: 2px 2px 5px rgba(0,0,0,0.1);
h1 { font-family: 'Permanent Marker', cursive; font-size: 32pt; color: #000000; text-align: center; margin-bottom: 24px; transform: rotate(-1deg); }
h2 { font-size: 18pt; font-weight: 700; color: #D50000; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 4px solid #D50000; padding-left: 12px; margin: 20px 0; }
`
},
{
id: 'brick-toy',
name: 'Brick Toy',
category: 'Playful',
description: 'Plastic building block aesthetic. Primary red, blue, and yellow with chunky text.',
vibe: 'Playful, Primary, Chunky',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Titan+One&family=Nunito:wght@400;800&display=swap',
wordConfig: {
heading1: {
font: "Titan One", size: 36, color: "D32F2F", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" } // Yellow background
},
heading2: {
font: "Nunito", size: 18, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "1976D2", color: "auto", style: "clear" } // Blue background
},
body: {
font: "Nunito", size: 12, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Nunito', sans-serif;
h1 { font-family: 'Titan One', cursive; font-size: 36pt; color: #D32F2F; background: #FFEB3B; padding: 16px; border-radius: 8px; text-align: center; margin-bottom: 24px; border: 4px solid #D32F2F; }
h2 { font-size: 18pt; font-weight: 800; color: #FFFFFF; background: #1976D2; padding: 8px 16px; border-radius: 4px; display: inline-block; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 4px dotted #D32F2F; padding: 16px; margin: 24px 0; background: #E3F2FD; }
`
},
{
id: 'jungle-explorer',
name: 'Jungle Explorer',
category: 'Adventure',
description: 'Safari expedition style. Khaki, olive drab, and canvas textures.',
vibe: 'Adventure, Khaki, Nature',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Stardos+Stencil&family=Domine:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Stardos Stencil", size: 36, color: "33691E", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "827717", space: 6, style: "thick", size: 18 } }
},
heading2: {
font: "Domine", size: 16, color: "558B2F", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Domine", size: 11, color: "1B1B1B", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "827717"
},
previewCss: `
font-family: 'Domine', serif;
background: #F0F4C3; /* Khaki tint */
h1 { font-family: 'Stardos Stencil', cursive; font-size: 36pt; font-weight: 700; color: #33691E; border-bottom: 4px solid #827717; padding-bottom: 12px; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #558B2F; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #1B1B1B; margin-bottom: 14px; }
blockquote { background: #DCEDC8; padding: 20px; border-left: 6px solid #33691E; margin: 24px 0; }
`
},
{
id: 'volcanic-ash',
name: 'Volcanic Ash',
category: 'Nature',
description: 'Geological extreme. Obsidian greys with magma orange cracks.',
vibe: 'Dark, Hot, Geological',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Creepster&family=Merriweather+Sans:wght@400;800&display=swap',
wordConfig: {
heading1: {
font: "Merriweather Sans", size: 36, color: "FF3D00", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "212121", color: "auto", style: "clear" } // Obsidian
},
heading2: {
font: "Merriweather Sans", size: 16, color: "424242", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "FF3D00", space: 4, style: "single", size: 6 } }
},
body: {
font: "Merriweather Sans", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF3D00"
},
previewCss: `
font-family: 'Merriweather Sans', sans-serif;
background: #FAFAFA;
h1 { font-size: 36pt; font-weight: 800; color: #FF3D00; background: #212121; padding: 16px; margin-bottom: 24px; border-bottom: 4px solid #DD2C00; }
h2 { font-size: 16pt; font-weight: 800; color: #424242; margin-top: 32px; margin-bottom: 16px; border-bottom: 2px solid #FF3D00; display: inline-block; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 8px solid #212121; padding-left: 16px; margin: 24px 0; background: #FFCCBC; }
`
},
{
id: 'desert-sands',
name: 'Desert Sands',
category: 'Nature',
description: 'Dune aesthetic. Warm beige backgrounds with wind-swept, eroded typefaces.',
vibe: 'Warm, Sandy, Soft',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cinzel&family=Fauna+One&display=swap',
wordConfig: {
heading1: {
font: "Cinzel", size: 36, color: "5D4037", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Fauna One", size: 16, color: "8D6E63", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Fauna One", size: 11, color: "4E342E", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "8D6E63"
},
previewCss: `
font-family: 'Fauna One', serif;
background: #F3E5AB; /* Sand color */
h1 { font-family: 'Cinzel', serif; font-size: 36pt; color: #5D4037; text-align: center; margin-bottom: 24px; letter-spacing: 2px; }
h2 { font-size: 16pt; font-weight: 700; color: #8D6E63; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #4E342E; margin-bottom: 14px; text-align: center; }
blockquote { border-top: 1px solid #5D4037; border-bottom: 1px solid #5D4037; padding: 20px; margin: 24px 0; }
`
},
{
id: 'bistro-chalkboard',
name: 'Bistro Chalkboard',
category: 'Food',
description: 'French cafe menu. White chalk text on slate black.',
vibe: 'French, Food, Chalk',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Homemade+Apple&family=Caveat:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Homemade Apple", size: 32, color: "FFFFFF", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "212121", color: "auto", style: "clear" }
},
heading2: {
font: "Caveat", size: 20, color: "E0E0E0", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Caveat", size: 14, color: "BDBDBD", align: 'center',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "263238", color: "auto", style: "clear" }
},
accentColor: "FFFFFF"
},
previewCss: `
font-family: 'Caveat', cursive;
background: #212121;
color: #FFFFFF;
h1 { font-family: 'Homemade Apple', cursive; font-size: 32pt; color: #FFFFFF; text-align: center; margin-bottom: 24px; border-bottom: 1px solid #757575; padding-bottom: 12px; }
h2 { font-size: 20pt; font-weight: 700; color: #E0E0E0; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.5; color: #BDBDBD; margin-bottom: 14px; text-align: center; }
blockquote { border: 1px dashed #FFFFFF; padding: 16px; margin: 24px 0; font-style: italic; text-align: center; }
`
}
];

View File

@@ -1,354 +0,0 @@
import { StyleOption } from '../types';
export const minimalistStyles: StyleOption[] = [
{
id: 'swiss-grid',
name: 'Swiss International',
category: 'Minimalist',
description: 'Objective, bold, and asymmetric. Heavy use of black and white, strong grid alignment.',
vibe: 'Architecture, Design, Modernism',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700;900&display=swap',
wordConfig: {
heading1: {
font: "Inter", size: 42, color: "000000", bold: true, tracking: -40, align: 'left',
spacing: { before: 800, after: 400, line: 240 },
border: { top: { color: "000000", space: 24, style: "single", size: 36 } }
},
heading2: {
font: "Inter", size: 16, color: "000000", bold: true, allCaps: true, tracking: 40, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
body: {
font: "Inter", size: 10, color: "111111", align: 'left',
spacing: { before: 0, after: 200, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Inter', sans-serif;
h1 { font-size: 42pt; font-weight: 900; letter-spacing: -2px; border-top: 6px solid black; padding-top: 24px; margin-bottom: 24px; line-height: 1; }
h2 { font-size: 16pt; font-weight: 700; text-transform: uppercase; letter-spacing: 2px; margin-top: 40px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.5; margin-bottom: 16px; max-width: 65ch; }
blockquote { border-left: 4px solid black; padding-left: 20px; font-weight: 700; font-style: normal; margin-left: 0; }
`
},
{
id: 'minimalist-white',
name: 'Minimalist White',
category: 'Minimal',
description: 'Ultra-clean design with maximum white space. Less is more philosophy with restrained typography that lets content breathe.',
vibe: 'Clean, Modern, Sophisticated',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Work+Sans:wght@300;400;600&display=swap',
wordConfig: {
heading1: {
font: "Work Sans", size: 32, color: "111111", bold: false, align: 'left',
spacing: { before: 600, after: 300, line: 240 }
},
heading2: {
font: "Work Sans", size: 14, color: "444444", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
body: {
font: "Work Sans", size: 10, color: "555555", align: 'left',
spacing: { before: 0, after: 200, line: 320 }
},
accentColor: "111111"
},
previewCss: `
font-family: 'Work Sans', sans-serif;
h1 { font-size: 32pt; font-weight: 300; color: #111111; margin-bottom: 32px; letter-spacing: -1px; }
h2 { font-size: 14pt; font-weight: 600; color: #444444; margin-top: 40px; margin-bottom: 20px; }
p { font-size: 10pt; line-height: 1.8; color: #555555; margin-bottom: 18px; }
blockquote { border-left: 1px solid #DDDDDD; padding-left: 24px; margin: 32px 0; color: #666666; }
`
},
{
id: 'scandinavian-clean',
name: 'Scandinavian Clean',
category: 'Minimal',
description: 'Inspired by Nordic design principles. Clean lines, functional typography, and subtle warmth with muted colors.',
vibe: 'Nordic, Clean, Warm Minimal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap',
wordConfig: {
heading1: {
font: "Poppins", size: 28, color: "2C2C2C", bold: false, align: 'left',
spacing: { before: 480, after: 240, line: 240 }
},
heading2: {
font: "Poppins", size: 14, color: "5D5D5D", bold: true, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Poppins", size: 10, color: "4A4A4A", align: 'left',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "E07A5F"
},
previewCss: `
font-family: 'Poppins', sans-serif;
h1 { font-size: 28pt; font-weight: 300; color: #2C2C2C; margin-bottom: 28px; }
h2 { font-size: 14pt; font-weight: 600; color: #5D5D5D; margin-top: 36px; margin-bottom: 18px; }
p { font-size: 10pt; line-height: 1.7; color: #4A4A4A; margin-bottom: 16px; font-weight: 300; }
blockquote { border-left: 3px solid #E07A5F; padding-left: 20px; margin: 28px 0; color: #666666; }
`
},
{
id: 'minimalist-black',
name: 'Minimalist Black',
category: 'Minimal',
description: 'Ultra-minimal dark design with stark contrasts. High-impact typography on dark backgrounds.',
vibe: 'Dark, Minimal, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Work+Sans:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Bebas Neue", size: 48, color: "FFFFFF", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 200 },
shading: { fill: "0A0A0A", color: "auto", style: "clear" }
},
heading2: {
font: "Work Sans", size: 12, color: "FFFFFF", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "1A1A1A", color: "auto", style: "clear" }
},
body: {
font: "Work Sans", size: 10, color: "E0E0E0", align: 'left',
spacing: { before: 0, after: 180, line: 300 },
shading: { fill: "121212", color: "auto", style: "clear" }
},
accentColor: "FFFFFF"
},
previewCss: `
font-family: 'Work Sans', sans-serif;
background: #0A0A0A;
h1 { font-family: 'Bebas Neue', sans-serif; font-size: 48pt; color: #FFFFFF; background: #0A0A0A; padding: 16px 20px; margin-bottom: 24px; letter-spacing: 3px; }
h2 { font-size: 12pt; color: #FFFFFF; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 4px; font-weight: 300; }
p { font-size: 10pt; line-height: 1.7; color: #E0E0E0; margin-bottom: 16px; font-weight: 300; }
blockquote { border-left: 1px solid #FFFFFF; padding-left: 24px; margin: 28px 0; }
`
},
{
id: 'japanese-zen',
name: 'Japanese Zen',
category: 'Minimal',
description: 'Inspired by Japanese minimalism and wabi-sabi aesthetics. Peaceful typography with intentional asymmetry.',
vibe: 'Zen, Peaceful, Minimal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Shippori+Mincho:wght@400;600;700&family=Zen+Kaku+Gothic+New:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Shippori Mincho", size: 28, color: "2D2D2D", bold: false, align: 'left',
spacing: { before: 560, after: 320, line: 240 }
},
heading2: {
font: "Zen Kaku Gothic New", size: 12, color: "5C5C5C", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
body: {
font: "Zen Kaku Gothic New", size: 10, color: "4A4A4A", align: 'left',
spacing: { before: 0, after: 200, line: 320 }
},
accentColor: "C41E3A"
},
previewCss: `
font-family: 'Zen Kaku Gothic New', sans-serif;
h1 { font-family: 'Shippori Mincho', serif; font-size: 28pt; color: #2D2D2D; margin-bottom: 36px; }
h2 { font-size: 12pt; color: #5C5C5C; margin-top: 40px; margin-bottom: 20px; font-weight: 300; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 1.8; color: #4A4A4A; margin-bottom: 18px; font-weight: 300; }
blockquote { border-left: 2px solid #C41E3A; padding-left: 24px; margin: 36px 0; }
`
},
{
id: 'zen-garden',
name: 'Zen Garden',
category: 'Minimal',
description: 'Inspired by Japanese rock gardens. Extreme minimalism with meditative spacing and natural balance.',
vibe: 'Meditative, Balanced, Serene',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Noto+Serif+JP:wght@400;600&family=Noto+Sans+JP:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Noto Serif JP", size: 26, color: "2C2C2C", bold: false, align: 'left',
spacing: { before: 600, after: 360, line: 240 }
},
heading2: {
font: "Noto Sans JP", size: 11, color: "5C5C5C", bold: false, align: 'left',
spacing: { before: 480, after: 240, line: 240 }
},
body: {
font: "Noto Sans JP", size: 10, color: "4A4A4A", align: 'left',
spacing: { before: 0, after: 240, line: 360 }
},
accentColor: "6B8E6B"
},
previewCss: `
font-family: 'Noto Sans JP', sans-serif;
h1 { font-family: 'Noto Serif JP', serif; font-size: 26pt; font-weight: 600; color: #2C2C2C; margin-bottom: 40px; }
h2 { font-size: 11pt; color: #5C5C5C; margin-top: 48px; margin-bottom: 24px; font-weight: 300; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 2; color: #4A4A4A; margin-bottom: 20px; font-weight: 300; }
blockquote { border-left: 1px solid #6B8E6B; padding-left: 32px; margin: 40px 0; }
`
},
{
id: 'de-stijl',
name: 'De Stijl',
category: 'Modern',
description: 'Inspired by the Dutch movement (Mondrian). Primary colors, thick black lines, and rigid grid geometry.',
vibe: 'Abstract, Geometric, Primary',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Geo:wght@400&family=Arimo:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Geo", size: 32, color: "FF0000", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: {
bottom: { color: "000000", space: 12, style: "single", size: 24 },
left: { color: "000000", space: 12, style: "single", size: 24 }
}
},
heading2: {
font: "Arimo", size: 14, color: "0000FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FFFF00", color: "auto", style: "clear" }
},
body: {
font: "Arimo", size: 10, color: "000000", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF0000"
},
previewCss: `
font-family: 'Arimo', sans-serif;
h1 { font-family: 'Geo', sans-serif; font-size: 32pt; color: #FF0000; border-bottom: 4px solid #000000; border-left: 4px solid #000000; padding-left: 16px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #0000FF; background: #FFFF00; display: inline-block; padding: 4px 12px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #000000; margin-bottom: 14px; }
blockquote { border: 4px solid #000000; padding: 16px; margin: 24px 0; box-shadow: 8px 8px 0px #0000FF; }
`
},
{
id: 'bauhaus-construction',
name: 'Bauhaus Construction',
category: 'Modernism',
description: 'Form follows function. Geometric simplicity using primary colors and heavy structural borders.',
vibe: 'Geometric, Functional, Primary',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Jura:wght@400;700&family=Tenor+Sans&display=swap',
wordConfig: {
heading1: {
font: "Jura", size: 32, color: "111111", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { left: { color: "D50000", space: 12, style: "single", size: 48 } }
},
heading2: {
font: "Tenor Sans", size: 16, color: "111111", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" }
},
body: {
font: "Tenor Sans", size: 11, color: "212121", align: 'both',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0000FF"
},
previewCss: `
font-family: 'Tenor Sans', sans-serif;
h1 { font-family: 'Jura', sans-serif; font-size: 32pt; font-weight: 700; color: #111111; border-left: 12px solid #D50000; padding-left: 16px; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #111111; background: #FFEB3B; display: inline-block; padding: 4px 12px; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; text-align: justify; }
blockquote { border-top: 4px solid #0000FF; border-bottom: 4px solid #0000FF; padding: 16px 0; margin: 24px 0; }
`
},
{
id: 'museum-gallery',
name: 'Museum Gallery',
category: 'Institutional',
description: 'Art gallery placard style. Extremely neutral sans-serifs, high contrast, small but clear text.',
vibe: 'Neutral, Institutional, Clean',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Karla:wght@400;700&family=Work+Sans:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Work Sans", size: 24, color: "000000", bold: true, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
heading2: {
font: "Karla", size: 12, color: "616161", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 },
allCaps: true
},
body: {
font: "Karla", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Karla', sans-serif;
h1 { font-family: 'Work Sans', sans-serif; font-size: 24pt; font-weight: 700; color: #000000; margin-bottom: 20px; }
h2 { font-size: 12pt; font-weight: 700; color: #616161; margin-top: 24px; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 1px; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 2px solid #000; padding-left: 16px; margin: 20px 0; color: #424242; }
`
},
{
id: 'wireframe-layout',
name: 'Wireframe Layout',
category: 'Minimal',
description: 'Digital blueprint style. Grey backgrounds, monospaced fonts, and placeholder-like blocks.',
vibe: 'Digital, Prototype, Structural',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Redacted+Script&family=Flow+Circular&family=Roboto+Mono&display=swap',
wordConfig: {
heading1: {
font: "Roboto Mono", size: 24, color: "0000FF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "E0E0E0", color: "auto", style: "clear" }
},
heading2: {
font: "Roboto Mono", size: 14, color: "555555", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "CCCCCC", space: 4, style: "single", size: 4 } }
},
body: {
font: "Roboto Mono", size: 10, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0000FF"
},
previewCss: `
font-family: 'Roboto Mono', monospace;
h1 { font-size: 24pt; font-weight: 700; color: #0000FF; background: #E0E0E0; padding: 12px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #555555; border-bottom: 1px solid #CCC; margin-top: 32px; margin-bottom: 16px; padding-bottom: 4px; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { border: 1px dashed #999; padding: 16px; margin: 24px 0; background: #F9F9F9; }
`
},
{
id: 'muted-pastel',
name: 'Muted Pastel',
category: 'Minimal',
description: 'Soft and calming. Very light background tints with gentle, rounded typography.',
vibe: 'Soft, Calming, Gentle',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Nunito:wght@400;700&family=Quicksand:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Nunito", size: 28, color: "7986CB", bold: true, align: 'center',
spacing: { before: 480, after: 240, line: 240 }
},
heading2: {
font: "Quicksand", size: 14, color: "9575CD", bold: true, align: 'center',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Quicksand", size: 11, color: "616161", align: 'center',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "9575CD"
},
previewCss: `
font-family: 'Quicksand', sans-serif;
background: #FDFBF7;
h1 { font-family: 'Nunito', sans-serif; font-size: 28pt; font-weight: 700; color: #7986CB; text-align: center; margin-bottom: 32px; }
h2 { font-size: 14pt; font-weight: 600; color: #9575CD; text-align: center; margin-top: 36px; margin-bottom: 18px; }
p { font-size: 11pt; line-height: 1.7; color: #616161; margin-bottom: 16px; text-align: center; }
blockquote { background: #F3E5F5; padding: 24px; border-radius: 20px; margin: 32px 40px; text-align: center; }
`
}
];

View File

@@ -1,957 +0,0 @@
import { StyleOption } from '../types';
export const techStyles: StyleOption[] = [
{
id: 'tech-startup',
name: 'Tech Startup',
category: 'Tech',
description: 'Modern, energetic design for technology companies and startups. Bold gradients, vibrant colors, and confident typography.',
vibe: 'Innovative, Dynamic, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Plus Jakarta Sans", size: 28, color: "6366F1", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Plus Jakarta Sans", size: 16, color: "1E1E1E", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Plus Jakarta Sans", size: 10, color: "4B5563", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "6366F1"
},
previewCss: `
font-family: 'Plus Jakarta Sans', sans-serif;
h1 { font-size: 28pt; font-weight: 700; color: #6366F1; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #1E1E1E; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #4B5563; margin-bottom: 14px; }
blockquote { background: linear-gradient(135deg, #EEF2FF 0%, #E0E7FF 100%); padding: 20px; border-radius: 8px; border-left: 4px solid #6366F1; margin: 24px 0; }
`
},
{
id: 'tech-documentation',
name: 'Tech Documentation',
category: 'Technical',
description: 'Clean and functional design for technical documentation. Optimized for code samples and clear instruction.',
vibe: 'Technical, Clear, Functional',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap',
wordConfig: {
heading1: {
font: "IBM Plex Sans", size: 24, color: "161616", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "IBM Plex Sans", size: 16, color: "161616", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "IBM Plex Sans", size: 10, color: "525252", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0F62FE"
},
previewCss: `
font-family: 'IBM Plex Sans', sans-serif;
h1 { font-size: 24pt; font-weight: 600; color: #161616; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 600; color: #161616; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #525252; margin-bottom: 14px; }
blockquote { font-family: 'IBM Plex Mono', monospace; background: #F4F4F4; padding: 16px; margin: 20px 0; font-size: 9pt; border-left: 4px solid #0F62FE; }
`
},
{
id: 'industrial-blueprint',
name: 'Industrial Blueprint',
category: 'Technical',
description: 'Engineering-inspired design reminiscent of technical blueprints. Precise typography with drafting aesthetics.',
vibe: 'Technical, Precise, Engineering',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Titillium+Web:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Titillium Web", size: 24, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "1A3A5C", color: "auto", style: "clear" }
},
heading2: {
font: "Titillium Web", size: 14, color: "1A3A5C", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Share Tech Mono", size: 9, color: "2C4A6B", align: 'left',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "1A3A5C"
},
previewCss: `
font-family: 'Share Tech Mono', monospace;
h1 { font-family: 'Titillium Web', sans-serif; font-size: 24pt; font-weight: 700; color: #FFFFFF; background: #1A3A5C; padding: 16px 20px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-family: 'Titillium Web', sans-serif; font-size: 14pt; font-weight: 700; color: #1A3A5C; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 9pt; line-height: 1.7; color: #2C4A6B; margin-bottom: 14px; }
blockquote { background: #E8F4FC; padding: 16px; border: 1px dashed #1A3A5C; margin: 20px 0; }
`
},
{
id: 'crypto-futurism',
name: 'Crypto Futurism',
category: 'Tech',
description: 'Cutting-edge design for blockchain and cryptocurrency content. Dark themes with electric accents.',
vibe: 'Futuristic, Digital, Cutting-edge',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Audiowide&family=Rajdhani:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Audiowide", size: 26, color: "00D4AA", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "0D1117", color: "auto", style: "clear" }
},
heading2: {
font: "Rajdhani", size: 16, color: "58A6FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Rajdhani", size: 10, color: "C9D1D9", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "161B22", color: "auto", style: "clear" }
},
accentColor: "00D4AA"
},
previewCss: `
font-family: 'Rajdhani', sans-serif;
background: #0D1117;
h1 { font-family: 'Audiowide', cursive; font-size: 26pt; color: #00D4AA; background: #0D1117; padding: 16px 20px; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 600; color: #58A6FF; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #C9D1D9; margin-bottom: 14px; }
blockquote { background: #21262D; padding: 16px; border-left: 4px solid #00D4AA; margin: 20px 0; }
`
},
{
id: 'aerospace-modern',
name: 'Aerospace Modern',
category: 'Tech',
description: 'Sleek design inspired by aerospace and space exploration. Clean lines with futuristic precision.',
vibe: 'Futuristic, Sleek, Precision',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Michroma&family=Saira:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Michroma", size: 22, color: "0B3D91", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Saira", size: 13, color: "FC3D21", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Saira", size: 10, color: "2E3A45", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FC3D21"
},
previewCss: `
font-family: 'Saira', sans-serif;
h1 { font-family: 'Michroma', sans-serif; font-size: 22pt; color: #0B3D91; margin-bottom: 24px; letter-spacing: 2px; }
h2 { font-size: 13pt; font-weight: 500; color: #FC3D21; margin-top: 30px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #2E3A45; margin-bottom: 14px; font-weight: 300; }
blockquote { background: #E8F0FE; padding: 16px; border-left: 4px solid #0B3D91; margin: 20px 0; }
`
},
{
id: 'data-analytics',
name: 'Data Analytics',
category: 'Tech',
description: 'Clean design for data science and analytics content. Precise typography optimized for charts and metrics.',
vibe: 'Analytical, Clean, Data-driven',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Overpass:wght@400;600;700&family=Overpass+Mono:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Overpass", size: 26, color: "0F172A", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Overpass", size: 14, color: "3730A3", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Overpass", size: 10, color: "475569", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "3730A3"
},
previewCss: `
font-family: 'Overpass', sans-serif;
h1 { font-size: 26pt; font-weight: 700; color: #0F172A; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 700; color: #3730A3; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.6; color: #475569; margin-bottom: 14px; }
blockquote { font-family: 'Overpass Mono', monospace; background: #F1F5F9; padding: 16px; border-left: 4px solid #3730A3; margin: 20px 0; font-size: 9pt; }
`
},
{
id: 'artificial-intelligence',
name: 'Artificial Intelligence',
category: 'Tech',
description: 'Cutting-edge design for AI and machine learning content. Neural network inspired with gradient accents.',
vibe: 'Cutting-edge, Neural, Intelligent',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Oxanium:wght@400;600;700&family=Lexend:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Oxanium", size: 28, color: "7C3AED", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Lexend", size: 14, color: "2563EB", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Lexend", size: 10, color: "334155", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "7C3AED"
},
previewCss: `
font-family: 'Lexend', sans-serif;
h1 { font-family: 'Oxanium', sans-serif; font-size: 28pt; font-weight: 700; color: #7C3AED; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 500; color: #2563EB; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #334155; margin-bottom: 14px; font-weight: 300; }
blockquote { background: linear-gradient(135deg, #EDE9FE 0%, #DBEAFE 100%); padding: 16px; border-radius: 8px; margin: 20px 0; }
`
},
{
id: 'marine-biology',
name: 'Marine Biology',
category: 'Science',
description: 'Ocean-inspired design for marine science and aquatic content. Deep blues with flowing elegance.',
vibe: 'Oceanic, Scientific, Flowing',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600&family=Lora:wght@400;500&display=swap',
wordConfig: {
heading1: {
font: "Lora", size: 26, color: "0C4A6E", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Archivo", size: 13, color: "0369A1", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Archivo", size: 10, color: "334155", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0EA5E9"
},
previewCss: `
font-family: 'Archivo', sans-serif;
h1 { font-family: 'Lora', serif; font-size: 26pt; font-weight: 700; color: #0C4A6E; margin-bottom: 24px; }
h2 { font-size: 13pt; font-weight: 600; color: #0369A1; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.6; color: #334155; margin-bottom: 14px; }
blockquote { background: #E0F2FE; padding: 16px; border-left: 4px solid #0EA5E9; margin: 20px 0; }
`
},
{
id: 'startup-saas',
name: 'Startup SaaS',
category: 'Tech',
description: 'Modern SaaS product design language. Clean, conversion-focused with trustworthy tech aesthetics.',
vibe: 'Modern, Trustworthy, Product',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap',
wordConfig: {
heading1: {
font: "Inter", size: 32, color: "111827", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Inter", size: 18, color: "4F46E5", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Inter", size: 10, color: "6B7280", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "4F46E5"
},
previewCss: `
font-family: 'Inter', sans-serif;
h1 { font-size: 32pt; font-weight: 700; color: #111827; margin-bottom: 24px; letter-spacing: -0.5px; }
h2 { font-size: 18pt; font-weight: 600; color: #4F46E5; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #6B7280; margin-bottom: 14px; }
blockquote { background: #EEF2FF; padding: 16px; border-radius: 8px; border-left: 4px solid #4F46E5; margin: 20px 0; }
`
},
{
id: 'cybersecurity',
name: 'Cybersecurity',
category: 'Tech',
description: 'High-security design for infosec and cybersecurity. Matrix-inspired with threat-level colors.',
vibe: 'Secure, Technical, Alert',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Exo+2:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Exo 2", size: 26, color: "00E676", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "0D0D0D", color: "auto", style: "clear" }
},
heading2: {
font: "Exo 2", size: 14, color: "00E676", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Share Tech Mono", size: 9, color: "B0BEC5", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "1A1A1A", color: "auto", style: "clear" }
},
accentColor: "00E676"
},
previewCss: `
font-family: 'Share Tech Mono', monospace;
background: #0D0D0D;
h1 { font-family: 'Exo 2', sans-serif; font-size: 26pt; font-weight: 600; color: #00E676; background: #0D0D0D; padding: 16px 20px; margin-bottom: 24px; }
h2 { font-family: 'Exo 2', sans-serif; font-size: 14pt; font-weight: 600; color: #00E676; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 9pt; line-height: 1.6; color: #B0BEC5; margin-bottom: 14px; }
blockquote { background: #1A1A1A; padding: 16px; border-left: 4px solid #00E676; margin: 20px 0; }
`
},
{
id: 'astronomy-space',
name: 'Astronomy Space',
category: 'Science',
description: 'Cosmic design for astronomy and space science. Deep space colors with stellar typography.',
vibe: 'Cosmic, Scientific, Vast',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700&family=Space+Grotesk:wght@400;500&display=swap',
wordConfig: {
heading1: {
font: "Orbitron", size: 26, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "0A0A23", color: "auto", style: "clear" }
},
heading2: {
font: "Space Grotesk", size: 14, color: "7C4DFF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Space Grotesk", size: 10, color: "B0BEC5", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "121225", color: "auto", style: "clear" }
},
accentColor: "7C4DFF"
},
previewCss: `
font-family: 'Space Grotesk', sans-serif;
background: #0A0A23;
h1 { font-family: 'Orbitron', sans-serif; font-size: 26pt; font-weight: 700; color: #FFFFFF; background: #0A0A23; padding: 16px 20px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 500; color: #7C4DFF; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #B0BEC5; margin-bottom: 14px; }
blockquote { background: #1A1A3E; padding: 16px; border-left: 4px solid #7C4DFF; margin: 20px 0; }
`
},
{
id: 'blockchain-protocol',
name: 'Blockchain Protocol',
category: 'Tech',
description: 'Technical blockchain and Web3 design. Decentralized aesthetics with protocol documentation style.',
vibe: 'Technical, Decentralized, Protocol',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Inter:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Inter", size: 28, color: "6366F1", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Inter", size: 16, color: "1E1E1E", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Inter", size: 10, color: "4B5563", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "6366F1"
},
previewCss: `
font-family: 'Inter', sans-serif;
h1 { font-size: 28pt; font-weight: 600; color: #6366F1; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 600; color: #1E1E1E; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #4B5563; margin-bottom: 14px; }
blockquote { font-family: 'JetBrains Mono', monospace; background: #F3F4F6; padding: 16px; border-radius: 6px; margin: 20px 0; font-size: 9pt; border-left: 4px solid #6366F1; }
`
},
{
id: 'smart-home-tech',
name: 'Smart Home Tech',
category: 'Tech',
description: 'Modern IoT and smart home design. Clean, connected typography with ambient intelligence.',
vibe: 'Connected, Modern, Ambient',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Poppins", size: 28, color: "00BFA5", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Poppins", size: 15, color: "263238", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Poppins", size: 10, color: "546E7A", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "00BFA5"
},
previewCss: `
font-family: 'Poppins', sans-serif;
h1 { font-size: 28pt; font-weight: 600; color: #00BFA5; margin-bottom: 24px; }
h2 { font-size: 15pt; font-weight: 600; color: #263238; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #546E7A; margin-bottom: 14px; }
blockquote { background: #E0F2F1; padding: 16px; border-radius: 12px; border-left: 4px solid #00BFA5; margin: 20px 0; }
`
},
{
id: 'frutiger-aero',
name: 'Frutiger Aero',
category: 'Tech',
description: 'Mid-2000s tech aesthetic. Glossy textures, humanist sans-serifs, and optimistic skeuomorphic blues and greens.',
vibe: 'Glossy, Optimistic, 2000s',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Varela+Round&family=M+PLUS+Rounded+1c:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Varela Round", size: 28, color: "0288D1", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "M PLUS Rounded 1c", size: 15, color: "7CB342", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "M PLUS Rounded 1c", size: 10, color: "546E7A", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "03A9F4"
},
previewCss: `
font-family: 'M PLUS Rounded 1c', sans-serif;
h1 { font-family: 'Varela Round', sans-serif; font-size: 28pt; color: #0288D1; margin-bottom: 24px; background: linear-gradient(to right, #E1F5FE, #FFFFFF); padding: 10px; border-radius: 10px; }
h2 { font-size: 15pt; font-weight: 700; color: #7CB342; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #546E7A; margin-bottom: 14px; }
blockquote { background: linear-gradient(180deg, #FFFFFF 0%, #E0F7FA 100%); padding: 16px; border-radius: 15px; border: 1px solid #B3E5FC; margin: 20px 0; }
`
},
{
id: 'solarpunk',
name: 'Solarpunk',
category: 'Futuristic',
description: 'Nature-meets-technology future. Art Nouveau curves with high-tech sustainable green and gold.',
vibe: 'Sustainable, Hopeful, Organic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Caudex:wght@400;700&family=El+Messiri:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "El Messiri", size: 30, color: "2E7D32", bold: true, align: 'left',
spacing: { before: 440, after: 260, line: 240 }
},
heading2: {
font: "Caudex", size: 15, color: "AFB42B", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Caudex", size: 11, color: "33691E", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFD700"
},
previewCss: `
font-family: 'Caudex', serif;
h1 { font-family: 'El Messiri', sans-serif; font-size: 30pt; font-weight: 700; color: #2E7D32; margin-bottom: 30px; }
h2 { font-size: 15pt; font-weight: 700; color: #AFB42B; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #33691E; margin-bottom: 14px; }
blockquote { background: #F1F8E9; padding: 16px; border-left: 4px solid #FFD700; border-radius: 0 16px 16px 0; margin: 20px 0; }
`
},
{
id: 'biohazard-lab',
name: 'Biohazard Lab',
category: 'Sci-Fi',
description: 'Warning-label aesthetic. Monospace fonts with high-contrast yellow and black caution themes.',
vibe: 'Warning, Industrial, Hazardous',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Syne+Mono&family=Azeret+Mono:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Syne Mono", size: 32, color: "000000", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" }
},
heading2: {
font: "Azeret Mono", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { left: { color: "FFEB3B", space: 8, style: "single", size: 24 } }
},
body: {
font: "Azeret Mono", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFEB3B"
},
previewCss: `
font-family: 'Azeret Mono', monospace;
h1 { font-family: 'Syne Mono', monospace; font-size: 32pt; font-weight: 700; color: #000000; background: #FFEB3B; padding: 16px; text-align: center; margin-bottom: 28px; letter-spacing: -1px; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; border-left: 8px solid #FFEB3B; padding-left: 12px; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 2px dashed #000000; padding: 16px; margin: 20px 0; background: #FFF9C4; }
`
},
{
id: 'code-editor',
name: 'Code Editor',
category: 'Tech',
description: 'Dark mode IDE aesthetic. Monospaced fonts with syntax-highlighting color palette.',
vibe: 'Technical, Dark, Coding',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&family=Source+Code+Pro:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Inconsolata", size: 26, color: "61AFEF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "282C34", color: "auto", style: "clear" }
},
heading2: {
font: "Source Code Pro", size: 14, color: "98C379", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Source Code Pro", size: 10, color: "ABB2BF", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "21252B", color: "auto", style: "clear" }
},
accentColor: "E06C75"
},
previewCss: `
font-family: 'Source Code Pro', monospace;
background: #282C34;
h1 { font-family: 'Inconsolata', monospace; font-size: 26pt; font-weight: 700; color: #61AFEF; background: #282C34; padding: 16px 20px; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 600; color: #98C379; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.6; color: #ABB2BF; margin-bottom: 14px; }
blockquote { background: #2C313C; padding: 16px; border-left: 4px solid #E06C75; margin: 20px 0; color: #E06C75; }
`
},
{
id: 'neon-noir',
name: 'Neon Noir',
category: 'Futuristic',
description: 'High-tech noir aesthetic. Ultra-thin, futuristic typography with glowing neon accents.',
vibe: 'Futuristic, Thin, Neon',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Megrim&family=Gruppo&display=swap',
wordConfig: {
heading1: {
font: "Megrim", size: 36, color: "D500F9", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "121212", color: "auto", style: "clear" }
},
heading2: {
font: "Gruppo", size: 18, color: "00E5FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Gruppo", size: 12, color: "E0E0E0", align: 'left',
spacing: { before: 0, after: 160, line: 300 },
shading: { fill: "1C1C1C", color: "auto", style: "clear" }
},
accentColor: "D500F9"
},
previewCss: `
font-family: 'Gruppo', sans-serif;
background: #121212;
h1 { font-family: 'Megrim', cursive; font-size: 36pt; font-weight: 700; color: #D500F9; background: #121212; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #00E5FF; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.6; color: #E0E0E0; margin-bottom: 14px; }
blockquote { border: 1px solid #D500F9; padding: 16px; margin: 20px 0; background: #000; color: #D500F9; }
`
},
{
id: '8-bit-arcade',
name: '8-Bit Arcade',
category: 'Gaming',
description: 'Retro gaming high-score screen. Pixelated fonts on dark backgrounds.',
vibe: 'Retro, Gaming, Pixel',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap',
wordConfig: {
heading1: {
font: "Press Start 2P", size: 20, color: "76FF03", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 300 },
shading: { fill: "212121", color: "auto", style: "clear" }
},
heading2: {
font: "Press Start 2P", size: 12, color: "D500F9", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "VT323", size: 14, color: "FFFFFF", align: 'left',
spacing: { before: 0, after: 160, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
accentColor: "76FF03"
},
previewCss: `
font-family: 'VT323', monospace;
background: #000000;
h1 { font-family: 'Press Start 2P', cursive; font-size: 20pt; color: #76FF03; background: #212121; text-align: center; padding: 20px; margin-bottom: 28px; line-height: 1.5; }
h2 { font-family: 'Press Start 2P', cursive; font-size: 12pt; color: #D500F9; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.4; color: #FFFFFF; margin-bottom: 14px; }
blockquote { border: 4px dashed #FFFFFF; padding: 16px; margin: 20px 0; color: #FFFF00; }
`
},
{
id: 'bsod-blue',
name: 'BSOD Blue',
category: 'Tech',
description: 'The infamous crash screen. Pure white terminal fonts on a deep, error blue background.',
vibe: 'Crash, Error, Terminal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Lucident&family=Fira+Mono&display=swap',
wordConfig: {
heading1: {
font: "Lucident", size: 24, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "0000AA", color: "auto", style: "clear" }
},
heading2: {
font: "Fira Mono", size: 14, color: "FFFFFF", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "0000AA", color: "auto", style: "clear" }
},
body: {
font: "Fira Mono", size: 11, color: "FFFFFF", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "0000AA", color: "auto", style: "clear" }
},
accentColor: "FFFFFF"
},
previewCss: `
font-family: 'Fira Mono', monospace;
background: #0000AA;
color: #FFFFFF;
h1 { font-family: 'Lucident', monospace; font-size: 24pt; font-weight: 700; color: #FFFFFF; margin-bottom: 24px; }
h2 { font-size: 14pt; font-weight: 400; color: #FFFFFF; margin-top: 32px; margin-bottom: 16px; text-decoration: underline; }
p { font-size: 11pt; line-height: 1.5; color: #FFFFFF; margin-bottom: 14px; }
blockquote { border: 2px solid #FFFFFF; padding: 16px; margin: 20px 0; }
`
},
{
id: 'amber-monitor',
name: 'Amber Monitor',
category: 'Retro',
description: '1980s monochrome computing. Glowing amber text on a black void.',
vibe: 'Retro, Computing, Amber',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=VT323&display=swap',
wordConfig: {
heading1: {
font: "VT323", size: 36, color: "FFB300", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "212121", color: "auto", style: "clear" }
},
heading2: {
font: "VT323", size: 20, color: "FFCA28", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "FFB300", space: 4, style: "single", size: 12 } }
},
body: {
font: "VT323", size: 16, color: "FFE082", align: 'left',
spacing: { before: 0, after: 160, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
accentColor: "FFB300"
},
previewCss: `
font-family: 'VT323', monospace;
background: #000000;
h1 { font-size: 36pt; color: #FFB300; margin-bottom: 24px; text-shadow: 0 0 5px #FF6F00; }
h2 { font-size: 20pt; color: #FFCA28; margin-top: 32px; margin-bottom: 16px; border-bottom: 1px solid #FFB300; }
p { font-size: 16pt; line-height: 1.4; color: #FFE082; margin-bottom: 14px; }
blockquote { border-left: 4px solid #FFB300; padding-left: 16px; margin: 24px 0; color: #FFECB3; }
`
},
{
id: 'game-handheld',
name: 'Game Handheld',
category: 'Gaming',
description: 'Classic 90s handheld console. Four shades of olive green pixel art style.',
vibe: 'Pixel, Green, Retro',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap',
wordConfig: {
heading1: {
font: "Press Start 2P", size: 20, color: "0f380f", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "8bac0f", color: "auto", style: "clear" }
},
heading2: {
font: "Press Start 2P", size: 12, color: "306230", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Press Start 2P", size: 10, color: "0f380f", align: 'left',
spacing: { before: 0, after: 160, line: 240 }
},
accentColor: "306230"
},
previewCss: `
font-family: 'Press Start 2P', cursive;
background: #9bbc0f;
h1 { font-size: 20pt; color: #0f380f; background: #8bac0f; padding: 20px; text-align: center; border: 4px solid #0f380f; margin-bottom: 24px; }
h2 { font-size: 12pt; color: #306230; margin-top: 32px; margin-bottom: 16px; border-bottom: 4px solid #306230; }
p { font-size: 10pt; line-height: 1.6; color: #0f380f; margin-bottom: 14px; }
blockquote { background: #8bac0f; border: 2px solid #306230; padding: 16px; margin: 20px 0; }
`
},
{
id: 'weather-radar',
name: 'Weather Radar',
category: 'Scientific',
description: 'Meteorological map aesthetic. Deep map green backgrounds with rainbow radar text colors.',
vibe: 'Scientific, Map, Storm',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;700&family=Sarpanch:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Chakra Petch", size: 32, color: "FF1744", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "263238", color: "auto", style: "clear" }
},
heading2: {
font: "Sarpanch", size: 16, color: "00E676", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "37474F", color: "auto", style: "clear" }
},
body: {
font: "Chakra Petch", size: 11, color: "E0F2F1", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "455A64", color: "auto", style: "clear" }
},
accentColor: "FF1744"
},
previewCss: `
font-family: 'Chakra Petch', sans-serif;
background: #455A64;
color: #E0F2F1;
h1 { font-size: 32pt; font-weight: 700; color: #FF1744; background: #263238; padding: 16px; margin-bottom: 24px; border-left: 8px solid #FF1744; }
h2 { font-family: 'Sarpanch', sans-serif; font-size: 16pt; font-weight: 700; color: #00E676; background: #37474F; padding: 8px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 11pt; line-height: 1.5; color: #E0F2F1; margin-bottom: 14px; }
blockquote { border: 2px solid #FFEA00; padding: 16px; margin: 24px 0; background: #263238; color: #FFEA00; }
`
},
{
id: 'mars-rover',
name: 'Mars Rover',
category: 'Space',
description: 'Red planet exploration. Dusty orange and terracotta tones with functional technical type.',
vibe: 'Space, Dusty, Technical',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Orbitron&family=Exo:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Orbitron", size: 30, color: "BF360C", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "FF7043", space: 6, style: "thick", size: 18 } }
},
heading2: {
font: "Exo", size: 16, color: "E64A19", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Exo", size: 11, color: "3E2723", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF5722"
},
previewCss: `
font-family: 'Exo', sans-serif;
background: #FBE9E7;
h1 { font-family: 'Orbitron', sans-serif; font-size: 30pt; font-weight: 700; color: #BF360C; border-bottom: 4px solid #FF7043; padding-bottom: 12px; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #E64A19; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #3E2723; margin-bottom: 14px; }
blockquote { background: #FFCCBC; border-left: 6px solid #BF360C; padding: 16px; margin: 24px 0; }
`
},
{
id: 'deep-sea-sub',
name: 'Deep Sea Sub',
category: 'Nature',
description: 'Bioluminescent abyss. Deepest navy backgrounds with glowing cyan/purple text.',
vibe: 'Dark, Underwater, Glowing',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Abel&family=Rajdhani:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Rajdhani", size: 36, color: "00E5FF", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "1A237E", color: "auto", style: "clear" }
},
heading2: {
font: "Abel", size: 20, color: "E040FB", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Abel", size: 12, color: "B2EBF2", align: 'center',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "000051", color: "auto", style: "clear" }
},
accentColor: "00E5FF"
},
previewCss: `
font-family: 'Abel', sans-serif;
background: #000051;
h1 { font-family: 'Rajdhani', sans-serif; font-size: 36pt; font-weight: 700; color: #00E5FF; background: #1A237E; padding: 20px; border-radius: 50%; width: 100px; height: 100px; line-height: 100px; margin: 0 auto 24px auto; text-align: center; display: block; box-shadow: 0 0 20px #00E5FF; }
h2 { font-size: 20pt; font-weight: 700; color: #E040FB; text-align: center; margin-top: 32px; margin-bottom: 16px; text-shadow: 0 0 5px #E040FB; }
p { font-size: 12pt; line-height: 1.6; color: #B2EBF2; margin-bottom: 14px; text-align: center; }
blockquote { border: 1px solid #00E5FF; padding: 16px; margin: 24px 0; color: #E0F7FA; text-align: center; }
`
},
{
id: 'arctic-base',
name: 'Arctic Base',
category: 'Scientific',
description: 'Polar research station. Stark white and ice blues with minimal sterile typography.',
vibe: 'Cold, Sterile, Blue',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Iceland&family=Roboto:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Iceland", size: 36, color: "01579B", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "E1F5FE", color: "auto", style: "clear" }
},
heading2: {
font: "Roboto", size: 18, color: "0277BD", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Roboto", size: 11, color: "455A64", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0288D1"
},
previewCss: `
font-family: 'Roboto', sans-serif;
h1 { font-family: 'Iceland', cursive; font-size: 36pt; color: #01579B; background: #E1F5FE; padding: 12px 20px; margin-bottom: 24px; border-left: 4px solid #01579B; }
h2 { font-size: 18pt; font-weight: 700; color: #0277BD; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 11pt; line-height: 1.6; color: #455A64; margin-bottom: 14px; font-weight: 300; }
blockquote { border: 1px solid #B3E5FC; padding: 16px; margin: 24px 0; background: #F0F8FF; color: #0277BD; }
`
},
{
id: 'schematic-diagram',
name: 'Schematic Diagram',
category: 'Technical',
description: 'Patent drawing style. Thin, precise lines on stark white with serif lettering.',
vibe: 'Technical, Precise, Patent',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Courier+Prime&family=Libre+Baskerville:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Libre Baskerville", size: 28, color: "000000", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
allCaps: true
},
heading2: {
font: "Courier Prime", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "single", size: 4 } }
},
body: {
font: "Courier Prime", size: 11, color: "212121", align: 'both',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-family: 'Libre Baskerville', serif; font-size: 28pt; font-weight: 700; color: #000000; text-align: center; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; margin-top: 32px; margin-bottom: 16px; border-bottom: 1px solid #000; display: inline-block; padding-bottom: 2px; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; text-align: justify; }
blockquote { border: 1px solid #000; padding: 20px; margin: 24px 0; font-size: 10pt; }
`
},
{
id: 'circuit-board',
name: 'Circuit Board',
category: 'Tech',
description: 'PCB aesthetic. Deep forest green background with copper/gold tracks.',
vibe: 'Tech, Green, Copper',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Teko:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Teko", size: 36, color: "FFD54F", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "FFB300", space: 4, style: "dotted", size: 12 } }
},
heading2: {
font: "Share Tech Mono", size: 16, color: "A5D6A7", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Share Tech Mono", size: 11, color: "C8E6C9", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFD54F"
},
previewCss: `
font-family: 'Share Tech Mono', monospace;
background: #1B5E20;
h1 { font-family: 'Teko', sans-serif; font-size: 36pt; font-weight: 700; color: #FFD54F; margin-bottom: 24px; border-bottom: 4px dotted #FFB300; }
h2 { font-size: 16pt; color: #A5D6A7; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.5; color: #C8E6C9; margin-bottom: 14px; }
blockquote { border: 1px solid #FFD54F; padding: 16px; margin: 24px 0; color: #FFD54F; }
`
},
{
id: 'hologram-blue',
name: 'Hologram Blue',
category: 'Sci-Fi',
description: 'Star Wars style communications. Translucent flickering blue text on dark voids.',
vibe: 'Holographic, Futuristic, Translucent',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Audiowide&family=Rajdhani:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Audiowide", size: 30, color: "00B0FF", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
heading2: {
font: "Rajdhani", size: 18, color: "80D8FF", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Rajdhani", size: 12, color: "B3E5FC", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "00B0FF"
},
previewCss: `
font-family: 'Rajdhani', sans-serif;
background: #000000;
h1 { font-family: 'Audiowide', cursive; font-size: 30pt; color: #00B0FF; text-align: center; margin-bottom: 24px; opacity: 0.8; text-shadow: 0 0 8px #00B0FF; }
h2 { font-size: 18pt; font-weight: 700; color: #80D8FF; text-align: center; margin-top: 32px; margin-bottom: 16px; border-top: 1px solid #00B0FF; border-bottom: 1px solid #00B0FF; display: inline-block; padding: 5px 20px; }
p { font-size: 12pt; line-height: 1.6; color: #B3E5FC; margin-bottom: 14px; text-align: center; opacity: 0.9; }
blockquote { border: 1px dashed #00B0FF; padding: 16px; margin: 24px 0; color: #E1F5FE; text-align: center; }
`
},
{
id: 'biopunk-lab',
name: 'Biopunk Lab',
category: 'Sci-Fi',
description: 'Organic technology. Slime greens, flesh tones, and rounded organic fonts.',
vibe: 'Organic, Gross, Tech',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Creepster&family=Kodchasan:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Creepster", size: 32, color: "2E7D32", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "C8E6C9", color: "auto", style: "clear" }
},
heading2: {
font: "Kodchasan", size: 18, color: "1B5E20", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "2E7D32", space: 4, style: "wave", size: 12 } }
},
body: {
font: "Kodchasan", size: 11, color: "33691E", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2E7D32"
},
previewCss: `
font-family: 'Kodchasan', sans-serif;
h1 { font-family: 'Creepster', cursive; font-size: 32pt; color: #2E7D32; background: #C8E6C9; padding: 16px; border-radius: 20px; text-align: center; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #1B5E20; margin-top: 32px; margin-bottom: 16px; text-decoration: underline wavy #2E7D32; }
p { font-size: 11pt; line-height: 1.6; color: #33691E; margin-bottom: 14px; }
blockquote { background: #F1F8E9; border: 2px solid #2E7D32; border-radius: 16px; padding: 16px; margin: 24px 0; }
`
}
];

View File

@@ -0,0 +1,153 @@
import { StyleOption } from '../../../types';
export const academicJournal: StyleOption = {
id: 'academic-journal',
name: 'Academic Journal',
category: 'Academic',
description: 'Scholarly and rigorous design for academic papers and research publications. Traditional serif typography optimized for extended reading.',
vibe: 'Scholarly, Serious, Traditional',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&family=Source+Sans+3:wght@400;600&display=swap',
typography: {
fonts: {
heading: 'Libre Baskerville',
body: 'Libre Baskerville',
code: 'Source Sans 3'
},
colors: {
text: '1A1A1A',
textSecondary: '333333',
background: 'FFFFFF',
accent: '800000',
border: '800000',
codeBg: 'F5F5F5',
blockquoteBorder: '333333'
}
},
elements: {
h1: {
font: 'heading',
size: 18,
color: '000000',
bold: true,
align: 'center',
spacing: { before: 18, after: 12, line: 1.2 }
},
h2: {
font: 'heading',
size: 13,
color: '000000',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h3: {
font: 'heading',
size: 12,
color: '000000',
bold: true,
italic: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'heading',
size: 11,
color: '333333',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'heading',
size: 10,
color: '333333',
bold: true,
italic: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'heading',
size: 10,
color: '333333',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 10,
color: '1A1A1A',
align: 'both',
spacing: { before: 0, after: 7, line: 1.7 }
},
blockquote: {
font: 'body',
size: 9,
color: '333333',
spacing: { before: 10, after: 10, line: 1.5 },
padding: 10,
borderLeft: { color: '800000', width: 3, style: 'solid' }
},
code: {
font: 'code',
size: 9,
color: '333333',
background: 'F5F5F5'
},
pre: {
font: 'code',
size: 9,
color: '333333',
background: 'F5F5F5',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 10,
color: '1A1A1A',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '800000',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 10,
color: '000000',
bold: true,
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 10,
color: '1A1A1A',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '800000', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,157 @@
import { StyleOption } from '../../../types';
export const arcticBase: StyleOption = {
id: 'arctic-base',
name: 'Arctic Base',
category: 'Scientific',
description: 'Polar research station. Stark white and ice blues with minimal sterile typography.',
vibe: 'Cold, Sterile, Blue',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Iceland&family=Roboto:wght@300;400&display=swap',
typography: {
fonts: {
heading: 'Iceland',
body: 'Roboto',
code: 'Roboto'
},
colors: {
text: '455A64',
textSecondary: '78909C',
background: 'FFFFFF',
accent: '0288D1',
border: '01579B',
codeBg: 'E3F2FD',
blockquoteBorder: '0277BD'
}
},
elements: {
h1: {
font: 'heading',
size: 36,
color: '01579B',
bold: false,
align: 'left',
background: 'E1F5FE',
spacing: { before: 20, after: 10, line: 1.2 },
borderLeft: { color: '01579B', width: 4, style: 'solid' },
padding: 12
},
h2: {
font: 'body',
size: 18,
color: '0277BD',
bold: true,
align: 'left',
allCaps: true,
spacing: { before: 16, after: 8, line: 1.2 }
},
h3: {
font: 'body',
size: 16,
color: '0288D1',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'body',
size: 14,
color: '0288D1',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'body',
size: 12,
color: '039BE5',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'body',
size: 11,
color: '039BE5',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '455A64',
align: 'left',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'body',
size: 10,
color: '0277BD',
spacing: { before: 12, after: 12, line: 1.5 },
padding: 16,
background: 'F0F8FF',
border: { color: 'B3E5FC', width: 1, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: '01579B',
background: 'E3F2FD'
},
pre: {
font: 'code',
size: 10,
color: '01579B',
background: 'E3F2FD',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '455A64',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '0288D1',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '01579B',
bold: true,
background: 'E1F5FE',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '455A64',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '81D4FA', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,156 @@
import { StyleOption } from '../../../types';
export const botanicalTextbook: StyleOption = {
id: 'botanical-textbook',
name: 'Botanical Textbook',
category: 'Scientific',
description: 'Vintage science textbook aesthetic. Academic yet organic, like a 19th-century flora guide.',
vibe: 'Vintage, Scientific, Organic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Alice&family=Gentium+Book+Basic:wght@400;700&display=swap',
typography: {
fonts: {
heading: 'Alice',
body: 'Gentium Book Basic',
code: 'Gentium Book Basic'
},
colors: {
text: '1B1B1B',
textSecondary: '33691E',
background: 'FFFFFF',
accent: '827717',
border: '827717',
codeBg: 'F9FBE7',
blockquoteBorder: '827717'
}
},
elements: {
h1: {
font: 'heading',
size: 28,
color: '33691E',
bold: false,
align: 'left',
spacing: { before: 20, after: 10, line: 1.2 },
border: { color: '827717', width: 2, style: 'single' }
},
h2: {
font: 'body',
size: 14,
color: '558B2F',
bold: true,
italic: true,
align: 'left',
spacing: { before: 15, after: 7.5, line: 1.2 }
},
h3: {
font: 'body',
size: 13,
color: '558B2F',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'body',
size: 12,
color: '689F38',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'body',
size: 11,
color: '689F38',
bold: true,
italic: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'body',
size: 11,
color: '7CB342',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '1B1B1B',
align: 'both',
spacing: { before: 0, after: 8, line: 1.7 }
},
blockquote: {
font: 'body',
size: 10,
color: '33691E',
spacing: { before: 10, after: 10, line: 1.5 },
padding: 16,
background: 'F9FBE7',
borderLeft: { color: '827717', width: 4, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: '33691E',
background: 'F9FBE7'
},
pre: {
font: 'code',
size: 10,
color: '33691E',
background: 'F9FBE7',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '1B1B1B',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '558B2F',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '33691E',
bold: true,
background: 'F0F4C3',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '1B1B1B',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '827717', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,153 @@
import { StyleOption } from '../../../types';
export const chemistryLab: StyleOption = {
id: 'chemistry-lab',
name: 'Chemistry Lab',
category: 'Scientific',
description: 'Clean, molecular design. Modern thin typography suitable for diagrams and formulas.',
vibe: 'Scientific, Modern, Molecular',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Advent+Pro:wght@400;600;700&family=Heebo:wght@300;400;500&display=swap',
typography: {
fonts: {
heading: 'Advent Pro',
body: 'Heebo',
code: 'Advent Pro'
},
colors: {
text: '37474F',
textSecondary: '546E7A',
background: 'FFFFFF',
accent: '00BCD4',
border: '00BCD4',
codeBg: 'E0F7FA',
blockquoteBorder: '00BCD4'
}
},
elements: {
h1: {
font: 'heading',
size: 32,
color: '00BCD4',
bold: true,
align: 'left',
spacing: { before: 20, after: 10, line: 1.2 }
},
h2: {
font: 'body',
size: 16,
color: '0097A7',
bold: true,
align: 'left',
spacing: { before: 16, after: 8, line: 1.2 }
},
h3: {
font: 'body',
size: 14,
color: '00838F',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'body',
size: 12,
color: '00838F',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'body',
size: 11,
color: '006064',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'body',
size: 10,
color: '006064',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 10,
color: '37474F',
align: 'left',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'body',
size: 10,
color: '0097A7',
spacing: { before: 12, after: 12, line: 1.5 },
padding: 16,
background: 'E0F7FA',
border: { color: '00BCD4', width: 1, style: 'solid' }
},
code: {
font: 'code',
size: 9,
color: '00838F',
background: 'E0F7FA'
},
pre: {
font: 'code',
size: 9,
color: '00838F',
background: 'E0F7FA',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 10,
color: '37474F',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '00BCD4',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 10,
color: '00BCD4',
bold: true,
background: 'E0F7FA',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 10,
color: '37474F',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: 'B2EBF2', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,156 @@
import { StyleOption } from '../../../types';
export const darkAcademia: StyleOption = {
id: 'dark-academia',
name: 'Dark Academia',
category: 'Academic',
description: 'Moody, literary aesthetic. Tweed textures, old libraries, and classic serif typography.',
vibe: 'Moody, Scholarly, Literary',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Alegreya:wght@400;700&family=Alegreya+SC:wght@400;700&display=swap',
typography: {
fonts: {
heading: 'Alegreya SC',
body: 'Alegreya',
code: 'Alegreya'
},
colors: {
text: '212121',
textSecondary: '4E342E',
background: 'F5F1E8',
accent: '5D4037',
border: '5D4037',
codeBg: 'EFEBE9',
blockquoteBorder: '3E2723'
}
},
elements: {
h1: {
font: 'heading',
size: 28,
color: '3E2723',
bold: true,
align: 'center',
spacing: { before: 24, after: 14, line: 1.2 },
border: { color: '5D4037', width: 4, style: 'double' }
},
h2: {
font: 'body',
size: 16,
color: '4E342E',
bold: true,
italic: true,
align: 'left',
spacing: { before: 18, after: 9, line: 1.2 }
},
h3: {
font: 'body',
size: 14,
color: '4E342E',
bold: true,
align: 'left',
spacing: { before: 15, after: 7.5, line: 1.2 }
},
h4: {
font: 'body',
size: 12,
color: '5D4037',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'body',
size: 11,
color: '5D4037',
bold: true,
italic: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'body',
size: 10,
color: '6D4C41',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '212121',
align: 'both',
spacing: { before: 0, after: 9, line: 1.8 }
},
blockquote: {
font: 'body',
size: 10,
color: '4E342E',
italic: true,
spacing: { before: 14, after: 14, line: 1.6 },
padding: 10,
borderLeft: { color: '3E2723', width: 2, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: '3E2723',
background: 'EFEBE9'
},
pre: {
font: 'code',
size: 10,
color: '3E2723',
background: 'EFEBE9',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '212121',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '5D4037',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '3E2723',
bold: true,
background: 'EFEBE9',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '212121',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '5D4037', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,156 @@
import { StyleOption } from '../../../types';
export const educationFriendly: StyleOption = {
id: 'education-friendly',
name: 'Education Friendly',
category: 'Education',
description: 'Approachable and clear design for educational materials. High readability with friendly colors suitable for learning environments.',
vibe: 'Friendly, Educational, Accessible',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap',
typography: {
fonts: {
heading: 'Nunito',
body: 'Nunito',
code: 'Nunito'
},
colors: {
text: '424242',
textSecondary: '616161',
background: 'FFFFFF',
accent: '5E35B1',
border: '7E57C2',
codeBg: 'EDE7F6',
blockquoteBorder: '7E57C2'
}
},
elements: {
h1: {
font: 'heading',
size: 24,
color: '5E35B1',
bold: true,
align: 'left',
background: 'EDE7F6',
spacing: { before: 20, after: 10, line: 1.2 },
padding: 16,
border: { color: 'D1C4E9', width: 1, style: 'solid' }
},
h2: {
font: 'heading',
size: 14,
color: '7E57C2',
bold: true,
align: 'left',
spacing: { before: 15, after: 7.5, line: 1.2 }
},
h3: {
font: 'heading',
size: 13,
color: '7E57C2',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'heading',
size: 12,
color: '9575CD',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'heading',
size: 11,
color: '9575CD',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'heading',
size: 11,
color: 'B39DDB',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '424242',
align: 'left',
spacing: { before: 0, after: 8, line: 1.7 }
},
blockquote: {
font: 'body',
size: 10,
color: '5E35B1',
spacing: { before: 10, after: 10, line: 1.5 },
padding: 16,
background: 'F3E5F5',
borderLeft: { color: '7E57C2', width: 4, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: '5E35B1',
background: 'EDE7F6'
},
pre: {
font: 'code',
size: 10,
color: '5E35B1',
background: 'EDE7F6',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '424242',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '5E35B1',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '5E35B1',
bold: true,
background: 'EDE7F6',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '424242',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: 'D1C4E9', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,155 @@
import { StyleOption } from '../../../types';
export const emergencyRoom: StyleOption = {
id: 'emergency-room',
name: 'Emergency Room',
category: 'Medical',
description: 'Hospital signage. Sterile white, urgent red, and clean blue.',
vibe: 'Sterile, Urgent, Clean',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Assistant:wght@400;700&family=Heebo:wght@400;700&display=swap',
typography: {
fonts: {
heading: 'Assistant',
body: 'Heebo',
code: 'Heebo'
},
colors: {
text: '424242',
textSecondary: '616161',
background: 'FFFFFF',
accent: 'D50000',
border: 'D50000',
codeBg: 'FFEBEE',
blockquoteBorder: 'D50000'
}
},
elements: {
h1: {
font: 'heading',
size: 36,
color: 'D50000',
bold: true,
align: 'left',
spacing: { before: 20, after: 10, line: 1.2 },
border: { color: 'D50000', width: 4, style: 'single' }
},
h2: {
font: 'body',
size: 18,
color: '2962FF',
bold: true,
allCaps: true,
align: 'left',
spacing: { before: 16, after: 8, line: 1.2 }
},
h3: {
font: 'body',
size: 15,
color: '2962FF',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'body',
size: 13,
color: '2979FF',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'body',
size: 12,
color: '2979FF',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'body',
size: 11,
color: '448AFF',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '424242',
align: 'left',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'body',
size: 10,
color: 'D50000',
spacing: { before: 12, after: 12, line: 1.5 },
padding: 16,
background: 'FFEBEE',
borderLeft: { color: 'D50000', width: 6, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: 'D50000',
background: 'FFEBEE'
},
pre: {
font: 'code',
size: 10,
color: 'D50000',
background: 'FFEBEE',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '424242',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '2962FF',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: 'D50000',
bold: true,
background: 'FFEBEE',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '424242',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: 'EF5350', width: 3, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,157 @@
import { StyleOption } from '../../../types';
export const furnitureManual: StyleOption = {
id: 'furniture-manual',
name: 'Furniture Manual',
category: 'Instructional',
description: 'Flat-pack assembly guide. Clean, heavy line art vibes with friendly sans-serifs.',
vibe: 'Clean, Instructional, Neutral',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Varela+Round&family=Hind:wght@300;600&display=swap',
typography: {
fonts: {
heading: 'Varela Round',
body: 'Hind',
code: 'Hind'
},
colors: {
text: '424242',
textSecondary: '616161',
background: 'FFFFFF',
accent: '000000',
border: '000000',
codeBg: 'F5F5F5',
blockquoteBorder: '000000'
}
},
elements: {
h1: {
font: 'heading',
size: 32,
color: '000000',
bold: false,
align: 'left',
spacing: { before: 20, after: 10, line: 1.2 }
},
h2: {
font: 'heading',
size: 16,
color: '000000',
bold: false,
align: 'left',
background: 'F5F5F5',
spacing: { before: 16, after: 8, line: 1.2 },
borderLeft: { color: '000000', width: 4, style: 'single' },
border: { color: '000000', width: 2, style: 'single' },
padding: 8
},
h3: {
font: 'heading',
size: 14,
color: '000000',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'heading',
size: 12,
color: '212121',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'heading',
size: 11,
color: '424242',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'heading',
size: 10,
color: '616161',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '424242',
align: 'left',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'heading',
size: 12,
color: '212121',
spacing: { before: 12, after: 12, line: 1.5 },
padding: 20,
background: 'F5F5F5',
border: { color: '000000', width: 2, style: 'single' }
},
code: {
font: 'code',
size: 10,
color: '000000',
background: 'F5F5F5'
},
pre: {
font: 'code',
size: 10,
color: '000000',
background: 'F5F5F5',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '424242',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '000000',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '000000',
bold: true,
background: 'F5F5F5',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '424242',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '000000', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,154 @@
import { StyleOption } from '../../../types';
export const historyTextbook: StyleOption = {
id: 'history-textbook',
name: 'History Textbook',
category: 'Academic',
description: 'Classic textbook design. Sepia tones, distinct serif headers, and wide margins for notes.',
vibe: 'Classic, Educational, Historical',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Goudy+Bookletter+1911&family=Sorts+Mill+Goudy&display=swap',
typography: {
fonts: {
heading: 'Goudy Bookletter 1911',
body: 'Sorts Mill Goudy',
code: 'Sorts Mill Goudy'
},
colors: {
text: '212121',
textSecondary: '3E2723',
background: 'FFF8E1',
accent: '8D6E63',
border: '8D6E63',
codeBg: 'FFF3E0',
blockquoteBorder: '8D6E63'
}
},
elements: {
h1: {
font: 'heading',
size: 28,
color: '5D4037',
bold: false,
align: 'left',
spacing: { before: 20, after: 10, line: 1.2 },
border: { color: '8D6E63', width: 2, style: 'single' }
},
h2: {
font: 'body',
size: 16,
color: '3E2723',
bold: false,
align: 'left',
spacing: { before: 15, after: 7.5, line: 1.2 }
},
h3: {
font: 'body',
size: 14,
color: '4E342E',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'body',
size: 12,
color: '5D4037',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'body',
size: 11,
color: '6D4C41',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'body',
size: 10,
color: '795548',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '212121',
align: 'left',
spacing: { before: 0, after: 8, line: 1.7 }
},
blockquote: {
font: 'body',
size: 10,
color: '3E2723',
spacing: { before: 10, after: 10, line: 1.5 },
padding: 16,
background: 'FFFFFF',
border: { color: '8D6E63', width: 1, style: 'single' }
},
code: {
font: 'code',
size: 10,
color: '5D4037',
background: 'FFF3E0'
},
pre: {
font: 'code',
size: 10,
color: '5D4037',
background: 'FFF3E0',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '212121',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '5D4037',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '5D4037',
bold: true,
background: 'FFF3E0',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '212121',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '8D6E63', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
import { academicJournal } from './academic-journal';
import { arcticBase } from './arctic-base';
import { botanicalTextbook } from './botanical-textbook';
import { chemistryLab } from './chemistry-lab';
import { darkAcademia } from './dark-academia';
import { educationFriendly } from './education-friendly';
import { emergencyRoom } from './emergency-room';
import { furnitureManual } from './furniture-manual';
import { historyTextbook } from './history-textbook';
import { kidsEducation } from './kids-education';
import { mathematicsPaper } from './mathematics-paper';
import { medicalProfessional } from './medical-professional';
import { phdThesis } from './phd-thesis';
import { scantronTest } from './scantron-test';
import { scientificJournal } from './scientific-journal';
import { weatherRadar } from './weather-radar';
export const academicStyles: StyleOption[] = [
academicJournal,
arcticBase,
botanicalTextbook,
chemistryLab,
darkAcademia,
educationFriendly,
emergencyRoom,
furnitureManual,
historyTextbook,
kidsEducation,
mathematicsPaper,
medicalProfessional,
phdThesis,
scantronTest,
scientificJournal,
weatherRadar
];

View File

@@ -0,0 +1,156 @@
import { StyleOption } from '../../../types';
export const kidsEducation: StyleOption = {
id: 'kids-education',
name: 'Kids Education',
category: 'Education',
description: 'Fun and engaging design for children\'s educational content. Rounded typography with bright, cheerful colors.',
vibe: 'Educational, Fun, Engaging',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600&display=swap',
typography: {
fonts: {
heading: 'Fredoka',
body: 'Fredoka',
code: 'Fredoka'
},
colors: {
text: '495057',
textSecondary: '6C757D',
background: 'FFFFFF',
accent: 'FF6B6B',
border: 'FF6B6B',
codeBg: 'FFF9DB',
blockquoteBorder: '4ECDC4'
}
},
elements: {
h1: {
font: 'heading',
size: 32,
color: 'FF6B6B',
bold: true,
align: 'center',
background: 'FFF9DB',
spacing: { before: 20, after: 12, line: 1.2 },
padding: 16,
border: { color: 'FFE066', width: 2, style: 'solid' }
},
h2: {
font: 'heading',
size: 18,
color: '4ECDC4',
bold: true,
align: 'left',
spacing: { before: 16, after: 8, line: 1.2 }
},
h3: {
font: 'heading',
size: 15,
color: '45B7D1',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'heading',
size: 13,
color: '45B7D1',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'heading',
size: 12,
color: '96CEB4',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'heading',
size: 11,
color: '96CEB4',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 12,
color: '495057',
align: 'left',
spacing: { before: 0, after: 9, line: 1.8 }
},
blockquote: {
font: 'body',
size: 11,
color: '4ECDC4',
spacing: { before: 10, after: 10, line: 1.6 },
padding: 16,
background: 'E3F9F8',
borderLeft: { color: '4ECDC4', width: 6, style: 'solid' }
},
code: {
font: 'code',
size: 11,
color: 'FF6B6B',
background: 'FFF9DB'
},
pre: {
font: 'code',
size: 11,
color: 'FF6B6B',
background: 'FFF9DB',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 12,
color: '495057',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: 'FF6B6B',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 12,
color: 'FF6B6B',
bold: true,
background: 'FFF9DB',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 12,
color: '495057',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: 'FFE066', width: 3, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,154 @@
import { StyleOption } from '../../../types';
export const mathematicsPaper: StyleOption = {
id: 'mathematics-paper',
name: 'Mathematics Paper',
category: 'Academic',
description: 'Inspired by LaTeX and Computer Modern. Serif-heavy, precise, and highly legible for academic rigor.',
vibe: 'Academic, Rigorous, Formal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Domine:wght@400;700&family=Noticia+Text:wght@400;700&display=swap',
typography: {
fonts: {
heading: 'Domine',
body: 'Noticia Text',
code: 'Noticia Text'
},
colors: {
text: '212121',
textSecondary: '424242',
background: 'FFFFFF',
accent: '000000',
border: '000000',
codeBg: 'F5F5F5',
blockquoteBorder: '000000'
}
},
elements: {
h1: {
font: 'heading',
size: 24,
color: '000000',
bold: true,
align: 'center',
spacing: { before: 20, after: 12, line: 1.2 }
},
h2: {
font: 'heading',
size: 14,
color: '000000',
bold: true,
align: 'left',
spacing: { before: 16, after: 8, line: 1.2 }
},
h3: {
font: 'heading',
size: 12,
color: '000000',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'body',
size: 11,
color: '212121',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'body',
size: 10,
color: '424242',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'body',
size: 10,
color: '616161',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: '212121',
align: 'both',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'body',
size: 10,
color: '212121',
italic: true,
spacing: { before: 12, after: 12, line: 1.5 },
padding: 10,
background: 'F5F5F5',
borderLeft: { color: '000000', width: 3, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: '000000',
background: 'F5F5F5'
},
pre: {
font: 'code',
size: 10,
color: '000000',
background: 'F5F5F5',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: '212121',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '000000',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: '000000',
bold: true,
background: 'F5F5F5',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: '212121',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '000000', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,153 @@
import { StyleOption } from '../../../types';
export const medicalProfessional: StyleOption = {
id: 'medical-professional',
name: 'Medical Professional',
category: 'Healthcare',
description: 'Clean, clinical design for healthcare and medical documentation. Emphasizes clarity and professionalism with calming blue accents.',
vibe: 'Clinical, Professional, Trustworthy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap',
typography: {
fonts: {
heading: 'Open Sans',
body: 'Open Sans',
code: 'Open Sans'
},
colors: {
text: '37474F',
textSecondary: '546E7A',
background: 'FFFFFF',
accent: '1565C0',
border: '0D47A1',
codeBg: 'E3F2FD',
blockquoteBorder: '1565C0'
}
},
elements: {
h1: {
font: 'heading',
size: 20,
color: '1565C0',
bold: true,
align: 'left',
spacing: { before: 18, after: 10, line: 1.2 }
},
h2: {
font: 'heading',
size: 13,
color: '0D47A1',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h3: {
font: 'heading',
size: 12,
color: '1565C0',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'heading',
size: 11,
color: '1976D2',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'heading',
size: 10,
color: '1976D2',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'heading',
size: 10,
color: '2196F3',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 10,
color: '37474F',
align: 'left',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'body',
size: 10,
color: '1565C0',
spacing: { before: 10, after: 10, line: 1.5 },
padding: 16,
background: 'E3F2FD',
borderLeft: { color: '1565C0', width: 4, style: 'solid' }
},
code: {
font: 'code',
size: 9,
color: '0D47A1',
background: 'E3F2FD'
},
pre: {
font: 'code',
size: 9,
color: '0D47A1',
background: 'E3F2FD',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 10,
color: '37474F',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '1565C0',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 10,
color: '1565C0',
bold: true,
background: 'E3F2FD',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 10,
color: '37474F',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '90CAF9', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,154 @@
import { StyleOption } from '../../../types';
export const phdThesis: StyleOption = {
id: 'phd-thesis',
name: 'PhD Thesis',
category: 'Academic',
description: 'The pinnacle of academic formatting. Extremely legible serif body, double spacing, and rigorous margin structure.',
vibe: 'Formal, Scholarly, Prestigious',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Tinos:wght@400;700&family=Crimson+Text:wght@400;600&display=swap',
typography: {
fonts: {
heading: 'Tinos',
body: 'Crimson Text',
code: 'Crimson Text'
},
colors: {
text: '000000',
textSecondary: '424242',
background: 'FFFFFF',
accent: '000000',
border: '000000',
codeBg: 'F5F5F5',
blockquoteBorder: '000000'
}
},
elements: {
h1: {
font: 'heading',
size: 24,
color: '000000',
bold: true,
allCaps: true,
align: 'center',
spacing: { before: 24, after: 12, line: 1.2 }
},
h2: {
font: 'heading',
size: 14,
color: '000000',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h3: {
font: 'heading',
size: 12,
color: '000000',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'body',
size: 11,
color: '212121',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'body',
size: 10,
color: '424242',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'body',
size: 10,
color: '616161',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 12,
color: '000000',
align: 'both',
spacing: { before: 0, after: 0, line: 2.0 }
},
blockquote: {
font: 'body',
size: 11,
color: '000000',
italic: true,
spacing: { before: 12, after: 12, line: 2.0 },
padding: 10,
indent: 40
},
code: {
font: 'code',
size: 11,
color: '000000',
background: 'F5F5F5'
},
pre: {
font: 'code',
size: 11,
color: '000000',
background: 'F5F5F5',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 2.0 }
},
ol: {
spacing: { before: 8, after: 8, line: 2.0 }
},
li: {
font: 'body',
size: 12,
color: '000000',
spacing: { before: 4, after: 4, line: 2.0 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '000000',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 12,
color: '000000',
bold: true,
background: 'F5F5F5',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 12,
color: '000000',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '000000', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,156 @@
import { StyleOption } from '../../../types';
export const scantronTest: StyleOption = {
id: 'scantron-test',
name: 'Scantron Test',
category: 'Academic',
description: 'Standardized testing sheet. Salmon pinks and greens with bubble-filling aesthetics.',
vibe: 'Academic, Retro, Bureaucratic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&display=swap',
typography: {
fonts: {
heading: 'Roboto Mono',
body: 'Roboto Mono',
code: 'Roboto Mono'
},
colors: {
text: '212121',
textSecondary: '424242',
background: 'FFF3E0',
accent: 'EF5350',
border: 'B71C1C',
codeBg: 'FFEBEE',
blockquoteBorder: 'B71C1C'
}
},
elements: {
h1: {
font: 'heading',
size: 28,
color: 'B71C1C',
bold: true,
align: 'left',
spacing: { before: 20, after: 10, line: 1.2 },
border: { color: 'EF5350', width: 2, style: 'single' }
},
h2: {
font: 'heading',
size: 14,
color: '1B5E20',
bold: true,
align: 'left',
background: 'C8E6C9',
spacing: { before: 16, after: 8, line: 1.2 },
padding: 4
},
h3: {
font: 'heading',
size: 12,
color: '2E7D32',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'heading',
size: 11,
color: '388E3C',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'heading',
size: 10,
color: '43A047',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'heading',
size: 10,
color: '4CAF50',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 10,
color: '212121',
align: 'left',
spacing: { before: 0, after: 8, line: 1.6 }
},
blockquote: {
font: 'body',
size: 10,
color: 'B71C1C',
spacing: { before: 12, after: 12, line: 1.5 },
padding: 16,
background: 'FFEBEE',
border: { color: 'B71C1C', width: 1, style: 'solid' }
},
code: {
font: 'code',
size: 9,
color: 'B71C1C',
background: 'FFEBEE'
},
pre: {
font: 'code',
size: 9,
color: 'B71C1C',
background: 'FFEBEE',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 10,
color: '212121',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: 'B71C1C',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 10,
color: 'B71C1C',
bold: true,
background: 'FFEBEE',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 10,
color: '212121',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: 'EF5350', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,152 @@
import { StyleOption } from '../../../types';
export const scientificJournal: StyleOption = {
id: 'scientific-journal',
name: 'Scientific Journal',
category: 'Academic',
description: 'Precise design for scientific publications. Clear hierarchy optimized for data-heavy content and citations.',
vibe: 'Scientific, Precise, Academic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Literata:wght@400;500;700&family=Fira+Sans:wght@400;500;600&display=swap',
typography: {
fonts: {
heading: 'Literata',
body: 'Literata',
code: 'Fira Sans'
},
colors: {
text: '262626',
textSecondary: '4A4A4A',
background: 'FFFFFF',
accent: '0066CC',
border: '0066CC',
codeBg: 'F5F5F5',
blockquoteBorder: '0066CC'
}
},
elements: {
h1: {
font: 'heading',
size: 18,
color: '1A1A1A',
bold: true,
align: 'left',
spacing: { before: 18, after: 10, line: 1.3 }
},
h2: {
font: 'code',
size: 12,
color: '1A1A1A',
bold: true,
align: 'left',
spacing: { before: 14, after: 6, line: 1.2 }
},
h3: {
font: 'code',
size: 11,
color: '262626',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h4: {
font: 'code',
size: 10,
color: '333333',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h5: {
font: 'code',
size: 10,
color: '424242',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
h6: {
font: 'code',
size: 9,
color: '4A4A4A',
bold: true,
align: 'left',
spacing: { before: 6, after: 3, line: 1.2 }
},
p: {
font: 'body',
size: 10,
color: '262626',
align: 'both',
spacing: { before: 0, after: 7, line: 1.6 }
},
blockquote: {
font: 'body',
size: 9,
color: '4A4A4A',
spacing: { before: 8, after: 8, line: 1.4 },
padding: 10,
borderLeft: { color: '0066CC', width: 2, style: 'solid' }
},
code: {
font: 'code',
size: 9,
color: '0066CC',
background: 'F5F5F5'
},
pre: {
font: 'code',
size: 9,
color: '0066CC',
background: 'F5F5F5',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 10,
color: '262626',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: '0066CC',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 10,
color: '1A1A1A',
bold: true,
background: 'F5F5F5',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 10,
color: '262626',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: '0066CC', width: 1, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,159 @@
import { StyleOption } from '../../../types';
export const weatherRadar: StyleOption = {
id: 'weather-radar',
name: 'Weather Radar',
category: 'Scientific',
description: 'Meteorological map aesthetic. Deep map green backgrounds with rainbow radar text colors.',
vibe: 'Scientific, Map, Storm',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;700&family=Sarpanch:wght@400;700&display=swap',
typography: {
fonts: {
heading: 'Chakra Petch',
body: 'Chakra Petch',
code: 'Sarpanch'
},
colors: {
text: 'E0F2F1',
textSecondary: 'B2DFDB',
background: '455A64',
accent: 'FF1744',
border: 'FF1744',
codeBg: '37474F',
blockquoteBorder: 'FFEA00'
}
},
elements: {
h1: {
font: 'heading',
size: 32,
color: 'FF1744',
bold: true,
align: 'left',
background: '263238',
spacing: { before: 20, after: 10, line: 1.2 },
borderLeft: { color: 'FF1744', width: 8, style: 'solid' },
padding: 16
},
h2: {
font: 'code',
size: 16,
color: '00E676',
bold: true,
align: 'left',
background: '37474F',
spacing: { before: 16, after: 8, line: 1.2 },
padding: 8
},
h3: {
font: 'code',
size: 14,
color: '69F0AE',
bold: true,
align: 'left',
spacing: { before: 14, after: 7, line: 1.2 }
},
h4: {
font: 'code',
size: 12,
color: '69F0AE',
bold: true,
align: 'left',
spacing: { before: 12, after: 6, line: 1.2 }
},
h5: {
font: 'code',
size: 11,
color: 'B9F6CA',
bold: true,
align: 'left',
spacing: { before: 10, after: 5, line: 1.2 }
},
h6: {
font: 'code',
size: 10,
color: 'B9F6CA',
bold: true,
align: 'left',
spacing: { before: 8, after: 4, line: 1.2 }
},
p: {
font: 'body',
size: 11,
color: 'E0F2F1',
align: 'left',
spacing: { before: 0, after: 8, line: 1.5 }
},
blockquote: {
font: 'body',
size: 10,
color: 'FFEA00',
spacing: { before: 12, after: 12, line: 1.5 },
padding: 16,
background: '263238',
border: { color: 'FFEA00', width: 2, style: 'solid' }
},
code: {
font: 'code',
size: 10,
color: 'FFEA00',
background: '37474F'
},
pre: {
font: 'code',
size: 10,
color: 'FFEA00',
background: '37474F',
spacing: { before: 12, after: 12, line: 1.4 },
padding: 12
},
ul: {
spacing: { before: 8, after: 8, line: 1.5 }
},
ol: {
spacing: { before: 8, after: 8, line: 1.5 }
},
li: {
font: 'body',
size: 11,
color: 'E0F2F1',
spacing: { before: 4, after: 4, line: 1.5 }
},
strong: {
bold: true
},
em: {
italic: true
},
a: {
color: 'FFEA00',
underline: true
},
table: {
spacing: { before: 12, after: 12, line: 1.4 }
},
th: {
font: 'heading',
size: 11,
color: 'FF1744',
bold: true,
background: '263238',
spacing: { before: 6, after: 6, line: 1.4 }
},
td: {
font: 'body',
size: 11,
color: 'E0F2F1',
background: '37474F',
spacing: { before: 6, after: 6, line: 1.4 }
},
hr: {
border: { color: 'FFEA00', width: 2, style: 'single' },
spacing: { before: 12, after: 12, line: 1 }
},
img: {
align: 'center',
spacing: { before: 12, after: 12, line: 1 }
}
}
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const circusSideshow: StyleOption = {
id: 'circus-sideshow',
name: 'Circus Sideshow',
category: 'Entertainment',
description: 'Big top aesthetic. Ornamental fonts with red and cream stripe vibes.',
vibe: 'Fun, Ornamental, Striped',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Rye&family=Sancreek&family=Roboto+Slab&display=swap',
wordConfig: {
heading1: {
font: "Sancreek", size: 40, color: "D32F2F", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 }
},
heading2: {
font: "Rye", size: 18, color: "1A237E", bold: false, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Roboto Slab", size: 11, color: "212121", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Roboto Slab', serif;
background: #FFF8E1;
h1 { font-family: 'Sancreek', cursive; font-size: 40pt; color: #D32F2F; text-align: center; margin-bottom: 24px; text-shadow: 2px 2px 0px #FBC02D; }
h2 { font-family: 'Rye', serif; font-size: 18pt; color: #1A237E; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; text-align: center; }
blockquote { border: 4px double #D32F2F; padding: 20px; margin: 24px auto; background: #FFF; border-radius: 16px; font-family: 'Sancreek', cursive; font-size: 14pt; color: #1A237E; text-align: center; max-width: 80%; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const environmentalGreen: StyleOption = {
id: 'environmental-green',
name: 'Environmental Green',
category: 'Sustainability',
description: 'Nature-inspired design for environmental and sustainability communications. Organic feel with earthy green palette.',
vibe: 'Natural, Sustainable, Organic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bitter:wght@400;500;700&family=Karla:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Bitter", size: 26, color: "2E7D32", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Bitter", size: 14, color: "388E3C", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Karla", size: 10, color: "3E4A3D", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2E7D32"
},
previewCss: `
font-family: 'Karla', sans-serif;
h1 { font-family: 'Bitter', serif; font-size: 26pt; font-weight: 700; color: #2E7D32; margin-bottom: 24px; }
h2 { font-family: 'Bitter', serif; font-size: 14pt; font-weight: 700; color: #388E3C; margin-top: 30px; margin-bottom: 14px; }
p { font-size: 10pt; line-height: 1.6; color: #3E4A3D; margin-bottom: 14px; }
blockquote { background: #E8F5E9; padding: 16px; border-left: 4px solid #2E7D32; margin: 20px 0; }
`
};

View File

@@ -0,0 +1,37 @@
import { StyleOption } from '../../../types';
export const highwayInterstate: StyleOption = {
id: 'highway-interstate',
name: 'Highway Interstate',
category: 'Urban',
description: 'Road signage aesthetic. Reflective white text on deep highway green backgrounds.',
vibe: 'Functional, Travel, Signage',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Overpass:wght@400;700&family=Public+Sans:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Overpass", size: 36, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "00695C", color: "auto", style: "clear" },
border: {
bottom: { color: "FFFFFF", space: 4, style: "single", size: 12 },
top: { color: "FFFFFF", space: 4, style: "single", size: 12 }
}
},
heading2: {
font: "Public Sans", size: 18, color: "004D40", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Public Sans", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "00695C"
},
previewCss: `
font-family: 'Public Sans', sans-serif;
h1 { font-family: 'Overpass', sans-serif; font-size: 36pt; font-weight: 700; color: #FFFFFF; background: #00695C; padding: 16px; border-top: 2px solid #FFF; border-bottom: 2px solid #FFF; margin-bottom: 24px; border-radius: 8px; }
h2 { font-size: 18pt; font-weight: 700; color: #004D40; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { background: #FFEB3B; color: #000; padding: 16px; margin: 24px 0; border: 4px solid #000; transform: skewX(-10deg); width: fit-content; }
`
};

View File

@@ -0,0 +1,26 @@
import { StyleOption } from '../../../types';
import { circusSideshow } from './circus-sideshow';
import { environmentalGreen } from './environmental-green';
import { highwayInterstate } from './highway-interstate';
import { jungleExplorer } from './jungle-explorer';
import { publicTransit } from './public-transit';
import { silentFilmIntertitle } from './silent-film-intertitle';
import { sportsDynamic } from './sports-dynamic';
import { steampunkInventor } from './steampunk-inventor';
import { subwayTile } from './subway-tile';
import { taxiCab } from './taxi-cab';
import { varsityTeam } from './varsity-team';
export const coreStyles: StyleOption[] = [
circusSideshow,
environmentalGreen,
highwayInterstate,
jungleExplorer,
publicTransit,
silentFilmIntertitle,
sportsDynamic,
steampunkInventor,
subwayTile,
taxiCab,
varsityTeam
];

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const jungleExplorer: StyleOption = {
id: 'jungle-explorer',
name: 'Jungle Explorer',
category: 'Adventure',
description: 'Safari expedition style. Khaki, olive drab, and canvas textures.',
vibe: 'Adventure, Khaki, Nature',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Stardos+Stencil&family=Domine:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Stardos Stencil", size: 36, color: "33691E", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "827717", space: 6, style: "thick", size: 18 } }
},
heading2: {
font: "Domine", size: 16, color: "558B2F", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Domine", size: 11, color: "1B1B1B", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "827717"
},
previewCss: `
font-family: 'Domine', serif;
background: #F0F4C3; /* Khaki tint */
h1 { font-family: 'Stardos Stencil', cursive; font-size: 36pt; font-weight: 700; color: #33691E; border-bottom: 4px solid #827717; padding-bottom: 12px; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #558B2F; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #1B1B1B; margin-bottom: 14px; }
blockquote { background: #DCEDC8; padding: 20px; border-left: 6px solid #33691E; margin: 24px 0; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const publicTransit: StyleOption = {
id: 'public-transit',
name: 'Public Transit',
category: 'Urban',
description: 'Subway map and transit signage aesthetic. Clean, highly legible sans-serifs with color-coded lines.',
vibe: 'Urban, Functional, Color-coded',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Mukta:wght@400;700&family=Hanken+Grotesk:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Hanken Grotesk", size: 32, color: "000000", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "FFC107", space: 8, style: "single", size: 24 } }
},
heading2: {
font: "Mukta", size: 18, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "EEEEEE", color: "auto", style: "clear" }
},
body: {
font: "Mukta", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFC107"
},
previewCss: `
font-family: 'Mukta', sans-serif;
h1 { font-family: 'Hanken Grotesk', sans-serif; font-size: 32pt; font-weight: 700; color: #000000; border-bottom: 6px solid #FFC107; padding-bottom: 12px; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #000000; background: #EEEEEE; padding: 4px 12px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 6px solid #F44336; padding-left: 16px; margin: 24px 0; }
`
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
export const silentFilmIntertitle: StyleOption = {
id: 'silent-film-intertitle',
name: 'Silent Film Intertitle',
category: 'Cinema',
description: '1920s cinema cards. White decorative text on black backgrounds with ornate borders.',
vibe: 'Cinema, Vintage, Dramatic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Gye-Gye&family=Sorts+Mill+Goudy&display=swap',
wordConfig: {
heading1: {
font: "Gye-Gye", size: 32, color: "FFFFFF", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
heading2: {
font: "Sorts Mill Goudy", size: 18, color: "EEEEEE", bold: false, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "212121", color: "auto", style: "clear" }
},
body: {
font: "Sorts Mill Goudy", size: 14, color: "000000", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Sorts Mill Goudy', serif;
h1 { font-family: 'Gye-Gye', sans-serif; font-size: 32pt; color: #FFFFFF; background: #000000; padding: 20px; text-align: center; margin-bottom: 24px; border: 4px double #FFF; }
h2 { font-size: 18pt; color: #EEEEEE; background: #212121; display: inline-block; padding: 10px 20px; margin-top: 30px; margin-bottom: 16px; border-radius: 8px; }
p { font-size: 14pt; line-height: 1.6; color: #000000; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px solid #000; padding: 20px; margin: 24px 0; font-style: italic; text-align: center; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const sportsDynamic: StyleOption = {
id: 'sports-dynamic',
name: 'Sports Dynamic',
category: 'Sports',
description: 'Energetic and bold design for sports and athletic content. Dynamic typography with high-impact colors.',
vibe: 'Energetic, Bold, Athletic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Rubik:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Bebas Neue", size: 36, color: "D32F2F", bold: false, align: 'left',
spacing: { before: 360, after: 200, line: 220 }
},
heading2: {
font: "Rubik", size: 14, color: "1A1A1A", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 }
},
body: {
font: "Rubik", size: 10, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Rubik', sans-serif;
h1 { font-family: 'Bebas Neue', sans-serif; font-size: 36pt; color: #D32F2F; margin-bottom: 24px; letter-spacing: 2px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #1A1A1A; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { background: #FFEBEE; padding: 16px; border-left: 6px solid #D32F2F; margin: 20px 0; font-weight: 500; }
`
};

View File

@@ -0,0 +1,37 @@
import { StyleOption } from '../../../types';
export const steampunkInventor: StyleOption = {
id: 'steampunk-inventor',
name: 'Steampunk Inventor',
category: 'Fantasy',
description: 'Brass and gear aesthetic. Victorian fonts with industrial metallic colors.',
vibe: 'Mechanical, Brass, Victorian',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Rye&family=Lora:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Rye", size: 30, color: "5D4037", bold: false, align: 'left',
spacing: { before: 400, after: 240, line: 240 },
border: { bottom: { color: "B8860B", space: 8, style: "single", size: 24 } }
},
heading2: {
font: "Lora", size: 16, color: "8D6E63", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Lora", size: 11, color: "3E2723", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "B8860B"
},
previewCss: `
font-family: 'Lora', serif;
background: #EFEBE9;
h1 { font-family: 'Rye', serif; font-size: 30pt; color: #5D4037; border-bottom: 6px double #B8860B; padding-bottom: 12px; margin-bottom: 24px; }
h2 { font-size: 16pt; font-weight: 700; color: #8D6E63; margin-top: 30px; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 1px; }
p { font-size: 11pt; line-height: 1.6; color: #3E2723; margin-bottom: 14px; }
blockquote { background: #D7CCC8; padding: 16px; border: 2px solid #5D4037; border-radius: 4px; margin: 24px 0; }
`
};

View File

@@ -0,0 +1,38 @@
import { StyleOption } from '../../../types';
export const subwayTile: StyleOption = {
id: 'subway-tile',
name: 'Subway Tile',
category: 'Urban',
description: 'Classic station ceramics. Clean white backgrounds, heavy black text, and tile-like framing.',
vibe: 'Clean, Ceramic, Urban',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Work+Sans:wght@300;600&family=Lexend:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Lexend", size: 36, color: "000000", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "000000", space: 12, style: "single", size: 4 },
bottom: { color: "000000", space: 12, style: "single", size: 4 },
left: { color: "000000", space: 12, style: "single", size: 4 },
right: { color: "000000", space: 12, style: "single", size: 4 }
}
},
heading2: {
font: "Work Sans", size: 16, color: "212121", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Work Sans", size: 11, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Work Sans', sans-serif;
h1 { font-family: 'Lexend', sans-serif; font-size: 36pt; font-weight: 700; color: #000000; border: 4px solid #000000; padding: 16px; margin-bottom: 24px; text-align: center; background: #FFFFFF; }
h2 { font-size: 16pt; font-weight: 700; color: #212121; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { border-left: 8px solid #000; padding-left: 16px; margin: 24px 0; background: #F5F5F5; }
`
};

View File

@@ -0,0 +1,40 @@
import { StyleOption } from '../../../types';
export const taxiCab: StyleOption = {
id: 'taxi-cab',
name: 'Taxi Cab',
category: 'Urban',
description: 'Yellow cab aesthetic. Checkerboard patterns (simulated) and bold black on yellow.',
vibe: 'Urban, Yellow, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Archivo+Black&family=Roboto:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Archivo Black", size: 36, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" },
border: {
top: { color: "000000", space: 4, style: "dashed", size: 24 }, // Simulates checkers
bottom: { color: "000000", space: 4, style: "dashed", size: 24 }
}
},
heading2: {
font: "Roboto", size: 18, color: "000000", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FFFFFF", color: "auto", style: "clear" }
},
body: {
font: "Roboto", size: 12, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFEB3B"
},
previewCss: `
font-family: 'Roboto', sans-serif;
background: #212121;
padding: 20px;
h1 { font-family: 'Archivo Black', sans-serif; font-size: 36pt; color: #000000; background: #FFEB3B; padding: 16px; margin-bottom: 24px; text-align: center; border-top: 6px dashed #000; border-bottom: 6px dashed #000; }
h2 { font-size: 18pt; font-weight: 700; color: #000000; background: #FFFFFF; padding: 8px; margin-top: 32px; margin-bottom: 16px; text-align: center; display: inline-block; }
p { font-size: 12pt; line-height: 1.5; color: #FFFFFF; margin-bottom: 14px; }
blockquote { background: #FFEB3B; color: #000; padding: 16px; margin: 24px 0; font-weight: 700; }
`
};

View File

@@ -0,0 +1,38 @@
import { StyleOption } from '../../../types';
export const varsityTeam: StyleOption = {
id: 'varsity-team',
name: 'Varsity Team',
category: 'Sport',
description: 'College sports jersey style. Block lettering with athletic gold and navy.',
vibe: 'Athletic, College, Bold',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Graduate&family=Saira:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Graduate", size: 36, color: "FDD835", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
shading: { fill: "1A237E", color: "auto", style: "clear" },
border: {
top: { color: "FDD835", space: 8, style: "single", size: 24 },
bottom: { color: "FDD835", space: 8, style: "single", size: 24 }
}
},
heading2: {
font: "Saira", size: 20, color: "1A237E", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Saira", size: 12, color: "212121", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FDD835"
},
previewCss: `
font-family: 'Saira', sans-serif;
h1 { font-family: 'Graduate', serif; font-size: 36pt; color: #FDD835; background: #1A237E; border-top: 4px solid #FDD835; border-bottom: 4px solid #FDD835; padding: 20px; text-align: center; margin-bottom: 24px; }
h2 { font-size: 20pt; font-weight: 700; color: #1A237E; text-align: center; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 12pt; line-height: 1.5; color: #212121; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px dashed #1A237E; padding: 16px; margin: 24px 0; background: #E8EAF6; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const annualReport: StyleOption = {
id: 'annual-report',
name: 'Annual Report',
category: 'Corporate',
description: 'Trustworthy and substantial. Deep navy blues, clean sans-serifs, and grid-like precision.',
vibe: 'Financial, Serious, Trust',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@400;700;900&family=Merriweather:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Libre Franklin", size: 32, color: "0D2B46", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Libre Franklin", size: 16, color: "A0A0A0", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Merriweather", size: 10, color: "333333", align: 'both',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "0D2B46"
},
previewCss: `
font-family: 'Merriweather', serif;
h1 { font-family: 'Libre Franklin', sans-serif; font-size: 32pt; font-weight: 900; color: #0D2B46; margin-bottom: 24px; }
h2 { font-family: 'Libre Franklin', sans-serif; font-size: 16pt; font-weight: 700; color: #A0A0A0; text-transform: uppercase; margin-top: 32px; margin-bottom: 16px; border-bottom: 1px solid #CCC; padding-bottom: 4px; }
p { font-size: 10pt; line-height: 1.8; color: #333333; margin-bottom: 14px; text-align: justify; }
blockquote { border-left: 4px solid #0D2B46; padding-left: 16px; margin: 24px 0; color: #0D2B46; font-style: italic; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const corporateExecutive: StyleOption = {
id: 'corporate-executive',
name: 'Corporate Executive',
category: 'Corporate',
description: 'Authoritative and professional. Designed for board reports, executive summaries, and high-stakes business communications.',
vibe: 'Executive, Formal, Trustworthy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Source+Serif+4:wght@400;600;700&family=Source+Sans+3:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Source Serif 4", size: 24, color: "1A237E", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Source Sans 3", size: 14, color: "303F9F", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Source Sans 3", size: 10, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "1A237E"
},
previewCss: `
font-family: 'Source Sans 3', sans-serif;
h1 { font-family: 'Source Serif 4', serif; font-size: 24pt; font-weight: 700; color: #1A237E; margin-bottom: 24px; border-bottom: 2px solid #1A237E; padding-bottom: 8px; }
h2 { font-size: 14pt; font-weight: 600; color: #303F9F; margin-top: 30px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { border-left: 4px solid #1A237E; padding-left: 16px; color: #1A237E; margin: 20px 0; font-style: italic; }
`
};

View File

@@ -0,0 +1,35 @@
import { StyleOption } from '../../../types';
export const creditCardPlatinum: StyleOption = {
id: 'credit-card-platinum',
name: 'Credit Card Platinum',
category: 'Financial',
description: 'Premium card aesthetic. Silver/gradient shading with OCR-style raised numbering fonts.',
vibe: 'Premium, Silver, Metallic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Krub:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Krub", size: 32, color: "E0E0E0", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "424242", color: "auto", style: "clear" }
},
heading2: {
font: "Share Tech Mono", size: 18, color: "C0C0C0", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true,
tracking: 100
},
body: {
font: "Krub", size: 11, color: "616161", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "C0C0C0"
},
previewCss: `
font-family: 'Krub', sans-serif;
h1 { font-size: 32pt; font-weight: 700; color: #E0E0E0; background: linear-gradient(135deg, #616161, #212121); padding: 16px; margin-bottom: 24px; border-radius: 12px; }
h2 { font-family: 'Share Tech Mono', monospace; font-size: 18pt; color: #757575; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 3px; text-shadow: 1px 1px 0 #FFF; }
p { font-size: 11pt; line-height: 1.6; color: #616161; margin-bottom: 14px; }
blockquote { border-left: 4px solid #C0C0C0; padding-left: 16px; margin: 24px 0; background: #F5F5F5; }
`
};

View File

@@ -0,0 +1,38 @@
import { StyleOption } from '../../../types';
export const currencyBill: StyleOption = {
id: 'currency-bill',
name: 'Currency Bill',
category: 'Official',
description: 'Banknote aesthetic. Intricate guilloche-style borders and emerald green serif fonts.',
vibe: 'Financial, Secure, Green',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display+SC:wght@700&family=Noto+Serif:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display SC", size: 36, color: "1B5E20", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "2E7D32", space: 6, style: "double", size: 12 },
bottom: { color: "2E7D32", space: 6, style: "double", size: 12 }
}
},
heading2: {
font: "Noto Serif", size: 16, color: "388E3C", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Noto Serif", size: 11, color: "1B5E20", align: 'both',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2E7D32"
},
previewCss: `
font-family: 'Noto Serif', serif;
background: #E8F5E9;
h1 { font-family: 'Playfair Display SC', serif; font-size: 36pt; font-weight: 700; color: #1B5E20; text-align: center; border-top: 4px double #2E7D32; border-bottom: 4px double #2E7D32; padding: 12px 0; margin-bottom: 24px; letter-spacing: 2px; }
h2 { font-size: 16pt; font-weight: 700; color: #388E3C; text-align: center; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #1B5E20; margin-bottom: 14px; text-align: justify; }
blockquote { border: 1px solid #2E7D32; padding: 20px; margin: 24px 0; background: #C8E6C9; text-align: center; border-radius: 50%; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; margin: 0 auto; }
`
};

View File

@@ -0,0 +1,30 @@
import { StyleOption } from '../../../types';
import { annualReport } from './annual-report';
import { corporateExecutive } from './corporate-executive';
import { creditCardPlatinum } from './credit-card-platinum';
import { currencyBill } from './currency-bill';
import { legalContract } from './legal-contract';
import { legalPleading } from './legal-pleading';
import { passportOfficial } from './passport-official';
import { policeBlotter } from './police-blotter';
import { politicalCampaign } from './political-campaign';
import { stockTicker } from './stock-ticker';
import { techMemo } from './tech-memo';
import { topSecretRedacted } from './top-secret-redacted';
import { whiteboardStrategy } from './whiteboard-strategy';
export const corporateStyles: StyleOption[] = [
annualReport,
corporateExecutive,
creditCardPlatinum,
currencyBill,
legalContract,
legalPleading,
passportOfficial,
policeBlotter,
politicalCampaign,
stockTicker,
techMemo,
topSecretRedacted,
whiteboardStrategy
];

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const legalContract: StyleOption = {
id: 'legal-contract',
name: 'Legal Contract',
category: 'Corporate',
description: 'Binding agreement style. Serif fonts, justified text, and numbered section aesthetics.',
vibe: 'Legal, Binding, Formal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Tinos:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Tinos", size: 14, color: "000000", bold: true, align: 'center',
spacing: { before: 240, after: 240, line: 240 },
allCaps: true
},
heading2: {
font: "Tinos", size: 12, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 },
underline: true
},
body: {
font: "Tinos", size: 11, color: "000000", align: 'both',
spacing: { before: 0, after: 120, line: 240 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Tinos', serif;
h1 { font-size: 14pt; font-weight: 700; color: #000000; text-align: center; text-transform: uppercase; margin-bottom: 16px; }
h2 { font-size: 12pt; font-weight: 700; color: #000000; text-decoration: underline; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 11pt; line-height: 1.4; color: #000000; margin-bottom: 12px; text-align: justify; }
blockquote { margin: 20px 40px; border: 1px solid #000; padding: 10px; }
`
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
export const legalPleading: StyleOption = {
id: 'legal-pleading',
name: 'Legal Pleading',
category: 'Professional',
description: 'Courtroom document style. Rigid framing, double spacing, and traditional serif fonts.',
vibe: 'Legal, Strict, Formal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=EB+Garamond:wght@400;700&family=Courier+Prime&display=swap',
wordConfig: {
heading1: {
font: "EB Garamond", size: 14, color: "000000", bold: true, align: 'center',
spacing: { before: 240, after: 240, line: 240 },
allCaps: true,
border: { bottom: { color: "000000", space: 4, style: "single", size: 4 } }
},
heading2: {
font: "EB Garamond", size: 12, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 }
},
body: {
font: "Courier Prime", size: 12, color: "000000", align: 'both',
spacing: { before: 0, after: 0, line: 480 },
border: { left: { color: "BDBDBD", space: 12, style: "single", size: 4 } }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
line-height: 2.0;
h1 { font-family: 'EB Garamond', serif; font-size: 14pt; font-weight: 700; color: #000000; text-align: center; text-decoration: underline; text-transform: uppercase; margin-bottom: 24px; }
h2 { font-family: 'EB Garamond', serif; font-size: 12pt; font-weight: 700; color: #000000; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 12pt; color: #000000; margin-bottom: 0px; text-align: justify; border-left: 1px solid #BDBDBD; padding-left: 15px; }
blockquote { margin-left: 40px; border-left: 1px solid #BDBDBD; padding-left: 15px; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const passportOfficial: StyleOption = {
id: 'passport-official',
name: 'Passport Official',
category: 'Government',
description: 'International travel document aesthetic. Machine-readable fonts with secure, bureaucratic vibes.',
vibe: 'Official, Secure, Monospace',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Martian+Mono:wght@400;700&family=Courier+Prime&display=swap',
wordConfig: {
heading1: {
font: "Martian Mono", size: 22, color: "1A237E", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 240 }
},
heading2: {
font: "Martian Mono", size: 12, color: "B71C1C", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 },
allCaps: true
},
body: {
font: "Courier Prime", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 140, line: 260 }
},
accentColor: "B71C1C"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-family: 'Martian Mono', monospace; font-size: 22pt; font-weight: 700; color: #1A237E; margin-bottom: 24px; letter-spacing: -1px; }
h2 { font-family: 'Martian Mono', monospace; font-size: 12pt; font-weight: 700; color: #B71C1C; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.5; color: #212121; margin-bottom: 12px; }
blockquote { border: 1px dashed #1A237E; padding: 16px; margin: 20px 0; background: #E8EAF6; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const policeBlotter: StyleOption = {
id: 'police-blotter',
name: 'Police Blotter',
category: 'Official',
description: 'Law enforcement paperwork. Carbon copy blue fonts on stark white forms.',
vibe: 'Official, Blue, Bureaucratic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Courier+Prime&family=Special+Elite&display=swap',
wordConfig: {
heading1: {
font: "Special Elite", size: 26, color: "0D47A1", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
allCaps: true
},
heading2: {
font: "Courier Prime", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "E3F2FD", color: "auto", style: "clear" }
},
body: {
font: "Courier Prime", size: 11, color: "1A237E", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "0D47A1"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-family: 'Special Elite', cursive; font-size: 26pt; color: #0D47A1; margin-bottom: 24px; text-transform: uppercase; text-decoration: underline; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; background: #E3F2FD; display: block; padding: 4px; margin-top: 32px; margin-bottom: 16px; border: 1px solid #0D47A1; }
p { font-size: 11pt; line-height: 1.5; color: #1A237E; margin-bottom: 14px; }
blockquote { border: 1px solid #000; padding: 16px; margin: 24px 0; background: #FAFAFA; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const politicalCampaign: StyleOption = {
id: 'political-campaign',
name: 'Political Campaign',
category: 'Professional',
description: 'Bold, patriotic design for campaigns. Strong headers with red, white, and blue motifs.',
vibe: 'Patriotic, Bold, Official',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Francois+One&family=Cabin:wght@400;600;700&display=swap',
wordConfig: {
heading1: {
font: "Francois One", size: 36, color: "0D47A1", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "D32F2F", space: 8, style: "single", size: 12 } }
},
heading2: {
font: "Cabin", size: 16, color: "D32F2F", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Cabin", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Cabin', sans-serif;
h1 { font-family: 'Francois One', sans-serif; font-size: 36pt; color: #0D47A1; border-bottom: 4px solid #D32F2F; padding-bottom: 12px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 16pt; font-weight: 700; color: #D32F2F; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { background: #E3F2FD; padding: 20px; border-left: 8px solid #0D47A1; margin: 24px 0; }
`
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
export const stockTicker: StyleOption = {
id: 'stock-ticker',
name: 'Stock Ticker',
category: 'Financial',
description: 'LED moving message sign. Dot matrix fonts with bright green/red on black.',
vibe: 'Financial, Digital, LED',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=DotGothic16&display=swap',
wordConfig: {
heading1: {
font: "DotGothic16", size: 32, color: "00E676", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
heading2: {
font: "DotGothic16", size: 18, color: "FF1744", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
body: {
font: "DotGothic16", size: 12, color: "E0E0E0", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "212121", color: "auto", style: "clear" }
},
accentColor: "00E676"
},
previewCss: `
font-family: 'DotGothic16', sans-serif;
background: #212121;
h1 { font-size: 32pt; color: #00E676; background: #000000; padding: 12px; margin-bottom: 24px; border-bottom: 2px solid #333; }
h2 { font-size: 18pt; color: #FF1744; background: #000000; padding: 8px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 12pt; line-height: 1.5; color: #E0E0E0; margin-bottom: 14px; }
blockquote { border-left: 4px solid #00E676; padding-left: 16px; margin: 24px 0; color: #B9F6CA; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const techMemo: StyleOption = {
id: 'tech-memo',
name: 'Tech Memorandum',
category: 'Corporate',
description: 'Modern, functional, and authoritative. Uses shading for headers to create distinct sections.',
vibe: 'Corporate, Tech, Strategy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "Roboto", size: 24, color: "0F172A", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "F1F5F9", color: "auto", style: "clear" },
border: { left: { color: "2563EB", space: 10, style: "single", size: 48 } }
},
heading2: {
font: "Roboto", size: 14, color: "2563EB", bold: true, align: 'left',
spacing: { before: 300, after: 150, line: 240 }
},
body: {
font: "Roboto", size: 10, color: "334155", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "2563EB"
},
previewCss: `
font-family: 'Roboto', sans-serif;
h1 { font-size: 24pt; font-weight: 700; background: #F1F5F9; color: #0F172A; padding: 12px 16px; border-left: 8px solid #2563EB; margin-bottom: 20px; }
h2 { font-size: 14pt; font-weight: 700; color: #2563EB; margin-top: 30px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #334155; margin-bottom: 14px; }
blockquote { background: #EFF6FF; padding: 16px; border-radius: 4px; color: #1E40AF; border-left: 4px solid #2563EB; margin: 20px 0; }
`
};

View File

@@ -0,0 +1,39 @@
import { StyleOption } from '../../../types';
export const topSecretRedacted: StyleOption = {
id: 'top-secret-redacted',
name: 'Top Secret Redacted',
category: 'Government',
description: 'Declassified document style. Typewriter fonts with "blacked out" highlight effects.',
vibe: 'Secret, Classified, Rough',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Special+Elite&family=Courier+Prime:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Special Elite", size: 28, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
border: {
top: { color: "000000", space: 6, style: "single", size: 24 },
bottom: { color: "000000", space: 6, style: "single", size: 24 }
}
},
heading2: {
font: "Courier Prime", size: 14, color: "B71C1C", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
body: {
font: "Courier Prime", size: 11, color: "000000", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
background: #F5F5F5;
h1 { font-family: 'Special Elite', cursive; font-size: 28pt; color: #000000; border-top: 4px solid #000; border-bottom: 4px solid #000; padding: 16px 0; text-align: center; margin-bottom: 28px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #B71C1C; background: #000000; display: inline-block; padding: 4px 12px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.6; color: #000000; margin-bottom: 14px; }
blockquote { background: #000; color: #000; padding: 10px; margin: 20px 0; user-select: none; }
blockquote:hover { color: #FFF; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const whiteboardStrategy: StyleOption = {
id: 'whiteboard-strategy',
name: 'Whiteboard Strategy',
category: 'Business',
description: 'Brainstorming session. Dry-erase marker fonts in standard office colors (Black, Blue, Red).',
vibe: 'Corporate, Brainstorm, Sketch',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Patrick+Hand&display=swap',
wordConfig: {
heading1: {
font: "Architects Daughter", size: 36, color: "D32F2F", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "1976D2", space: 4, style: "single", size: 12 } }
},
heading2: {
font: "Patrick Hand", size: 20, color: "1976D2", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Patrick Hand", size: 14, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Patrick Hand', cursive;
background: #FFFFFF;
h1 { font-family: 'Architects Daughter', cursive; font-size: 36pt; font-weight: 700; color: #D32F2F; border-bottom: 3px solid #1976D2; padding-bottom: 10px; margin-bottom: 24px; text-align: center; }
h2 { font-size: 20pt; font-weight: 700; color: #1976D2; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.5; color: #212121; margin-bottom: 14px; }
blockquote { border: 2px solid #388E3C; padding: 16px; margin: 24px 0; border-radius: 16px; color: #388E3C; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const bauhausPoster: StyleOption = {
id: 'bauhaus-poster',
name: 'Bauhaus Poster',
category: 'Creative',
description: 'Geometric minimalism. Primary colors, angled text, and strong diagonal compositions.',
vibe: 'Artistic, Geometric, Modernist',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@400;700&family=Jost:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Josefin Sans", size: 36, color: "D50000", bold: true, align: 'right',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "000000", space: 8, style: "single", size: 36 } }
},
heading2: {
font: "Jost", size: 16, color: "2962FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" }
},
body: {
font: "Jost", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D50000"
},
previewCss: `
font-family: 'Jost', sans-serif;
h1 { font-family: 'Josefin Sans', sans-serif; font-size: 36pt; font-weight: 700; color: #D50000; text-align: right; border-bottom: 8px solid #000; padding-bottom: 10px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 16pt; font-weight: 700; color: #2962FF; background: #FFEB3B; padding: 8px 16px; margin-top: 32px; margin-bottom: 16px; display: inline-block; transform: rotate(-1deg); }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border-left: 10px solid #D50000; padding-left: 20px; margin: 24px 0; font-style: italic; }
`
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
export const blueprintCyanotype: StyleOption = {
id: 'blueprint-cyanotype',
name: 'Blueprint Cyanotype',
category: 'Creative',
description: 'Architectural blueprint style. White lines on a deep cyan blue background.',
vibe: 'Technical, Blue, Schematic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Roboto+Mono:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Architects Daughter", size: 32, color: "FFFFFF", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "FFFFFF", space: 4, style: "single", size: 12 } }
},
heading2: {
font: "Roboto Mono", size: 14, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true
},
body: {
font: "Roboto Mono", size: 10, color: "E0F7FA", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FFFFFF"
},
previewCss: `
font-family: 'Roboto Mono', monospace;
background: #0047AB; /* Cobalt/Blueprint Blue */
color: #FFFFFF;
h1 { font-family: 'Architects Daughter', cursive; font-size: 32pt; font-weight: 700; color: #FFFFFF; text-align: center; border-bottom: 2px solid #FFFFFF; margin-bottom: 24px; padding-bottom: 8px; }
h2 { font-size: 14pt; font-weight: 700; color: #FFFFFF; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; border: 1px solid #FFF; display: inline-block; padding: 4px; }
p { font-size: 10pt; line-height: 1.6; color: #E0F7FA; margin-bottom: 14px; }
blockquote { border: 1px dashed #FFFFFF; padding: 16px; margin: 24px 0; background: #003380; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const brickToy: StyleOption = {
id: 'brick-toy',
name: 'Brick Toy',
category: 'Playful',
description: 'Plastic building block aesthetic. Primary red, blue, and yellow with chunky text.',
vibe: 'Playful, Primary, Chunky',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Titan+One&family=Nunito:wght@400;800&display=swap',
wordConfig: {
heading1: {
font: "Titan One", size: 36, color: "D32F2F", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFEB3B", color: "auto", style: "clear" } // Yellow background
},
heading2: {
font: "Nunito", size: 18, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "1976D2", color: "auto", style: "clear" } // Blue background
},
body: {
font: "Nunito", size: 12, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D32F2F"
},
previewCss: `
font-family: 'Nunito', sans-serif;
h1 { font-family: 'Titan One', cursive; font-size: 36pt; color: #D32F2F; background: #FFEB3B; padding: 16px; border-radius: 8px; text-align: center; margin-bottom: 24px; border: 4px solid #D32F2F; }
h2 { font-size: 18pt; font-weight: 800; color: #FFFFFF; background: #1976D2; padding: 8px 16px; border-radius: 4px; display: inline-block; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 4px dotted #D32F2F; padding: 16px; margin: 24px 0; background: #E3F2FD; }
`
};

View File

@@ -0,0 +1,39 @@
import { StyleOption } from '../../../types';
export const brutalistMono: StyleOption = {
id: 'brutalist-mono',
name: 'Brutalist Lab',
category: 'Creative',
description: 'Raw, monospaced aesthetic. Boxes, thick outlines, and typewriter vibes.',
vibe: 'Code, Industrial, Raw',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Space Mono", size: 24, color: "000000", bold: true, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
border: {
top: { color: "000000", space: 12, style: "single", size: 24 },
bottom: { color: "000000", space: 12, style: "single", size: 24 },
left: { color: "000000", space: 12, style: "single", size: 24 },
right: { color: "000000", space: 12, style: "single", size: 24 }
}
},
heading2: {
font: "Space Mono", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "E0E0E0", color: "auto", style: "clear" }
},
body: {
font: "Space Mono", size: 10, color: "111111", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Space Mono', monospace;
h1 { font-size: 24pt; font-weight: 700; color: #000000; border: 4px solid #000; padding: 16px; margin-bottom: 24px; text-transform: uppercase; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; background: #E0E0E0; padding: 8px; margin-top: 32px; margin-bottom: 16px; display: inline-block; }
p { font-size: 10pt; line-height: 1.6; color: #111111; margin-bottom: 14px; }
blockquote { border-left: 6px solid #000; padding-left: 16px; margin: 24px 0; font-weight: 700; }
`
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
export const glitchArt: StyleOption = {
id: 'glitch-art',
name: 'Glitch Art',
category: 'Creative',
description: 'Digital distortion aesthetic. Monospaced fonts with neon colors and "broken" styling.',
vibe: 'Digital, Glitch, Cyber',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Rubik+Glitch&family=Share+Tech+Mono&display=swap',
wordConfig: {
heading1: {
font: "Rubik Glitch", size: 40, color: "D500F9", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "121212", color: "auto", style: "clear" }
},
heading2: {
font: "Share Tech Mono", size: 18, color: "00E5FF", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Share Tech Mono", size: 11, color: "BDBDBD", align: 'left',
spacing: { before: 0, after: 160, line: 280 },
shading: { fill: "000000", color: "auto", style: "clear" }
},
accentColor: "00E5FF"
},
previewCss: `
font-family: 'Share Tech Mono', monospace;
background: #000000;
color: #BDBDBD;
h1 { font-family: 'Rubik Glitch', cursive; font-size: 40pt; color: #D500F9; background: #121212; padding: 10px; margin-bottom: 24px; text-shadow: 2px 0 #00E5FF; }
h2 { font-size: 18pt; font-weight: 700; color: #00E5FF; margin-top: 32px; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 11pt; line-height: 1.5; color: #BDBDBD; margin-bottom: 14px; }
blockquote { border: 1px solid #D500F9; padding: 16px; margin: 24px 0; color: #D500F9; box-shadow: 4px 4px 0 #00E5FF; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const grunge90s: StyleOption = {
id: 'grunge-90s',
name: 'Grunge 90s',
category: 'Creative',
description: 'Distressed and edgy. David Carson inspired typography with overlapping elements and grit.',
vibe: 'Dirty, Edgy, Chaotic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Special+Elite&display=swap',
wordConfig: {
heading1: {
font: "Permanent Marker", size: 36, color: "333333", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Special Elite", size: 18, color: "8B0000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "dashed", size: 12 } }
},
body: {
font: "Special Elite", size: 11, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "8B0000"
},
previewCss: `
font-family: 'Special Elite', cursive;
background-color: #F0F0F0;
h1 { font-family: 'Permanent Marker', cursive; font-size: 36pt; color: #333333; text-align: center; margin-bottom: 24px; transform: rotate(1deg); opacity: 0.9; }
h2 { font-size: 18pt; font-weight: 700; color: #8B0000; margin-top: 32px; margin-bottom: 16px; border-bottom: 2px dashed #000; display: inline-block; transform: skewX(-5deg); }
p { font-size: 11pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 3px solid #000; padding: 16px; margin: 24px 0; background: #D3D3D3; transform: rotate(-1deg); }
`
};

View File

@@ -0,0 +1,30 @@
import { StyleOption } from '../../../types';
import { bauhausPoster } from './bauhaus-poster';
import { blueprintCyanotype } from './blueprint-cyanotype';
import { brickToy } from './brick-toy';
import { brutalistMono } from './brutalist-mono';
import { glitchArt } from './glitch-art';
import { grunge90s } from './grunge-90s';
import { kindergartenArt } from './kindergarten-art';
import { origamiPaper } from './origami-paper';
import { popArtComic } from './pop-art-comic';
import { risographPrint } from './risograph-print';
import { streetArtGraffiti } from './street-art-graffiti';
import { vaporwaveAesthetic } from './vaporwave-aesthetic';
import { watercolorWash } from './watercolor-wash';
export const creativeStyles: StyleOption[] = [
bauhausPoster,
blueprintCyanotype,
brickToy,
brutalistMono,
glitchArt,
grunge90s,
kindergartenArt,
origamiPaper,
popArtComic,
risographPrint,
streetArtGraffiti,
vaporwaveAesthetic,
watercolorWash
];

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const kindergartenArt: StyleOption = {
id: 'kindergarten-art',
name: 'Kindergarten Art',
category: 'Playful',
description: 'Child-like crayon aesthetic. Primary colors, rounded fonts, and large friendly text.',
vibe: 'Childish, Fun, Primary',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Chewy&family=Balsamiq+Sans:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Chewy", size: 40, color: "2962FF", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Balsamiq Sans", size: 18, color: "D50000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Balsamiq Sans", size: 14, color: "111111", align: 'left',
spacing: { before: 0, after: 160, line: 320 }
},
accentColor: "FFD600"
},
previewCss: `
font-family: 'Balsamiq Sans', cursive;
h1 { font-family: 'Chewy', cursive; font-size: 40pt; color: #2962FF; text-align: center; margin-bottom: 24px; transform: rotate(-2deg); }
h2 { font-size: 18pt; font-weight: 700; color: #D50000; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 14pt; line-height: 1.6; color: #111111; margin-bottom: 14px; }
blockquote { border: 4px dashed #FFD600; padding: 20px; margin: 24px 0; border-radius: 20px; background: #FFFDE7; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const origamiPaper: StyleOption = {
id: 'origami-paper',
name: 'Origami Paper',
category: 'Creative',
description: 'Folded paper aesthetic. Sharp angles, subtle shadows, and crisp typography.',
vibe: 'Delicate, Sharp, Paper',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cormorant+Unicase:wght@400;700&family=Muli:wght@300;400&display=swap',
wordConfig: {
heading1: {
font: "Cormorant Unicase", size: 32, color: "37474F", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Cormorant Unicase", size: 16, color: "546E7A", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Muli", size: 10, color: "455A64", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "37474F"
},
previewCss: `
font-family: 'Muli', sans-serif;
background: #FAFAFA;
h1 { font-family: 'Cormorant Unicase', serif; font-size: 32pt; font-weight: 700; color: #37474F; text-align: center; margin-bottom: 24px; text-shadow: 1px 1px 0 #CCC; }
h2 { font-family: 'Cormorant Unicase', serif; font-size: 16pt; color: #546E7A; margin-top: 32px; margin-bottom: 16px; border-bottom: 1px solid #CFD8DC; display: inline-block; padding-bottom: 4px; }
p { font-size: 10pt; line-height: 1.7; color: #455A64; margin-bottom: 14px; }
blockquote { background: #FFF; padding: 20px; border: 1px solid #ECEFF1; box-shadow: 2px 2px 5px rgba(0,0,0,0.05); margin: 24px 0; transform: rotate(1deg); }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const popArtComic: StyleOption = {
id: 'pop-art-comic',
name: 'Pop Art Comic',
category: 'Creative',
description: 'Roy Lichtenstein inspired. Bold black outlines, halftone vibes, and primary colors.',
vibe: 'Comic, Bold, Pop',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Bangers", size: 42, color: "000000", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFFF00", color: "auto", style: "clear" } // Yellow back
},
heading2: {
font: "Bangers", size: 24, color: "FFFFFF", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "FF0000", color: "auto", style: "clear" } // Red back
},
body: {
font: "Comic Neue", size: 12, color: "000000", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Comic Neue', sans-serif;
h1 { font-family: 'Bangers', cursive; font-size: 42pt; color: #000000; background: #FFFF00; padding: 10px; border: 4px solid #000; box-shadow: 5px 5px 0 #000; margin-bottom: 24px; display: inline-block; }
h2 { font-family: 'Bangers', cursive; font-size: 24pt; color: #FFFFFF; background: #FF0000; padding: 5px 15px; border: 3px solid #000; margin-top: 32px; margin-bottom: 16px; display: inline-block; transform: rotate(-2deg); }
p { font-size: 12pt; line-height: 1.5; color: #000000; margin-bottom: 14px; font-weight: 700; }
blockquote { background: #FFFFFF; border: 3px solid #000; padding: 20px; border-radius: 50% / 20%; margin: 24px 0; text-align: center; }
`
};

View File

@@ -0,0 +1,35 @@
import { StyleOption } from '../../../types';
export const risographPrint: StyleOption = {
id: 'risograph-print',
name: 'Risograph Print',
category: 'Creative',
description: 'Independent publishing style. Grainy textures, misalignment, and vibrant spot colors (Pink/Teal).',
vibe: 'Artistic, Indie, Textured',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Eb+Garamond:ital,wght@0,400;0,700;1,400&family=Karla:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Eb Garamond", size: 42, color: "FF007F", bold: true, align: 'left', // Riso Pink
spacing: { before: 400, after: 200, line: 200 },
italic: true
},
heading2: {
font: "Karla", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
shading: { fill: "00A9FF", color: "auto", style: "clear" } // Riso Blue background
},
body: {
font: "Karla", size: 10, color: "1A1A1A", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "FF007F"
},
previewCss: `
font-family: 'Karla', sans-serif;
background: #FFFEF0;
h1 { font-family: 'Eb Garamond', serif; font-size: 42pt; font-weight: 700; font-style: italic; color: #FF007F; margin-bottom: 24px; mix-blend-mode: multiply; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; background: #00A9FF; display: inline-block; padding: 4px 10px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #1A1A1A; margin-bottom: 14px; }
blockquote { border-left: 6px solid #FF007F; padding-left: 16px; margin: 24px 0; color: #555; font-style: italic; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const streetArtGraffiti: StyleOption = {
id: 'street-art-graffiti',
name: 'Street Art Graffiti',
category: 'Creative',
description: 'Urban street style. Spray paint fonts, dripping effects, and concrete vibes.',
vibe: 'Urban, Rebel, Spray',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Rock+Salt&display=swap',
wordConfig: {
heading1: {
font: "Permanent Marker", size: 48, color: "000000", bold: false, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "FFFF00", color: "auto", style: "clear" } // Hazard Yellow
},
heading2: {
font: "Rock Salt", size: 18, color: "FF0000", bold: false, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Permanent Marker", size: 12, color: "333333", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Permanent Marker', cursive;
background: #F0F0F0;
h1 { font-size: 48pt; color: #000000; text-align: center; background: #FFFF00; padding: 10px; transform: rotate(-2deg); margin-bottom: 24px; box-shadow: 4px 4px 0 #000; }
h2 { font-family: 'Rock Salt', cursive; font-size: 18pt; color: #FF0000; margin-top: 32px; margin-bottom: 16px; transform: rotate(1deg); }
p { font-size: 12pt; line-height: 1.6; color: #333333; margin-bottom: 14px; }
blockquote { border-left: 5px solid #000; padding-left: 16px; margin: 24px 0; font-family: 'Rock Salt', cursive; font-size: 10pt; }
`
};

View File

@@ -0,0 +1,35 @@
import { StyleOption } from '../../../types';
export const vaporwaveAesthetic: StyleOption = {
id: 'vaporwave-aesthetic',
name: 'Vaporwave Aesthetic',
category: 'Creative',
description: 'Nostalgic 80s/90s internet culture. Pink and cyan gradients, wide tracking, and ironic luxury.',
vibe: 'Nostalgic, Digital, Surreal',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Varela+Round&family=Montserrat:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Montserrat", size: 32, color: "FF71CE", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
allCaps: true,
tracking: 100 // Wide spacing
},
heading2: {
font: "Varela Round", size: 16, color: "01CDFE", bold: false, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Montserrat", size: 10, color: "B967FF", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "01CDFE"
},
previewCss: `
font-family: 'Montserrat', sans-serif;
background: #F0F0FF;
h1 { font-size: 32pt; font-weight: 700; color: #FF71CE; text-align: center; margin-bottom: 24px; letter-spacing: 4px; text-shadow: 2px 2px #01CDFE; text-transform: uppercase; }
h2 { font-family: 'Varela Round', sans-serif; font-size: 16pt; color: #01CDFE; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #B967FF; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px solid #FF71CE; padding: 16px; margin: 24px 0; background: #E0FFFF; color: #01CDFE; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const watercolorWash: StyleOption = {
id: 'watercolor-wash',
name: 'Watercolor Wash',
category: 'Creative',
description: 'Artistic and fluid. Soft blended colors and painterly fonts.',
vibe: 'Artistic, Soft, Fluid',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Kalam:wght@400;700&family=Amatic+SC:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Kalam", size: 36, color: "7986CB", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Amatic SC", size: 24, color: "4DB6AC", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Kalam", size: 12, color: "616161", align: 'center',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "7986CB"
},
previewCss: `
font-family: 'Kalam', cursive;
h1 { font-size: 36pt; font-weight: 700; color: #7986CB; text-align: center; margin-bottom: 24px; text-shadow: 2px 2px 4px rgba(121, 134, 203, 0.3); }
h2 { font-family: 'Amatic SC', cursive; font-size: 24pt; font-weight: 700; color: #4DB6AC; text-align: center; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 12pt; line-height: 1.6; color: #616161; margin-bottom: 14px; text-align: center; }
blockquote { border: 2px solid #E0F2F1; background: #E8EAF6; padding: 20px; border-radius: 50% 20% / 10% 40%; margin: 24px 0; text-align: center; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const artNouveauOrganic: StyleOption = {
id: 'art-nouveau-organic',
name: 'Art Nouveau Organic',
category: 'Classic',
description: 'Inspired by the Art Nouveau movement (1890-1910). Flowing organic lines with nature-inspired elegance and whiplash curves.',
vibe: 'Organic, Flowing, Decorative',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Sorts+Mill+Goudy:wght@400&family=Fanwood+Text:wght@400&display=swap',
wordConfig: {
heading1: {
font: "Sorts Mill Goudy", size: 32, color: "4A5D23", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 },
border: { bottom: { color: "8FA876", space: 10, style: "single", size: 8 } }
},
heading2: {
font: "Sorts Mill Goudy", size: 16, color: "6B7F4A", bold: false, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Fanwood Text", size: 11, color: "3D4A2D", align: 'both',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "8FA876"
},
previewCss: `
font-family: 'Fanwood Text', serif;
h1 { font-family: 'Sorts Mill Goudy', serif; font-size: 32pt; color: #4A5D23; text-align: center; border-bottom: 2px solid #8FA876; padding-bottom: 16px; margin-bottom: 32px; }
h2 { font-family: 'Sorts Mill Goudy', serif; font-size: 16pt; color: #6B7F4A; margin-top: 36px; margin-bottom: 18px; font-style: italic; }
p { font-size: 11pt; line-height: 1.8; color: #3D4A2D; margin-bottom: 16px; text-align: justify; }
blockquote { background: #F4F7F0; padding: 20px; border-left: 3px solid #8FA876; margin: 28px 0; font-style: italic; }
`
};

View File

@@ -0,0 +1,35 @@
import { StyleOption } from '../../../types';
export const artsCraftsHeritage: StyleOption = {
id: 'arts-crafts-heritage',
name: 'Arts & Crafts Heritage',
category: 'Classic',
description: 'Inspired by William Morris and the Arts & Crafts movement (1880s-1900). Emphasizes craftsmanship, natural forms, and medieval aesthetics with readable typography.',
vibe: 'Artisan, Literary, Handcrafted',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;600;700&family=EB+Garamond:wght@400;500&display=swap',
wordConfig: {
heading1: {
font: "Cormorant Garamond", size: 28, color: "2F4F4F", bold: true, align: 'center',
spacing: { before: 400, after: 240, line: 240 },
border: {
bottom: { color: "556B2F", space: 8, style: "single", size: 8 }
}
},
heading2: {
font: "Cormorant Garamond", size: 16, color: "556B2F", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "EB Garamond", size: 11, color: "3C3C3C", align: 'both',
spacing: { before: 0, after: 160, line: 320 }
},
accentColor: "556B2F"
},
previewCss: `
font-family: 'EB Garamond', serif;
h1 { font-family: 'Cormorant Garamond', serif; font-size: 28pt; font-weight: 700; color: #2F4F4F; text-align: center; border-bottom: 2px solid #556B2F; padding-bottom: 16px; margin-bottom: 28px; }
h2 { font-family: 'Cormorant Garamond', serif; font-size: 16pt; font-weight: 700; color: #556B2F; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 11pt; line-height: 1.8; color: #3C3C3C; margin-bottom: 14px; text-align: justify; }
blockquote { background: #F5F5DC; padding: 20px; border-left: 4px solid #556B2F; margin: 24px 0; font-style: italic; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const baroqueSplendor: StyleOption = {
id: 'baroque-splendor',
name: 'Baroque Splendor',
category: 'Classic',
description: 'Heavy, dramatic 17th-century luxury. Deep golds and crimsons with deeply flourished script.',
vibe: 'Luxurious, Dramatic, Heavy',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Mrs+Saint+Delafield&family=Mate:wght@400;500&display=swap',
wordConfig: {
heading1: {
font: "Mrs Saint Delafield", size: 48, color: "800000", bold: false, align: 'center',
spacing: { before: 520, after: 320, line: 240 }
},
heading2: {
font: "Mate", size: 16, color: "B8860B", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
body: {
font: "Mate", size: 12, color: "3D2B1F", align: 'both',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "B8860B"
},
previewCss: `
font-family: 'Mate', serif;
h1 { font-family: 'Mrs Saint Delafield', cursive; font-size: 48pt; color: #800000; text-align: center; margin-bottom: 36px; }
h2 { font-size: 16pt; font-weight: 700; color: #B8860B; text-align: center; margin-top: 40px; margin-bottom: 20px; text-transform: uppercase; letter-spacing: 2px; }
p { font-size: 12pt; line-height: 1.8; color: #3D2B1F; margin-bottom: 16px; text-align: justify; }
blockquote { background: #FFF8E7; border: 2px double #B8860B; padding: 24px; margin: 32px 0; text-align: center; font-style: italic; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const cottagecoreJournal: StyleOption = {
id: 'cottagecore-journal',
name: 'Cottagecore Journal',
category: 'Aesthetic',
description: 'Whimsical countryside diary. Handwritten fonts with soft sage greens and earthy browns.',
vibe: 'Whimsical, Nature, Handmade',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Amatic+SC:wght@400;700&family=Patrick+Hand&display=swap',
wordConfig: {
heading1: {
font: "Amatic SC", size: 40, color: "33691E", bold: true, align: 'center',
spacing: { before: 400, after: 200, line: 240 }
},
heading2: {
font: "Patrick Hand", size: 18, color: "5D4037", bold: true, align: 'center',
spacing: { before: 320, after: 160, line: 240 }
},
body: {
font: "Patrick Hand", size: 14, color: "424242", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "33691E"
},
previewCss: `
font-family: 'Patrick Hand', cursive;
background: #F1F8E9;
h1 { font-family: 'Amatic SC', cursive; font-size: 40pt; font-weight: 700; color: #33691E; text-align: center; margin-bottom: 24px; }
h2 { font-size: 18pt; font-weight: 700; color: #5D4037; text-align: center; margin-top: 30px; margin-bottom: 14px; border-bottom: 1px dashed #8D6E63; display: inline-block; }
p { font-size: 14pt; line-height: 1.6; color: #424242; margin-bottom: 14px; }
blockquote { background: #FFF; border: 1px dotted #33691E; padding: 20px; border-radius: 10px; margin: 24px 0; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const dutchGoldenAge: StyleOption = {
id: 'dutch-golden-age',
name: 'Dutch Golden Age',
category: 'Classic',
description: 'Inspired by 17th century Dutch art and typography. Rich, painterly aesthetics with Rembrandt-era elegance.',
vibe: 'Classical, Rich, Painterly',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Pirata+One&family=Gentium+Book+Plus:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Pirata One", size: 32, color: "3E2723", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Gentium Book Plus", size: 14, color: "5D4037", bold: true, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Gentium Book Plus", size: 11, color: "4E342E", align: 'both',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "BF8040"
},
previewCss: `
font-family: 'Gentium Book Plus', serif;
h1 { font-family: 'Pirata One', cursive; font-size: 32pt; color: #3E2723; text-align: center; margin-bottom: 32px; }
h2 { font-size: 14pt; font-weight: 700; color: #5D4037; margin-top: 36px; margin-bottom: 18px; }
p { font-size: 11pt; line-height: 1.8; color: #4E342E; margin-bottom: 16px; text-align: justify; }
blockquote { background: #EFEBE9; padding: 20px; border: 1px solid #BF8040; margin: 28px 0; }
`
};

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const fashionMagazine: StyleOption = {
id: 'fashion-magazine',
name: 'Fashion Magazine',
category: 'Editorial',
description: 'Trendy and bold. High-contrast typography with oversized lettering and pink accents.',
vibe: 'Trendy, Loud, Pink',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Abril+Fatface&family=Raleway:wght@300;700&display=swap',
wordConfig: {
heading1: {
font: "Abril Fatface", size: 48, color: "E91E63", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 200 }
},
heading2: {
font: "Raleway", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
allCaps: true,
tracking: 200
},
body: {
font: "Raleway", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "E91E63"
},
previewCss: `
font-family: 'Raleway', sans-serif;
h1 { font-family: 'Abril Fatface', cursive; font-size: 48pt; color: #E91E63; margin-bottom: 24px; line-height: 0.9; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; text-transform: uppercase; letter-spacing: 4px; margin-top: 32px; margin-bottom: 16px; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; font-weight: 300; }
blockquote { border-left: 10px solid #E91E63; padding-left: 20px; margin: 24px 0; font-family: 'Abril Fatface', cursive; font-size: 18pt; }
`
};

View File

@@ -0,0 +1,35 @@
import { StyleOption } from '../../../types';
export const filmScript: StyleOption = {
id: 'film-script',
name: 'Film Script',
category: 'Editorial',
description: 'Hollywood screenplay format. Courier font, specific margins, and character name centering.',
vibe: 'Cinematic, Format, Draft',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Courier+Prime:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Courier Prime", size: 12, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 240, line: 240 },
allCaps: true,
underline: true
},
heading2: {
font: "Courier Prime", size: 12, color: "000000", bold: true, align: 'center',
spacing: { before: 480, after: 0, line: 240 },
allCaps: true
},
body: {
font: "Courier Prime", size: 12, color: "000000", align: 'left',
spacing: { before: 0, after: 0, line: 240 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Courier Prime', monospace;
h1 { font-size: 12pt; font-weight: 700; color: #000000; text-decoration: underline; text-transform: uppercase; margin-bottom: 12px; }
h2 { font-size: 12pt; font-weight: 700; color: #000000; text-transform: uppercase; text-align: center; margin-top: 24px; margin-bottom: 0px; }
p { font-size: 12pt; line-height: 1; color: #000000; margin-bottom: 12px; }
blockquote { margin: 0 40px; text-align: center; }
`
};

View File

@@ -0,0 +1,38 @@
import { StyleOption } from '../../../types';
import { artNouveauOrganic } from './art-nouveau-organic';
import { artsCraftsHeritage } from './arts-crafts-heritage';
import { baroqueSplendor } from './baroque-splendor';
import { cottagecoreJournal } from './cottagecore-journal';
import { dutchGoldenAge } from './dutch-golden-age';
import { fashionMagazine } from './fashion-magazine';
import { filmScript } from './film-script';
import { indieZine } from './indie-zine';
import { literaryReview } from './literary-review';
import { luxuryEditorial } from './luxury-editorial';
import { neoGothicRevival } from './neo-gothic-revival';
import { newspaperClassic } from './newspaper-classic';
import { newspaperModern } from './newspaper-modern';
import { newspaperTabloid } from './newspaper-tabloid';
import { nyEditor } from './ny-editor';
import { rococoRomance } from './rococo-romance';
import { victorianOrnate } from './victorian-ornate';
export const editorialStyles: StyleOption[] = [
artNouveauOrganic,
artsCraftsHeritage,
baroqueSplendor,
cottagecoreJournal,
dutchGoldenAge,
fashionMagazine,
filmScript,
indieZine,
literaryReview,
luxuryEditorial,
neoGothicRevival,
newspaperClassic,
newspaperModern,
newspaperTabloid,
nyEditor,
rococoRomance,
victorianOrnate
];

View File

@@ -0,0 +1,34 @@
import { StyleOption } from '../../../types';
export const indieZine: StyleOption = {
id: 'indie-zine',
name: 'Indie Zine',
category: 'Editorial',
description: 'DIY photocopier aesthetic. Typewriter fonts, cut-out look, and rebellious asymmetry.',
vibe: 'DIY, Rebellious, Rough',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Special+Elite&family=Rock+Salt&display=swap',
wordConfig: {
heading1: {
font: "Rock Salt", size: 28, color: "000000", bold: false, align: 'left',
spacing: { before: 400, after: 200, line: 240 },
shading: { fill: "D1C4E9", color: "auto", style: "clear" }
},
heading2: {
font: "Special Elite", size: 14, color: "000000", bold: true, align: 'left',
spacing: { before: 320, after: 160, line: 240 },
border: { bottom: { color: "000000", space: 4, style: "single", size: 12 } }
},
body: {
font: "Special Elite", size: 10, color: "212121", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Special Elite', cursive;
h1 { font-family: 'Rock Salt', cursive; font-size: 28pt; color: #000000; background: #D1C4E9; padding: 10px; transform: rotate(-2deg); margin-bottom: 24px; display: inline-block; }
h2 { font-size: 14pt; font-weight: 700; color: #000000; border-bottom: 2px solid #000; margin-top: 32px; margin-bottom: 16px; width: fit-content; }
p { font-size: 10pt; line-height: 1.6; color: #212121; margin-bottom: 14px; }
blockquote { border: 2px dashed #000; padding: 16px; margin: 24px 0; transform: rotate(1deg); }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const literaryReview: StyleOption = {
id: 'literary-review',
name: 'Literary Review',
category: 'Editorial',
description: 'Classic bookish aesthetic. Dense serif text, elegant headers, perfect for long-form essays.',
vibe: 'Intellectual, Dense, Classic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,400;0,700;1,400&family=Sorts+Mill+Goudy&display=swap',
wordConfig: {
heading1: {
font: "Crimson Pro", size: 30, color: "4A148C", bold: true, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Crimson Pro", size: 14, color: "000000", bold: false, align: 'center',
spacing: { before: 360, after: 180, line: 240 },
smallCaps: true
},
body: {
font: "Sorts Mill Goudy", size: 11, color: "212121", align: 'both',
spacing: { before: 0, after: 160, line: 300 }
},
accentColor: "4A148C"
},
previewCss: `
font-family: 'Sorts Mill Goudy', serif;
h1 { font-family: 'Crimson Pro', serif; font-size: 30pt; font-weight: 700; color: #4A148C; text-align: center; margin-bottom: 32px; }
h2 { font-family: 'Crimson Pro', serif; font-size: 14pt; color: #000000; text-align: center; margin-top: 36px; margin-bottom: 18px; font-variant: small-caps; }
p { font-size: 11pt; line-height: 1.8; color: #212121; margin-bottom: 16px; text-align: justify; }
blockquote { margin: 24px 40px; font-style: italic; color: #4A148C; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const luxuryEditorial: StyleOption = {
id: 'luxury-editorial',
name: 'Luxury Editorial',
category: 'Editorial',
description: 'High-fashion magazine inspired design. Elegant serifs with sophisticated spacing and premium feel for upscale content.',
vibe: 'Fashion, Premium, Sophisticated',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cormorant:wght@400;500;700&family=Montserrat:wght@300;400;500&display=swap',
wordConfig: {
heading1: {
font: "Cormorant", size: 36, color: "1C1C1C", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Montserrat", size: 11, color: "1C1C1C", bold: true, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Montserrat", size: 10, color: "3A3A3A", align: 'left',
spacing: { before: 0, after: 180, line: 300 }
},
accentColor: "B8860B"
},
previewCss: `
font-family: 'Montserrat', sans-serif;
h1 { font-family: 'Cormorant', serif; font-size: 36pt; font-weight: 500; color: #1C1C1C; text-align: center; margin-bottom: 32px; letter-spacing: 2px; }
h2 { font-size: 11pt; font-weight: 500; color: #1C1C1C; margin-top: 36px; margin-bottom: 18px; text-transform: uppercase; letter-spacing: 3px; }
p { font-size: 10pt; line-height: 1.7; color: #3A3A3A; margin-bottom: 16px; font-weight: 300; }
blockquote { border-top: 1px solid #B8860B; border-bottom: 1px solid #B8860B; padding: 24px 0; margin: 32px 0; text-align: center; font-family: 'Cormorant', serif; font-size: 14pt; font-style: italic; }
`
};

View File

@@ -0,0 +1,36 @@
import { StyleOption } from '../../../types';
export const neoGothicRevival: StyleOption = {
id: 'neo-gothic-revival',
name: 'Neo-Gothic Revival',
category: 'Classic',
description: 'Inspired by Gothic architecture and medieval manuscripts. Dark, dramatic typography with ecclesiastical gravitas.',
vibe: 'Medieval, Dramatic, Historic',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700&family=Spectral:wght@400;500;600&display=swap',
wordConfig: {
heading1: {
font: "Cinzel Decorative", size: 28, color: "1A1A1A", bold: true, align: 'center',
spacing: { before: 480, after: 280, line: 240 },
border: {
top: { color: "4A0E0E", space: 12, style: "single", size: 16 },
bottom: { color: "4A0E0E", space: 12, style: "single", size: 16 }
}
},
heading2: {
font: "Cinzel Decorative", size: 14, color: "4A0E0E", bold: false, align: 'left',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Spectral", size: 11, color: "2C2C2C", align: 'both',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "4A0E0E"
},
previewCss: `
font-family: 'Spectral', serif;
h1 { font-family: 'Cinzel Decorative', cursive; font-size: 28pt; font-weight: 700; color: #1A1A1A; text-align: center; border-top: 3px solid #4A0E0E; border-bottom: 3px solid #4A0E0E; padding: 20px 0; margin-bottom: 32px; }
h2 { font-family: 'Cinzel Decorative', cursive; font-size: 14pt; color: #4A0E0E; margin-top: 36px; margin-bottom: 18px; }
p { font-size: 11pt; line-height: 1.8; color: #2C2C2C; margin-bottom: 16px; text-align: justify; }
blockquote { background: #F5F5EB; padding: 20px; border-left: 4px solid #4A0E0E; margin: 28px 0; font-style: italic; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const newspaperClassic: StyleOption = {
id: 'newspaper-classic',
name: 'Newspaper Classic',
category: 'Editorial',
description: 'Traditional newspaper layout with strong typographic hierarchy. Bold headlines, readable body text, and authoritative presence.',
vibe: 'News, Authority, Information',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=PT+Serif:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display", size: 36, color: "000000", bold: true, align: 'center',
spacing: { before: 240, after: 160, line: 220 }
},
heading2: {
font: "Playfair Display", size: 16, color: "000000", bold: true, align: 'left',
spacing: { before: 240, after: 120, line: 240 }
},
body: {
font: "PT Serif", size: 10, color: "1A1A1A", align: 'both',
spacing: { before: 0, after: 120, line: 280 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'PT Serif', serif;
h1 { font-family: 'Playfair Display', serif; font-size: 36pt; font-weight: 900; color: #000000; text-align: center; border-bottom: 2px solid #000000; padding-bottom: 12px; margin-bottom: 20px; }
h2 { font-family: 'Playfair Display', serif; font-size: 16pt; font-weight: 700; color: #000000; margin-top: 24px; margin-bottom: 12px; }
p { font-size: 10pt; line-height: 1.6; color: #1A1A1A; margin-bottom: 10px; text-align: justify; }
blockquote { font-size: 14pt; font-style: italic; text-align: center; margin: 24px 40px; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const newspaperModern: StyleOption = {
id: 'newspaper-modern',
name: 'Newspaper Modern',
category: 'Editorial',
description: 'Contemporary newspaper design balancing tradition with modern sensibilities. Clean grid-based layout.',
vibe: 'Modern, Editorial, Informative',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=DM+Sans:wght@400;500;700&display=swap',
wordConfig: {
heading1: {
font: "DM Serif Display", size: 36, color: "1A1A1A", bold: false, align: 'left',
spacing: { before: 320, after: 200, line: 220 }
},
heading2: {
font: "DM Sans", size: 12, color: "1A1A1A", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 240 }
},
body: {
font: "DM Sans", size: 10, color: "333333", align: 'both',
spacing: { before: 0, after: 140, line: 280 }
},
accentColor: "DC2626"
},
previewCss: `
font-family: 'DM Sans', sans-serif;
h1 { font-family: 'DM Serif Display', serif; font-size: 36pt; color: #1A1A1A; margin-bottom: 24px; }
h2 { font-size: 12pt; font-weight: 700; color: #1A1A1A; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 1px; }
p { font-size: 10pt; line-height: 1.6; color: #333333; margin-bottom: 12px; text-align: justify; }
blockquote { font-size: 16pt; font-style: italic; font-family: 'DM Serif Display', serif; text-align: center; margin: 28px 20px; color: #DC2626; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const newspaperTabloid: StyleOption = {
id: 'newspaper-tabloid',
name: 'Newspaper Tabloid',
category: 'Editorial',
description: 'Sensationalist news design. Condensed, heavy headers with urgent red accents.',
vibe: 'Urgent, Bold, Sensational',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Antonio:wght@700&family=Pathway+Extreme:wght@400;600&display=swap',
wordConfig: {
heading1: {
font: "Antonio", size: 42, color: "FFFFFF", bold: true, align: 'left',
spacing: { before: 360, after: 200, line: 200 },
shading: { fill: "D50000", color: "auto", style: "clear" }
},
heading2: {
font: "Antonio", size: 20, color: "000000", bold: true, align: 'left',
spacing: { before: 280, after: 140, line: 220 }
},
body: {
font: "Pathway Extreme", size: 10, color: "111111", align: 'left',
spacing: { before: 0, after: 160, line: 280 }
},
accentColor: "D50000"
},
previewCss: `
font-family: 'Pathway Extreme', sans-serif;
h1 { font-family: 'Antonio', sans-serif; font-size: 42pt; font-weight: 700; color: #FFFFFF; background: #D50000; padding: 12px 16px; margin-bottom: 24px; text-transform: uppercase; line-height: 1; }
h2 { font-family: 'Antonio', sans-serif; font-size: 20pt; font-weight: 700; color: #000000; margin-top: 28px; margin-bottom: 14px; text-transform: uppercase; }
p { font-size: 10pt; line-height: 1.5; color: #111111; margin-bottom: 14px; }
blockquote { font-size: 14pt; font-weight: 700; font-style: italic; color: #000000; border-left: 6px solid #D50000; padding-left: 16px; margin: 24px 0; }
`
};

View File

@@ -0,0 +1,33 @@
import { StyleOption } from '../../../types';
export const nyEditor: StyleOption = {
id: 'ny-editor',
name: 'The Editorial',
category: 'Editorial',
description: 'High-contrast serifs, centered headers, delicate borders. Feels like a premium magazine feature.',
vibe: 'Luxury, Fashion, Literature',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400;1,400&display=swap',
wordConfig: {
heading1: {
font: "Playfair Display", size: 36, color: "111111", bold: true, italic: true, align: 'center',
spacing: { before: 600, after: 400, line: 240 }
},
heading2: {
font: "Playfair Display", size: 14, color: "444444", bold: true, allCaps: true, tracking: 100, align: 'center',
spacing: { before: 400, after: 200, line: 240 },
border: { bottom: { color: "DDDDDD", space: 8, style: "single", size: 4 } }
},
body: {
font: "Lora", size: 11, color: "333333", align: 'both',
spacing: { before: 0, after: 200, line: 320 }
},
accentColor: "000000"
},
previewCss: `
font-family: 'Lora', serif;
h1 { font-family: 'Playfair Display', serif; font-size: 36pt; font-weight: 700; font-style: italic; text-align: center; margin-bottom: 30px; }
h2 { font-family: 'Playfair Display', serif; font-size: 14pt; font-weight: 700; text-transform: uppercase; letter-spacing: 3px; text-align: center; border-bottom: 1px solid #ddd; padding-bottom: 10px; margin-top: 40px; color: #444; }
p { font-size: 11pt; line-height: 1.8; text-align: justify; margin-bottom: 20px; }
blockquote { font-family: 'Playfair Display', serif; font-size: 18pt; font-style: italic; text-align: center; color: #555; margin: 40px 20px; border: none; }
`
};

View File

@@ -0,0 +1,32 @@
import { StyleOption } from '../../../types';
export const rococoRomance: StyleOption = {
id: 'rococo-romance',
name: 'Rococo Romance',
category: 'Classic',
description: 'Inspired by 18th-century Rococo. Light, playful, and intricate with pastel tones and swirling elegance.',
vibe: 'Romantic, Ornate, Pastel',
googleFontsImport: 'https://fonts.googleapis.com/css2?family=Pinyon+Script&family=Playfair+Display+SC:wght@400;700&display=swap',
wordConfig: {
heading1: {
font: "Pinyon Script", size: 36, color: "D87093", bold: false, align: 'center',
spacing: { before: 480, after: 280, line: 240 }
},
heading2: {
font: "Playfair Display SC", size: 14, color: "C0A080", bold: true, align: 'center',
spacing: { before: 360, after: 180, line: 240 }
},
body: {
font: "Playfair Display SC", size: 10, color: "555555", align: 'center',
spacing: { before: 0, after: 180, line: 320 }
},
accentColor: "FFB6C1"
},
previewCss: `
font-family: 'Playfair Display SC', serif;
h1 { font-family: 'Pinyon Script', cursive; font-size: 36pt; color: #D87093; text-align: center; margin-bottom: 32px; }
h2 { font-size: 14pt; font-weight: 700; color: #C0A080; text-align: center; margin-top: 36px; margin-bottom: 18px; letter-spacing: 2px; }
p { font-size: 10pt; line-height: 1.8; color: #555555; margin-bottom: 16px; text-align: center; }
blockquote { border: 1px solid #FFB6C1; padding: 20px; border-radius: 50% 50% 50% 50% / 10% 10% 10% 10%; margin: 28px 40px; text-align: center; }
`
};

Some files were not shown because too many files have changed in this diff Show More