feat: landing page redesign - Phase 1 & 2
- Hero: gradient text, enhanced CTAs, improved terminal styling - Features: color-coded cards (blue/purple/cyan) with hover effects - Why: card grid layout with color-coded left borders - CTA: stronger visual hierarchy with gradient text - Accessibility: skip-to-content link, main landmark - Remove duplicate main tag from page.tsx Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
1b640d4b12
commit
79e74b4810
@ -50,8 +50,13 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
|
<html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
|
||||||
<body className="min-h-screen bg-zinc-950 text-white font-sans antialiased">
|
<body className="min-h-screen bg-zinc-950 text-white font-sans antialiased">
|
||||||
|
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-blue-500 focus:text-white focus:rounded-lg">
|
||||||
|
Skip to content
|
||||||
|
</a>
|
||||||
<Header />
|
<Header />
|
||||||
|
<main id="main-content" role="main">
|
||||||
{children}
|
{children}
|
||||||
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import CTA from '@/components/CTA'
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<main className="relative z-10">
|
<>
|
||||||
<Hero />
|
<Hero />
|
||||||
<Features />
|
<Features />
|
||||||
<Why />
|
<Why />
|
||||||
<CTA />
|
<CTA />
|
||||||
</main>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
export default function CTA() {
|
export default function CTA() {
|
||||||
return (
|
return (
|
||||||
<section className="py-20 px-6">
|
<section className="py-24 px-6 bg-gradient-to-b from-zinc-900/30 to-zinc-900/60">
|
||||||
<div className="max-w-4xl mx-auto text-center">
|
<div className="max-w-4xl mx-auto text-center">
|
||||||
<h2 className="text-3xl md:text-4xl font-bold mb-4">
|
<h2 className="text-4xl md:text-5xl font-extrabold mb-4 bg-gradient-to-r from-white to-blue-400 bg-clip-text text-transparent">
|
||||||
Start inspecting in 30 seconds
|
Start inspecting in 30 seconds
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xl text-zinc-400 mb-8">
|
<p className="text-xl text-zinc-400 mb-10">
|
||||||
One binary. Your data stays yours.
|
One binary. Your data stays yours.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/elfoundation/hatch"
|
href="https://github.com/elfoundation/hatch"
|
||||||
className="inline-flex items-center justify-center gap-2 px-8 py-4 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition-colors text-lg"
|
className="inline-flex items-center justify-center gap-2 px-10 py-5 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg transition-all shadow-lg shadow-blue-500/30 hover:shadow-xl hover:shadow-blue-500/40 hover:-translate-y-0.5 text-lg"
|
||||||
>
|
>
|
||||||
Get Hatch →
|
Get Hatch →
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ const features = [
|
|||||||
),
|
),
|
||||||
title: 'Capture',
|
title: 'Capture',
|
||||||
description: 'Method, path, headers, query, body. Persists across restarts because the storage is SQLite on disk, not a hosted queue.',
|
description: 'Method, path, headers, query, body. Persists across restarts because the storage is SQLite on disk, not a hosted queue.',
|
||||||
|
color: 'blue',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
@ -18,6 +19,7 @@ const features = [
|
|||||||
),
|
),
|
||||||
title: 'Inspect',
|
title: 'Inspect',
|
||||||
description: 'A live SSE feed of incoming requests. Click any captured request to see the headers, the body, the timing.',
|
description: 'A live SSE feed of incoming requests. Click any captured request to see the headers, the body, the timing.',
|
||||||
|
color: 'purple',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
@ -28,9 +30,25 @@ const features = [
|
|||||||
),
|
),
|
||||||
title: 'Mock',
|
title: 'Mock',
|
||||||
description: 'Return a 200, a 500, or a custom JSON payload. For testing your own retry, backoff, and error-handling logic.',
|
description: 'Return a 200, a 500, or a custom JSON payload. For testing your own retry, backoff, and error-handling logic.',
|
||||||
|
color: 'cyan',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const colorClasses = {
|
||||||
|
blue: {
|
||||||
|
icon: 'bg-blue-500/15 text-blue-400',
|
||||||
|
hover: 'hover:border-blue-500/40 hover:shadow-blue-500/10',
|
||||||
|
},
|
||||||
|
purple: {
|
||||||
|
icon: 'bg-purple-500/15 text-purple-400',
|
||||||
|
hover: 'hover:border-purple-500/40 hover:shadow-purple-500/10',
|
||||||
|
},
|
||||||
|
cyan: {
|
||||||
|
icon: 'bg-cyan-500/15 text-cyan-400',
|
||||||
|
hover: 'hover:border-cyan-500/40 hover:shadow-cyan-500/10',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default function Features() {
|
export default function Features() {
|
||||||
return (
|
return (
|
||||||
<section className="py-20 px-6">
|
<section className="py-20 px-6">
|
||||||
@ -40,12 +58,14 @@ export default function Features() {
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-3 gap-8">
|
<div className="grid md:grid-cols-3 gap-8">
|
||||||
{features.map((feature, index) => (
|
{features.map((feature) => {
|
||||||
|
const colors = colorClasses[feature.color as keyof typeof colorClasses]
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
key={feature.title}
|
key={feature.title}
|
||||||
className="p-6 rounded-xl bg-zinc-900/50 border border-zinc-800 hover:border-zinc-700 transition-colors"
|
className={`p-6 rounded-xl bg-gradient-to-b from-zinc-900/80 to-zinc-900/40 border border-zinc-800 ${colors.hover} transition-all hover:-translate-y-1 shadow-lg hover:shadow-xl`}
|
||||||
>
|
>
|
||||||
<div className="w-12 h-12 flex items-center justify-center rounded-lg bg-blue-500/10 text-blue-400 mb-4">
|
<div className={`w-14 h-14 flex items-center justify-center rounded-xl ${colors.icon} mb-4 shadow-lg`}>
|
||||||
{feature.icon}
|
{feature.icon}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-semibold mb-2">{feature.title}</h3>
|
<h3 className="text-xl font-semibold mb-2">{feature.title}</h3>
|
||||||
@ -53,7 +73,8 @@ export default function Features() {
|
|||||||
{feature.description}
|
{feature.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -2,16 +2,16 @@ export default function Hero() {
|
|||||||
return (
|
return (
|
||||||
<section className="pt-32 pb-20 px-6">
|
<section className="pt-32 pb-20 px-6">
|
||||||
<div className="max-w-4xl mx-auto text-center">
|
<div className="max-w-4xl mx-auto text-center">
|
||||||
<div className="inline-block mb-6 px-4 py-2 rounded-full bg-blue-500/10 border border-blue-500/20 text-blue-400 text-sm font-medium">
|
<div className="inline-block mb-6 px-5 py-2 rounded-full bg-blue-500/10 border border-blue-500/30 text-blue-400 text-sm font-medium">
|
||||||
Open source · MIT licensed
|
Open source · MIT licensed
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="text-5xl md:text-6xl font-bold tracking-tight mb-6 leading-tight">
|
<h1 className="text-4xl md:text-5xl lg:text-6xl font-extrabold tracking-tight mb-6 leading-tight bg-gradient-to-r from-white to-zinc-400 bg-clip-text text-transparent">
|
||||||
Self-hostable HTTP request<br />
|
Self-hostable HTTP request<br />
|
||||||
inspector + mocker
|
inspector + mocker
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="text-xl text-zinc-400 mb-8 max-w-2xl mx-auto">
|
<p className="text-lg md:text-xl text-zinc-400 mb-10 max-w-2xl mx-auto leading-relaxed">
|
||||||
One Go binary. SQLite under the hood.
|
One Go binary. SQLite under the hood.
|
||||||
<code className="mx-1 px-2 py-0.5 bg-zinc-800 border border-zinc-700 rounded text-fuchsia-400 text-base">
|
<code className="mx-1 px-2 py-0.5 bg-zinc-800 border border-zinc-700 rounded text-fuchsia-400 text-base">
|
||||||
docker compose up
|
docker compose up
|
||||||
@ -19,10 +19,10 @@ export default function Hero() {
|
|||||||
, and you have an inspection endpoint and a live feed in under 30 seconds.
|
, and you have an inspection endpoint and a live feed in under 30 seconds.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-12">
|
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-16">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/elfoundation/hatch"
|
href="https://github.com/elfoundation/hatch"
|
||||||
className="inline-flex items-center justify-center gap-2 px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition-colors"
|
className="inline-flex items-center justify-center gap-2 px-8 py-4 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg transition-all shadow-lg shadow-blue-500/30 hover:shadow-xl hover:shadow-blue-500/40 hover:-translate-y-0.5"
|
||||||
>
|
>
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
|
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
|
||||||
@ -31,28 +31,28 @@ export default function Hero() {
|
|||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/elfoundation/hatch#quick-start"
|
href="https://github.com/elfoundation/hatch#quick-start"
|
||||||
className="inline-flex items-center justify-center gap-2 px-6 py-3 bg-zinc-800 hover:bg-zinc-700 text-white font-medium rounded-lg border border-zinc-700 transition-colors"
|
className="inline-flex items-center justify-center gap-2 px-8 py-4 bg-zinc-800 hover:bg-zinc-700 text-white font-semibold rounded-lg border border-zinc-700 transition-all shadow-lg hover:shadow-xl hover:-translate-y-0.5"
|
||||||
>
|
>
|
||||||
Quick Start →
|
Quick Start →
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Terminal */}
|
{/* Terminal */}
|
||||||
<div className="max-w-2xl mx-auto rounded-xl overflow-hidden border border-zinc-800 bg-zinc-900/50 backdrop-blur-sm">
|
<div className="max-w-2xl mx-auto rounded-xl overflow-hidden border border-zinc-800 bg-zinc-900/50 backdrop-blur-sm shadow-2xl">
|
||||||
<div className="flex items-center gap-2 px-4 py-3 bg-zinc-800/50 border-b border-zinc-800">
|
<div className="flex items-center gap-2 px-4 py-3 bg-gradient-to-r from-zinc-800/50 to-zinc-800/30 border-b border-zinc-800">
|
||||||
<div className="w-3 h-3 rounded-full bg-zinc-600"></div>
|
<div className="w-3 h-3 rounded-full bg-zinc-600"></div>
|
||||||
<div className="w-3 h-3 rounded-full bg-zinc-600"></div>
|
<div className="w-3 h-3 rounded-full bg-zinc-600"></div>
|
||||||
<div className="w-3 h-3 rounded-full bg-zinc-600"></div>
|
<div className="w-3 h-3 rounded-full bg-zinc-600"></div>
|
||||||
<span className="ml-2 text-sm text-zinc-500">terminal</span>
|
<span className="ml-2 text-sm text-zinc-500">terminal</span>
|
||||||
</div>
|
</div>
|
||||||
<pre className="p-4 text-left text-sm font-mono overflow-x-auto">
|
<pre className="p-5 text-left text-sm font-mono overflow-x-auto">
|
||||||
<code>
|
<code>
|
||||||
<span className="text-green-400">$</span>{' '}
|
<span className="text-blue-400 font-semibold">$</span>{' '}
|
||||||
<span className="text-zinc-300">docker compose up -d</span>{'\n'}
|
<span className="text-zinc-300">docker compose up -d</span>{'\n'}
|
||||||
<span className="text-zinc-500">✓ hatch started on :8080</span>{'\n'}
|
<span className="text-emerald-400">✓ hatch started on :8080</span>{'\n'}
|
||||||
<span className="text-green-400">$</span>{' '}
|
<span className="text-blue-400 font-semibold">$</span>{' '}
|
||||||
<span className="text-zinc-300">curl -X POST https://your-bin.hatch.surf/test -d {'{\'hello\':\'world\'}'}</span>{'\n'}
|
<span className="text-zinc-300">curl -X POST https://your-bin.hatch.surf/test -d {'{\'hello\':\'world\'}'}</span>{'\n'}
|
||||||
<span className="text-zinc-500">✓ captured — view at https://your-bin.hatch.surf/inspect</span>
|
<span className="text-emerald-400">✓ captured — view at https://your-bin.hatch.surf/inspect</span>
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,11 +3,13 @@ const reasons = [
|
|||||||
number: '01',
|
number: '01',
|
||||||
title: 'Compliance and privacy',
|
title: 'Compliance and privacy',
|
||||||
description: 'Some teams cannot legally send webhook payloads to a hosted SaaS. Hatch keeps the data on your own network.',
|
description: 'Some teams cannot legally send webhook payloads to a hosted SaaS. Hatch keeps the data on your own network.',
|
||||||
|
color: 'blue',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
number: '02',
|
number: '02',
|
||||||
title: 'Cost',
|
title: 'Cost',
|
||||||
description: 'A hosted inspector charges per request, per seat, or per retention day. Hatch is one Go binary on a $5 VPS. There is no per-request fee because there is no one to charge it.',
|
description: 'A hosted inspector charges per request, per seat, or per retention day. Hatch is one Go binary on a $5 VPS. There is no per-request fee because there is no one to charge it.',
|
||||||
|
color: 'purple',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
number: '03',
|
number: '03',
|
||||||
@ -20,33 +22,38 @@ const reasons = [
|
|||||||
is faster than signing up for a SaaS, verifying your email, configuring your first bin, and pasting the URL into your webhook config.
|
is faster than signing up for a SaaS, verifying your email, configuring your first bin, and pasting the URL into your webhook config.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
color: 'cyan',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const colorClasses = {
|
||||||
|
blue: 'border-l-blue-500',
|
||||||
|
purple: 'border-l-purple-500',
|
||||||
|
cyan: 'border-l-cyan-500',
|
||||||
|
}
|
||||||
|
|
||||||
export default function Why() {
|
export default function Why() {
|
||||||
return (
|
return (
|
||||||
<section className="py-20 px-6 bg-zinc-900/30">
|
<section className="py-20 px-6">
|
||||||
<div className="max-w-4xl mx-auto">
|
<div className="max-w-4xl mx-auto">
|
||||||
<h2 className="text-3xl md:text-4xl font-bold text-center mb-16">
|
<h2 className="text-3xl md:text-4xl font-bold text-center mb-16">
|
||||||
Why a single binary, not a SaaS
|
Why a single binary, not a SaaS
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="space-y-8">
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
{reasons.map((reason) => (
|
{reasons.map((reason) => (
|
||||||
<div
|
<div
|
||||||
key={reason.number}
|
key={reason.number}
|
||||||
className="flex gap-6 items-start"
|
className={`p-6 rounded-xl bg-gradient-to-b from-zinc-900/80 to-zinc-900/40 border border-zinc-800 border-l-4 ${colorClasses[reason.color as keyof typeof colorClasses]} hover:-translate-y-1 transition-all shadow-lg hover:shadow-xl`}
|
||||||
>
|
>
|
||||||
<div className="text-4xl font-bold text-zinc-700 font-mono shrink-0">
|
<div className="text-4xl font-bold text-zinc-600 font-mono mb-4">
|
||||||
{reason.number}
|
{reason.number}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<h3 className="text-xl font-semibold mb-2">{reason.title}</h3>
|
<h3 className="text-xl font-semibold mb-2">{reason.title}</h3>
|
||||||
<p className="text-zinc-400 leading-relaxed">
|
<p className="text-zinc-400 leading-relaxed">
|
||||||
{reason.description}
|
{reason.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user