Skip to content

Commit f3d6194

Browse files
committed
Squashed commit of the following:
commit bbf390b6917aaf89692dc20d5a4727c5b5970c04 Author: Tanner Linsley <[email protected]> Date: Tue Aug 12 13:34:52 2025 -0600 done commit fb94aa3b66cad69d3125a1acc5206405ee767b3e Author: Tanner Linsley <[email protected]> Date: Mon Aug 11 11:26:43 2025 -0600 refactor checkpoint
1 parent f564338 commit f3d6194

20 files changed

+914
-1408
lines changed

src/components/BottomCTA.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as React from 'react'
2+
import type { LinkProps } from '@tanstack/react-router'
3+
import { Link } from '@tanstack/react-router'
4+
import { twMerge } from 'tailwind-merge'
5+
6+
type BottomCTAProps = {
7+
linkProps: Omit<LinkProps, 'className' | 'children'>
8+
label?: string
9+
className?: string
10+
}
11+
12+
export function BottomCTA({
13+
linkProps,
14+
label = 'Get Started!',
15+
className,
16+
}: BottomCTAProps) {
17+
return (
18+
<div className="flex flex-col gap-4 items-center">
19+
<div className="font-extrabold text-xl lg:text-2xl">
20+
Wow, you've come a long way!
21+
</div>
22+
<div className="italic font-sm opacity-70">
23+
Only one thing left to do...
24+
</div>
25+
<div>
26+
<Link
27+
{...linkProps}
28+
className={twMerge(
29+
'inline-block py-2 px-4 rounded uppercase font-extrabold transition-colors',
30+
className
31+
)}
32+
>
33+
{label}
34+
</Link>
35+
</div>
36+
</div>
37+
)
38+
}

src/components/FeatureGrid.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as React from 'react'
2+
import { FaCheckCircle } from 'react-icons/fa'
3+
4+
type FeatureGridProps = {
5+
title?: string
6+
items: React.ReactNode[]
7+
gridClassName?: string
8+
}
9+
10+
export function FeatureGrid({ title, items, gridClassName }: FeatureGridProps) {
11+
return (
12+
<div className="px-4 sm:px-6 lg:px-8 mx-auto">
13+
{title ? (
14+
<div className=" sm:text-center pb-16">
15+
<h3 className="text-3xl text-center mx-auto leading-tight font-extrabold tracking-tight sm:text-4xl lg:leading-none mt-2">
16+
{title}
17+
</h3>
18+
</div>
19+
) : null}
20+
<div
21+
className={
22+
gridClassName ||
23+
'grid grid-flow-row grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-4 mx-auto'
24+
}
25+
>
26+
{items.map((d, i) => (
27+
<span key={i} className="flex items-center gap-2">
28+
<FaCheckCircle className="text-green-500 " /> {d}
29+
</span>
30+
))}
31+
</div>
32+
</div>
33+
)
34+
}

src/components/LibraryHero.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as React from 'react'
2+
import { twMerge } from 'tailwind-merge'
3+
import { Link } from '@tanstack/react-router'
4+
5+
type LibraryHeroProps = {
6+
libraryName: string // e.g., "Query"
7+
gradientFrom: string
8+
gradientTo: string
9+
subtitle: React.ReactNode
10+
description?: React.ReactNode
11+
cta?: {
12+
linkProps: React.ComponentProps<typeof Link>
13+
label: string
14+
className?: string
15+
}
16+
statusBadge?: string
17+
}
18+
19+
export function LibraryHero({
20+
libraryName,
21+
gradientFrom,
22+
gradientTo,
23+
subtitle,
24+
description,
25+
cta,
26+
statusBadge,
27+
}: LibraryHeroProps) {
28+
const gradientText = `pr-1 inline-block text-transparent bg-clip-text bg-gradient-to-r ${gradientFrom} ${gradientTo}`
29+
30+
return (
31+
<div className="flex flex-col items-center gap-8 text-center px-4">
32+
<h1 className="font-black flex gap-3 items-center text-4xl md:text-6xl lg:text-7xl xl:text-8xl uppercase [letter-spacing:-.05em]">
33+
<span>TanStack</span>
34+
<span className={twMerge(gradientText)}>{libraryName}</span>
35+
</h1>
36+
{statusBadge ? (
37+
<div
38+
className={twMerge(
39+
'text-sm md:text-base font-black lg:text-lg align-super text-white animate-bounce uppercase',
40+
'dark:text-black bg-black dark:bg-white shadow-xl shadow-black/30 px-2 py-1 rounded-md',
41+
'leading-none whitespace-nowrap'
42+
)}
43+
>
44+
STATUS: {statusBadge}
45+
</div>
46+
) : null}
47+
<h2 className="font-bold text-2xl max-w-md md:text-3xl lg:text-5xl lg:max-w-2xl">
48+
{subtitle}
49+
</h2>
50+
{description ? (
51+
<p className="text opacity-90 max-w-sm lg:text-xl lg:max-w-2xl">
52+
{description}
53+
</p>
54+
) : null}
55+
{cta ? (
56+
<Link
57+
{...cta.linkProps}
58+
className={twMerge(
59+
'inline-block py-2 px-4 rounded uppercase font-extrabold transition-colors',
60+
cta.className
61+
)}
62+
>
63+
{cta.label}
64+
</Link>
65+
) : null}
66+
</div>
67+
)
68+
}

src/components/PartnersSection.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as React from 'react'
2+
import { Link } from '@tanstack/react-router'
3+
import { partners } from '~/utils/partners'
4+
5+
type PartnersSectionProps = {
6+
libraryId?: string
7+
title?: string
8+
gridClassName?: string
9+
showPreviousLink?: boolean
10+
}
11+
12+
export function PartnersSection({
13+
libraryId,
14+
title = 'Partners',
15+
gridClassName = 'grid grid-cols-1 gap-6 sm:grid-cols-2',
16+
showPreviousLink = true,
17+
}: PartnersSectionProps) {
18+
const filtered = partners.filter((p) =>
19+
libraryId
20+
? p.libraries?.includes(libraryId as any) && p.status === 'active'
21+
: p.status === 'active'
22+
)
23+
24+
return (
25+
<div className="px-4 lg:max-w-screen-lg md:mx-auto mx-auto">
26+
<h3 className="text-center text-3xl leading-8 font-extrabold tracking-tight sm:text-4xl sm:leading-10 lg:leading-none mt-8">
27+
{title}
28+
</h3>
29+
<div className="h-8" />
30+
<div className={gridClassName}>
31+
{filtered.map((partner) => (
32+
<a
33+
key={partner.name}
34+
href={partner.href}
35+
target="_blank"
36+
className="shadow-xl shadow-gray-500/20 rounded-lg dark:border border-gray-500/20 bg-white dark:bg-black/40 dark:shadow-none group overflow-hidden grid"
37+
rel="noreferrer"
38+
>
39+
<div className="z-0 row-start-1 col-start-1 flex items-center justify-center group-hover:blur-sm transition-all duration-200">
40+
{partner.homepageImg}
41+
</div>
42+
<div className="z-10 row-start-1 col-start-1 max-w-full p-4 text-sm flex flex-col gap-4 items-start opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-white/70 dark:bg-gray-800/70">
43+
{partner.content}
44+
</div>
45+
</a>
46+
))}
47+
</div>
48+
{showPreviousLink ? (
49+
<div className="text-center mt-6">
50+
<Link
51+
to="/partners"
52+
search={
53+
libraryId
54+
? { libraries: [libraryId], status: 'inactive' }
55+
: { status: 'inactive' }
56+
}
57+
className="inline-flex items-center text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors"
58+
>
59+
View Previous Partners →
60+
</Link>
61+
</div>
62+
) : null}
63+
</div>
64+
)
65+
}

src/components/SponsorsSection.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import * as React from 'react'
2+
import { Await } from '@tanstack/react-router'
3+
import { CgSpinner } from 'react-icons/cg'
4+
import SponsorPack from '~/components/SponsorPack'
5+
import { twMerge } from 'tailwind-merge'
6+
7+
type SponsorsSectionProps = {
8+
sponsorsPromise: Promise<any>
9+
title?: string
10+
aspectRatio?: string
11+
showCTA?: boolean
12+
ctaClassName?: string
13+
}
14+
15+
export function SponsorsSection({
16+
sponsorsPromise,
17+
title = 'Sponsors',
18+
aspectRatio = '1/1',
19+
showCTA = true,
20+
ctaClassName = 'bg-emerald-500 text-white',
21+
}: SponsorsSectionProps) {
22+
return (
23+
<div className="relative text-lg overflow-hidden">
24+
<h3 className="text-center text-3xl leading-8 font-extrabold tracking-tight sm:text-4xl sm:leading-10 lg:leading-none mt-8">
25+
{title}
26+
</h3>
27+
<div
28+
className="my-4 flex flex-wrap mx-auto max-w-screen-lg"
29+
style={{ aspectRatio }}
30+
>
31+
<Await
32+
promise={sponsorsPromise}
33+
fallback={<CgSpinner className="text-2xl animate-spin" />}
34+
>
35+
{(sponsors: any) => <SponsorPack sponsors={sponsors} />}
36+
</Await>
37+
</div>
38+
{showCTA ? (
39+
<div className="text-center">
40+
<a
41+
href="https://github.com/sponsors/tannerlinsley"
42+
className={twMerge(
43+
'inline-block py-2 px-4 rounded uppercase font-extrabold transition-colors',
44+
ctaClassName
45+
)}
46+
>
47+
Become a Sponsor!
48+
</a>
49+
</div>
50+
) : null}
51+
</div>
52+
)
53+
}

src/components/StackBlitzEmbed.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from 'react'
2+
import { useIsDark } from '~/hooks/useIsDark'
3+
4+
type StackBlitzEmbedProps = {
5+
repo: string
6+
branch: string
7+
examplePath: string
8+
file?: string
9+
preset?: 'node'
10+
height?: string | number
11+
title?: string
12+
}
13+
14+
export function StackBlitzEmbed({
15+
repo,
16+
branch,
17+
examplePath,
18+
file,
19+
preset = 'node',
20+
height = '80vh',
21+
title,
22+
}: StackBlitzEmbedProps) {
23+
const isDark = useIsDark()
24+
const themeParam = isDark ? 'dark' : 'light'
25+
const fileParam = file ? `&file=${encodeURIComponent(file)}` : ''
26+
27+
const src = `https://stackblitz.com/github/${repo}/tree/${branch}/${examplePath}?embed=1&theme=${themeParam}&preset=${preset}${fileParam}`
28+
29+
return (
30+
<iframe
31+
key={`${examplePath}-${themeParam}`}
32+
src={src}
33+
title={title || `${repo}: ${examplePath}`}
34+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
35+
className="shadow-2xl"
36+
loading="lazy"
37+
style={{ width: '100%', height, border: '0' }}
38+
/>
39+
)
40+
}

src/components/TrustedByMarquee.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as React from 'react'
2+
import { twMerge } from 'tailwind-merge'
3+
4+
type TrustedByMarqueeProps = {
5+
brands: string[]
6+
speed?: number // pixels per second
7+
className?: string
8+
}
9+
10+
export function TrustedByMarquee({
11+
brands,
12+
speed = 40,
13+
className,
14+
}: TrustedByMarqueeProps) {
15+
const animationDuration = `${(brands.length * 150) / speed}s`
16+
17+
return (
18+
<div className={twMerge('overflow-hidden w-full', className)}>
19+
<div className="uppercase tracking-wider text-sm font-semibold text-center text-gray-400 mb-3">
20+
Trusted in Production by
21+
</div>
22+
<div className="relative w-full">
23+
<div
24+
className="flex gap-6 items-center text-3xl font-bold whitespace-nowrap will-change-transform animate-[marquee_linear_infinite]"
25+
style={{
26+
animationDuration,
27+
}}
28+
>
29+
{[...brands, ...brands, ...brands].map((d, i) => (
30+
<span key={i} className="opacity-70 even:opacity-40">
31+
{d}
32+
</span>
33+
))}
34+
</div>
35+
</div>
36+
<style
37+
dangerouslySetInnerHTML={{
38+
__html: `
39+
@keyframes marquee { 0% { transform: translateX(0); } 100% { transform: translateX(-33.333%); } }
40+
.animate-\[marquee_linear_infinite\] { animation-name: marquee; animation-timing-function: linear; animation-iteration-count: infinite; }
41+
`,
42+
}}
43+
/>
44+
</div>
45+
)
46+
}

src/hooks/useIsDark.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { useTheme } from '~/components/ThemeProvider'
2+
3+
export function useIsDark(): boolean {
4+
const { resolvedTheme } = useTheme()
5+
return resolvedTheme === 'dark'
6+
}

0 commit comments

Comments
 (0)