Beacon: launch site from blocks
A four-page product site assembled entirely from Ballmac blocks (Hero6, Stats1, Features5, Testimonials2, Cta3, Pricing2, Pricing3, Faq2, Changelog1, Contact1) in one palette, so any section can be swapped for another block.
// Ballmac UI: Launch template fonts. https://ui.ballmac.com/templates/template-launch
import { Geist_Mono, Schibsted_Grotesk } from "next/font/google"
export const launchSans = Schibsted_Grotesk({ variable: "--launch-sans", subsets: ["latin"], display: "swap", preload: false })
export const launchMono = Geist_Mono({ variable: "--launch-mono", subsets: ["latin"], display: "swap", preload: false })// Ballmac UI: Launch template shell. https://ui.ballmac.com/templates/template-launch
"use client"
import * as React from "react"
import { Globe, Mail } from "lucide-react"
import { Footer2 } from "@/components/ballmac/blocks/footer-2/footer-2"
import { Header2 } from "@/components/ballmac/blocks/header-2/header-2"
import { launchMono, launchSans } from "@/components/ballmac/templates/launch/launch-fonts"
import { cn } from "@/lib/utils"
type LaunchPage = "home" | "pricing" | "changelog" | "contact"
type LaunchHrefs = Record<LaunchPage, string>
const defaultHrefs: LaunchHrefs = { home: "/launch", pricing: "/launch/pricing", changelog: "/launch/changelog", contact: "/launch/contact" }
/** Beacon's palette: warm paper, graphite ink and one electric violet. Dark mode is graphite with the violet turned up. */
const launchCss = `
.launch-theme,body:not(:has([data-gallery])):has(.launch-theme){--background:oklch(0.985 0.006 85);--foreground:oklch(0.2 0.02 285);--card:oklch(1 0 0);--card-foreground:oklch(0.2 0.02 285);--popover:oklch(1 0 0);--popover-foreground:oklch(0.2 0.02 285);--primary:oklch(0.5 0.24 285);--primary-foreground:oklch(0.99 0.005 285);--secondary:oklch(0.95 0.012 85);--secondary-foreground:oklch(0.2 0.02 285);--muted:oklch(0.955 0.01 85);--muted-foreground:oklch(0.46 0.03 285);--accent:oklch(0.94 0.025 290);--accent-foreground:oklch(0.2 0.02 285);--border:oklch(0.2 0.02 285 / 12%);--input:oklch(0.2 0.02 285 / 20%);--ring:oklch(0.55 0.22 285);--surface:oklch(0.968 0.01 85);--destructive:oklch(0.52 0.21 27);--chart-1:oklch(0.5 0.24 285);--chart-2:oklch(0.7 0.17 160);--chart-3:oklch(0.75 0.16 70);--chart-4:oklch(0.65 0.2 25);--chart-5:oklch(0.62 0.17 230);--radius:0.9rem}
.dark .launch-theme,.dark body:not(:has([data-gallery])):has(.launch-theme){--background:oklch(0.15 0.015 285);--foreground:oklch(0.96 0.008 85);--card:oklch(0.19 0.018 285);--card-foreground:oklch(0.96 0.008 85);--popover:oklch(0.21 0.02 285);--popover-foreground:oklch(0.96 0.008 85);--primary:oklch(0.72 0.19 285);--primary-foreground:oklch(0.15 0.03 285);--secondary:oklch(0.24 0.02 285);--secondary-foreground:oklch(0.96 0.008 85);--muted:oklch(0.23 0.02 285);--muted-foreground:oklch(0.74 0.03 285);--accent:oklch(0.28 0.04 285);--accent-foreground:oklch(0.96 0.008 85);--border:oklch(1 0 0 / 10%);--input:oklch(1 0 0 / 15%);--ring:oklch(0.72 0.19 285);--surface:oklch(0.175 0.017 285);--destructive:oklch(0.7 0.19 27);--chart-1:oklch(0.72 0.19 285);--chart-2:oklch(0.78 0.16 160);--chart-3:oklch(0.82 0.14 75);--chart-4:oklch(0.74 0.17 25);--chart-5:oklch(0.76 0.13 230)}
body:not(:has([data-gallery])):has(.launch-theme){font-family:var(--launch-sans),ui-sans-serif,system-ui,sans-serif}
`
type LaunchShellProps = React.ComponentProps<"div"> & {
/** The page being shown. */
page: LaunchPage
/** Override where pages live (used by previews). */
hrefs?: Partial<LaunchHrefs>
}
/**
* Beacon's frame: Header2 and Footer2 with Beacon's links, in Beacon's palette and type.
* Every page of the template is this shell around Ballmac blocks, so any block can be swapped for another.
*/
function LaunchShell({ page: _page, hrefs: overrides, className, style, children, ...props }: LaunchShellProps) {
const hrefs = { ...defaultHrefs, ...overrides }
React.useEffect(() => {
const classes = [launchSans.variable, launchMono.variable].filter(Boolean)
document.body.classList.add(...classes)
return () => document.body.classList.remove(...classes)
}, [])
return (
<div
data-slot="launch"
className={cn("launch-theme bg-background text-foreground min-h-dvh overflow-x-clip", launchSans.variable, launchMono.variable, className)}
style={{ fontFamily: "var(--launch-sans), ui-sans-serif, system-ui, sans-serif", ...style }}
{...props}
>
<style>{launchCss}</style>
<Header2
brand="Beacon"
brandHref={hrefs.home}
items={[{ label: "Product", href: hrefs.home }, { label: "Pricing", href: hrefs.pricing }, { label: "Changelog", href: hrefs.changelog }, { label: "Contact", href: hrefs.contact }]}
secondaryAction={{ label: "Sign in", href: hrefs.contact }}
primaryAction={{ label: "Start free", href: hrefs.pricing }}
/>
{children}
<Footer2
brand="Beacon"
tagline="Preview every branch, watch every budget and roll back in one click."
columns={[
{ title: "Product", links: [{ label: "Overview", href: hrefs.home }, { label: "Pricing", href: hrefs.pricing }, { label: "Changelog", href: hrefs.changelog }] },
{ title: "Company", links: [{ label: "About", href: hrefs.home }, { label: "Careers", href: hrefs.contact }, { label: "Contact", href: hrefs.contact }] },
{ title: "Resources", links: [{ label: "Documentation", href: hrefs.home }, { label: "Status", href: hrefs.home }, { label: "Security", href: hrefs.home }] },
]}
socials={[{ label: "Beacon website", href: hrefs.home, icon: <Globe /> }, { label: "Email Beacon", href: hrefs.contact, icon: <Mail /> }]}
legal="© 2026 Beacon Labs, Inc."
legalLinks={[{ label: "Privacy", href: hrefs.home }, { label: "Terms", href: hrefs.home }]}
/>
</div>
)
}
export { LaunchShell, defaultHrefs as launchDefaultHrefs, launchMono, type LaunchHrefs, type LaunchPage, type LaunchShellProps }// Ballmac UI: Launch template page. https://ui.ballmac.com/templates/template-launch
"use client"
import * as React from "react"
import { Check, GitBranch, Gauge, Rocket, RotateCcw } from "lucide-react"
import { Cta3 } from "@/components/ballmac/blocks/cta-3/cta-3"
import { Features5 } from "@/components/ballmac/blocks/features-5/features-5"
import { Hero6, Hero6Float } from "@/components/ballmac/blocks/hero-6/hero-6"
import { LogoCloud1 } from "@/components/ballmac/blocks/logo-cloud-1/logo-cloud-1"
import { Stats1 } from "@/components/ballmac/blocks/stats-1/stats-1"
import { Testimonials2 } from "@/components/ballmac/blocks/testimonials-2/testimonials-2"
import { LaunchShell, launchMono, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme"
import { cn } from "@/lib/utils"
const card = "bg-card w-64 rounded-2xl border p-4 shadow-lg"
/** The three cards that float around the hero: a deploy, a budget and a rollback. Replace with product shots. */
function HeroCards() {
return (
<>
<Hero6Float depth={22} bob={6.5} className="xl:top-[50%] xl:start-0 2xl:start-[3%]">
<div className={card}>
<p className="flex items-center gap-2 text-sm font-semibold"><Rocket className="text-primary size-4" aria-hidden="true" />Preview ready</p>
<p className={cn("text-muted-foreground mt-2 text-xs", launchMono.className)}>feat/checkout-v2 · 41s</p>
<p className="mt-3 flex items-center gap-1.5 text-xs font-medium"><span className="bg-chart-2 size-2 rounded-full" aria-hidden="true" />Live at pe-482.beacon.app</p>
</div>
</Hero6Float>
<Hero6Float depth={34} bob={7.5} delay={1.2} className="xl:top-[44%] xl:end-0 2xl:end-[3%]">
<div className={card}>
<p className="flex items-center gap-2 text-sm font-semibold"><Gauge className="text-primary size-4" aria-hidden="true" />Performance budget</p>
<p className="mt-2 text-2xl font-semibold tabular-nums">94 <span className="text-muted-foreground text-xs font-medium">/ 90 required</span></p>
<div className="bg-muted mt-2 h-1.5 overflow-hidden rounded-full" aria-hidden="true"><div className="bg-chart-2 h-full w-[94%]" /></div>
</div>
</Hero6Float>
<Hero6Float depth={14} bob={8} delay={0.6} className="xl:end-[3%] xl:bottom-[1%] 2xl:end-[9%]">
<div className={card}>
<p className="flex items-center gap-2 text-sm font-semibold"><RotateCcw className="text-primary size-4" aria-hidden="true" />Rolled back</p>
<p className="text-muted-foreground mt-2 text-xs">Error rate crossed 2%. Production returned to <span className={launchMono.className}>v2.18.3</span> in 6s.</p>
</div>
</Hero6Float>
</>
)
}
function Visual({ children, label }: { children: React.ReactNode; label: string }) {
return <div role="img" aria-label={label} className="bg-surface grid min-h-72 place-items-center rounded-3xl border p-6">{children}</div>
}
const steps = [
{
title: "Every branch gets a URL",
description: "Push a branch and Beacon builds it, runs your checks and posts a live preview on the pull request. Reviewers click a link instead of cloning a repo.",
points: ["Previews in under a minute", "Comments pinned to the page", "Shareable with people outside your team"],
visual: (
<Visual label="A list of three preview deployments, the newest building">
<ul className="grid w-full max-w-sm gap-2.5">{[["feat/checkout-v2", "Ready", "bg-chart-2"], ["fix/nav-focus", "Ready", "bg-chart-2"], ["chore/deps", "Building", "bg-chart-3"]].map(([b, s, c]) => <li key={b} className="bg-card flex items-center justify-between rounded-xl border px-4 py-3 text-sm shadow-xs"><span className="flex items-center gap-2"><GitBranch className="text-muted-foreground size-4" aria-hidden="true" /><span className={launchMono.className}>{b}</span></span><span className="flex items-center gap-1.5 text-xs font-medium"><span className={cn("size-2 rounded-full", c)} aria-hidden="true" />{s}</span></li>)}</ul>
</Visual>
),
},
{
title: "Budgets that block bad merges",
description: "Set a ceiling for bundle size, load time or accessibility score. If a change breaks it, the check fails with the exact line that did.",
points: ["Lighthouse and bundle budgets", "Per-route limits", "Trend lines over time"],
visual: (
<Visual label="Three budget bars: bundle size passes, load time passes and accessibility fails">
<ul className="grid w-full max-w-sm gap-4">{[["Bundle size", 72, true, "148 KB of 200 KB"], ["Load time", 58, true, "1.6 s of 2.5 s"], ["Accessibility", 100, false, "Score 86, needs 90"]].map(([l, w, ok, note]) => <li key={l as string}><div className="flex justify-between text-sm font-medium"><span>{l as string}</span><span className="text-muted-foreground text-xs">{note as string}</span></div><div className="bg-muted mt-1.5 h-2 overflow-hidden rounded-full" aria-hidden="true"><div className={cn("h-full rounded-full", ok ? "bg-chart-2" : "bg-chart-4")} style={{ width: `${w}%` }} /></div></li>)}</ul>
</Visual>
),
},
{
title: "Roll back before anyone notices",
description: "Beacon watches errors and latency after every release. Cross a threshold and it returns production to the last good build, then tells you why.",
points: ["One-click or automatic rollback", "Alerts in Slack and email", "A full audit trail"],
visual: (
<Visual label="A release timeline where version 2.19.0 is rolled back to 2.18.3">
<ol className="grid w-full max-w-sm gap-3">{[["v2.19.0", "Released 14:02", "bg-chart-4", "Rolled back at 14:03"], ["v2.18.3", "Restored 14:03", "bg-chart-2", "Serving traffic"]].map(([v, t, c, n]) => <li key={v} className="bg-card flex items-start gap-3 rounded-xl border p-4 shadow-xs"><span className={cn("mt-1.5 size-2.5 shrink-0 rounded-full", c)} aria-hidden="true" /><span><span className={cn("block text-sm font-semibold", launchMono.className)}>{v}</span><span className="text-muted-foreground block text-xs">{t}</span><span className="mt-1 flex items-center gap-1 text-xs font-medium"><Check className="size-3.5" aria-hidden="true" />{n}</span></span></li>)}</ol>
</Visual>
),
},
]
type LaunchHomeProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> }
/**
* The Launch home page: every section is a Ballmac block (Hero6, LogoCloud1, Stats1, Features5, Testimonials2, Cta3).
* Edit the copy through each block's props, reorder them, or swap one for another block in the same category.
*/
function LaunchPage({ hrefs, ...props }: LaunchHomeProps) {
const link = { pricing: "/launch/pricing", contact: "/launch/contact", changelog: "/launch/changelog", ...hrefs }
return (
<LaunchShell page="home" hrefs={hrefs} {...props}>
<main>
<Hero6
eyebrow="Beacon 3.2 · automatic rollbacks"
title="Ship every branch with"
highlight="nothing to fear."
description="Preview URLs, performance budgets and one-click rollbacks for every pull request. Know what you are about to ship, and undo it in seconds if you were wrong."
primaryAction={{ label: "Start for free", href: link.pricing }}
secondaryAction={{ label: "Talk to us", href: link.contact }}
highlights={["Free for small teams", "Connects to GitHub in a minute", "No card needed"]}
cards={<HeroCards />}
/>
<LogoCloud1 title="Trusted by teams who deploy on a Friday" />
<Stats1
eyebrow="By the numbers"
title="Fewer incidents, faster merges."
description="Across 9,400 teams and 3.1 million deploys last quarter."
link={{ label: "Read the report", href: link.changelog }}
stats={[
{ label: "Teams on Beacon", value: 9400, suffix: "+", delta: "+21%", trend: [3, 4, 5, 6, 7, 8, 8, 9, 9.4], trendLabel: "Teams, last 9 months", note: "Up from 7,800 in the spring." },
{ label: "Deploys each quarter", value: 3.1, suffix: "M", decimals: 1, delta: "+32%", trend: [1.2, 1.5, 1.7, 2, 2.2, 2.4, 2.7, 2.9, 3.1], trendLabel: "Deploys, last 9 months", note: "Roughly one every three seconds." },
{ label: "Median preview time", value: 41, suffix: " s", delta: "−12 s", trend: [70, 66, 60, 55, 52, 48, 45, 43, 41], trendLabel: "Preview time, last 9 months", note: "From push to a live URL." },
{ label: "Rollback time", value: 6, suffix: " s", trend: [30, 24, 18, 12, 10, 8, 7, 6, 6], trendLabel: "Rollback time, last 9 months", note: "From alert to the last good build." },
]}
/>
<Features5 eyebrow="How it works" title="Preview it. Check it. Undo it." description="Three things every release needs, built into the same pull request." steps={steps} />
<Testimonials2
eyebrow="What teams say"
items={[
{ quote: "We used to dread Fridays. Now a bad deploy rolls itself back before I finish reading the alert.", name: "Maya Chen", role: "VP Engineering", company: "Northwind", result: "−71% incident minutes" },
{ quote: "Budgets on pull requests changed how my team thinks about performance. Nobody argues about it anymore, the check just says no.", name: "Tomás Reyes", role: "Staff Engineer", company: "Parcel", result: "Bundle −38%" },
{ quote: "Design review used to mean screenshots in Slack. Now it is a link and a comment pinned to the button.", name: "Ingrid Solheim", role: "Design Director", company: "Fjord & Co", result: "Reviews 3× faster" },
]}
/>
<Cta3
title="Your next deploy, with a safety net."
description="Connect a repository and Beacon builds your first preview in about a minute."
command="create-beacon@latest"
storageKey="beacon-pm"
primaryAction={{ label: "Start for free", href: link.pricing }}
secondaryAction={{ label: "Read the docs", href: link.changelog }}
notes={["Works with Next.js, Remix, Astro and plain static sites", "Free forever for personal projects"]}
/>
</main>
</LaunchShell>
)
}
export { LaunchPage, type LaunchHomeProps }// Ballmac UI: Launch pricing page. https://ui.ballmac.com/templates/template-launch
"use client"
import * as React from "react"
import { Faq2 } from "@/components/ballmac/blocks/faq-2/faq-2"
import { Pricing2 } from "@/components/ballmac/blocks/pricing-2/pricing-2"
import { Pricing3 } from "@/components/ballmac/blocks/pricing-3/pricing-3"
import { LaunchShell, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme"
type LaunchPricingProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> }
/** The Launch pricing page: Pricing2 (plans with a yearly switch), Pricing3 (full comparison) and Faq2. */
function LaunchPricing({ hrefs, ...props }: LaunchPricingProps) {
const link = { contact: "/launch/contact", ...hrefs }
return (
<LaunchShell page="pricing" hrefs={hrefs} {...props}>
<main>
<Pricing2
title="Pay for the team you have."
description="Start free. Upgrade when your team grows. Every plan includes unlimited previews."
plans={[
{ name: "Hobby", description: "For side projects and learning.", price: { monthly: 0, yearly: 0 }, unit: "free forever", features: ["3 projects", "Unlimited previews", "Community support", "7 days of history"], cta: { label: "Start free", href: link.contact } },
{ name: "Team", description: "For product teams who ship weekly.", price: { monthly: 24, yearly: 19 }, unit: "per seat", lead: "Everything in Hobby, plus:", features: ["Unlimited projects", "Performance budgets", "Automatic rollbacks", "90 days of history", "Slack alerts"], cta: { label: "Start a 14-day trial", href: link.contact }, featured: true },
{ name: "Company", description: "For organisations with compliance needs.", price: "Custom", lead: "Everything in Team, plus:", features: ["SSO and SCIM", "Audit log export", "Private networking", "Dedicated support", "Custom retention"], cta: { label: "Talk to sales", href: link.contact } },
]}
/>
<Pricing3
title="Compare every feature"
description="The details, side by side."
stickyOffset={64}
plans={[
{ key: "hobby", name: "Hobby", price: "$0", period: "free forever", cta: { label: "Start free", href: link.contact } },
{ key: "team", name: "Team", price: "$24", period: "per seat / month", cta: { label: "Start trial", href: link.contact }, featured: true },
{ key: "company", name: "Company", price: "Custom", period: "talk to us", cta: { label: "Talk to sales", href: link.contact } },
]}
groups={[
{ title: "Deploys", rows: [
{ label: "Preview deployments", values: { hobby: "Unlimited", team: "Unlimited", company: "Unlimited" } },
{ label: "Concurrent builds", hint: "Builds that can run at the same time", values: { hobby: "1", team: "10", company: "50" } },
{ label: "Build history", values: { hobby: "7 days", team: "90 days", company: "Custom" } },
] },
{ title: "Safety", rows: [
{ label: "Performance budgets", values: { hobby: false, team: true, company: true } },
{ label: "Automatic rollbacks", values: { hobby: false, team: true, company: true } },
{ label: "Audit log export", values: { hobby: false, team: false, company: true } },
] },
{ title: "Support", rows: [
{ label: "Slack and email alerts", values: { hobby: false, team: true, company: true } },
{ label: "SSO and SCIM", values: { hobby: false, team: false, company: true } },
{ label: "Support", values: { hobby: "Community", team: "Email, 1 day", company: "Dedicated, 1 hour" } },
] },
]}
/>
<Faq2
title="Pricing questions"
description="Still unsure? These are the ones we hear most."
support={{ label: "Contact support", href: link.contact, text: "Not here? A human replies within a working day." }}
categories={[
{ name: "Billing", questions: [
{ question: "Can I change plans later?", answer: "Yes. Upgrade or downgrade at any time. We prorate the difference, so you only pay for what you use." },
{ question: "How do seats work?", answer: "You pay for people who can deploy. Viewers who only comment on previews are always free." },
{ question: "Do you offer discounts?", answer: "Open source projects and students get Team free. Annual billing saves about 20%." },
] },
{ name: "Product", questions: [
{ question: "What counts as a preview?", answer: "Every branch or pull request build. There is no limit on how many you create." },
{ question: "How do rollbacks decide?", answer: "You set thresholds for error rate and latency. If a release crosses one, Beacon restores the last good build." },
] },
{ name: "Security", questions: [
{ question: "Where is my code stored?", answer: "Builds run in isolated, short-lived containers in the region you choose. Source is never kept after the build." },
] },
]}
/>
</main>
</LaunchShell>
)
}
export { LaunchPricing, type LaunchPricingProps }// Ballmac UI: Launch changelog page. https://ui.ballmac.com/templates/template-launch
"use client"
import * as React from "react"
import { Changelog1 } from "@/components/ballmac/blocks/changelog-1/changelog-1"
import { LaunchShell, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme"
type LaunchChangelogProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> }
/** The Launch changelog page: Changelog1 with Beacon's releases. */
function LaunchChangelog({ hrefs, ...props }: LaunchChangelogProps) {
return (
<LaunchShell page="changelog" hrefs={hrefs} {...props}>
<main>
<Changelog1
title="What’s new in Beacon"
description="Every release, newest first. We ship something most weeks."
initialCount={3}
releases={[
{ version: "3.2.0", date: "2026-09-24", title: "Automatic rollbacks", summary: "Set an error or latency threshold and Beacon returns production to the last good build when a release crosses it.", cover: 0, changes: [{ type: "new", text: "Rollback rules per project, with Slack and email alerts" }, { type: "new", text: "A rollback timeline on every deployment" }, { type: "improved", text: "Rollbacks finish in a median of six seconds" }] },
{ version: "3.1.2", date: "2026-09-09", title: "Budgets for accessibility", summary: "Fail a pull request when its accessibility score drops below your ceiling.", cover: 1, changes: [{ type: "new", text: "Accessibility score as a budget" }, { type: "improved", text: "Budget failures link to the offending element" }, { type: "fixed", text: "Budgets no longer reset when a project is renamed" }] },
{ version: "3.1.0", date: "2026-08-20", title: "Comments pinned to the page", changes: [{ type: "new", text: "Pin a comment to any element on a preview" }, { type: "improved", text: "Reviewers outside your team can comment without an account" }] },
{ version: "3.0.4", date: "2026-08-02", title: "Faster monorepo builds", changes: [{ type: "improved", text: "Builds skip packages that did not change" }, { type: "fixed", text: "Cached dependencies are no longer invalidated by lockfile whitespace" }] },
{ version: "3.0.0", date: "2026-06-18", title: "Beacon 3", summary: "A new dashboard, regions and a rebuilt build pipeline.", cover: 2, changes: [{ type: "new", text: "Choose where builds run: Frankfurt, Virginia or Singapore" }, { type: "improved", text: "A new dashboard that loads in under a second" }, { type: "fixed", text: "Fifteen long-standing bugs in the Git integration" }] },
]}
/>
</main>
</LaunchShell>
)
}
export { LaunchChangelog, type LaunchChangelogProps }// Ballmac UI: Launch contact page. https://ui.ballmac.com/templates/template-launch
"use client"
import * as React from "react"
import { Calendar, LifeBuoy, Mail } from "lucide-react"
import { Contact1 } from "@/components/ballmac/blocks/contact-1/contact-1"
import { LaunchShell, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme"
type LaunchContactProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> }
/** The Launch contact page: Contact1 with Beacon's channels and topics. */
function LaunchContact({ hrefs, ...props }: LaunchContactProps) {
return (
<LaunchShell page="contact" hrefs={hrefs} {...props}>
<main>
<Contact1
title="Talk to a person."
description="Questions about plans, security or migrating from another platform? Tell us what you need and the right person will reply."
topics={["Sales and pricing", "Security review", "Migrating to Beacon", "Support", "Something else"]}
responseTime="We reply within one working day"
methods={[
{ icon: <Mail />, label: "Email", value: "hello@beacon.example", href: "mailto:hello@beacon.example" },
{ icon: <Calendar />, label: "Book a demo", value: "Thirty minutes with an engineer", href: "#" },
{ icon: <LifeBuoy />, label: "Support", value: "Customers: open a ticket in the dashboard", href: "#" },
]}
/>
</main>
</LaunchShell>
)
}
export { LaunchContact, type LaunchContactProps }// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch
import type { Metadata } from "next"
import { LaunchPage } from "@/components/ballmac/templates/launch/launch-page"
export const metadata: Metadata = {
title: "Beacon: ship every branch with nothing to fear",
description: "Preview URLs, performance budgets and one-click rollbacks for every pull request.",
}
export default function Page() {
return <LaunchPage />
}// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch
import type { Metadata } from "next"
import { LaunchPricing } from "@/components/ballmac/templates/launch/launch-pricing"
export const metadata: Metadata = {
title: "Pricing · Beacon",
description: "Start free. Upgrade when your team grows.",
}
export default function Page() {
return <LaunchPricing />
}// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch
import type { Metadata } from "next"
import { LaunchChangelog } from "@/components/ballmac/templates/launch/launch-changelog"
export const metadata: Metadata = {
title: "Changelog · Beacon",
description: "Every release, newest first.",
}
export default function Page() {
return <LaunchChangelog />
}// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch
import type { Metadata } from "next"
import { LaunchContact } from "@/components/ballmac/templates/launch/launch-contact"
export const metadata: Metadata = {
title: "Contact · Beacon",
description: "Talk to a person about plans, security or migrating.",
}
export default function Page() {
return <LaunchContact />
}Installation
$ pnpm dlx shadcn@latest add @ballmac/template-launchInstall the dependencies.
$ pnpm add lucide-reactAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/header-2 @ballmac/footer-2 @ballmac/hero-6 @ballmac/logo-cloud-1 @ballmac/stats-1 @ballmac/features-5 @ballmac/testimonials-2 @ballmac/cta-3 @ballmac/pricing-2 @ballmac/pricing-3 @ballmac/faq-2 @ballmac/changelog-1 @ballmac/contact-1Copy the source into your project.
components/ballmac/templates/launch/launch-fonts.ts// Ballmac UI: Launch template fonts. https://ui.ballmac.com/templates/template-launch import { Geist_Mono, Schibsted_Grotesk } from "next/font/google" export const launchSans = Schibsted_Grotesk({ variable: "--launch-sans", subsets: ["latin"], display: "swap", preload: false }) export const launchMono = Geist_Mono({ variable: "--launch-mono", subsets: ["latin"], display: "swap", preload: false })components/ballmac/templates/launch/launch-theme.tsx// Ballmac UI: Launch template shell. https://ui.ballmac.com/templates/template-launch "use client" import * as React from "react" import { Globe, Mail } from "lucide-react" import { Footer2 } from "@/components/ballmac/blocks/footer-2/footer-2" import { Header2 } from "@/components/ballmac/blocks/header-2/header-2" import { launchMono, launchSans } from "@/components/ballmac/templates/launch/launch-fonts" import { cn } from "@/lib/utils" type LaunchPage = "home" | "pricing" | "changelog" | "contact" type LaunchHrefs = Record<LaunchPage, string> const defaultHrefs: LaunchHrefs = { home: "/launch", pricing: "/launch/pricing", changelog: "/launch/changelog", contact: "/launch/contact" } /** Beacon's palette: warm paper, graphite ink and one electric violet. Dark mode is graphite with the violet turned up. */ const launchCss = ` .launch-theme,body:not(:has([data-gallery])):has(.launch-theme){--background:oklch(0.985 0.006 85);--foreground:oklch(0.2 0.02 285);--card:oklch(1 0 0);--card-foreground:oklch(0.2 0.02 285);--popover:oklch(1 0 0);--popover-foreground:oklch(0.2 0.02 285);--primary:oklch(0.5 0.24 285);--primary-foreground:oklch(0.99 0.005 285);--secondary:oklch(0.95 0.012 85);--secondary-foreground:oklch(0.2 0.02 285);--muted:oklch(0.955 0.01 85);--muted-foreground:oklch(0.46 0.03 285);--accent:oklch(0.94 0.025 290);--accent-foreground:oklch(0.2 0.02 285);--border:oklch(0.2 0.02 285 / 12%);--input:oklch(0.2 0.02 285 / 20%);--ring:oklch(0.55 0.22 285);--surface:oklch(0.968 0.01 85);--destructive:oklch(0.52 0.21 27);--chart-1:oklch(0.5 0.24 285);--chart-2:oklch(0.7 0.17 160);--chart-3:oklch(0.75 0.16 70);--chart-4:oklch(0.65 0.2 25);--chart-5:oklch(0.62 0.17 230);--radius:0.9rem} .dark .launch-theme,.dark body:not(:has([data-gallery])):has(.launch-theme){--background:oklch(0.15 0.015 285);--foreground:oklch(0.96 0.008 85);--card:oklch(0.19 0.018 285);--card-foreground:oklch(0.96 0.008 85);--popover:oklch(0.21 0.02 285);--popover-foreground:oklch(0.96 0.008 85);--primary:oklch(0.72 0.19 285);--primary-foreground:oklch(0.15 0.03 285);--secondary:oklch(0.24 0.02 285);--secondary-foreground:oklch(0.96 0.008 85);--muted:oklch(0.23 0.02 285);--muted-foreground:oklch(0.74 0.03 285);--accent:oklch(0.28 0.04 285);--accent-foreground:oklch(0.96 0.008 85);--border:oklch(1 0 0 / 10%);--input:oklch(1 0 0 / 15%);--ring:oklch(0.72 0.19 285);--surface:oklch(0.175 0.017 285);--destructive:oklch(0.7 0.19 27);--chart-1:oklch(0.72 0.19 285);--chart-2:oklch(0.78 0.16 160);--chart-3:oklch(0.82 0.14 75);--chart-4:oklch(0.74 0.17 25);--chart-5:oklch(0.76 0.13 230)} body:not(:has([data-gallery])):has(.launch-theme){font-family:var(--launch-sans),ui-sans-serif,system-ui,sans-serif} ` type LaunchShellProps = React.ComponentProps<"div"> & { /** The page being shown. */ page: LaunchPage /** Override where pages live (used by previews). */ hrefs?: Partial<LaunchHrefs> } /** * Beacon's frame: Header2 and Footer2 with Beacon's links, in Beacon's palette and type. * Every page of the template is this shell around Ballmac blocks, so any block can be swapped for another. */ function LaunchShell({ page: _page, hrefs: overrides, className, style, children, ...props }: LaunchShellProps) { const hrefs = { ...defaultHrefs, ...overrides } React.useEffect(() => { const classes = [launchSans.variable, launchMono.variable].filter(Boolean) document.body.classList.add(...classes) return () => document.body.classList.remove(...classes) }, []) return ( <div data-slot="launch" className={cn("launch-theme bg-background text-foreground min-h-dvh overflow-x-clip", launchSans.variable, launchMono.variable, className)} style={{ fontFamily: "var(--launch-sans), ui-sans-serif, system-ui, sans-serif", ...style }} {...props} > <style>{launchCss}</style> <Header2 brand="Beacon" brandHref={hrefs.home} items={[{ label: "Product", href: hrefs.home }, { label: "Pricing", href: hrefs.pricing }, { label: "Changelog", href: hrefs.changelog }, { label: "Contact", href: hrefs.contact }]} secondaryAction={{ label: "Sign in", href: hrefs.contact }} primaryAction={{ label: "Start free", href: hrefs.pricing }} /> {children} <Footer2 brand="Beacon" tagline="Preview every branch, watch every budget and roll back in one click." columns={[ { title: "Product", links: [{ label: "Overview", href: hrefs.home }, { label: "Pricing", href: hrefs.pricing }, { label: "Changelog", href: hrefs.changelog }] }, { title: "Company", links: [{ label: "About", href: hrefs.home }, { label: "Careers", href: hrefs.contact }, { label: "Contact", href: hrefs.contact }] }, { title: "Resources", links: [{ label: "Documentation", href: hrefs.home }, { label: "Status", href: hrefs.home }, { label: "Security", href: hrefs.home }] }, ]} socials={[{ label: "Beacon website", href: hrefs.home, icon: <Globe /> }, { label: "Email Beacon", href: hrefs.contact, icon: <Mail /> }]} legal="© 2026 Beacon Labs, Inc." legalLinks={[{ label: "Privacy", href: hrefs.home }, { label: "Terms", href: hrefs.home }]} /> </div> ) } export { LaunchShell, defaultHrefs as launchDefaultHrefs, launchMono, type LaunchHrefs, type LaunchPage, type LaunchShellProps }components/ballmac/templates/launch/launch-page.tsx// Ballmac UI: Launch template page. https://ui.ballmac.com/templates/template-launch "use client" import * as React from "react" import { Check, GitBranch, Gauge, Rocket, RotateCcw } from "lucide-react" import { Cta3 } from "@/components/ballmac/blocks/cta-3/cta-3" import { Features5 } from "@/components/ballmac/blocks/features-5/features-5" import { Hero6, Hero6Float } from "@/components/ballmac/blocks/hero-6/hero-6" import { LogoCloud1 } from "@/components/ballmac/blocks/logo-cloud-1/logo-cloud-1" import { Stats1 } from "@/components/ballmac/blocks/stats-1/stats-1" import { Testimonials2 } from "@/components/ballmac/blocks/testimonials-2/testimonials-2" import { LaunchShell, launchMono, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme" import { cn } from "@/lib/utils" const card = "bg-card w-64 rounded-2xl border p-4 shadow-lg" /** The three cards that float around the hero: a deploy, a budget and a rollback. Replace with product shots. */ function HeroCards() { return ( <> <Hero6Float depth={22} bob={6.5} className="xl:top-[50%] xl:start-0 2xl:start-[3%]"> <div className={card}> <p className="flex items-center gap-2 text-sm font-semibold"><Rocket className="text-primary size-4" aria-hidden="true" />Preview ready</p> <p className={cn("text-muted-foreground mt-2 text-xs", launchMono.className)}>feat/checkout-v2 · 41s</p> <p className="mt-3 flex items-center gap-1.5 text-xs font-medium"><span className="bg-chart-2 size-2 rounded-full" aria-hidden="true" />Live at pe-482.beacon.app</p> </div> </Hero6Float> <Hero6Float depth={34} bob={7.5} delay={1.2} className="xl:top-[44%] xl:end-0 2xl:end-[3%]"> <div className={card}> <p className="flex items-center gap-2 text-sm font-semibold"><Gauge className="text-primary size-4" aria-hidden="true" />Performance budget</p> <p className="mt-2 text-2xl font-semibold tabular-nums">94 <span className="text-muted-foreground text-xs font-medium">/ 90 required</span></p> <div className="bg-muted mt-2 h-1.5 overflow-hidden rounded-full" aria-hidden="true"><div className="bg-chart-2 h-full w-[94%]" /></div> </div> </Hero6Float> <Hero6Float depth={14} bob={8} delay={0.6} className="xl:end-[3%] xl:bottom-[1%] 2xl:end-[9%]"> <div className={card}> <p className="flex items-center gap-2 text-sm font-semibold"><RotateCcw className="text-primary size-4" aria-hidden="true" />Rolled back</p> <p className="text-muted-foreground mt-2 text-xs">Error rate crossed 2%. Production returned to <span className={launchMono.className}>v2.18.3</span> in 6s.</p> </div> </Hero6Float> </> ) } function Visual({ children, label }: { children: React.ReactNode; label: string }) { return <div role="img" aria-label={label} className="bg-surface grid min-h-72 place-items-center rounded-3xl border p-6">{children}</div> } const steps = [ { title: "Every branch gets a URL", description: "Push a branch and Beacon builds it, runs your checks and posts a live preview on the pull request. Reviewers click a link instead of cloning a repo.", points: ["Previews in under a minute", "Comments pinned to the page", "Shareable with people outside your team"], visual: ( <Visual label="A list of three preview deployments, the newest building"> <ul className="grid w-full max-w-sm gap-2.5">{[["feat/checkout-v2", "Ready", "bg-chart-2"], ["fix/nav-focus", "Ready", "bg-chart-2"], ["chore/deps", "Building", "bg-chart-3"]].map(([b, s, c]) => <li key={b} className="bg-card flex items-center justify-between rounded-xl border px-4 py-3 text-sm shadow-xs"><span className="flex items-center gap-2"><GitBranch className="text-muted-foreground size-4" aria-hidden="true" /><span className={launchMono.className}>{b}</span></span><span className="flex items-center gap-1.5 text-xs font-medium"><span className={cn("size-2 rounded-full", c)} aria-hidden="true" />{s}</span></li>)}</ul> </Visual> ), }, { title: "Budgets that block bad merges", description: "Set a ceiling for bundle size, load time or accessibility score. If a change breaks it, the check fails with the exact line that did.", points: ["Lighthouse and bundle budgets", "Per-route limits", "Trend lines over time"], visual: ( <Visual label="Three budget bars: bundle size passes, load time passes and accessibility fails"> <ul className="grid w-full max-w-sm gap-4">{[["Bundle size", 72, true, "148 KB of 200 KB"], ["Load time", 58, true, "1.6 s of 2.5 s"], ["Accessibility", 100, false, "Score 86, needs 90"]].map(([l, w, ok, note]) => <li key={l as string}><div className="flex justify-between text-sm font-medium"><span>{l as string}</span><span className="text-muted-foreground text-xs">{note as string}</span></div><div className="bg-muted mt-1.5 h-2 overflow-hidden rounded-full" aria-hidden="true"><div className={cn("h-full rounded-full", ok ? "bg-chart-2" : "bg-chart-4")} style={{ width: `${w}%` }} /></div></li>)}</ul> </Visual> ), }, { title: "Roll back before anyone notices", description: "Beacon watches errors and latency after every release. Cross a threshold and it returns production to the last good build, then tells you why.", points: ["One-click or automatic rollback", "Alerts in Slack and email", "A full audit trail"], visual: ( <Visual label="A release timeline where version 2.19.0 is rolled back to 2.18.3"> <ol className="grid w-full max-w-sm gap-3">{[["v2.19.0", "Released 14:02", "bg-chart-4", "Rolled back at 14:03"], ["v2.18.3", "Restored 14:03", "bg-chart-2", "Serving traffic"]].map(([v, t, c, n]) => <li key={v} className="bg-card flex items-start gap-3 rounded-xl border p-4 shadow-xs"><span className={cn("mt-1.5 size-2.5 shrink-0 rounded-full", c)} aria-hidden="true" /><span><span className={cn("block text-sm font-semibold", launchMono.className)}>{v}</span><span className="text-muted-foreground block text-xs">{t}</span><span className="mt-1 flex items-center gap-1 text-xs font-medium"><Check className="size-3.5" aria-hidden="true" />{n}</span></span></li>)}</ol> </Visual> ), }, ] type LaunchHomeProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> } /** * The Launch home page: every section is a Ballmac block (Hero6, LogoCloud1, Stats1, Features5, Testimonials2, Cta3). * Edit the copy through each block's props, reorder them, or swap one for another block in the same category. */ function LaunchPage({ hrefs, ...props }: LaunchHomeProps) { const link = { pricing: "/launch/pricing", contact: "/launch/contact", changelog: "/launch/changelog", ...hrefs } return ( <LaunchShell page="home" hrefs={hrefs} {...props}> <main> <Hero6 eyebrow="Beacon 3.2 · automatic rollbacks" title="Ship every branch with" highlight="nothing to fear." description="Preview URLs, performance budgets and one-click rollbacks for every pull request. Know what you are about to ship, and undo it in seconds if you were wrong." primaryAction={{ label: "Start for free", href: link.pricing }} secondaryAction={{ label: "Talk to us", href: link.contact }} highlights={["Free for small teams", "Connects to GitHub in a minute", "No card needed"]} cards={<HeroCards />} /> <LogoCloud1 title="Trusted by teams who deploy on a Friday" /> <Stats1 eyebrow="By the numbers" title="Fewer incidents, faster merges." description="Across 9,400 teams and 3.1 million deploys last quarter." link={{ label: "Read the report", href: link.changelog }} stats={[ { label: "Teams on Beacon", value: 9400, suffix: "+", delta: "+21%", trend: [3, 4, 5, 6, 7, 8, 8, 9, 9.4], trendLabel: "Teams, last 9 months", note: "Up from 7,800 in the spring." }, { label: "Deploys each quarter", value: 3.1, suffix: "M", decimals: 1, delta: "+32%", trend: [1.2, 1.5, 1.7, 2, 2.2, 2.4, 2.7, 2.9, 3.1], trendLabel: "Deploys, last 9 months", note: "Roughly one every three seconds." }, { label: "Median preview time", value: 41, suffix: " s", delta: "−12 s", trend: [70, 66, 60, 55, 52, 48, 45, 43, 41], trendLabel: "Preview time, last 9 months", note: "From push to a live URL." }, { label: "Rollback time", value: 6, suffix: " s", trend: [30, 24, 18, 12, 10, 8, 7, 6, 6], trendLabel: "Rollback time, last 9 months", note: "From alert to the last good build." }, ]} /> <Features5 eyebrow="How it works" title="Preview it. Check it. Undo it." description="Three things every release needs, built into the same pull request." steps={steps} /> <Testimonials2 eyebrow="What teams say" items={[ { quote: "We used to dread Fridays. Now a bad deploy rolls itself back before I finish reading the alert.", name: "Maya Chen", role: "VP Engineering", company: "Northwind", result: "−71% incident minutes" }, { quote: "Budgets on pull requests changed how my team thinks about performance. Nobody argues about it anymore, the check just says no.", name: "Tomás Reyes", role: "Staff Engineer", company: "Parcel", result: "Bundle −38%" }, { quote: "Design review used to mean screenshots in Slack. Now it is a link and a comment pinned to the button.", name: "Ingrid Solheim", role: "Design Director", company: "Fjord & Co", result: "Reviews 3× faster" }, ]} /> <Cta3 title="Your next deploy, with a safety net." description="Connect a repository and Beacon builds your first preview in about a minute." command="create-beacon@latest" storageKey="beacon-pm" primaryAction={{ label: "Start for free", href: link.pricing }} secondaryAction={{ label: "Read the docs", href: link.changelog }} notes={["Works with Next.js, Remix, Astro and plain static sites", "Free forever for personal projects"]} /> </main> </LaunchShell> ) } export { LaunchPage, type LaunchHomeProps }components/ballmac/templates/launch/launch-pricing.tsx// Ballmac UI: Launch pricing page. https://ui.ballmac.com/templates/template-launch "use client" import * as React from "react" import { Faq2 } from "@/components/ballmac/blocks/faq-2/faq-2" import { Pricing2 } from "@/components/ballmac/blocks/pricing-2/pricing-2" import { Pricing3 } from "@/components/ballmac/blocks/pricing-3/pricing-3" import { LaunchShell, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme" type LaunchPricingProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> } /** The Launch pricing page: Pricing2 (plans with a yearly switch), Pricing3 (full comparison) and Faq2. */ function LaunchPricing({ hrefs, ...props }: LaunchPricingProps) { const link = { contact: "/launch/contact", ...hrefs } return ( <LaunchShell page="pricing" hrefs={hrefs} {...props}> <main> <Pricing2 title="Pay for the team you have." description="Start free. Upgrade when your team grows. Every plan includes unlimited previews." plans={[ { name: "Hobby", description: "For side projects and learning.", price: { monthly: 0, yearly: 0 }, unit: "free forever", features: ["3 projects", "Unlimited previews", "Community support", "7 days of history"], cta: { label: "Start free", href: link.contact } }, { name: "Team", description: "For product teams who ship weekly.", price: { monthly: 24, yearly: 19 }, unit: "per seat", lead: "Everything in Hobby, plus:", features: ["Unlimited projects", "Performance budgets", "Automatic rollbacks", "90 days of history", "Slack alerts"], cta: { label: "Start a 14-day trial", href: link.contact }, featured: true }, { name: "Company", description: "For organisations with compliance needs.", price: "Custom", lead: "Everything in Team, plus:", features: ["SSO and SCIM", "Audit log export", "Private networking", "Dedicated support", "Custom retention"], cta: { label: "Talk to sales", href: link.contact } }, ]} /> <Pricing3 title="Compare every feature" description="The details, side by side." stickyOffset={64} plans={[ { key: "hobby", name: "Hobby", price: "$0", period: "free forever", cta: { label: "Start free", href: link.contact } }, { key: "team", name: "Team", price: "$24", period: "per seat / month", cta: { label: "Start trial", href: link.contact }, featured: true }, { key: "company", name: "Company", price: "Custom", period: "talk to us", cta: { label: "Talk to sales", href: link.contact } }, ]} groups={[ { title: "Deploys", rows: [ { label: "Preview deployments", values: { hobby: "Unlimited", team: "Unlimited", company: "Unlimited" } }, { label: "Concurrent builds", hint: "Builds that can run at the same time", values: { hobby: "1", team: "10", company: "50" } }, { label: "Build history", values: { hobby: "7 days", team: "90 days", company: "Custom" } }, ] }, { title: "Safety", rows: [ { label: "Performance budgets", values: { hobby: false, team: true, company: true } }, { label: "Automatic rollbacks", values: { hobby: false, team: true, company: true } }, { label: "Audit log export", values: { hobby: false, team: false, company: true } }, ] }, { title: "Support", rows: [ { label: "Slack and email alerts", values: { hobby: false, team: true, company: true } }, { label: "SSO and SCIM", values: { hobby: false, team: false, company: true } }, { label: "Support", values: { hobby: "Community", team: "Email, 1 day", company: "Dedicated, 1 hour" } }, ] }, ]} /> <Faq2 title="Pricing questions" description="Still unsure? These are the ones we hear most." support={{ label: "Contact support", href: link.contact, text: "Not here? A human replies within a working day." }} categories={[ { name: "Billing", questions: [ { question: "Can I change plans later?", answer: "Yes. Upgrade or downgrade at any time. We prorate the difference, so you only pay for what you use." }, { question: "How do seats work?", answer: "You pay for people who can deploy. Viewers who only comment on previews are always free." }, { question: "Do you offer discounts?", answer: "Open source projects and students get Team free. Annual billing saves about 20%." }, ] }, { name: "Product", questions: [ { question: "What counts as a preview?", answer: "Every branch or pull request build. There is no limit on how many you create." }, { question: "How do rollbacks decide?", answer: "You set thresholds for error rate and latency. If a release crosses one, Beacon restores the last good build." }, ] }, { name: "Security", questions: [ { question: "Where is my code stored?", answer: "Builds run in isolated, short-lived containers in the region you choose. Source is never kept after the build." }, ] }, ]} /> </main> </LaunchShell> ) } export { LaunchPricing, type LaunchPricingProps }components/ballmac/templates/launch/launch-changelog.tsx// Ballmac UI: Launch changelog page. https://ui.ballmac.com/templates/template-launch "use client" import * as React from "react" import { Changelog1 } from "@/components/ballmac/blocks/changelog-1/changelog-1" import { LaunchShell, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme" type LaunchChangelogProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> } /** The Launch changelog page: Changelog1 with Beacon's releases. */ function LaunchChangelog({ hrefs, ...props }: LaunchChangelogProps) { return ( <LaunchShell page="changelog" hrefs={hrefs} {...props}> <main> <Changelog1 title="What’s new in Beacon" description="Every release, newest first. We ship something most weeks." initialCount={3} releases={[ { version: "3.2.0", date: "2026-09-24", title: "Automatic rollbacks", summary: "Set an error or latency threshold and Beacon returns production to the last good build when a release crosses it.", cover: 0, changes: [{ type: "new", text: "Rollback rules per project, with Slack and email alerts" }, { type: "new", text: "A rollback timeline on every deployment" }, { type: "improved", text: "Rollbacks finish in a median of six seconds" }] }, { version: "3.1.2", date: "2026-09-09", title: "Budgets for accessibility", summary: "Fail a pull request when its accessibility score drops below your ceiling.", cover: 1, changes: [{ type: "new", text: "Accessibility score as a budget" }, { type: "improved", text: "Budget failures link to the offending element" }, { type: "fixed", text: "Budgets no longer reset when a project is renamed" }] }, { version: "3.1.0", date: "2026-08-20", title: "Comments pinned to the page", changes: [{ type: "new", text: "Pin a comment to any element on a preview" }, { type: "improved", text: "Reviewers outside your team can comment without an account" }] }, { version: "3.0.4", date: "2026-08-02", title: "Faster monorepo builds", changes: [{ type: "improved", text: "Builds skip packages that did not change" }, { type: "fixed", text: "Cached dependencies are no longer invalidated by lockfile whitespace" }] }, { version: "3.0.0", date: "2026-06-18", title: "Beacon 3", summary: "A new dashboard, regions and a rebuilt build pipeline.", cover: 2, changes: [{ type: "new", text: "Choose where builds run: Frankfurt, Virginia or Singapore" }, { type: "improved", text: "A new dashboard that loads in under a second" }, { type: "fixed", text: "Fifteen long-standing bugs in the Git integration" }] }, ]} /> </main> </LaunchShell> ) } export { LaunchChangelog, type LaunchChangelogProps }components/ballmac/templates/launch/launch-contact.tsx// Ballmac UI: Launch contact page. https://ui.ballmac.com/templates/template-launch "use client" import * as React from "react" import { Calendar, LifeBuoy, Mail } from "lucide-react" import { Contact1 } from "@/components/ballmac/blocks/contact-1/contact-1" import { LaunchShell, type LaunchHrefs } from "@/components/ballmac/templates/launch/launch-theme" type LaunchContactProps = React.ComponentProps<"div"> & { hrefs?: Partial<LaunchHrefs> } /** The Launch contact page: Contact1 with Beacon's channels and topics. */ function LaunchContact({ hrefs, ...props }: LaunchContactProps) { return ( <LaunchShell page="contact" hrefs={hrefs} {...props}> <main> <Contact1 title="Talk to a person." description="Questions about plans, security or migrating from another platform? Tell us what you need and the right person will reply." topics={["Sales and pricing", "Security review", "Migrating to Beacon", "Support", "Something else"]} responseTime="We reply within one working day" methods={[ { icon: <Mail />, label: "Email", value: "hello@beacon.example", href: "mailto:hello@beacon.example" }, { icon: <Calendar />, label: "Book a demo", value: "Thirty minutes with an engineer", href: "#" }, { icon: <LifeBuoy />, label: "Support", value: "Customers: open a ticket in the dashboard", href: "#" }, ]} /> </main> </LaunchShell> ) } export { LaunchContact, type LaunchContactProps }app/launch/page.tsx// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch import type { Metadata } from "next" import { LaunchPage } from "@/components/ballmac/templates/launch/launch-page" export const metadata: Metadata = { title: "Beacon: ship every branch with nothing to fear", description: "Preview URLs, performance budgets and one-click rollbacks for every pull request.", } export default function Page() { return <LaunchPage /> }app/launch/pricing/page.tsx// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch import type { Metadata } from "next" import { LaunchPricing } from "@/components/ballmac/templates/launch/launch-pricing" export const metadata: Metadata = { title: "Pricing · Beacon", description: "Start free. Upgrade when your team grows.", } export default function Page() { return <LaunchPricing /> }app/launch/changelog/page.tsx// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch import type { Metadata } from "next" import { LaunchChangelog } from "@/components/ballmac/templates/launch/launch-changelog" export const metadata: Metadata = { title: "Changelog · Beacon", description: "Every release, newest first.", } export default function Page() { return <LaunchChangelog /> }app/launch/contact/page.tsx// Ballmac UI: Launch template route. https://ui.ballmac.com/templates/template-launch import type { Metadata } from "next" import { LaunchContact } from "@/components/ballmac/templates/launch/launch-contact" export const metadata: Metadata = { title: "Contact · Beacon", description: "Talk to a person about plans, security or migrating.", } export default function Page() { return <LaunchContact /> }Update the import paths to match your project setup.
The page is added as a route in your app, with every block it uses.
What's inside
- Pages
- Home/launch
- Pricing/launch/pricing
- Changelog/launch/changelog
- Contact/launch/contact
- Fonts
- Schibsted Grotesk, Geist Mono (Google Fonts, loaded with next/font)
- Files
- 10 files · 13 Ballmac blocks and components
Built with
Dependencies
- npm
- lucide-react
Use with AI
Installs a four-page product site built entirely from Ballmac blocks in one palette. Change copy through block props, swap blocks freely, and edit launchCss for the look. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Beacon: launch site from blocks (@ballmac/template-launch) to this project with the shadcn MCP, then use it where it fits.
Use it for
- A new SaaS or developer-tool site you will customize section by section
- A starting point that shows how blocks compose
Not for
- Adding one section to an existing page (install that block instead)
Registry JSON: https://ui.ballmac.com/r/template-launch.json
Free to use in personal and commercial projects.