Skip to content

Comparison Pages (WIP: Needs content) #6717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "Apollo Router",
"logo": "apollo",
"sections": [
{
"title": "Features Set 1",
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"rows": [
{ "feature": "Feature 1", "values": [false, true] },
{ "feature": "Feature 2", "values": [false, true] },
{ "feature": "Feature 3", "values": [false, true] },
{ "feature": "Feature 4", "values": [true, false] },
{ "feature": "Feature ...", "values": [true, false] }
]
},
{
"title": "Features Set 2",
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"rows": [{ "feature": "Feature 1", "values": [false, true] }]
}
]
}
141 changes: 141 additions & 0 deletions packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { readdir, readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { CommunitySection } from '#components/community-section';
import { CompanyTestimonialsSection } from '#components/company-testimonials';
import { ErrorBoundary } from '#components/error-boundary';
import { LandingPageContainer } from '#components/landing-page-container';
import { TrustedBySection } from '#components/trusted-by-section';
import {
CallToAction,
GetYourAPIGameRightSection,
Hero,
HeroLogo,
HiveGatewayIcon,
NextPageProps,
} from '@theguild/components';
import { FederationCompatibleBenchmarksSection } from '../../../gateway/federation-compatible-benchmarks';
import { GatewayHeroDecoration } from '../../../gateway/gateway-hero-decoration';
import { otherLogos } from '../../../gateway/other-logos';
import { metadata as rootMetadata } from '../../../layout';
import { ComparisonSection, ComparisonTable } from '../comparison-table';

const __dirname = dirname(new URL(import.meta.url).pathname)
.replace('%5B', '[')
.replace('%5D', ']');
Comment on lines +22 to +24
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I miss having a __dirname global. Big L for esmodules.


const DESCRIPTION =
'See why teams choose a fully open-source gateway instead of other closed solutions';

export default async function ComparisonPage(props: NextPageProps<'comparison'>) {
const comparison = JSON.parse(
await readFile(join(__dirname, `${(await props.params).comparison}.json`), 'utf-8'),
) as Comparison; /* we don't really need to parse this because it's a static build */

const Logo = otherLogos[comparison.logo as keyof typeof otherLogos];

return (
<LandingPageContainer className="text-green-1000 light mx-auto max-w-[90rem] overflow-hidden">
<Hero
top={
<HeroLogo>
<HiveGatewayIcon />
</HeroLogo>
}
className="bg-beige-100 mx-4 max-sm:mt-2 md:mx-6"
heading={`Hive Gateway vs. ${comparison.name}`}
checkmarks={['Fully open source', 'No vendor lock-in', 'Can be self-hosted!']}
text={DESCRIPTION}
>
<CallToAction variant="primary-inverted" href="/docs/gateway">
Get Started
</CallToAction>
<CallToAction variant="secondary-inverted" href="https://github.com/graphql-hive/gateway">
GitHub
</CallToAction>
<GatewayHeroDecoration>
<defs>
<linearGradient
id="gateway-hero-gradient"
x1="-188.558"
y1="63.4883"
x2="118.605"
y2="411.163"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" stopOpacity="0.5" />
<stop offset="1" stopColor="white" />
</linearGradient>
<linearGradient
id="gateway-hero-gradient-mobile"
x1="-188.558"
y1="63.4883"
x2="118.605"
y2="411.163"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" stopOpacity="0.3" />
<stop offset="1" stopColor="white" />
</linearGradient>
</defs>
</GatewayHeroDecoration>
</Hero>
<TrustedBySection className="mx-auto my-8 md:my-16 lg:my-24" />
<ComparisonTable
// We assume Hive Gateway is always the second column.
columns={[
{
name: comparison.name,
icon: <Logo className="text-green-800" />,
},
{
name: 'Hive Gateway',
icon: <HiveGatewayIcon className="text-green-800" />,
},
]}
sections={comparison.sections}
/>
<CompanyTestimonialsSection className="mx-4 mt-6 md:mx-6" />
<ErrorBoundary
fallback={
// this section doesn't make sense if data didn't load, so we just unmount
null
}
>
<FederationCompatibleBenchmarksSection />
</ErrorBoundary>
{/* todo: smaller Community-driven open-source section */}
<CommunitySection className="mx-4 mt-6 md:mx-6" />
<GetYourAPIGameRightSection className="mx-4 mt-6 sm:mb-6 md:mx-6 lg:mt-[64px]" />
</LandingPageContainer>
);
}

export async function generateStaticParams() {
const dir = await readdir(__dirname);
const jsonFiles = dir.filter(file => file.endsWith('.json'));
return jsonFiles.map(file => ({ comparison: file.replace('.json', '') }));
}

export async function generateMetadata({ params }: NextPageProps<'comparison'>) {
const file = join(__dirname, `${(await params).comparison}.json`);

const comparison = JSON.parse(
await readFile(file, 'utf-8'),
) as Comparison; /* we don't really need to parse this because it's a static build */

return {
title: `Hive Gateway vs. ${comparison.name}`,
description: DESCRIPTION,
alternates: {
// to remove leading slash
canonical: '.',
},
openGraph: rootMetadata!.openGraph,
};
}

interface Comparison {
name: string;
logo: string;
sections: ComparisonSection[];
}
118 changes: 118 additions & 0 deletions packages/web/docs/src/app/compare/gateway/comparison-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
'use client';

import { Fragment, ReactNode } from 'react';
import { cn, Heading, ComparisonTable as Table } from '@theguild/components';
import { CheckmarkIcon, XIcon } from '../../../components/icons';
import { NestedSticky } from '../../../components/nested-sticky';
import { TableSubheaderRow } from '../../../components/pricing/table-subheader-row';

const NO = <XIcon className="text-critical-dark mx-auto size-6" />;
const YES = <CheckmarkIcon className="text-positive-dark mx-auto size-6" />;

export type ComparisonColumn = {
name: string;
icon?: ReactNode;
};

export type ComparisonSection = {
title: string;
description: ReactNode;
icon?: ReactNode;
rows: ComparisonRow[];
};

export type ComparisonRow = {
feature: string;
values: (ReactNode | boolean)[];
};

export type ComparisonTableProps = {
className?: string;
columns: ComparisonColumn[];
sections: ComparisonSection[];
};

export function ComparisonTable({ className, columns, sections }: ComparisonTableProps) {
return (
<section className={cn('relative p-4 py-12', className)}>
<Heading size="md" as="h2" className="mx-auto w-[940px] max-w-full text-pretty text-center">
Hive Gateway allows you to do so much more. On your own terms.
</Heading>
<p className="mt-4 text-center text-green-800">
Part of the Hive ecosystem, Hive Gateway is a fully-fledged solution that you can easily
tailor to your needs.
</p>

<div className="md:nextra-scrollbar mt-16 md:-mx-6 md:overflow-x-auto md:px-6">
<NestedSticky offsetTop={80} offsetBottom={90}>
<div
aria-hidden
className="bg-beige-100 [[data-sticky]>&]:border-beige-200 relative flex items-center rounded-3xl border border-transparent *:text-left max-sm:-translate-y-4 sm:*:w-1/3 [[data-sticky]>&]:rounded-t-none [[data-sticky]>&]:shadow-sm"
>
<div className="z-10 flex-1 rounded-l-3xl p-6 text-xl/6 font-normal">
Compare features
</div>
{columns.map(column => (
<div className="py-6 last:rounded-r-3xl" key={column.name}>
<div className="border-beige-400 flex items-center justify-center gap-4 border-l px-6">
{column.icon && <div className="mr-2 *:size-10">{column.icon}</div>}
<div className="text-xl/6 font-medium max-sm:sr-only">{column.name}</div>
</div>
</div>
))}
</div>
</NestedSticky>

<Table
scheme="green"
className="table w-full border-separate border-spacing-0 border-none [&_tbody_tr>:last-child:not(:first-child)]:bg-green-100"
>
<thead className="sr-only">
<tr>
<th>Compare features</th>
{columns.map((column, i) => (
<th key={i}>{column.name}</th>
))}
</tr>
</thead>
<tbody>
{sections.map((section, sectionIndex) => (
<Fragment key={sectionIndex}>
<TableSubheaderRow
icon={section.icon}
title={section.title}
description={section.description}
/>
{section.rows.map((row, rowIndex) => (
<tr key={rowIndex}>
<ComparisonTableCell className="whitespace-pre">
{row.feature}
</ComparisonTableCell>
{row.values.map((value, columnIndex) => (
<ComparisonTableCell key={columnIndex}>
{typeof value === 'boolean' ? (value ? YES : NO) : value}
</ComparisonTableCell>
))}
</tr>
))}
</Fragment>
))}
</tbody>
</Table>
</div>
</section>
);
}

function ComparisonTableCell({ children, className }: { children: ReactNode; className?: string }) {
return (
<td
className={cn(
'border-beige-400 border-b border-r p-4 first:border-l first:font-medium max-sm:min-w-[97px] max-sm:text-sm max-sm:first:w-full sm:w-1/3 sm:py-6 [&:not(:first-child)]:border-l-0 [&:not(:first-child)]:text-center [&:not(:first-child)]:text-sm [&:not(:first-child)]:text-green-800 [.subheader+tr>&:last-child]:rounded-tr-3xl [.subheader+tr>&]:border-t [.subheader+tr>&]:first:rounded-tl-3xl [tr:is(:has(+.subheader),:last-child)>&:last-child]:rounded-br-3xl [tr:is(:last-child,:has(+.subheader))>&]:first:rounded-bl-3xl',
className,
)}
>
{children}
</td>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function FederationCompatibleBenchmarksSection({
</CallToAction>
</div>
<div className="hive-focus nextra-scrollbar border-beige-400 [&_:is(td,th)]:border-beige-400 overflow-x-auto rounded-2xl border [scrollbar-width:auto] max-sm:-mx-8">
<Table className="table w-full border-none max-sm:rounded-none max-sm:text-sm">
<Table className="table w-full border-none max-sm:rounded-none max-sm:text-sm max-md:[&_:is(td,th):first-of-type]:bg-white">
<thead>
<Table.Row className="*:text-left">
<Table.Header className="whitespace-pre pl-6">
Expand Down
19 changes: 19 additions & 0 deletions packages/web/docs/src/app/gateway/gateway-hero-decoration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ReactNode } from 'react';
import { DecorationIsolation, HiveGatewayIcon } from '@theguild/components';

export function GatewayHeroDecoration({ children }: { children: ReactNode }) {
return (
<DecorationIsolation className="-z-10">
<HiveGatewayIcon className="absolute left-[-268px] top-[-8px] size-[520px] fill-[url(#gateway-hero-gradient)] max-lg:hidden" />
<HiveGatewayIcon className="absolute right-[-144px] top-[-64px] size-[320px] fill-[url(#gateway-hero-gradient-mobile)] md:bottom-[-64px] md:right-[-268px] md:top-auto md:size-[520px] md:fill-[url(#gateway-hero-gradient)] lg:bottom-[-8px]" />
<svg
className="pointer-events-none -z-50 size-0"
width="192"
height="296"
viewBox="0 0 192 296"
>
{children}
</svg>
</DecorationIsolation>
);
}
28 changes: 11 additions & 17 deletions packages/web/docs/src/app/gateway/orchestrate-your-way.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ReactNode } from 'react';
import { Anchor, Heading, InfoCard } from '@theguild/components';
import { ApolloLogo } from './other-logos';

export function OrchestrateYourWay({ className, ...rest }: React.HTMLAttributes<HTMLDivElement>) {
return (
Expand Down Expand Up @@ -39,8 +40,16 @@ export function OrchestrateYourWay({ className, ...rest }: React.HTMLAttributes<
</ul>
<div className="nextra-scrollbar max-w-full basis-full overflow-auto rounded-3xl">
<ul className="grid w-fit grid-cols-[repeat(4,minmax(240px,1fr))] bg-[#F8F7F6] [background-image:linear-gradient(55deg,#F8F7F6_25%,theme(colors.blue.700/0.25)_70%,#F8F7F6_94%)] [&>:not(:first-child)]:border-l [&>:not(:first-child)]:border-blue-300">
<LinkCard href="/federation" logo={<ApolloLogo />} text="Apollo Federation V1" />
<LinkCard href="/federation" logo={<ApolloLogo />} text="Apollo Federation V2" />
<LinkCard
href="/federation"
logo={<ApolloLogo className="text-green-1000" />}
text="Apollo Federation V1"
/>
<LinkCard
href="/federation"
logo={<ApolloLogo className="text-green-1000" />}
text="Apollo Federation V2"
/>
<LinkCard
href="/docs/get-started/first-steps#create-hive-project"
logo={<StitchingLogo />}
Expand Down Expand Up @@ -97,21 +106,6 @@ function LinkCard({ href, text, logo }: { href: string; text: ReactNode; logo: R
);
}

function ApolloLogo() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="58" viewBox="0 0 60 58" fill="none">
<path
d="M29.2787 0.0405435C29.1666 0.045613 28.7435 0.0810993 28.3357 0.111516C23.2382 0.486658 18.0694 2.36237 13.8334 5.37364C12.4316 6.36726 11.3255 7.30005 10.0919 8.52686C3.00132 15.5836 0.0804735 25.682 2.29787 35.4915C3.20521 39.5166 5.14225 43.5672 7.71137 46.8218C11.7435 51.9369 17.2691 55.5413 23.4676 57.1027C25.9908 57.7414 28.0859 58 30.7315 58C33.3414 58 35.2631 57.7668 37.7507 57.1635C41.324 56.2916 44.464 54.8569 47.6041 52.672C50.6116 50.5732 53.4101 47.6329 55.4185 44.4645C58.008 40.3683 59.4811 35.8463 59.8431 30.8833C59.9195 29.839 59.8736 27.2535 59.7615 26.189C59.5372 24.075 58.9663 21.2868 58.635 20.7038C58.5024 20.4756 58.1303 20.1816 57.855 20.0853C57.7582 20.0549 57.5441 20.0295 57.3708 20.0295C56.6062 20.0295 55.9639 20.6632 55.9639 21.4084C55.9639 21.5859 56.0455 22.0117 56.1627 22.473C57.172 26.3765 57.2535 30.6653 56.3921 34.579C55.291 39.5927 52.8341 44.0133 49.1282 47.638C47.4002 49.3261 45.7333 50.5732 43.5771 51.8C38.5459 54.6643 32.2913 55.7491 26.4241 54.7758C21.969 54.0407 18.1 52.3374 14.4247 49.4934C12.6967 48.155 10.5914 45.9498 9.28648 44.1045C7.0334 40.9209 5.61631 37.5649 4.88228 33.6664C4.51016 31.6792 4.38273 29.0431 4.55604 26.8987C4.82111 23.6441 5.6418 20.6227 7.0334 17.7584C7.67568 16.4251 8.07329 15.7306 8.843 14.5798C10.413 12.2175 12.4928 10.0072 14.7917 8.25311C17.4883 6.18983 20.6895 4.62336 23.9264 3.78182C25.6748 3.32557 27.0715 3.09744 28.8454 2.97071C35.2631 2.51445 41.5024 4.34961 46.5846 8.19735C46.9618 8.48124 47.3594 8.79048 47.4664 8.8868L47.6601 9.05409L47.5837 9.26701C47.1912 10.362 47.4817 11.7257 48.3075 12.6788C48.7306 13.1654 49.4952 13.6217 50.1426 13.7738C50.5861 13.8802 51.3558 13.8802 51.8044 13.7738C52.9411 13.5051 54.0014 12.5368 54.3378 11.4621C54.715 10.2606 54.4958 9.07437 53.721 8.11117C52.8646 7.04657 51.3915 6.57004 50.0305 6.92491L49.7348 7.00095L49.3168 6.65622C47.6194 5.24184 45.5651 3.93391 43.4038 2.89466C39.8967 1.20653 36.3744 0.299087 32.3627 0.0557519C31.5929 0.0101265 29.7732 -1.24276e-05 29.2787 0.0405435Z"
fill="#00342C"
/>
<path
d="M26.261 16.7192C25.7207 18.1083 24.5483 21.15 23.646 23.4769C22.7438 25.8038 20.8934 30.5843 19.5324 34.0974C18.1765 37.6106 17.0499 40.5002 17.0397 40.5205C17.0244 40.5458 18.4568 40.561 20.2154 40.561H23.4217L23.6103 40.0389C23.7174 39.755 24.0946 38.7512 24.4463 37.8083L25.0886 36.0999H29.9006C33.7339 36.0999 34.7126 36.0847 34.6973 36.034C34.6463 35.8971 33.8613 33.6665 33.4433 32.4752L32.9948 31.2078L29.8088 31.1825L26.628 31.1571L28.6976 25.4844C29.9414 22.0777 30.7825 19.8471 30.7978 19.8978C30.8181 19.9434 31.6439 22.2805 32.6379 25.089C33.6319 27.8975 35.2733 32.5208 36.2826 35.3648L38.1126 40.5357L41.3036 40.5509C43.0571 40.556 44.4946 40.5509 44.4946 40.5408C44.4946 40.5306 44.0817 39.4508 43.572 38.148C43.0622 36.84 41.9714 34.0265 41.1456 31.8922C40.3198 29.758 39.3921 27.3652 39.0811 26.5693C38.7753 25.7733 38.24 24.3944 37.8934 23.5022C37.5468 22.61 36.6445 20.273 35.8799 18.306C35.1153 16.339 34.4475 14.6103 34.3915 14.4633L34.2946 14.1997H27.2346L26.261 16.7192Z"
fill="#00342C"
/>
</svg>
);
}

function StitchingLogo() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60" fill="none">
Expand Down
Loading
Loading