@marktiderman/genesis-sites 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,250 @@
1
+ import { brandDisplayName } from './chunk-FBQF2L7O.js';
2
+
3
+ // src/core/templates.ts
4
+ var year = () => (/* @__PURE__ */ new Date()).getUTCFullYear();
5
+ function node(id, type, variant, props, extra = {}) {
6
+ return { id, type, v: 1, variant, props, ...extra };
7
+ }
8
+ function footerNode(name, contact, variant = "simple") {
9
+ const links = [];
10
+ if (contact?.email) links.push({ label: contact.email, href: `mailto:${contact.email}` });
11
+ if (contact?.phone) links.push({ label: contact.phone, href: `tel:${contact.phone.replace(/[^+\d]/g, "")}` });
12
+ return node("footer", "footer", variant, {
13
+ ...links.length ? { links } : {},
14
+ copyright: `\xA9 ${year()} ${name}`
15
+ });
16
+ }
17
+ function seo(name, message) {
18
+ return {
19
+ titleTemplate: `%s \xB7 ${name}`,
20
+ ...message.subhead ? { description: message.subhead.slice(0, 200) } : {},
21
+ organization: { name }
22
+ };
23
+ }
24
+ function primaryCta(input) {
25
+ return input.message.cta ?? { label: "Get in touch", href: "#contact" };
26
+ }
27
+ function benefitsNode(message) {
28
+ if (message.benefits?.length) {
29
+ return node("features", "features", "grid", {
30
+ heading: "What you get",
31
+ items: message.benefits.slice(0, 12).map((b, i) => ({ title: b.title, body: b.body, icon: ["check", "star", "heart", "zap", "shield", "clock"][i % 6] }))
32
+ });
33
+ }
34
+ return node(
35
+ "features",
36
+ "features",
37
+ "grid",
38
+ {
39
+ heading: "What you get",
40
+ items: [
41
+ { title: "Your first offer", body: "Say what someone gets and why it matters to them.", icon: "check" },
42
+ { title: "Your second offer", body: "Keep each one to a sentence or two.", icon: "star" },
43
+ { title: "Your third offer", body: "Three is plenty for a landing page.", icon: "heart" }
44
+ ]
45
+ },
46
+ { placeholder: true }
47
+ );
48
+ }
49
+ var testimonialsNode = () => node(
50
+ "testimonials",
51
+ "testimonials",
52
+ "grid",
53
+ {
54
+ heading: "What people say",
55
+ items: [
56
+ { quote: "Add a quote from someone you have helped, in their words and with their permission.", name: "Their name", role: "What they did with you" },
57
+ { quote: "A second quote, from someone different.", name: "Their name" }
58
+ ]
59
+ },
60
+ { placeholder: true }
61
+ );
62
+ function formNode(input) {
63
+ if (!input.form) return null;
64
+ return node("contact", "form-embed", "split", {
65
+ heading: input.form.heading ?? "Get in touch",
66
+ ...input.form.body ? { body: input.form.body } : { body: "Tell us a little about yourself and we'll get back to you." },
67
+ form: input.form.slug,
68
+ anchor: "contact"
69
+ });
70
+ }
71
+ function landingPage(input) {
72
+ const name = brandDisplayName(input.brand);
73
+ const cta = primaryCta(input);
74
+ const form = formNode(input);
75
+ const content = [
76
+ node("hero", "hero", "centered", {
77
+ heading: input.message.headline,
78
+ ...input.message.subhead ? { body: input.message.subhead } : {},
79
+ primaryCta: cta
80
+ }),
81
+ benefitsNode(input.message),
82
+ testimonialsNode(),
83
+ node("cta", "cta", "banner", { heading: "Ready when you are", primaryCta: cta, surface: "accent" })
84
+ ];
85
+ if (form) content.push(form);
86
+ return {
87
+ site: {
88
+ schemaVersion: 1,
89
+ name,
90
+ header: [node("nav", "nav", "minimal", { cta })],
91
+ nav: [],
92
+ footer: [footerNode(name, input.contact, "minimal")],
93
+ seo: seo(name, input.message)
94
+ },
95
+ pages: [{ path: "/", kind: "landing", doc: { schemaVersion: 1, root: { props: { title: input.message.headline.slice(0, 120) } }, content, seo: input.message.subhead ? { description: input.message.subhead.slice(0, 200) } : void 0 } }]
96
+ };
97
+ }
98
+ function onePageSite(input) {
99
+ const brand = { name: input.name, colors: input.colors, ...input.fonts ? { fonts: input.fonts } : {}, ...input.logo ? { logo: input.logo } : {} };
100
+ const cta = input.message.cta ?? (input.contact?.email ? { label: "Email us", href: `mailto:${input.contact.email}` } : { label: "Get in touch", href: "#contact" });
101
+ return {
102
+ brand,
103
+ site: {
104
+ schemaVersion: 1,
105
+ name: input.name,
106
+ header: [node("nav", "nav", "minimal", { cta })],
107
+ nav: [],
108
+ footer: [footerNode(input.name, input.contact)],
109
+ seo: seo(input.name, input.message)
110
+ },
111
+ pages: [
112
+ {
113
+ path: "/",
114
+ kind: "page",
115
+ doc: {
116
+ schemaVersion: 1,
117
+ root: { props: { title: input.message.headline.slice(0, 120) } },
118
+ content: [
119
+ node("hero", "hero", "centered", {
120
+ heading: input.message.headline,
121
+ ...input.message.subhead ? { body: input.message.subhead } : {},
122
+ primaryCta: cta
123
+ }),
124
+ node("contact", "cta", "card", { heading: `Talk to ${input.name}`, primaryCta: cta, anchor: "contact" })
125
+ ],
126
+ ...input.message.subhead ? { seo: { description: input.message.subhead.slice(0, 200) } } : {}
127
+ }
128
+ }
129
+ ]
130
+ };
131
+ }
132
+ function starterSite(input) {
133
+ const name = brandDisplayName(input.brand);
134
+ const cta = input.message.cta ?? { label: "Contact us", href: "/contact" };
135
+ const form = formNode(input);
136
+ const page = (title, content, description) => ({
137
+ schemaVersion: 1,
138
+ root: { props: { title } },
139
+ content,
140
+ ...description ? { seo: { description } } : {}
141
+ });
142
+ return {
143
+ site: {
144
+ schemaVersion: 1,
145
+ name,
146
+ header: [node("nav", "nav", "simple", { cta })],
147
+ nav: [
148
+ { label: "Home", href: "/" },
149
+ { label: "About", href: "/about" },
150
+ { label: "Contact", href: "/contact" }
151
+ ],
152
+ footer: [
153
+ node("footer", "footer", "columns", {
154
+ tagline: input.message.subhead?.slice(0, 200),
155
+ columns: [
156
+ { heading: name, links: [{ label: "About", href: "/about" }, { label: "Contact", href: "/contact" }] },
157
+ ...input.contact?.email || input.contact?.phone ? [
158
+ {
159
+ heading: "Get in touch",
160
+ links: [
161
+ ...input.contact.email ? [{ label: input.contact.email, href: `mailto:${input.contact.email}` }] : [],
162
+ ...input.contact.phone ? [{ label: input.contact.phone, href: `tel:${input.contact.phone.replace(/[^+\d]/g, "")}` }] : []
163
+ ]
164
+ }
165
+ ] : []
166
+ ],
167
+ copyright: `\xA9 ${year()} ${name}`
168
+ })
169
+ ],
170
+ seo: seo(name, input.message)
171
+ },
172
+ pages: [
173
+ {
174
+ path: "/",
175
+ kind: "page",
176
+ doc: page(
177
+ "Home",
178
+ [
179
+ node("hero", "hero", "split", {
180
+ heading: input.message.headline,
181
+ ...input.message.subhead ? { body: input.message.subhead } : {},
182
+ primaryCta: cta,
183
+ secondaryCta: { label: "About us", href: "/about" }
184
+ }),
185
+ benefitsNode(input.message),
186
+ testimonialsNode(),
187
+ node("cta", "cta", "banner", { heading: "Come and see for yourself", primaryCta: cta, surface: "accent" })
188
+ ],
189
+ input.message.subhead?.slice(0, 200)
190
+ )
191
+ },
192
+ {
193
+ path: "/about",
194
+ kind: "page",
195
+ doc: page(
196
+ "About",
197
+ [
198
+ node("hero", "hero", "centered", { eyebrow: "About", heading: `About ${name}` }),
199
+ node(
200
+ "story",
201
+ "rich-text",
202
+ "narrow",
203
+ {
204
+ body: {
205
+ type: "doc",
206
+ content: [
207
+ { type: "heading", attrs: { level: 2 }, content: [{ type: "text", text: "Our story" }] },
208
+ { type: "paragraph", content: [{ type: "text", text: "Tell people how you started, who you help and what you believe." }] }
209
+ ]
210
+ }
211
+ },
212
+ { placeholder: true }
213
+ )
214
+ ],
215
+ `About ${name}.`
216
+ )
217
+ },
218
+ {
219
+ path: "/contact",
220
+ kind: "page",
221
+ doc: page(
222
+ "Contact",
223
+ [
224
+ node("hero", "hero", "centered", { eyebrow: "Contact", heading: "Get in touch", ...input.message.subhead ? { body: input.message.subhead } : {} }),
225
+ ...form ? [form] : [node("contact", "cta", "card", { heading: `Talk to ${name}`, primaryCta: input.contact?.email ? { label: "Email us", href: `mailto:${input.contact.email}` } : cta })]
226
+ ],
227
+ `How to reach ${name}.`
228
+ )
229
+ }
230
+ ]
231
+ };
232
+ }
233
+
234
+ // src/core/examples.ts
235
+ var exampleBrands = Object.freeze({
236
+ riverside: {
237
+ name: "Riverside Gym",
238
+ colors: { primary: "#0f766e", primaryDark: "#2dd4bf" },
239
+ fonts: { heading: "space-grotesk", body: "inter" }
240
+ },
241
+ hilltop: {
242
+ name: "Hilltop Fitness",
243
+ colors: { primary: "#b45309", primaryDark: "#fbbf24" },
244
+ fonts: { heading: "playfair-display", body: "work-sans" }
245
+ }
246
+ });
247
+
248
+ export { exampleBrands, landingPage, onePageSite, starterSite };
249
+ //# sourceMappingURL=chunk-KPOX7D4S.js.map
250
+ //# sourceMappingURL=chunk-KPOX7D4S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/templates.ts","../src/core/examples.ts"],"names":[],"mappings":"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","file":"chunk-KPOX7D4S.js","sourcesContent":["/**\n * Templates: whole sites from a little input.\n *\n * - `landingPage`: the one-click landing page from a brand and a message.\n * Nav, hero, features, testimonials, a call to action, a form embed and a\n * footer, on one page.\n * - `onePageSite`: the smallest site a brand with no site can be seeded\n * with: just a name, a logo, a message, and the brand's colours and fonts.\n * - `starterSite`: three pages (home, about, contact) with nav and footer.\n *\n * Each returns documents (`site`, `pages`) for any `SiteStore` to create.\n * Nothing is invented and passed off as real: copy a template can't know\n * (a client's quote, a statistic) is marked `placeholder`, and the publish\n * check refuses it until someone replaces it or hides the section.\n */\nimport type { Link, PageDoc, PageKind, SectionNode, SiteDocInput } from \"./schemas\";\nimport { brandDisplayName, type BrandInput, type SiteBrand } from \"./theme\";\n\nexport interface TemplateMessage {\n /** The hero's headline: what you do, for whom. */\n headline: string;\n /** One or two sentences under it. */\n subhead?: string;\n /** The main button. Default: \"Get in touch\", to the form (or `#contact`). */\n cta?: Link;\n /** What you offer, as the features band. Without it the band is a placeholder to fill in. */\n benefits?: { title: string; body: string }[];\n}\n\nexport interface TemplateInput {\n brand: BrandInput;\n message: TemplateMessage;\n /** A form to embed (its slug, for the host's forms resolver). */\n form?: { slug: string; heading?: string; body?: string };\n /** For the footer. */\n contact?: { email?: string; phone?: string };\n}\n\nexport interface TemplateResult {\n site: SiteDocInput;\n pages: { path: string; kind: PageKind; doc: PageDoc }[];\n}\n\nconst year = () => new Date().getUTCFullYear();\n\nfunction node(id: string, type: string, variant: string, props: Record<string, unknown>, extra: Partial<SectionNode> = {}): SectionNode {\n return { id, type, v: 1, variant, props, ...extra };\n}\n\nfunction footerNode(name: string, contact: TemplateInput[\"contact\"], variant = \"simple\"): SectionNode {\n const links: Link[] = [];\n if (contact?.email) links.push({ label: contact.email, href: `mailto:${contact.email}` });\n if (contact?.phone) links.push({ label: contact.phone, href: `tel:${contact.phone.replace(/[^+\\d]/g, \"\")}` });\n return node(\"footer\", \"footer\", variant, {\n ...(links.length ? { links } : {}),\n copyright: `© ${year()} ${name}`,\n });\n}\n\nfunction seo(name: string, message: TemplateMessage) {\n return {\n titleTemplate: `%s · ${name}`,\n ...(message.subhead ? { description: message.subhead.slice(0, 200) } : {}),\n organization: { name },\n };\n}\n\nfunction primaryCta(input: TemplateInput): Link {\n return input.message.cta ?? { label: \"Get in touch\", href: \"#contact\" };\n}\n\nfunction benefitsNode(message: TemplateMessage): SectionNode {\n if (message.benefits?.length) {\n return node(\"features\", \"features\", \"grid\", {\n heading: \"What you get\",\n items: message.benefits.slice(0, 12).map((b, i) => ({ title: b.title, body: b.body, icon: ([\"check\", \"star\", \"heart\", \"zap\", \"shield\", \"clock\"] as const)[i % 6] })),\n });\n }\n return node(\n \"features\",\n \"features\",\n \"grid\",\n {\n heading: \"What you get\",\n items: [\n { title: \"Your first offer\", body: \"Say what someone gets and why it matters to them.\", icon: \"check\" },\n { title: \"Your second offer\", body: \"Keep each one to a sentence or two.\", icon: \"star\" },\n { title: \"Your third offer\", body: \"Three is plenty for a landing page.\", icon: \"heart\" },\n ],\n },\n { placeholder: true },\n );\n}\n\nconst testimonialsNode = () =>\n node(\n \"testimonials\",\n \"testimonials\",\n \"grid\",\n {\n heading: \"What people say\",\n items: [\n { quote: \"Add a quote from someone you have helped, in their words and with their permission.\", name: \"Their name\", role: \"What they did with you\" },\n { quote: \"A second quote, from someone different.\", name: \"Their name\" },\n ],\n },\n { placeholder: true },\n );\n\nfunction formNode(input: TemplateInput): SectionNode | null {\n if (!input.form) return null;\n return node(\"contact\", \"form-embed\", \"split\", {\n heading: input.form.heading ?? \"Get in touch\",\n ...(input.form.body ? { body: input.form.body } : { body: \"Tell us a little about yourself and we'll get back to you.\" }),\n form: input.form.slug,\n anchor: \"contact\",\n });\n}\n\n/**\n * The one-click landing page: brand plus message, on one page.\n *\n * @stability Beta\n */\nexport function landingPage(input: TemplateInput): TemplateResult {\n const name = brandDisplayName(input.brand);\n const cta = primaryCta(input);\n const form = formNode(input);\n const content: SectionNode[] = [\n node(\"hero\", \"hero\", \"centered\", {\n heading: input.message.headline,\n ...(input.message.subhead ? { body: input.message.subhead } : {}),\n primaryCta: cta,\n }),\n benefitsNode(input.message),\n testimonialsNode(),\n node(\"cta\", \"cta\", \"banner\", { heading: \"Ready when you are\", primaryCta: cta, surface: \"accent\" }),\n ];\n if (form) content.push(form);\n return {\n site: {\n schemaVersion: 1,\n name,\n header: [node(\"nav\", \"nav\", \"minimal\", { cta })],\n nav: [],\n footer: [footerNode(name, input.contact, \"minimal\")],\n seo: seo(name, input.message),\n },\n pages: [{ path: \"/\", kind: \"landing\", doc: { schemaVersion: 1, root: { props: { title: input.message.headline.slice(0, 120) } }, content, seo: input.message.subhead ? { description: input.message.subhead.slice(0, 200) } : undefined } }],\n };\n}\n\nexport interface OnePageSiteInput {\n name: string;\n logo?: { src: string; alt?: string };\n message: TemplateMessage;\n /** The brand's colours and fonts. */\n colors: SiteBrand[\"colors\"];\n fonts?: SiteBrand[\"fonts\"];\n contact?: TemplateInput[\"contact\"];\n}\n\n/**\n * The smallest site, for seeding a brand that has none: a header, a hero,\n * a call to action and a footer. `brand` is the minimal brand to store\n * beside it (the host's `BrandSource` returns it).\n *\n * @stability Beta\n */\nexport function onePageSite(input: OnePageSiteInput): TemplateResult & { brand: SiteBrand } {\n const brand: SiteBrand = { name: input.name, colors: input.colors, ...(input.fonts ? { fonts: input.fonts } : {}), ...(input.logo ? { logo: input.logo } : {}) };\n const cta = input.message.cta ?? (input.contact?.email ? { label: \"Email us\", href: `mailto:${input.contact.email}` } : { label: \"Get in touch\", href: \"#contact\" });\n return {\n brand,\n site: {\n schemaVersion: 1,\n name: input.name,\n header: [node(\"nav\", \"nav\", \"minimal\", { cta })],\n nav: [],\n footer: [footerNode(input.name, input.contact)],\n seo: seo(input.name, input.message),\n },\n pages: [\n {\n path: \"/\",\n kind: \"page\",\n doc: {\n schemaVersion: 1,\n root: { props: { title: input.message.headline.slice(0, 120) } },\n content: [\n node(\"hero\", \"hero\", \"centered\", {\n heading: input.message.headline,\n ...(input.message.subhead ? { body: input.message.subhead } : {}),\n primaryCta: cta,\n }),\n node(\"contact\", \"cta\", \"card\", { heading: `Talk to ${input.name}`, primaryCta: cta, anchor: \"contact\" }),\n ],\n ...(input.message.subhead ? { seo: { description: input.message.subhead.slice(0, 200) } } : {}),\n },\n },\n ],\n };\n}\n\n/**\n * A three-page starter site: home, about and contact, with the menu, a\n * footer and redirects-ready paths.\n *\n * @stability Beta\n */\nexport function starterSite(input: TemplateInput): TemplateResult {\n const name = brandDisplayName(input.brand);\n const cta: Link = input.message.cta ?? { label: \"Contact us\", href: \"/contact\" };\n const form = formNode(input);\n const page = (title: string, content: SectionNode[], description?: string): PageDoc => ({\n schemaVersion: 1,\n root: { props: { title } },\n content,\n ...(description ? { seo: { description } } : {}),\n });\n return {\n site: {\n schemaVersion: 1,\n name,\n header: [node(\"nav\", \"nav\", \"simple\", { cta })],\n nav: [\n { label: \"Home\", href: \"/\" },\n { label: \"About\", href: \"/about\" },\n { label: \"Contact\", href: \"/contact\" },\n ],\n footer: [\n node(\"footer\", \"footer\", \"columns\", {\n tagline: input.message.subhead?.slice(0, 200),\n columns: [\n { heading: name, links: [{ label: \"About\", href: \"/about\" }, { label: \"Contact\", href: \"/contact\" }] },\n ...(input.contact?.email || input.contact?.phone\n ? [\n {\n heading: \"Get in touch\",\n links: [\n ...(input.contact.email ? [{ label: input.contact.email, href: `mailto:${input.contact.email}` }] : []),\n ...(input.contact.phone ? [{ label: input.contact.phone, href: `tel:${input.contact.phone.replace(/[^+\\d]/g, \"\")}` }] : []),\n ],\n },\n ]\n : []),\n ],\n copyright: `© ${year()} ${name}`,\n }),\n ],\n seo: seo(name, input.message),\n },\n pages: [\n {\n path: \"/\",\n kind: \"page\",\n doc: page(\n \"Home\",\n [\n node(\"hero\", \"hero\", \"split\", {\n heading: input.message.headline,\n ...(input.message.subhead ? { body: input.message.subhead } : {}),\n primaryCta: cta,\n secondaryCta: { label: \"About us\", href: \"/about\" },\n }),\n benefitsNode(input.message),\n testimonialsNode(),\n node(\"cta\", \"cta\", \"banner\", { heading: \"Come and see for yourself\", primaryCta: cta, surface: \"accent\" }),\n ],\n input.message.subhead?.slice(0, 200),\n ),\n },\n {\n path: \"/about\",\n kind: \"page\",\n doc: page(\n \"About\",\n [\n node(\"hero\", \"hero\", \"centered\", { eyebrow: \"About\", heading: `About ${name}` }),\n node(\n \"story\",\n \"rich-text\",\n \"narrow\",\n {\n body: {\n type: \"doc\",\n content: [\n { type: \"heading\", attrs: { level: 2 }, content: [{ type: \"text\", text: \"Our story\" }] },\n { type: \"paragraph\", content: [{ type: \"text\", text: \"Tell people how you started, who you help and what you believe.\" }] },\n ],\n },\n },\n { placeholder: true },\n ),\n ],\n `About ${name}.`,\n ),\n },\n {\n path: \"/contact\",\n kind: \"page\",\n doc: page(\n \"Contact\",\n [\n node(\"hero\", \"hero\", \"centered\", { eyebrow: \"Contact\", heading: \"Get in touch\", ...(input.message.subhead ? { body: input.message.subhead } : {}) }),\n ...(form ? [form] : [node(\"contact\", \"cta\", \"card\", { heading: `Talk to ${name}`, primaryCta: input.contact?.email ? { label: \"Email us\", href: `mailto:${input.contact.email}` } : cta })]),\n ],\n `How to reach ${name}.`,\n ),\n },\n ],\n };\n}\n","/**\n * Made-up brands for demos, tests and docs. They are not anyone's real\n * business.\n */\nimport type { SiteBrand } from \"./theme\";\n\nexport const exampleBrands = Object.freeze({\n riverside: {\n name: \"Riverside Gym\",\n colors: { primary: \"#0f766e\", primaryDark: \"#2dd4bf\" },\n fonts: { heading: \"space-grotesk\", body: \"inter\" },\n },\n hilltop: {\n name: \"Hilltop Fitness\",\n colors: { primary: \"#b45309\", primaryDark: \"#fbbf24\" },\n fonts: { heading: \"playfair-display\", body: \"work-sans\" },\n },\n} satisfies Record<string, SiteBrand>);\n"]}