From 26a203429e5c53fc7bdfba7ffec904ff21ba230b Mon Sep 17 00:00:00 2001 From: Nicholas Kissel Date: Fri, 12 Jun 2026 01:10:22 -0700 Subject: [PATCH 1/2] [SLOP(claude-fable-5)] feat(website): premium compare page redesign --- .../marketing/compare/ComparePage.tsx | 284 ++++++++++-------- .../marketing/compare/ComparisonTable.tsx | 50 ++- .../marketing/compare/FeatureStatus.tsx | 53 +--- .../sections/ObservabilitySection.tsx | 57 +++- website/src/pages/compare/index.astro | 17 +- 5 files changed, 265 insertions(+), 196 deletions(-) diff --git a/website/src/components/marketing/compare/ComparePage.tsx b/website/src/components/marketing/compare/ComparePage.tsx index 60f39bd3b7..3e3175f302 100644 --- a/website/src/components/marketing/compare/ComparePage.tsx +++ b/website/src/components/marketing/compare/ComparePage.tsx @@ -1,4 +1,5 @@ import { Icon, faArrowRight, faRivet, faServer } from '@rivet-gg/icons'; +import type { ReactNode } from 'react'; import { FaqList } from '@/components/faq/FaqSection'; import { formatTimestamp } from '@/lib/formatDate'; import { compareEntries, getCompareEntry } from '@/data/compare'; @@ -12,22 +13,34 @@ interface ComparePageProps { slug: string; } +function SectionHeading({ + title, + subtitle, + center = false, +}: { + title: string; + subtitle?: string; + center?: boolean; +}) { + return ( +
+

{title}

+ {subtitle &&

{subtitle}

} +
+ ); +} + function HeroSection({ entry }: { entry: CompareEntry }) { return ( -
+
-

+

{entry.rivetProductName} vs
{entry.competitorName}

-

- {entry.heroSubtitle} -

-

- Last updated {formatTimestamp(entry.lastUpdated)} -

-
+

{entry.heroSubtitle}

+ +

+ Last updated {formatTimestamp(entry.lastUpdated)} +

); } -function OverviewSection({ entry }: { entry: CompareEntry }) { +function ChoiceList({ heading, choices }: { heading: string; choices: CompareEntry['whenToChooseRivet'] }) { return ( -
-
-
-

- Overview -

-

- Compare the two approaches and decide which is right for your project. -

-
+
+
{heading}
+
+ {choices.map((choice) => ( +
+
{choice.title}
+
{choice.description}
+
+ ))} +
+
+ ); +} -
-
-
- -
-

{entry.rivetProductName}

-

{entry.rivetSummary}

+function OverviewPanel({ + icon, + name, + summary, + children, + highlight = false, +}: { + icon: ReactNode; + name: string; + summary: string; + children: ReactNode; + highlight?: boolean; +}) { + return ( +
+
+ {icon} +

{name}

+
+

{summary}

+
+ {children} +
+ ); +} -

- When to choose {entry.rivetProductName} -

-
- {entry.whenToChooseRivet.map((choice) => ( -
-
{choice.title}
-
{choice.description}
-
- ))} -
-
+function OverviewSection({ entry }: { entry: CompareEntry }) { + return ( +
+
+ +
+ } + name={entry.rivetProductName} + summary={entry.rivetSummary} + highlight + > + + -
- -
-
- -
-

{entry.competitorName}

-

{entry.competitorSummary}

+ -

- When to choose {entry.competitorName} -

-
- {entry.whenToChooseCompetitor.map((choice) => ( -
-
{choice.title}
-
{choice.description}
-
- ))} -
-
+ } + name={entry.competitorName} + summary={entry.competitorSummary} + > + +
@@ -112,22 +161,20 @@ function OverviewSection({ entry }: { entry: CompareEntry }) { function ComparisonSection({ entry }: { entry: CompareEntry }) { return ( -
-
-
-

- Feature Comparison -

-

- A detailed breakdown of capabilities across both platforms. -

-
- +
+ +
+ +
); @@ -135,19 +182,23 @@ function ComparisonSection({ entry }: { entry: CompareEntry }) { function VerdictSection({ entry }: { entry: CompareEntry }) { return ( -
-
-

- Verdict -

- {entry.verdict.map((paragraph) => ( -

- {paragraph} -

- ))} +
+
+ +
+ {entry.verdict.map((paragraph, index) => ( +

+ {paragraph} +

+ ))} +
); @@ -155,13 +206,11 @@ function VerdictSection({ entry }: { entry: CompareEntry }) { function MigrationSection({ migration }: { migration: NonNullable }) { return ( -
-
-

- {migration.heading} -

-

{migration.body}

-
+
+
+ +

{migration.body}

+
-
-

- Frequently asked questions -

- +
+
+ +
+ +
); @@ -194,23 +243,24 @@ function OtherComparisonsSection({ entry }: { entry: CompareEntry }) { } return ( -
-
-

- Other comparisons -

-
+
+
+ +
{others.map((other) => ( -

{other.title}

-

{other.description}

- +

{other.title}

+

{other.description}

+ Read the comparison - +
))} @@ -222,15 +272,15 @@ function OtherComparisonsSection({ entry }: { entry: CompareEntry }) { function CTASection() { return ( -
+
-

+

The primitive for stateful workloads.

-

+

The next generation of software needs a new kind of backend. This is it.

-
+
- +
+
- - - - @@ -45,27 +45,25 @@ export function ComparisonTable({ {featureGroups.map((group) => ( - - + {group.rows.map((row) => ( - - - + + - + - ))} diff --git a/website/src/components/marketing/compare/FeatureStatus.tsx b/website/src/components/marketing/compare/FeatureStatus.tsx index 0f0b2b72f6..e184bde861 100644 --- a/website/src/components/marketing/compare/FeatureStatus.tsx +++ b/website/src/components/marketing/compare/FeatureStatus.tsx @@ -1,52 +1,27 @@ -import { - Icon, - faCheck, - faHourglass, - faMinus, - faXmark, -} from '@rivet-gg/icons'; import type { ReactNode } from 'react'; import type { ComparisonStatus } from '@/data/compare/types'; +const STATUS_STYLES: Record = { + yes: { dot: 'bg-emerald-400/90', label: 'Yes' }, + partial: { dot: 'bg-amber-400/80', label: 'Partial' }, + 'coming-soon': { dot: 'bg-violet-400/80', label: 'Coming soon' }, + no: { dot: 'bg-zinc-700', label: 'No' }, +}; + interface FeatureStatusProps { status: ComparisonStatus; text: ReactNode; } export function FeatureStatus({ status, text }: FeatureStatusProps) { - let icon, bgColor, textColor; - - switch (status) { - case 'yes': - icon = faCheck; - bgColor = 'bg-green-500/20'; - textColor = 'text-green-500'; - break; - case 'no': - icon = faXmark; - bgColor = 'bg-red-500/20'; - textColor = 'text-red-500'; - break; - case 'partial': - icon = faMinus; - bgColor = 'bg-amber-500/20'; - textColor = 'text-amber-500'; - break; - case 'coming-soon': - icon = faHourglass; - bgColor = 'bg-purple-500/20'; - textColor = 'text-purple-500'; - break; - } - + const { dot, label } = STATUS_STYLES[status]; return ( -
-
- -
-
{text}
+
+ +
{text}
); } diff --git a/website/src/components/marketing/sections/ObservabilitySection.tsx b/website/src/components/marketing/sections/ObservabilitySection.tsx index ccff156ed9..3fdb26db69 100644 --- a/website/src/components/marketing/sections/ObservabilitySection.tsx +++ b/website/src/components/marketing/sections/ObservabilitySection.tsx @@ -1,9 +1,28 @@ 'use client'; -import { Database, GitBranch, Activity, Terminal, ArrowRight } from 'lucide-react'; +import { Database, GitBranch, Activity, Terminal, ArrowRight, Sun, Moon } from 'lucide-react'; import { motion } from 'framer-motion'; -const imgInspector = { src: "https://assets.rivet.dev/repo/website/src/components/marketing/images/screenshots/inspector-6.png", width: 5984, height: 3224, format: "png" }; +import { useState } from 'react'; + +const inspectorImages = { + dark: { + src: 'https://assets.rivet.dev/repo/website/src/components/marketing/images/screenshots/rivet-actor-inspector-dark.png', + width: 2446, + height: 1658, + }, + light: { + src: 'https://assets.rivet.dev/repo/website/src/components/marketing/images/screenshots/rivet-actor-inspector-light.png', + width: 2446, + height: 1658, + }, +} as const; + +type InspectorTheme = keyof typeof inspectorImages; + export const ObservabilitySection = () => { + const [theme, setTheme] = useState('dark'); + const inspector = inspectorImages[theme]; + const features = [ { title: 'SQLite Viewer', @@ -40,13 +59,39 @@ export const ObservabilitySection = () => { transition={{ duration: 0.5 }} className='relative' > -
-
- Rivet Inspector Dashboard +
+ {`Rivet
+ + {/* Light/dark mode toggle */} +
+ + +
diff --git a/website/src/pages/compare/index.astro b/website/src/pages/compare/index.astro index aba5b3d82a..5ca7e2b803 100644 --- a/website/src/pages/compare/index.astro +++ b/website/src/pages/compare/index.astro @@ -46,13 +46,13 @@ const collectionSchema = { @@ -199,7 +201,7 @@ const showRssFeed = currentPath.startsWith('/blog') || currentPath.startsWith('/ -
+ Feature -
- - {rivetProductName} +
+
+ + {rivetProductName}
-
- {competitorIcon && } - {competitorName} +
+
+ {competitorIcon && } + {competitorName}
+ Why it matters
+
{group.title}
{row.feature} +
+ {row.feature} + - + + + + {row.importance} {row.importance}