ui: Polish marketing website

This commit is contained in:
Dries Augustyns
2026-04-18 13:39:22 +02:00
parent 3bb3411994
commit c91d8c2e29
36 changed files with 5373 additions and 7508 deletions
+12 -17
View File
@@ -13,26 +13,21 @@ interface ComparisonTableProps {
rows: ComparisonRow[];
}
/**
* Reusable comparison table component for competitor pages
*/
export function ComparisonTable({competitorName, rows}: ComparisonTableProps) {
return (
<div className={'overflow-hidden rounded-xl border border-neutral-200'}>
{/* Header */}
<div className={'overflow-hidden rounded-[24px] border border-neutral-200'}>
<div className={'grid grid-cols-3 gap-px bg-neutral-200'}>
<div className={'bg-white p-6'}>
<span className={'text-sm font-semibold text-neutral-900'}>Feature</span>
<div className={'bg-neutral-50 p-6'}>
<span style={{fontFamily: 'var(--font-mono)'}} className={'text-[11px] uppercase tracking-[0.18em] text-neutral-500'}>Feature</span>
</div>
<div className={'bg-white p-6 text-center'}>
<span className={'text-sm font-semibold text-neutral-900'}>Plunk</span>
<div className={'bg-neutral-900 p-6 text-center'}>
<span style={{fontFamily: 'var(--font-mono)'}} className={'text-[11px] uppercase tracking-[0.18em] text-white'}>Plunk</span>
</div>
<div className={'bg-white p-6 text-center'}>
<span className={'text-sm font-semibold text-neutral-900'}>{competitorName}</span>
<div className={'bg-neutral-50 p-6 text-center'}>
<span style={{fontFamily: 'var(--font-mono)'}} className={'text-[11px] uppercase tracking-[0.18em] text-neutral-500'}>{competitorName}</span>
</div>
</div>
{/* Rows */}
<div className={'grid gap-px bg-neutral-200'}>
{rows.map((row, index) => (
<motion.div
@@ -40,7 +35,7 @@ export function ComparisonTable({competitorName, rows}: ComparisonTableProps) {
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: index * 0.05, ease: [0.22, 1, 0.36, 1]}}
transition={{duration: 0.4, delay: index * 0.04, ease: [0.22, 1, 0.36, 1]}}
className={'grid grid-cols-3 gap-px bg-neutral-200'}
>
<div className={'bg-white p-6'}>
@@ -52,10 +47,10 @@ export function ComparisonTable({competitorName, rows}: ComparisonTableProps) {
row.plunk ? (
<Check className="h-5 w-5 text-neutral-900" strokeWidth={2} />
) : (
<X className="h-5 w-5 text-neutral-400" strokeWidth={2} />
<X className="h-5 w-5 text-neutral-300" strokeWidth={2} />
)
) : (
<span className={'text-sm text-neutral-900'}>{row.plunk}</span>
<span className={'text-sm font-medium text-neutral-900'}>{row.plunk}</span>
)}
</div>
</div>
@@ -65,10 +60,10 @@ export function ComparisonTable({competitorName, rows}: ComparisonTableProps) {
row.competitor ? (
<Check className="h-5 w-5 text-neutral-900" strokeWidth={2} />
) : (
<X className="h-5 w-5 text-neutral-400" strokeWidth={2} />
<X className="h-5 w-5 text-neutral-300" strokeWidth={2} />
)
) : (
<span className={'text-sm text-neutral-900'}>{row.competitor}</span>
<span className={'text-sm text-neutral-600'}>{row.competitor}</span>
)}
</div>
</div>
+28 -21
View File
@@ -12,9 +12,6 @@ interface FAQSectionProps {
schemaId?: string;
}
/**
* Reusable FAQ section component with structured data support
*/
export function FAQSection({faqs, schemaId = 'faq-schema'}: FAQSectionProps) {
return (
<>
@@ -37,34 +34,44 @@ export function FAQSection({faqs, schemaId = 'faq-schema'}: FAQSectionProps) {
}}
/>
<section className={'py-32'}>
<motion.div
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.7, ease: [0.22, 1, 0.36, 1]}}
className={'mx-auto max-w-4xl'}
>
<h2 className={'mb-16 text-center text-5xl font-bold tracking-tight text-neutral-900'}>
Frequently asked questions
</h2>
<section className={'border-t border-neutral-200'}>
<div className={'mx-auto max-w-[88rem] px-6 py-20 sm:px-10'}>
<motion.div
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.7, ease: [0.22, 1, 0.36, 1]}}
className={'mb-16'}
>
<h2
style={{fontFamily: 'var(--font-display)'}}
className={'text-[clamp(2rem,5vw,4rem)] font-extrabold leading-[0.95] tracking-[-0.03em] text-neutral-900'}
>
Frequently asked questions
</h2>
</motion.div>
<div className={'space-y-8'}>
<div className={'mx-auto max-w-3xl divide-y divide-neutral-200'}>
{faqs.map((faq, index) => (
<motion.div
key={index}
initial={{opacity: 0, y: 20}}
initial={{opacity: 0, y: 16}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: index * 0.1, ease: [0.22, 1, 0.36, 1]}}
className={'border-b border-neutral-200 pb-8 last:border-b-0'}
transition={{duration: 0.5, delay: index * 0.06, ease: [0.22, 1, 0.36, 1]}}
className={'py-8'}
>
<h3 className={'text-xl font-semibold text-neutral-900'}>{faq.question}</h3>
<p className={'mt-4 leading-relaxed text-neutral-600'}>{faq.answer}</p>
<h3
style={{fontFamily: 'var(--font-display)'}}
className={'text-lg font-bold tracking-[-0.02em] text-neutral-900'}
>
{faq.question}
</h3>
<p className={'mt-3 leading-relaxed text-neutral-600'}>{faq.answer}</p>
</motion.div>
))}
</div>
</motion.div>
</div>
</section>
</>
);
@@ -10,7 +10,7 @@ export default function Footer() {
return (
<>
<footer className={'border-t border-neutral-200 bg-white'}>
<div className="mx-auto max-w-7xl px-8 py-20 xl:px-0">
<div className="mx-auto max-w-[88rem] px-6 py-20 sm:px-10">
<div className="grid gap-12 lg:grid-cols-12">
{/* Logo and description */}
<div className="space-y-6 lg:col-span-3">
@@ -59,7 +59,7 @@ export default function Footer() {
{/* Links */}
<div className="grid grid-cols-2 gap-8 lg:col-span-9 lg:grid-cols-5">
<div>
<h3 className="text-sm font-semibold text-neutral-900">Product</h3>
<h3 style={{fontFamily: 'var(--font-mono)'}} className="text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">Product</h3>
<ul role="list" className="mt-6 space-y-4">
<li>
<Link href={'/pricing'} className="text-sm text-neutral-600 transition hover:text-neutral-900">
@@ -90,7 +90,7 @@ export default function Footer() {
</div>
<div>
<h3 className="text-sm font-semibold text-neutral-900">Features</h3>
<h3 style={{fontFamily: 'var(--font-mono)'}} className="text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">Features</h3>
<ul role="list" className="mt-6 space-y-4">
<li>
<Link href={'/features/email-editor'} className="text-sm text-neutral-600 transition hover:text-neutral-900">
@@ -121,7 +121,7 @@ export default function Footer() {
</div>
<div>
<h3 className="text-sm font-semibold text-neutral-900">Compare</h3>
<h3 style={{fontFamily: 'var(--font-mono)'}} className="text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">Compare</h3>
<ul role="list" className="mt-6 space-y-4">
<li>
<Link href={'/vs'} className="text-sm text-neutral-600 transition hover:text-neutral-900">
@@ -162,7 +162,7 @@ export default function Footer() {
</div>
<div>
<h3 className="text-sm font-semibold text-neutral-900">Community</h3>
<h3 style={{fontFamily: 'var(--font-mono)'}} className="text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">Community</h3>
<ul role="list" className="mt-6 space-y-4">
<li>
<Link href={'/discord'} className="text-sm text-neutral-600 transition hover:text-neutral-900">
@@ -181,7 +181,7 @@ export default function Footer() {
</li>
</ul>
<h3 className="mt-8 text-sm font-semibold text-neutral-900">Legal</h3>
<h3 style={{fontFamily: 'var(--font-mono)'}} className="mt-8 text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">Legal</h3>
<ul role="list" className="mt-6 space-y-4">
<li>
<Link href={'/privacy'} className="text-sm text-neutral-600 transition hover:text-neutral-900">
@@ -202,7 +202,7 @@ export default function Footer() {
</div>
<div>
<h3 className="text-sm font-semibold text-neutral-900">Guides</h3>
<h3 style={{fontFamily: 'var(--font-mono)'}} className="text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">Guides</h3>
<ul role="list" className="mt-6 space-y-4">
<li>
<Link href={'/guides/email-deliverability'} className="text-sm text-neutral-600 transition hover:text-neutral-900">
@@ -47,8 +47,8 @@ export default function Navbar() {
const [featuresOpen, setFeaturesOpen] = useState(false);
return (
<header className={'sticky top-0 z-40 w-full border-b border-neutral-100 bg-white/95 backdrop-blur-sm'}>
<div className={'relative mx-auto max-w-7xl px-8 xl:px-0'}>
<header className={'sticky top-0 z-40 w-full border-b border-neutral-200 bg-white/95 backdrop-blur-sm'}>
<div className={'relative mx-auto max-w-[88rem] px-6 sm:px-10'}>
<div className={'py-5'}>
<div className="flex items-center justify-between">
<div className="flex items-center gap-12">
@@ -83,14 +83,14 @@ export default function Navbar() {
onMouseEnter={() => setFeaturesOpen(true)}
onMouseLeave={() => setFeaturesOpen(false)}
className={
'absolute left-0 top-full z-50 mt-2 w-80 rounded-lg border border-neutral-200 bg-white p-2 shadow-lg'
'absolute left-0 top-full z-50 mt-2 w-80 rounded-[16px] border border-neutral-200 bg-white p-2 shadow-lg'
}
>
{featuresMenu.map(feature => (
<Link
key={feature.href}
href={feature.href}
className={'flex items-start gap-3 rounded-lg p-3 transition hover:bg-neutral-50'}
className={'flex items-start gap-3 rounded-[10px] p-3 transition hover:bg-neutral-50'}
>
<div
className={
@@ -166,7 +166,7 @@ export default function Navbar() {
whileTap={{scale: 0.98}}
href={`${DASHBOARD_URI}/auth/signup`}
className={
'rounded-lg bg-neutral-900 px-6 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-neutral-800'
'rounded-full bg-neutral-900 px-6 py-2.5 text-sm font-semibold text-white transition hover:bg-neutral-800'
}
>
Get started
@@ -226,7 +226,7 @@ export default function Navbar() {
className="space-y-1 p-4"
>
<div className="mb-2">
<div className="px-4 py-2 text-xs font-semibold uppercase tracking-wider text-neutral-500">
<div style={{fontFamily: 'var(--font-mono)'}} className="px-4 py-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-neutral-500">
Features
</div>
{featuresMenu.map(feature => (
@@ -293,7 +293,7 @@ export default function Navbar() {
</a>
<a
href={`${DASHBOARD_URI}/auth/signup`}
className="mt-2 block rounded-lg bg-neutral-900 px-4 py-3 text-center text-sm font-semibold text-white transition hover:bg-neutral-800"
className="mt-2 block rounded-full bg-neutral-900 px-4 py-3 text-center text-sm font-semibold text-white transition hover:bg-neutral-800"
>
Get started
</a>
@@ -0,0 +1,48 @@
import {motion} from 'framer-motion';
import React from 'react';
export function SectionHeader({
number,
label,
title,
titleAccent,
subtitle,
}: {
number: string;
label: string;
title: string;
titleAccent?: string;
subtitle?: string;
}) {
return (
<motion.div
initial={{opacity: 0, y: 16}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true, margin: '-10%'}}
transition={{duration: 0.7, ease: [0.22, 1, 0.36, 1]}}
className={'grid gap-8 lg:grid-cols-12 lg:gap-16'}
>
<div
style={{fontFamily: 'var(--font-mono)'}}
className={
'flex items-center gap-4 border-t border-neutral-900 pt-4 text-[11px] uppercase tracking-[0.2em] text-neutral-700 lg:col-span-3 lg:self-start'
}
>
<span className={'font-medium text-neutral-900'}>§ {number}</span>
<span className={'text-neutral-500'}>{label}</span>
</div>
<div className={'lg:col-span-9'}>
<h2
style={{fontFamily: 'var(--font-display)'}}
className={
'text-[clamp(2.25rem,5.5vw,4.5rem)] font-extrabold leading-[0.95] tracking-[-0.03em] text-neutral-900'
}
>
{title}
{titleAccent && <> {titleAccent}</>}
</h2>
{subtitle && <p className={'mt-6 max-w-2xl text-lg leading-relaxed text-neutral-600'}>{subtitle}</p>}
</div>
</motion.div>
);
}
@@ -114,7 +114,7 @@ export function GuideLayout({
<Navbar />
<main className={'mx-auto max-w-7xl px-4 sm:px-8 w-full overflow-x-hidden'}>
<main className={'mx-auto max-w-[88rem] px-4 sm:px-8 w-full overflow-x-hidden'}>
<div className={'flex flex-col lg:flex-row gap-8 lg:gap-12 py-8 sm:py-16 w-full'}>
{/* Main Content */}
<article className={'flex-1 max-w-full lg:max-w-4xl w-full'}>
+1
View File
@@ -3,3 +3,4 @@ export * from './Footer';
export * from './ComparisonTable';
export * from './FAQSection';
export * from './CodeBlock';
export * from './SectionHeader';