diff --git a/client/src/App.jsx b/client/src/App.jsx
index f21be5b..5b205d6 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -66,11 +66,18 @@ function App() {
}
};
+ const handleApplyTemplate = (template) => {
+ // Clear existing elements and apply template
+ template.elements.forEach((el, index) => {
+ setTimeout(() => addElement({ ...el }), index * 50);
+ });
+ };
+
return (
{/* Left Sidebar */}
{/* Center Canvas */}
diff --git a/client/src/components/sidebar/Sidebar.jsx b/client/src/components/sidebar/Sidebar.jsx
index 0f5e806..80dd71d 100644
--- a/client/src/components/sidebar/Sidebar.jsx
+++ b/client/src/components/sidebar/Sidebar.jsx
@@ -2,14 +2,16 @@ import { useState } from 'react';
import { UploadTab } from './UploadTab';
import { StickersTab } from './StickersTab';
import { TextTab } from './TextTab';
+import { TemplatesTab } from './TemplatesTab';
const TABS = [
{ id: 'upload', label: 'Upload', icon: '📁' },
{ id: 'stickers', label: 'Stickers', icon: '😊' },
{ id: 'text', label: 'Text', icon: 'T' },
+ { id: 'templates', label: 'Templates', icon: '📋' },
];
-export function Sidebar({ onElementAdd, onUpload }) {
+export function Sidebar({ onElementAdd, onUpload, onApplyTemplate }) {
const [activeTab, setActiveTab] = useState('upload');
const renderTabContent = () => {
@@ -20,6 +22,8 @@ export function Sidebar({ onElementAdd, onUpload }) {
return
;
case 'text':
return
;
+ case 'templates':
+ return
;
default:
return null;
}
diff --git a/client/src/components/sidebar/TemplatesTab.jsx b/client/src/components/sidebar/TemplatesTab.jsx
new file mode 100644
index 0000000..7a3e913
--- /dev/null
+++ b/client/src/components/sidebar/TemplatesTab.jsx
@@ -0,0 +1,56 @@
+import { useState } from 'react';
+import { TEMPLATES, TEMPLATE_CATEGORIES } from '../../constants/templates';
+
+export function TemplatesTab({ onApplyTemplate }) {
+ const [selectedCategory, setSelectedCategory] = useState('All');
+
+ const filteredTemplates =
+ selectedCategory === 'All'
+ ? TEMPLATES
+ : TEMPLATES.filter((t) => t.category === selectedCategory);
+
+ return (
+
+
Templates
+
+ {TEMPLATE_CATEGORIES.map((cat) => (
+
+ ))}
+
+
+ {filteredTemplates.map((template) => (
+
+ ))}
+
+
+ );
+}
diff --git a/client/src/components/sidebar/index.js b/client/src/components/sidebar/index.js
index 216fdb5..d983c43 100644
--- a/client/src/components/sidebar/index.js
+++ b/client/src/components/sidebar/index.js
@@ -2,3 +2,4 @@ export { Sidebar } from './Sidebar';
export { UploadTab } from './UploadTab';
export { StickersTab } from './StickersTab';
export { TextTab } from './TextTab';
+export { TemplatesTab } from './TemplatesTab';
diff --git a/client/src/constants/templates.js b/client/src/constants/templates.js
new file mode 100644
index 0000000..f2d3969
--- /dev/null
+++ b/client/src/constants/templates.js
@@ -0,0 +1,309 @@
+// Pre-designed templates for t-shirt customization
+export const TEMPLATES = [
+ {
+ id: 'team-sport',
+ name: 'Team Sport',
+ category: 'Sports',
+ description: 'Classic team jersey with number and text',
+ elements: [
+ {
+ type: 'text',
+ text: 'TEAM NAME',
+ x: 75,
+ y: 80,
+ fontSize: 28,
+ fontFamily: 'Impact',
+ fill: '#ffffff',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: '23',
+ x: 150,
+ y: 150,
+ fontSize: 72,
+ fontFamily: 'Impact',
+ fill: '#ffffff',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'band-merch',
+ name: 'Band Merch',
+ category: 'Music',
+ description: 'Classic band t-shirt design',
+ elements: [
+ {
+ type: 'text',
+ text: 'BAND NAME',
+ x: 150,
+ y: 70,
+ fontSize: 32,
+ fontFamily: 'Georgia',
+ fill: '#fbbf24',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'WORLD TOUR 2026',
+ x: 150,
+ y: 110,
+ fontSize: 16,
+ fontFamily: 'Arial',
+ fill: '#ffffff',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: '🎸',
+ x: 150,
+ y: 180,
+ fontSize: 64,
+ fontFamily: 'Arial',
+ fill: '#ffffff',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'minimal-quote',
+ name: 'Minimal Quote',
+ category: 'Quotes',
+ description: 'Simple centered quote design',
+ elements: [
+ {
+ type: 'text',
+ text: '"Be the change"',
+ x: 150,
+ y: 130,
+ fontSize: 24,
+ fontFamily: 'Georgia',
+ fill: '#1e293b',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'you wish to see',
+ x: 150,
+ y: 160,
+ fontSize: 18,
+ fontFamily: 'Arial',
+ fill: '#64748b',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'funny-cat',
+ name: 'Funny Cat',
+ category: 'Animals',
+ description: 'Cute cat with funny text',
+ elements: [
+ {
+ type: 'text',
+ text: '😼',
+ x: 150,
+ y: 100,
+ fontSize: 80,
+ fontFamily: 'Arial',
+ fill: '#000000',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'I do what I want',
+ x: 150,
+ y: 200,
+ fontSize: 20,
+ fontFamily: 'Comic Sans MS',
+ fill: '#475569',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'gradient-vibes',
+ name: 'Gradient Vibes',
+ category: 'Abstract',
+ description: 'Modern gradient text design',
+ elements: [
+ {
+ type: 'text',
+ text: 'GOOD',
+ x: 150,
+ y: 110,
+ fontSize: 48,
+ fontFamily: 'Impact',
+ fill: '#ec4899',
+ rotation: -5,
+ },
+ {
+ type: 'text',
+ text: 'VIBES',
+ x: 150,
+ y: 160,
+ fontSize: 48,
+ fontFamily: 'Impact',
+ fill: '#8b5cf6',
+ rotation: 5,
+ },
+ {
+ type: 'text',
+ text: '✨',
+ x: 80,
+ y: 90,
+ fontSize: 32,
+ fontFamily: 'Arial',
+ fill: '#fbbf24',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: '🌙',
+ x: 220,
+ y: 190,
+ fontSize: 32,
+ fontFamily: 'Arial',
+ fill: '#38bdf8',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'vintage-badge',
+ name: 'Vintage Badge',
+ category: 'Vintage',
+ description: 'Retro badge style design',
+ elements: [
+ {
+ type: 'text',
+ text: 'EST.',
+ x: 150,
+ y: 80,
+ fontSize: 18,
+ fontFamily: 'Times New Roman',
+ fill: '#78716c',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: '2026',
+ x: 150,
+ y: 105,
+ fontSize: 36,
+ fontFamily: 'Times New Roman',
+ fill: '#78716c',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'AUTHENTIC',
+ x: 150,
+ y: 150,
+ fontSize: 24,
+ fontFamily: 'Times New Roman',
+ fill: '#78716c',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'QUALITY',
+ x: 150,
+ y: 180,
+ fontSize: 24,
+ fontFamily: 'Times New Roman',
+ fill: '#78716c',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'nature-lover',
+ name: 'Nature Lover',
+ category: 'Nature',
+ description: 'Mountain and nature themed',
+ elements: [
+ {
+ type: 'text',
+ text: '🏔️',
+ x: 150,
+ y: 90,
+ fontSize: 56,
+ fontFamily: 'Arial',
+ fill: '#000000',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'ADVENTURE',
+ x: 150,
+ y: 160,
+ fontSize: 28,
+ fontFamily: 'Impact',
+ fill: '#059669',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'AWAITS',
+ x: 150,
+ y: 190,
+ fontSize: 20,
+ fontFamily: 'Arial',
+ fill: '#6b7280',
+ rotation: 0,
+ },
+ ],
+ },
+ {
+ id: 'tech-geek',
+ name: 'Tech Geek',
+ category: 'Tech',
+ description: 'Programming themed design',
+ elements: [
+ {
+ type: 'text',
+ text: '>',
+ x: 150,
+ y: 100,
+ fontSize: 64,
+ fontFamily: 'Courier New',
+ fill: '#3b82f6',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: 'Hello, World!',
+ x: 150,
+ y: 170,
+ fontSize: 20,
+ fontFamily: 'Courier New',
+ fill: '#1e293b',
+ rotation: 0,
+ },
+ {
+ type: 'text',
+ text: '// Code is life',
+ x: 150,
+ y: 195,
+ fontSize: 14,
+ fontFamily: 'Courier New',
+ fill: '#94a3b8',
+ rotation: 0,
+ },
+ ],
+ },
+];
+
+export const TEMPLATE_CATEGORIES = [
+ 'All',
+ 'Sports',
+ 'Music',
+ 'Quotes',
+ 'Animals',
+ 'Abstract',
+ 'Vintage',
+ 'Nature',
+ 'Tech',
+];
diff --git a/client/src/index.css b/client/src/index.css
index d65746c..af023d6 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -389,6 +389,72 @@ input, textarea, select {
text-align: center;
}
+/* Templates Tab */
+.templates-tab h3 {
+ font-size: 0.875rem;
+ margin: 0 0 1rem 0;
+}
+
+.templates-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.75rem;
+ max-height: 400px;
+ overflow-y: auto;
+}
+
+.template-card {
+ display: flex;
+ flex-direction: column;
+ padding: 0.75rem;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ background: var(--bg-primary);
+ cursor: pointer;
+ transition: all 0.2s;
+ text-align: left;
+}
+
+.template-card:hover {
+ border-color: var(--accent);
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-md);
+}
+
+.template-preview {
+ height: 60px;
+ background: var(--bg-tertiary);
+ border-radius: var(--radius-sm);
+ margin-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.25rem;
+ flex-wrap: wrap;
+ padding: 0.25rem;
+}
+
+.template-preview-element {
+ font-weight: 500;
+}
+
+.template-info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.template-name {
+ font-size: 0.75rem;
+ font-weight: 500;
+ color: var(--text-primary);
+}
+
+.template-category {
+ font-size: 0.625rem;
+ color: var(--text-muted);
+}
+
/* Properties Panel */
.properties-panel {
padding: 1rem;