Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
**Ready-made cinematic scroll websites — no code, pure HTML.**

Pick a template, change the words, and export a production‑ready HTML/CSS/JS ZIP you can
deploy anywhere. Every template is free on every plan.
deploy anywhere. The editor, publishing and ZIP export are free, along with 13 of the 21
templates. The other 8 are bought once, outright.

[Live demo](https://scrollcraft-gilt.vercel.app) · [Report a bug](https://github.com/singhharsh1708/scrollcraft/issues) · [Request a feature](https://github.com/singhharsh1708/scrollcraft/issues)

Expand Down Expand Up @@ -250,10 +251,10 @@ coding standards, and how to pick up an issue.
## Sponsor

ScrollCraft is free and open source, and the hosted version costs real money to run
(model calls, frame rendering, storage). If it saved you a studio invoice, you can
(frame rendering, blob storage, a database). If it saved you a studio invoice, you can
[sponsor the project on GitHub](https://github.com/sponsors/singhharsh1708), one-off or
monthly. Sponsoring is separate from the paid plans: it funds the open source work, not an
account upgrade.
monthly. Sponsoring funds the open source work — it is not a purchase and unlocks
nothing, which is deliberate.

Not in a position to pay? Starring the repo, filing a good bug report, or shipping a PR
helps just as much.
Expand Down
10 changes: 2 additions & 8 deletions src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge";
import { ArrowRight, Sparkles, Zap, Globe, Users } from "lucide-react";
import Navbar from "@/components/Navbar";
import { PRESETS } from "@/lib/presets";
import SiteFooter from "@/components/SiteFooter";

const TEAM = [
{ name: "Harsh Singh", role: "Founder & Engineer", avatar: "HS", bio: "Building the future of no-code web experiences." },
Expand Down Expand Up @@ -128,14 +129,7 @@ export default function AboutPage() {
</Link>
</section>

<footer className="py-8 px-6 border-t border-white/5 text-center text-sm text-muted-foreground">
<div className="flex items-center justify-center gap-2">
<div className="w-5 h-5 rounded bg-primary/20 flex items-center justify-center">
<Sparkles className="w-3 h-3 text-primary" />
</div>
ScrollCraft — Built with Next.js
</div>
</footer>
<SiteFooter compact />
</main>
);
}
12 changes: 3 additions & 9 deletions src/app/changelog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Badge } from "@/components/ui/badge";
import { Sparkles } from "lucide-react";
import { } from "lucide-react";
import Navbar from "@/components/Navbar";
import SiteFooter from "@/components/SiteFooter";

const ENTRIES = [
{
Expand Down Expand Up @@ -131,14 +132,7 @@ export default function ChangelogPage() {
</div>
</section>

<footer className="py-8 px-6 border-t border-white/5 text-center text-sm text-muted-foreground">
<div className="flex items-center justify-center gap-2">
<div className="w-5 h-5 rounded bg-primary/20 flex items-center justify-center">
<Sparkles className="w-3 h-3 text-primary" />
</div>
ScrollCraft — Built with Next.js
</div>
</footer>
<SiteFooter compact />
</main>
);
}
12 changes: 3 additions & 9 deletions src/app/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Sparkles, Mail, MessageSquare, Zap, CheckCircle2 } from "lucide-react";
import { Mail, MessageSquare, Zap, CheckCircle2 } from "lucide-react";
import { toast } from "sonner";
import Navbar from "@/components/Navbar";
import SiteFooter from "@/components/SiteFooter";

const TOPICS = ["General question", "Bug report", "Feature request", "Enterprise inquiry", "Billing", "Partnership"];

Expand Down Expand Up @@ -118,14 +119,7 @@ export default function ContactPage() {
</div>
</section>

<footer className="py-8 px-6 border-t border-white/5 text-center text-sm text-muted-foreground">
<div className="flex items-center justify-center gap-2">
<div className="w-5 h-5 rounded bg-primary/20 flex items-center justify-center">
<Sparkles className="w-3 h-3 text-primary" />
</div>
ScrollCraft — Built with Next.js & AI
</div>
</footer>
<SiteFooter compact />
</main>
);
}
2 changes: 2 additions & 0 deletions src/app/cookies/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from "next/link";
import Navbar from "@/components/Navbar";
import SiteFooter from "@/components/SiteFooter";

export const metadata = { title: "Cookie Policy" };

Expand Down Expand Up @@ -51,6 +52,7 @@ export default function CookiesPage() {
<h2>Contact</h2>
<p>Questions? Email <a href="mailto:hello@scrollcraft.app" className="underline">hello@scrollcraft.app</a>. See also our <Link href="/privacy" className="underline">Privacy Policy</Link>.</p>
</article>
<SiteFooter compact />
</main>
);
}
11 changes: 2 additions & 9 deletions src/app/launch/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge";
import { ArrowRight, Check, Copy, Sparkles, Zap, Download, Play, ExternalLink } from "lucide-react";
import { toast } from "sonner";
import Navbar from "@/components/Navbar";
import SiteFooter from "@/components/SiteFooter";

const PROMO_CODE = "PHHUNT";
const DISCOUNT = 30;
Expand Down Expand Up @@ -205,15 +206,7 @@ export default function LaunchPage() {
</div>
</section>

{/* Footer */}
<footer className="border-t border-white/5 py-8 text-center text-sm text-muted-foreground">
<p>© {new Date().getFullYear()} ScrollCraft. Made with ♥ for builders.</p>
<div className="flex justify-center gap-6 mt-3">
<Link href="/privacy" className="hover:text-foreground transition-colors">Privacy</Link>
<Link href="/terms" className="hover:text-foreground transition-colors">Terms</Link>
<Link href="mailto:hello@scrollcraft.app" className="hover:text-foreground transition-colors">Contact</Link>
</div>
</footer>
<SiteFooter compact />
</main>
);
}
52 changes: 8 additions & 44 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { ArrowRight, Sparkles, Zap, Download, Play, Check } from "lucide-react";
import Navbar from "@/components/Navbar";
import StylePreview from "@/components/StylePreview";
import { findPreset, PRESETS } from "@/lib/presets";
import SponsorCard from "@/components/SponsorCard";
import SiteFooter from "@/components/SiteFooter";

const DEMO_COUNT = 60;
const demoFrames = Array.from({ length: DEMO_COUNT }, (_, i) => `/api/demo-frame?i=${i}&total=${DEMO_COUNT}`);
Expand Down Expand Up @@ -396,6 +398,11 @@ export default function Home() {
</div>
</section>

{/* Open source */}
<section className="px-6 pb-4 max-w-5xl mx-auto">
<SponsorCard />
</section>

{/* Final CTA */}
<section className="py-32 px-6 text-center border-t border-white/5">
<div className="relative max-w-3xl mx-auto">
Expand Down Expand Up @@ -423,50 +430,7 @@ export default function Home() {
</div>
</section>

{/* Footer */}
<footer className="py-12 px-6 border-t border-white/5">
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 mb-12">
<div>
<div className="flex items-center gap-2 mb-4">
<div className="w-6 h-6 rounded bg-primary/20 flex items-center justify-center">
<Sparkles className="w-3 h-3 text-primary" />
</div>
<span className="font-semibold">ScrollCraft</span>
</div>
<p className="text-xs text-muted-foreground leading-relaxed">Ready-made cinematic scroll templates with animated canvas backgrounds. Ship a site in minutes.</p>
</div>
<div>
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4">Product</p>
<div className="space-y-2.5">
{[["Features", "/#features"], ["Presets", "/presets"], ["Pricing", "/pricing"], ["Builder", "/create"]].map(([label, href]) => (
<Link key={label} href={href} className="block text-sm text-muted-foreground hover:text-foreground transition-colors">{label}</Link>
))}
</div>
</div>
<div>
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4">Company</p>
<div className="space-y-2.5">
{[["About", "/about"], ["Blog", "/changelog"], ["Changelog", "/changelog"], ["Contact", "/contact"]].map(([label, href]) => (
<Link key={label} href={href} className="block text-sm text-muted-foreground hover:text-foreground transition-colors">{label}</Link>
))}
</div>
</div>
<div>
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4">Legal</p>
<div className="space-y-2.5">
{[["Privacy Policy", "/privacy"], ["Terms of Service", "/terms"], ["Cookie Policy", "/cookies"]].map(([label, href]) => (
<Link key={label} href={href} className="block text-sm text-muted-foreground hover:text-foreground transition-colors">{label}</Link>
))}
</div>
</div>
</div>
<div className="flex flex-col md:flex-row items-center justify-between pt-8 border-t border-white/5 gap-4">
<p className="text-xs text-muted-foreground">© 2026 ScrollCraft. All rights reserved.</p>
<p className="text-xs text-muted-foreground">Built with Next.js</p>
</div>
</div>
</footer>
<SiteFooter />
</main>
);
}
11 changes: 2 additions & 9 deletions src/app/presets/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ArrowRight, Search, Sparkles, Eye } from "lucide-react";
import Navbar from "@/components/Navbar";
import StylePreview from "@/components/StylePreview";
import { PRESETS } from "@/lib/presets";
import SiteFooter from "@/components/SiteFooter";

const CATEGORIES = [
"All", "SaaS", "Agency", "E-commerce", "Mobile App", "Startup",
Expand Down Expand Up @@ -172,15 +173,7 @@ export default function PresetsPage() {
</Link>
</section>

{/* Footer */}
<footer className="py-8 px-6 border-t border-white/5 text-center text-sm text-muted-foreground">
<div className="flex items-center justify-center gap-2">
<div className="w-5 h-5 rounded bg-primary/20 flex items-center justify-center">
<Sparkles className="w-3 h-3 text-primary" />
</div>
ScrollCraft — Built with Next.js & AI
</div>
</footer>
<SiteFooter compact />
</main>
);
}
17 changes: 8 additions & 9 deletions src/app/pricing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Navbar from "@/components/Navbar";
import { PLANS, siteAllowance } from "@/lib/plans";
import { TEMPLATES } from "@/lib/templates";
import { PRESETS } from "@/lib/presets";
import SiteFooter from "@/components/SiteFooter";
import SponsorCard from "@/components/SponsorCard";

// ScrollCraft is free. The subscription tiers this page used to sell are retired — see
// the note in lib/plans.ts — so there is no billing toggle, no promo field and no
Expand Down Expand Up @@ -186,6 +188,11 @@ export default function PricingPage() {
</div>
</section>

{/* Open source */}
<section className="px-6 pb-16 max-w-6xl mx-auto">
<SponsorCard />
</section>

{/* FAQ */}
<section className="px-6 pb-24 max-w-3xl mx-auto">
<h2 className="text-2xl font-black tracking-tighter text-center mb-8">Questions</h2>
Expand Down Expand Up @@ -223,15 +230,7 @@ export default function PricingPage() {
</Link>
</section>

{/* Footer */}
<footer className="py-8 px-6 border-t border-white/5 text-center text-sm text-muted-foreground">
<div className="flex items-center justify-center gap-2">
<div className="w-5 h-5 rounded bg-primary/20 flex items-center justify-center">
<Sparkles className="w-3 h-3 text-primary" />
</div>
ScrollCraft — Built with Next.js
</div>
</footer>
<SiteFooter compact />
</main>
);
}
2 changes: 2 additions & 0 deletions src/app/privacy/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from "next/link";
import Navbar from "@/components/Navbar";
import SiteFooter from "@/components/SiteFooter";

export const metadata = { title: "Privacy Policy" };

Expand Down Expand Up @@ -37,6 +38,7 @@ export default function PrivacyPage() {
<h2>Contact</h2>
<p>Questions? Email <a href="mailto:hello@scrollcraft.app" className="underline">hello@scrollcraft.app</a>.</p>
</article>
<SiteFooter compact />
</main>
);
}
18 changes: 14 additions & 4 deletions src/app/templates/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { use, useCallback, useEffect, useState } from "react";
import { use, useCallback, useEffect, useMemo, useState } from "react";
import { notFound } from "next/navigation";
import Link from "next/link";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -39,12 +39,21 @@ function Preview({
sections: Section[];
children: React.ReactNode;
}) {
// SiteRenderer regenerates its frames whenever `styleSpec` changes identity, so this
// has to be a stable object. Passing a fresh literal restarted generation on every
// re-render, which left the premium preview stuck on "Rendering frames…" forever once
// the ownership check gave the component something to re-render for.
const styleSpec = useMemo(
() => ({ style: template.style, colors: template.colors }),
[template.style, template.colors]
);

return (
<SiteRenderer
name={template.name}
sections={sections}
theme={template.theme}
styleSpec={{ style: template.style, colors: template.colors }}
styleSpec={styleSpec}
cacheKey={`scrollcraft_template_${template.slug}`}
>
<div className="fixed top-4 left-4 right-4 z-40 flex items-center justify-between gap-3 pointer-events-none">
Expand Down Expand Up @@ -158,8 +167,9 @@ function PremiumPreview({ template }: { template: Template }) {
You&apos;re seeing the opening section
</p>
<p className="text-xs text-white/70 mb-3">
{hidden} more {hidden === 1 ? "section is" : "sections are"} written and waiting.
Unlock once and it&apos;s yours to edit and export forever.
{hidden} more {hidden === 1 ? "section is" : "sections are"}{" "}
written and waiting. Unlock once and it&apos;s yours to edit and export
forever.
</p>
<Button
size="sm"
Expand Down
2 changes: 2 additions & 0 deletions src/app/templates/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ArrowRight, Search, Layers, Eye, Lock } from "lucide-react";
import Navbar from "@/components/Navbar";
import StylePreview from "@/components/StylePreview";
import { TEMPLATES, templateCategories, templateScrollHeight, templateSectionCount } from "@/lib/templates";
import SiteFooter from "@/components/SiteFooter";

export default function TemplatesPage() {
const [search, setSearch] = useState("");
Expand Down Expand Up @@ -166,6 +167,7 @@ export default function TemplatesPage() {
</div>
)}
</section>
<SiteFooter compact />
</main>
);
}
2 changes: 2 additions & 0 deletions src/app/terms/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Navbar from "@/components/Navbar";
import SiteFooter from "@/components/SiteFooter";

export const metadata = { title: "Terms of Service" };

Expand Down Expand Up @@ -34,6 +35,7 @@ export default function TermsPage() {
<h2>Contact</h2>
<p>Questions? Email <a href="mailto:hello@scrollcraft.app" className="underline">hello@scrollcraft.app</a>.</p>
</article>
<SiteFooter compact />
</main>
);
}
8 changes: 8 additions & 0 deletions src/components/GitHubMark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** The GitHub mark. lucide-react dropped its brand icons, so this is the official path. */
export default function GitHubMark({ className = "w-4 h-4" }: { className?: string }) {
return (
<svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" className={className}>
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
</svg>
);
}
Loading
Loading