mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
new pricing plans table (#6581)
This commit is contained in:
parent
06e7012968
commit
e638e6876d
16 changed files with 688 additions and 116 deletions
|
|
@ -231,6 +231,7 @@ module.exports = {
|
|||
'nextra-scrollbar',
|
||||
'no-scrollbar', // from Nextra
|
||||
'hive-slider',
|
||||
'subheader',
|
||||
],
|
||||
config: path.join(__dirname, './packages/web/docs/tailwind.config.ts'),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "0.5.16",
|
||||
"@theguild/tailwind-config": "0.6.2",
|
||||
"@theguild/tailwind-config": "0.6.3",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/rss": "^0.0.32",
|
||||
"next-sitemap": "4.2.3",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { use } from 'react';
|
||||
import { cn, ComparisonTable as Table } from '@theguild/components';
|
||||
import { functionalTones } from './functional-tones';
|
||||
import { CheckmarkIcon, XIcon } from './icons';
|
||||
|
||||
interface BenchmarkDatum {
|
||||
|
|
@ -47,48 +46,35 @@ export function BenchmarkTableBody() {
|
|||
>
|
||||
<div className="flex items-center gap-2.5 whitespace-nowrap">
|
||||
<div
|
||||
className="size-3 rounded-full"
|
||||
style={{
|
||||
background:
|
||||
compatibility > 99
|
||||
? functionalTones.positiveBright
|
||||
: compatibility > 90
|
||||
? functionalTones.warning
|
||||
: functionalTones.criticalBright,
|
||||
}}
|
||||
className={cn(
|
||||
'size-3 rounded-full',
|
||||
compatibility > 99
|
||||
? 'bg-positive-bright'
|
||||
: compatibility > 90
|
||||
? 'bg-warning-bright'
|
||||
: 'bg-critical-bright',
|
||||
)}
|
||||
/>
|
||||
{row.name}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell className="text-sm text-green-800">{compatibility.toFixed(2)}%</Table.Cell>
|
||||
<Table.Cell>
|
||||
<span
|
||||
className="inline-flex items-center gap-0.5 text-sm"
|
||||
style={{ color: functionalTones.positiveDark }}
|
||||
>
|
||||
<span className="text-positive-dark inline-flex items-center gap-0.5 text-sm">
|
||||
<CheckmarkIcon className="size-4" /> {row.cases.passed}
|
||||
</span>
|
||||
{row.cases.failed > 0 && (
|
||||
<span
|
||||
className="ml-2 inline-flex items-center text-sm"
|
||||
style={{ color: functionalTones.criticalDark }}
|
||||
>
|
||||
<span className="text-critical-dark ml-2 inline-flex items-center text-sm">
|
||||
<XIcon className="size-4" /> {row.cases.failed}
|
||||
</span>
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<span
|
||||
className="inline-flex items-center gap-0.5 text-sm"
|
||||
style={{ color: functionalTones.positiveDark }}
|
||||
>
|
||||
<span className="text-positive-dark inline-flex items-center gap-0.5 text-sm">
|
||||
<CheckmarkIcon className="size-4" /> {row.suites.passed}
|
||||
</span>
|
||||
{row.suites.failed > 0 && (
|
||||
<span
|
||||
className="ml-2 inline-flex items-center text-sm"
|
||||
style={{ color: functionalTones.criticalDark }}
|
||||
>
|
||||
<span className="text-critical-dark ml-2 inline-flex items-center text-sm">
|
||||
<XIcon className="size-4" /> {row.suites.failed}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
/**
|
||||
* todo: move this to the design system as Tailwind classes
|
||||
*/
|
||||
export const functionalTones = {
|
||||
criticalBright: '#FD3325',
|
||||
criticalDark: ' #F81202',
|
||||
warning: '#FE8830',
|
||||
positiveBright: '#24D551',
|
||||
positiveDark: '#1BA13D',
|
||||
};
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { Suspense } from 'react';
|
||||
import { CallToAction, cn, Heading, ComparisonTable as Table } from '@theguild/components';
|
||||
import { BenchmarkTableBody } from './benchmark-table-body';
|
||||
import { functionalTones } from './functional-tones';
|
||||
import { CheckmarkIcon, XIcon } from './icons';
|
||||
|
||||
export interface FederationCompatibleBenchmarksSectionProps
|
||||
|
|
@ -90,26 +89,22 @@ function BenchmarkLegend() {
|
|||
<div className="mt-6 flex flex-wrap gap-2 whitespace-nowrap text-xs text-green-800 sm:gap-4">
|
||||
<div className="flex gap-2 max-sm:-mx-1 max-sm:w-full sm:contents">
|
||||
<div className="flex items-center gap-1">
|
||||
<CheckmarkIcon className="size-4" style={{ color: functionalTones.positiveDark }} />{' '}
|
||||
Passed tests
|
||||
<CheckmarkIcon className="text-positive-dark size-4" /> Passed tests
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<XIcon className="size-4" style={{ color: functionalTones.criticalDark }} /> Failed tests
|
||||
<XIcon className="text-critical-dark size-4" /> Failed tests
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="size-2 rounded-full"
|
||||
style={{ background: functionalTones.positiveBright }}
|
||||
/>
|
||||
<div className="bg-positive-bright size-2 rounded-full" />
|
||||
Perfect compatibility
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="size-2 rounded-full" style={{ background: functionalTones.warning }} />
|
||||
<div className="bg-warning-bright size-2 rounded-full" />
|
||||
75% and higher
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="size-2 rounded-full" style={{ background: functionalTones.criticalDark }} />
|
||||
<div className="bg-critical-dark size-2 rounded-full" />
|
||||
Less than 75%
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { DynamicMetaTags } from './dynamic-meta-tags';
|
|||
import graphQLConfLocalImage from '../components/graphql-conf-image.webp';
|
||||
import '@theguild/components/style.css';
|
||||
import '../selection-styles.css';
|
||||
import '../easing-functions.css';
|
||||
import '../mermaid.css';
|
||||
import { NarrowPages } from './narrow-pages';
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { FrequentlyAskedQuestions } from '../../components/frequently-asked-ques
|
|||
import { LandingPageContainer } from '../../components/landing-page-container';
|
||||
import { PlanComparison } from '../../components/plan-comparison';
|
||||
import { Pricing } from '../../components/pricing';
|
||||
import { PlansTable } from '../../components/pricing/plans-table';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Hive Platform Pricing',
|
||||
|
|
@ -26,6 +27,8 @@ export default function PricingPage() {
|
|||
|
||||
<PlanComparison className="mx-4 md:mx-6" />
|
||||
|
||||
<PlansTable />
|
||||
|
||||
<CompanyTestimonialsSection className="mx-4 mt-6 md:mx-6" />
|
||||
|
||||
<FrequentlyAskedQuestions className="mx-4 md:mx-6" />
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ export function CompanyTestimonialsSection({ className }: { className?: string }
|
|||
value={company}
|
||||
tabIndex={-1}
|
||||
className={cn(
|
||||
'relative flex w-full shrink-0 snap-center flex-col',
|
||||
'relative flex w-full shrink-0 snap-center flex-col outline-none',
|
||||
'gap-6 md:flex-row lg:gap-12',
|
||||
'lg:data-[state="inactive"]:hidden',
|
||||
caseStudyHref
|
||||
|
|
|
|||
17
packages/web/docs/src/components/icons.tsx
Normal file
17
packages/web/docs/src/components/icons.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// these are different than CheckIcon and CloseIcon we have in the design system
|
||||
|
||||
export function CheckmarkIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" {...props}>
|
||||
<path d="M6.66668 10.1134L12.7947 3.98608L13.7373 4.92875L6.66668 11.9994L2.42401 7.75675L3.36668 6.81408L6.66668 10.1134Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function XIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" {...props}>
|
||||
<path d="M7.99999 7.05806L11.3 3.75806L12.2427 4.70072L8.94266 8.00072L12.2427 11.3007L11.2993 12.2434L7.99932 8.94339L4.69999 12.2434L3.75732 11.3001L7.05732 8.00006L3.75732 4.70006L4.69999 3.75872L7.99999 7.05806Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
137
packages/web/docs/src/components/nested-sticky.tsx
Normal file
137
packages/web/docs/src/components/nested-sticky.tsx
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
'use client';
|
||||
|
||||
import { ReactNode, useEffect, useRef } from 'react';
|
||||
|
||||
const BOTTOM_THRESHOLD_ADJUSTMENT = 10;
|
||||
|
||||
interface NestedStickyProps {
|
||||
children: ReactNode;
|
||||
offsetTop: number;
|
||||
offsetBottom: number;
|
||||
zIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* `position: sticky` doesn't work in nested divs with overflow-x-hidden,
|
||||
* and restructuring the app to put pricing table header on top level would
|
||||
* require tricky state management, so we have this for the cases where we
|
||||
* need position: sticky, but can't use it directly.
|
||||
*/
|
||||
export function NestedSticky({
|
||||
children,
|
||||
offsetTop,
|
||||
offsetBottom,
|
||||
zIndex = 10,
|
||||
}: NestedStickyProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const placeholder = container.firstElementChild as HTMLElement;
|
||||
const sticky = container.lastElementChild as HTMLElement;
|
||||
const parent = container.parentElement;
|
||||
|
||||
if (!placeholder || !sticky || !parent) return;
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let rafId: number | null = null;
|
||||
|
||||
// relative at the top
|
||||
// fixed when we scroll
|
||||
// absolute when we're near the bottom
|
||||
type State = 'fixed' | 'absolute' | 'relative';
|
||||
let state: State = 'relative';
|
||||
let prevState: State = 'relative';
|
||||
|
||||
const measureDimensions = () => {
|
||||
const rect = sticky.getBoundingClientRect();
|
||||
width = rect.width;
|
||||
height = rect.height;
|
||||
|
||||
sticky.style.zIndex = String(zIndex);
|
||||
};
|
||||
|
||||
const updateStyles = () => {
|
||||
placeholder.style.height = state !== 'relative' ? `${height}px` : '0';
|
||||
|
||||
if (state === 'fixed') {
|
||||
sticky.style.position = 'fixed';
|
||||
sticky.style.top = `${offsetTop}px`;
|
||||
sticky.style.width = `${width}px`;
|
||||
sticky.setAttribute('data-sticky', 'fixed');
|
||||
} else if (state === 'absolute') {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const stickyRect = sticky.getBoundingClientRect();
|
||||
|
||||
const relativeTop = stickyRect.top - containerRect.top;
|
||||
|
||||
sticky.style.position = 'absolute';
|
||||
sticky.style.top = `${relativeTop}px`;
|
||||
sticky.style.width = `${width}px`;
|
||||
sticky.setAttribute('data-sticky', 'absolute');
|
||||
} else {
|
||||
sticky.style.position = 'relative';
|
||||
sticky.style.top = '';
|
||||
sticky.style.width = '';
|
||||
sticky.removeAttribute('data-sticky');
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = () => {
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
}
|
||||
|
||||
rafId = requestAnimationFrame(() => {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const parentRect = parent.getBoundingClientRect();
|
||||
|
||||
const shouldBeFixed = containerRect.top < offsetTop;
|
||||
|
||||
const nearBottom =
|
||||
parentRect.bottom < offsetTop + height + offsetBottom + BOTTOM_THRESHOLD_ADJUSTMENT;
|
||||
|
||||
state = shouldBeFixed && nearBottom ? 'absolute' : shouldBeFixed ? 'fixed' : 'relative';
|
||||
|
||||
if (state !== prevState) {
|
||||
prevState = state;
|
||||
updateStyles();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleResize = () => {
|
||||
const placeholderRect = placeholder.getBoundingClientRect();
|
||||
|
||||
width = placeholderRect.width;
|
||||
|
||||
updateStyles();
|
||||
handleScroll();
|
||||
};
|
||||
|
||||
measureDimensions();
|
||||
handleScroll();
|
||||
|
||||
window.addEventListener('resize', handleResize, { passive: true });
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
|
||||
return () => {
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
}
|
||||
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [offsetTop, offsetBottom, zIndex]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="relative">
|
||||
<div style={{ width: '100%', height: 0 }} />
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -289,8 +289,9 @@ export function Pricing({ className }: { className?: string }): ReactElement {
|
|||
}
|
||||
callToAction={
|
||||
<ContactButton variant="primary">
|
||||
Shape a custom plan
|
||||
<span className="hidden sm:inline">for your business</span>
|
||||
<span>
|
||||
Shape a custom plan <span className="hidden sm:inline">for your business</span>
|
||||
</span>
|
||||
</ContactButton>
|
||||
}
|
||||
features={
|
||||
|
|
|
|||
441
packages/web/docs/src/components/pricing/plans-table.tsx
Normal file
441
packages/web/docs/src/components/pricing/plans-table.tsx
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
'use client';
|
||||
|
||||
import { ReactNode, useState } from 'react';
|
||||
import {
|
||||
CallToAction,
|
||||
cn,
|
||||
Heading,
|
||||
ComparisonTable as Table,
|
||||
TextLink,
|
||||
} from '@theguild/components';
|
||||
import { CheckmarkIcon, XIcon } from '../../app/gateway/federation-compatible-benchmarks/icons';
|
||||
import { NestedSticky } from '../nested-sticky';
|
||||
import {
|
||||
AvailabilityIcon,
|
||||
EnterpriseSupportIcon,
|
||||
OperationsIcon,
|
||||
SSOIcon,
|
||||
UsageIcon,
|
||||
} from './icons';
|
||||
|
||||
type PlanName = 'Hobby' | 'Pro' | 'Enterprise';
|
||||
interface PricingPlan {
|
||||
name: PlanName;
|
||||
cta: ReactNode;
|
||||
}
|
||||
|
||||
const pricingTiers: PricingPlan[] = [
|
||||
{
|
||||
name: 'Hobby',
|
||||
cta: (
|
||||
<CallToAction
|
||||
variant="tertiary"
|
||||
// todo: move this style as size="sm" to design system
|
||||
className="px-3 py-2 text-sm"
|
||||
href="https://app.graphql-hive.com"
|
||||
>
|
||||
Get started for free
|
||||
</CallToAction>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'Pro',
|
||||
cta: (
|
||||
<CallToAction
|
||||
variant="primary"
|
||||
className="px-3 py-2 text-sm"
|
||||
href="https://app.graphql-hive.com"
|
||||
>
|
||||
Try free for 30 days
|
||||
</CallToAction>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'Enterprise',
|
||||
cta: (
|
||||
<CallToAction
|
||||
variant="primary"
|
||||
className="px-3 py-2 text-sm"
|
||||
href="https://the-guild.dev/contact"
|
||||
>
|
||||
Shape your business
|
||||
</CallToAction>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export function PlansTable({ className }: { className?: string }) {
|
||||
const [activePlan, setActivePlan] = useState<PlanName>('Hobby');
|
||||
|
||||
const NO = <XIcon className="text-critical-dark mx-auto size-6" />;
|
||||
const YES = <CheckmarkIcon className="text-positive-dark mx-auto size-6" />;
|
||||
|
||||
return (
|
||||
<section className={cn('relative p-4 py-12 md:px-6 lg:py-24 xl:px-[120px]', className)}>
|
||||
<Heading
|
||||
size="md"
|
||||
as="h3"
|
||||
className="text-pretty text-center max-md:text-[32px]/10 max-md:tracking-[-.16px]"
|
||||
>
|
||||
Hive Console allows you to do so much more.
|
||||
<br className="max-xl:hidden" /> On your own terms.
|
||||
</Heading>
|
||||
<p className="mb-8 mt-4 text-center md:mb-16">
|
||||
Part of the Hive ecosystem, Hive Console is a fully-fledged solution that you can easily
|
||||
tailor to your needs.
|
||||
</p>
|
||||
|
||||
<MobileNavbar setActivePlan={setActivePlan} activePlan={activePlan} />
|
||||
|
||||
<div className="md:nextra-scrollbar 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-md:hidden md:*:w-1/4 [[data-sticky]>&]:rounded-t-none [[data-sticky]>&]:shadow-sm"
|
||||
>
|
||||
<div className="z-10 rounded-l-3xl p-6 text-xl/6 font-normal">Features</div>
|
||||
{pricingTiers.map(tier => (
|
||||
<div className="py-6 last:rounded-r-3xl" key={tier.name}>
|
||||
<div className="border-beige-400 flex items-center justify-between gap-4 border-l px-6 sm:[@media(width<1400px)]:[&>a]:hidden">
|
||||
<div className="text-xl/6 font-medium">{tier.name}</div>
|
||||
{tier.cta}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</NestedSticky>
|
||||
<Table className="table w-full border-separate border-spacing-0 border-none">
|
||||
<thead className="sr-only">
|
||||
<tr>
|
||||
<th>Features</th>
|
||||
{pricingTiers.map(tier => (
|
||||
<th key={tier.name}>{tier.name}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<TableSubheaderRow
|
||||
icon={<OperationsIcon />}
|
||||
title="Operations and data retention"
|
||||
description="Structured by your plan—analyze the limits, manage your potential."
|
||||
/>
|
||||
<tr>
|
||||
<PlansTableCell className="whitespace-pre">Operations per month</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
Limit of 100 operations
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
1M operations per month
|
||||
<br className="max-sm:inline" />
|
||||
Then $10 per million operations
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
Custom operation limit
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>Usage data retention</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
7 days
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
90 days
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
One-year Minimum, Customizable
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
|
||||
<TableSubheaderRow
|
||||
icon={<UsageIcon />}
|
||||
title="Usage"
|
||||
description="All plans, all features, all unlimited. Know exactly what you're working with."
|
||||
/>
|
||||
<tr>
|
||||
<PlansTableCell>Scale: projects and organizations</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
Unlimited
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
Unlimited
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
Unlimited
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>GitHub issues and chat support</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>Schema pushes and checks</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
Unlimited
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
Unlimited
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
Unlimited
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
|
||||
<TableSubheaderRow
|
||||
icon={<AvailabilityIcon />}
|
||||
title="Availability"
|
||||
description="Engineered for uninterrupted performance and reliability."
|
||||
/>
|
||||
<tr>
|
||||
<PlansTableCell>99.95% uptime of operation collection</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell className="lg:whitespace-pre">
|
||||
100% uptime of schema registry CDN
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
|
||||
<TableSubheaderRow
|
||||
icon={<SSOIcon />}
|
||||
title="SSO"
|
||||
description={
|
||||
<>
|
||||
Single sign-on via Open ID provider.{' '}
|
||||
<TextLink href="/docs/management/sso-oidc-provider">Learn more.</TextLink>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<tr>
|
||||
<PlansTableCell>Single sign-on via Open ID provider</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
|
||||
<TableSubheaderRow
|
||||
icon={<EnterpriseSupportIcon />}
|
||||
title="Enterprise Support"
|
||||
description="Dedicated resources and personalized guidance designed for enterprise-scale needs."
|
||||
/>
|
||||
<tr>
|
||||
<PlansTableCell>Dedicated Slack channel for support</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>White-glove onboarding</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>Support SLA</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
<TextLink
|
||||
href="https://the-guild.dev/graphql/hive/sla.pdf"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-green-800"
|
||||
>
|
||||
Pre-defined SLA
|
||||
</TextLink>
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
<TextLink
|
||||
href="https://the-guild.dev/graphql/hive/sla.pdf"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-green-800"
|
||||
>
|
||||
Pre-defined SLA
|
||||
</TextLink>
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
Tailored to your needs
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>Technical Account Manager & guidance from The Guild</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>
|
||||
Flexible billing options & extended procurement processes
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<PlansTableCell>Custom Data Processing Agreements (DPA)</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Hobby">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Pro">
|
||||
{NO}
|
||||
</PlansTableCell>
|
||||
<PlansTableCell activePlan={activePlan} plan="Enterprise">
|
||||
{YES}
|
||||
</PlansTableCell>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileNavbar({
|
||||
setActivePlan,
|
||||
activePlan,
|
||||
}: {
|
||||
setActivePlan: (plan: PlanName) => void;
|
||||
activePlan: PlanName;
|
||||
}) {
|
||||
return (
|
||||
<NestedSticky
|
||||
offsetTop={
|
||||
// --nextra-navbar-height
|
||||
64
|
||||
}
|
||||
offsetBottom={482}
|
||||
>
|
||||
<div className="bg-beige-100 before:bg-beige-100 before:border-b-beige-400 relative top-0 z-10 w-full rounded-2xl p-2 duration-100 ease-[var(--hive-ease-overshoot-a-bit)] before:absolute before:inset-0 before:opacity-0 before:transition md:hidden [[data-sticky]>&:before]:scale-x-125 [[data-sticky]>&:before]:border-b [[data-sticky]>&:before]:opacity-100 [[data-sticky]>&:before]:shadow-sm">
|
||||
<div className="relative flex w-full">
|
||||
{pricingTiers.map(tier => (
|
||||
<button
|
||||
key={tier.name}
|
||||
onClick={() => setActivePlan(tier.name)}
|
||||
className={cn(
|
||||
'hive-focus bg-beige-100 flex-1 rounded-xl px-3 py-2 text-center text-sm font-medium leading-5 transition hover:z-10 hover:ring hover:ring-inset focus:z-10',
|
||||
activePlan === tier.name && 'bg-white',
|
||||
)}
|
||||
>
|
||||
{tier.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="relative mt-3 h-9">
|
||||
{pricingTiers.map((plan, i) => {
|
||||
const isActive = plan.name === activePlan;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute inset-0 z-10 flex items-center justify-center rounded-lg *:!w-full aria-hidden:pointer-events-none aria-hidden:z-0',
|
||||
i === 0 && 'bg-beige-100',
|
||||
)}
|
||||
aria-hidden={!isActive}
|
||||
key={plan.name}
|
||||
>
|
||||
{plan.cta}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</NestedSticky>
|
||||
);
|
||||
}
|
||||
|
||||
function PlansTableCell({
|
||||
plan,
|
||||
activePlan: currentPlan,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
plan?: PlanName;
|
||||
activePlan?: PlanName;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<td
|
||||
aria-hidden={plan !== currentPlan}
|
||||
className={cn(
|
||||
'border-beige-400 border-b border-r p-4 first:border-l first:font-medium max-md:w-1/2 max-sm:text-sm sm:py-6 md:w-1/4 [&:not(:first-child)]:border-l-0 [&:not(:first-child)]:text-center [&:not(:first-child)]:text-sm [&:not(:first-child)]:text-green-800 md:[.subheader+tr>&:last-child]:rounded-tr-3xl max-md:[.subheader+tr>&:not(:first-child,:has(+td[aria-hidden=false]))]:rounded-tr-3xl [.subheader+tr>&]:border-t [.subheader+tr>&]:first:rounded-tl-3xl md:[tr:is(:has(+.subheader),:last-child)>&:last-child]:rounded-br-3xl max-md:[tr:is(:has(+.subheader),:last-child)>&:not(:first-child,:has(+td[aria-hidden=false]))]:rounded-br-3xl [tr:is(:last-child,:has(+.subheader))>&]:first:rounded-bl-3xl',
|
||||
plan && plan !== currentPlan && 'max-md:hidden',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
interface TableSubheaderRowProps {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
description: ReactNode;
|
||||
}
|
||||
function TableSubheaderRow({ icon, title, description }: TableSubheaderRowProps) {
|
||||
return (
|
||||
<tr className="subheader">
|
||||
<td colSpan={4} className="pb-6 pt-8">
|
||||
<div className="flex items-center text-[32px]/10 max-md:text-[20px]/6 max-md:font-medium [&>svg]:m-[6.67px] [&>svg]:mr-[10.67px] [&>svg]:size-[26.67px] [&>svg]:text-green-600">
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
<p className="mt-2 text-green-800">{description}</p>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,32 +16,33 @@ export function PricingSlider({
|
|||
const max = 300;
|
||||
|
||||
const [popoverOpen, setPopoverOpen] = useState(false);
|
||||
const rootRef = useRef<HTMLLabelElement>(null);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<label
|
||||
<div
|
||||
ref={rootRef}
|
||||
className={cn(
|
||||
'relative isolate block select-none rounded-3xl border border-green-400 p-4 [counter-set:ops_calc(var(--ops))_price_calc(var(--price))] sm:p-8',
|
||||
'relative isolate block select-none rounded-3xl border border-green-400 p-4 [counter-set:ops_calc(var(--ops))] sm:p-8',
|
||||
className,
|
||||
)}
|
||||
// 10$ base price + 10$ per 1M
|
||||
style={{ '--ops': min, '--price': 'calc(10 + var(--ops) * 10)' }}
|
||||
{...rest}
|
||||
>
|
||||
<div className="text-green-1000 items-center text-2xl font-medium md:flex md:h-12 md:w-[calc(100%-260px)]">
|
||||
<div className="relative max-w-[clamp(calc(60.95px+14.47px*round(down,log(max(var(--ops),1),10),1)),(2-var(--ops))*111px,111px)] shrink grow motion-safe:transition-all">
|
||||
<div
|
||||
aria-hidden
|
||||
className="flex w-full whitespace-pre rounded-[40px] bg-blue-300 px-3 py-1 tabular-nums leading-8 opacity-[calc(var(--ops)-1)] [transition-duration:calc(clamp(0,var(--ops)-1,1)*350ms)] before:tracking-[-0.12em] before:content-[''_counter(ops)_'_'] motion-safe:transition-all"
|
||||
>
|
||||
<div
|
||||
aria-hidden
|
||||
className="text-green-1000 flex flex-wrap items-center text-2xl font-medium md:h-12 md:w-[calc(100%-260px)]"
|
||||
>
|
||||
<div className="relative min-w-[clamp(calc(60.95px+14.47px*round(down,log(max(var(--ops),1),10),1)),(2-var(--ops))*111px,111px)] max-w-[clamp(calc(60.95px+14.47px*round(down,log(max(var(--ops),1),10),1)),(2-var(--ops))*111px,111px)] shrink grow motion-safe:transition-all">
|
||||
<div className="flex w-full whitespace-pre rounded-[40px] bg-blue-300 px-3 py-1 tabular-nums leading-8 opacity-[calc(var(--ops)-1)] [transition-duration:calc(clamp(0,var(--ops)-1,1)*350ms)] before:tracking-[-0.12em] before:content-[''_counter(ops)_'_'] motion-safe:transition-all">
|
||||
M
|
||||
</div>
|
||||
<div className="absolute left-0 top-0 whitespace-pre leading-10 opacity-[calc(2-var(--ops))] [transition-duration:calc(clamp(0,2-var(--ops),1)*350ms)] motion-safe:transition">
|
||||
How many
|
||||
</div>
|
||||
</div>
|
||||
<div className="whitespace-pre"> operations per month </div>
|
||||
<div className="shrink-0 whitespace-pre"> operations </div>
|
||||
<div className="whitespace-pre [@media(width<900px)]:hidden">per month </div>
|
||||
<div className="whitespace-pre opacity-[calc(2-var(--ops))] [transition-duration:350ms] motion-safe:transition">
|
||||
do you need?
|
||||
</div>
|
||||
|
|
@ -50,12 +51,13 @@ export function PricingSlider({
|
|||
<div className="text-green-1000 flex items-center gap-5 pt-12 text-sm">
|
||||
<span className="font-medium">{min}M</span>
|
||||
<Slider
|
||||
aria-label="How many operations per month do you need?"
|
||||
deadZone="16px"
|
||||
min={min}
|
||||
max={max}
|
||||
step={1}
|
||||
defaultValue={min}
|
||||
counter="after:content-['$'_counter(price)_'_/_month']"
|
||||
counter="after:content-['$'_counter(price)_'_/_month'] after:[counter-set:price_calc(var(--price))]"
|
||||
onChange={event => {
|
||||
const value = event.currentTarget.valueAsNumber;
|
||||
rootRef.current!.style.setProperty('--ops', `${value}`);
|
||||
|
|
@ -92,6 +94,6 @@ export function PricingSlider({
|
|||
(assuming no sampling).
|
||||
</Content>
|
||||
</Root>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,58 +89,15 @@ export function Slider({ counter, className, deadZone, style, ...rest }: SliderP
|
|||
}
|
||||
|
||||
@supports not (font: -apple-system-body) {
|
||||
{/* todo: move to @theme in Tailwind 4 */}
|
||||
.hive-slider {
|
||||
--ease-overshoot-far: linear(
|
||||
0 0%,
|
||||
0.5007 7.21%,
|
||||
0.7803 12.29%,
|
||||
0.8883 14.93%,
|
||||
0.9724 17.63%,
|
||||
1.0343 20.44%,
|
||||
1.0754 23.44%,
|
||||
1.0898 25.22%,
|
||||
1.0984 27.11%,
|
||||
1.1014 29.15%,
|
||||
1.0989 31.4%,
|
||||
1.0854 35.23%,
|
||||
1.0196 48.86%,
|
||||
1.0043 54.06%,
|
||||
0.9956 59.6%,
|
||||
0.9925 68.11%,
|
||||
1 100%
|
||||
);
|
||||
|
||||
--ease-overshoot-a-bit: linear(
|
||||
0 0%,
|
||||
0.5007 7.21%,
|
||||
0.7803 12.29%,
|
||||
0.8883 14.93%,
|
||||
0.9724 17.63%,
|
||||
1.011319 20.44%,
|
||||
1.024882 23.44%,
|
||||
1.029634 25.22%,
|
||||
1.032472 27.11%,
|
||||
1.033462 29.15%,
|
||||
1.032637 31.4%,
|
||||
1.028182 35.23%,
|
||||
1.006468 48.86%,
|
||||
1.001419 54.06%,
|
||||
0.9956 59.6%,
|
||||
0.9925 68.11%,
|
||||
1 100%
|
||||
);
|
||||
}
|
||||
|
||||
div,
|
||||
div:after {
|
||||
transition: transform var(--ease-overshoot-far) 500ms;
|
||||
transition: transform var(--hive-ease-overshoot-far) 500ms;
|
||||
}
|
||||
|
||||
@container (width >= 512px) {
|
||||
div,
|
||||
div:after {
|
||||
transition: transform var(--ease-overshoot-a-bit) 500ms;
|
||||
transition: transform var(--hive-ease-overshoot-a-bit) 500ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +110,7 @@ export function Slider({ counter, className, deadZone, style, ...rest }: SliderP
|
|||
) : (
|
||||
<div className="flex w-full">
|
||||
<button
|
||||
className="z-10"
|
||||
className="z-10 my-3"
|
||||
style={{ width: deadZone }}
|
||||
onClick={event => {
|
||||
const input = event.currentTarget.parentElement!.querySelector(
|
||||
|
|
|
|||
41
packages/web/docs/src/easing-functions.css
Normal file
41
packages/web/docs/src/easing-functions.css
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
:root {
|
||||
--hive-ease-overshoot-far: linear(
|
||||
0 0%,
|
||||
0.5007 7.21%,
|
||||
0.7803 12.29%,
|
||||
0.8883 14.93%,
|
||||
0.9724 17.63%,
|
||||
1.0343 20.44%,
|
||||
1.0754 23.44%,
|
||||
1.0898 25.22%,
|
||||
1.0984 27.11%,
|
||||
1.1014 29.15%,
|
||||
1.0989 31.4%,
|
||||
1.0854 35.23%,
|
||||
1.0196 48.86%,
|
||||
1.0043 54.06%,
|
||||
0.9956 59.6%,
|
||||
0.9925 68.11%,
|
||||
1 100%
|
||||
);
|
||||
|
||||
--hive-ease-overshoot-a-bit: linear(
|
||||
0 0%,
|
||||
0.5007 7.21%,
|
||||
0.7803 12.29%,
|
||||
0.8883 14.93%,
|
||||
0.9724 17.63%,
|
||||
1.011319 20.44%,
|
||||
1.024882 23.44%,
|
||||
1.029634 25.22%,
|
||||
1.032472 27.11%,
|
||||
1.033462 29.15%,
|
||||
1.032637 31.4%,
|
||||
1.028182 35.23%,
|
||||
1.006468 48.86%,
|
||||
1.001419 54.06%,
|
||||
0.9956 59.6%,
|
||||
0.9925 68.11%,
|
||||
1 100%
|
||||
);
|
||||
}
|
||||
|
|
@ -2038,7 +2038,7 @@ importers:
|
|||
version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@theguild/components':
|
||||
specifier: 9.3.4
|
||||
version: 9.3.4(@theguild/tailwind-config@0.6.2(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.1.6(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0))
|
||||
version: 9.3.4(@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.1.6(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0))
|
||||
date-fns:
|
||||
specifier: 4.1.0
|
||||
version: 4.1.0
|
||||
|
|
@ -2068,8 +2068,8 @@ importers:
|
|||
specifier: 0.5.16
|
||||
version: 0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))
|
||||
'@theguild/tailwind-config':
|
||||
specifier: 0.6.2
|
||||
version: 0.6.2(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))
|
||||
specifier: 0.6.3
|
||||
version: 0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))
|
||||
'@types/react':
|
||||
specifier: 18.3.18
|
||||
version: 18.3.18
|
||||
|
|
@ -7525,8 +7525,8 @@ packages:
|
|||
'@theguild/remark-npm2yarn@0.3.2':
|
||||
resolution: {integrity: sha512-H9T/GOuS/+4H7AY1cfD5DJIIIcGIIw1zMCB8OeTgXk7azJULsnuOurZ/CR54rvuTD+Krx0MVQccaUCvCWfP+vw==}
|
||||
|
||||
'@theguild/tailwind-config@0.6.2':
|
||||
resolution: {integrity: sha512-dl0P3qDjj8Us0PSuPQKE+N1ZJXjuhr4uWi66pdTy24PdSNODFSBe7YgK87/UOfDUCDNnImRgt3Do6uAYsP46oA==}
|
||||
'@theguild/tailwind-config@0.6.3':
|
||||
resolution: {integrity: sha512-kmHHBnGRPiFtyBNuWCuKdC+kymh16OuFTD+R/Yb3yo+mNFJlOzGYdcu2ay6g1M+9+OlwSffifD2Zo97AZUwaSg==}
|
||||
peerDependencies:
|
||||
postcss-import: ^16.1.0
|
||||
postcss-lightningcss: ^1.0.1
|
||||
|
|
@ -23852,14 +23852,14 @@ snapshots:
|
|||
typescript: 4.9.5
|
||||
yargs: 16.2.0
|
||||
|
||||
'@theguild/components@9.3.4(@theguild/tailwind-config@0.6.2(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.1.6(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0))':
|
||||
'@theguild/components@9.3.4(@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.1.6(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0))':
|
||||
dependencies:
|
||||
'@giscus/react': 3.1.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@next/bundle-analyzer': 15.1.5
|
||||
'@radix-ui/react-accordion': 1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-icons': 1.3.2(react@19.0.0)
|
||||
'@radix-ui/react-navigation-menu': 1.2.0(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@theguild/tailwind-config': 0.6.2(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))
|
||||
'@theguild/tailwind-config': 0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))
|
||||
clsx: 2.1.1
|
||||
fuzzy: 0.1.3
|
||||
next: 15.1.6(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
|
|
@ -23955,7 +23955,7 @@ snapshots:
|
|||
npm-to-yarn: 3.0.0
|
||||
unist-util-visit: 5.0.0
|
||||
|
||||
'@theguild/tailwind-config@0.6.2(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))':
|
||||
'@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))':
|
||||
dependencies:
|
||||
'@tailwindcss/container-queries': 0.1.1(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3)))
|
||||
postcss-import: 16.1.0(postcss@8.4.49)
|
||||
|
|
|
|||
Loading…
Reference in a new issue