From dff17b0917b7c164baaf8daeb7ff4aab712012cc Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Wed, 7 Oct 2026 11:37:31 -0700 Subject: [PATCH] feat(site-builder): re-author restaurant, small-business, saas-landing, agency templates onto design tokens Refs #44 Co-Authored-By: Claude Sonnet 5.5 --- .../templates/definitions.tokens-A.test.ts | 125 +++++++++ craft/src/templates/definitions.ts | 247 ++++++++++-------- 2 files changed, 257 insertions(+), 115 deletions(-) create mode 100644 craft/src/templates/definitions.tokens-A.test.ts diff --git a/craft/src/templates/definitions.tokens-A.test.ts b/craft/src/templates/definitions.tokens-A.test.ts new file mode 100644 index 0000000..b912d1a --- /dev/null +++ b/craft/src/templates/definitions.tokens-A.test.ts @@ -0,0 +1,125 @@ +import { describe, test, expect, afterEach } from 'vitest'; +import { renderEditorHarness, EditorHarness } from '../test-utils/editorHarness'; +import { allTemplates } from './index'; +import type { TemplateComponent, TemplateDefinition } from './definitions'; +import { buildNodeTree } from '../utils/craft-tree'; +import { templateComponentToTreeNode } from './apply-template'; +import { exportBodyHtml, exportToHtml } from '../utils/html-export'; +import { DEFAULT_SITE_DESIGN } from '../state/SiteDesignContext'; + +/** + * #44 group A: restaurant / small-business / saas-landing / agency must carry + * their palette and fonts as `var(--wsb-*)` token references, not hex/font + * literals, so changing Site Design after applying a template restyles it. + */ + +const GROUP_A = ['restaurant', 'small-business', 'saas-landing', 'agency']; +const templates = GROUP_A.map((id) => allTemplates.find((t) => t.id === id)!); + +/** Design values that must NOT appear as literals inside component props. */ +function forbiddenValues(tpl: TemplateDefinition): Map { + const d = tpl.design as Record; + const keys = [ + 'primaryColor', 'secondaryColor', 'accentColor', 'backgroundColor', 'textColor', + 'mutedTextColor', 'borderColor', 'linkColor', 'headingFont', 'bodyFont', + ]; + const out = new Map(); + for (const k of keys) if (typeof d[k] === 'string') out.set(d[k].trim().toLowerCase(), k); + return out; +} + +function walkStrings(v: any, path: string, visit: (s: string, path: string) => void) { + if (typeof v === 'string') visit(v, path); + else if (Array.isArray(v)) v.forEach((x, i) => walkStrings(x, `${path}[${i}]`, visit)); + else if (v && typeof v === 'object') for (const k of Object.keys(v)) walkStrings(v[k], `${path}.${k}`, visit); +} + +function walkComponents(comps: TemplateComponent[], visit: (c: TemplateComponent) => void) { + for (const c of comps) { + visit(c); + if (c.children) walkComponents(c.children, visit); + } +} + +function allTrees(tpl: TemplateDefinition): { where: string; comps: TemplateComponent[] }[] { + return [ + { where: 'header', comps: tpl.header.components }, + { where: 'footer', comps: tpl.footer.components }, + ...tpl.pages.map((p) => ({ where: `page:${p.slug}`, comps: p.content.components })), + ]; +} + +let harness: EditorHarness | null = null; +afterEach(() => { + harness?.unmount(); + harness = null; +}); + +function loadFirstPage(tpl: TemplateDefinition): string { + harness = renderEditorHarness(); + for (const comp of tpl.pages[0].content.components) { + harness.actions.addNodeTree(buildNodeTree(harness.query, templateComponentToTreeNode(comp)), 'ROOT'); + } + return harness.getSerialized(); +} + +describe.each(templates)('tokens-A: $id', (tpl) => { + test('template exists and declares a muted text role', () => { + expect(tpl).toBeDefined(); + expect(tpl.design.mutedTextColor).toMatch(/^#[0-9a-f]{6}$/i); + }); + + test('no component prop equals one of the template design colours or fonts', () => { + const bad = forbiddenValues(tpl); + const hits: string[] = []; + for (const { where, comps } of allTrees(tpl)) { + walkComponents(comps, (c) => + walkStrings(c.props, `${where}/${c.type}`, (s, p) => { + if (bad.has(s.trim().toLowerCase())) hits.push(`${p} = ${s}`); + // gradients / compound values: no design hex embedded either + for (const v of bad.keys()) if (v.startsWith('#') && s.toLowerCase().includes(v)) hits.push(`${p} embeds ${v}`); + }), + ); + } + expect(hits).toEqual([]); + }); + + test('applies through templateComponentToTreeNode + buildNodeTree without throwing', () => { + for (const { comps } of allTrees(tpl)) { + for (const comp of comps) { + harness = renderEditorHarness(); + expect(() => + harness!.actions.addNodeTree(buildNodeTree(harness!.query, templateComponentToTreeNode(comp)), 'ROOT'), + ).not.toThrow(); + harness.unmount(); + harness = null; + } + } + }); + + test('first page body uses var(--wsb-primary) or var(--wsb-secondary) and no design hex', () => { + const state = loadFirstPage(tpl); + const { html } = exportBodyHtml(state); + expect(html).toContain('var(--wsb-'); + expect(html).toContain('var(--wsb-primary)'); + expect(html.toLowerCase()).not.toContain(tpl.design.primaryColor!.toLowerCase()); + expect(html.toLowerCase()).not.toContain(tpl.design.secondaryColor!.toLowerCase()); + }); + + test('exported document defines the template palette in :root and body carries no former hex', () => { + const state = loadFirstPage(tpl); + const design = { ...DEFAULT_SITE_DESIGN, ...tpl.design }; + const { html } = exportToHtml(state, { design } as any); + const root = html.match(/:root\s*\{[^}]*\}/)?.[0] ?? ''; + expect(root.toLowerCase()).toContain(`--wsb-primary:${tpl.design.primaryColor!.toLowerCase()}`.replace(/:/, ':')); + const body = html.slice(html.indexOf(' { + const state = loadFirstPage(tpl); + const { html } = exportBodyHtml(state); + expect(html).toMatch(/background:linear-gradient\(135deg, var\(--wsb-(primary|secondary)\)/); + }); +}); + diff --git a/craft/src/templates/definitions.ts b/craft/src/templates/definitions.ts index a21c6f5..b82d351 100644 --- a/craft/src/templates/definitions.ts +++ b/craft/src/templates/definitions.ts @@ -1,4 +1,5 @@ import { SiteDesign } from '../state/SiteDesignContext'; +import { tokenRef } from '../utils/tokens'; // --------------------------------------------------------------------------- // Types @@ -218,6 +219,7 @@ const restaurant: TemplateDefinition = { backgroundColor: '#faf8f5', textColor: '#2c1810', navStyle: 'dark', + mutedTextColor: '#5c4030', }, header: makeHeader( 'La Maison', @@ -227,11 +229,11 @@ const restaurant: TemplateDefinition = { { text: 'About', href: 'about' }, { text: 'Reserve', href: 'contact', isCta: true }, ], - '#2c1810', - '#d4a574', - '#c9a96e', + tokenRef('secondary'), + tokenRef('accent'), + tokenRef('primary'), ), - footer: makeFooterContent('La Maison', '#2c1810', '#d4a574', { + footer: makeFooterContent('La Maison', tokenRef('secondary'), tokenRef('accent'), { tagline: 'Fine dining in the heart of the city.', contactText: '123 Gourmet Avenue, Downtown \u00b7 (555) 234-5678', }), @@ -248,16 +250,19 @@ const restaurant: TemplateDefinition = { subtitle: 'Fine dining in the heart of the city. Experience culinary artistry with locally-sourced ingredients and world-class hospitality.', buttonText: 'View Our Menu', buttonHref: 'menu', - gradient: 'linear-gradient(135deg, #2c1810 0%, #4a2c1a 100%)', + bgType: 'gradient', + bgGradientFrom: tokenRef('secondary'), + bgGradientTo: '#4a2c1a', + bgGradientAngle: 135, style: { minHeight: '500px' }, }, }, { type: 'Section', - props: { style: { padding: '80px 20px', backgroundColor: '#faf8f5' } }, + props: { style: { padding: '80px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Our Story', level: 'h2', style: { textAlign: 'center', color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'For over 20 years, La Maison has been crafting unforgettable dining experiences. Our chefs blend classical French technique with bold, modern flavors to create dishes that surprise and delight.', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: '#5c4030', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Our Story', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'For over 20 years, La Maison has been crafting unforgettable dining experiences. Our chefs blend classical French technique with bold, modern flavors to create dishes that surprise and delight.', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { @@ -268,15 +273,15 @@ const restaurant: TemplateDefinition = { { title: 'Award-Winning Chef', description: 'Chef Laurent brings two Michelin stars and 25 years of culinary passion.', icon: '\ud83d\udc68\u200d\ud83c\udf73' }, { title: 'Private Dining', description: 'Intimate rooms available for celebrations, corporate events, and special occasions.', icon: '\ud83e\udd42' }, ], - style: { backgroundColor: '#faf8f5' }, + style: { backgroundColor: tokenRef('bg') }, }, }, { type: 'Section', props: { style: { padding: '80px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'What Our Guests Say', level: 'h2', style: { textAlign: 'center', color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '24px' } } }, - { type: 'TextBlock', props: { text: '"An unforgettable evening. The tasting menu was a journey through flavor that I will remember for years." \u2014 Sarah M., Food & Wine Magazine', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: '#5c4030', lineHeight: '1.8', fontStyle: 'italic' } } }, + { type: 'Heading', props: { text: 'What Our Guests Say', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '24px' } } }, + { type: 'TextBlock', props: { text: '"An unforgettable evening. The tasting menu was a journey through flavor that I will remember for years." \u2014 Sarah M., Food & Wine Magazine', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8', fontStyle: 'italic' } } }, ], }, { @@ -286,7 +291,7 @@ const restaurant: TemplateDefinition = { description: 'Join us for an evening of exceptional food and warm hospitality. Reservations recommended.', buttonText: 'Book Now', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #2c1810 0%, #5c3a20 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('secondary')} 0%, #5c3a20 100%)`, }, }, ], @@ -299,9 +304,9 @@ const restaurant: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#faf8f5' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Our Menu', level: 'h1', style: { textAlign: 'center', color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '8px' } } }, + { type: 'Heading', props: { text: 'Our Menu', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '8px' } } }, { type: 'TextBlock', props: { text: 'Seasonal selections prepared with care', style: { textAlign: 'center', color: '#8a7060', fontStyle: 'italic', marginBottom: '40px' } } }, ], }, @@ -309,27 +314,27 @@ const restaurant: TemplateDefinition = { type: 'Section', props: { style: { padding: '40px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'Starters', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'French Onion Soup \u2014 Gruyere crouton, caramelized onions \u2014 $14\n\nTuna Tartare \u2014 Avocado, sesame, citrus ponzu \u2014 $18\n\nBurrata Salad \u2014 Heirloom tomatoes, basil, aged balsamic \u2014 $16', style: { color: '#5c4030', lineHeight: '2' } } }, - { type: 'Divider', props: { color: '#d4a574', thickness: '1px', style: { margin: '32px 0' } } }, - { type: 'Heading', props: { text: 'Main Courses', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Filet Mignon \u2014 Truffle butter, roasted vegetables, red wine jus \u2014 $48\n\nSeared Salmon \u2014 Lemon beurre blanc, asparagus, fingerling potatoes \u2014 $36\n\nMushroom Risotto \u2014 Wild mushrooms, parmesan, truffle oil \u2014 $28', style: { color: '#5c4030', lineHeight: '2' } } }, + { type: 'Heading', props: { text: 'Starters', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'French Onion Soup \u2014 Gruyere crouton, caramelized onions \u2014 $14\n\nTuna Tartare \u2014 Avocado, sesame, citrus ponzu \u2014 $18\n\nBurrata Salad \u2014 Heirloom tomatoes, basil, aged balsamic \u2014 $16', style: { color: tokenRef('muted'), lineHeight: '2' } } }, + { type: 'Divider', props: { color: tokenRef('accent'), thickness: '1px', style: { margin: '32px 0' } } }, + { type: 'Heading', props: { text: 'Main Courses', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Filet Mignon \u2014 Truffle butter, roasted vegetables, red wine jus \u2014 $48\n\nSeared Salmon \u2014 Lemon beurre blanc, asparagus, fingerling potatoes \u2014 $36\n\nMushroom Risotto \u2014 Wild mushrooms, parmesan, truffle oil \u2014 $28', style: { color: tokenRef('muted'), lineHeight: '2' } } }, ], }, { type: 'Section', - props: { style: { padding: '40px 20px', backgroundColor: '#faf8f5' } }, + props: { style: { padding: '40px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Desserts', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Cr\u00e8me Br\u00fbl\u00e9e \u2014 Madagascar vanilla, caramelized sugar \u2014 $12\n\nChocolate Fondant \u2014 Warm center, raspberry coulis \u2014 $14\n\nCheese Board \u2014 Artisan selections, honeycomb, walnut bread \u2014 $18', style: { color: '#5c4030', lineHeight: '2' } } }, + { type: 'Heading', props: { text: 'Desserts', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Cr\u00e8me Br\u00fbl\u00e9e \u2014 Madagascar vanilla, caramelized sugar \u2014 $12\n\nChocolate Fondant \u2014 Warm center, raspberry coulis \u2014 $14\n\nCheese Board \u2014 Artisan selections, honeycomb, walnut bread \u2014 $18', style: { color: tokenRef('muted'), lineHeight: '2' } } }, ], }, { type: 'Section', props: { style: { padding: '40px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'Wine Pairings', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Our sommelier curates a wine list featuring over 200 selections from France, Italy, California, and beyond. Ask about our 5-course tasting menu with paired wines for a truly memorable experience.', style: { color: '#5c4030', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Wine Pairings', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Our sommelier curates a wine list featuring over 200 selections from France, Italy, California, and beyond. Ask about our 5-course tasting menu with paired wines for a truly memorable experience.', style: { color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, ], @@ -342,18 +347,18 @@ const restaurant: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#faf8f5' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'About La Maison', level: 'h1', style: { textAlign: 'center', color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Founded in 2005, La Maison was born from a passion for bringing authentic French cuisine to our community. What started as a small bistro has grown into one of the most celebrated dining destinations in the city.', style: { textAlign: 'center', maxWidth: '700px', margin: '0 auto', color: '#5c4030', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'About La Maison', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Founded in 2005, La Maison was born from a passion for bringing authentic French cuisine to our community. What started as a small bistro has grown into one of the most celebrated dining destinations in the city.', style: { textAlign: 'center', maxWidth: '700px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { type: 'Section', props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'Meet Chef Laurent', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Chef Laurent Dubois trained at Le Cordon Bleu in Paris before honing his craft in Michelin-starred kitchens across Europe. His philosophy centers on respecting the ingredient and elevating simplicity into art.', style: { color: '#5c4030', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Meet Chef Laurent', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Chef Laurent Dubois trained at Le Cordon Bleu in Paris before honing his craft in Michelin-starred kitchens across Europe. His philosophy centers on respecting the ingredient and elevating simplicity into art.', style: { color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { @@ -364,15 +369,15 @@ const restaurant: TemplateDefinition = { { title: 'Local Partnerships', description: 'We source from 15 local farms within a 50-mile radius of the restaurant.', icon: '\ud83c\udf3e' }, { title: '20 Years of Service', description: 'Two decades of memorable evenings, celebrations, and shared meals.', icon: '\ud83c\udf7e' }, ], - style: { backgroundColor: '#faf8f5' }, + style: { backgroundColor: tokenRef('bg') }, }, }, { type: 'Section', props: { style: { padding: '60px 20px', backgroundColor: '#ffffff', textAlign: 'center' } }, children: [ - { type: 'Heading', props: { text: 'Visit Us', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Open Tuesday\u2013Sunday, 5:00 PM \u2013 10:30 PM\n123 Gourmet Avenue, Downtown\nValet parking available', style: { color: '#5c4030', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Visit Us', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Open Tuesday\u2013Sunday, 5:00 PM \u2013 10:30 PM\n123 Gourmet Avenue, Downtown\nValet parking available', style: { color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, ], @@ -385,17 +390,17 @@ const restaurant: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#faf8f5' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Reservations & Contact', level: 'h1', style: { textAlign: 'center', color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: '123 Gourmet Avenue, Downtown\nPhone: (555) 234-5678\nEmail: reservations@lamaison.com\n\nOpen Tuesday\u2013Sunday, 5:00 PM \u2013 10:30 PM', style: { textAlign: 'center', color: '#5c4030', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Reservations & Contact', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: '123 Gourmet Avenue, Downtown\nPhone: (555) 234-5678\nEmail: reservations@lamaison.com\n\nOpen Tuesday\u2013Sunday, 5:00 PM \u2013 10:30 PM', style: { textAlign: 'center', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { type: 'Section', props: { style: { padding: '40px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'Send Us a Message', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '20px' } } }, + { type: 'Heading', props: { text: 'Send Us a Message', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '20px' } } }, { type: 'FormContainer', props: { style: { maxWidth: '600px' } }, @@ -403,17 +408,17 @@ const restaurant: TemplateDefinition = { { type: 'InputField', props: { label: 'Name', placeholder: 'Your name' } }, { type: 'InputField', props: { label: 'Email', placeholder: 'your@email.com', inputType: 'email' } }, { type: 'TextareaField', props: { label: 'Message', placeholder: 'Tell us about your reservation needs...' } }, - { type: 'FormButton', props: { text: 'Send Message', style: { backgroundColor: '#c9a96e', color: '#ffffff' } } }, + { type: 'FormButton', props: { text: 'Send Message', style: { backgroundColor: tokenRef('primary'), color: '#ffffff' } } }, ], }, ], }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#faf8f5', textAlign: 'center' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } }, children: [ - { type: 'Heading', props: { text: 'Private Events', level: 'h2', style: { color: '#2c1810', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Our private dining room seats up to 24 guests and is perfect for birthdays, anniversaries, corporate dinners, and other special occasions. Contact our events team for custom menus and pricing.', style: { color: '#5c4030', lineHeight: '1.8', maxWidth: '600px', margin: '0 auto' } } }, + { type: 'Heading', props: { text: 'Private Events', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Our private dining room seats up to 24 guests and is perfect for birthdays, anniversaries, corporate dinners, and other special occasions. Contact our events team for custom menus and pricing.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '600px', margin: '0 auto' } } }, ], }, ], @@ -440,6 +445,7 @@ const smallBusiness: TemplateDefinition = { backgroundColor: '#f8fafc', textColor: '#1e293b', navStyle: 'dark', + mutedTextColor: '#64748b', }, header: makeHeader( 'Apex Solutions', @@ -449,11 +455,11 @@ const smallBusiness: TemplateDefinition = { { text: 'About', href: 'about' }, { text: 'Contact Us', href: 'contact', isCta: true }, ], - '#1e293b', + tokenRef('secondary'), '#e2e8f0', - '#2563eb', + tokenRef('primary'), ), - footer: makeFooterContent('Apex Solutions', '#1e293b', '#94a3b8', { + footer: makeFooterContent('Apex Solutions', tokenRef('secondary'), '#94a3b8', { tagline: 'Strategic consulting and digital solutions for growing businesses.', contactText: '500 Business Park Drive, Suite 200 \u00b7 hello@apexsolutions.com', socialText: 'LinkedIn \u00b7 Twitter \u00b7 Facebook', @@ -471,7 +477,10 @@ const smallBusiness: TemplateDefinition = { subtitle: 'Strategic consulting and digital solutions tailored for small businesses. We help you compete with the big players.', buttonText: 'Our Services', buttonHref: 'services', - gradient: 'linear-gradient(135deg, #1e293b 0%, #334155 100%)', + bgType: 'gradient', + bgGradientFrom: tokenRef('secondary'), + bgGradientTo: '#334155', + bgGradientAngle: 135, }, }, { @@ -487,18 +496,18 @@ const smallBusiness: TemplateDefinition = { }, { type: 'Section', - props: { style: { padding: '80px 20px', backgroundColor: '#f8fafc' } }, + props: { style: { padding: '80px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Trusted by 200+ Local Businesses', level: 'h2', style: { textAlign: 'center', color: '#1e293b', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'From startups to established companies, we have helped businesses across every industry achieve their growth targets. Our client retention rate speaks for itself: 94%.', style: { textAlign: 'center', maxWidth: '650px', margin: '0 auto', color: '#64748b', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Trusted by 200+ Local Businesses', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'From startups to established companies, we have helped businesses across every industry achieve their growth targets. Our client retention rate speaks for itself: 94%.', style: { textAlign: 'center', maxWidth: '650px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { type: 'Section', props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'What Our Clients Say', level: 'h2', style: { textAlign: 'center', color: '#1e293b', marginBottom: '24px' } } }, - { type: 'TextBlock', props: { text: '"Apex Solutions transformed our online presence. Within 6 months, our leads tripled and revenue grew by 45%." \u2014 Michael Torres, CEO of BrightPath Retail', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: '#64748b', lineHeight: '1.8', fontStyle: 'italic' } } }, + { type: 'Heading', props: { text: 'What Our Clients Say', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '24px' } } }, + { type: 'TextBlock', props: { text: '"Apex Solutions transformed our online presence. Within 6 months, our leads tripled and revenue grew by 45%." \u2014 Michael Torres, CEO of BrightPath Retail', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8', fontStyle: 'italic' } } }, ], }, { @@ -508,7 +517,7 @@ const smallBusiness: TemplateDefinition = { description: 'Schedule a free 30-minute consultation to discuss your business goals.', buttonText: 'Get in Touch', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #1d4ed8 100%)`, }, }, ], @@ -521,10 +530,10 @@ const smallBusiness: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#f8fafc' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Our Services', level: 'h1', style: { textAlign: 'center', color: '#1e293b', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Comprehensive solutions to help your business thrive in the digital age.', style: { textAlign: 'center', color: '#64748b', marginBottom: '40px' } } }, + { type: 'Heading', props: { text: 'Our Services', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Comprehensive solutions to help your business thrive in the digital age.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } }, ], }, { @@ -551,10 +560,10 @@ const smallBusiness: TemplateDefinition = { }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#f8fafc', textAlign: 'center' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } }, children: [ - { type: 'Heading', props: { text: 'Our Process', level: 'h2', style: { color: '#1e293b', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: '1. Discovery \u2014 We learn about your business, goals, and challenges.\n2. Strategy \u2014 We build a custom plan with clear milestones and KPIs.\n3. Execution \u2014 Our team implements across design, development, and marketing.\n4. Optimize \u2014 We measure results and continuously refine for growth.', style: { color: '#64748b', lineHeight: '2', maxWidth: '600px', margin: '0 auto' } } }, + { type: 'Heading', props: { text: 'Our Process', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: '1. Discovery \u2014 We learn about your business, goals, and challenges.\n2. Strategy \u2014 We build a custom plan with clear milestones and KPIs.\n3. Execution \u2014 Our team implements across design, development, and marketing.\n4. Optimize \u2014 We measure results and continuously refine for growth.', style: { color: tokenRef('muted'), lineHeight: '2', maxWidth: '600px', margin: '0 auto' } } }, ], }, { @@ -564,7 +573,7 @@ const smallBusiness: TemplateDefinition = { description: 'Every business is unique. Let us build a package that fits your needs and budget.', buttonText: 'Contact Us', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #1d4ed8 100%)`, }, }, ], @@ -577,10 +586,10 @@ const smallBusiness: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#f8fafc' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'About Apex Solutions', level: 'h1', style: { textAlign: 'center', color: '#1e293b', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Founded in 2018, Apex Solutions is a boutique consulting firm dedicated to helping small and mid-sized businesses compete in an increasingly digital world. Our team combines deep industry expertise with cutting-edge tools to deliver results.', style: { textAlign: 'center', maxWidth: '700px', margin: '0 auto', color: '#64748b', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'About Apex Solutions', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Founded in 2018, Apex Solutions is a boutique consulting firm dedicated to helping small and mid-sized businesses compete in an increasingly digital world. Our team combines deep industry expertise with cutting-edge tools to deliver results.', style: { textAlign: 'center', maxWidth: '700px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { @@ -596,10 +605,10 @@ const smallBusiness: TemplateDefinition = { }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#f8fafc' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Our Values', level: 'h2', style: { textAlign: 'center', color: '#1e293b', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Transparency, measurable outcomes, and genuine partnership. We do not believe in vanity metrics or cookie-cutter strategies. Every recommendation we make is backed by data and tailored to your specific business context.', style: { textAlign: 'center', maxWidth: '650px', margin: '0 auto', color: '#64748b', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Our Values', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Transparency, measurable outcomes, and genuine partnership. We do not believe in vanity metrics or cookie-cutter strategies. Every recommendation we make is backed by data and tailored to your specific business context.', style: { textAlign: 'center', maxWidth: '650px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { @@ -609,7 +618,7 @@ const smallBusiness: TemplateDefinition = { description: 'We are always looking for talented strategists, designers, and engineers.', buttonText: 'View Careers', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #1e293b 0%, #334155 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('secondary')} 0%, #334155 100%)`, }, }, ], @@ -622,17 +631,17 @@ const smallBusiness: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#f8fafc' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Get in Touch', level: 'h1', style: { textAlign: 'center', color: '#1e293b', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: '500 Business Park Drive, Suite 200\nPhone: (555) 987-6543\nEmail: hello@apexsolutions.com', style: { textAlign: 'center', color: '#64748b', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Get in Touch', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: '500 Business Park Drive, Suite 200\nPhone: (555) 987-6543\nEmail: hello@apexsolutions.com', style: { textAlign: 'center', color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, { type: 'Section', props: { style: { padding: '40px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'Request a Consultation', level: 'h2', style: { color: '#1e293b', marginBottom: '20px' } } }, + { type: 'Heading', props: { text: 'Request a Consultation', level: 'h2', style: { color: tokenRef('text'), marginBottom: '20px' } } }, { type: 'FormContainer', props: { style: { maxWidth: '600px' } }, @@ -641,17 +650,17 @@ const smallBusiness: TemplateDefinition = { { type: 'InputField', props: { label: 'Email', placeholder: 'your@email.com', inputType: 'email' } }, { type: 'InputField', props: { label: 'Company', placeholder: 'Company name' } }, { type: 'TextareaField', props: { label: 'How can we help?', placeholder: 'Tell us about your business goals...' } }, - { type: 'FormButton', props: { text: 'Send Request', style: { backgroundColor: '#2563eb', color: '#ffffff' } } }, + { type: 'FormButton', props: { text: 'Send Request', style: { backgroundColor: tokenRef('primary'), color: '#ffffff' } } }, ], }, ], }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#f8fafc', textAlign: 'center' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } }, children: [ - { type: 'Heading', props: { text: 'Office Hours', level: 'h3', style: { color: '#1e293b', marginBottom: '12px' } } }, - { type: 'TextBlock', props: { text: 'Monday \u2013 Friday: 9:00 AM \u2013 6:00 PM\nSaturday: By appointment\nSunday: Closed', style: { color: '#64748b', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Office Hours', level: 'h3', style: { color: tokenRef('text'), marginBottom: '12px' } } }, + { type: 'TextBlock', props: { text: 'Monday \u2013 Friday: 9:00 AM \u2013 6:00 PM\nSaturday: By appointment\nSunday: Closed', style: { color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, ], @@ -678,6 +687,7 @@ const saasLanding: TemplateDefinition = { backgroundColor: '#faf5ff', textColor: '#0f172a', navStyle: 'dark', + mutedTextColor: '#64748b', }, header: makeHeader( 'FlowStack', @@ -686,11 +696,11 @@ const saasLanding: TemplateDefinition = { { text: 'Pricing', href: '#pricing' }, { text: 'Start Free', href: '#cta', isCta: true }, ], - '#0f172a', + tokenRef('secondary'), '#e2e8f0', - '#7c3aed', + tokenRef('primary'), ), - footer: makeFooterContent('FlowStack, Inc.', '#0f172a', '#94a3b8', { + footer: makeFooterContent('FlowStack, Inc.', tokenRef('secondary'), '#94a3b8', { tagline: 'Ship products faster with smart automation.', socialText: 'Twitter \u00b7 GitHub \u00b7 Discord \u00b7 LinkedIn', }), @@ -707,7 +717,10 @@ const saasLanding: TemplateDefinition = { subtitle: 'FlowStack streamlines your development workflow with smart automation, real-time collaboration, and powerful integrations. Get from idea to launch in half the time.', buttonText: 'Start Free Trial', buttonHref: '#cta', - gradient: 'linear-gradient(135deg, #7c3aed 0%, #4f46e5 50%, #2563eb 100%)', + bgType: 'gradient', + bgGradientFrom: tokenRef('primary'), + bgGradientTo: '#4f46e5', + bgGradientAngle: 135, style: { minHeight: '520px' }, }, }, @@ -726,16 +739,16 @@ const saasLanding: TemplateDefinition = { { title: 'Real-Time Collab', description: 'Work together seamlessly with live cursors, inline comments, and instant sync.', icon: '\ud83d\udc65' }, { title: '500+ Integrations', description: 'Connect with your favorite tools \u2014 GitHub, Slack, Jira, Figma, and hundreds more.', icon: '\ud83d\udd17' }, ], - style: { backgroundColor: '#faf5ff' }, + style: { backgroundColor: tokenRef('bg') }, }, }, { type: 'Section', props: { style: { padding: '80px 20px', backgroundColor: '#ffffff' } }, children: [ - { type: 'Heading', props: { text: 'Simple, Transparent Pricing', level: 'h2', style: { textAlign: 'center', color: '#0f172a', marginBottom: '8px' } } }, - { type: 'TextBlock', props: { text: 'Free for individuals. Pro plans start at $12/user/month. Enterprise pricing available.', style: { textAlign: 'center', color: '#64748b', marginBottom: '32px' } } }, - { type: 'ButtonLink', props: { text: 'View Pricing Details', href: '#pricing', style: { display: 'block', textAlign: 'center', padding: '12px 32px', backgroundColor: '#7c3aed', color: '#ffffff', borderRadius: '8px', fontWeight: '600', margin: '0 auto', maxWidth: '220px' } } }, + { type: 'Heading', props: { text: 'Simple, Transparent Pricing', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '8px' } } }, + { type: 'TextBlock', props: { text: 'Free for individuals. Pro plans start at $12/user/month. Enterprise pricing available.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '32px' } } }, + { type: 'ButtonLink', props: { text: 'View Pricing Details', href: '#pricing', style: { display: 'block', textAlign: 'center', padding: '12px 32px', backgroundColor: tokenRef('primary'), color: '#ffffff', borderRadius: '8px', fontWeight: '600', margin: '0 auto', maxWidth: '220px' } } }, ], }, { @@ -745,7 +758,7 @@ const saasLanding: TemplateDefinition = { description: 'Join thousands of teams shipping better software. No credit card required.', buttonText: 'Create Free Account', buttonHref: '#cta', - gradient: 'linear-gradient(135deg, #7c3aed 0%, #4f46e5 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #4f46e5 100%)`, }, }, ], @@ -772,6 +785,7 @@ const agency: TemplateDefinition = { backgroundColor: '#171717', textColor: '#fafafa', navStyle: 'dark', + mutedTextColor: '#a3a3a3', }, header: makeHeader( 'FORGE', @@ -781,11 +795,11 @@ const agency: TemplateDefinition = { { text: 'Team', href: 'team' }, { text: 'Start a Project', href: 'contact', isCta: true }, ], - '#0a0a0a', - '#fafafa', - '#f97316', + tokenRef('secondary'), + tokenRef('text'), + tokenRef('primary'), ), - footer: makeFooterContent('FORGE Agency', '#0a0a0a', '#737373', { + footer: makeFooterContent('FORGE Agency', tokenRef('secondary'), '#737373', { tagline: 'Built with passion. Designed to perform.', contactText: 'hello@forgeagency.com \u00b7 New York / London / Remote', socialText: 'Instagram \u00b7 Dribbble \u00b7 LinkedIn \u00b7 Twitter', @@ -803,7 +817,10 @@ const agency: TemplateDefinition = { subtitle: 'FORGE is a creative agency specializing in branding, digital design, and marketing strategy for ambitious companies.', buttonText: 'See Our Work', buttonHref: 'work', - gradient: 'linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 100%)', + bgType: 'gradient', + bgGradientFrom: tokenRef('secondary'), + bgGradientTo: '#1a1a2e', + bgGradientAngle: 135, style: { minHeight: '550px' }, }, }, @@ -815,15 +832,15 @@ const agency: TemplateDefinition = { { title: 'Digital Design', description: 'Websites, apps, and digital experiences that look stunning and perform brilliantly.', icon: '\u2726' }, { title: 'Marketing', description: 'Campaigns that cut through the noise and deliver real business results.', icon: '\ud83d\udce1' }, ], - style: { backgroundColor: '#171717' }, + style: { backgroundColor: tokenRef('bg') }, }, }, { type: 'Section', - props: { style: { padding: '80px 20px', backgroundColor: '#0a0a0a', textAlign: 'center' } }, + props: { style: { padding: '80px 20px', backgroundColor: tokenRef('secondary'), textAlign: 'center' } }, children: [ - { type: 'Heading', props: { text: 'Results That Speak', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '24px' } } }, - { type: 'TextBlock', props: { text: '150+ brands launched \u00b7 $2B+ in client revenue generated \u00b7 12 industry awards', style: { color: '#a3a3a3', fontSize: '18px', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Results That Speak', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '24px' } } }, + { type: 'TextBlock', props: { text: '150+ brands launched \u00b7 $2B+ in client revenue generated \u00b7 12 industry awards', style: { color: tokenRef('muted'), fontSize: '18px', lineHeight: '1.8' } } }, ], }, { @@ -833,7 +850,7 @@ const agency: TemplateDefinition = { description: 'We would love to hear about it. Let us turn your vision into reality.', buttonText: 'Get in Touch', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #f97316 0%, #ea580c 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #ea580c 100%)`, }, }, ], @@ -846,10 +863,10 @@ const agency: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#171717' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Selected Work', level: 'h1', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'A showcase of projects that pushed boundaries and delivered results.', style: { textAlign: 'center', color: '#a3a3a3', marginBottom: '40px' } } }, + { type: 'Heading', props: { text: 'Selected Work', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'A showcase of projects that pushed boundaries and delivered results.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } }, ], }, { @@ -860,15 +877,15 @@ const agency: TemplateDefinition = { { title: 'Horizon App', description: 'Mobile app design and development for a travel company. 50K downloads in the first month.', icon: '\ud83d\udcf1' }, { title: 'Summit Conference', description: 'Event branding, website, and marketing campaign for a 5,000-attendee tech conference.', icon: '\ud83c\udfd4\ufe0f' }, ], - style: { backgroundColor: '#0a0a0a' }, + style: { backgroundColor: tokenRef('secondary') }, }, }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#171717' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Our Approach', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Every project starts with understanding your audience, your goals, and your competitive landscape. We do not just make things look good \u2014 we engineer experiences that drive measurable business outcomes. From initial research to final deployment, our team stays embedded with yours.', style: { color: '#a3a3a3', lineHeight: '1.8', maxWidth: '700px' } } }, + { type: 'Heading', props: { text: 'Our Approach', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Every project starts with understanding your audience, your goals, and your competitive landscape. We do not just make things look good \u2014 we engineer experiences that drive measurable business outcomes. From initial research to final deployment, our team stays embedded with yours.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } }, ], }, { @@ -878,7 +895,7 @@ const agency: TemplateDefinition = { description: 'We have case studies with detailed results for every project. Get in touch to learn more.', buttonText: 'Request Case Studies', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #f97316 0%, #ea580c 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #ea580c 100%)`, }, }, ], @@ -891,10 +908,10 @@ const agency: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#171717' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'The Team', level: 'h1', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'Creatives, strategists, and builders united by a passion for craft.', style: { textAlign: 'center', color: '#a3a3a3', marginBottom: '40px' } } }, + { type: 'Heading', props: { text: 'The Team', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'Creatives, strategists, and builders united by a passion for craft.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } }, ], }, { @@ -905,15 +922,15 @@ const agency: TemplateDefinition = { { title: 'Sam Chen', description: 'Head of Strategy. Former McKinsey consultant turned brand strategist.', icon: '\ud83e\udde0' }, { title: 'Jordan Blake', description: 'Tech Lead. Full-stack developer obsessed with performance and beautiful code.', icon: '\u2699\ufe0f' }, ], - style: { backgroundColor: '#0a0a0a' }, + style: { backgroundColor: tokenRef('secondary') }, }, }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#171717' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Our Culture', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'We are a remote-first team of 22 people spread across 8 countries. We believe great work comes from diverse perspectives, deep trust, and the freedom to challenge conventional thinking. Every voice matters, from intern to partner.', style: { color: '#a3a3a3', lineHeight: '1.8', maxWidth: '700px' } } }, + { type: 'Heading', props: { text: 'Our Culture', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'We are a remote-first team of 22 people spread across 8 countries. We believe great work comes from diverse perspectives, deep trust, and the freedom to challenge conventional thinking. Every voice matters, from intern to partner.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } }, ], }, { @@ -923,7 +940,7 @@ const agency: TemplateDefinition = { description: 'We are always looking for talented, ambitious people. Check out open roles or send us your portfolio.', buttonText: 'View Open Roles', buttonHref: 'contact', - gradient: 'linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 100%)', + gradient: `linear-gradient(135deg, ${tokenRef('secondary')} 0%, #1a1a2e 100%)`, }, }, ], @@ -936,15 +953,15 @@ const agency: TemplateDefinition = { components: [ { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#171717' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } }, children: [ - { type: 'Heading', props: { text: 'Start a Project', level: 'h1', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } }, - { type: 'TextBlock', props: { text: 'hello@forgeagency.com\nNew York / London / Remote', style: { textAlign: 'center', color: '#a3a3a3', marginBottom: '40px' } } }, + { type: 'Heading', props: { text: 'Start a Project', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } }, + { type: 'TextBlock', props: { text: 'hello@forgeagency.com\nNew York / London / Remote', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } }, ], }, { type: 'Section', - props: { style: { padding: '40px 20px', backgroundColor: '#0a0a0a' } }, + props: { style: { padding: '40px 20px', backgroundColor: tokenRef('secondary') } }, children: [ { type: 'FormContainer', @@ -954,17 +971,17 @@ const agency: TemplateDefinition = { { type: 'InputField', props: { label: 'Email', placeholder: 'your@email.com', inputType: 'email' } }, { type: 'InputField', props: { label: 'Budget Range', placeholder: '$10K - $50K' } }, { type: 'TextareaField', props: { label: 'Project Details', placeholder: 'Tell us what you need...' } }, - { type: 'FormButton', props: { text: 'Send Brief', style: { backgroundColor: '#f97316', color: '#ffffff' } } }, + { type: 'FormButton', props: { text: 'Send Brief', style: { backgroundColor: tokenRef('primary'), color: '#ffffff' } } }, ], }, ], }, { type: 'Section', - props: { style: { padding: '60px 20px', backgroundColor: '#171717', textAlign: 'center' } }, + props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } }, children: [ - { type: 'Heading', props: { text: 'Response Time', level: 'h3', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '12px' } } }, - { type: 'TextBlock', props: { text: 'We respond to all inquiries within 24 hours. For urgent projects, call us directly at (212) 555-0199.', style: { color: '#a3a3a3', lineHeight: '1.8' } } }, + { type: 'Heading', props: { text: 'Response Time', level: 'h3', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '12px' } } }, + { type: 'TextBlock', props: { text: 'We respond to all inquiries within 24 hours. For urgent projects, call us directly at (212) 555-0199.', style: { color: tokenRef('muted'), lineHeight: '1.8' } } }, ], }, ],