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
1 change: 1 addition & 0 deletions server/chatPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Rules:
- Preserve the exact JSON shape and keys: brand, navLinks, hero, features, testimonials, callToAction, footer.
- Return valid JSON that matches the schema (same structure as the current content).
- Keep nav link hrefs as hash anchors (#features, #testimonials, #cta) unless the user asks to change them.
- Nav labels should match those sections (e.g. Features, Testimonials, Get started) — not duplicate names for different targets.

Response format:
1. Write 1–3 short sentences explaining what you changed.
Expand Down
5 changes: 3 additions & 2 deletions src/editor/PagePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ export function PagePreview({ content, isProposal }: PagePreviewProps) {
)}

<div
id="page-preview-scroll"
tabIndex={-1}
className={
isProposal
? 'flex-1 overflow-y-auto pt-12 ring-2 ring-inset ring-amber-500/40 focus:outline-none'
: 'flex-1 overflow-y-auto focus:outline-none'
? '@container flex-1 overflow-y-auto pt-12 ring-2 ring-inset ring-amber-500/40 focus:outline-none'
: '@container flex-1 overflow-y-auto focus:outline-none'
}
>
<LandingPage content={content} />
Expand Down
70 changes: 55 additions & 15 deletions src/landing/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
import type { MouseEvent } from 'react'
import type { LandingPageContent } from './types'

type LandingPageProps = {
content: LandingPageContent
}

const PREVIEW_HEADER_OFFSET = 64

function scrollToSection(id: string) {
const target = document.getElementById(id)
const scroller = document.getElementById('page-preview-scroll')
if (!target) return

if (!scroller) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
return
}

const top =
target.getBoundingClientRect().top -
scroller.getBoundingClientRect().top +
scroller.scrollTop -
PREVIEW_HEADER_OFFSET

scroller.scrollTo({ top, behavior: 'smooth' })
}

function handleNavClick(event: MouseEvent<HTMLAnchorElement>, href: string) {
if (!href.startsWith('#')) return
event.preventDefault()
scrollToSection(href.slice(1))
}

const featureIcons = [
(
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5} aria-hidden>
Expand All @@ -26,37 +54,49 @@ export function LandingPage({ content }: LandingPageProps) {
const { brand, navLinks, hero, features, testimonials, callToAction, footer } = content

return (
<div className="min-h-full">
<div className="pointer-events-none fixed inset-0 overflow-hidden" aria-hidden>
<div className="relative min-h-full">
<div className="pointer-events-none absolute inset-0 overflow-hidden" aria-hidden>
<div className="absolute -top-40 left-1/2 h-[32rem] w-[32rem] -translate-x-1/2 rounded-full bg-violet-600/20 blur-3xl" />
<div className="absolute top-1/3 right-0 h-96 w-96 rounded-full bg-cyan-500/10 blur-3xl" />
</div>

<header className="relative z-10 border-b border-white/5">
<nav className="container-page flex items-center justify-between py-5">
<a href="#" className="flex items-center gap-2 text-lg font-semibold tracking-tight">
<header className="sticky top-0 z-20 border-b border-white/5 bg-slate-950/95 backdrop-blur-md">
<nav className="container-page flex items-center justify-between gap-4 py-4">
<a
href="#hero"
onClick={event => handleNavClick(event, '#hero')}
className="flex shrink-0 items-center gap-2 text-lg font-semibold tracking-tight"
>
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-cyan-400 text-sm font-bold text-white">
N
</span>
{brand}
</a>
<ul className="hidden items-center gap-8 text-sm text-slate-400 md:flex">
<ul className="hidden items-center gap-8 text-sm text-slate-400 @3xl:flex">
{navLinks.map((navLink) => (
<li key={navLink.href}>
<a href={navLink.href} className="transition hover:text-white">
<a
href={navLink.href}
onClick={event => handleNavClick(event, navLink.href)}
className="transition hover:text-white"
>
{navLink.label}
</a>
</li>
))}
</ul>
<button type="button" className="btn-header">
Sign in
<button
type="button"
className="btn-header max-w-40 shrink-0 truncate sm:max-w-none"
onClick={() => scrollToSection('cta')}
>
{hero.primaryCta}
</button>
</nav>
</header>

<main className="relative z-10">
<section className="container-page pb-24 pt-20 text-center md:pt-28">
<main className="relative z-0">
<section id="hero" className="scroll-mt-16 container-page pb-24 pt-20 text-center md:pt-28">
<span className="inline-flex items-center gap-2 rounded-full border border-violet-500/30 bg-violet-500/10 px-4 py-1.5 text-xs font-medium text-violet-300">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />
{hero.badge}
Expand Down Expand Up @@ -97,7 +137,7 @@ export function LandingPage({ content }: LandingPageProps) {
</div>
</section>

<section id="features" className="border-t border-white/5 bg-slate-900/30 py-24">
<section id="features" className="scroll-mt-16 border-t border-white/5 bg-slate-900/30 py-24">
<div className="container-page">
<div className="max-w-2xl">
<h2 className="heading-section">
Expand All @@ -122,7 +162,7 @@ export function LandingPage({ content }: LandingPageProps) {
</div>
</section>

<section id="testimonials" className="py-24">
<section id="testimonials" className="scroll-mt-16 py-24">
<div className="container-page">
<h2 className="heading-section text-center">
{testimonials.heading}
Expand All @@ -146,7 +186,7 @@ export function LandingPage({ content }: LandingPageProps) {
</div>
</section>

<section id="cta" className="border-t border-white/5 py-24">
<section id="cta" className="scroll-mt-16 border-t border-white/5 py-24">
<div className="container-page-narrow text-center">
<h2 className="heading-section">
{callToAction.title}
Expand All @@ -159,7 +199,7 @@ export function LandingPage({ content }: LandingPageProps) {
</section>
</main>

<footer className="relative z-10 border-t border-white/5 py-12">
<footer className="relative z-0 border-t border-white/5 py-12">
<div className="container-page flex flex-col items-center justify-between gap-4 text-center text-sm text-slate-500 md:flex-row md:text-left">
<p>{footer.tagline}</p>
<p>{footer.copyright}</p>
Expand Down
4 changes: 2 additions & 2 deletions src/landing/defaultContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export const defaultLandingContent: LandingPageContent = {
brand: 'Nimbus',
navLinks: [
{ label: 'Features', href: '#features' },
{ label: 'Stories', href: '#testimonials' },
{ label: 'Pricing', href: '#cta' },
{ label: 'Testimonials', href: '#testimonials' },
{ label: 'Get started', href: '#cta' },
],
hero: {
badge: 'Now in public beta',
Expand Down
Loading