From 64efdf408f028147c5ca4fe681541c5eaa0cf912 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Thu, 15 Oct 2026 14:10:54 +0200 Subject: [PATCH] feat(ai): rotate build targets in the AI landing headline --- src/components/landing/AiLanding.tsx | 73 ++++++++++++++++++++++- src/components/landing/LibraryLanding.tsx | 2 +- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/components/landing/AiLanding.tsx b/src/components/landing/AiLanding.tsx index cd5688eb5..c476a9f69 100644 --- a/src/components/landing/AiLanding.tsx +++ b/src/components/landing/AiLanding.tsx @@ -23,6 +23,7 @@ import { import { getLibrary } from '~/libraries' import { CodeBlock } from '~/components/markdown/CodeBlock' +import { usePrefersReducedMotion } from '~/utils/usePrefersReducedMotion' import { AiCampaignHero } from './AiCampaignHero' import { LandingPromptBox } from './LandingPromptBox' import { @@ -41,7 +42,7 @@ export default function AiLanding() { return ( } description="TanStack AI is a TypeScript library for building AI features and agents. It ships the agent loop, provider adapters, durability, interrupts, sandboxes, and tools, and plugs into the server, database, and UI you already have." hero={} beforeActions={} @@ -189,6 +190,76 @@ export default function AiLanding() { ) } +const buildTargets = [ + 'chat endpoints', + 'custom harnesses', + 'CI agents', + 'agent frameworks', + 'support bots', + 'coding agents', + 'voice assistants', + 'background agents', + 'MCP servers', + 'image and video tools', +] +const baseTargets = ['database', 'stack', 'API'] + +function RotatingWord({ + delay = 0, + words, +}: { + delay?: number + words: Array +}) { + const [index, setIndex] = React.useState(0) + const reducedMotion = usePrefersReducedMotion() + + React.useEffect(() => { + if (reducedMotion !== false) { + return + } + let intervalId = 0 + // Offset start so the two words never swap at the same moment. + const timeoutId = window.setTimeout(() => { + setIndex((current) => (current + 1) % words.length) + intervalId = window.setInterval( + () => setIndex((current) => (current + 1) % words.length), + 3200, + ) + }, 3200 + delay) + + return () => { + window.clearTimeout(timeoutId) + window.clearInterval(intervalId) + } + }, [delay, reducedMotion, words.length]) + + return ( + + {words[index]} + + ) +} + +function PrimitivesHeadline() { + return ( + <> + + We give you the primitives to build {buildTargets.join(', ')} on top of + your {baseTargets.join(', ')}. + + + + ) +} + // Paper well in light, #111 in dark. Same --th-* tokens as CodeBlock. const codeWellClass = 'm-0 min-w-0 overflow-hidden rounded-lg border-0 bg-gray-100 [--th-background:var(--color-gray-100)] dark:bg-ds-neutral-500 dark:[--th-background:var(--color-ds-neutral-500)] [&_pre]:overflow-x-auto [&_pre]:rounded-none [&_pre]:p-4 [&_pre]:text-xs [&_pre]:leading-6' diff --git a/src/components/landing/LibraryLanding.tsx b/src/components/landing/LibraryLanding.tsx index 833403c19..6485911e7 100644 --- a/src/components/landing/LibraryLanding.tsx +++ b/src/components/landing/LibraryLanding.tsx @@ -82,7 +82,7 @@ export type LibraryLandingConfig = { export type LibraryLandingShellProps = { children: React.ReactNode description: React.ReactNode - headline: string + headline: React.ReactNode hero?: React.ReactNode heroBackground?: React.ReactNode beforeActions?: React.ReactNode