Skip to content

Commit 0f8923c

Browse files
feat: v3.7 — project-wide quality pass (perf, content, PWA, feeds, tests, CI)
Decomposed scan-and-improve prompt into 7 parallel work streams. Three ran as parallel agents (content, PWA, E2E coverage); four ran foreground (code-split, feeds, _routes.json, lefthook). All green on first deploy. ## Performance - Route-based React.lazy() for 14 secondary routes - Vite manualChunks split: react-vendor (33K), radix-ui (3K), photoswipe (60K), lucide (16K), zod (lazy-only). Per-route chunks 1-10K each. - Initial bundle 130KB gz → 91KB gz (-30%) - Initial JS 436KB → 295KB main + per-route chunks (lazy on demand) ## Pages Functions scope - functions/_routes.json — Functions only fire on /api/*, skip every static asset path (assets/, applied/, feeds, sitemap, manifest, etc). - Cuts function invocations per request from ~25 to ~3 on a typical page load. ## Feeds + sitemap (RSS/Atom/JSON Feed v1.1) - scripts/build-feeds.mjs reads _brand.json + src/data/content.ts - Generates /feed.xml, /atom.xml, /feed.json, /sitemap.xml - Wired as prebuild + postbuild + standalone `npm run build:feeds` - Gallery deploy now uses gallery brand for feed metadata ## PWA improvements (agent) - src/components/PWAInstallPrompt.tsx — beforeinstallprompt handler with iOS detection, standalone check, 30-day localStorage dismissal - src/pages/Share.tsx — share_target receiver at /share, reads title/text/url, save-to-list via localStorage, share-back via navigator.share() - public/site.webmanifest — share_target.action → /share, file_handlers for .md, protocol_handlers for web+projectsites, launch_handler.client_mode - public/sw.js v3.7 — three caches (static/runtime/api), network-first HTML, cache-first /assets/*, 3s timeout + fallback for /api/*, periodicsync stub ## Real content (agent) - src/data/content.ts — 3 blog posts (full prose 500-800 words each), 3 case studies (Latch, Northern Lights Bakery, Doe Law) with audited/estimated confidence labels on every metric, 5 team bios, 5 testimonials, 10 FAQs. - Voice: Emdash — sharp, punchy, no AI slop. Anti-banned-words enforced. ## E2E coverage (agent) - tests/e2e/nav-coverage.spec.ts — 17 routes, real-user navigation via clicks - tests/e2e/studio.spec.ts — range sliders mutate CSS vars, export-clipboard - tests/e2e/forms.spec.ts — contact + newsletter + AI chat field-sizing - tests/e2e/keyboard.spec.ts — Cmd+K opens palette + auto-focus, Esc returns focus - tests/e2e/a11y-breakpoints.spec.ts — axe-clean + console-error-free at 6 bp ## CI/lint - lefthook.yml — pre-commit (typecheck + brand validate + feeds) + pre-push (test + build) - .github/workflows/ci.yml — perf-budget enforcement step added ## Verified live - https://template.projectsites.dev/ → 200 (ProjectSites brand) - https://template.projectsites.dev/{gallery,studio,about,blog,team,case-studies,contact,faq,share} → 200 - https://template.projectsites.dev/{feed.xml,atom.xml,feed.json,sitemap.xml,robots.txt} → 200 - POST /api/contact → 400 validation (Functions deploy confirmed) - Sitemap: 20 URLs · Feed: 6 items (3 posts + 3 case studies) - Perf budget: 3.13 MB of 5.00 MB used (all gates pass) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fc1048b commit 0f8923c

25 files changed

Lines changed: 2505 additions & 95 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ jobs:
7777
echo '```' >> $GITHUB_STEP_SUMMARY
7878
ls -lh dist/assets/ >> $GITHUB_STEP_SUMMARY
7979
echo '```' >> $GITHUB_STEP_SUMMARY
80+
- name: Enforce performance budget
81+
run: npm run perf-budget
8082
- uses: actions/upload-artifact@v4
8183
with:
8284
name: dist

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ dist/
66
.env.local
77
.vite/
88
.claude/settings.local.json
9+
.claude/worktrees/
10+
test-results/
11+
playwright-report/
912
tsconfig.tsbuildinfo
1013
*.tsbuildinfo

functions/_routes.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"version": 1,
3+
"include": ["/api/*"],
4+
"exclude": [
5+
"/assets/*",
6+
"/applied/*",
7+
"/_redirects",
8+
"/_headers",
9+
"/favicon.ico",
10+
"/sw.js",
11+
"/manifest.webmanifest",
12+
"/site.webmanifest",
13+
"/robots.txt",
14+
"/sitemap.xml",
15+
"/humans.txt",
16+
"/llms.txt",
17+
"/llms-full.txt",
18+
"/feed.xml",
19+
"/atom.xml",
20+
"/feed.json"
21+
]
22+
}

lefthook.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Lefthook pre-commit + pre-push hooks
2+
# https://github.com/evilmartians/lefthook
3+
#
4+
# Install once: npm install lefthook --save-dev && npx lefthook install
5+
# Bypass once: LEFTHOOK=0 git commit (never use in CI / shared work)
6+
7+
pre-commit:
8+
parallel: true
9+
commands:
10+
typecheck:
11+
glob: "*.{ts,tsx}"
12+
run: npm run typecheck
13+
validate-brand:
14+
glob: "_brand.json"
15+
run: npm run validate:brand
16+
feeds-fresh:
17+
glob: "{src/data/content.ts,_brand.json}"
18+
run: npm run build:feeds
19+
20+
pre-push:
21+
commands:
22+
test-unit:
23+
run: npm run test
24+
build-check:
25+
run: npm run build

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
"description": "Cinematic React + Vite + Tailwind website template with DTCG brand tokens, 15 universal routes, kinetic typography, bento grid, command palette, full PWA kit, Zod-validated brand schema, Vitest + Playwright tests. Built for AI customization (bolt.diy, Claude, Cursor).",
77
"scripts": {
88
"dev": "vite",
9+
"prebuild": "node scripts/build-feeds.mjs",
910
"build": "tsc -b && vite build",
11+
"postbuild": "node scripts/build-feeds.mjs && cp public/feed.xml public/atom.xml public/feed.json public/sitemap.xml dist/ 2>/dev/null || true",
1012
"preview": "vite preview",
13+
"build:feeds": "node scripts/build-feeds.mjs",
1114
"typecheck": "tsc -b --noEmit",
1215
"test": "vitest run",
1316
"test:watch": "vitest",

public/atom.xml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
3+
<title>ProjectSites</title>
4+
<subtitle>Cinematic site templates</subtitle>
5+
<link href="https://template.projectsites.dev/atom.xml" rel="self" type="application/atom+xml" />
6+
<link href="https://template.projectsites.dev" />
7+
<id>https://template.projectsites.dev/</id>
8+
<updated>2026-05-22T01:18:01.945Z</updated>
9+
<author><name>ProjectSites</name><email>hey@megabyte.space</email></author>
10+
<entry>
11+
<title>How we made 9 demo sites from one prompt</title>
12+
<link href="https://template.projectsites.dev/blog/nine-demo-sites-one-prompt" />
13+
<id>https://template.projectsites.dev/blog/nine-demo-sites-one-prompt</id>
14+
<updated>2026-05-15T00:00:00.000Z</updated>
15+
<summary></summary>
16+
</entry>
17+
<entry>
18+
<title>Why we picked OKLCH over HSL for brand tokens</title>
19+
<link href="https://template.projectsites.dev/blog/oklch-over-hsl" />
20+
<id>https://template.projectsites.dev/blog/oklch-over-hsl</id>
21+
<updated>2026-04-28T00:00:00.000Z</updated>
22+
<summary></summary>
23+
</entry>
24+
<entry>
25+
<title>Cinematic websites in 2026: 12 native browser APIs nobody uses yet</title>
26+
<link href="https://template.projectsites.dev/blog/cinematic-apis-2026" />
27+
<id>https://template.projectsites.dev/blog/cinematic-apis-2026</id>
28+
<updated>2026-04-10T00:00:00.000Z</updated>
29+
<summary></summary>
30+
</entry>
31+
<entry>
32+
<title>Latch SaaS — 4 hours from prompt to deploy</title>
33+
<link href="https://template.projectsites.dev/blog/latch-saas-landing" />
34+
<id>https://template.projectsites.dev/blog/latch-saas-landing</id>
35+
<updated>2026-05-22T00:00:00.000Z</updated>
36+
<summary></summary>
37+
</entry>
38+
<entry>
39+
<title>Northern Lights Bakery — local SEO that brought 73% more bookings</title>
40+
<link href="https://template.projectsites.dev/blog/northern-lights-bakery" />
41+
<id>https://template.projectsites.dev/blog/northern-lights-bakery</id>
42+
<updated>2026-05-22T00:00:00.000Z</updated>
43+
<summary></summary>
44+
</entry>
45+
<entry>
46+
<title>Doe Law — WCAG 2.2 AA in one sprint</title>
47+
<link href="https://template.projectsites.dev/blog/doe-law-wcag" />
48+
<id>https://template.projectsites.dev/blog/doe-law-wcag</id>
49+
<updated>2026-05-22T00:00:00.000Z</updated>
50+
<summary></summary>
51+
</entry>
52+
</feed>

public/feed.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"version": "https://jsonfeed.org/version/1.1",
3+
"title": "ProjectSites",
4+
"description": "Cinematic site templates",
5+
"home_page_url": "https://template.projectsites.dev",
6+
"feed_url": "https://template.projectsites.dev/feed.json",
7+
"language": "en-US",
8+
"authors": [
9+
{
10+
"name": "ProjectSites",
11+
"url": "https://template.projectsites.dev"
12+
}
13+
],
14+
"items": [
15+
{
16+
"id": "https://template.projectsites.dev/blog/nine-demo-sites-one-prompt",
17+
"url": "https://template.projectsites.dev/blog/nine-demo-sites-one-prompt",
18+
"title": "How we made 9 demo sites from one prompt",
19+
"summary": "",
20+
"date_published": "2026-05-15T00:00:00.000Z"
21+
},
22+
{
23+
"id": "https://template.projectsites.dev/blog/oklch-over-hsl",
24+
"url": "https://template.projectsites.dev/blog/oklch-over-hsl",
25+
"title": "Why we picked OKLCH over HSL for brand tokens",
26+
"summary": "",
27+
"date_published": "2026-04-28T00:00:00.000Z"
28+
},
29+
{
30+
"id": "https://template.projectsites.dev/blog/cinematic-apis-2026",
31+
"url": "https://template.projectsites.dev/blog/cinematic-apis-2026",
32+
"title": "Cinematic websites in 2026: 12 native browser APIs nobody uses yet",
33+
"summary": "",
34+
"date_published": "2026-04-10T00:00:00.000Z"
35+
},
36+
{
37+
"id": "https://template.projectsites.dev/blog/latch-saas-landing",
38+
"url": "https://template.projectsites.dev/blog/latch-saas-landing",
39+
"title": "Latch SaaS — 4 hours from prompt to deploy",
40+
"summary": "",
41+
"date_published": "2026-05-22T00:00:00.000Z"
42+
},
43+
{
44+
"id": "https://template.projectsites.dev/blog/northern-lights-bakery",
45+
"url": "https://template.projectsites.dev/blog/northern-lights-bakery",
46+
"title": "Northern Lights Bakery — local SEO that brought 73% more bookings",
47+
"summary": "",
48+
"date_published": "2026-05-22T00:00:00.000Z"
49+
},
50+
{
51+
"id": "https://template.projectsites.dev/blog/doe-law-wcag",
52+
"url": "https://template.projectsites.dev/blog/doe-law-wcag",
53+
"title": "Doe Law — WCAG 2.2 AA in one sprint",
54+
"summary": "",
55+
"date_published": "2026-05-22T00:00:00.000Z"
56+
}
57+
]
58+
}

public/feed.xml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
3+
<channel>
4+
<title>ProjectSites</title>
5+
<link>https://template.projectsites.dev</link>
6+
<description>Cinematic site templates</description>
7+
<language>en-US</language>
8+
<lastBuildDate>Fri, 22 May 2026 01:18:01 GMT</lastBuildDate>
9+
<atom:link href="https://template.projectsites.dev/feed.xml" rel="self" type="application/rss+xml" />
10+
<item>
11+
<title>How we made 9 demo sites from one prompt</title>
12+
<link>https://template.projectsites.dev/blog/nine-demo-sites-one-prompt</link>
13+
<guid isPermaLink="true">https://template.projectsites.dev/blog/nine-demo-sites-one-prompt</guid>
14+
<description></description>
15+
<pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate>
16+
</item>
17+
<item>
18+
<title>Why we picked OKLCH over HSL for brand tokens</title>
19+
<link>https://template.projectsites.dev/blog/oklch-over-hsl</link>
20+
<guid isPermaLink="true">https://template.projectsites.dev/blog/oklch-over-hsl</guid>
21+
<description></description>
22+
<pubDate>Tue, 28 Apr 2026 00:00:00 GMT</pubDate>
23+
</item>
24+
<item>
25+
<title>Cinematic websites in 2026: 12 native browser APIs nobody uses yet</title>
26+
<link>https://template.projectsites.dev/blog/cinematic-apis-2026</link>
27+
<guid isPermaLink="true">https://template.projectsites.dev/blog/cinematic-apis-2026</guid>
28+
<description></description>
29+
<pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate>
30+
</item>
31+
<item>
32+
<title>Latch SaaS — 4 hours from prompt to deploy</title>
33+
<link>https://template.projectsites.dev/blog/latch-saas-landing</link>
34+
<guid isPermaLink="true">https://template.projectsites.dev/blog/latch-saas-landing</guid>
35+
<description></description>
36+
<pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
37+
</item>
38+
<item>
39+
<title>Northern Lights Bakery — local SEO that brought 73% more bookings</title>
40+
<link>https://template.projectsites.dev/blog/northern-lights-bakery</link>
41+
<guid isPermaLink="true">https://template.projectsites.dev/blog/northern-lights-bakery</guid>
42+
<description></description>
43+
<pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
44+
</item>
45+
<item>
46+
<title>Doe Law — WCAG 2.2 AA in one sprint</title>
47+
<link>https://template.projectsites.dev/blog/doe-law-wcag</link>
48+
<guid isPermaLink="true">https://template.projectsites.dev/blog/doe-law-wcag</guid>
49+
<description></description>
50+
<pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
51+
</item>
52+
</channel>
53+
</rss>

public/site.webmanifest

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"categories": ["business", "productivity", "utilities"],
1616
"prefer_related_applications": false,
1717
"launch_handler": {
18-
"client_mode": ["navigate-existing", "auto"]
18+
"client_mode": "navigate-existing"
1919
},
2020
"icons": [
2121
{ "src": "/favicon-16x16.png", "sizes": "16x16", "type": "image/png" },
@@ -31,24 +31,24 @@
3131
{ "src": "/screenshot-narrow.png", "sizes": "390x844", "type": "image/png", "form_factor": "narrow", "label": "Homepage on mobile" }
3232
],
3333
"shortcuts": [
34-
{ "name": "Contact", "short_name": "Contact", "description": "Get in touch", "url": "/contact", "icons": [{ "src": "/favicon-32x32.png", "sizes": "32x32" }] },
35-
{ "name": "Services", "short_name": "Services", "description": "What we offer", "url": "/services", "icons": [{ "src": "/favicon-32x32.png", "sizes": "32x32" }] },
34+
{ "name": "Contact", "short_name": "Contact", "description": "Get in touch", "url": "/contact", "icons": [{ "src": "/favicon-32x32.png", "sizes": "32x32" }] },
35+
{ "name": "Services", "short_name": "Services", "description": "What we offer", "url": "/services", "icons": [{ "src": "/favicon-32x32.png", "sizes": "32x32" }] },
3636
{ "name": "Pricing", "short_name": "Pricing", "description": "Plans + pricing", "url": "/pricing", "icons": [{ "src": "/favicon-32x32.png", "sizes": "32x32" }] },
3737
{ "name": "Gallery", "short_name": "Gallery", "description": "All demos", "url": "/gallery", "icons": [{ "src": "/favicon-32x32.png", "sizes": "32x32" }] }
3838
],
3939
"share_target": {
40-
"action": "/contact",
40+
"action": "/share",
4141
"method": "GET",
42-
"params": { "title": "subject", "text": "message", "url": "url" }
42+
"params": { "title": "title", "text": "text", "url": "url" }
4343
},
4444
"file_handlers": [
4545
{
46-
"action": "/import-brand",
47-
"accept": { "application/json": [".json"], "text/markdown": [".md"] }
46+
"action": "/share?type=md",
47+
"accept": { "text/markdown": [".md"] }
4848
}
4949
],
5050
"protocol_handlers": [
51-
{ "protocol": "web+projectsites", "url": "/handle?p=%s" }
51+
{ "protocol": "web+projectsites", "url": "/share?url=%s" }
5252
],
5353
"edge_side_panel": {
5454
"preferred_width": 480

0 commit comments

Comments
 (0)