feat(site-builder): re-author medical/portfolio/photography/content-creator templates onto design tokens
Palette and heading fonts become var(--wsb-*) refs; mutedTextColor added to each template's design. Gradient companion stops stay hex. Refs #44 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ce3db23506
commit
e94bec8d58
2 files changed
+251
-107
No files matched your search
@@ -0,0 +1,137 @@
|
||||
import { describe, test, expect, afterEach } from 'vitest';
|
||||
import { renderEditorHarness, EditorHarness } from '../test-utils/editorHarness';
|
||||
import { allTemplates } from './index';
|
||||
import type { TemplateComponent } 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';
|
||||
import { tokenRef } from '../utils/tokens';
|
||||
|
||||
/**
|
||||
* Group B templates (#44): every colour/font that is one of the template's own
|
||||
* `design` values must be a `var(--wsb-*)` reference in component props, so
|
||||
* changing Site Design after applying the template restyles it.
|
||||
*/
|
||||
const IDS = ['medical', 'portfolio', 'photography', 'content-creator'] as const;
|
||||
const COLOR_KEYS = [
|
||||
'primaryColor', 'secondaryColor', 'accentColor', 'backgroundColor', 'textColor', 'mutedTextColor',
|
||||
] as const;
|
||||
const FONT_KEYS = ['headingFont', 'bodyFont'] as const;
|
||||
|
||||
/**
|
||||
* Design hexes that still appear in the exported first-page body because a
|
||||
* COMPONENT hard-codes them (not the template): CTASection's heading/button
|
||||
* (#ffffff, #18181b -- it has no colour props), FeaturesGrid's card
|
||||
* background (#ffffff) and title (#18181b). Pinned as exact counts so a
|
||||
* template that re-introduces a literal still fails; removing them needs a
|
||||
* component change (out of scope for the template pass, tracked in the report).
|
||||
*/
|
||||
const COMPONENT_DEFAULT_HEX: Record<string, Record<string, number>> = {
|
||||
medical: { '#ffffff': 5 },
|
||||
portfolio: { '#18181b': 4, '#ffffff': 2 },
|
||||
photography: {},
|
||||
'content-creator': {},
|
||||
};
|
||||
|
||||
function* strings(v: unknown): Generator<string> {
|
||||
if (typeof v === 'string') yield v;
|
||||
else if (Array.isArray(v)) for (const x of v) yield* strings(x);
|
||||
else if (v && typeof v === 'object') for (const x of Object.values(v)) yield* strings(x);
|
||||
}
|
||||
function* walk(comps: TemplateComponent[]): Generator<TemplateComponent> {
|
||||
for (const c of comps) {
|
||||
yield c;
|
||||
if (c.children) yield* walk(c.children);
|
||||
}
|
||||
}
|
||||
|
||||
let harness: EditorHarness | null = null;
|
||||
afterEach(() => {
|
||||
harness?.unmount();
|
||||
harness = null;
|
||||
});
|
||||
|
||||
describe.each(IDS)('template %s is token-driven', (id) => {
|
||||
const tpl = allTemplates.find((t) => t.id === id)!;
|
||||
const design = tpl.design as Record<string, string>;
|
||||
const colors = COLOR_KEYS.map((k) => design[k]).filter(Boolean).map((c) => c.toLowerCase());
|
||||
const fonts = FONT_KEYS.map((k) => design[k]).filter(Boolean);
|
||||
const firstPage = tpl.pages[0].content.components;
|
||||
|
||||
test('sanity: the design declares colours, a muted role and fonts', () => {
|
||||
expect(tpl).toBeDefined();
|
||||
expect(colors.length).toBeGreaterThanOrEqual(5);
|
||||
expect(design.mutedTextColor).toBeTruthy();
|
||||
expect(fonts.length).toBe(2);
|
||||
});
|
||||
|
||||
test('no component prop (header, footer, any page) hard-codes a design colour or font', () => {
|
||||
const all = [
|
||||
...tpl.header.components,
|
||||
...tpl.footer.components,
|
||||
...tpl.pages.flatMap((p) => p.content.components),
|
||||
];
|
||||
const offenders: string[] = [];
|
||||
for (const c of walk(all)) {
|
||||
for (const s of strings(c.props)) {
|
||||
const lower = s.toLowerCase();
|
||||
for (const col of colors) if (lower.includes(col)) offenders.push(`${c.type}: ${s.slice(0, 80)}`);
|
||||
for (const f of fonts) if (s.trim() === f) offenders.push(`${c.type}: ${s}`);
|
||||
}
|
||||
}
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
test('applies through templateComponentToTreeNode + buildNodeTree in a real harness', () => {
|
||||
harness = renderEditorHarness();
|
||||
expect(() => {
|
||||
harness!.act(() => {
|
||||
for (const comp of firstPage) {
|
||||
harness!.actions.addNodeTree(buildNodeTree(harness!.query, templateComponentToTreeNode(comp)), 'ROOT');
|
||||
}
|
||||
});
|
||||
}).not.toThrow();
|
||||
|
||||
const { html } = exportBodyHtml(harness.getSerialized());
|
||||
expect(html).toContain(tokenRef('primary'));
|
||||
const known = COMPONENT_DEFAULT_HEX[id];
|
||||
if (!(design.primaryColor.toLowerCase() in known)) {
|
||||
expect(html.toLowerCase()).not.toContain(design.primaryColor.toLowerCase());
|
||||
}
|
||||
});
|
||||
|
||||
test('exported document defines the template primary in :root and the body no longer carries the old hexes', () => {
|
||||
harness = renderEditorHarness();
|
||||
harness.act(() => {
|
||||
for (const comp of firstPage) {
|
||||
harness!.actions.addNodeTree(buildNodeTree(harness!.query, templateComponentToTreeNode(comp)), 'ROOT');
|
||||
}
|
||||
});
|
||||
const { html } = exportToHtml(harness.getSerialized(), {
|
||||
design: { ...DEFAULT_SITE_DESIGN, ...tpl.design },
|
||||
});
|
||||
const root = /:root\{([^}]*)\}/.exec(html);
|
||||
expect(root).not.toBeNull();
|
||||
expect(root![1]).toContain(`--wsb-primary:${design.primaryColor}`);
|
||||
const body = html.slice(html.indexOf('<body'));
|
||||
for (const col of new Set(colors)) {
|
||||
const n = (body.toLowerCase().match(new RegExp(col, 'g')) || []).length;
|
||||
expect(n, `${id} body occurrences of ${col}`).toBe(COMPONENT_DEFAULT_HEX[id][col] ?? 0);
|
||||
}
|
||||
});
|
||||
|
||||
test('restyling Site Design restyles the body: changing primary changes only :root, not the body', () => {
|
||||
harness = renderEditorHarness();
|
||||
harness.act(() => {
|
||||
for (const comp of firstPage) {
|
||||
harness!.actions.addNodeTree(buildNodeTree(harness!.query, templateComponentToTreeNode(comp)), 'ROOT');
|
||||
}
|
||||
});
|
||||
const ser = harness.getSerialized();
|
||||
const a = exportToHtml(ser, { design: { ...DEFAULT_SITE_DESIGN, ...tpl.design } }).html;
|
||||
const b = exportToHtml(ser, { design: { ...DEFAULT_SITE_DESIGN, ...tpl.design, primaryColor: '#123abc' } }).html;
|
||||
expect(b).toContain('--wsb-primary:#123abc');
|
||||
expect(b.slice(b.indexOf('<body'))).toBe(a.slice(a.indexOf('<body')));
|
||||
});
|
||||
});
|
||||
+114
-107
@@ -1,4 +1,5 @@
|
||||
import { SiteDesign } from '../state/SiteDesignContext';
|
||||
import { tokenRef } from '../utils/tokens';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
@@ -990,6 +991,7 @@ const medical: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#ffffff',
|
||||
textColor: '#134e4a',
|
||||
mutedTextColor: '#5c8a85',
|
||||
navStyle: 'light',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -1000,11 +1002,12 @@ const medical: TemplateDefinition = {
|
||||
{ text: 'About', href: 'about' },
|
||||
{ text: 'Book Appointment', href: 'contact', isCta: true },
|
||||
],
|
||||
'#ffffff',
|
||||
'#134e4a',
|
||||
'#0d9488',
|
||||
tokenRef('bg'),
|
||||
tokenRef('text'),
|
||||
tokenRef('primary'),
|
||||
tokenRef('bg'),
|
||||
),
|
||||
footer: makeFooterContent('Greenfield Dental', '#134e4a', '#5eead4', {
|
||||
footer: makeFooterContent('Greenfield Dental', tokenRef('secondary'), '#5eead4', {
|
||||
tagline: 'Your smile, our priority.',
|
||||
contactText: '456 Wellness Blvd, Suite 100 \u00b7 (555) 321-0987',
|
||||
}),
|
||||
@@ -1021,7 +1024,7 @@ const medical: TemplateDefinition = {
|
||||
subtitle: 'Modern dentistry in a comfortable, welcoming environment. Accepting new patients \u2014 schedule your visit today.',
|
||||
buttonText: 'Book Appointment',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #0d9488 0%, #0f766e 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #0f766e 100%)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1037,18 +1040,18 @@ const medical: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '80px 20px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '80px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Why Patients Choose Us', level: 'h2', style: { textAlign: 'center', color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'With over 15 years of experience, state-of-the-art technology, and a team that genuinely cares, Greenfield Dental has become the trusted dental home for thousands of families in our community.', style: { textAlign: 'center', maxWidth: '650px', margin: '0 auto', color: '#5c8a85', lineHeight: '1.8' } } },
|
||||
{ type: 'Heading', props: { text: 'Why Patients Choose Us', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'With over 15 years of experience, state-of-the-art technology, and a team that genuinely cares, Greenfield Dental has become the trusted dental home for thousands of families in our community.', style: { textAlign: 'center', maxWidth: '650px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Patient Testimonials', level: 'h2', style: { textAlign: 'center', color: '#134e4a', marginBottom: '24px' } } },
|
||||
{ type: 'TextBlock', props: { text: '"Dr. Greenfield and her team made my first visit so comfortable. I actually look forward to my appointments now!" \u2014 Rachel P.', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: '#5c8a85', lineHeight: '1.8', fontStyle: 'italic' } } },
|
||||
{ type: 'Heading', props: { text: 'Patient Testimonials', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '24px' } } },
|
||||
{ type: 'TextBlock', props: { text: '"Dr. Greenfield and her team made my first visit so comfortable. I actually look forward to my appointments now!" \u2014 Rachel P.', style: { textAlign: 'center', maxWidth: '600px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8', fontStyle: 'italic' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1058,7 +1061,7 @@ const medical: TemplateDefinition = {
|
||||
description: 'Comprehensive exam, cleaning, and X-rays for just $99. Limited time offer for new patients.',
|
||||
buttonText: 'Claim Your Offer',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #14b8a6 0%, #0d9488 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('accent')} 0%, ${tokenRef('primary')} 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1073,8 +1076,8 @@ const medical: TemplateDefinition = {
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Services', level: 'h1', style: { textAlign: 'center', color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Complete dental care under one roof.', style: { textAlign: 'center', color: '#5c8a85' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Services', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Complete dental care under one roof.', style: { textAlign: 'center', color: tokenRef('muted') } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1085,7 +1088,7 @@ const medical: TemplateDefinition = {
|
||||
{ title: 'Restorative', description: 'Fillings, crowns, bridges, and implants to restore damaged or missing teeth.', icon: '\ud83d\udd27' },
|
||||
{ title: 'Orthodontics', description: 'Invisalign and traditional braces for children and adults.', icon: '\ud83d\ude01' },
|
||||
],
|
||||
style: { backgroundColor: '#ffffff' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1096,15 +1099,15 @@ const medical: TemplateDefinition = {
|
||||
{ title: 'Oral Surgery', description: 'Wisdom teeth removal, extractions, and surgical procedures with sedation options.', icon: '\ud83c\udfe5' },
|
||||
{ title: 'Teeth Whitening', description: 'Professional in-office and take-home whitening treatments for a brighter smile.', icon: '\ud83d\udca1' },
|
||||
],
|
||||
style: { backgroundColor: '#ffffff', paddingTop: '0' },
|
||||
style: { backgroundColor: tokenRef('bg'), paddingTop: '0' },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa', textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Insurance & Payment', level: 'h2', style: { color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We accept most major dental insurance plans and offer flexible payment options including CareCredit. Our front desk team will verify your benefits before your appointment so there are no surprises.', style: { color: '#5c8a85', lineHeight: '1.8', maxWidth: '650px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Insurance & Payment', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We accept most major dental insurance plans and offer flexible payment options including CareCredit. Our front desk team will verify your benefits before your appointment so there are no surprises.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '650px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1114,7 +1117,7 @@ const medical: TemplateDefinition = {
|
||||
description: 'Schedule a consultation to discuss your dental health goals.',
|
||||
buttonText: 'Book Now',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #0d9488 0%, #0f766e 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #0f766e 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1129,8 +1132,8 @@ const medical: TemplateDefinition = {
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Meet Dr. Sarah Greenfield', level: 'h1', style: { textAlign: 'center', color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Dr. Greenfield graduated top of her class from the University of Pacific School of Dentistry and has been providing exceptional dental care since 2010. She is passionate about patient education and making every visit as comfortable as possible.', style: { textAlign: 'center', maxWidth: '700px', margin: '0 auto', color: '#5c8a85', lineHeight: '1.8' } } },
|
||||
{ type: 'Heading', props: { text: 'Meet Dr. Sarah Greenfield', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Dr. Greenfield graduated top of her class from the University of Pacific School of Dentistry and has been providing exceptional dental care since 2010. She is passionate about patient education and making every visit as comfortable as possible.', style: { textAlign: 'center', maxWidth: '700px', margin: '0 auto', color: tokenRef('muted'), lineHeight: '1.8' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1141,23 +1144,23 @@ const medical: TemplateDefinition = {
|
||||
{ title: 'Continuing Education', description: 'Regular training in the latest dental technologies and techniques.', icon: '\ud83d\udcda' },
|
||||
{ title: 'Community Focused', description: 'Volunteer dental clinics and school programs every quarter.', icon: '\u2764\ufe0f' },
|
||||
],
|
||||
style: { backgroundColor: '#ffffff' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Team', level: 'h2', style: { color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Our team includes two dental hygienists, three dental assistants, and a dedicated front desk coordinator. Together, we create a warm, professional environment where every patient feels like family. We take the time to explain every procedure and answer all your questions.', style: { color: '#5c8a85', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Team', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Our team includes two dental hygienists, three dental assistants, and a dedicated front desk coordinator. Together, we create a warm, professional environment where every patient feels like family. We take the time to explain every procedure and answer all your questions.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Technology', level: 'h2', style: { color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Digital X-rays, intraoral cameras, laser dentistry, and 3D imaging \u2014 we invest in the latest technology to provide more accurate diagnoses, less invasive treatments, and faster healing times.', style: { color: '#5c8a85', lineHeight: '1.8', maxWidth: '650px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Technology', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Digital X-rays, intraoral cameras, laser dentistry, and 3D imaging \u2014 we invest in the latest technology to provide more accurate diagnoses, less invasive treatments, and faster healing times.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '650px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1172,13 +1175,13 @@ const medical: TemplateDefinition = {
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Book an Appointment', level: 'h1', style: { textAlign: 'center', color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '456 Wellness Blvd, Suite 100\nPhone: (555) 321-0987\nMon\u2013Fri: 8am\u20135pm, Sat: 9am\u20131pm', style: { textAlign: 'center', color: '#5c8a85', lineHeight: '1.8' } } },
|
||||
{ type: 'Heading', props: { text: 'Book an Appointment', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '456 Wellness Blvd, Suite 100\nPhone: (555) 321-0987\nMon\u2013Fri: 8am\u20135pm, Sat: 9am\u20131pm', style: { textAlign: 'center', color: tokenRef('muted'), lineHeight: '1.8' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '40px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{
|
||||
type: 'FormContainer',
|
||||
@@ -1188,7 +1191,7 @@ const medical: TemplateDefinition = {
|
||||
{ type: 'InputField', props: { label: 'Phone', placeholder: '(555) 000-0000' } },
|
||||
{ type: 'InputField', props: { label: 'Preferred Date', placeholder: 'MM/DD/YYYY' } },
|
||||
{ type: 'TextareaField', props: { label: 'Notes', placeholder: 'Any concerns or special requests...' } },
|
||||
{ type: 'FormButton', props: { text: 'Request Appointment', style: { backgroundColor: '#0d9488', color: '#ffffff' } } },
|
||||
{ type: 'FormButton', props: { text: 'Request Appointment', style: { backgroundColor: tokenRef('primary'), color: tokenRef('bg') } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1197,8 +1200,8 @@ const medical: TemplateDefinition = {
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdfa', textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Emergency Dental Care', level: 'h2', style: { color: '#134e4a', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Experiencing a dental emergency? Call us immediately at (555) 321-0987. We offer same-day emergency appointments and will do everything we can to see you as quickly as possible.', style: { color: '#5c8a85', lineHeight: '1.8', maxWidth: '600px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Emergency Dental Care', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Experiencing a dental emergency? Call us immediately at (555) 321-0987. We offer same-day emergency appointments and will do everything we can to see you as quickly as possible.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '600px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1228,6 +1231,7 @@ const portfolio: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#ffffff',
|
||||
textColor: '#18181b',
|
||||
mutedTextColor: '#71717a',
|
||||
navStyle: 'light',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -1238,11 +1242,12 @@ const portfolio: TemplateDefinition = {
|
||||
{ text: 'About', href: 'about' },
|
||||
{ text: 'Contact', href: 'contact', isCta: true },
|
||||
],
|
||||
'#ffffff',
|
||||
'#18181b',
|
||||
'#18181b',
|
||||
tokenRef('bg'),
|
||||
tokenRef('text'),
|
||||
tokenRef('primary'),
|
||||
tokenRef('bg'),
|
||||
),
|
||||
footer: makeFooterContent('Jane Doe', '#18181b', '#a3a3a3', {
|
||||
footer: makeFooterContent('Jane Doe', tokenRef('primary'), tokenRef('accent'), {
|
||||
tagline: 'Designed with intention.',
|
||||
socialText: 'Dribbble \u00b7 Behance \u00b7 LinkedIn \u00b7 Twitter',
|
||||
}),
|
||||
@@ -1254,19 +1259,19 @@ const portfolio: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '120px 20px 80px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '120px 20px 80px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Jane Doe', level: 'h1', style: { fontSize: '56px', fontWeight: '700', color: '#18181b', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Product Designer & Visual Storyteller. I create digital experiences that are intuitive, beautiful, and human-centered.', style: { fontSize: '20px', color: '#71717a', maxWidth: '600px', lineHeight: '1.7' } } },
|
||||
{ type: 'Heading', props: { text: 'Jane Doe', level: 'h1', style: { fontSize: '56px', fontWeight: '700', color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Product Designer & Visual Storyteller. I create digital experiences that are intuitive, beautiful, and human-centered.', style: { fontSize: '20px', color: tokenRef('muted'), maxWidth: '600px', lineHeight: '1.7' } } },
|
||||
{ type: 'Spacer', props: { height: '32px' } },
|
||||
{ type: 'ButtonLink', props: { text: 'View Projects', href: 'projects', style: { padding: '12px 32px', backgroundColor: '#18181b', color: '#ffffff', borderRadius: '8px', fontWeight: '600', fontSize: '15px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'View Projects', href: 'projects', style: { padding: '12px 32px', backgroundColor: tokenRef('primary'), color: tokenRef('bg'), borderRadius: '8px', fontWeight: '600', fontSize: '15px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px 80px', backgroundColor: '#fafafa' } },
|
||||
props: { style: { padding: '40px 20px 80px', backgroundColor: tokenRef('secondary') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Selected Work', level: 'h2', style: { color: '#18181b', marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'Selected Work', level: 'h2', style: { color: tokenRef('text'), marginBottom: '32px' } } },
|
||||
{ type: 'ImageBlock', props: { alt: 'Project preview', style: { width: '100%', borderRadius: '12px', marginBottom: '24px' } } },
|
||||
{ type: 'ImageBlock', props: { alt: 'Project preview', style: { width: '100%', borderRadius: '12px' } } },
|
||||
],
|
||||
@@ -1279,7 +1284,7 @@ const portfolio: TemplateDefinition = {
|
||||
{ title: 'Brand Identity', description: 'Logos, typography systems, and visual guidelines that define your brand.', icon: '\ud83c\udfa8' },
|
||||
{ title: 'Design Systems', description: 'Scalable component libraries that keep teams aligned and efficient.', icon: '\ud83e\udde9' },
|
||||
],
|
||||
style: { backgroundColor: '#ffffff' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1289,7 +1294,7 @@ const portfolio: TemplateDefinition = {
|
||||
description: 'I am always open to new collaborations and interesting challenges.',
|
||||
buttonText: 'Get in Touch',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #18181b 0%, #3f3f46 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #3f3f46 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1302,31 +1307,31 @@ const portfolio: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Projects', level: 'h1', style: { color: '#18181b', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'A curated selection of product design, branding, and digital experience work.', style: { color: '#71717a', marginBottom: '40px', fontSize: '18px' } } },
|
||||
{ type: 'Heading', props: { text: 'Projects', level: 'h1', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'A curated selection of product design, branding, and digital experience work.', style: { color: tokenRef('muted'), marginBottom: '40px', fontSize: '18px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '20px 20px 40px', backgroundColor: '#fafafa' } },
|
||||
props: { style: { padding: '20px 20px 40px', backgroundColor: tokenRef('secondary') } },
|
||||
children: [
|
||||
{ type: 'ImageBlock', props: { alt: 'Project 1', style: { width: '100%', borderRadius: '12px', marginBottom: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'Finova Banking App', level: 'h3', style: { color: '#18181b', marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Complete redesign of a mobile banking experience for 2M+ users. Reduced support tickets by 40%.', style: { color: '#71717a', marginBottom: '40px' } } },
|
||||
{ type: 'Heading', props: { text: 'Finova Banking App', level: 'h3', style: { color: tokenRef('text'), marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Complete redesign of a mobile banking experience for 2M+ users. Reduced support tickets by 40%.', style: { color: tokenRef('muted'), marginBottom: '40px' } } },
|
||||
{ type: 'ImageBlock', props: { alt: 'Project 2', style: { width: '100%', borderRadius: '12px', marginBottom: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'Bloom E-Commerce', level: 'h3', style: { color: '#18181b', marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Brand identity and web store design for a sustainable fashion brand. Launched to $200K in first-month sales.', style: { color: '#71717a' } } },
|
||||
{ type: 'Heading', props: { text: 'Bloom E-Commerce', level: 'h3', style: { color: tokenRef('text'), marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Brand identity and web store design for a sustainable fashion brand. Launched to $200K in first-month sales.', style: { color: tokenRef('muted') } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px 80px', backgroundColor: '#fafafa' } },
|
||||
props: { style: { padding: '40px 20px 80px', backgroundColor: tokenRef('secondary') } },
|
||||
children: [
|
||||
{ type: 'ImageBlock', props: { alt: 'Project 3', style: { width: '100%', borderRadius: '12px', marginBottom: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'Waypoint Travel', level: 'h3', style: { color: '#18181b', marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Redesigned the trip planning experience for a travel startup. App Store featured within first week of launch.', style: { color: '#71717a' } } },
|
||||
{ type: 'Heading', props: { text: 'Waypoint Travel', level: 'h3', style: { color: tokenRef('text'), marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Redesigned the trip planning experience for a travel startup. App Store featured within first week of launch.', style: { color: tokenRef('muted') } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1336,7 +1341,7 @@ const portfolio: TemplateDefinition = {
|
||||
description: 'I would love to hear about your next project.',
|
||||
buttonText: 'Contact Me',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #18181b 0%, #3f3f46 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #3f3f46 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1349,10 +1354,10 @@ const portfolio: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'About Me', level: 'h1', style: { color: '#18181b', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'I am a product designer based in San Francisco with 8 years of experience creating digital products for startups and Fortune 500 companies. I believe great design is invisible \u2014 it just works.\n\nWhen I am not designing, you will find me hiking coastal trails, experimenting with film photography, or teaching design workshops.', style: { color: '#71717a', lineHeight: '1.8', maxWidth: '700px', fontSize: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'About Me', level: 'h1', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'I am a product designer based in San Francisco with 8 years of experience creating digital products for startups and Fortune 500 companies. I believe great design is invisible \u2014 it just works.\n\nWhen I am not designing, you will find me hiking coastal trails, experimenting with film photography, or teaching design workshops.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px', fontSize: '16px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1363,15 +1368,15 @@ const portfolio: TemplateDefinition = {
|
||||
{ title: '8 Years Experience', description: 'Deep expertise in product design, user research, and design systems.', icon: '\u23f0' },
|
||||
{ title: '3 Awards', description: 'Recognized by Awwwards, CSS Design Awards, and UXDA.', icon: '\ud83c\udfc6' },
|
||||
],
|
||||
style: { backgroundColor: '#fafafa' },
|
||||
style: { backgroundColor: tokenRef('secondary') },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Tools & Skills', level: 'h2', style: { color: '#18181b', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Figma, Sketch, Adobe Creative Suite, Framer, Webflow, HTML/CSS, JavaScript, React. I am tool-agnostic and pick what is best for each project. My process emphasizes user research, rapid prototyping, and iterative testing.', style: { color: '#71717a', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'Tools & Skills', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Figma, Sketch, Adobe Creative Suite, Framer, Webflow, HTML/CSS, JavaScript, React. I am tool-agnostic and pick what is best for each project. My process emphasizes user research, rapid prototyping, and iterative testing.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1384,10 +1389,10 @@ const portfolio: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Get in Touch', level: 'h1', style: { color: '#18181b', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'I am always open to new projects and collaborations.\njane@janedoe.design', style: { color: '#71717a', lineHeight: '1.8', marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'Get in Touch', level: 'h1', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'I am always open to new projects and collaborations.\njane@janedoe.design', style: { color: tokenRef('muted'), lineHeight: '1.8', marginBottom: '32px' } } },
|
||||
{
|
||||
type: 'FormContainer',
|
||||
props: { style: { maxWidth: '500px' } },
|
||||
@@ -1395,17 +1400,17 @@ const portfolio: 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 me about your project...' } },
|
||||
{ type: 'FormButton', props: { text: 'Send', style: { backgroundColor: '#18181b', color: '#ffffff' } } },
|
||||
{ type: 'FormButton', props: { text: 'Send', style: { backgroundColor: tokenRef('primary'), color: tokenRef('bg') } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fafafa', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('secondary'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Find Me Online', level: 'h3', style: { color: '#18181b', marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Dribbble \u00b7 Behance \u00b7 LinkedIn \u00b7 Twitter\nSan Francisco, CA', style: { color: '#71717a', lineHeight: '1.8' } } },
|
||||
{ type: 'Heading', props: { text: 'Find Me Online', level: 'h3', style: { color: tokenRef('text'), marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Dribbble \u00b7 Behance \u00b7 LinkedIn \u00b7 Twitter\nSan Francisco, CA', style: { color: tokenRef('muted'), lineHeight: '1.8' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1431,6 +1436,7 @@ const photography: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#0a0a0a',
|
||||
textColor: '#d4d4d4',
|
||||
mutedTextColor: '#a3a3a3',
|
||||
navStyle: 'dark',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -1440,12 +1446,12 @@ const photography: TemplateDefinition = {
|
||||
{ text: 'About', href: '#about' },
|
||||
{ text: 'Hire Me', href: '#contact', isCta: true },
|
||||
],
|
||||
'#0a0a0a',
|
||||
'#d4d4d4',
|
||||
'#d4d4d4',
|
||||
'#0a0a0a',
|
||||
tokenRef('bg'),
|
||||
tokenRef('text'),
|
||||
tokenRef('primary'),
|
||||
tokenRef('bg'),
|
||||
),
|
||||
footer: makeFooterContent('LENS Photography', '#0a0a0a', '#525252', {
|
||||
footer: makeFooterContent('LENS Photography', tokenRef('bg'), '#525252', {
|
||||
tagline: 'All images are copyrighted.',
|
||||
contactText: 'hello@lensphotography.com \u00b7 Portland, OR',
|
||||
socialText: 'Instagram \u00b7 500px \u00b7 Flickr',
|
||||
@@ -1458,15 +1464,15 @@ const photography: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '120px 20px', backgroundColor: '#0a0a0a', textAlign: 'center' } },
|
||||
props: { style: { padding: '120px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Capturing Moments That Matter', level: 'h1', style: { fontSize: '48px', color: '#fafafa', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Editorial, portrait, and landscape photography based in Portland, OR.', style: { color: '#737373', fontSize: '18px' } } },
|
||||
{ type: 'Heading', props: { text: 'Capturing Moments That Matter', level: 'h1', style: { fontSize: '48px', color: '#fafafa', fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Editorial, portrait, and landscape photography based in Portland, OR.', style: { color: tokenRef('accent'), fontSize: '18px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px', backgroundColor: '#0a0a0a' } },
|
||||
props: { style: { padding: '40px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'ImageBlock', props: { alt: 'Gallery image 1', style: { width: '100%', marginBottom: '16px', borderRadius: '4px' } } },
|
||||
{ type: 'ImageBlock', props: { alt: 'Gallery image 2', style: { width: '100%', marginBottom: '16px', borderRadius: '4px' } } },
|
||||
@@ -1488,17 +1494,17 @@ const photography: TemplateDefinition = {
|
||||
type: 'Section',
|
||||
props: { style: { padding: '80px 20px', backgroundColor: '#171717' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'About', level: 'h2', style: { color: '#fafafa', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'With 12 years behind the lens, I specialize in finding beauty in the everyday. My work has been featured in National Geographic, WIRED, and The New York Times. I am available for editorial assignments, commercial projects, and private commissions.', style: { color: '#a3a3a3', lineHeight: '1.8', maxWidth: '650px' } } },
|
||||
{ type: 'Heading', props: { text: 'About', level: 'h2', style: { color: '#fafafa', fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'With 12 years behind the lens, I specialize in finding beauty in the everyday. My work has been featured in National Geographic, WIRED, and The New York Times. I am available for editorial assignments, commercial projects, and private commissions.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '650px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#0a0a0a' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Work With Me', level: 'h2', style: { color: '#fafafa', fontFamily: 'Playfair Display, serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'hello@lensphotography.com\nPortland, OR \u2014 Available worldwide', style: { color: '#a3a3a3', lineHeight: '1.8', marginBottom: '24px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Send Inquiry', href: '#contact', style: { padding: '12px 32px', backgroundColor: '#d4d4d4', color: '#0a0a0a', borderRadius: '4px', fontWeight: '600' } } },
|
||||
{ type: 'Heading', props: { text: 'Work With Me', level: 'h2', style: { color: '#fafafa', fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'hello@lensphotography.com\nPortland, OR \u2014 Available worldwide', style: { color: tokenRef('muted'), lineHeight: '1.8', marginBottom: '24px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Send Inquiry', href: '#contact', style: { padding: '12px 32px', backgroundColor: tokenRef('primary'), color: tokenRef('bg'), borderRadius: '4px', fontWeight: '600' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1524,6 +1530,7 @@ const contentCreator: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#fdf2f8',
|
||||
textColor: '#1e1b4b',
|
||||
mutedTextColor: '#6b21a8',
|
||||
navStyle: 'dark',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -1534,11 +1541,11 @@ const contentCreator: TemplateDefinition = {
|
||||
{ text: 'About', href: 'about' },
|
||||
{ text: 'Links', href: 'links', isCta: true },
|
||||
],
|
||||
'#1e1b4b',
|
||||
tokenRef('secondary'),
|
||||
'#f0abfc',
|
||||
'#ec4899',
|
||||
tokenRef('primary'),
|
||||
),
|
||||
footer: makeFooterContent('SkyeVlogs', '#1e1b4b', '#c084fc', {
|
||||
footer: makeFooterContent('SkyeVlogs', tokenRef('secondary'), '#c084fc', {
|
||||
tagline: 'Follow the journey!',
|
||||
socialText: 'YouTube \u00b7 Instagram \u00b7 TikTok \u00b7 Twitter',
|
||||
}),
|
||||
@@ -1555,7 +1562,7 @@ const contentCreator: TemplateDefinition = {
|
||||
subtitle: 'Tech reviews, travel vlogs, and lifestyle content. 500K+ subscribers on YouTube. Let\'s hang out.',
|
||||
buttonText: 'Watch Latest Video',
|
||||
buttonHref: 'videos',
|
||||
gradient: 'linear-gradient(135deg, #ec4899 0%, #8b5cf6 50%, #6366f1 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #8b5cf6 50%, #6366f1 100%)`,
|
||||
style: { minHeight: '480px' },
|
||||
},
|
||||
},
|
||||
@@ -1567,15 +1574,15 @@ const contentCreator: TemplateDefinition = {
|
||||
{ title: 'Brand Collabs', description: 'Worked with Apple, Samsung, Adobe, and 50+ brands on authentic content.', icon: '\ud83e\udd1d' },
|
||||
{ title: 'Weekly Uploads', description: 'New videos every Tuesday and Friday. Never miss an upload!', icon: '\ud83d\udcc5' },
|
||||
],
|
||||
style: { backgroundColor: '#fdf2f8' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Latest Upload', level: 'h2', style: { textAlign: 'center', color: '#1e1b4b', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'iPhone 18 Pro Max \u2014 The REAL Truth After 30 Days\nPublished March 28, 2026 \u00b7 1.2M views', style: { textAlign: 'center', color: '#6b21a8', lineHeight: '1.8', marginBottom: '24px' } } },
|
||||
{ type: 'Heading', props: { text: 'Latest Upload', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'iPhone 18 Pro Max \u2014 The REAL Truth After 30 Days\nPublished March 28, 2026 \u00b7 1.2M views', style: { textAlign: 'center', color: tokenRef('muted'), lineHeight: '1.8', marginBottom: '24px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Watch Now on YouTube', href: '#', style: { display: 'inline-block', padding: '12px 32px', backgroundColor: '#ef4444', color: '#ffffff', borderRadius: '12px', fontWeight: '600' } } },
|
||||
],
|
||||
},
|
||||
@@ -1586,7 +1593,7 @@ const contentCreator: TemplateDefinition = {
|
||||
description: 'Join the crew and get notified when new content drops.',
|
||||
buttonText: 'Subscribe on YouTube',
|
||||
buttonHref: '#',
|
||||
gradient: 'linear-gradient(135deg, #7c3aed 0%, #ec4899 100%)',
|
||||
gradient: `linear-gradient(135deg, #7c3aed 0%, ${tokenRef('primary')} 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1599,10 +1606,10 @@ const contentCreator: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fdf2f8' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Latest Videos', level: 'h1', style: { textAlign: 'center', color: '#1e1b4b', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Check out my recent uploads and most popular content.', style: { textAlign: 'center', color: '#6b21a8', marginBottom: '40px' } } },
|
||||
{ type: 'Heading', props: { text: 'Latest Videos', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Check out my recent uploads and most popular content.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1634,7 +1641,7 @@ const contentCreator: TemplateDefinition = {
|
||||
description: 'Head over to YouTube for the full library of 200+ videos.',
|
||||
buttonText: 'Go to YouTube',
|
||||
buttonHref: '#',
|
||||
gradient: 'linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #8b5cf6 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1647,9 +1654,9 @@ const contentCreator: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fdf2f8' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'About Skye', level: 'h1', style: { color: '#1e1b4b', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'About Skye', level: 'h1', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'I started making YouTube videos in my dorm room in 2019. What began as a hobby turned into my full-time career. I am passionate about helping people discover amazing tech and inspiring them to explore the world.\n\nWhen I am not filming, you will find me rock climbing, reading sci-fi, or trying every coffee shop in the city.', style: { color: '#581c87', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
@@ -1666,9 +1673,9 @@ const contentCreator: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fdf2f8' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Brand Partnerships', level: 'h2', style: { color: '#1e1b4b', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'Brand Partnerships', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'I work with brands that align with my values and genuinely interest my audience. Past partners include Apple, Samsung, Adobe, Notion, Squarespace, and NordVPN. For partnership inquiries, please use the contact form or email partnerships@skyevlogs.com.', style: { color: '#581c87', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
@@ -1682,21 +1689,21 @@ const contentCreator: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fdf2f8', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'All My Links', level: 'h1', style: { color: '#1e1b4b', fontFamily: 'Montserrat, sans-serif', marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'All My Links', level: 'h1', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '32px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'YouTube', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: '#ef4444', color: '#ffffff', borderRadius: '12px', fontWeight: '600', marginBottom: '12px', maxWidth: '400px', margin: '0 auto 12px', textAlign: 'center' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Instagram', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: '#ec4899', color: '#ffffff', borderRadius: '12px', fontWeight: '600', marginBottom: '12px', maxWidth: '400px', margin: '0 auto 12px', textAlign: 'center' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'TikTok', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: '#1e1b4b', color: '#ffffff', borderRadius: '12px', fontWeight: '600', marginBottom: '12px', maxWidth: '400px', margin: '0 auto 12px', textAlign: 'center' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Instagram', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: tokenRef('primary'), color: '#ffffff', borderRadius: '12px', fontWeight: '600', marginBottom: '12px', maxWidth: '400px', margin: '0 auto 12px', textAlign: 'center' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'TikTok', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: tokenRef('secondary'), color: '#ffffff', borderRadius: '12px', fontWeight: '600', marginBottom: '12px', maxWidth: '400px', margin: '0 auto 12px', textAlign: 'center' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Twitter / X', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: '#18181b', color: '#ffffff', borderRadius: '12px', fontWeight: '600', marginBottom: '12px', maxWidth: '400px', margin: '0 auto 12px', textAlign: 'center' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'My Gear List', href: '#', style: { display: 'block', padding: '14px 24px', backgroundColor: '#7c3aed', color: '#ffffff', borderRadius: '12px', fontWeight: '600', maxWidth: '400px', margin: '0 auto', textAlign: 'center' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px', backgroundColor: '#fdf2f8', textAlign: 'center' } },
|
||||
props: { style: { padding: '40px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'TextBlock', props: { text: 'For business inquiries: partnerships@skyevlogs.com', style: { color: '#6b21a8', fontSize: '14px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'For business inquiries: partnerships@skyevlogs.com', style: { color: tokenRef('muted'), fontSize: '14px' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user