From 6a8455e7b8f6db3d4e041fce3d9ec1e096909e7f Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Mon, 9 Mar 2026 15:58:39 -0400 Subject: [PATCH] more nits --- .../(home)/_components/CommunitySection.tsx | 8 +- .../_components/CustomerStoriesSection.tsx | 199 +++++++------- .../_components/DashboardFeaturesSection.tsx | 6 +- .../(home)/_components/FrameworksSection.tsx | 257 +++++------------- .../_components/FrameworksSectionClient.tsx | 94 +++++++ .../app/(home)/_components/HomeContent.tsx | 57 ++++ apps/www/app/(home)/_components/LogosGrid.tsx | 8 +- .../(home)/_components/OpenSourceSection.tsx | 21 +- apps/www/app/(home)/page.tsx | 58 +--- apps/www/components/Panel/Panel.tsx | 8 +- apps/www/components/Products/index.tsx | 2 + apps/www/package.json | 1 + .../images/customers/logos/chatbase-icon.svg | 5 + .../images/customers/logos/hyper-icon.svg | 6 + .../customers/logos/markprompt-icon.svg | 6 + pnpm-lock.yaml | 93 +++++++ 16 files changed, 462 insertions(+), 367 deletions(-) create mode 100644 apps/www/app/(home)/_components/FrameworksSectionClient.tsx create mode 100644 apps/www/app/(home)/_components/HomeContent.tsx create mode 100644 apps/www/public/images/customers/logos/chatbase-icon.svg create mode 100644 apps/www/public/images/customers/logos/hyper-icon.svg create mode 100644 apps/www/public/images/customers/logos/markprompt-icon.svg diff --git a/apps/www/app/(home)/_components/CommunitySection.tsx b/apps/www/app/(home)/_components/CommunitySection.tsx index 6f6524de026..718c0f48dd6 100644 --- a/apps/www/app/(home)/_components/CommunitySection.tsx +++ b/apps/www/app/(home)/_components/CommunitySection.tsx @@ -31,7 +31,13 @@ export function CommunitySection() { -
+
{columns.map((col, colIdx) => (
-
+

How industry leaders
{' '} are building with Supabase

-
-
-
- - {/* Cards row */} -
-
-
- {customerStories.map((story, index) => { - const isActive = index === activeIdx - return ( - setActiveIdx(index)} - className={cn( - 'text-left border-r border-border last:border-r-0 p-5 flex flex-col gap-3 overflow-hidden', - isActive ? 'bg-surface-75' : 'items-center hover:bg-surface-75/50' - )} - animate={{ flex: isActive ? '0 0 50%' : '1 1 0%' }} - transition={{ type: 'spring', duration: 0.5, bounce: 0 }} - > - {story.name} - - - {isActive && ( - -

{story.name}

-

- {story.description} -

-

{story.author}

- - View more about {story.name} - -
- )} -
-
- ) - })} -
-
-
- - {/* Footer row */} -
-
-
-

Powering the worlds best brands

+ {/* Cards row */} +
+
+ (i === activeIdx ? '4fr' : '1fr')) + .join(' '), + }} + transition={{ type: 'spring', duration: 0.4, bounce: 0 }} + > + {customerStories.map((story, index) => { + const isActive = index === activeIdx + return ( + + ) + })} + +
+
+ {/* AI Builder stories */}
-
-
+
+

- Learn about + Powering the next wave
- AI Builder stories + of AI builders

+

+ Create full backend experiences in behalf of your users with Supabase for Platforms, + the all-in-one solution for building AI-native platforms and marketplaces. +

+ +
@@ -171,7 +182,7 @@ export function CustomerStoriesSection() { }`, - lang: 'js' as const, + lang: 'javascript' as const, }, { name: 'Next.js', @@ -142,7 +94,7 @@ export default async function Page() { return }`, - lang: 'js' as const, + lang: 'javascript' as const, }, { name: 'Flutter', @@ -183,7 +135,7 @@ final data = await supabase todos = data } `, - lang: 'xml' as const, + lang: 'svelte' as const, }, { name: 'Vue', @@ -205,7 +157,7 @@ onMounted(async () => { todos.value = data }) `, - lang: 'xml' as const, + lang: 'vue' as const, }, { name: 'Nuxt', @@ -227,105 +179,26 @@ const { data: todos } = await useAsyncData( `, - lang: 'xml' as const, + lang: 'vue' as const, }, ] -export function FrameworksSection() { - const { resolvedTheme } = useTheme() - const isDark = resolvedTheme?.includes('dark') ?? false - const [activeIdx, setActiveIdx] = useState(0) - const [mounted, setMounted] = useState(false) - const active = frameworksList[activeIdx] +// ── Server component: pre-highlights all code ─────────────────────────────── - useEffect(() => setMounted(true), []) +export async function FrameworksSection() { + const hl = await createHighlighter({ + themes: [supabaseDark, supabaseLight], + langs: ['javascript', 'dart', 'svelte', 'vue'], + }) - return ( -
-
-
- {/* Left: title */} -
-
- Use Supabase with{' '} - - - - {active.name} - - - -
-
+ const frameworks = frameworksList.map((fw) => ({ + name: fw.name, + icon: fw.icon, + darkHtml: hl.codeToHtml(fw.code, { lang: fw.lang, theme: 'supabase-dark' }), + lightHtml: hl.codeToHtml(fw.code, { lang: fw.lang, theme: 'supabase-light' }), + })) - {/* Right: icon tabs + code */} -
- {/* 6-col icon row */} -
- {frameworksList.map((framework, index) => ( - - ))} -
+ hl.dispose() - {/* Code area */} -
- {mounted && ( - - - {/* @ts-ignore */} - - {active.code} - - - - )} -
-
-
-
-
- ) + return } diff --git a/apps/www/app/(home)/_components/FrameworksSectionClient.tsx b/apps/www/app/(home)/_components/FrameworksSectionClient.tsx new file mode 100644 index 00000000000..ec547269f60 --- /dev/null +++ b/apps/www/app/(home)/_components/FrameworksSectionClient.tsx @@ -0,0 +1,94 @@ +'use client' + +import { AnimatePresence, motion } from 'framer-motion' +import { useState } from 'react' +import { cn } from 'ui' + +type Framework = { + name: string + icon: string + darkHtml: string + lightHtml: string +} + +export function FrameworksSectionClient({ frameworks }: { frameworks: Framework[] }) { + const [activeIdx, setActiveIdx] = useState(0) + const active = frameworks[activeIdx] + + return ( +
+
+
+ {/* Left: title */} +
+
+ Use Supabase with{' '} + + + + {active.name} + + + +
+
+ + {/* Right: icon tabs + code */} +
+ {/* 6-col icon row */} +
+ {frameworks.map((framework, index) => ( + + ))} +
+ + {/* Code area */} +
+ + +
+ + +
+
+
+
+
+ ) +} diff --git a/apps/www/app/(home)/_components/HomeContent.tsx b/apps/www/app/(home)/_components/HomeContent.tsx new file mode 100644 index 00000000000..b722cf49cbc --- /dev/null +++ b/apps/www/app/(home)/_components/HomeContent.tsx @@ -0,0 +1,57 @@ +'use client' + +import dynamic from 'next/dynamic' +import getContent from '~/data/home/content' +import { BuiltWithSupabaseSection } from './BuiltWithSupabaseSection' +import { CommunitySection } from './CommunitySection' +import { CTASection } from './CTASection' +import { CustomerStoriesSection } from './CustomerStoriesSection' +import { DashboardFeaturesSection } from './DashboardFeaturesSection' +import { Hero } from './Hero' +import { LogosGrid } from './LogosGrid' +import { OpenSourceSection } from './OpenSourceSection' + +const Products = dynamic(() => import('~/components/Products/index')) + +function SectionBlock({ children, className }: { children: React.ReactNode; className?: string }) { + return
{children}
+} + +export function HomeContent({ frameworksSlot }: { frameworksSlot: React.ReactNode }) { + const content = getContent() + + return ( +
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
{frameworksSlot}
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ ) +} diff --git a/apps/www/app/(home)/_components/LogosGrid.tsx b/apps/www/app/(home)/_components/LogosGrid.tsx index c2270fad973..ccea5bfcf8a 100644 --- a/apps/www/app/(home)/_components/LogosGrid.tsx +++ b/apps/www/app/(home)/_components/LogosGrid.tsx @@ -1,6 +1,5 @@ -'use client' - import { ComponentProps } from 'react' + import { BetasharesLogo, BoltLogo, @@ -68,7 +67,10 @@ export function LogosGrid() {
{gridLogos.map(({ name, Logo }) => ( -
+
))} diff --git a/apps/www/app/(home)/_components/OpenSourceSection.tsx b/apps/www/app/(home)/_components/OpenSourceSection.tsx index ccff983a1f5..a6e40bf1df0 100644 --- a/apps/www/app/(home)/_components/OpenSourceSection.tsx +++ b/apps/www/app/(home)/_components/OpenSourceSection.tsx @@ -1,10 +1,10 @@ 'use client' -import { animate, motion, useInView } from 'framer-motion' -import Link from 'next/link' -import { useEffect, useRef, useState } from 'react' -import { Button } from 'ui' import { useSendTelemetryEvent } from '~/lib/telemetry' +import { motion, useInView } from 'framer-motion' +import Link from 'next/link' +import { useRef } from 'react' +import { Button } from 'ui' // ── Contribution graph ────────────────────────────────────────────────────── @@ -85,23 +85,14 @@ export function OpenSourceSection() { const inView = useInView(panelRef, { once: true, amount: 0.4 }) const sendTelemetryEvent = useSendTelemetryEvent() - const [stars, setStars] = useState('0.0K') - useEffect(() => { - if (!inView) return - const controls = animate(0, 98400, { - duration: 2.2, - ease: 'easeOut', - onUpdate: (v: number) => setStars(`${(v / 1000).toFixed(1)}K`), - }) - return controls.stop - }, [inView]) + const stars = '98.4K' return (
{/* Main content — spans 2 cols */} -
+

Open source from day one

diff --git a/apps/www/app/(home)/page.tsx b/apps/www/app/(home)/page.tsx index 05ceb769633..6fd479bb6b8 100644 --- a/apps/www/app/(home)/page.tsx +++ b/apps/www/app/(home)/page.tsx @@ -1,60 +1,6 @@ -'use client' - -import dynamic from 'next/dynamic' -import getContent from '~/data/home/content' -import { BuiltWithSupabaseSection } from './_components/BuiltWithSupabaseSection' -import { CommunitySection } from './_components/CommunitySection' -import { CTASection } from './_components/CTASection' -import { CustomerStoriesSection } from './_components/CustomerStoriesSection' -import { DashboardFeaturesSection } from './_components/DashboardFeaturesSection' import { FrameworksSection } from './_components/FrameworksSection' -import { Hero } from './_components/Hero' -import { LogosGrid } from './_components/LogosGrid' -import { OpenSourceSection } from './_components/OpenSourceSection' - -const Products = dynamic(() => import('~/components/Products/index')) - -function SectionBlock({ children, className }: { children: React.ReactNode; className?: string }) { - return

{children}
-} +import { HomeContent } from './_components/HomeContent' export default function HomePage() { - const content = getContent() - - return ( -
-
- -
- -
- -
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- ) + return } /> } diff --git a/apps/www/components/Panel/Panel.tsx b/apps/www/components/Panel/Panel.tsx index 95b320ade41..787a151c5cd 100644 --- a/apps/www/components/Panel/Panel.tsx +++ b/apps/www/components/Panel/Panel.tsx @@ -1,7 +1,9 @@ -import React, { PropsWithChildren, useEffect, useRef } from 'react' -import { motion } from 'framer-motion' -import { cn } from 'ui' +'use client' + import { detectBrowser, isBrowser } from 'common' +import { motion } from 'framer-motion' +import React, { PropsWithChildren, useEffect, useRef } from 'react' +import { cn } from 'ui' interface Props { outerClassName?: string diff --git a/apps/www/components/Products/index.tsx b/apps/www/components/Products/index.tsx index d2a23e446a9..8fc5f1bc5b8 100644 --- a/apps/www/components/Products/index.tsx +++ b/apps/www/components/Products/index.tsx @@ -1,3 +1,5 @@ +'use client' + import SectionContainer from '~/components/Layouts/SectionContainer' import type { ProductType } from '~/data/MainProducts' import { useSendTelemetryEvent } from '~/lib/telemetry' diff --git a/apps/www/package.json b/apps/www/package.json index 6df69f53cf0..ef089bd4f73 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -83,6 +83,7 @@ "remark": "^15.0.1", "remark-gfm": "^4.0.1", "shared-data": "workspace:*", + "shiki": "^4.0.1", "swiper": "^12.1.2", "typed.js": "^2.0.16", "typescript": "catalog:", diff --git a/apps/www/public/images/customers/logos/chatbase-icon.svg b/apps/www/public/images/customers/logos/chatbase-icon.svg new file mode 100644 index 00000000000..e772486c2a6 --- /dev/null +++ b/apps/www/public/images/customers/logos/chatbase-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/www/public/images/customers/logos/hyper-icon.svg b/apps/www/public/images/customers/logos/hyper-icon.svg new file mode 100644 index 00000000000..790af453eec --- /dev/null +++ b/apps/www/public/images/customers/logos/hyper-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/www/public/images/customers/logos/markprompt-icon.svg b/apps/www/public/images/customers/logos/markprompt-icon.svg new file mode 100644 index 00000000000..0e288cb1d8e --- /dev/null +++ b/apps/www/public/images/customers/logos/markprompt-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3b275f3579..c5668a8e752 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1751,6 +1751,9 @@ importers: shared-data: specifier: workspace:* version: link:../../packages/shared-data + shiki: + specifier: ^4.0.1 + version: 4.0.1 swiper: specifier: ^12.1.2 version: 12.1.2 @@ -7937,24 +7940,52 @@ packages: '@shikijs/core@3.13.0': resolution: {integrity: sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==} + '@shikijs/core@4.0.1': + resolution: {integrity: sha512-vWvqi9JNgz1dRL9Nvog5wtx7RuNkf7MEPl2mU/cyUUxJeH1CAr3t+81h8zO8zs7DK6cKLMoU9TvukWIDjP4Lzg==} + engines: {node: '>=20'} + '@shikijs/engine-javascript@3.13.0': resolution: {integrity: sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==} + '@shikijs/engine-javascript@4.0.1': + resolution: {integrity: sha512-DJK9NiwtGYqMuKCRO4Ip0FKNDQpmaiS+K5bFjJ7DWFn4zHueDWgaUG8kAofkrnXF6zPPYYQY7J5FYVW9MbZyBg==} + engines: {node: '>=20'} + '@shikijs/engine-oniguruma@3.13.0': resolution: {integrity: sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==} + '@shikijs/engine-oniguruma@4.0.1': + resolution: {integrity: sha512-oCWdCTDch3J8Kc0OZJ98KuUPC02O1VqIE3W/e2uvrHqTxYRR21RGEJMtchrgrxhsoJJCzmIciKsqG+q/yD+Cxg==} + engines: {node: '>=20'} + '@shikijs/langs@3.13.0': resolution: {integrity: sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==} + '@shikijs/langs@4.0.1': + resolution: {integrity: sha512-v/mluaybWdnGJR4GqAR6zh8qAZohW9k+cGYT28Y7M8+jLbC0l4yG085O1A+WkseHTn+awd+P3UBymb2+MXFc8w==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.0.1': + resolution: {integrity: sha512-ns0hHZc5eWZuvuIEJz2pTx3Qecz0aRVYumVQJ8JgWY2tq/dH8WxdcVM49Fc2NsHEILNIT6vfdW9MF26RANWiTA==} + engines: {node: '>=20'} + '@shikijs/themes@3.13.0': resolution: {integrity: sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==} + '@shikijs/themes@4.0.1': + resolution: {integrity: sha512-FW41C/D6j/yKQkzVdjrRPiJCtgeDaYRJFEyCKFCINuRJRj9WcmubhP4KQHPZ4+9eT87jruSrYPyoblNRyDFzvA==} + engines: {node: '>=20'} + '@shikijs/transformers@1.6.0': resolution: {integrity: sha512-qGfHe1ECiqfE2STPWvfogIj/9Q0SK+MCRJdoITkW7AmFuB7DmbFnBT2US84+zklJOB51MzNO8RUXZiauWssJlQ==} '@shikijs/types@3.13.0': resolution: {integrity: sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==} + '@shikijs/types@4.0.1': + resolution: {integrity: sha512-EaygPEn57+jJ76mw+nTLvIpJMAcMPokFbrF8lufsZP7Ukk+ToJYEcswN1G0e49nUZAq7aCQtoeW219A8HK1ZOw==} + engines: {node: '>=20'} + '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -14880,6 +14911,9 @@ packages: oniguruma-to-es@4.3.3: resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + oniguruma-to-es@4.3.4: + resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + oo-ascii-tree@1.98.0: resolution: {integrity: sha512-+GE7ywhtS6MctbfcO+vZzqIxcFzucZCwmawcwCVo89DxQDakV1JFfFViTXG4A90UzTAsU4tQteGmwDtwOlOXLw==} engines: {node: '>= 14.17.0'} @@ -16726,6 +16760,10 @@ packages: shiki@3.13.0: resolution: {integrity: sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==} + shiki@4.0.1: + resolution: {integrity: sha512-EkAEhDTN5WhpoQFXFw79OHIrSAfHhlImeCdSyg4u4XvrpxKEmdo/9x/HWSowujAnUrFsGOwWiE58a6GVentMnQ==} + engines: {node: '>=20'} + should-equal@2.0.0: resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} @@ -25119,25 +25157,58 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 + '@shikijs/core@4.0.1': + dependencies: + '@shikijs/primitive': 4.0.1 + '@shikijs/types': 4.0.1 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + '@shikijs/engine-javascript@3.13.0': dependencies: '@shikijs/types': 3.13.0 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.3 + '@shikijs/engine-javascript@4.0.1': + dependencies: + '@shikijs/types': 4.0.1 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 + '@shikijs/engine-oniguruma@3.13.0': dependencies: '@shikijs/types': 3.13.0 '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/engine-oniguruma@4.0.1': + dependencies: + '@shikijs/types': 4.0.1 + '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/langs@3.13.0': dependencies: '@shikijs/types': 3.13.0 + '@shikijs/langs@4.0.1': + dependencies: + '@shikijs/types': 4.0.1 + + '@shikijs/primitive@4.0.1': + dependencies: + '@shikijs/types': 4.0.1 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + '@shikijs/themes@3.13.0': dependencies: '@shikijs/types': 3.13.0 + '@shikijs/themes@4.0.1': + dependencies: + '@shikijs/types': 4.0.1 + '@shikijs/transformers@1.6.0': dependencies: shiki: 1.6.0 @@ -25147,6 +25218,11 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + '@shikijs/types@4.0.1': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + '@shikijs/vscode-textmate@10.0.2': {} '@shuding/opentype.js@1.4.0-beta.0': @@ -33806,6 +33882,12 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 + oniguruma-to-es@4.3.4: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + oo-ascii-tree@1.98.0: {} open@10.2.0: @@ -36052,6 +36134,17 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + shiki@4.0.1: + dependencies: + '@shikijs/core': 4.0.1 + '@shikijs/engine-javascript': 4.0.1 + '@shikijs/engine-oniguruma': 4.0.1 + '@shikijs/langs': 4.0.1 + '@shikijs/themes': 4.0.1 + '@shikijs/types': 4.0.1 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + should-equal@2.0.0: dependencies: should-type: 1.4.0