Navbar update (#17756)

* update desktop and mobile www navbar
This commit is contained in:
Francesco Sansalvadore authored and GitHub committed 2023-10-09 18:49:52 +02:00
1 parent ae4a06f715
commit 655893e501
30 files changed
+1068 -922

No files matched your search

-1
View File
@@ -1,4 +1,3 @@
import { useEffect } from 'react'
import Nav from 'components/Nav/index'
import Footer from 'components/Footer/index'
-95
View File
@@ -1,95 +0,0 @@
import React from 'react'
import CompanyData from 'data/Company.json'
import BlogPostsData from 'data/BlogPosts.json'
type Props = {
text: string
description?: string
url?: string
icon?: string
}
const Company = () => {
const iconSections = Object.values(CompanyData).map((company: Props) => {
const { text, description, url, icon } = company
const content = (
<a
href="#"
className="dark:hover:bg-dark-700 -m-3 flex items-start rounded-lg p-3 transition duration-150 ease-in-out hover:bg-gray-50"
>
{/* <!-- Heroicon name: support --> */}
<svg
className="stroke-gray h-6 w-6 flex-shrink-0 dark:stroke-white "
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={icon} />
</svg>
<div className="ml-4">
<p className="text-base font-medium text-gray-900 dark:text-white">{text}</p>
<p className="dark:text-dark-300 mt-1 text-sm text-gray-500">{description}</p>
</div>
</a>
)
return (
<a
key={text}
href={url}
className="-m-3 flex flex-col justify-between rounded-lg p-3 transition duration-150 ease-in-out"
>
{content}
</a>
)
})
return (
<React.Fragment>
<nav
className="grid gap-y-10 px-4 py-8 sm:grid-cols-2 sm:gap-x-8 sm:py-12 sm:px-6 lg:px-8 xl:pr-12"
aria-labelledby="solutionsHeading"
>
{iconSections}
</nav>
<div className="px-4 py-8 sm:py-12 sm:px-6 lg:px-8 xl:pl-12">
<div>
<h3 className="dark:text-dark-300 text-sm font-medium uppercase tracking-wide text-gray-500">
Latest blog posts
</h3>
<ul className="mt-6 space-y-6">
{BlogPostsData.map((caseStudy: any, idx: number) => (
<li className="flow-root" key={`flyout_case_${idx}`}>
<a
href={caseStudy.url}
className="dark:hover:bg-dark-600 -m-3 flex rounded-lg py-3 transition duration-150 ease-in-out hover:bg-gray-100"
>
<div className="min-w-0 flex-1 sm:ml-3">
<h4 className="truncate text-base font-medium text-gray-900 dark:text-white">
{caseStudy.title}
</h4>
<p className="dark:text-dark-300 mt-1 text-sm text-gray-500">
{caseStudy.description}
</p>
</div>
</a>
</li>
))}
</ul>
</div>
<div className="mt-6 text-sm font-medium">
<a
href="/blog"
className="dark:text-brand dark:hover:text-brand-400 text-gray-600 transition duration-150 ease-in-out hover:text-gray-500"
>
View all posts <span aria-hidden="true">&rarr;</span>
</a>
</div>
</div>
</React.Fragment>
)
}
export default Company
-102
View File
@@ -1,102 +0,0 @@
import React from 'react'
import { links } from 'data/Developers'
import AnnouncementsData from 'data/Announcements.json'
import { useRouter } from 'next/router'
import Image from 'next/image'
import Link from 'next/link'
import { TextLink } from 'ui'
type Props = {
text: string
description?: string
url?: string
icon?: string
svg?: any
}
const Developers = () => {
const { basePath } = useRouter()
const iconSections = links.map((link: Props) => {
const { text, description, url, icon, svg: Svg } = link
const content = (
<div className="dark:hover:bg-scale-500 -m-3 flex items-start rounded-lg p-3 transition duration-150 ease-in-out hover:bg-gray-50">
{/* <!-- Heroicon name: support --> */}
{icon && (
<svg
className="stroke-scale-900 h-5 w-5 flex-shrink-0"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d={icon} />
</svg>
)}
{Svg && <Svg />}
<div className="ml-4">
<h5 className="text-scale-1200 text-base">{text}</h5>
<p className="text-scale-900 text-sm">{description}</p>
</div>
</div>
)
return url ? (
<Link href={url} key={text}>
<a className="dark:hover:bg-scale-500 col-span-6 rounded p-3 transition hover:bg-gray-50">
{content}
</a>
</Link>
) : (
<div
key={text}
className="-m-3 flex flex-col justify-between rounded-lg p-3 transition duration-150 ease-in-out"
>
{content}
</div>
)
})
return (
<div className="grid grid-cols-12">
<nav className="col-span-6 py-6" aria-labelledby="developersResources">
<div className="m-3 grid grid-cols-12 gap-2 pr-3">{iconSections}</div>
</nav>
<div className="col-span-6 py-8">
<div className="py-3 mx-6">
<p className="p">Latest announcements</p>
<ul className="mt-6 space-y-3">
{AnnouncementsData.map((announcement: any, idx: number) => (
<li className="flow-root" key={`flyout_case_${idx}`}>
<Link href={announcement.url}>
<a className="dark:hover:bg-dark-700 flex items-center rounded-lg border p-3 transition duration-150 ease-in-out hover:bg-gray-100">
<div className="relative hidden h-20 w-32 flex-shrink-0 overflow-auto rounded-md sm:block">
<Image
src={`${basePath}${announcement.imgUrl}`}
alt={announcement.title}
layout="fill"
objectFit="cover"
/>
</div>
<div className="min-w-0 flex-1 sm:ml-4">
<h4 className="text-scale-1200 text-normal mb-0 text-base">
{announcement.title}
</h4>
<p className="p text-sm">{announcement.description}</p>
</div>
</a>
</Link>
</li>
))}
</ul>
<div className="pt-4">
<TextLink url="/blog" label="Explore more" />
</div>
</div>
</div>
</div>
)
}
export default Developers
@@ -0,0 +1,97 @@
import React from 'react'
import Link from 'next/link'
import { IconChevronRight } from 'ui'
import Announcements from '~/data/Announcements.json'
import { data as DevelopersData } from 'data/Developers'
type Props = {
text: string
description?: string
url?: string
icon?: string
svg?: any
}
const DevelopersDropdown = () => {
const iconsWithFill = ['GitHub Discussions', 'Launch Week', 'Open Source']
return (
<div className="flex flex-col xl:flex-row">
<div className="w-[550px] xl:w-[500px] py-8 px-8 bg-background grid gap-3 grid-cols-2">
{DevelopersData['navigation'].map((column) => (
<div key={column.label} className="p-0 flex flex-col gap-6">
<label className="text-lighter text-xs uppercase tracking-widest font-mono">
{column.label}
</label>
<ul className="flex flex-col gap-4">
{column.links.map((link: Props) => (
<li key={link.text}>
<Link href={link.url!}>
<a className="flex group items-center gap-2 text-light text-sm hover:text-foreground focus-visible:text-foreground focus-visible:ring-2 focus-visible:outline-none focus-visible:rounded focus-visible:ring-foreground-lighter">
<svg
className="h-5 w-5"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
{...(iconsWithFill.includes(link.text)
? {
fillRule: 'evenodd',
clipRule: 'evenodd',
fill: 'currentColor',
}
: {
stroke: 'currentColor',
strokeMiterlimit: '10',
strokeLinecap: 'round',
strokeLinejoin: 'round',
})}
d={link.icon}
/>
</svg>
<span>{link.text}</span>
<IconChevronRight
strokeWidth={2}
className="w-3 -ml-1 transition-all will-change-transform -translate-x-1 opacity-0 group-hover:translate-x-0 group-hover:opacity-100"
/>
</a>
</Link>
</li>
))}
</ul>
</div>
))}
</div>
<div className="bg-overlay flex flex-col w-[550px] xl:w-[480px] border-t xl:border-t-0 xl:border-l">
<div className="flex-col gap-2 py-8 px-10">
<Link href="/blog">
<a className="group flex items-center gap-1 text-lighter hover:text-foreground text-xs uppercase tracking-widest font-mono mb-5 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm focus-visible:text-foreground">
<span>Blog</span>
<IconChevronRight className="h-3 w-3 transition-transform will-change-transform -translate-x-1 group-hover:translate-x-0" />
</a>
</Link>
<ul className="flex flex-col gap-5">
{Announcements.map((announcement: any) => (
<li key={announcement.title}>
<Link href={announcement.url}>
<a className="group flex flex-col focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded">
<p className="text-light mb-0 line-clamp-2 group-hover:text-foreground group-focus-visible:text-foreground">
{announcement.title}
</p>
<p className="text-sm line-clamp-2 text-lighter leading-relaxed !mb-0 group-hover:text-foreground-light group-focus-visible:text-foreground-light">
{announcement.description}
</p>
</a>
</Link>
</li>
))}
</ul>
</div>
</div>
</div>
)
}
export default DevelopersDropdown
+59
View File
@@ -0,0 +1,59 @@
import React, { useEffect, useState } from 'react'
import { Button } from 'ui'
import { Octokit } from 'octokit'
const GitHubButton = () => {
const [stars, setStars] = useState<string>('56')
const octokit = new Octokit()
const kFormatter = (num: number) => {
const kFormat = num / 1000
const decimals = kFormat.toString().split('.')[1]
const firstDecimal = decimals.split('')[0]
const showDecimals = firstDecimal !== '0'
return kFormat.toFixed(showDecimals ? 1 : 0)
}
useEffect(() => {
async function fetchOctoData() {
const res = await octokit.request('GET /repos/{org}/{repo}', {
org: 'supabase',
repo: 'supabase',
type: 'public',
})
setStars(kFormatter(res.data?.stargazers_count))
}
fetchOctoData()
}, [])
return (
<Button
className="hidden group lg:flex text-foreground-light hover:text-foreground-strong"
type="text"
asChild
>
<a href="https://github.com/supabase/supabase" target="_blank">
<span className="flex items-center gap-1">
<svg
className="w-6 h-6"
viewBox="0 0 17 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.5 2.22168C5.23312 2.22168 2.58496 4.87398 2.58496 8.14677C2.58496 10.7642 4.27962 12.9853 6.63026 13.7684C6.92601 13.8228 7.03366 13.6401 7.03366 13.4827C7.03366 13.3425 7.02893 12.9693 7.02597 12.4754C5.38041 12.8333 5.0332 11.681 5.0332 11.681C4.76465 10.996 4.37663 10.8139 4.37663 10.8139C3.83954 10.4471 4.41744 10.4542 4.41744 10.4542C5.01072 10.4956 5.32303 11.0647 5.32303 11.0647C5.85065 11.9697 6.70774 11.7082 7.04431 11.5568C7.09873 11.1741 7.25134 10.9132 7.42051 10.7654C6.10737 10.6157 4.72621 10.107 4.72621 7.83683C4.72621 7.19031 4.95689 6.66092 5.33486 6.24686C5.27394 6.09721 5.07105 5.49447 5.39283 4.67938C5.39283 4.67938 5.88969 4.51967 7.01947 5.28626C7.502 5.15466 7.99985 5.08763 8.5 5.08692C9.00278 5.08929 9.50851 5.15495 9.98113 5.28626C11.1103 4.51967 11.606 4.67879 11.606 4.67879C11.9289 5.49447 11.7255 6.09721 11.6651 6.24686C12.0437 6.66092 12.2732 7.19031 12.2732 7.83683C12.2732 10.1129 10.8897 10.6139 9.5724 10.7606C9.78475 10.9434 9.97344 11.3048 9.97344 11.8579C9.97344 12.6493 9.96634 13.2887 9.96634 13.4827C9.96634 13.6413 10.0728 13.8258 10.3733 13.7678C11.5512 13.3728 12.5751 12.6175 13.3003 11.6089C14.0256 10.6002 14.4155 9.38912 14.415 8.14677C14.415 4.87398 11.7663 2.22168 8.5 2.22168Z"
fill="currentColor"
/>
</svg>
{stars}K
</span>
</a>
</Button>
)
}
export default GitHubButton
+58
View File
@@ -0,0 +1,58 @@
import React from 'react'
import { cn } from 'ui'
type HamburgerButtonProps = {
toggleFlyOut: Function
showLaunchWeekNavMode?: boolean
}
const HamburgerButton = (props: HamburgerButtonProps) => (
<div
className="inset-y-0 flex mr-2 items-center px-4 lg:hidden"
onClick={() => props.toggleFlyOut()}
>
<button
className={cn(
'text-lighter focus:ring-brand dark:bg-background dark:hover:bg-scale-300 inline-flex items-center justify-center rounded-md bg-gray-50 p-2 hover:bg-white focus:outline-none focus:ring-2 focus:ring-inset',
props.showLaunchWeekNavMode && '!bg-transparent border border-[#be9eea]'
)}
aria-expanded="false"
>
<span className="sr-only">Open main menu</span>
<svg
className="block w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke={props.showLaunchWeekNavMode ? '#be9eea' : 'currentColor'}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<svg
className="hidden w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
)
export default HamburgerButton
+68
View File
@@ -0,0 +1,68 @@
import React from 'react'
import Link from 'next/link'
import { IconChevronRight, cn } from 'ui'
const MenuItem = React.forwardRef<
React.ElementRef<'a'>,
React.ComponentPropsWithoutRef<'a'> & {
description?: string
icon?: string
hasChevron?: boolean
}
>(({ className, title, href = '', description, icon, hasChevron, children, ...props }, ref) => {
return (
<Link href={href} passHref>
<a
ref={ref}
className={cn(
'group/menu-item flex items-center text-light text-sm hover:text-foreground select-none gap-3 rounded-md p-2 leading-none no-underline outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground',
description && 'items-center',
className
)}
{...props}
>
{children ?? (
<>
{icon && (
<div className="shrink-0 bg-surface-200 min-w-10 w-10 h-10 flex items-center justify-center rounded-lg">
<svg
className="h-5 w-5 group-hover/menu-item:text-foreground group-focus-visible/menu-item:text-foreground"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d={icon}
stroke="currentColor"
/>
</svg>
</div>
)}
<div className="flex flex-col justify-center">
<div className="flex items-center gap-1">
<p className="leading-snug text-foreground">{title}</p>
{hasChevron && (
<IconChevronRight
strokeWidth={2}
className="w-3 text-foreground transition-all will-change-transform -translate-x-1 opacity-0 group-hover/menu-item:translate-x-0 group-hover/menu-item:opacity-100"
/>
)}
</div>
{description && (
<p className="line-clamp-1 -mb-1 leading-relaxed text-lighter group-hover/menu-item:text-light group-focus-visible/menu-item:text-light">
{description}
</p>
)}
</div>
</>
)}
</a>
</Link>
)
})
export default MenuItem
+200
View File
@@ -0,0 +1,200 @@
import React, { Dispatch, SetStateAction } from 'react'
import Image from 'next/image'
import Link from 'next/link'
import { m, AnimatePresence, LazyMotion, domAnimation } from 'framer-motion'
import { DEFAULT_EASE } from '~/lib/animations'
import { Accordion, Button, TextLink } from 'ui'
import MenuItem from './MenuItem'
import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png'
import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png'
import { useKey } from 'react-use'
interface Props {
open: boolean
setOpen: Dispatch<SetStateAction<boolean>>
isDarkMode: boolean
menu: any
}
const MobileMenu = ({ open, setOpen, isDarkMode, menu }: Props) => {
const container = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { duration: 0.15, staggerChildren: 0.05, ease: DEFAULT_EASE } },
exit: { opacity: 0, transition: { duration: 0.15 } },
}
const listItem = {
hidden: { opacity: 0, y: 10 },
show: { opacity: 1, y: 0, transition: { duration: 0.25, ease: DEFAULT_EASE } },
exit: { opacity: 0, transition: { duration: 0.05 } },
}
useKey('Escape', () => setOpen(false))
const AccordionMenuItem = ({ menuItem }: any) => (
<>
{menuItem.title === 'Product' ? (
Object.values(menuItem.subMenu)?.map((component: any) => (
<MenuItem
key={component.name}
title={component.name}
href={component.url}
description={component.description_short}
icon={component.icon}
/>
))
) : menuItem.title === 'Developers' ? (
<div className="px-3 mb-2 flex flex-col gap-2">
{menuItem.subMenu['navigation'].map((column: any) => (
<div key={column.label} className="flex flex-col gap-3">
{column.label !== 'Developers' && (
<label className="text-muted text-xs uppercase tracking-widest font-mono mt-4">
{column.label}
</label>
)}
{column.links.map((link: any) => (
<TextLink
hasChevron={false}
key={link.text}
url={link.url}
label={link.text}
className="focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay !mt-0"
/>
))}
</div>
))}
<div className="flex flex-col py-2">
<label className="text-muted text-xs uppercase tracking-widest font-mono">
Troubleshooting
</label>
<TextLink
hasChevron={false}
url={menuItem.subMenu['footer']['support'].url}
label={menuItem.subMenu['footer']['support'].text}
className="focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay"
/>
<TextLink
hasChevron={false}
url={menuItem.subMenu['footer']['systemStatus'].url}
label={menuItem.subMenu['footer']['systemStatus'].text}
className="focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay"
/>
</div>
</div>
) : null}
</>
)
const Menu = () => (
<Accordion
type="default"
openBehaviour="multiple"
size="large"
className="space-y-1"
justified
chevronAlign="right"
>
{menu.primaryNav.map((menuItem: any) => (
<m.div variants={listItem} className="border-b [&>div]:!rounded-none" key={menuItem.title}>
{menuItem.hasDropdown ? (
<Accordion.Item
header={menuItem.title}
id={menuItem.title}
className="block relative py-2 pl-3 pr-4 text-base font-medium text-foreground hover:bg-surface-200"
>
<AccordionMenuItem menuItem={menuItem} />
</Accordion.Item>
) : (
<Link href={menuItem.url}>
<a className="block py-2 pl-3 pr-4 text-base font-medium text-strong hover:bg-surface-200 dark:text-white focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:rounded">
{menuItem.title}
</a>
</Link>
)}
</m.div>
))}
</Accordion>
)
return (
<LazyMotion features={domAnimation}>
<AnimatePresence exitBeforeEnter>
{open && (
<m.div
variants={container}
initial="hidden"
animate="show"
exit="exit"
className="bg-overlay fixed overflow-hidden inset-0 z-50 h-screen max-h-screen w-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-[100svh] transform"
>
<div className="absolute h-16 px-6 flex items-center justify-between w-screen left-0 top-0 z-50 bg-overlay before:content[''] before:absolute before:w-full before:h-3 before:inset-0 before:top-full before:bg-gradient-to-b before:from-background-overlay before:to-transparent">
<Link href="/" as="/">
<a className="block w-auto h-6 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm">
<Image
src={isDarkMode ? supabaseLogoWordmarkDark : supabaseLogoWordmarkLight}
width={124}
height={24}
alt="Supabase Logo"
/>
</a>
</Link>
<button
onClick={() => setOpen(false)}
type="button"
className="inline-flex items-center justify-center p-2 bg-white rounded-md text-lighter focus:ring-brand dark:bg-surface-100 dark:hover:bg-surface-200 hover:bg-surface-100 focus:outline-none focus:ring-2 focus:ring-inset"
>
<span className="sr-only">Close menu</span>
<svg
className="w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div className="max-h-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-[100svh] overflow-y-auto pt-20 pb-32 px-4">
<Menu />
</div>
<div className="absolute bottom-0 left-0 right-0 top-auto w-full bg-alternative flex items-stretch p-4 gap-4">
<Link href="https://supabase.com/dashboard" passHref>
<Button block type="default" asChild>
<a className="">Sign in</a>
</Button>
</Link>
<Link href="https://supabase.com/dashboard" passHref>
<Button block asChild>
<a className="h-10 py-4">Start your project</a>
</Button>
</Link>
</div>
</m.div>
)}
</AnimatePresence>
<AnimatePresence exitBeforeEnter>
{open && (
<m.div
variants={container}
initial="hidden"
animate="show"
exit="exit"
className="bg-alternative fixed overflow-hidden inset-0 z-40 h-screen w-screen transform"
/>
)}
</AnimatePresence>
</LazyMotion>
)
}
export default MobileMenu
-91
View File
@@ -1,91 +0,0 @@
import React from 'react'
import SolutionsData from 'data/Solutions'
import CustomerStoriesData from 'data/CustomerStories.json'
import { useRouter } from 'next/router'
import ProductIcon from '../ProductIcon'
import { Badge } from 'ui'
import Image from 'next/image'
import Link from 'next/link'
import TextLink from '../TextLink'
const Product = () => {
const { basePath } = useRouter()
const iconSections = Object.values(SolutionsData).map((solution) => {
const { name, description, icon, label, url } = solution
const content = (
<div className="flex lg:flex-col">
<div className="flex flex-row items-center">
<ProductIcon icon={icon} />
<h5 className="text-scale-1200 overwrite ml-3 mb-0 text-base">{name}</h5>
</div>
<div className="ml-4 mt-3 md:flex md:flex-1 md:flex-col md:justify-between lg:ml-0">
<p className="text-scale-1000 text-sm">{description}</p>
{label && (
<div className="mt-2">
<Badge>{label}</Badge>
</div>
)}
</div>
</div>
)
return (
url && (
<Link href={url} key={name}>
<a className="hover:bg-scale-300 dark:hover:bg-scale-500 col-span-6 rounded p-3 transition">
{content}
</a>
</Link>
)
)
})
return (
<div className="grid grid-cols-12">
<nav className="col-span-6" aria-labelledby="product">
<div className="m-3 grid grid-cols-12 gap-x-8 gap-y-4 py-4 pr-3">{iconSections}</div>
</nav>
<div className="col-span-6">
<div className="m-3 mx-6 flex flex-col items-start">
<p className="p">Latest customer stories</p>
<ul className="mt-6 space-y-3">
{CustomerStoriesData.map((customer: any, idx: number) => {
if (idx > 1) {
return null
}
return (
<li className="flow-root" key={`flyout_case_${idx}`}>
<Link href={customer.url}>
<a className="dark:hover:bg-dark-700 flex items-center rounded-lg border p-3 transition duration-150 ease-in-out hover:bg-gray-100">
<div className="relative hidden mx-2 h-12 w-32 flex-shrink-0 overflow-auto rounded-md sm:block">
<Image
src={`${basePath}/${customer.imgUrl}`}
alt={customer.title}
layout="fill"
objectFit="contain"
className="dark:brightness-200 dark:contrast-0 dark:filter"
/>
</div>
<div className="min-w-0 flex-1 sm:ml-4">
<h4 className="text-scale-1200 text-normal mb-0 text-base">
{customer.title}
</h4>
<p className="p text-sm !mb-0">{customer.description}</p>
</div>
</a>
</Link>
</li>
)
})}
</ul>
<div className="mt-6">
<TextLink url="/customers" label="Explore more" />
</div>
</div>
</div>
</div>
)
}
export default Product
@@ -0,0 +1,90 @@
import React from 'react'
import Image from 'next/image'
import { useRouter } from 'next/router'
import Link from 'next/link'
import { useBreakpoint } from 'common'
import { IconChevronRight, TextLink } from 'ui'
import { NavigationMenuLink } from 'ui/src/components/shadcn/ui/navigation-menu'
import MenuItem from './MenuItem'
import ComparisonsData from 'data/Comparisons'
import CustomersData from 'data/CustomerStories'
import SolutionsData from 'data/Solutions'
const ProductDropdown = () => {
const { basePath } = useRouter()
const isTablet = useBreakpoint(1279)
return (
<div className="flex flex-col xl:flex-row">
<ul className="bg-background grid gap-2 py-6 px-6 grid-cols-2 xl:grid-cols-1 w-[700px] xl:w-[360px]">
{Object.values(SolutionsData).map((component) => (
<NavigationMenuLink key={component.name} asChild>
<MenuItem
title={component.name}
href={component.url}
description={component.description_short}
icon={component.icon}
className="h-fit"
hasChevron
/>
</NavigationMenuLink>
))}
</ul>
<div className="bg-overlay border-t xl:border-t-0 xl:border-l py-8 px-10 gap-8 grid grid-cols-5 xl:flex xl:flex-col w-full xl:w-[500px]">
<div className="col-span-3 xl:w-auto">
<Link href="/customers">
<a className="group flex items-center gap-1 text-lighter hover:text-foreground text-xs uppercase tracking-widest font-mono mb-6 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm focus-visible:text-foreground">
Customer Stories
<IconChevronRight className="h-3 w-3 transition-transform will-change-transform -translate-x-1 group-hover:translate-x-0" />
</a>
</Link>
<ul className="flex flex-col gap-2">
{CustomersData.slice(0, isTablet ? 2 : 3).map((customer) => (
<li key={customer.organization}>
<Link href={customer.url}>
<a className="group flex items-center gap-3 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded">
<div className="relative rounded-md bg-background p-2 border group-hover:border-foreground-muted/50 h-16 w-32 flex-shrink-0 overflow-auto">
<Image
src={`${basePath}/${customer.imgUrl}`}
alt={customer.title}
layout="fill"
objectFit="contain"
className="!p-4 brightness-50 contrast-50 dark:contrast-0 filter"
/>
</div>
<div className="flex flex-col">
<h4 className="text-light group-hover:text-foreground group-focus-visible:text-foreground text-normal mb-0 text-sm">
{customer.title}
</h4>
</div>
</a>
</Link>
</li>
))}
</ul>
</div>
<div className="col-span-2">
<p className="text-lighter text-xs uppercase tracking-widest font-mono mb-6">
{ComparisonsData.label}
</p>
<ul className="flex flex-col gap-2">
{ComparisonsData.comparisons.map((link) => (
<li key={link.text}>
<TextLink
chevronAnimation="fadeIn"
url={link.url}
label={link.text}
className="mt-0 hover:text-foreground focus-visible:text-foreground focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay"
/>
</li>
))}
</ul>
</div>
</div>
</div>
)
}
export default ProductDropdown
+94 -386
View File
@@ -1,38 +1,41 @@
import React, { useState } from 'react'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { Button, Badge, IconStar, IconChevronDown } from 'ui'
import FlyOut from '~/components/UI/FlyOut'
import Transition from 'lib/Transition'
import SolutionsData from 'data/Solutions'
import Solutions from '~/components/Nav/Product'
import Developers from '~/components/Nav/Developers'
import ScrollProgress from '~/components/ScrollProgress'
import { useIsLoggedIn, useIsUserLoading } from 'common'
import { useTheme } from 'next-themes'
import TextLink from '../TextLink'
import Image from 'next/image'
import { useWindowSize } from 'react-use'
import { Button, cn } from 'ui'
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from 'ui/src/components/shadcn/ui/navigation-menu'
import { useIsLoggedIn, useIsUserLoading } from 'common'
import ScrollProgress from '~/components/ScrollProgress'
import GitHubButton from './GitHubButton'
import HamburgerButton from './HamburgerMenu'
import MobileMenu from './MobileMenu'
import MenuItem from './MenuItem'
import { menu } from '~/data/nav'
import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png'
import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png'
const Nav = () => {
const { theme, resolvedTheme } = useTheme()
const { resolvedTheme } = useTheme()
const router = useRouter()
const { width } = useWindowSize()
const [open, setOpen] = useState(false)
const [openProduct, setOpenProduct] = useState(false)
const [openDevelopers, setOpenDevelopers] = useState(false)
const isLoggedIn = useIsLoggedIn()
const isUserLoading = useIsUserLoading()
const isHomePage = router.pathname === '/'
const isLaunchWeekPage = router.pathname.includes('launch-week')
const showLaunchWeekNavMode =
(isLaunchWeekPage || isHomePage) && !open && !openProduct && !openDevelopers
const showLaunchWeekNavMode = isLaunchWeekPage && !open
React.useEffect(() => {
if (open) {
@@ -43,188 +46,34 @@ const Nav = () => {
}
}, [open])
function handleToggle(callback: any) {
handleCancel()
callback()
}
function handleCancel() {
setOpenProduct(false)
setOpenDevelopers(false)
}
const iconSections = Object.values(SolutionsData).map((solution: any, idx: number) => {
const { name, description, icon, label, url } = solution
const content = (
<div className="flex mb-3 md:h-full lg:flex-col">
<div className="flex-shrink-0">
<div className="inline-flex items-center justify-center w-10 h-10 text-white bg-gray-800 rounded-md dark:bg-white dark:text-gray-800 sm:h-12 sm:w-12">
{/* <!-- Heroicon name: chart-bar --> */}
<svg
className="w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={icon} />
</svg>
</div>
</div>
<div className="ml-4 md:flex md:flex-1 md:flex-col md:justify-between lg:ml-0 lg:mt-4">
<div>
<p className="space-x-2 text-base font-medium text-gray-900 dark:text-white">
<span>{name}</span>
{label && (
<Badge dot color="green">
{label}
</Badge>
)}
</p>
<p className="mt-1 text-sm text-scale-1100 dark:text-dark-100">{description}</p>
</div>
{url && (
<p className="mt-2 text-sm font-medium text-brand lg:mt-4">
<TextLink label={label ? 'Get notified' : 'Learn more'} url={url} />
</p>
)}
</div>
</div>
)
return url ? (
<Link href={url} key={`solution_${idx}`}>
<a className="flex flex-col justify-between p-3 my-2 -m-3 transition duration-150 ease-in-out rounded-lg dark:hover:bg-scale-600 hover:bg-gray-50">
{content}
</a>
</Link>
) : (
<div
key={`solution_${idx}`}
className="flex flex-col justify-between p-3 -m-3 transition duration-150 ease-in-out rounded-lg"
>
{content}
</div>
)
})
type HamburgerButtonProps = {
toggleFlyOut: Function
showLaunchWeekNavMode?: boolean
}
const HamburgerButton = (props: HamburgerButtonProps) => (
<div
className="absolute inset-y-0 left-0 flex items-center px-4 lg:hidden"
onClick={() => props.toggleFlyOut()}
>
<button
className={[
'text-scale-900 focus:ring-brand dark:bg-scale-200 dark:hover:bg-scale-300 inline-flex items-center justify-center rounded-md bg-gray-50 p-2 hover:bg-white focus:outline-none focus:ring-2 focus:ring-inset',
showLaunchWeekNavMode && '!bg-transparent border border-[#be9eea]',
].join(' ')}
aria-expanded="false"
>
<span className="sr-only">Open main menu</span>
<svg
className="block w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke={showLaunchWeekNavMode ? '#be9eea' : 'currentColor'}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<svg
className="hidden w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
)
const FlyOutNavButton = (props: any) => (
<div
className={[
`
inline-flex cursor-pointer items-center
border-b-2
border-transparent
px-1
text-sm font-medium
text-scale-1200 transition-colors`,
showLaunchWeekNavMode && '!text-white',
props.active ? 'text-brand' : 'hover:text-brand',
props.active,
].join(' ')}
onClick={props.onClick}
>
<>
<span>{props.title}</span>
<div
className={
'text-scale-900 group-hover:text-scale-900 ml-2 flex h-5 w-5 items-center justify-center transition duration-150 ease-in-out' +
(props.active && ' rotate-180 transform transition-all duration-100')
}
>
<IconChevronDown
size={14}
strokeWidth={2}
className={showLaunchWeekNavMode ? 'text-white' : ''}
/>
</div>
</>
</div>
)
// Close mobile menu when desktop
React.useEffect(() => {
if (width >= 1024) setOpen(false)
}, [width])
return (
<>
<div className="sticky top-0 z-40 transform" style={{ transform: 'translate3d(0,0,999px)' }}>
<div
className={[
'absolute inset-0 h-full w-full opacity-80 bg-scale-200',
className={cn(
'absolute inset-0 h-full w-full opacity-80 bg-background',
!showLaunchWeekNavMode && '!opacity-100 transition-opacity',
showLaunchWeekNavMode && '!bg-transparent transition-all',
].join(' ')}
showLaunchWeekNavMode && '!bg-transparent transition-all'
)}
/>
<nav
className={[
`relative z-40 border-scale-300 border-b backdrop-blur-sm transition-opacity`,
className={cn(
`relative z-40 border-border border-b backdrop-blur-sm transition-opacity`,
showLaunchWeekNavMode ? '!opacity-100 !border-[#e0d2f430]' : '',
isLaunchWeekPage && showLaunchWeekNavMode ? '!border-b-0' : '',
].join(' ')}
isLaunchWeekPage && showLaunchWeekNavMode ? '!border-b-0' : ''
)}
>
{/* <div className="relative flex justify-between h-16 mx-auto lg:container lg:px-10 xl:px-0"> */}
<div className="relative flex justify-between h-16 mx-auto lg:container lg:px-16 xl:px-20">
<HamburgerButton
toggleFlyOut={() => setOpen(true)}
showLaunchWeekNavMode={showLaunchWeekNavMode}
/>
<div className="flex items-center justify-center flex-1 sm:items-stretch lg:justify-between">
<div className="flex items-center px-6 lg:px-0 flex-1 sm:items-stretch justify-between">
<div className="flex items-center">
<div className="flex items-center flex-shrink-0">
<Link href="/" as="/">
<a className="block w-auto h-6">
<a className="block w-auto h-6 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm">
<Image
src={
isLaunchWeekPage || resolvedTheme === 'dark' || isHomePage
@@ -240,119 +89,66 @@ const Nav = () => {
{isLaunchWeekPage && (
<Link href="/launch-week" as="/launch-week">
<a className="hidden ml-2 xl:block font-mono text-sm uppercase leading-4">
<a className="hidden ml-2 xl:block font-mono text-sm uppercase leading-4 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm">
Launch Week
</a>
</Link>
)}
</div>
<div className="hidden pl-4 sm:ml-6 sm:space-x-4 lg:flex">
<FlyOutNavButton
title={'Product'}
onClick={() => handleToggle(() => setOpenProduct(!openProduct))}
active={openProduct}
/>
<FlyOutNavButton
title={'Developers'}
onClick={() => handleToggle(() => setOpenDevelopers(!openDevelopers))}
active={openDevelopers}
/>
<Link href="/pricing">
<a
className={[
`text-scale-1200 hover:text-brand hover:border-brand dark:text-dark-100 dark:hover:border-dark-100 inline-flex items-center
border-b-2 border-transparent p-5 px-1
text-sm font-medium`,
showLaunchWeekNavMode && '!text-white',
].join(' ')}
>
Pricing
</a>
</Link>
<Link href="/docs">
<a
className={[
`text-scale-1200 hover:text-brand hover:border-brand dark:text-dark-100 dark:hover:border-dark-100 inline-flex items-center
border-b-2 border-transparent p-5 px-1
text-sm font-medium`,
showLaunchWeekNavMode && '!text-white',
].join(' ')}
>
Docs
</a>
</Link>
<Link href="/blog">
<a
className={[
`text-scale-1200 hover:text-brand hover:border-brand dark:text-dark-100 dark:hover:border-dark-100 inline-flex items-center
border-b-2 border-transparent p-5 px-1
text-sm font-medium`,
showLaunchWeekNavMode && '!text-white',
].join(' ')}
>
Blog
</a>
</Link>
</div>
<NavigationMenu
delayDuration={0}
className="hidden pl-8 sm:space-x-4 lg:flex h-16"
viewportClassName="rounded-xl bg-background"
>
<NavigationMenuList>
{menu.primaryNav.map((menuItem) =>
menuItem.hasDropdown ? (
<NavigationMenuItem className="text-sm font-medium" key={menuItem.title}>
<NavigationMenuTrigger className="bg-transparent data-[state=open]:!text-brand data-[radix-collection-item]:focus-visible:ring-2 data-[radix-collection-item]:focus-visible:ring-foreground-lighter data-[radix-collection-item]:focus-visible:text-foreground-strong p-2 h-auto">
{menuItem.title}
</NavigationMenuTrigger>
<NavigationMenuContent
className={cn('rounded-xl', menuItem.dropdownContainerClassName)}
>
{menuItem.dropdown}
</NavigationMenuContent>
</NavigationMenuItem>
) : (
<NavigationMenuItem className="text-sm font-medium" key={menuItem.title}>
<NavigationMenuLink asChild>
<MenuItem
href={menuItem.url}
title={menuItem.title}
className="group-hover:bg-transparent text-strong hover:text-brand focus-visible:text-brand"
/>
</NavigationMenuLink>
</NavigationMenuItem>
)
)}
</NavigationMenuList>
</NavigationMenu>
</div>
<div className="flex items-center gap-2">
<GitHubButton />
{!isUserLoading && (
<>
<a href="https://github.com/supabase/supabase" target="_blank">
<Button
className="hidden group lg:flex"
type="text"
icon={
<div className="flex items-center justify-center w-4 h-4 text-brand-300">
<div
className={[
`text-scale-900 flex h-3 w-3 items-center justify-center
transition-all
group-hover:h-4
group-hover:w-4
group-hover:text-yellow-900
group-focus:h-4
group-focus:w-4
group-focus:text-yellow-900`,
showLaunchWeekNavMode && '!text-white',
].join(' ')}
>
<IconStar strokeWidth={2} />
</div>
</div>
}
>
<span
// className={isLaunchWeekPage ? '!text-white' : ''}
>
Star us on GitHub
</span>
</Button>
</a>
{isLoggedIn ? (
<Link href="/dashboard/projects">
<a>
<Button className="hidden text-white lg:block">Dashboard</Button>
</a>
<Link href="/dashboard/projects" passHref>
<Button className="hidden text-white lg:block" asChild>
<a>Dashboard</a>
</Button>
</Link>
) : (
<>
<Link href="https://supabase.com/dashboard">
<a>
<Button type="default" className="hidden lg:block">
Sign in
</Button>
</a>
<Link href="https://supabase.com/dashboard" passHref>
<Button type="default" className="hidden lg:block" asChild>
<a>Sign in</a>
</Button>
</Link>
<Link href="https://supabase.com/dashboard">
<a>
<Button className="hidden text-white lg:block">
Start your project
</Button>
</a>
<Link href="https://supabase.com/dashboard" passHref>
<Button className="hidden text-white lg:block" asChild>
<a>Start your project</a>
</Button>
</Link>
</>
)}
@@ -360,107 +156,19 @@ const Nav = () => {
)}
</div>
</div>
<HamburgerButton
toggleFlyOut={() => setOpen(true)}
showLaunchWeekNavMode={showLaunchWeekNavMode}
/>
</div>
{/* </div> */}
{/* Mobile Nav Menu */}
<Transition
appear={true}
show={open}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<div
className={[
'dark:bg-scale-300 fixed -inset-y-0 z-50 h-screen w-screen transform overflow-y-scroll bg-white p-4 md:p-8',
open && '!bg-scale-300',
].join(' ')}
>
<div className="absolute items-center justify-between right-4 top-4">
<div className="-mr-2">
<button
onClick={() => setOpen(false)}
type="button"
className="inline-flex items-center justify-center p-2 bg-white rounded-md text-scale-900 focus:ring-brand dark:bg-scale-300 dark:hover:bg-scale-400 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset"
>
<span className="sr-only">Close menu</span>
<svg
className="w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
{/* </div> */}
<div className="mt-6 mb-12">
<div className="pt-2 pb-4 space-y-1">
<Link href="https://supabase.com/dashboard">
<a className="block pl-3 pr-4 text-base font-medium text-scale-900 dark:text-white">
Sign in
</a>
</Link>
</div>
<div className="pt-2 pb-4 space-y-1">
<Link href="/docs">
<a className="block py-2 pl-3 pr-4 text-base font-medium rounded-md text-scale-900 dark:hover:bg-scale-600 hover:border-gray-300 hover:bg-gray-50 dark:text-white">
Developers
</a>
</Link>
<Link href="/pricing">
<a className="block py-2 pl-3 pr-4 text-base font-medium rounded-md text-scale-900 dark:hover:bg-scale-600 hover:border-gray-300 hover:bg-gray-50 dark:text-white">
Pricing
</a>
</Link>
<Link href="/docs">
<a
target="_blank"
className="block py-2 pl-3 pr-4 text-base font-medium rounded-md text-scale-900 dark:hover:bg-scale-600 hover:border-gray-300 hover:bg-gray-50 dark:text-white"
>
Docs
</a>
</Link>
<Link href="/blog">
<a
target="_blank"
className="block py-2 pl-3 pr-4 text-base font-medium rounded-md text-scale-900 dark:hover:bg-scale-600 hover:border-gray-300 hover:bg-gray-50 dark:text-white"
>
Blog
</a>
</Link>
<Link href="/support">
<a className="block py-2 pl-3 pr-4 text-base font-medium rounded-md text-scale-900 dark:hover:bg-scale-600 hover:border-gray-300 hover:bg-gray-50 dark:text-white">
Support
</a>
</Link>
</div>
<div className="p-3">
<p className="mb-6 text-sm text-scale-900">Products available:</p>
{iconSections}
</div>
</div>
</div>
</Transition>
<MobileMenu
open={open}
setOpen={setOpen}
isDarkMode={isLaunchWeekPage || resolvedTheme === 'dark' || isHomePage}
menu={menu}
/>
</nav>
<FlyOut open={openProduct} handleCancel={handleCancel}>
<Solutions />
</FlyOut>
<FlyOut open={openDevelopers} handleCancel={handleCancel}>
<Developers />
</FlyOut>
<ScrollProgress />
</div>
</>
+38 -4
View File
@@ -1,21 +1,55 @@
type Color = 'black' | 'gray' | 'green' | 'alt'
interface ProductIcon {
icon: string
color?: 'black' | 'green'
color?: Color
}
function ProductIcon({ icon, color }: ProductIcon) {
const handleColor = (color: Color) => {
switch (color) {
case 'green':
return {
container: 'bg-brand-600 dark:bg-brand-500 text-brand-100',
svg: 'stroke-brand',
}
case 'alt':
return {
container: 'bg-brand-200 text-brand',
svg: 'stroke-brand',
}
case 'gray':
return {
container: 'bg- text-brand',
svg: 'stroke-brand',
}
case 'black':
default:
return {
container: 'bg-scale-1200 text-scale-100',
svg: 'stroke-white dark:stroke-black',
}
}
}
return (
<div
className={[
'inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md',
!color || color === 'black' ? 'bg-scale-1200 text-scale-100' : '',
color && color === 'green' ? 'bg-brand-600 dark:bg-brand-500 text-brand-100' : '',
color && color === 'gray' ? 'bg-scale-700 text-light' : '',
color && color === 'green' ? 'bg-brand dark:bg-brand-500 text-brand-100' : '',
color && color === 'alt' ? 'bg-alternative text-brand' : '',
// color && color === 'alt' ? 'bg-surface-300 text-brand' : '',
].join(' ')}
>
<svg
className={[
'h-5 w-5 stroke-white dark:stroke-black',
color && color === 'green' ? 'dark:stroke-brand' : '',
'h-5 w-5',
!color || color === 'black' ? 'stroke-white dark:stroke-black' : '',
color && color === 'gray' ? 'stroke-foreground-light' : '',
color && color === 'green' ? 'stroke-brand-200 dark:stroke-brand' : '',
color && color === 'alt' ? 'stroke-brand' : '',
].join(' ')}
xmlns="http://www.w3.org/2000/svg"
fill="none"
@@ -1,7 +1,6 @@
import React from 'react'
import Link from 'next/link'
import Panel from '~/components/Panel'
import { detectBrowser } from 'common'
const ProductCard = ({
className,
+12 -6
View File
@@ -15,12 +15,18 @@ const StorageVisual = () => {
role="img"
aria-label="Supabase Storage supports images, documents and videos"
>
{range(0, 2).map((_rangeCol) => (
<div className="relative h-full left-0 w-auto items-end pb-2 z-10 flex pause animate-marquee group-hover:run will-change-transform transition-transform">
{range(0, 10).map((_rangeCol) => (
<div className="flex flex-col ml-2 gap-2 md:gap-2">
{cols.map((col: any) => (
<div className="w-[60px] h-[60px] md:min-w-[62px] md:w-[62px] md:h-[62px] flex items-center justify-center rounded-lg border bg hover:border-brand">
{range(0, 2).map((_, idx1: number) => (
<div
key={`row-${idx1}`}
className="relative h-full left-0 w-auto items-end pb-2 z-10 flex pause animate-marquee group-hover:run will-change-transform transition-transform"
>
{range(0, 10).map((_, idx2: number) => (
<div key={`col-${idx2}`} className="flex flex-col ml-2 gap-2 md:gap-2">
{cols.map((col: any, idx3: number) => (
<div
key={`icon-${idx3}`}
className="w-[60px] h-[60px] md:min-w-[62px] md:w-[62px] md:h-[62px] flex items-center justify-center rounded-lg border bg hover:border-brand"
>
{col}
</div>
))}
-26
View File
@@ -1,26 +0,0 @@
[
{
"text": "Blog",
"description": "Read the latest developments from Supabase",
"url": "/blog",
"icon": "M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
},
{
"text": "Open Source",
"description": "Supabase is an opensource company, supporting existing open source tools and communities wherever possible.",
"url": "/open-source",
"icon": "M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"
},
{
"text": "Humans.txt",
"description": "Overview of Supabase staff",
"url": "/humans.txt",
"icon": "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
},
{
"text": "Lawyers.txt",
"description": "Details of technology used by Supabase",
"url": "/lawyers.txt",
"icon": "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
}
]
+17
View File
@@ -0,0 +1,17 @@
export const data = {
label: 'Compare Supabase',
comparisons: [
{
text: 'Supabase vs Firebase',
description: '',
url: '/alternatives/supabase-vs-firebase',
},
{
text: 'Supabase vs Heroku Postgres',
description: '',
url: '/alternatives/supabase-vs-heroku-postgres',
},
],
}
export default data
-138
View File
@@ -1,138 +0,0 @@
[
{
"type": "Customer Story",
"title": "Scaling securely: one million users in 7 months protected with Supabase Auth",
"description": "Learn how Pebblely, an AI image generation company, used Supabase Auth for rapid growth and adaptable security solutions.",
"imgUrl": "images/customers/logos/pebblely.png",
"logoUrl": "images/customers/logos/pebblely.png",
"organization": "Pebblely",
"url": "/customers/pebblely",
"path": "/customers/pebblely",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Sep 29, 2023",
"readLength": 4
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "Bootstrapped founder builds an AI app with Supabase and scales to $1M in 5 months.",
"description": "How Yasser leveraged Supabase to build Chatbase and became one of the most successful single-founder AI products.",
"imgUrl": "images/customers/logos/chatbase.png",
"logoUrl": "images/customers/logos/chatbase.png",
"organization": "Chatbase",
"url": "/customers/chatbase",
"path": "/customers/chatbase",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Sep 6, 2023",
"readLength": 6
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "Quivr launch 5,000 Vector databases on Supabase.",
"description": "Learn how one of the most popular Generative AI projects uses Supabase as their Vector Store.",
"imgUrl": "images/customers/logos/quivr.png",
"logoUrl": "images/customers/logos/quivr.png",
"organization": "Quivr",
"url": "/customers/quivr",
"path": "/customers/quivr",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Sep 5, 2023",
"readLength": 6
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "Berri AI Boosts Productivity by Migrating from AWS RDS to Supabase with pgvector.",
"description": "Learn how Berri AI overcame challenges with self-hosting their vector database on AWS RDS and successfully migrated to Supabase.",
"imgUrl": "images/customers/logos/berriai.png",
"logoUrl": "images/customers/logos/berriai.png",
"organization": "Berri AI",
"url": "/customers/berriai",
"path": "/customers/berriai",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Jun 6, 2023",
"readLength": 6
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "Mendable.ai switches from Pinecone to Supabase for PostgreSQL vector embeddings.",
"description": "How Mendable.ai boosts efficiency and accuracy of chat powered search for documentation using Supabase with pg_vector.",
"imgUrl": "images/customers/logos/mendableai.png",
"logoUrl": "images/customers/logos/mendableai.png",
"organization": "Mendable.ai",
"url": "/customers/mendableai",
"path": "/customers/mendableai",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Feb 16, 2023",
"readLength": 6
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "HappyTeams unlocks better performance and reduces cost with Supabase.",
"description": "How a bootstrapped startup migrated from Heroku to Supabase in 30 minutes and never looked back.",
"imgUrl": "images/customers/logos/happyteams.png",
"logoUrl": "images/customers/logos/happyteams.png",
"organization": "HappyTeams",
"url": "/customers/happyteams",
"path": "/customers/happyteams",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Feb 16, 2023",
"readLength": 6
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "Xendit use Supabase and create a full solution shipped to production in less than one week.",
"description": "As a payment processor, Xendit are responsible for verifying that all transactions are legal.",
"imgUrl": "images/customers/logos/xendit.png",
"logoUrl": "images/customers/logos/xendit.png",
"organization": "Xendit",
"url": "/customers/xendit",
"path": "/customers/xendit",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Feb 14, 2023",
"readLength": 6
},
"ctaText": "View story"
},
{
"type": "Customer Story",
"title": "Replenysh uses Supabase to implement OTP in less than 24 hours.",
"description": "With Supabase, Replenysh gets a slick auth experience, reduces DevOps overhead, and continues to scale with Postgres.",
"imgUrl": "images/customers/logos/replenysh.png",
"logoUrl": "images/customers/logos/replenysh.png",
"organization": "Replenysh",
"url": "/customers/replenysh",
"path": "/customers/replenysh",
"postMeta": {
"name": "Paul Copplestone",
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
"publishDate": "Feb 14, 2023",
"readLength": 6
},
"ctaText": "View story"
}
]
+149
View File
@@ -0,0 +1,149 @@
export const data = [
{
type: 'Customer Story',
title: 'Scaling securely: one million users in 7 months protected with Supabase Auth',
description:
'Learn how Pebblely, an AI image generation company, used Supabase Auth for rapid growth and adaptable security solutions.',
imgUrl: 'images/customers/logos/pebblely.png',
logoUrl: 'images/customers/logos/pebblely.png',
organization: 'Pebblely',
url: '/customers/pebblely',
path: '/customers/pebblely',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Sep 29, 2023',
readLength: 4,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title: 'Bootstrapped founder builds an AI app with Supabase and scales to $1M in 5 months.',
description:
'How Yasser leveraged Supabase to build Chatbase and became one of the most successful single-founder AI products.',
imgUrl: 'images/customers/logos/chatbase.png',
logoUrl: 'images/customers/logos/chatbase.png',
organization: 'Chatbase',
url: '/customers/chatbase',
path: '/customers/chatbase',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Sep 6, 2023',
readLength: 6,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title: 'Quivr launch 5,000 Vector databases on Supabase.',
description:
'Learn how one of the most popular Generative AI projects uses Supabase as their Vector Store.',
imgUrl: 'images/customers/logos/quivr.png',
logoUrl: 'images/customers/logos/quivr.png',
organization: 'Quivr',
url: '/customers/quivr',
path: '/customers/quivr',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Sep 5, 2023',
readLength: 6,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title: 'Berri AI Boosts Productivity by Migrating from AWS RDS to Supabase with pgvector.',
description:
'Learn how Berri AI overcame challenges with self-hosting their vector database on AWS RDS and successfully migrated to Supabase.',
imgUrl: 'images/customers/logos/berriai.png',
logoUrl: 'images/customers/logos/berriai.png',
organization: 'Berri AI',
url: '/customers/berriai',
path: '/customers/berriai',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Jun 6, 2023',
readLength: 6,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title: 'Mendable.ai switches from Pinecone to Supabase for PostgreSQL vector embeddings.',
description:
'How Mendable.ai boosts efficiency and accuracy of chat powered search for documentation using Supabase with pg_vector.',
imgUrl: 'images/customers/logos/mendableai.png',
logoUrl: 'images/customers/logos/mendableai.png',
organization: 'Mendable.ai',
url: '/customers/mendableai',
path: '/customers/mendableai',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Feb 16, 2023',
readLength: 6,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title: 'HappyTeams unlocks better performance and reduces cost with Supabase.',
description:
'How a bootstrapped startup migrated from Heroku to Supabase in 30 minutes and never looked back.',
imgUrl: 'images/customers/logos/happyteams.png',
logoUrl: 'images/customers/logos/happyteams.png',
organization: 'HappyTeams',
url: '/customers/happyteams',
path: '/customers/happyteams',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Feb 16, 2023',
readLength: 6,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title:
'Xendit use Supabase and create a full solution shipped to production in less than one week.',
description:
'As a payment processor, Xendit are responsible for verifying that all transactions are legal.',
imgUrl: 'images/customers/logos/xendit.png',
logoUrl: 'images/customers/logos/xendit.png',
organization: 'Xendit',
url: '/customers/xendit',
path: '/customers/xendit',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Feb 14, 2023',
readLength: 6,
},
ctaText: 'View story',
},
{
type: 'Customer Story',
title: 'Replenysh uses Supabase to implement OTP in less than 24 hours.',
description:
'With Supabase, Replenysh gets a slick auth experience, reduces DevOps overhead, and continues to scale with Postgres.',
imgUrl: 'images/customers/logos/replenysh.png',
logoUrl: 'images/customers/logos/replenysh.png',
organization: 'Replenysh',
url: '/customers/replenysh',
path: '/customers/replenysh',
postMeta: {
name: 'Paul Copplestone',
avatarUrl: 'https://avatars0.githubusercontent.com/u/10214025?v=4',
publishDate: 'Feb 14, 2023',
readLength: 6,
},
ctaText: 'View story',
},
]
export default data
+88 -42
View File
@@ -1,44 +1,90 @@
export const links = [
{
text: 'Resources',
description: 'View examples, curated content from the community, migration guides, and more',
url: '/docs/guides/resources',
icon: 'M19.4994 17.9996V7.50107L19.4864 3L14.0723 3L4.5 3L4.5 18.75M4.5 18.75C4.5 19.9926 5.50736 21 6.75 21H19.5V18.5255L19.4871 16.5H14.0728H6.75C5.50736 16.5 4.5 17.5074 4.5 18.75ZM7.50724 3V9L9.75771 6.45713L12.0072 9V3H7.50724Z',
export const data = {
header: [
{
text: 'Documentation',
description: '',
url: '/docs',
icon: 'M19.4994 17.9996V7.50107L19.4864 3L14.0723 3L4.5 3L4.5 18.75M4.5 18.75C4.5 19.9926 5.50736 21 6.75 21H19.5V18.5255L19.4871 16.5H14.0728H6.75C5.50736 16.5 4.5 17.5074 4.5 18.75ZM7.50724 3V9L9.75771 6.45713L12.0072 9V3H7.50724Z',
},
],
navigation: [
{
label: 'Developers',
links: [
{
text: 'Documentation',
url: '/docs',
icon: 'M4.84741 8.30011H10.0974M4.84741 6.55011H10.0974M4.84741 10.0501H8.34741M11.8795 4.90069V10.2495C11.8795 11.3541 10.9841 12.2495 9.87951 12.2495H5.12988C4.02531 12.2495 3.12988 11.3541 3.12988 10.2495V3.75C3.12988 2.64543 4.02531 1.75 5.12988 1.75H8.71375L11.8795 4.90069ZM11.8487 4.88599L8.71263 1.75L8.71248 3.88592C8.71245 4.43823 9.16017 4.88599 9.71248 4.88599L11.8487 4.88599Z',
},
{
text: 'Integrations',
url: '/partners/integrations',
icon: 'M10.4956 1.83496V6.20996M8.30811 4.02267L12.6831 4.02267M6.86316 9.83727C6.86316 11.1348 5.81129 12.1867 4.51373 12.1867C3.21618 12.1867 2.16431 11.1348 2.16431 9.83727C2.16431 8.53972 3.21618 7.48785 4.51373 7.48785C5.81129 7.48785 6.86316 8.53972 6.86316 9.83727ZM12.6831 11.1158V8.74078C12.6831 8.1885 12.2354 7.74078 11.6831 7.74078H9.30811C8.75582 7.74078 8.30811 8.1885 8.30811 8.74078V11.1158C8.30811 11.6681 8.75582 12.1158 9.30811 12.1158H11.6831C12.2354 12.1158 12.6831 11.6681 12.6831 11.1158ZM3.32666 6.20996H5.70166C6.25394 6.20996 6.70166 5.76225 6.70166 5.20996V2.83496C6.70166 2.28268 6.25394 1.83496 5.70166 1.83496H3.32666C2.77438 1.83496 2.32666 2.28268 2.32666 2.83496V5.20996C2.32666 5.76225 2.77438 6.20996 3.32666 6.20996Z',
},
{
text: 'Changelog',
description: 'See the latest updates and product improvements.',
url: '/changelog',
icon: 'M11.0049 7.00011L4.00488 7.00011M10.5674 5.0731L4.44238 5.0731M10.5674 8.99879L4.44238 8.99879M3.16404 3.32494C4.20731 2.0939 5.76484 1.3125 7.50488 1.3125C10.646 1.3125 13.1924 3.85888 13.1924 7C13.1924 10.1411 10.646 12.6875 7.50488 12.6875C4.36376 12.6875 1.81738 10.1411 1.81738 7C1.81738 6.25829 1.95936 5.54974 2.21763 4.90004M2.85766 1.51563L2.85766 3.19986C2.85766 3.44149 3.05353 3.63736 3.29516 3.63736L4.85069 3.63736',
},
{
text: 'Support',
description: 'See the latest updates and product improvements.',
url: '/support',
icon: 'M3.38062 2.87598L5.85395 5.34931M9.15576 8.65039L11.6291 11.1237M11.6291 2.87598L9.15576 5.34931L11.2149 3.29004M3.38062 11.1237L5.85395 8.65039M13.3383 6.99935C13.3383 10.221 10.7266 12.8327 7.50496 12.8327C4.2833 12.8327 1.67163 10.221 1.67163 6.99935C1.67163 3.77769 4.2833 1.16602 7.50496 1.16602C10.7266 1.16602 13.3383 3.77769 13.3383 6.99935ZM9.8383 6.99935C9.8383 8.28801 8.79363 9.33268 7.50496 9.33268C6.2163 9.33268 5.17163 8.28801 5.17163 6.99935C5.17163 5.71068 6.2163 4.66602 7.50496 4.66602C8.79363 4.66602 9.8383 5.71068 9.8383 6.99935Z',
},
],
},
{
label: 'Resources',
links: [
{
text: 'Open Source',
description: 'We support existing open source tools and communities wherever possible.',
url: '/open-source',
icon: 'M7.83826 1.22168C4.57138 1.22168 1.92322 3.87398 1.92322 7.14677C1.92322 9.76418 3.61788 11.9853 5.96851 12.7684C6.26427 12.8228 6.37192 12.6401 6.37192 12.4827C6.37192 12.3425 6.36719 11.9693 6.36423 11.4754C4.71867 11.8333 4.37145 10.681 4.37145 10.681C4.10291 9.99605 3.71488 9.81386 3.71488 9.81386C3.1778 9.44713 3.7557 9.45423 3.7557 9.45423C4.34898 9.49564 4.66129 10.0647 4.66129 10.0647C5.18891 10.9697 6.046 10.7082 6.38257 10.5568C6.43698 10.1741 6.58959 9.91324 6.75876 9.76536C5.44562 9.61571 4.06446 9.10702 4.06446 6.83683C4.06446 6.19031 4.29515 5.66092 4.67312 5.24686C4.61219 5.09721 4.40931 4.49447 4.73109 3.67938C4.73109 3.67938 5.22795 3.51967 6.35772 4.28626C6.84025 4.15466 7.3381 4.08763 7.83826 4.08692C8.34104 4.08929 8.84677 4.15495 9.31938 4.28626C10.4486 3.51967 10.9442 3.67879 10.9442 3.67879C11.2672 4.49447 11.0637 5.09721 11.0034 5.24686C11.382 5.66092 11.6115 6.19031 11.6115 6.83683C11.6115 9.11293 10.2279 9.61394 8.91065 9.76063C9.123 9.94341 9.31169 10.3048 9.31169 10.8579C9.31169 11.6493 9.30459 12.2887 9.30459 12.4827C9.30459 12.6413 9.41107 12.8258 9.71155 12.7678C10.8894 12.3728 11.9133 11.6175 12.6386 10.6089C13.3639 9.60021 13.7538 8.38912 13.7533 7.14677C13.7533 3.87398 11.1045 1.22168 7.83826 1.22168Z',
},
{
text: 'GitHub Discussions',
url: 'https://github.com/orgs/supabase/discussions',
icon: 'M1.81738 3.5C1.81738 2.29188 2.79676 1.3125 4.00488 1.3125H11.0049C12.213 1.3125 13.1924 2.29188 13.1924 3.5V8.20172C13.1924 9.40984 12.213 10.3892 11.0049 10.3892H7.26195L5.18436 12.553C5.06068 12.6818 4.87113 12.7225 4.70547 12.6559C4.5398 12.5892 4.43127 12.4286 4.43127 12.25V10.3892H4.00488C2.79676 10.3892 1.81738 9.40984 1.81738 8.20172V3.5ZM4.00488 2.1875C3.28001 2.1875 2.69238 2.77513 2.69238 3.5V8.20172C2.69238 8.92659 3.28001 9.51422 4.00488 9.51422H4.86877C5.1104 9.51422 5.30627 9.7101 5.30627 9.95172V11.1627L6.75992 9.64871C6.84242 9.56279 6.95638 9.51422 7.0755 9.51422H11.0049C11.7298 9.51422 12.3174 8.92659 12.3174 8.20172V3.5C12.3174 2.77513 11.7298 2.1875 11.0049 2.1875H4.00488Z M5.75488 5.48201C5.99651 5.48201 6.19238 5.67788 6.19238 5.91951C6.19238 6.16113 5.99651 6.35701 5.75488 6.35701C5.51326 6.35701 5.31738 6.16113 5.31738 5.91951C5.31738 5.67788 5.51326 5.48201 5.75488 5.48201Z M7.50488 5.48201C7.74651 5.48201 7.94238 5.67788 7.94238 5.91951C7.94238 6.16113 7.74651 6.35701 7.50488 6.35701C7.26326 6.35701 7.06738 6.16113 7.06738 5.91951C7.06738 5.67788 7.26326 5.48201 7.50488 5.48201Z M9.25488 5.48201C9.49651 5.48201 9.69238 5.67788 9.69238 5.91951C9.69238 6.16113 9.49651 6.35701 9.25488 6.35701C9.01326 6.35701 8.81738 6.16113 8.81738 5.91951C8.81738 5.67788 9.01326 5.48201 9.25488 5.48201Z',
},
{
text: 'Become a Partner',
url: '/partners',
icon: 'M11.7035 8.12415L12.3422 7.48542C13.4972 6.33043 13.4972 4.45781 12.3422 3.30281C11.1872 2.14781 9.31462 2.14781 8.15963 3.30281L7.10738 4.35506L6.62074 4.84167C6.08709 5.37532 6.08709 6.24053 6.62074 6.77418C7.15439 7.30783 8.0196 7.30783 8.55325 6.77418L10.1548 5.1726M7.50497 3.95743L6.85031 3.30277C5.69532 2.14777 3.8227 2.14777 2.6677 3.30277C1.5127 4.45777 1.5127 6.33039 2.6677 7.48538L2.79695 7.61444L6.53948 11.3864L7.10059 11.9475C7.48909 12.336 8.11897 12.336 8.50746 11.9475C8.88805 11.5669 8.86601 10.9249 8.5009 10.5349M8.15569 10.1887L8.50754 10.5407C8.51541 10.5486 8.49335 10.5268 8.5009 10.5349M8.5009 10.5349L8.70668 10.7407C9.09515 11.1292 9.72504 11.1292 10.1135 10.7407C10.502 10.3523 10.502 9.72247 10.1136 9.33399L9.21878 8.43906M8.5009 10.5349L7.84177 9.87564M9.23249 8.43969L10.1426 9.34975C10.531 9.73823 11.1609 9.73823 11.5493 9.34975C11.9378 8.96128 11.9378 8.33144 11.5493 7.94296L10.6249 7.01855',
},
{
text: 'Careers',
url: '/careers',
icon: 'M13.0883 7.73143C11.4202 8.4059 9.6375 8.75155 7.83826 8.74935C5.98151 8.74935 4.20992 8.38768 2.58826 7.73143M10.1716 3.49935V2.33268C10.1716 2.02326 10.0487 1.72652 9.82988 1.50772C9.61109 1.28893 9.31434 1.16602 9.00492 1.16602H6.67159C6.36217 1.16602 6.06542 1.28893 5.84663 1.50772C5.62784 1.72652 5.50492 2.02326 5.50492 2.33268V3.49935M7.83826 6.99935H7.84409M3.75492 11.666H11.9216C12.231 11.666 12.5278 11.5431 12.7465 11.3243C12.9653 11.1055 13.0883 10.8088 13.0883 10.4993V4.66602C13.0883 4.3566 12.9653 4.05985 12.7465 3.84106C12.5278 3.62227 12.231 3.49935 11.9216 3.49935H3.75492C3.4455 3.49935 3.14876 3.62227 2.92997 3.84106C2.71117 4.05985 2.58826 4.3566 2.58826 4.66602V10.4993C2.58826 10.8088 2.71117 11.1055 2.92997 11.3243C3.14876 11.5431 3.4455 11.666 3.75492 11.666Z',
},
],
},
],
guides: {
label: 'Guides',
links: [
{
text: 'Documentation',
description: '',
url: '/docs',
icon: 'M19.4994 17.9996V7.50107L19.4864 3L14.0723 3L4.5 3L4.5 18.75M4.5 18.75C4.5 19.9926 5.50736 21 6.75 21H19.5V18.5255L19.4871 16.5H14.0728H6.75C5.50736 16.5 4.5 17.5074 4.5 18.75ZM7.50724 3V9L9.75771 6.45713L12.0072 9V3H7.50724Z',
},
],
},
{
text: 'Changelog',
description: 'See the latest updates and product improvements',
url: '/changelog',
icon: 'M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z',
footer: {
support: {
text: 'Support',
description: '',
url: '/support',
icon: 'M6.87232 21.5743C9.09669 21.5743 10.8999 19.7711 10.8999 17.5467C10.8999 15.3223 9.09669 13.5191 6.87232 13.5191C4.64794 13.5191 2.84473 15.3223 2.84473 17.5467C2.84473 19.7711 4.64794 21.5743 6.87232 21.5743Z M17.127 3.67236V11.1724M20.877 7.42274H13.377M3.12305 3.67236H10.6231V11.1724H3.12305V3.67236ZM13.377 13.7966H20.877V21.2966H13.377V13.7966ZM10.8999 17.5467C10.8999 19.7711 9.09669 21.5743 6.87232 21.5743C4.64794 21.5743 2.84473 19.7711 2.84473 17.5467C2.84473 15.3223 4.64794 13.5191 6.87232 13.5191C9.09669 13.5191 10.8999 15.3223 10.8999 17.5467Z',
},
systemStatus: {
text: 'System Status',
description: '',
url: 'https://status.supabase.com/',
icon: 'M6.87232 21.5743C9.09669 21.5743 10.8999 19.7711 10.8999 17.5467C10.8999 15.3223 9.09669 13.5191 6.87232 13.5191C4.64794 13.5191 2.84473 15.3223 2.84473 17.5467C2.84473 19.7711 4.64794 21.5743 6.87232 21.5743Z M17.127 3.67236V11.1724M20.877 7.42274H13.377M3.12305 3.67236H10.6231V11.1724H3.12305V3.67236ZM13.377 13.7966H20.877V21.2966H13.377V13.7966ZM10.8999 17.5467C10.8999 19.7711 9.09669 21.5743 6.87232 21.5743C4.64794 21.5743 2.84473 19.7711 2.84473 17.5467C2.84473 15.3223 4.64794 13.5191 6.87232 13.5191C9.09669 13.5191 10.8999 15.3223 10.8999 17.5467Z',
},
},
{
text: 'Careers',
description: 'Join the Supabase team and get involved',
url: '/careers',
icon: 'M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z',
},
{
text: 'Open Source',
description: 'We support existing open source tools and communities wherever possible',
url: '/open-source',
icon: 'M10.7607 3.08859C15.7723 2.38592 20.0295 5.94789 20.8525 10.7642C21.5964 15.1122 19.4634 19.1692 15.8778 20.9967L14.0736 16.084C15.129 15.4346 15.8232 14.4223 16.0198 13.0819C16.3702 10.7219 14.7442 8.47426 12.3801 8.25648C10.2228 8.04047 8.33304 9.61601 7.99823 11.7702L7.99761 11.7743C7.73427 13.5096 8.47082 15.1847 9.92827 16.0841L8.12693 21C6.34571 20.1066 4.88125 18.6129 3.97963 16.7387L3.97879 16.7369C3.15872 15.0395 3.07406 13.7081 3.01524 12.783C3.01012 12.7024 3.0052 12.6249 3 12.5506C3.10941 7.30353 6.59243 3.6764 10.7607 3.08859Z',
},
{
text: 'Partners',
description: 'Become a Supabase Partner and enable new business opportunities',
url: '/partners',
icon: 'M12.0003 6.1488L10.5812 4.76334C8.68921 2.91613 5.62161 2.91613 3.72957 4.76334C1.98628 6.46532 1.84923 9.14165 3.31842 10.9968C3.44378 11.1551 3.77166 11.514 3.92041 11.6592M12.0003 6.1488L13.4193 4.76334C15.3113 2.91613 18.3789 2.91613 20.271 4.76334C22.163 6.61054 22.163 9.60546 20.271 11.4527L19.2017 12.5186M12.0003 6.1488L10.7542 7.39725C10.0214 8.13141 10.0219 9.32043 10.7554 10.0539C11.4894 10.7879 12.6793 10.7879 13.4133 10.0539L16.0693 7.39787M14.9131 13.4531L17.4954 16.1809M17.4954 16.1809C18.2089 16.9347 18.1764 18.1242 17.4226 18.8378C16.6693 19.5509 15.4807 19.5189 14.7669 18.7663L13.3188 17.2217M17.4954 16.1809C18.2086 16.9344 19.3982 16.9676 20.1516 16.2543C20.9051 15.5411 20.9376 14.3521 20.2244 13.5987L17.6415 10.8703M6.13555 14.7589L7.46387 13.4306M6.13555 14.7589C5.40193 15.4925 4.21251 15.4925 3.47889 14.7589C2.74528 14.0253 2.74528 12.8358 3.47889 12.1022L4.80722 10.7739C5.54084 10.0403 6.73026 10.0403 7.46388 10.7739C8.19749 11.5075 8.19749 12.6969 7.46387 13.4306M6.13555 14.7589C5.40193 15.4925 5.40193 16.6819 6.13555 17.4155C6.86916 18.1492 8.05859 18.1492 8.7922 17.4155M7.46387 13.4306C8.19749 12.6969 9.38691 12.6969 10.1205 13.4306C10.8541 14.1642 10.8541 15.3536 10.1205 16.0872M8.7922 17.4155L10.1205 16.0872M8.7922 17.4155C8.05859 18.1492 8.05859 19.3386 8.7922 20.0722C9.52582 20.8058 10.7152 20.8058 11.4489 20.0722L12.7772 18.7439C13.5108 18.0102 13.5108 16.8208 12.7772 16.0872C12.0436 15.3536 10.8541 15.3536 10.1205 16.0872',
},
{
text: 'Integrations',
description: 'Use your favorite tools with Supabase',
url: '/partners/integrations',
icon: 'M6.87232 21.5743C9.09669 21.5743 10.8999 19.7711 10.8999 17.5467C10.8999 15.3223 9.09669 13.5191 6.87232 13.5191C4.64794 13.5191 2.84473 15.3223 2.84473 17.5467C2.84473 19.7711 4.64794 21.5743 6.87232 21.5743Z M17.127 3.67236V11.1724M20.877 7.42274H13.377M3.12305 3.67236H10.6231V11.1724H3.12305V3.67236ZM13.377 13.7966H20.877V21.2966H13.377V13.7966ZM10.8999 17.5467C10.8999 19.7711 9.09669 21.5743 6.87232 21.5743C4.64794 21.5743 2.84473 19.7711 2.84473 17.5467C2.84473 15.3223 4.64794 13.5191 6.87232 13.5191C9.09669 13.5191 10.8999 15.3223 10.8999 17.5467Z',
},
{
text: 'Support',
description: 'The Supabase Support Team is ready to help',
url: '/support',
icon: 'M8.72766 9C9.27678 7.83481 10.7584 7 12.5001 7C14.7092 7 16.5001 8.34315 16.5001 10C16.5001 11.3994 15.2224 12.5751 13.4943 12.9066C12.9519 13.0107 12.5001 13.4477 12.5001 14M12.5 17H12.51M21.5 12C21.5 16.9706 17.4706 21 12.5 21C7.52944 21 3.5 16.9706 3.5 12C3.5 7.02944 7.52944 3 12.5 3C17.4706 3 21.5 7.02944 21.5 12Z',
},
]
}
+6 -6
View File
@@ -7,7 +7,7 @@ const solutions = {
icon: products.database.icon[24],
description:
"Every project is a full Postgres database, the world's most trusted relational database.",
description_short: '',
description_short: 'Fully portable Postgres database',
label: '',
url: '/database',
},
@@ -15,7 +15,7 @@ const solutions = {
name: PRODUCT_NAMES.AUTHENTICATION,
icon: products.authentication.icon[24],
description: 'Add user sign ups and logins, securing your data with Row Level Security.',
description_short: '',
description_short: 'User Management out of the box',
label: '',
url: '/auth',
},
@@ -23,7 +23,7 @@ const solutions = {
name: PRODUCT_NAMES.STORAGE,
icon: products.storage.icon[24],
description: 'Store, organize, and serve large files. Any media, including videos and images.',
description_short: '',
description_short: 'Serverless storage for any media',
label: '',
url: '/storage',
},
@@ -31,7 +31,7 @@ const solutions = {
name: PRODUCT_NAMES.FUNCTIONS,
icon: products.functions.icon[24],
description: 'Write custom code without deploying or scaling servers.',
description_short: '',
description_short: 'Deploy code globally on the edge',
label: '',
url: '/edge-functions',
},
@@ -40,7 +40,7 @@ const solutions = {
icon: products.realtime.icon[24],
description:
'Create multiplayer experiences by sharing, broadcasting, and listening to changes from other clients or the Database.',
description_short: '',
description_short: 'Synchronize and broadcast events',
label: '',
url: '/realtime',
},
@@ -48,7 +48,7 @@ const solutions = {
name: PRODUCT_NAMES.VECTOR,
icon: products.vector.icon[24],
description: 'Integrate your favorite ML-models to store, index and search vector embeddings.',
description_short: '',
description_short: 'AI toolkit to manage embeddings',
label: '',
url: '/vector',
},
+36
View File
@@ -0,0 +1,36 @@
import DevelopersDropdown from '~/components/Nav/DevelopersDropdown'
import ProductDropdown from '~/components/Nav/ProductDropdown'
import { data as DevelopersData } from 'data/Developers'
import SolutionsData from 'data/Solutions'
export const menu = {
primaryNav: [
{
title: 'Product',
hasDropdown: true,
dropdown: <ProductDropdown />,
dropdownContainerClassName: 'rounded-xl',
subMenu: SolutionsData,
},
{
title: 'Developers',
hasDropdown: true,
dropdown: <DevelopersDropdown />,
dropdownContainerClassName: 'rounded-xl',
subMenu: DevelopersData,
},
{
title: 'Pricing',
url: '/pricing',
},
{
title: 'Docs',
url: '/docs',
},
{
title: 'Blog',
url: '/blog',
},
],
}
+1 -1
View File
@@ -16,7 +16,7 @@ const CTABanner = dynamic(() => import('components/CTABanner/index'))
const CustomerStories = dynamic(() => import('components/CustomerStories'))
const TwitterSocialSection = dynamic(() => import('~/components/TwitterSocialSection'))
type Props = { customerStories: PostTypes[] }
type Props = { customerStories: PostTypes[]; blogPosts: PostTypes[] }
const Index = ({ customerStories }: Props) => {
return (
+1 -1
View File
@@ -2,7 +2,7 @@ import { NextSeo } from 'next-seo'
import { useRouter } from 'next/router'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import BecomeAPartner from '~/components/Partners/BecomeAPartners'
import BecomeAPartner from '~/components/Partners/BecomeAPartner'
import PartnerLinkBox from '~/components/Partners/PartnerLinkBox'
import supabase from '~/lib/supabase'
import { Partner } from '~/types/partners'
@@ -5,7 +5,7 @@ import { IconLoader, IconSearch, Input } from 'ui'
import { useDebounce } from 'use-debounce'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import BecomeAPartner from '~/components/Partners/BecomeAPartners'
import BecomeAPartner from '~/components/Partners/BecomeAPartner'
import PartnerLinkBox from '~/components/Partners/PartnerLinkBox'
import supabase from '~/lib/supabase'
import { Partner } from '~/types/partners'
+8 -8
View File
@@ -33,56 +33,56 @@
<title>Berri AI boosts productivity by migrating from AWS RDS to Supabase Vector</title>
<link>https://supabase.com/customers/berriai</link>
<description>Learn how Berri AI overcame challenges with self-hosting their vector database on AWS RDS and successfully migrated to Supabase.</description>
<pubDate>Mon, 05 Jun 2023 22:00:00 GMT</pubDate>
<pubDate>Mon, 05 Jun 2023 04:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/markprompt</guid>
<title>Markprompt and Supabase - GDPR-compliant AI chatbots for docs and websites.</title>
<link>https://supabase.com/customers/markprompt</link>
<description>AI-powered chatbot platform, Markprompt, empowers developers to deliver efficient and GDPR-compliant prompt experiences on top of their content, by leveraging Supabase&apos;s secure and privacy-focused vector database and authentication solutions.</description>
<pubDate>Tue, 16 May 2023 22:00:00 GMT</pubDate>
<pubDate>Tue, 16 May 2023 04:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/mendableai</guid>
<title>Mendable switches from Pinecone to Supabase Vector for PostgreSQL vector embeddings.</title>
<link>https://supabase.com/customers/mendableai</link>
<description>How Mendable boosts efficiency and accuracy of chat powered search for documentation using Supabase Vector.</description>
<pubDate>Thu, 04 May 2023 22:00:00 GMT</pubDate>
<pubDate>Thu, 04 May 2023 04:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/happyteams</guid>
<title>HappyTeams unlocks better performance and reduces cost with Supabase.</title>
<link>https://supabase.com/customers/happyteams</link>
<description>How a bootstrapped startup migrated from Heroku to Supabase in 30 minutes and never looked back.</description>
<pubDate>Wed, 15 Feb 2023 23:00:00 GMT</pubDate>
<pubDate>Wed, 15 Feb 2023 05:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/xendit</guid>
<title>Xendit use Supabase and create a full solution shipped to production in less than one week.</title>
<link>https://supabase.com/customers/xendit</link>
<description>As a payment processor, Xendit are responsible for verifying that all transactions are legal.</description>
<pubDate>Mon, 13 Feb 2023 23:00:00 GMT</pubDate>
<pubDate>Mon, 13 Feb 2023 05:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/replenysh</guid>
<title>Replenysh uses Supabase to implement OTP in less than 24 hours.</title>
<link>https://supabase.com/customers/replenysh</link>
<description>With Supabase, Replenysh gets a slick auth experience, reduces DevOps overhead, and continues to scale with Postgres.</description>
<pubDate>Mon, 13 Feb 2023 23:00:00 GMT</pubDate>
<pubDate>Mon, 13 Feb 2023 05:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/mobbin</guid>
<title>How Mobbin migrated 200,000 users from Firebase for a better authentication experience.</title>
<link>https://supabase.com/customers/mobbin</link>
<description>Mobbin helps over 200,000 creators globally search and view the latest design patterns from well-known apps.</description>
<pubDate>Mon, 13 Feb 2023 23:00:00 GMT</pubDate>
<pubDate>Mon, 13 Feb 2023 05:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/customers/epsilon3</guid>
<title>Epsilon3 digitize paper-based procedures in the space industry using telemetry data, simplifying testing and operations.</title>
<link>https://supabase.com/customers/epsilon3</link>
<description>Epsilon3 uses Supabase to help teams execute secure and reliable operations in an industry where project spend runs into the billions.</description>
<pubDate>Mon, 13 Feb 2023 23:00:00 GMT</pubDate>
<pubDate>Mon, 13 Feb 2023 05:00:00 GMT</pubDate>
</item>
</channel>
+3 -2
View File
@@ -162,12 +162,13 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
icon,
iconRight,
iconLeft,
htmlType = 'button',
htmlType,
...props
},
ref
) => {
const Comp = asChild ? Slot : 'button'
const resolvedHtmlType = asChild ? htmlType : 'button'
const { className, disabled } = props
const showIcon = loading || icon
// decrecating 'showIcon' for rightIcon
@@ -178,7 +179,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
return (
<Comp
ref={ref}
type={htmlType}
type={resolvedHtmlType}
{...props}
className={cn(buttonVariants({ type, size, disabled, block }), className)}
>
@@ -1,19 +1,44 @@
import { IconChevronRight } from 'ui'
import { IconChevronRight, cn } from 'ui'
import Link from 'next/link'
function TextLink({ url = '', label, ...props }: any) {
interface Props {
label: string
url?: string
className?: string
hasChevron?: boolean
chevronAnimation?: 'translate' | 'fadeIn'
}
function TextLink({
url = '',
label,
className,
hasChevron = true,
chevronAnimation = 'translate',
...props
}: Props) {
return (
<Link href={url} passHref>
<a
className="text-scale-1100 hover:text-scale-1200 mt-3 block cursor-pointer text-sm"
className={cn(
'text-scale-1100 hover:text-scale-1200 mt-3 block cursor-pointer text-sm focus-visible:ring-2 focus-visible:outline-none focus-visible:rounded-sm focus-visible:ring-foreground-lighter focus-visible:text-foreground-strong',
className
)}
{...props}
>
<div className="group flex items-center gap-1">
<span className="sr-only">{`${label} about ${url}`}</span>
<span>{label}</span>
<div className="transition-all group-hover:ml-0.5">
<IconChevronRight size={14} strokeWidth={2} />
</div>
{hasChevron && (
<div
className={cn(
'transition-all group-hover:ml-0.5',
chevronAnimation === 'fadeIn' && 'opacity-0 group-hover:opacity-100'
)}
>
<IconChevronRight size={14} strokeWidth={2} />
</div>
)}
</div>
</a>
</Link>
@@ -5,17 +5,22 @@ import { ChevronDown } from 'lucide-react'
import { cn } from '@ui/lib/utils'
interface NavigationMenuProps
extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> {
viewportClassName?: string | undefined
}
const NavigationMenu = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
NavigationMenuProps
>(({ className, viewportClassName, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn('relative z-10 flex flex-1 items-center justify-center', className)}
{...props}
>
{children}
<NavigationMenuViewport />
<NavigationMenuViewport className={viewportClassName} />
</NavigationMenuPrimitive.Root>
))
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
@@ -63,7 +68,7 @@ const NavigationMenuContent = React.forwardRef<
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
'left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ',
'left-0 top-0 w-full data-[motion^=from-]:animate-fade-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ',
className
)}
{...props}
@@ -80,7 +85,7 @@ const NavigationMenuViewport = React.forwardRef<
<div className={cn('absolute left-0 top-full flex justify-center')}>
<NavigationMenuPrimitive.Viewport
className={cn(
'origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-overlay text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]',
'origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-overlay text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=open]:fade-in data-[state=open]:slide-in-from-right-10 data-[state=open]:duration-100 data-[state=open]:ease-out data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=closed]:duration-75 md:w-[var(--radix-navigation-menu-viewport-width)]',
className
)}
ref={ref}
@@ -112,6 +112,7 @@ export default {
first:rounded-tl-md first:rounded-tr-md
last:rounded-bl-md last:rounded-br-md
overflow-hidden
will-change-transform
`,
trigger: `
flex flex-row