Merge pull request #5374 from supabase/feat/works-with-supabase-gallery

feat: works with gallery page start
This commit is contained in:
Jonathan Summers-Muir authored and GitHub committed 2022-03-28 16:49:04 +02:00
commit 66f0fb905d
82 files changed
+6901 -32790

No files matched your search

+494 -29143
View File
File diff suppressed because it is too large. Load diff
+23 -26
View File
@@ -13,15 +13,14 @@ date: '2022-03-08'
toc_depth: 3
---
Data auditing is a system that tracks changes to tables' contents over time.
Data auditing is a system that tracks changes to tables' contents over time.
PostgreSQL has a robust set of features which we can leverage to create a generic auditing solution in 150 lines of SQL.
Auditing is particularly useful for historical analysis. To demonstrate, imagine you have a `users` table that tracks when a user is online.
You might add a `status` column which can have one of two values: `online` and `offline`. How would you track how long a user is online for throughout an entire month?
Auditing is particularly useful for historical analysis. To demonstrate, imagine you have a `users` table that tracks when a user is online.
You might add a `status` column which can have one of two values: `online` and `offline`. How would you track how long a user is online for throughout an entire month?
An auditing system would track every change with timestamps, and so you can measure the difference between each timestamp and sum them up for the entire month.
The goals of our auditing solution are:
The goals of our auditing solution are:
- low maintenance
- easy to use
@@ -60,11 +59,11 @@ select * from audit.record_history
```
```markdown
id | record_id | old_record_id | op | ts | table_oid | table_schema | table_name | record | old_record
id | record_id | old_record_id | op | ts | table_oid | table_schema | table_name | record | old_record
----+--------------------------------------+--------------------------------------+--------+-------------------------------------+-----------+--------------+------------+--------------------------+--------------------------
2 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | | INSERT | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "foo"} |
3 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | UPDATE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "bar"} | {"id": 1, "name": "foo"}
4 | | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | DELETE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | | {"id": 1, "name": "bar"}
2 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | | INSERT | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "foo"} |
3 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | UPDATE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "bar"} | {"id": 1, "name": "foo"}
4 | | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | DELETE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | | {"id": 1, "name": "bar"}
(3 rows)
```
@@ -74,10 +73,9 @@ Notice that our `record_id` and `old_record_id` stayed constant as we updated th
### Namespace
To quote a tenet from [the zen of python](https://www.python.org/dev/peps/pep-0020/):
To quote a tenet from [the zen of python](https://www.python.org/dev/peps/pep-0020/):
> Namespaces are one honking great idea -- let's do more of those!
>
So first things first, we'll create a separate schema named `audit` to house our auditing entities.
@@ -89,12 +87,12 @@ create schema if not exists audit;
Next, we need a table to track inserts, updates and deletes.
Classically, an audit table's schema mirrors the table being audited and appends some metadata columns like the commit's timestamp. That solution has a few maintenance challenges:
Classically, an audit table's schema mirrors the table being audited and appends some metadata columns like the commit's timestamp. That solution has a few maintenance challenges:
- enabling auditing on a table requires a database migration
- when the source table's schema changes, the audit table's schema must also change
So instead, we'll lean on PostgreSQL's schema-less [`JSONB` data type](https://www.postgresql.org/docs/current/datatype-json.html) to store each record's data in a single column.
So instead, we'll lean on PostgreSQL's schema-less [`JSONB` data type](https://www.postgresql.org/docs/current/datatype-json.html) to store each record's data in a single column.
That approach has the added benefit of allowing us to store multiple tables' audit history in a single audit table.
```sql
@@ -110,23 +108,22 @@ create table audit.record_version(
table_schema name not null, -- audited table's schema name e.g. 'public'
table_name name not null, -- audited table's table name e.g. 'account'
-- record data
record jsonb, -- contents of the new record
record jsonb, -- contents of the new record
old_record jsonb -- previous record contents (for UPDATE/DELETE)
);
```
<details>
<summary>Postgres version compatibility</summary>
<p>
The table above uses PostgreSQL's built-in <a href="https://www.postgresql.org/docs/14/functions-uuid.html">uuid functionality</a>, which is available from version 14.
For backwards compatibility you can use the uuid-ossp extension.
</p>
<p>
<code>create extension if not exists "uuid-ossp";</code>
</p>
<summary>Postgres version compatibility</summary>
<p>
The table above uses PostgreSQL's built-in{' '}
<a href="https://www.postgresql.org/docs/14/functions-uuid.html">uuid functionality</a>, which
is available from version 14. For backwards compatibility you can use the uuid-ossp extension.
</p>
<p>
<code>create extension if not exists "uuid-ossp";</code>
</p>
</details>
### Query Patterns
@@ -134,7 +131,7 @@ An audit log doesn't do us much good if its too slow to query! There are 2 query
**Changes to a Table in a Time Range**
For time slices, we need an index on the `ts` column. Since the table is append-only and the `ts` column is populated by insertion date, our values for `ts` are naturally in ascending order.
For time slices, we need an index on the `ts` column. Since the table is append-only and the `ts` column is populated by insertion date, our values for `ts` are naturally in ascending order.
PostgreSQL's builtin [BRIN index](https://www.postgresql.org/docs/current/brin-intro.html) can leverage that correlation between value and physical location to produce an index that, at scale, is many hundreds of times smaller than the default (BTREE index) with faster lookup times.
@@ -213,7 +210,7 @@ as $$
select
uuid_generate_v5(
'fd62bc3d-8d6e-43c2-919c-802ba3762271',
(
(
jsonb_build_array(to_jsonb($1))
|| jsonb_agg($3 ->> key_)
)::text
+2 -5
View File
@@ -6,7 +6,6 @@ import SectionContainer from '../Layouts/SectionContainer'
import ImageCarousel from '../Carousels/ImageCarousel'
import TableViewCarouselData from 'data/products/database/table-view-carousel.json'
import { Typography } from '@supabase/ui'
import AdminAccessData from 'data/home/admin-access.json'
@@ -16,10 +15,8 @@ const AdminAccess = () => {
return (
<SectionContainer>
<div>
<Typography.Title level={2} className="mb-16">
Build your app without leaving the dashboard
</Typography.Title>
<div className="mb-16">
<h2 className="h3">Build your app without leaving the dashboard</h2>
</div>
<ImageCarousel content={AdminAccessData} altTabView={true} />
</SectionContainer>
+7 -7
View File
@@ -1,5 +1,5 @@
import TabMarkup from 'data/AdminAccess.json'
import { Tabs as SBTabs, Typography, Button } from '@supabase/ui'
import { Tabs as SBTabs, Button } from '@supabase/ui'
type Props = {
tabId: string
@@ -26,9 +26,9 @@ function Tabs(props: Props) {
>
<SBTabs.Panel id="tabTableEditor" label="Table Editor">
<div className="mt-5">
<Typography.Title level={3}>{TabMarkup['tabTableEditor'].header}</Typography.Title>
<h3>{TabMarkup['tabTableEditor'].header}</h3>
<dd className="mt-8">
<Typography.Text>{TabMarkup['tabTableEditor'].description}</Typography.Text>
<p>{TabMarkup['tabTableEditor'].description}</p>
</dd>
</div>
<a href={TabMarkup['tabTableEditor'].url}>
@@ -38,9 +38,9 @@ function Tabs(props: Props) {
<SBTabs.Panel id="tabSqlEditor" label=" SQL Editor">
<div className="mt-5">
<Typography.Title level={3}>{TabMarkup['tabSqlEditor'].header}</Typography.Title>
<h3>{TabMarkup['tabSqlEditor'].header}</h3>
<dd className="mt-8">
<Typography.Text>{TabMarkup['tabSqlEditor'].description}</Typography.Text>
<p>{TabMarkup['tabSqlEditor'].description}</p>
</dd>
</div>
<a href={TabMarkup['tabSqlEditor'].url}>
@@ -49,9 +49,9 @@ function Tabs(props: Props) {
</SBTabs.Panel>
<SBTabs.Panel id="tabAuthRules" label="Auth Rules">
<div className="mt-5">
<Typography.Title level={3}>{TabMarkup['tabAuthRules'].header}</Typography.Title>
<h3>{TabMarkup['tabAuthRules'].header}</h3>
<dd className="mt-8">
<Typography.Text>{TabMarkup['tabAuthRules'].description}</Typography.Text>
<p>{TabMarkup['tabAuthRules'].description}</p>
</dd>
</div>
<a href={TabMarkup['tabAuthRules'].url}>
@@ -4,10 +4,9 @@ import {
Badge,
Button,
Card,
Auth,
// Auth,
Space,
Tabs,
Typography,
IconCode,
IconSearch,
} from '@supabase/ui'
@@ -28,31 +27,31 @@ function AuthComponentExample() {
const { isDarkMode } = useTheme()
const { basePath } = useRouter()
// store API swiper instance
const [imageSwiper, setImageSwiper] = useState(undefined)
const [imageSwiperActiveIndex, setImageSwiperActiveIndex] = useState(0)
// // store API swiper instance
// const [imageSwiper, setImageSwiper] = useState(undefined)
// const [imageSwiperActiveIndex, setImageSwiperActiveIndex] = useState(0)
function handleNavChange(e: number) {
console.log(e)
setImageSwiperActiveIndex(e)
// @ts-ignore
imageSwiper.slideTo(e)
}
// function handleNavChange(e: number) {
// console.log(e)
// setImageSwiperActiveIndex(e)
// // @ts-ignore
// imageSwiper.slideTo(e)
// }
const AuthContainer = (props: any) => {
const { user } = Auth.useUser()
if (user) {
return (
<>
<Typography.Text>Signed in: {user.email}</Typography.Text>
<Button block onClick={() => props.supabaseClient.auth.signOut()}>
Sign out
</Button>
</>
)
}
return props.children
}
// const AuthContainer = (props: any) => {
// const { user } = Auth.useUser()
// if (user) {
// return (
// <>
// <p>Signed in: {user.email}</p>
// <Button block onClick={() => props.supabaseClient.auth.signOut()}>
// Sign out
// </Button>
// </>
// )
// }
// return props.children
// }
return (
<div className="sbui-tabs--alt">
@@ -96,11 +95,11 @@ function AuthComponentExample() {
width="96"
alt="Logo"
/>
<Typography.Title level={3}>{'Acme Company'}</Typography.Title>
<h3>{'Acme Company'}</h3>
</Space>
<Badge dot>Demo</Badge>
</div>
<Typography.Text>Sign in with</Typography.Text>
<p>Sign in with</p>
<div className="flex justify-evenly">
<Link href="https://supabase.com/docs/guides/auth/auth-facebook">
<Button type="default" size="large">
@@ -189,16 +188,14 @@ function AuthComponentExample() {
</div>
<div className="flex flex-row w-full items-center justify-center">
<div className="w-full h-[1px] bg-gray-500" />
<Typography.Text type="secondary" className="w-full text-center text-gray-500">
or continue with
</Typography.Text>
<p className="w-full text-center text-gray-500">or continue with</p>
<div className="w-full h-[1px] bg-gray-500" />
</div>
<Auth.UserContextProvider supabaseClient={supabase}>
{/* <Auth.UserContextProvider supabaseClient={supabase}>
<AuthContainer supabaseClient={supabase}>
<Auth supabaseClient={supabase} />
</AuthContainer>
</Auth.UserContextProvider>
</Auth.UserContextProvider> */}
</Space>
</Card>
</div>
@@ -208,7 +205,7 @@ function AuthComponentExample() {
<div className="p-8">
<CodeBlock
children={`import React, { useState } from 'react'
import { Auth, Typography, Button } from '@supabase/ui'
import { Auth, Button } from '@supabase/ui'
import { createClient } from '@supabase/supabase-js'
export default function app() {
@@ -222,7 +219,7 @@ export default function app() {
if (user) {
return (
<div>
<Typography.Text>Signed in: {user.email}</Typography.Text>
<p>Signed in: {user.email}</p>
<Button block onClick={() => props.supabaseClient.auth.signOut()}>
Sign out
</Button>
+10 -15
View File
@@ -5,7 +5,7 @@ import DefaultLayout from '~/components/Layouts/Default'
import CodeBlock from '~/components/CodeBlock/CodeBlock'
import {
Button,
Auth,
// Auth,
Card,
Checkbox,
Divider,
@@ -13,7 +13,6 @@ import {
Select,
Space,
Tabs,
Typography,
Input,
Toggle,
IconCode,
@@ -49,13 +48,11 @@ function AuthPage() {
<div className="container mx-auto px-8 sm:px-16 xl:px-20 relative py-32">
<div className="grid grid-cols-12">
<div className="col-span-8">
<Typography.Title level={2} className="mb-4">
ReactJS Auth Widget
</Typography.Title>
<Typography.Text>
<h2 className="mb-4">ReactJS Auth Widget</h2>
<p>
An API built from the groud up just for Storage.With powerful library clients coming
soon that allow for asset optimasation and image transformation
</Typography.Text>
</p>
</div>
<div className="col-span-8">
<div
@@ -85,16 +82,16 @@ function AuthPage() {
<div>
<Space size={3} direction="vertical">
<img src="https://app.supabase.io/img/supabase-dark.svg" width="96" />
<Typography.Title level={3}>{companyName}</Typography.Title>
<h3>{companyName}</h3>
</Space>
</div>
<Auth
{/* <Auth
providers={socialLogins ? socials : []}
socialButtonSize={widgetSize}
socialLayout={socialLoginLayout}
supabaseClient={supabase}
socialColors={socialColors}
/>
/> */}
</Space>
</Card>
</div>
@@ -109,7 +106,7 @@ function AuthPage() {
import { Auth } from './'
import { createClient } from '@supabase/supabase-js'
// @ts-ignore
import { Typography, Button, Space } from '../../index'
import { Button, Space } from '../../index'
export default function app() {
@@ -141,10 +138,8 @@ function AuthPage() {
<div className={`p-8`}>
<Space size={12} direction="vertical">
<div>
<Typography.Title level={3}>Customise your login form</Typography.Title>
<Typography.Text>
Our ReactJS Auth form will help you get going even quicker{' '}
</Typography.Text>
<h3>Customise your login form</h3>
<p>Our ReactJS Auth form will help you get going even quicker </p>
</div>
<Divider light />
<Input
+2 -3
View File
@@ -1,5 +1,4 @@
import React from 'react'
import { Typography } from '@supabase/ui'
interface Props {
title: string
@@ -10,9 +9,9 @@ export default function BlogHeader({ title }: Props) {
<div className="bg-white dark:bg-dark-800 overflow-hidden pt-4 pb-4 border-b dark:border-gray-600">
<div className="container mx-auto px-8 sm:px-16 xl:px-20">
<div className="mx-auto">
<Typography.Title>
<h1>
<span className="text-3xl">{title}</span>
</Typography.Title>
</h1>
</div>
</div>
</div>
+47 -37
View File
@@ -1,62 +1,72 @@
import { Space, Typography } from '@supabase/ui'
import authors from 'lib/authors.json'
import React from 'react'
import Image from 'next/image'
import React from 'react'
import PostTypes from '~/types/post'
interface Props {
blog: PostTypes
post: PostTypes
}
const BlogListItem = ({ blog }: Props) => {
const BlogListItem = ({ post }: Props) => {
// @ts-ignore
const author = blog.author ? authors[blog.author] : authors['supabase']
const authorArray = post.author && post.author.split(',')
const author = []
if (post.type === 'blog' && post.author) {
for (let i = 0; i < authorArray.length; i++) {
author.push(
// @ts-ignore
authors.find((authors: string) => {
// @ts-ignore
return authors.author_id === authorArray[i]
})
)
}
}
return (
<div key={blog.slug}>
<a href={`/blog/${blog.url}`}>
<div>
<a href={`/blog/${post.url}`}>
<div className="inline-block min-w-full group">
<div className="flex flex-col space-y-6">
<div className="flex flex-col space-y-3">
<div className="relative overflow-auto w-full h-60 border dark:border-dark shadow-sm rounded-lg mb-4">
<div
className={`relative overflow-auto w-full h-60 border border-scale-300 shadow-sm rounded-lg mb-4`}
>
<Image
layout="fill"
src={
!blog.thumb ? `/images/blog/blog-placeholder.png` : `/images/blog/${blog.thumb}`
!post.thumb
? `/images/blog/blog-placeholder.png`
: post.type === 'casestudy'
? post.thumb
: `/images/blog/${post.thumb}`
}
objectFit="cover"
className="transform duration-100 ease-in scale-100 group-hover:scale-105"
className="duration-100 ease-in transform scale-100 group-hover:scale-105"
/>
</div>
<div>
<Typography.Title level={3} className="m-0">
{blog.title}
</Typography.Title>
</div>
<Typography.Text type="secondary" small>
{blog.date}
</Typography.Text>
<Typography.Text className="m-0" type="secondary">
<p className="text-base mb-0">{blog.description}</p>
</Typography.Text>
<h3 className="max-w-sm text-xl text-scale-1200">{post.title}</h3>
{post.type === 'blog' && post.date && (
<p className="text-xs text-scale-1100">{post.date}</p>
)}
<p className="max-w-sm text-base text-scale-1100">{post.description}</p>
</div>
<div className="flex items-center -space-x-2">
{author.map((author: any) => {
return (
<div>
{author.author_image_url && (
<img
src={author.author_image_url}
className="w-10 rounded-full ring-2 ring-white dark:ring-scale-200"
/>
)}
</div>
)
})}
</div>
{author && (
<div>
<Space size={4}>
{author.author_image_url && (
<img src={author.author_image_url} className="rounded-full w-10" />
)}
<Space direction="vertical" size={0}>
<Typography.Text>{author.author}</Typography.Text>
<Typography.Text type="secondary" small>
{author.position}
</Typography.Text>
</Space>
</Space>
</div>
)}
</div>
</div>
</a>
+19 -26
View File
@@ -1,7 +1,5 @@
import { useRouter } from 'next/router'
import SectionHeader from 'components/UI/SectionHeader'
import ProjectExamples from 'data/ProjectExamples.json'
import { Button, Card, Typography, Space, IconGitHub } from '@supabase/ui'
import { Button, IconGitHub } from '@supabase/ui'
import SectionContainer from '../Layouts/SectionContainer'
import ExampleCard from '../ExampleCard'
@@ -14,29 +12,24 @@ const BuiltExamples = () => {
return (
<SectionContainer className="xl:pt-32">
<div className="text-center">
<Typography.Title level={2}>What can you build with supabase?</Typography.Title>
<Typography.Text>
<p className="lg:text-lg">
There are many example apps and starter projects to get going
</p>
<Space className="justify-center">
<Link href="/docs/guides/examples" as="/docs/guides/examples">
<a>
<Button type="default">View all examples</Button>
</a>
</Link>
<Link
href="https://github.com/supabase/supabase/tree/master/examples"
as="https://github.com/supabase/supabase/tree/master/examples"
>
<a>
<Button type="default" icon={<IconGitHub />}>
Official github library
</Button>
</a>
</Link>
</Space>
</Typography.Text>
<h3 className="h2">What can you build with supabase?</h3>
<p className="p">There are many example apps and starter projects to get going</p>
<div className="flex gap-2 justify-center py-4">
<Link href="/docs/guides/examples" as="/docs/guides/examples" passHref>
<Button as="a" type="default" size="small">
View all examples
</Button>
</Link>
<Link
href="https://github.com/supabase/supabase/tree/master/examples"
as="https://github.com/supabase/supabase/tree/master/examples"
passHref
>
<Button as="a" type="default" icon={<IconGitHub />} size="small">
Official github library
</Button>
</Link>
</div>
</div>
<div className="grid grid-cols-12 gap-5 mt-16">
{Examples.slice(0, 6).map((example, i) => {
+8 -6
View File
@@ -1,4 +1,4 @@
import { Button, Typography } from '@supabase/ui'
import { Button } from '@supabase/ui'
const CTABanner = (props: any) => {
const { darkerBg } = props
@@ -10,14 +10,16 @@ const CTABanner = (props: any) => {
`}
>
<div className="col-span-12">
<Typography.Title level={2}>
<span style={{ color: '#bbbbbb' }}>Build in a weekend,</span>
<span style={{ color: '#ffffff' }}> scale to millions</span>
</Typography.Title>
<h2 className="h2">
<span className="text-scale-900">Build in a weekend,</span>
<span className="text-scale-1200 dark:text-white"> scale to millions</span>
</h2>
</div>
<div className="col-span-12 mt-4">
<a href="https://app.supabase.io/">
<Button size="medium">Start your project</Button>
<Button size="medium" className="text-white">
Start your project
</Button>
</a>
</div>
</div>
+1 -1
View File
@@ -59,7 +59,7 @@ const Card = (props: Props) => {
<div className="flex-shrink-0 dark:bg-dark-800">
<img className="h-64 w-full object-cover" src={imgUrl} alt={title} />
</div>
<div className="flex-1 bg-white p-8 flex flex-col justify-between dark:bg-dark-700">
<div className="flex-1 bg-white p-8 flex flex-col justify-between dark:bg-scale-300">
<div className="flex-1">
{/* {icons && (
<div className="-mt-12 mb-6 flex -space-x-3">
+17 -37
View File
@@ -1,5 +1,5 @@
import { useRouter } from 'next/router'
import { Tabs, Typography, Button, IconCornerRightUp, IconArrowUpRight } from '@supabase/ui'
import { Tabs, Button, IconCornerRightUp, IconArrowUpRight } from '@supabase/ui'
import { useState } from 'react'
// Import Swiper React components
@@ -11,6 +11,7 @@ import 'swiper/swiper.min.css'
import ImageCarouselStyles from './ImageCarousel.module.css'
import Link from 'next/link'
import Image from 'next/image'
import TextLink from '../TextLink'
interface Content {
title: string
@@ -48,20 +49,20 @@ function ImageCarousel(props: ImageCarouselProps) {
const details = (
<div className="h-64 bg-white">
<Typography.Text>
<p>
<span className="block text-white mb-8">Allow fetch something</span>
</Typography.Text>
<Typography.Text type="secondary">
</p>
<p>
<p>
This would only allow the authenticated user access to a folder that is named after their
own account UID. This is useful for things like profile images.
</p>
</Typography.Text>
<Typography.Text>
</p>
<p>
<Button type="outline" size="small" icon={<IconCornerRightUp />}>
View documentation
</Button>
</Typography.Text>
</p>
</div>
)
@@ -133,17 +134,14 @@ function ImageCarousel(props: ImageCarouselProps) {
</div>
</div>
</div>
<div className="mt-8 lg:mt-0 col-span-12 lg:col-span-5 lg:col-start-8 xl:col-span-4 xl:col-start-9">
<div className="mt-8 lg:mt-0 col-span-12 lg:col-span-5 lg:col-start-8 xl:col-span-5 xl:col-start-8">
<div className={`sbui-tabs--underline-alt ${props.altTabView ? 'block' : 'hidden'} mb-3`}>
<Tabs
scrollable
type="underlined"
size="small"
// @ts-ignore
activeId={imageSwiperActiveIndex.toString()}
// @ts-ignore
onChange={(id: string) => handleImageSwiperNav(Number(id))}
block
>
{props.content.map((content: Content, i: number) => {
return (
@@ -151,9 +149,7 @@ function ImageCarousel(props: ImageCarouselProps) {
label={content.label ? content.label : content.title}
id={i.toString()}
key={i}
>
<span></span>
</Tabs.Panel>
/>
)
})}
</Tabs>
@@ -168,29 +164,13 @@ function ImageCarousel(props: ImageCarouselProps) {
>
{props.content.map((content, i) => {
return (
<SwiperSlide key={i}>
<div className="bg-white dark:bg-gray-800 p-px">
<Typography.Title level={4}>{content.title}</Typography.Title>
<Typography.Text>
<p className="text-base">{content.text}</p>
</Typography.Text>
{content.url && (
<Typography.Text>
<Link href={content.url} as={content.url}>
<a>
<Button
className="mb-8"
type="outline"
size="small"
icon={<IconArrowUpRight />}
>
{content.cta ? content.cta : 'View documentation'}
</Button>
</a>
</Link>
</Typography.Text>
)}
</div>
<SwiperSlide key={i} className="py-4">
<h4 className="text-xl text-scale-1200 mb-4">{content.title}</h4>
<p className="p text-base">{content.text}</p>
<TextLink
label={content.cta ? content.cta : 'View documentation'}
url={content.url}
/>
</SwiperSlide>
)
})}
@@ -2,7 +2,7 @@ import { useState } from 'react'
// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react'
import { Tabs, Typography, Button, Space } from '@supabase/ui'
import { Tabs, Button, Space } from '@supabase/ui'
import CodeBlock from '../CodeBlock/CodeBlock'
// Import Swiper styles
@@ -44,29 +44,30 @@ function SplitCodeBlockCarousel(props: SplitCodeBlockCarousel) {
const details = (
<div className="">
<Typography.Text>
<p>
<span className="block text-white mb-8">Allow fetch something</span>
</Typography.Text>
<Typography.Text type="secondary">
</p>
<p>
<p>
This would only allow the authenticated user access to a folder that is named after their
own account UID. This is useful for things like profile images.
</p>
</Typography.Text>
<Typography.Text>
</p>
<p>
<Button type="outline">View documentation</Button>
</Typography.Text>
</p>
</div>
)
return (
<div className="col-span-12 lg:col-span-6 lg:col-start-7 sbui-tabs--alt">
<div className="col-span-12 lg:col-span-6 lg:col-start-7 sbui-tabs--alt space-y-2">
<Tabs
scrollable
// @ts-ignore
activeId={apiSwiperActiveIndex.toString()}
// @ts-ignore
onChange={(id: string) => handleApiSwiperNavChange(Number(id))}
type="pills"
>
{props.content.map((extension, i) => {
return (
@@ -76,80 +77,73 @@ function SplitCodeBlockCarousel(props: SplitCodeBlockCarousel) {
)
})}
</Tabs>
<div className="border border-gray-100 dark:border-gray-600 rounded-md bg-gray-800 overflow-hidden">
<Swiper
// @ts-ignore
onSwiper={setApiSwiper}
style={{ zIndex: 0 }}
initialSlide={apiSwiperActiveIndex}
spaceBetween={0}
slidesPerView={1}
speed={300}
autoHeight={true}
allowTouchMove={false}
>
{props.content.map((extension, i) => {
return (
<SwiperSlide key={i}>
<CodeBlock key={i} lang={extension.lang}>
{extension.code}
</CodeBlock>
</SwiperSlide>
)
})}
</Swiper>
<div className="overflow-hidden p-8 bg-scale-100 dark:bg-scale-300 rounded-md border border-scale-500">
<Swiper
// @ts-ignore
onSwiper={setApiSwiper}
onSwiper={setSwiperDetails}
style={{ zIndex: 0 }}
initialSlide={apiSwiperActiveIndex}
initialSlide={0}
spaceBetween={0}
slidesPerView={1}
direction="horizontal"
// style={{ overflow: 'hidden' }}
speed={300}
autoHeight={true}
allowTouchMove={false}
>
{props.content.map((extension, i) => {
return (
<SwiperSlide key={i}>
<CodeBlock key={i} lang={extension.lang}>
{extension.code}
</CodeBlock>
<p>
<span className="block text-lg text-scale-1200 dark:text-white mb-4">
{extension.detail_title}
</span>
</p>
<p className="p block mb-6">{extension.detail_text}</p>
<Space className="justify-between">
{extension.url && (
<Link href={extension.url} as={extension.url}>
<a className="ml-px">
<Button as="a" type="default">
View documentation
</Button>
</a>
</Link>
)}
<div>
<p className="p mr-4">{extension.badges_label}</p>
{extension.badges &&
extension.badges.map((badge, i) => {
return <Badge key={badge}>{badge}</Badge>
})}
</div>
</Space>
</SwiperSlide>
)
})}
</Swiper>
<div className="overflow-hidden bg-gray-800 p-8 dark rounded-md border border-t-none border-t-0 border-gray-600">
<Swiper
// @ts-ignore
onSwiper={setSwiperDetails}
style={{ zIndex: 0 }}
initialSlide={0}
spaceBetween={0}
slidesPerView={1}
direction="horizontal"
// style={{ overflow: 'hidden' }}
speed={300}
allowTouchMove={false}
>
{props.content.map((extension, i) => {
return (
<SwiperSlide key={i}>
<div className="">
<Typography.Text>
<span className="block text-lg text-white mb-4">
{extension.detail_title}
</span>
</Typography.Text>
<Typography.Text type="secondary" className="block mb-6">
<p>{extension.detail_text}</p>
</Typography.Text>
<Space className="justify-between">
{extension.url && (
<Link href={extension.url} as={extension.url}>
<a>
<Typography.Text>
<Button type="outline">View documentation</Button>
</Typography.Text>
</a>
</Link>
)}
<div>
<Typography.Text type="secondary" className="mr-4">
{extension.badges_label}
</Typography.Text>
{extension.badges &&
extension.badges.map((badge, i) => {
return <Badge key={badge}>{badge}</Badge>
})}
</div>
</Space>
</div>
</SwiperSlide>
)
})}
</Swiper>
</div>
</div>
</div>
)
+5 -5
View File
@@ -1,4 +1,4 @@
import { Button, Typography } from '@supabase/ui'
import { Button } from '@supabase/ui'
import { useRouter } from 'next/router'
const Benchmark = () => {
@@ -18,17 +18,17 @@ const Benchmark = () => {
alt="Benchmark illustration"
/>
<div className="px-12 py-5 lg:px-0 lg:py-0">
<Typography.Title level={4}>
<h4>
<span className="text-white">Built for speed and reliability</span>
</Typography.Title>
<Typography.Text type="secondary">
</h4>
<p>
<span className="block text-gray-300">
Supabase outperforms equivalent systems by more than 300%.
</span>
<span className="block text-gray-300">
Sign up for our public benchmark release and we'll send it to your inbox.
</span>
</Typography.Text>
</p>
</div>
</div>
<div className="pb-12 px-12 col-span-1 lg:pb-0 lg:px-0 lg:col-span-2 rounded-r-md">
+31 -21
View File
@@ -1,8 +1,7 @@
import { useRouter } from 'next/router'
import Benchmark from 'components/CaseStudies/benchmark'
import SectionHeader from 'components/UI/SectionHeader'
import CaseStudiesData from 'data/CaseStudies.json'
import { Card, Space, Typography } from '@supabase/ui'
import { useRouter } from 'next/router'
import BlogListItem from '../Blog/BlogListItem'
import SectionContainer from '../Layouts/SectionContainer'
const CaseStudies = () => {
@@ -12,33 +11,44 @@ const CaseStudies = () => {
<SectionContainer>
<div>
<SectionHeader
className="mb-12"
title={'Scale up'}
title_alt={' with no extra effort'}
subtitle={'Enterprise Solutions'}
paragraph={
"Supabase is built with proven, enterprise-ready tools. We're supporting everything from fintech providers to social networks. "
"Supabase is built with proven, enterprise-ready tools.\n We're supporting everything from fintech providers to social networks. "
}
/>
</div>
<div className="mt-5 max-w-lg mx-auto grid gap-5 lg:grid-cols-3 lg:max-w-none">
{CaseStudiesData.map((caseStudy: any, idx: number) => (
<a href={caseStudy.url} key={idx}>
<Card
key={`caseStudy_${idx}`}
hoverable
cover={<img src={`${basePath}/${caseStudy.imgUrl}`} className="h-64 object-cover" />}
>
<Space className="justify-between h-40" direction="vertical">
<div className="grid max-w-lg gap-8 mx-auto mt-5 lg:gap-12 lg:grid-cols-3 lg:max-w-none">
{CaseStudiesData.map((caseStudy, idx: number) => (
<BlogListItem
key={idx}
post={{
type: 'casestudy',
title: caseStudy.title,
description: caseStudy.description,
thumb: `${basePath}/${caseStudy.imgUrl}`,
hideAuthor: true,
url: caseStudy.url.replace('/blog/', ''),
}}
/>
/* <a href={caseStudy.url} key={idx}>
<Card
key={`caseStudy_${idx}`}
hoverable
cover={
<img src={`${basePath}/${caseStudy.imgUrl}`} className="object-cover h-64" />
}
className="bg-scale-400"
>
<div>
<Typography.Text small type="secondary">
Project example
</Typography.Text>
<Typography.Title level={3}>{caseStudy.title}</Typography.Title>
<p className="p">Project example</p>
<h4 className="h5">{caseStudy.title}</h4>
</div>
<Typography.Text type="default">{caseStudy.description}</Typography.Text>
</Space>
</Card>
</a>
<p className="p">{caseStudy.description}</p>
</Card>
</a> */
))}
</div>
{/* <Benchmark /> */}
@@ -1,7 +1,6 @@
.code-block {
@apply rounded-lg;
/* padding-top: 8px !important; */
@apply border border-gray-600;
@apply rounded-lg overflow-hidden;
@apply border border-scale-500;
}
.code-block::before {
+1 -1
View File
@@ -66,7 +66,7 @@ function CodeBlock(props: Props) {
{!props.hideCopy && props.children ? (
<div className="absolute right-2 top-2 dark">
<CopyToClipboard text={props.children}>
<Button type="outline" className="dark:bg-dark-800" icon={<IconCopy />}>
<Button type="default" icon={<IconCopy />}>
Copy
</Button>
</CopyToClipboard>
+5 -43
View File
@@ -1,43 +1,5 @@
import { useTheme } from '~/components/Providers'
const SunEmoji = () => (
<svg width="32" height="32" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16 2a1.4 1.4 0 011.4 1.4v1.4a1.4 1.4 0 11-2.8 0V3.4A1.4 1.4 0 0116 2zM6.1 6.1a1.4 1.4 0 011.98 0l.99.99a1.4 1.4 0 11-1.98 1.98l-.99-.99a1.4 1.4 0 010-1.98zm19.8 0a1.4 1.4 0 010 1.98l-.99.99a1.4 1.4 0 01-1.98-1.98l.99-.99a1.4 1.4 0 011.98 0zM9 16a7 7 0 1114 0 7 7 0 01-14 0zm-7 0a1.4 1.4 0 011.4-1.4h1.4a1.4 1.4 0 110 2.8H3.4A1.4 1.4 0 012 16zm23.8 0a1.4 1.4 0 011.4-1.4h1.4a1.4 1.4 0 110 2.8h-1.4a1.4 1.4 0 01-1.4-1.4zm-2.87 6.93a1.4 1.4 0 011.98 0l.99.99a1.4 1.4 0 01-1.98 1.98l-.99-.99a1.4 1.4 0 010-1.98zm-15.84 0a1.4 1.4 0 011.98 1.98l-.99.99a1.4 1.4 0 01-1.98-1.98l.99-.99zM16 25.8a1.4 1.4 0 011.4 1.4v1.4a1.4 1.4 0 11-2.8 0v-1.4a1.4 1.4 0 011.4-1.4z"
fill="url(#paint0_linear)"
/>
<defs>
<linearGradient
id="paint0_linear"
x1="2"
y1="2"
x2="30"
y2="30"
gradientUnits="userSpaceOnUse"
>
<stop className="transition-all duration-200" stopColor="#bbbbbb" />
<stop className="transition-all duration-200" offset="1" stopColor="#bbbbbb" />
</linearGradient>
</defs>
</svg>
)
const MoonEmoji = () => (
<svg
width="24"
height="24"
fill="currentColor"
className="transition-colors duration-200 text-gray-300"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.353 2.939a1 1 0 01.22 1.08 8 8 0 0010.408 10.408 1 1 0 011.301 1.3A10.003 10.003 0 0112 22C6.477 22 2 17.523 2 12c0-4.207 2.598-7.805 6.273-9.282a1 1 0 011.08.22z"
/>
</svg>
)
import { IconSun, IconMoon } from '@supabase/ui'
function DarkModeToggle() {
const { isDarkMode, toggleTheme } = useTheme()
@@ -52,14 +14,14 @@ function DarkModeToggle() {
return (
<div className="flex items-center">
<SunEmoji />
<IconSun className="text-scale-900" strokeWidth={2} />
<button
type="button"
aria-pressed="false"
className={`
relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer
transition-colors ease-in-out duration-200 focus:outline-none ${
isDarkMode ? 'bg-dark-500' : 'bg-gray-200'
isDarkMode ? 'bg-scale-500' : 'bg-scale-900'
} mx-5
`}
onClick={() => toggleDarkMode()}
@@ -71,11 +33,11 @@ function DarkModeToggle() {
${
isDarkMode ? 'translate-x-5' : 'translate-x-0'
} inline-block h-5 w-5 rounded-full
bg-white dark:bg-dark-700 shadow-lg transform ring-0 transition ease-in-out duration-200
bg-white dark:bg-scale-300 shadow-lg transform ring-0 transition ease-in-out duration-200
`}
/>
</button>
<MoonEmoji />
<IconMoon className="text-scale-900" strokeWidth={2} />
</div>
)
}
+31 -39
View File
@@ -1,63 +1,55 @@
import {
Button,
Divider,
IconArrowUpRight,
IconGitHub,
IconTriangle,
Space,
Typography,
} from '@supabase/ui'
import { Button, Divider, IconArrowUpRight, IconGitHub, IconTriangle, Space } from '@supabase/ui'
import Link from 'next/link'
function ExampleCard(props: any) {
return (
<>
<div
className="h-40 bg-white dark:bg-gray-700
border-t border-r border-l border-gray-100 dark:border-gray-600
className="h-40
bg-white
dark:bg-scale-300
border
border-scale-400
border-t border-r border-l
p-5
flex flex-col justify-between
rounded rounded-b-none
"
>
<div className="mb-4">
<Typography.Title level={5} className="mb-1">
{props.title}
</Typography.Title>
<Typography.Text className="block">
<p>{props.description}</p>
<div className="flex flex-row items-center mb-2">
Created by:
<img
src={props.author_img}
className="ml-2 inline w-6 rounded-full border dark:border-gray-500"
/>
<span className="ml-2">{props.author}</span>
</div>
</Typography.Text>
<h4 className="h6">{props.title}</h4>
<p className="p text-sm">{props.description}</p>
<div>
<img src={props.author_img} className="inline w-6 rounded-full border-scale-500" />
<span className="text-sm ml-2 text-scale-1200">{props.author}</span>
</div>
</div>
</div>
<Divider light />
<div>
<div
className="
bg-white dark:bg-gray-800
border-b border-r border-l border-gray-100 dark:border-gray-600
bg-scale-100
dark:bg-scale-400
border border-scale-400
border-b border-r border-l border-t-0
p-5
flex flex-col justify-between
rounded rounded-t-none"
>
<Typography.Text>
<Link href={props.repo_url} as={props.repo_url}>
<a className="flex flex-row items-center" target="_blank">
<span>{props.repo_name}</span>
<span className="ml-1 inline-block">
<IconGitHub size="small" />
</span>
</a>
</Link>
</Typography.Text>
<Space className="mt-3">
<Link href={props.repo_url} as={props.repo_url} passHref>
<a
className="text-sm text-scale-1100 hover:text-scale-1200 flex flex-row items-center"
target="_blank"
>
<span>{props.repo_name}</span>
<span className="ml-1 inline-block">
<IconGitHub size={14} />
</span>
</a>
</Link>
<div className="flex gap-2 items-center mt-3">
{props.vercel_deploy_url && (
<a target="_blank" href={props.vercel_deploy_url}>
<img src="https://vercel.com/button" />
@@ -81,7 +73,7 @@ function ExampleCard(props: any) {
</a>
</Link>
)}
</Space>
</div>
</div>
</div>
</>
+3 -9
View File
@@ -1,15 +1,9 @@
import { Typography } from '@supabase/ui'
function FeatureColumn({ icon, title, text }: any) {
return (
<>
<Typography.Text>
<p>{icon}</p>
</Typography.Text>
<Typography.Title level={4}>{title}</Typography.Title>
<Typography.Text>
<p>{text}</p>
</Typography.Text>
{icon && <div className="mb-2">{icon}</div>}
<h4 className="text-base text-scale-1200 mb-4">{title}</h4>
<p className="p">{text}</p>
</>
)
}
+9 -15
View File
@@ -1,4 +1,4 @@
import { Button, Badge, Typography, IconArrowRight } from '@supabase/ui'
import { Button, Badge, IconArrowRight } from '@supabase/ui'
import SectionHeader from 'components/UI/SectionHeader'
import Solutions from 'data/Solutions.json'
import Link from 'next/link'
@@ -10,20 +10,16 @@ const Features = () => {
const IconSections = Object.values(Solutions).map((solution: any) => {
const { name, description, icon, label, url } = solution
return (
<div key={name} className="mb-10 md:mb-0">
<div key={name} className="mb-10 md:mb-0 space-y-4">
<div className="flex items-center">
<ProductIcon icon={icon} />
<dt className="flex flex-row xl:flex-col ml-4">
<Typography.Title level={4} className="m-0">
{name}
</Typography.Title>
</dt>
</div>
<div className="mt-5">
<Typography.Text>{description}</Typography.Text>
<dt className="text-scale-1200 flex flex-row xl:flex-col ml-4">{name}</dt>
</div>
<p className="p">{description}</p>
{label && (
<div className="mt-3">
<div>
<Badge dot>{label}</Badge>
</div>
)}
@@ -33,10 +29,8 @@ const Features = () => {
})
return (
<SectionContainer className="pb-0">
<Typography.Title level={2} className="mb-16">
Build faster and focus on your products
</Typography.Title>
<SectionContainer className="pb-0 space-y-16">
<h3 className="h3">Build faster and focus on your products</h3>
<dl className="grid grid-cols-1 sm:grid-cols-2 gap-y-4 md:grid-cols-2 lg:grid-cols-4 md:gap-16 lg:gap-x-8 xl:gap-x-24">
{IconSections}
</dl>
+27 -17
View File
@@ -4,6 +4,7 @@ import SectionContainer from '../Layouts/SectionContainer'
import DarkModeToggle from '../DarkModeToggle'
import Link from 'next/link'
import { useTheme } from '../Providers'
import { Badge } from '@supabase/ui'
const Footer = () => {
const { basePath } = useRouter()
@@ -11,7 +12,7 @@ const Footer = () => {
return (
<footer
className="bg-white dark:bg-dark-800 border-t border-gray-100 dark:border-gray-600"
className="border-t border-scale-500 dark:border-scale-600"
aria-labelledby="footerHeading"
>
<h2 id="footerHeading" className="sr-only">
@@ -34,14 +35,20 @@ const Footer = () => {
</a>
</Link>
<div className="flex space-x-5">
<a href="https://twitter.com/supabase" className="text-gray-300 hover:text-gray-400">
<a
href="https://twitter.com/supabase"
className="transition text-scale-900 hover:text-scale-1200"
>
<span className="sr-only">Twitter</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
</svg>
</a>
<a href="https://github.com/supabase" className="text-gray-300 hover:text-gray-400">
<a
href="https://github.com/supabase"
className="transition text-scale-900 hover:text-scale-1200"
>
<span className="sr-only">GitHub</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
@@ -52,7 +59,10 @@ const Footer = () => {
</svg>
</a>
<a href="https://discord.supabase.com/" className="text-gray-300 hover:text-gray-400">
<a
href="https://discord.supabase.com/"
className="transition text-scale-900 hover:text-scale-1200"
>
<span className="sr-only">Discord</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 71 55" aria-hidden="true">
<path
@@ -65,7 +75,7 @@ const Footer = () => {
<a
href="https://youtube.com/c/supabase"
className="text-gray-300 hover:text-gray-400"
className="transition text-scale-900 hover:text-scale-1200"
>
<span className="sr-only">Youtube</span>
<svg
@@ -84,23 +94,25 @@ const Footer = () => {
{FooterLinks.map((segment: any) => {
return (
<div key={`footer_${segment.title}`}>
<h3 className="text-sm text-gray-300 dark:text-dark-400">{segment.title}</h3>
<h6 className="text-base text-scale-1200 overwrite">{segment.title}</h6>
<ul className="mt-4 space-y-2">
{segment.links.map((link: any, idx: number) => (
<li key={`${segment.title}_link_${idx}`}>
<a
href={link.url}
className={`text-sm ${
className={`transition-colors text-sm ${
link.url
? 'text-gray-500 dark:text-dark-100'
: 'text-gray-400 dark:text-dark-200'
} hover:text-gray-900 dark:hover:text-gray-300`}
? 'text-scale-1100 hover:text-scale-1200 '
: 'text-scale-900 hover:text-scale-900'
} `}
>
{link.text}
{!link.url && (
<span className="block text-sm text-gray-300 dark:text-dark-300">
Coming soon
</span>
<div className="inline ml-2 xl:ml-0 xl:block 2xl:inline 2xl:ml-2 text-xs">
<Badge color="scale" size="small">
Coming soon
</Badge>
</div>
)}
</a>
</li>
@@ -112,10 +124,8 @@ const Footer = () => {
</div>
</div>
</div>
<div className="mt-32 border-t dark:border-dark pt-8 flex justify-between">
<p className="mb-0 self-center text-base text-gray-400 dark:text-dark-400">
&copy; Supabase Inc
</p>
<div className="mt-32 border-t border-scale-500 dark:border-scale-600 pt-8 flex justify-between">
<small className="small">&copy; Supabase Inc</small>
<DarkModeToggle />
</div>
</SectionContainer>
+61 -55
View File
@@ -1,4 +1,4 @@
import { Button, IconBookOpen, Space, Typography } from '@supabase/ui'
import { Button, IconBookOpen, Space } from '@supabase/ui'
import Link from 'next/link'
import { useRouter } from 'next/router'
import SectionContainer from './Layouts/SectionContainer'
@@ -14,70 +14,76 @@ const Hero = () => {
<div className="mx-auto">
<div className="lg:grid lg:grid-cols-12 lg:gap-16">
<div className="md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left lg:flex lg:items-center">
<div className="sm:mx-auto md:w-3/4 lg:w-full lg:mx-0">
<h1 className="mt-4 text-4xl text-gray-800 dark:text-white sm:mt-5 sm:leading-none lg:mt-6 lg:text-5xl xl:text-6xl">
<span className="block text-gray-800 dark:text-white">The Open Source</span>
<span className="text-brand-600 md:ml-0 block">Firebase Alternative</span>
</h1>
<Typography.Text>
<p className="mt-5 text-base sm:mt-5 lg:text-lg ">
Create a backend in less than 2 minutes. Start your project with a Postgres
Database, Authentication, instant APIs, Realtime subscriptions and Storage.
</p>
<p className="mt-3 text-base">Serverless functions coming soon</p>
</Typography.Text>
<Space>
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a className="mt-10">
<Button className="py-3" size="medium">
Start your project
</Button>
</a>
<div
className="
sm:mx-auto md:w-3/4 lg:w-full lg:mx-0
space-y-12"
>
<div>
<h1
className="
sm:leading-none
text-xl lg:text-4xl xl:text-5xl
text-scale-1200
"
>
<span className="block">The Open Source</span>
<span className="text-brand-900 md:ml-0 block">Firebase Alternative</span>
</h1>
<div>
<p className="p text-base sm:mt-5 lg:text-lg ">
Create a backend in less than 2 minutes. Start your project with a
Postgres Database, Authentication, instant APIs, Realtime subscriptions
and Storage.
</p>
<p className="p text-base">Serverless functions coming soon</p>
</div>
</div>
<div className="flex gap-2 items-center">
<Link href="https://app.supabase.io" as="https://app.supabase.io" passHref>
<Button as="a" size="small" className="text-white">
Start your project
</Button>
</Link>
<Link href="/docs" as="/docs">
<a className="mt-10">
<Button
className="py-3"
size="medium"
type="text"
icon={<IconBookOpen />}
>
Documentation
</Button>
</a>
<Link href="/docs" as="/docs" passHref>
<Button as="a" size="small" type="default" icon={<IconBookOpen />}>
Documentation
</Button>
</Link>
</Space>
<p className="mt-8 text-sm text-dark-400 tracking-wide sm:mt-10">backed by</p>
<div className="mt-5 w-full sm:max-w-lg lg:ml-0">
<div className="flex flex-wrap items-center justify-start">
<img
className="h-8 sm:h-10 pr-5 md:pr-10 mb-5"
src={`${basePath}/images/logos/yc--grey.png`}
alt="Y Combinator"
/>
<img
className="relative h-5 sm:h-7 pr-5 md:pr-10 mb-5"
src={`${basePath}/images/logos/mozilla--grey.png`}
alt="Mozilla"
/>
<img
className="relative h-5 sm:h-7 pr-5 md:pr-10 mb-5"
src={`${basePath}/images/logos/coatue.png`}
alt="Coatue"
/>
</div>
<div className="flex flex-col gap-4">
<small className="small text-xs">backed by</small>
<div className="w-full sm:max-w-lg lg:ml-0">
<div className="flex flex-wrap items-center justify-start">
<img
className="h-8 sm:h-10 pr-5 md:pr-10 mb-5"
src={`${basePath}/images/logos/yc--grey.png`}
alt="Y Combinator"
/>
<img
className="relative h-5 sm:h-7 pr-5 md:pr-10 mb-5"
src={`${basePath}/images/logos/mozilla--grey.png`}
alt="Mozilla"
/>
<img
className="relative h-5 sm:h-7 pr-5 md:pr-10 mb-5"
src={`${basePath}/images/logos/coatue.png`}
alt="Coatue"
/>
</div>
</div>
</div>
</div>
</div>
<div className="flex content-center mt-16 sm:mt-24 lg:absolute lg:-right-80 lg:w-9/12 lg:mt-0 lg:col-span-6 xl:relative xl:right-0 xl:w-full">
<div className="flex flex-col items-center justify-center relative w-full rounded-md">
<div className="w-full rounded-t-md h-5 bg-dark-700 flex items-center justify-start px-2">
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
<div className="w-full rounded-t-md h-5 bg-scale-400 flex items-center justify-start px-2">
<div className="h-2 w-2 mr-2 rounded-full bg-scale-800" />
<div className="h-2 w-2 mr-2 rounded-full bg-scale-800" />
<div className="h-2 w-2 mr-2 rounded-full bg-scale-800" />
</div>
<div
className="w-full relative bg-dark-900 shadow-lg rounded-b-md"
className="w-full relative bg-scale-1000 shadow-lg rounded-b-md"
style={{ padding: '56.25% 0 0 0' }}
>
<iframe
+2 -2
View File
@@ -87,8 +87,8 @@ const ImageGrid = ({
className={`
col-span-1 flex items-center justify-center
bg-gray-50
dark:bg-gray-700
${x.link && 'hover:bg-gray-100 dark:hover:bg-gray-600'}
dark:bg-scale-400
${x.link && 'hover:bg-gray-100 dark:hover:bg-scale-600'}
p-8 ${className}`}
>
<div
+1 -1
View File
@@ -24,7 +24,7 @@ const DefaultLayout = (props: Props) => {
return (
<>
{!hideHeader && <Nav />}
<div className="min-h-screen bg-white dark:bg-gray-800">
<div className="min-h-screen">
<main>{children}</main>
</div>
{!hideFooter && <Footer />}
+6 -1
View File
@@ -1,3 +1,5 @@
import classNames from 'classnames'
interface Props {
children: React.ReactNode
className?: string
@@ -5,7 +7,10 @@ interface Props {
const SectionContainer = ({ children, className }: Props) => (
<div
className={`container mx-auto px-6 lg:px-16 xl:px-20 relative py-16 sm:py-18 md:py-24 lg:py-24 ${className}`}
className={classNames(
`container mx-auto px-6 lg:px-16 xl:px-20 relative py-16 sm:py-18 md:py-24 lg:py-24`,
className
)}
>
{children}
</div>
@@ -132,7 +132,7 @@ const CodeExamples = () => {
<div className="grid grid-cols-12 gap-2 xl:gap-8">
<div className="col-span-12 text-center lg:col-span-3 lg:hidden">{<TabNav />}</div>
<div className="col-span-12 lg:col-span-9">
<div className="rounded-md rounded-b-lg bg-dark-600 dark:bg-dark-700">
<div className="rounded-md rounded-b-lg bg-dark-600 dark:bg-scale-300">
<div className="p-2 pl-5 flex justify-between items-center">
<p className="text-sm truncate mr-2 sm:text-base text-dark-100">
{exampleList[example].description}
+3 -3
View File
@@ -44,8 +44,8 @@ const Announcement = () => {
className="
relative
cursor-pointer
bg-gradient-to-r from-green-400 to-green-600
hover:from-green-500 hover:to-green-700
bg-gradient-to-r from-green-900 to-green-1000
hover:from-green-1000 hover:to-green-1100
text-white
flex flex-row space-x-3
"
@@ -53,7 +53,7 @@ const Announcement = () => {
<div
className="
flex items-center justify-center p-3 mx-auto text-sm lg:container lg:px-16 xl:px-20 font-medium
lg:divide-x divide-green-400
lg:divide-x divide-white
"
>
<span className="px-3 hidden lg:block">{announcement.text}</span>
+1 -1
View File
@@ -82,7 +82,7 @@ const Company = () => {
<div className="mt-6 text-sm font-medium">
<a
href="/blog"
className="text-gray-600 hover:text-gray-500 dark:text-brand-600 dark:hover:text-brand-700 transition ease-in-out duration-150"
className="text-gray-600 hover:text-gray-500 dark:text-brand-900 dark:hover:text-brand-700 transition ease-in-out duration-150"
>
View all posts <span aria-hidden="true">&rarr;</span>
</a>
+16 -21
View File
@@ -2,7 +2,6 @@ import React from 'react'
import DevelopersData from 'data/Developers.json'
import AnnouncementsData from 'data/Announcements.json'
import { useRouter } from 'next/router'
import { Typography } from '@supabase/ui'
type Props = {
text: string
@@ -13,38 +12,34 @@ type Props = {
const Developers = () => {
const { basePath } = useRouter()
const iconSections = Object.values(DevelopersData).map((company: Props) => {
const { text, description, url, icon } = company
const content = (
<a
href={url}
className="-m-3 p-3 flex items-start rounded-lg hover:bg-gray-50 dark:hover:bg-dark-700 transition ease-in-out duration-150"
>
<div className="-m-3 p-3 flex items-start rounded-lg hover:bg-gray-50 dark:hover:bg-scale-500 transition ease-in-out duration-150">
{/* <!-- Heroicon name: support --> */}
<svg
className="flex-shrink-0 h-6 w-6 stroke-gray dark:stroke-white "
className="flex-shrink-0 h-5 w-5 stroke-scale-900"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={icon} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d={icon} />
</svg>
<div className="ml-4">
<Typography.Title level={5}>{text}</Typography.Title>
<Typography.Text>
<p>{description}</p>
</Typography.Text>
<h5 className="text-base text-scale-1200">{text}</h5>
<p className="text-sm text-scale-900">{description}</p>
</div>
</a>
</div>
)
return url ? (
<a
key={text}
href={url}
className="p-3 col-span-6 rounded hover:bg-gray-50 dark:hover:bg-dark-700 transition"
className="p-3 col-span-6 rounded hover:bg-gray-50 dark:hover:bg-scale-500 transition"
>
{content}
</a>
@@ -60,18 +55,18 @@ const Developers = () => {
return (
<div className="grid grid-cols-12">
<nav className="col-span-6" aria-labelledby="solutionsHeading">
<div className="grid grid-cols-12 m-3 gap-3">{iconSections}</div>
<nav className="col-span-6 py-8" aria-labelledby="solutionsHeading">
<div className="grid grid-cols-12 m-3 gap-x-8 gap-y-4 py-4 pr-3">{iconSections}</div>
</nav>
<div className="col-span-6">
<div className="m-3 mx-6">
<Typography.Text type="secondary">Latest announcements</Typography.Text>
<p className="p">Latest announcements</p>
<ul className="mt-6 space-y-3 pb-6">
{AnnouncementsData.map((caseStudy: any, idx: number) => (
<li className="flow-root" key={`flyout_case_${idx}`}>
<a
href={caseStudy.url}
className="p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-700 transition ease-in-out duration-150 border dark:border-gray-600"
className="p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-700 transition ease-in-out duration-150 border items-center"
>
<div className="hidden sm:block flex-shrink-0">
<img
@@ -80,11 +75,11 @@ const Developers = () => {
alt="caseStudyThumb"
/>
</div>
<div className="min-w-0 flex-1 sm:ml-8">
<Typography.Title level={5} className="mb-0">
<div className="min-w-0 flex-1 sm:ml-4">
<h4 className="text-base text-scale-1200 mb-0 text-normal">
{caseStudy.title}
</Typography.Title>
<Typography.Text type="secondary">{caseStudy.description}</Typography.Text>
</h4>
<p className="p text-sm">{caseStudy.description}</p>
</div>
</a>
</li>
+14 -20
View File
@@ -3,7 +3,7 @@ import SolutionsData from 'data/Solutions.json'
import CaseStudiesData from 'data/CaseStudies.json'
import { useRouter } from 'next/router'
import ProductIcon from '../ProductIcon'
import { Typography, Badge } from '@supabase/ui'
import { Badge } from '@supabase/ui'
const Product = () => {
const { basePath } = useRouter()
@@ -15,18 +15,12 @@ const Product = () => {
<div className="flex lg:flex-col">
<div className="flex flex-row items-center">
<ProductIcon icon={icon} />
<div>
<Typography.Title level={5} className="ml-3 mb-0">
{name}
</Typography.Title>
</div>
<h5 className="ml-3 mb-0 text-base text-scale-1200 overwrite">{name}</h5>
</div>
<div className="ml-4 md:flex-1 md:flex md:flex-col md:justify-between lg:ml-0 lg:mt-4">
<Typography.Text>
<p className="mt-1 text-sm">{description}</p>
</Typography.Text>
<div className="ml-4 md:flex-1 md:flex md:flex-col md:justify-between lg:ml-0 mt-3">
<p className="text-sm text-scale-1000">{description}</p>
{label && (
<div>
<div className="mt-2">
<Badge>{label}</Badge>
</div>
)}
@@ -38,7 +32,7 @@ const Product = () => {
<a
key={name}
href={url}
className="p-3 col-span-6 rounded hover:bg-gray-50 dark:hover:bg-dark-700 transition"
className="p-3 col-span-6 rounded hover:bg-scale-300 dark:hover:bg-scale-500 transition"
>
{content}
</a>
@@ -48,12 +42,12 @@ const Product = () => {
return (
<div className="grid grid-cols-12">
<nav className="col-span-6" aria-labelledby="solutionsHeading">
<div className="grid grid-cols-12 m-3 gap-3">{iconSections}</div>
<nav className="col-span-6" aria-labelledby="product">
<div className="grid grid-cols-12 m-3 gap-x-8 gap-y-4 py-4 pr-3">{iconSections}</div>
</nav>
<div className="col-span-6">
<div className="m-3 mx-6">
<Typography.Text type="secondary">Latest case studies</Typography.Text>
<p className="p">Latest case studies</p>
<ul className="mt-6 space-y-3">
{CaseStudiesData.map((caseStudy: any, idx: number) => {
if (idx > 1) {
@@ -63,7 +57,7 @@ const Product = () => {
<li className="flow-root" key={`flyout_case_${idx}`}>
<a
href={caseStudy.url}
className="p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-700 transition ease-in-out duration-150 border dark:border-gray-600"
className="p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-700 transition ease-in-out duration-150 border items-center"
>
<div className="hidden sm:block flex-shrink-0">
<img
@@ -72,11 +66,11 @@ const Product = () => {
alt="caseStudyThumb"
/>
</div>
<div className="min-w-0 flex-1 sm:ml-8">
<Typography.Title level={5} className="mb-0">
<div className="min-w-0 flex-1 sm:ml-4">
<h4 className="text-base text-scale-1200 mb-0 text-normal">
{caseStudy.title}
</Typography.Title>
<Typography.Text>{caseStudy.description}</Typography.Text>
</h4>
<p className="p text-sm">{caseStudy.description}</p>
</div>
</a>
</li>
+66 -50
View File
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { Button, Badge, IconStar } from '@supabase/ui'
import { Button, Badge, IconStar, IconChevronDown } from '@supabase/ui'
import FlyOut from '~/components/UI/FlyOut'
import Transition from 'lib/Transition'
@@ -13,6 +13,7 @@ import Developers from '~/components/Nav/Developers'
import Announcement from '~/components/Nav/Announcement'
import { useTheme } from '~/components/Providers'
import TextLink from '../TextLink'
const Nav = () => {
const { basePath } = useRouter()
@@ -71,11 +72,11 @@ const Nav = () => {
</Badge>
)}
</p>
<p className="mt-1 text-sm text-gray-500 dark:text-dark-100">{description}</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-600 lg:mt-4">
Learn more <span aria-hidden="true">&rarr;</span>
<p className="mt-2 text-sm font-medium text-brand-900 lg:mt-4">
<TextLink label={label ? 'Get notified' : 'Learn more'} url={url} />
</p>
)}
</div>
@@ -85,7 +86,7 @@ const Nav = () => {
<a
key={`solution_${idx}`}
href={url}
className="-m-3 p-3 my-2 flex flex-col justify-between rounded-lg hover:bg-gray-50 dark:hover:bg-dark-600 transition ease-in-out duration-150"
className="-m-3 p-3 my-2 flex flex-col justify-between rounded-lg hover:bg-gray-50 dark:hover:bg-scale-600 transition ease-in-out duration-150"
>
{content}
</a>
@@ -109,7 +110,7 @@ const Nav = () => {
onClick={() => props.toggleFlyOut()}
>
<button
className="inline-flex items-center justify-center p-2 rounded-md bg-white text-gray-400 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-brand-500 dark:bg-gray-800 dark:hover:bg-gray-900"
className="inline-flex items-center justify-center p-2 rounded-md bg-gray-50 text-scale-900 hover:bg-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-brand-900 dark:bg-scale-200 dark:hover:bg-scale-300"
aria-expanded="false"
>
<span className="sr-only">Open main menu</span>
@@ -153,30 +154,27 @@ const Nav = () => {
<div
className={
`
inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:text-gray-700
dark:text-dark-100 cursor-pointer
inline-flex items-center
px-1 border-b-2 border-transparent
text-sm
font-medium
transition-colors
text-scale-1200 hover:text-brand-900
cursor-pointer
` + props.active
}
onClick={props.onClick}
>
<>
<span>{props.title}</span>
<svg
<div
className={
'ml-2 h-5 w-5 text-gray-300 group-hover:text-gray-300 transition ease-in-out duration-150' +
(props.active && ' transform rotate-180')
'flex items-center justify-center ml-2 h-5 w-5 text-scale-900 group-hover:text-scale-900 transition ease-in-out duration-150' +
(props.active && ' transform transition-all duration-100 rotate-180')
}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
<IconChevronDown size={14} strokeWidth={2} />
</div>
</>
</div>
)
@@ -185,7 +183,8 @@ const Nav = () => {
<>
<Announcement />
<div className="sticky top-0 z-50">
<nav className="bg-white dark:bg-gray-800 border-b dark:border-gray-600">
<div className="absolute w-full h-full bg-scale-200 top-0 opacity-80"></div>
<nav className="backdrop-blur-sm border-b border-scale-400">
{/* <div className="lg:container mx-auto relative flex justify-between h-16 lg:px-10 xl:px-0"> */}
<div className="lg:container mx-auto relative flex justify-between h-16 lg:px-16 xl:px-20">
<HamburgerButton toggleFlyOut={() => setOpen(true)} />
@@ -221,7 +220,7 @@ const Nav = () => {
href="/pricing"
className={`
inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium
text-gray-500 hover:text-gray-700 hover:border-gray-500 p-5
text-scale-1200 hover:text-brand-900 hover:border-brand-900 p-5
dark:text-dark-100 dark:hover:border-dark-100
`}
>
@@ -231,7 +230,7 @@ const Nav = () => {
href="/blog"
className={`
inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium
text-gray-500 hover:text-gray-700 hover:border-gray-500 p-5
text-scale-1200 hover:text-brand-900 hover:border-brand-900 p-5
dark:text-dark-100 dark:hover:border-dark-100
`}
>
@@ -239,20 +238,43 @@ const Nav = () => {
</a>
</div>
</div>
<div className="hidden lg:flex items-center sm:space-x-3">
<a href="https://github.com/supabase/supabase" target="_blank">
<Button
className="sbui-btn sbui-btn-text sbui-btn--tiny sbui-btn--text-align-center"
icon={<IconStar />}
>
Star us on GitHub
<div className="flex items-center gap-2">
<Button
as="a"
className="group hidden lg:flex"
// @ts-ignore
href="https://github.com/supabase/supabase"
target="_blank"
type="text"
icon={
<div className="flex items-center justify-center text-brand-800 w-4 h-4">
<div
className="text-scale-900 flex items-center justify-center w-3 h-3
group-hover:w-4
group-hover:h-4
group-hover:text-yellow-900
group-focus:w-4
group-focus:h-4
group-focus:text-yellow-900
transition-all"
>
<IconStar strokeWidth={2} />
</div>
</div>
}
>
Star us on GitHub
</Button>
<a href="https://app.supabase.io/">
<Button type="default" className="hidden lg:block">
Sign in
</Button>
</a>
<a href="https://app.supabase.io/">
<Button type="default">Sign in</Button>
</a>
<a href="https://app.supabase.io/">
<Button>Start your project</Button>
<Button className="text-white hidden lg:block">Start your project</Button>
</a>
</div>
</div>
@@ -270,13 +292,13 @@ const Nav = () => {
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<div className="p-4 md:p-8 h-screen w-screen fixed bg-white transform overflow-y-scroll -inset-y-0 z-50 dark:bg-dark-700">
<div className="p-4 md:p-8 h-screen w-screen fixed bg-white transform overflow-y-scroll -inset-y-0 z-50 dark:bg-scale-300">
<div className="absolute right-4 top-4 items-center justify-between">
<div className="-mr-2">
<button
onClick={() => setOpen(false)}
type="button"
className="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-brand-500 dark:bg-gray-700 dark:hover:bg-gray-800"
className="inline-flex items-center justify-center p-2 rounded-md bg-white text-scale-900 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-brand-900 dark:bg-scale-300 dark:hover:bg-scale-400"
>
<span className="sr-only">Close menu</span>
<svg
@@ -302,7 +324,7 @@ const Nav = () => {
<div className="pt-2 pb-4 space-y-1">
<a
href="https://app.supabase.io/"
className="block pl-3 pr-4 text-base font-medium text-gray-600 dark:text-white"
className="block pl-3 pr-4 text-base font-medium text-scale-900 dark:text-white"
>
Sign in
</a>
@@ -310,45 +332,39 @@ const Nav = () => {
<div className="pt-2 pb-4 space-y-1">
<a
href="/docs"
className="block pl-3 pr-4 py-2 text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 dark:hover:bg-dark-600 hover:border-gray-300 dark:text-white rounded-md"
>
Product
</a>
<a
href="/docs"
className="block pl-3 pr-4 py-2 text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 dark:hover:bg-dark-600 hover:border-gray-300 dark:text-white rounded-md"
className="block pl-3 pr-4 py-2 text-base font-medium text-scale-900 hover:bg-gray-50 dark:hover:bg-scale-600 hover:border-gray-300 dark:text-white rounded-md"
>
Developers
</a>
<a
href="/company"
className="block pl-3 pr-4 py-2 text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 dark:hover:bg-dark-600 hover:border-gray-300 dark:text-white rounded-md"
className="block pl-3 pr-4 py-2 text-base font-medium text-scale-900 hover:bg-gray-50 dark:hover:bg-scale-600 hover:border-gray-300 dark:text-white rounded-md"
>
Company
</a>
<a
href="/pricing"
className="block pl-3 pr-4 py-2 text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 dark:hover:bg-dark-600 hover:border-gray-300 dark:text-white rounded-md"
className="block pl-3 pr-4 py-2 text-base font-medium text-scale-900 hover:bg-gray-50 dark:hover:bg-scale-600 hover:border-gray-300 dark:text-white rounded-md"
>
Pricing
</a>
<a
href="https://github.com/supabase/supabase"
target="_blank"
className="block pl-3 pr-4 py-2 text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 dark:hover:bg-dark-600 hover:border-gray-300 dark:text-white rounded-md"
className="block pl-3 pr-4 py-2 text-base font-medium text-scale-900 hover:bg-gray-50 dark:hover:bg-scale-600 hover:border-gray-300 dark:text-white rounded-md"
>
GitHub
</a>
<a
href="/blog"
target="_blank"
className="block pl-3 pr-4 py-2 text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 dark:hover:bg-dark-600 hover:border-gray-300 dark:text-white rounded-md"
className="block pl-3 pr-4 py-2 text-base font-medium text-scale-900 hover:bg-gray-50 dark:hover:bg-scale-600 hover:border-gray-300 dark:text-white rounded-md"
>
Blog
</a>
</div>
<div className="p-3">
<p className="mb-6 text-sm text-gray-400">Products available:</p>
<p className="mb-6 text-sm text-scale-900">Products available:</p>
{iconSections}
</div>
</div>
+203
View File
@@ -0,0 +1,203 @@
import { SupabaseClient } from '@supabase/supabase-js'
import { Button, Form, Input, InputNumber, Select } from '@supabase/ui'
import { useState } from 'react'
import countries from '~/data/Countries.json'
import { PartnerContact } from '~/types/partners'
const INITIAL_VALUES = {
type: 'expert',
first: '',
last: '',
company: '',
size: '',
title: '',
email: '',
phone: '',
country: 'US',
details: '',
}
const validate = (values: any) => {
const errors: any = {}
if (!values.first) {
errors.first = 'Required'
}
if (!values.last) {
errors.last = 'Required'
}
if (!values.email) {
errors.email = 'Required'
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
return errors
}
export default function BecomeAPartner({ supabase }: { supabase: SupabaseClient }) {
const [formSubmitted, setFormSubmitted] = useState<boolean>(false)
const handleFormSubmit = async (values: any) => {
const { error } = await supabase.from<PartnerContact>('partner_contacts').insert(
[
{
type: values.type,
first: values.first,
last: values.last,
company: values.company,
size: Number(values.size),
title: values.title,
email: values.email,
website: values.email.split('@')[1],
phone: values.phone,
country: values.country,
details: values.details,
},
],
{ returning: 'minimal' }
)
// TODO: handle error
console.log('error:', error)
setFormSubmitted(true)
}
return (
<div className="border-t">
<div id="become-a-partner" className="max-w-2xl mx-auto space-y-12 py-12 px-6">
<h2 className="h2">Become a Partner</h2>
<Form initialValues={INITIAL_VALUES} validate={validate} onSubmit={handleFormSubmit}>
{({ isSubmitting }: any) => (
<div className="grid grid-cols-2 gap-x-6 gap-y-1">
<div className="h-24 col-span-2">
<Select
id="type"
name="type"
className="font-sans"
label="What type of partner are you?"
layout="vertical"
>
<Select.Option value="expert" selected={true}>
Expert (Agency &amp; Consulting)
</Select.Option>
<Select.Option value="technology">Technology</Select.Option>
</Select>
</div>
<div className="h-24">
<Input
label="First Name *"
id="first"
name="first"
layout="vertical"
placeholder="Jane"
/>
</div>
<div className="h-24">
<Input
label="Last Name *"
id="last"
name="last"
layout="vertical"
placeholder="Doe"
/>
</div>
<div className="h-24">
<Input
label="Company Name"
id="company"
name="company"
layout="vertical"
placeholder="Supa Inc."
/>
</div>
<div className="h-24">
<InputNumber
label="Company Size"
id="size"
name="size"
layout="vertical"
placeholder="1"
/>
</div>
<div className="h-24">
<Input
label="Job Title"
id="title"
name="title"
layout="vertical"
placeholder="CEO"
/>
</div>
<div className="h-24">
<Input
label="Business email *"
id="email"
name="email"
layout="vertical"
placeholder="janedoe@example.sg"
/>
</div>
<div className="h-24">
<Input
label="Phone Number"
id="phone"
name="phone"
layout="vertical"
placeholder="+65 1234 1234"
/>
</div>
<div className="h-24">
<Select
label="Country / Main Timezone"
id="country"
name="country"
layout="vertical"
>
{countries.map(({ code, name }) => (
<Select.Option value={code}>{name}</Select.Option>
))}
</Select>
</div>
<div className="col-span-2">
<Input.TextArea
id="details"
name="details"
label="Additional Details"
placeholder="Tell us about your projects, clients, and technology..."
rows={10}
/>
</div>
<div className="flex flex-row-reverse w-full col-span-2 pt-4">
<Button
size="xlarge"
disabled={formSubmitted}
loading={isSubmitting}
htmlType="submit"
>
Send
</Button>
</div>
</div>
)}
</Form>
{formSubmitted && <h3 className="h3">Thanks, we'll reach out to you shortly 👁⚡️👁</h3>}
</div>
</div>
)
}
@@ -0,0 +1,57 @@
import Link from 'next/link'
import React from 'react'
const PartnerLinkBox = ({
href,
title,
description,
icon,
color = 'amber',
}: {
href: string
title: string
description: string
icon?: React.ReactNode
color?: 'amber' | 'brand' | 'blue'
}) => {
const colors = {
amber:
'bg-amber-400 dark:bg-scale-100 group-hover:bg-amber-500 dark:group-hover:bg-amber-300 text-amber-900',
blue: 'bg-blue-400 dark:bg-scale-100 group-hover:bg-blue-500 dark:group-hover:bg-blue-300 text-blue-900',
brand:
'bg-brand-400 dark:bg-scale-100 group-hover:bg-brand-500 dark:group-hover:bg-brand-300 text-brand-900',
}
const content = (
<div
className="
cursor-pointer
hover:bg-scale-300
rounded
group px-5 py-4 bg-scale-200
border border-scale-500 dark:border-scale-400
"
>
<div className="flex flex-col gap-3">
<div
className={`${colors[color]}
rounded-md h-8 w-8
flex items-center justify-center
transition-all
group-hover:scale-110
`}
>
{icon}
</div>
<div>
<h5 className="text-base mb-2 text-scale-1200">{title}</h5>
<p className="p text-sm">{description}</p>
</div>
</div>
</div>
)
return <Link href={href}>{content}</Link>
}
export default PartnerLinkBox
+64
View File
@@ -0,0 +1,64 @@
import Image from 'next/image'
import Link from 'next/link'
import { Partner } from '~/types/partners'
export default function TileGrid({
partnersByCategory,
hideCategories = false,
}: {
partnersByCategory: { [category: string]: Partner[] }
hideCategories?: boolean
}) {
return (
<>
{Object.keys(partnersByCategory).map((category) => (
<div key={category} id={category.toLowerCase()} className="space-y-8">
{!hideCategories && <h2 className="h2">{category}</h2>}
<div className="grid gap-5 grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 lg:max-w-none">
{partnersByCategory[category].map((p) => (
<Link key={p.slug} href={`/partners/${p.slug}`}>
<a
className="
"
>
<div
className="
bg-scale-100 dark:bg-scale-300
hover:bg-scale-200 hover:dark:bg-scale-400
group flex flex-col w-full h-full px-6 py-6 transition-all
border rounded
shadow
hover:shadow-lg"
>
<div className="flex w-full space-x-6">
<div className="w-10 h-10 transition-all scale-100 group-hover:scale-110">
<Image
layout="fixed"
width={40}
height={40}
className="w-10 h-10 bg-gray-300 rounded-full"
src={p.logo}
alt={p.title}
/>
</div>
<div>
<h3 className="transition-colors text-xl text-scale-1100 group-hover:text-scale-1200 mb-2">
{p.title}
</h3>
<p className="text-sm text-scale-900">{p.description}</p>
</div>
</div>
</div>
</a>
</Link>
))}
</div>
</div>
))}
</>
)
}
+24 -30
View File
@@ -1,13 +1,13 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { IconHelpCircle, IconWatch, Space, Typography } from '@supabase/ui'
import { IconHelpCircle, IconWatch, Space } from '@supabase/ui'
import ReactTooltip from 'react-tooltip'
import ProductIcon from 'components/ProductIcon'
const Chevron = (props: any) => (
<>
<svg
className={`h-5 w-5 text-green-500`}
className={`h-5 w-5 text-green-900`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
@@ -25,7 +25,7 @@ const Chevron = (props: any) => (
const Minus = (props: any) => (
<>
<svg
className="h-5 w-5 text-gray-400"
className="h-5 w-5 text-scale-1200 dark:text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
@@ -46,34 +46,32 @@ export const PricingTableRowDesktop = (props: any) => {
return (
<>
<tr className="divide-x dark:divide-gray-600" style={{ borderTop: 'none' }}>
<tr className="divide-x dark:divide-scale-900" style={{ borderTop: 'none' }}>
<th
className="pricing-table__product-row bg-gray-50 dark:bg-gray-700 py-3 pl-6 text-sm font-medium text-gray-900 dark:text-white text-left"
className="pricing-table__product-row bg-scale-50 dark:bg-scale-300 py-3 pl-6 text-sm font-medium text-scale-1200 dark:text-white text-left"
scope="colgroup"
>
<Space size={4}>
<div className="flex items-center">
{props.icon && <ProductIcon icon={props.icon} />}
<Typography.Title className="m-0" level={4}>
{category.title}
</Typography.Title>
</Space>
<h4 className="m-0 ml-2">{category.title}</h4>
</div>
</th>
<td className="pricing-table__product-row bg-gray-50 dark:bg-dark-700 py-5 px-6"></td>
<td className="pricing-table__product-row bg-gray-50 dark:bg-dark-700 py-5 px-6"></td>
<td className="pricing-table__product-row bg-gray-50 dark:bg-dark-700 py-5 px-6"></td>
<td className="pricing-table__product-row bg-scale-50 dark:bg-scale-300 py-5 px-6"></td>
<td className="pricing-table__product-row bg-scale-50 dark:bg-scale-300 py-5 px-6"></td>
<td className="pricing-table__product-row bg-scale-50 dark:bg-scale-300 py-5 px-6"></td>
</tr>
{category.features.map((feat: any) => {
return (
<tr className="divide-x dark:divide-gray-600">
<tr className="divide-x divide-scale-600 dark:divide-scale-400">
<th
className="flex items-center py-5 px-6 text-sm font-normal text-gray-500 dark:text-gray-300 text-left"
className="flex items-center py-5 px-6 text-sm font-normal text-scale-1200 dark:text-white text-left"
scope="row"
>
<span>{feat.title} </span>
{feat.tooltip && (
<span
className="ml-2 cursor-pointer hover:text-gray-800 dark:hover:text-white"
className="ml-2 cursor-pointer hover:text-scale-800 dark:hover:text-white"
data-tip={feat.tooltip}
>
<IconHelpCircle size="small" />
@@ -89,7 +87,7 @@ export const PricingTableRowDesktop = (props: any) => {
) : typeof tier === 'boolean' && tier === false ? (
<Minus tier={tier} />
) : (
<span className="block text-sm text-gray-700 dark:text-white">{tier}</span>
<span className="block text-sm text-scale-1200 dark:text-white">{tier}</span>
)}
</td>
)
@@ -109,13 +107,11 @@ export const PricingTableRowMobile = (props: any) => {
return (
<>
<table className="mt-8 w-full">
<caption className="bg-gray-50 dark:bg-dark-900 border-t border-gray-200 dark:border-gray-600 py-3 px-4 text-sm font-medium text-gray-900 dark:text-white text-left">
<Space size={4}>
<caption className="bg-scale-50 dark:bg-dark-900 border-t border-scale-200 dark:border-scale-600 py-3 px-4 text-sm font-medium text-scale-1200 dark:text-white text-left">
<div className="flex items-center gap-2">
{category.icon ? <ProductIcon icon={props.icon} /> : null}
<Typography.Title className="m-0" level={4}>
{category.title}
</Typography.Title>
</Space>
<span className="text-scale-1200 font-normal">{category.title}</span>
</div>
</caption>
<thead>
<tr>
@@ -127,13 +123,13 @@ export const PricingTableRowMobile = (props: any) => {
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-600 ">
<tbody className="divide-y divide-scale-600">
{category.features.map((feat: any) => {
return (
<tr className="border-t border-gray-200 dark:border-gray-600 ">
<th className="py-5 px-4 text-sm font-normal text-gray-500 text-left" scope="row">
<tr className="border-t ">
<th className="py-5 px-4 text-sm font-normal text-scale-1100 text-left" scope="row">
<span>
<Typography.Text type="secondary">{feat.title}</Typography.Text>
<p>{feat.title}</p>
</span>
</th>
<td className="py-5 pr-4 text-right">
@@ -146,9 +142,7 @@ export const PricingTableRowMobile = (props: any) => {
<Minus tier={tier} />
</div>
) : (
<span className="block text-sm text-gray-700 dark:text-white">
{feat.tiers[tier]}
</span>
<span className="block text-sm text-scale-1200">{feat.tiers[tier]}</span>
)}
</td>
</tr>
+3 -3
View File
@@ -4,15 +4,15 @@ interface ProductIcon {
function ProductIcon({ icon }: ProductIcon) {
return (
<div className="inline-flex items-center justify-center rounded-md bg-gray-800 dark:bg-white text-white h-8 w-8">
<div className="inline-flex items-center justify-center rounded-md bg-scale-1200 text-scale-100 h-8 w-8">
<svg
className="h-4 w-4 stroke-white dark:stroke-black"
className="h-5 w-5 stroke-white dark:stroke-black"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={icon} />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d={icon} />
</svg>
</div>
)
+2 -2
View File
@@ -1,4 +1,4 @@
import { Space, Typography } from '@supabase/ui'
import { Space } from '@supabase/ui'
import React from 'react'
interface Props {
@@ -23,7 +23,7 @@ export default function Quote(props: Props) {
style={{ margin: 0 }}
/>
<figcaption style={{ marginTop: 0 }}>
<Typography.Text>{caption}</Typography.Text>
<p>{caption}</p>
</figcaption>
</Space>
</Space>
+3 -3
View File
@@ -1,6 +1,6 @@
import { useState } from 'react'
// Import Swiper React components
import { Button, IconArrowUpRight, Tabs, Typography } from '@supabase/ui'
import { Button, IconArrowUpRight, Tabs } from '@supabase/ui'
import { Swiper, SwiperSlide } from 'swiper/react'
import CodeBlock from '../CodeBlock/CodeBlock'
import Link from 'next/link'
@@ -35,8 +35,8 @@ function APISection(props: Props) {
return (
<div className="grid grid-cols-12 lg:gap-16">
<div className="col-span-12 lg:col-span-5 xl:col-span-5 pb-8">
<Typography.Title level={2}>{props.title}</Typography.Title>
<Typography.Text>{props.text}</Typography.Text>
<h2 className="h3">{props.title}</h2>
<p className="p">{props.text}</p>
{props.documentation_link && (
<Link href={props.documentation_link} as={props.documentation_link}>
<a>
+10 -10
View File
@@ -1,4 +1,4 @@
import { IconDatabase, Typography } from '@supabase/ui'
import { IconDatabase } from '@supabase/ui'
const FeatureSection = (props: any) => {
return (
@@ -7,8 +7,8 @@ const FeatureSection = (props: any) => {
<p className="mb-4">
<IconDatabase />
</p>
<Typography.Title level={4}>Write less code</Typography.Title>
<Typography.Text>
<h4>Write less code</h4>
<p>
<p className="lg:text-lg">
Use media in your project without needing to reply on external services or learn new
frameworks.
@@ -17,14 +17,14 @@ const FeatureSection = (props: any) => {
Familiar and easy to use permissions mean that your content is secure and accessible
only to the right users
</p>
</Typography.Text>
</p>
</div>
<div className="col-span-3 col-start-5">
<p className="mb-4">
<IconDatabase />
</p>
<Typography.Title level={4}>Blazing fast</Typography.Title>
<Typography.Text>
<h4>Blazing fast</h4>
<p>
<p className="lg:text-lg">
Use media in your project without needing to reply on external services or learn new
frameworks.
@@ -33,14 +33,14 @@ const FeatureSection = (props: any) => {
Familiar and easy to use permissions mean that your content is secure and accessible
only to the right users
</p>
</Typography.Text>
</p>
</div>
<div className="col-span-3 col-start-9">
<p className="mb-4">
<IconDatabase />
</p>
<Typography.Title level={4}>Dependable</Typography.Title>
<Typography.Text>
<h4>Dependable</h4>
<p>
<p className="lg:text-lg">
Use media in your project without needing to reply on external services or learn new
frameworks.
@@ -49,7 +49,7 @@ const FeatureSection = (props: any) => {
Familiar and easy to use permissions mean that your content is secure and accessible
only to the right users
</p>
</Typography.Text>
</p>
</div>
</div>
)
+19 -35
View File
@@ -7,15 +7,7 @@ import { Swiper, SwiperSlide } from 'swiper/react'
import SwiperCore, { Navigation, Pagination } from 'swiper'
import Link from 'next/link'
import {
Button,
Typography,
IconGitHub,
IconArrowLeft,
IconArrowRight,
Space,
IconBookOpen,
} from '@supabase/ui'
import { Button, IconGitHub, IconArrowLeft, IconArrowRight, IconBookOpen } from '@supabase/ui'
import Examples from '../../data/Examples.json'
import ExampleCard from '../ExampleCard'
@@ -34,36 +26,28 @@ function GithubExamples() {
<>
<div className="grid grid-cols-12">
<div className="col-span-12 text-center">
<Typography.Title level={2}>
Community driven examples, libraries and guides
</Typography.Title>
<Typography.Text className="block mb-6">
<p className="text-base lg:text-lg">
Supported by a network of early advocates, contributors, and champions.
</p>
</Typography.Text>
<div className="flex space-y-1.5 flex-col lg:space-y-0 lg:space-x-1.5 lg:flex-row justify-center">
<h2 className="h3">Community driven examples, libraries and guides</h2>
<p className="p ">
Supported by a network of early advocates, contributors, and champions.
</p>
<div className="flex items-center gap-2 justify-center py-4">
<Link href="/docs/guides/examples#guides" as="/docs/guides/examples#guides">
<a>
<Button type="default" icon={<IconBookOpen />}>
View guides
</Button>
</a>
<Button size="small" as="a" type="default" icon={<IconBookOpen size={12} />}>
View guides
</Button>
</Link>
<Link href="/docs/guides/examples" as="/docs/guides/examples">
<a>
<Button type="default">View all examples</Button>
</a>
<Button size="small" as="a" type="default">
View all examples
</Button>
</Link>
<Link
href="https://github.com/supabase/supabase/tree/master/examples"
as="https://github.com/supabase/supabase/tree/master/examples"
>
<a>
<Button type="default" icon={<IconGitHub />}>
Official GitHub library
</Button>
</a>
<Button size="small" as="a" type="default" icon={<IconGitHub size={12} />}>
Official GitHub library
</Button>
</Link>
</div>
</div>
@@ -117,14 +101,14 @@ function GithubExamples() {
})}
<div className="container mx-auto hidden md:flex flex-row justify-between mt-3">
<div ref={prevRef} className="cursor-pointer ml-4">
<Typography.Text>
<p>
<IconArrowLeft />
</Typography.Text>
</p>
</div>
<div ref={nextRef} className="cursor-pointer mr-4">
<Typography.Text>
<p>
<IconArrowRight />
</Typography.Text>
</p>
</div>
</div>
</Swiper>
+27 -24
View File
@@ -1,4 +1,4 @@
import { Button, IconBookOpen, IconKey, Space, Typography } from '@supabase/ui'
import { Button, IconBookOpen } from '@supabase/ui'
import Link from 'next/link'
import ProductIcon from '../ProductIcon'
@@ -16,41 +16,44 @@ interface Types {
const ProductHeader = (props: Types) => (
<div className="container mx-auto px-6 sm:px-16 xl:px-20 relative pt-16 lg:pt-32 pb-0">
<div className="grid grid-cols-12">
<div className="col-span-12 lg:col-span-5">
{props.icon || props.title ? (
<div className="flex flex-row mb-4 item-center">
{props.icon && <ProductIcon icon={props.icon} />}
{props.title && (
<Typography.Title level={4} className="ml-3" key={`product-name-${props.title}`}>
{props.title}
</Typography.Title>
)}
</div>
) : null}
<Typography.Title level={1} key={`h1`}>
{props.h1}
</Typography.Title>
{props.subheader && (
<Typography.Text>
{props.subheader.map((subheader, i) => {
<div className="col-span-12 lg:col-span-5 space-y-8">
<div>
{props.icon || props.title ? (
<div className="flex mb-4 items-center gap-3">
{props.icon && <ProductIcon icon={props.icon} />}
{props.title && (
<span className="text-scale-1200" key={`product-name-${props.title}`}>
{props.title}
</span>
)}
</div>
) : null}
<h1 className="h1" key={`h1`}>
{props.h1}
</h1>
</div>
<div>
{props.subheader &&
props.subheader.map((subheader, i) => {
return (
<p className="lg:text-lg" key={i}>
<p className="p lg:text-lg" key={i}>
{subheader}
</p>
)
})}
</Typography.Text>
)}
<div className="mt-12 flex flex-row md:flex-row md:items-center">
</div>
<div className="flex flex-row md:flex-row md:items-center">
<Link href="https://app.supabase.io/" as="https://app.supabase.io/">
<a>
<Button size="medium">Start a project</Button>
<Button size="medium" className="text-white">
Start a project
</Button>
</a>
</Link>
{props.documentation_url && (
<Link href={props.documentation_url} as={props.documentation_url}>
<a className="ml-2">
<Button type="text" size="medium" icon={<IconBookOpen />}>
<Button type="default" size="medium" icon={<IconBookOpen />}>
See documentation
</Button>
</a>
+34 -30
View File
@@ -6,15 +6,7 @@ import { Swiper, SwiperSlide } from 'swiper/react'
// import Swiper core and required modules
import SwiperCore, { Navigation, Pagination } from 'swiper'
import {
Button,
Typography,
IconMessageCircle,
Space,
IconAlignLeft,
IconArrowLeft,
IconArrowRight,
} from '@supabase/ui'
import { Button, IconMessageCircle, IconArrowLeft, IconArrowRight } from '@supabase/ui'
import Examples from '../../data/tweets/Tweets.json'
import TweetCard from '../TweetCard'
@@ -39,24 +31,36 @@ function TwitterSocialProof() {
<>
<div className="grid grid-cols-12">
<div className="col-span-12 text-center">
<Typography.Title level={2}>Join the community</Typography.Title>
<Typography.Text>
<p className="lg:text-lg">
Supported by a network of early advocates, contributors, and champions.
</p>
<div className="my-4">
<Link
href={'https://github.com/supabase/supabase/discussions'}
as={'https://github.com/supabase/supabase/discussions'}
<h3 className="h2">Join the community</h3>
<p className="p">
Supported by a network of early advocates, contributors, and champions.
</p>
<div className="my-8 flex gap-2 justify-center">
<Link href={'https://github.com/supabase/supabase/discussions'} passHref>
<Button
as="a"
// @ts-ignore
target="_blank"
size="small"
iconRight={<IconMessageCircle size={14} />}
type="default"
>
<a className="block text-sm text-gray-400 dark:text-gray-400 mt-3" target="_blank">
<Button size="small" iconRight={<IconMessageCircle size="tiny" />} type="default">
GitHub discussions
</Button>
</a>
</Link>
</div>
</Typography.Text>
GitHub discussions
</Button>
</Link>
<Link href={'https://github.com/supabase/supabase/discussions'} passHref>
<Button
as="a"
type="default"
// @ts-ignore
target="_blank"
size="small"
iconRight={<IconMessageCircle size={14} />}
>
Discord
</Button>
</Link>
</div>
</div>
</div>
<div className="mt-6">
@@ -111,14 +115,14 @@ function TwitterSocialProof() {
})}
<div className="container mx-auto hidden md:flex flex-row justify-between mt-3">
<div ref={prevRef} className="cursor-pointer ml-4">
<Typography.Text>
<p>
<IconArrowLeft />
</Typography.Text>
</p>
</div>
<div ref={nextRef} className="cursor-pointer mr-4">
<Typography.Text>
<p>
<IconArrowRight />
</Typography.Text>
</p>
</div>
</div>
</Swiper>
+9 -6
View File
@@ -1,14 +1,17 @@
import { IconArrowRight } from '@supabase/ui'
import { IconArrowRight, IconChevronRight } from '@supabase/ui'
import Link from 'next/link'
function TextLink({ url, label }: any) {
return (
<a href={url} className="block text-sm text-gray-400 dark:text-gray-400 mt-3">
<div className="flex items-center">
<a
href={url}
className="block text-sm text-scale-1100 hover:text-scale-1200 mt-3 cursor-pointer"
>
<div className="flex items-center gap-1 group">
<span>{label}</span>
<span className="ml-2">
<IconArrowRight size="tiny" />
</span>
<div className="transition-all group-hover:ml-0.5">
<IconChevronRight size={14} strokeWidth={2} />
</div>
</div>
</a>
)
+28 -17
View File
@@ -1,4 +1,4 @@
import { IconTwitter, Typography } from '@supabase/ui'
import { IconTwitter } from '@supabase/ui'
import Image from 'next/image'
interface TweetCard {
@@ -9,26 +9,37 @@ interface TweetCard {
function TweetCard(props: TweetCard) {
return (
<div className="bg-white dark:bg-dark-800 rounded-lg border border-gray-100 dark:border-gray-600 p-6">
<div
className="
bg-white dark:bg-scale-300 rounded-md
border border-scale-300 dark:border-scale-400 p-6
drop-shadow-sm
"
>
<div className="relative">
<div className="rounded-md w-12 h-12 border dark:border-gray-600 overflow-hidden">
<Image src={props.img_url} layout="responsive" width="64" height="64" />
</div>
<div
className="absolute -top-2 -left-2 w-6 h-6 flex justify-center items-center rounded-md"
style={{ background: '#00acee' }}
>
<div className="text-white">
<IconTwitter fill={'white'} size="tiny" />
<div className="flex items-center gap-2">
<div className="rounded-full w-10 h-10 border dark:border-gray-600 overflow-hidden">
<Image src={props.img_url} layout="responsive" width="64" height="64" />
</div>
<p className="text-sm font-medium text-scale-1200 mt-3">{props.handle}</p>
<div
className="absolute -left-1 -top-1 w-5 h-5 flex justify-center items-center rounded-full"
style={{ background: '#00acee' }}
>
<div className="text-white">
<IconTwitter fill={'white'} size={12} />
</div>
</div>
</div>
</div>
<Typography.Text type="secondary" className="block mt-3">
{props.handle}
</Typography.Text>
<Typography.Text>
<p className="text-base mt-3 text-gray-900 dark:text-white">"{props.quote}"</p>
</Typography.Text>
<p>
<p className="text-base mt-3 text-scale-1100">"{props.quote}"</p>
</p>
</div>
)
}
+23
View File
@@ -0,0 +1,23 @@
import SectionContainer from './Layouts/SectionContainer'
import TwitterSocialProof from './Sections/TwitterSocialProof'
const TwitterSocialSection = () => {
return (
<div className="relative">
<div className="section--masked">
<div className="section--bg-masked">
<div className="section--bg border-t border-b"></div>
</div>
<div className="section-container pt-12 pb-0">
<div className="overflow-x-hidden">
<SectionContainer className="mb-0 pb-8">
<TwitterSocialProof />
</SectionContainer>
</div>
</div>
</div>
</div>
)
}
export default TwitterSocialSection
+4 -4
View File
@@ -34,15 +34,15 @@ const FlyOut = (props: Props) => {
leaveTo="opacity-0 translate-y-1"
>
<>
<div className="absolute inset-x-0 transform shadow-lg border-gray-100 dark:border-dark-500 w-full hidden lg:block bg-white dark:bg-gray-600">
<div className="absolute inset-x-0 transform shadow-lg ">
<div
className="border-b dark:border-gray-600 absolute inset-0 flex sm:flex-col lg:flex-row"
className="border-b dark:border-scale-500 absolute inset-0 flex sm:flex-col lg:flex-row"
aria-hidden="true"
>
<div className="bg-white dark:bg-gray-800 border-r dark:border-gray-600 sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full" />
<div className="bg-white dark:bg-scale-200 border-r dark:border-scale-400 sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full" />
<div
className={`${
singleBgColor ? 'bg-white dark:bg-dark-600' : 'bg-gray-50 dark:bg-gray-800'
singleBgColor ? 'bg-white dark:bg-scale-200' : 'bg-gray-50 dark:bg-scale-200'
} sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full`}
/>
</div>
+10 -22
View File
@@ -1,28 +1,16 @@
import { Typography } from '@supabase/ui'
const SectionHeader = (props: any) => {
return (
<div className={'grid grid-cols-12 gap-1 sm:gap-8 ' + props.className}>
<div className="relative grid py-12 col-span-12 grid-cols-12">
<div className="col-span-12">
<span className="col-span-12 block mb-6 text-sm tracking-widest uppercase text-gray-300 font-mono dark:text-gray-300">
{props.subtitle}
</span>
<Typography.Title level={2}>
<span>{props.title}</span>
{props.title_alt && (
<span className="text-gray-400 dark:text-gray-300 inline">{props.title_alt}</span>
)}
</Typography.Title>
</div>
<div className="col-span-12 lg:col-span-7">
{props.paragraph && (
<Typography.Text type="secondary">
<p className="mt-3 mx-auto text-xl sm:mt-4">{props.paragraph}</p>
</Typography.Text>
)}
</div>
<div className={props.className}>
<div className="space-y-4">
<span className="block text-xs tracking-widest uppercase text-scale-900 font-mono">
{props.subtitle}
</span>
<h3 className="h2">
<span>{props.title}</span>
{props.title_alt && <span className="text-scale-1100 inline">{props.title_alt}</span>}
</h3>
</div>
{props.paragraph && <p className="p text-lg sm:mt-4 max-w-3xl">{props.paragraph}</p>}
</div>
)
}
+3 -4
View File
@@ -1,4 +1,3 @@
import { Card, Space, Typography } from '@supabase/ui'
import DeveloperSignups from 'data/DeveloperSignups'
type CommunityItem = {
@@ -37,7 +36,7 @@ const data: CommunityItem[] = [
invertImgDarkMode: true,
detail: () => (
<div>
<Typography.Text>Some growth chart?</Typography.Text>
<p>Some growth chart?</p>
</div>
),
},
@@ -48,7 +47,7 @@ const data: CommunityItem[] = [
img: 'twitter.png',
detail: () => (
<div>
<Typography.Text>Some twitter callouts</Typography.Text>
<p>Some twitter callouts</p>
</div>
),
},
@@ -59,7 +58,7 @@ const data: CommunityItem[] = [
img: 'discord.png',
detail: () => (
<div>
<Typography.Text>Something great</Typography.Text>
<p>Something great</p>
</div>
),
},
File diff suppressed because it is too large. Load diff
+9 -1
View File
@@ -7,7 +7,7 @@
"url": "/database"
},
{
"text": "Authentication",
"text": "Auth",
"url": "/auth"
},
{
@@ -39,6 +39,14 @@
"text": "Brand Assets / Logos",
"url": "/brand-assets"
},
{
"text": "Integrations",
"url": "/partners/integrations"
},
{
"text": "Experts",
"url": "/partners/experts"
},
{
"text": "System Status",
"url": "https://status.supabase.com/"
-3
View File
@@ -1,6 +1,3 @@
import { Card, Space, Typography } from '@supabase/ui'
import DeveloperSignups from 'data/DeveloperSignups'
type TeamMember = {
name: string
img: string
+90
View File
@@ -0,0 +1,90 @@
module.exports = {
brand: {
brand1: 'hsla(153, 80%, 99%, 1)',
brand2: 'hsla(148, 88%, 97%, 1)',
brand3: 'hsla(148, 76%, 95%, 1)',
brand4: 'hsla(148, 68%, 92%, 1)',
brand5: 'hsla(148, 59%, 88%, 1)',
brand6: 'hsla(149, 43%, 82%, 1)',
brand7: 'hsla(149, 43%, 69%, 1)',
brand8: 'hsla(154, 55%, 45%, 1)',
brand9: 'hsla(153, 60%, 53%, 1)',
brand10: 'hsla(153, 50%, 50%, 1)',
brand11: 'hsla(153, 50%, 34%, 1)',
brand12: 'hsla(153, 40%, 12%, 1)',
},
brandDark: {
brand1: 'hsla(153, 75%, 6%, 1)',
brand2: 'hsla(153, 73%, 7%, 1)',
brand3: 'hsla(154, 69%, 9%, 1)',
brand4: 'hsla(154, 67%, 11%, 1)',
brand5: 'hsla(154, 66%, 13%, 1)',
brand6: 'hsla(154, 64%, 17%, 1)',
brand7: 'hsla(154, 62%, 22%, 1)',
brand8: 'hsla(153, 60%, 28%, 1)',
brand9: 'hsla(153, 60%, 53%, 1)',
brand10: 'hsla(153, 60%, 70%, 1)',
brand11: 'hsla(153, 60%, 50%, 1)',
brand12: 'hsla(153, 60%, 95%, 1)',
},
contrast: {
'brand-hiContrast': 'var(--colors-brand-9)',
'brand-loContrast': 'var(--colors-brand-4)',
},
scale: {
scale1: 'var(--colors-slate1)',
scale2: 'var(--colors-slate2)',
scale3: 'var(--colors-slate3)',
scale4: 'var(--colors-slate4)',
scale5: 'var(--colors-slate5)',
scale6: 'var(--colors-slate6)',
scale7: 'var(--colors-slate7)',
scale8: 'var(--colors-slate8)',
scale9: 'var(--colors-slate9)',
scale10: 'var(--colors-slate10)',
scale11: 'var(--colors-slate11)',
scale12: 'var(--colors-slate12)',
},
scaleA: {
scaleA1: 'var(--colors-slatea1)',
scaleA2: 'var(--colors-slatea2)',
scaleA3: 'var(--colors-slatea3)',
scaleA4: 'var(--colors-slatea4)',
scaleA5: 'var(--colors-slatea5)',
scaleA6: 'var(--colors-slatea6)',
scaleA7: 'var(--colors-slatea7)',
scaleA8: 'var(--colors-slatea8)',
scaleA9: 'var(--colors-slatea9)',
scaleA10: 'var(--colors-slatea10)',
scaleA11: 'var(--colors-slatea11)',
scaleA12: 'var(--colors-slatea12)',
},
scaleDark: {
scale1: 'var(--colors-gray1)',
scale2: 'var(--colors-gray2)',
scale3: 'var(--colors-gray3)',
scale4: 'var(--colors-gray4)',
scale5: 'var(--colors-gray5)',
scale6: 'var(--colors-gray6)',
scale7: 'var(--colors-gray7)',
scale8: 'var(--colors-gray8)',
scale9: 'var(--colors-gray9)',
scale10: 'var(--colors-gray10)',
scale11: 'var(--colors-gray11)',
scale12: 'var(--colors-gray12)',
},
scaleADark: {
scaleA1: 'var(--colors-graya1)',
scaleA2: 'var(--colors-graya2)',
scaleA3: 'var(--colors-graya3)',
scaleA4: 'var(--colors-graya4)',
scaleA5: 'var(--colors-graya5)',
scaleA6: 'var(--colors-graya6)',
scaleA7: 'var(--colors-graya7)',
scaleA8: 'var(--colors-graya8)',
scaleA9: 'var(--colors-graya9)',
scaleA10: 'var(--colors-graya10)',
scaleA11: 'var(--colors-graya11)',
scaleA12: 'var(--colors-graya12)',
},
}
+94 -79
View File
@@ -1,101 +1,116 @@
{
"rory_wilding": {
"author": "Rory Wilding",
"author_url": "https://github.com/roryw10",
"author_image_url": "https://github.com/roryw10.png",
"position": "Head of Growth"
},
"ant_wilson": {
"author": "Ant Wilson",
"author_url": "https://github.com/awalias",
"author_image_url": "https://github.com/awalias.png",
"position": "CTO and Co-Founder"
},
"paul_copplestone": {
"author": "Paul Copplestone",
"author_url": "https://github.com/kiwicopple",
"author_image_url": "https://github.com/kiwicopple.png",
"position": "CEO and Co-Founder"
},
"angelico_de_los_reyes": {
[
{
"author_id": "angelico_de_los_reyes",
"author": "Angelico de los Reyes",
"position": "",
"author_url": "https://github.com/dragarcia",
"author_image_url": "https://avatars0.githubusercontent.com/u/26374889?s=400&u=f5e35e9b47a50fa2b4d8c4bb96babd921071bcf1&v=4",
"position": ""
"author_image_url": "https://github.com/dragarcia.png"
},
"thor_schaeff": {
"author": "Thor Schaeff",
"author_url": "https://github.com/thorwebdev",
"author_image_url": "https://github.com/thorwebdev.png",
"position": "DevRel & DX"
{
"author_id": "ant_wilson",
"author": "Ant Wilson",
"position": "CTO and Co-Founder",
"author_url": "https://github.com/awalias",
"author_image_url": "https://github.com/awalias.png"
},
"supabase": {
"author": "Supabase",
"author_url": "https://github.com/supabase",
"author_image_url": "https://github.com/supabase.png",
"position": ""
},
"inian": {
"author": "Inian Parameshwaran",
"author_url": "https://twitter.com/everConfusedGuy",
"position": "Engineering",
"username": "inian",
"author_image_url": "https://avatars.githubusercontent.com/u/2155545?v=4"
},
"soedirgo": {
{
"author_id": "soedirgo",
"author": "Bobbie Soedirgo",
"author_url": "https://github.com/soedirgo",
"position": "Engineering",
"username": "soedirgo",
"position": "Engineering",
"author_url": "https://github.com/soedirgo",
"author_image_url": "https://github.com/soedirgo.png"
},
"fracek": {
"author": "Francesco Ceccon",
"author_url": "https://github.com/fracek",
"position": "Engineering",
"username": "fracek",
"author_image_url": "https://github.com/fracek.png"
},
"steve_chavez": {
"author": "Steve Chavez",
"author_url": "https://github.com/steve-chavez",
"position": "Engineering & PostgREST maintainer",
"username": "steve-chavez",
"author_image_url": "https://github.com/steve-chavez.png"
},
"kangmingtay": {
"author": "Kang Ming Tay",
"author_url": "https://github.com/kangmingtay",
"position": "Engineering",
"username": "kangmingtay",
"author_image_url": "https://github.com/kangmingtay.png"
},
"div_arora": {
{
"author_id": "div_arora",
"author": "Div Arora",
"author_url": "https://github.com/darora",
"position": "Engineering",
"username": "darora",
"position": "Engineering",
"author_url": "https://github.com/darora",
"author_image_url": "https://github.com/darora.png"
},
"gurjeet": {
"author": "Gurjeet Singh",
"author_url": "https://github.com/gurjeet",
{
"author_id": "fracek",
"author": "Francesco Ceccon",
"username": "fracek",
"position": "Engineering",
"author_url": "https://github.com/fracek",
"author_image_url": "https://github.com/fracek.png"
},
{
"author_id": "gurjeet",
"author": "Gurjeet Singh",
"username": "gurjeet",
"position": "Engineering",
"author_url": "https://github.com/gurjeet",
"author_image_url": "https://github.com/gurjeet.png"
},
"wenbo_xie": {
"author": "Wen Bo Xie",
"author_url": "https://github.com/w3b6x9",
{
"author_id": "inian",
"author": "Inian Parameshwaran",
"username": "inian",
"position": "Engineering",
"username": "w3b6x9",
"author_image_url": "https://github.com/w3b6x9.png"
"author_url": "https://twitter.com/everConfusedGuy",
"author_image_url": "https://avatars.githubusercontent.com/u/2155545?v=4"
},
"oli_rice": {
"author": "Oliver Rice",
"author_url": "https://github.com/olirice",
{
"author_id": "kangmingtay",
"author": "Kang Ming Tay",
"username": "kangmingtay",
"position": "Engineering",
"author_url": "https://github.com/kangmingtay",
"author_image_url": "https://github.com/kangmingtay.png"
},
{
"author_id": "oli_rice",
"author": "Oliver Rice",
"username": "olirice",
"position": "Engineering",
"author_url": "https://github.com/olirice",
"author_image_url": "https://github.com/olirice.png"
},
{
"author_id": "paul_copplestone",
"author": "Paul Copplestone",
"position": "CEO and Co-Founder",
"author_url": "https://github.com/kiwicopple",
"author_image_url": "https://github.com/kiwicopple.png"
},
{
"author_id": "rory_wilding",
"author": "Rory Wilding",
"position": "Head of Growth",
"author_url": "https://github.com/roryw10",
"author_image_url": "https://github.com/roryw10.png"
},
{
"author_id": "steve_chavez",
"author": "Steve Chavez",
"username": "steve-chavez",
"position": "Engineering & PostgREST maintainer",
"author_url": "https://github.com/steve-chavez",
"author_image_url": "https://github.com/steve-chavez.png"
},
{
"author_id": "supabase",
"author": "Supabase",
"position": "",
"author_url": "https://github.com/supabase",
"author_image_url": "https://github.com/supabase.png"
},
{
"author_id": "thor_schaeff",
"author": "Thor Schaeff",
"position": "DevRel & DX",
"author_url": "https://github.com/thorwebdev",
"author_image_url": "https://github.com/thorwebdev.png"
},
{
"author_id": "wenbo_xie",
"author": "Wen Bo Xie",
"username": "w3b6x9",
"position": "Engineering",
"author_url": "https://github.com/w3b6x9",
"author_image_url": "https://github.com/w3b6x9.png"
}
}
]
+13 -1
View File
@@ -6,7 +6,14 @@ module.exports = withMDX({
trailingSlash: false,
images: {
domains: ['github.com', 'ca.slack-edge.com', 'res.cloudinary.com', 'images.unsplash.com'],
domains: [
'github.com',
'ca.slack-edge.com',
'res.cloudinary.com',
'images.unsplash.com',
'supabase.com',
'obuldanrptloktxcffvn.supabase.co',
],
},
async headers() {
return [
@@ -635,6 +642,11 @@ module.exports = withMDX({
source: '/guides/auth',
destination: '/guides/auth/intro',
},
{
permanent: false,
source: '/partners',
destination: '/partners/integrations',
},
]
},
})
+1800 -1505
View File
File diff suppressed because it is too large. Load diff
+11 -6
View File
@@ -17,12 +17,14 @@
"@mdx-js/react": "^1.6.22",
"@next/mdx": "^10.0.7",
"@supabase/supabase-js": "^1.6.0",
"@supabase/ui": "^0.36.1",
"classnames": "2.2.6",
"@supabase/ui": "v0.37.0-alpha.50",
"classnames": "^2.3.1",
"date-fns": "2.10.0",
"deepmerge": "^4.2.2",
"globby": "^12.0.2",
"gray-matter": "^4.0.2",
"markdown-toc": "^1.2.0",
"marked": "^4.0.12",
"next": "latest",
"next-mdx-remote": "^2.1.3",
"next-seo": "^4.20.0",
@@ -41,22 +43,25 @@
"swiper": "^6.5.0",
"swr": "^0.5.4",
"typescript": "^4.0.3",
"use-debounce": "^7.0.1",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/classnames": "^2.2.10",
"@tailwindcss/typography": "^0.5.2",
"@types/classnames": "^2.3.1",
"@types/jest": "^25.2.2",
"@types/marked": "^4.0.3",
"@types/node": "^14.0.1",
"@types/react": "^16.9.35",
"@types/react-copy-to-clipboard": "^4.3.0",
"@types/react-dom": "^16.9.8",
"@types/react-syntax-highlighter": "^13.5.0",
"autoprefixer": "^10.3.7",
"autoprefixer": "^10.4.2",
"file-loader": "^6.2.0",
"postcss": "^8.3.11",
"postcss": "^8.4.7",
"postcss-preset-env": "^6.7.0",
"prettier": "^2.2.1",
"tailwindcss": "^2.2.17"
"tailwindcss": "^3.0.23"
},
"license": "MIT"
}
+9 -10
View File
@@ -1,8 +1,7 @@
import { NextPage } from 'next'
import Image from 'next/image'
import { useEffect, useState } from 'react'
import Link from 'next/link'
import { Button, Typography } from '@supabase/ui'
import { Button } from '@supabase/ui'
import DefaultLayout from '../components/Layouts/Default'
@@ -42,14 +41,14 @@ const Error404 = () => {
</nav>
</div>
<div className="absolute">
<Typography.Title
<h1
className={`select-none filter transition opacity-[5%] duration-200 ${
show404 ? 'blur-sm' : 'blur-none'
}`}
style={{ fontSize: '28rem' }}
>
404
</Typography.Title>
</h1>
</div>
<div
className={`transition flex flex-col space-y-6 items-center justify-center ${
@@ -57,16 +56,16 @@ const Error404 = () => {
}`}
>
<div className="w-[320px] flex flex-col items-center justify-center space-y-3">
<Typography.Title level={3}>Looking for something? 🔍</Typography.Title>
<Typography.Text className="text-center">
<h1 className="text-2xl m-2">Looking for something? 🔍</h1>
<p className="text-center text-sm">
We couldn't find the page that you're looking for!
</Typography.Text>
</p>
</div>
<div className="flex items-center space-x-4">
<Link href="/">
<a>
<Button>Head back</Button>
</a>
<Button as="a" size="small" className="text-white">
Head back
</Button>
</Link>
</div>
</div>
+51 -63
View File
@@ -9,7 +9,6 @@ import {
IconShield,
IconX,
Space,
Typography,
} from '@supabase/ui'
import ApiExamples from 'data/products/auth/auth-api-examples'
import AuthSqlRulesExamples from 'data/products/auth/auth-sql-rules-examples'
@@ -25,6 +24,7 @@ import FeatureColumn from '~/components/FeatureColumn'
import FloatingIcons from '~/components/FloatingIcons'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import ProductIcon from '~/components/ProductIcon'
import APISection from '~/components/Sections/APISection'
import GithubExamples from '~/components/Sections/GithubExamples'
import ProductHeader from '~/components/Sections/ProductHeader'
@@ -128,41 +128,31 @@ function AuthPage() {
})}
</div>
</p>
<Typography.Title level={4}>All the social providers</Typography.Title>
<Typography.Text>
<p className="text-base">
Enable social logins with the click of a button. Google, Facebook, GitHub, Azure,
Gitlab, Twitter, Discord, and many more.
</p>
</Typography.Text>
<h4 className="h4">All the social providers</h4>
<p className="p text-base">
Enable social logins with the click of a button. Google, Facebook, GitHub, Azure,
Gitlab, Twitter, Discord, and many more.
</p>
</div>
<div className="mb-10 lg:mb-0 col-span-12 lg:col-span-3 lg:col-start-5">
<Typography.Text>
<p className="mb-4">
<IconLink />
</p>
</Typography.Text>
<Typography.Title level={4}>Fully integrated</Typography.Title>
<Typography.Text>
<p className="text-base">
Incredibly simple Auth, without a single external authentication service. Built-in
Authentication, Authorization, and User Management.
</p>
</Typography.Text>
<p className="p mb-4">
<IconLink />
</p>
<h4 className="h4">Fully integrated</h4>
<p className="p text-base">
Incredibly simple Auth, without a single external authentication service. Built-in
Authentication, Authorization, and User Management.
</p>
</div>
<div className="col-span-12 lg:col-span-3 lg:col-start-9">
<Typography.Text>
<p className="mb-4">
<IconShield />
</p>
</Typography.Text>
<Typography.Title level={4}>Own your data</Typography.Title>
<Typography.Text>
<p className="text-base">
User data stored in your Supabase database so you never have to worry about 3rd
party privacy issues. Host your data in 8 different locations.
</p>
</Typography.Text>
<p className="p mb-4">
<IconShield />
</p>
<h4 className="h4">Own your data</h4>
<p className="p text-base">
User data stored in your Supabase database so you never have to worry about 3rd
party privacy issues. Host your data in 8 different locations.
</p>
</div>
</div>
</SectionContainer>
@@ -174,7 +164,7 @@ function AuthPage() {
content={ApiExamples}
size="large"
text={[
<Typography.Text key={0}>
<p key={0}>
<p className="text-base lg:text-lg">
APIs that you can understand. With powerful libraries that work on client and
server-side applications.
@@ -184,7 +174,7 @@ function AuthPage() {
With powerful client libraries that work on both client and server-side
applications.
</p> */}
</Typography.Text>,
</p>,
]}
footer={[
<div className="grid grid-cols-12 md:gap-8 lg:gap-0 xl:gap-16 mt-8" key={0}>
@@ -227,15 +217,15 @@ function AuthPage() {
<SectionContainer>
<div className="grid grid-cols-12 lg:gap-16">
<div className="col-span-12 lg:col-span-5 mb-8">
<Typography.Title level={2}>User permissions without the middleware</Typography.Title>
<Typography.Text>
<p className="text-base lg:text-lg">
Supabase Auth works without any additional servers. Build Authorization rules with
Postgres' Row Level Security, controlling who can create, edit and delete specific
rows in your database.
</p>
<p>Policies can be written in SQL or using the dashboard online.</p>
</Typography.Text>
<h2 className="h3">User permissions without the middleware</h2>
<p className="p text-base lg:text-lg">
Supabase Auth works without any additional servers. Build Authorization rules with
Postgres' Row Level Security, controlling who can create, edit and delete specific
rows in your database.
</p>
<p className="p">Policies can be written in SQL or using the dashboard online.</p>
<Link href="/docs/guides/auth#policy-examples">
<a>
<Button size="small" type="default" className="mt-4" icon={<IconArrowUpRight />}>
@@ -253,34 +243,32 @@ function AuthPage() {
</div>
</SectionContainer>
<SectionContainer>
{/* <SectionContainer>
<div className="grid grid-cols-12 lg:gap-16">
<div className="order-last col-span-12 lg:order-first lg:col-span-6 mt-8 lg:mt-0">
<AuthComponentExample />
</div>
<div className="col-span-12 lg:col-span-6 lg:col-start-7 xl:col-span-4 xl:col-start-8">
<Space className="mb-4">
<div className="w-8 h-8 rounded-md bg-gray-900 dark:bg-white text-white dark:text-gray-900 flex justify-center items-center">
<IconKey size="small" strokeWidth={1.5} />
<div className="mb-4 flex items-center space-x-2">
<div className="w-8 h-8 rounded-md bg-gray-900 dark:bg-white text-white dark:text-gray-900 flex justify-center items-center">
<ProductIcon icon={Solutions['authentication'].icon} />
</div>
<Typography.Text type="secondary">
<p className="m-0">
<IconX />
</Typography.Text>
</p>
<img className="w-8" src={`${basePath}/images/product/auth/react-icon.svg`} />
</Space>
</div>
<h2 className="h3">React Auth</h2>
<p className="p text-base lg:text-lg">
Pre-built auth widgets to get started in minutes.
</p>
<p className="p">
Supabase provides React libraries which handle common scenarios, including logging
in, signing up, magic link and forgot password forms.
</p>
<Typography.Title level={2} className="mb-4">
React Auth
</Typography.Title>
<Typography.Text>
<p className="text-base lg:text-lg">
Pre-built auth widgets to get started in minutes.
</p>
<p>
Supabase provides React libraries which handle common scenarios, including logging
in, signing up, magic link and forgot password forms.
</p>
</Typography.Text>
<Link
href="https://github.com/supabase/ui#using-supabase-ui-auth"
as="https://github.com/supabase/ui#using-supabase-ui-auth"
@@ -310,7 +298,7 @@ function AuthPage() {
</div>
</div>
</div>
</SectionContainer>
</SectionContainer> */}
<CTABanner />
</DefaultLayout>
</>
+174 -176
View File
@@ -1,18 +1,16 @@
import Container from 'components/Container'
import CountUp from 'components/CountUp'
import CTABanner from 'components/CTABanner/index'
import FlyOut from 'components/UI/FlyOut'
import { AlphaNumbers, IntroductionSegments, PerformanceComparisonData } from 'data/BetaPage'
import authors from 'lib/authors.json'
import { APP_NAME, DESCRIPTION } from 'lib/constants'
import { NextSeo } from 'next-seo'
import Head from 'next/head'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import Container from 'components/Container'
import Layout from '~/components/Layouts/Default'
import CountUp from 'components/CountUp'
import FlyOut from 'components/UI/FlyOut'
import CTABanner from 'components/CTABanner/index'
import { APP_NAME, DESCRIPTION } from 'lib/constants'
import { AlphaNumbers, IntroductionSegments, PerformanceComparisonData } from 'data/BetaPage'
import { render } from 'react-dom'
import Link from 'next/link'
import { NextSeo } from 'next-seo'
import authors from 'lib/authors.json'
const site_title = `${APP_NAME} | We are now in Beta`
@@ -29,7 +27,7 @@ const NavFlyOutMenu = (props: any) => {
return (
<>
<div className="col-span-12 grid grid-cols-12 items-center border-dark-300 dark:border-dark-400 sm:border-r border-b border-dashed">
<div className="grid items-center grid-cols-12 col-span-12 border-b border-dashed border-dark-300 dark:border-dark-400 sm:border-r">
{segments.flat().map((segment: any) => (
<div
key={segment.key}
@@ -39,7 +37,7 @@ const NavFlyOutMenu = (props: any) => {
hover:bg-dark-100 dark:hover:bg-dark-500 border-dark-300 dark:border-dark-400 border-t sm:border-l border-dashed`}
>
<p className="flex items-center text-black dark:text-white">
<span className="font-mono text-xs text-dark-300 dark:text-dark-400 mr-2">
<span className="mr-2 font-mono text-xs text-dark-300 dark:text-dark-400">
0{segment.no}
</span>
<span>{segment.name}</span>
@@ -54,12 +52,12 @@ const NavFlyOutMenu = (props: any) => {
const VideoShot = (props: any) => {
const { src } = props
return (
<div className="p-2 rounded-xl bg-gradient-to-r from-green-400 via-violet-500 to-blue-500">
<div className="shadow-lg rounded-md" style={{ height: 'fit-content' }}>
<div className="w-full rounded-t-md h-5 bg-dark-400 flex items-center justify-start px-2">
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
<div className="p-2 rounded-xl bg-gradient-to-r from-green-400 via-violet-500 to-blue-500">
<div className="rounded-md shadow-lg" style={{ height: 'fit-content' }}>
<div className="flex items-center justify-start w-full h-5 px-2 rounded-t-md bg-dark-400">
<div className="w-2 h-2 mr-2 rounded-full bg-dark-500" />
<div className="w-2 h-2 mr-2 rounded-full bg-dark-500" />
<div className="w-2 h-2 mr-2 rounded-full bg-dark-500" />
</div>
<video className="rounded-b-md" src={src} autoPlay loop muted playsInline>
Your browser does not support the video tag
@@ -95,10 +93,10 @@ const SectionHeader = (props: any) => {
const { sectionNumber, header } = props
return (
<div className="col-span-12">
<span className="block font-mono text-3xl text-dark-300 dark:text-dark-400 mb-3">
<span className="block mb-3 font-mono text-3xl text-dark-300 dark:text-dark-400">
0{sectionNumber}
</span>
<h3 className="text-black dark:text-white text-6xl">{header}</h3>
<h3 className="text-6xl text-black dark:text-white">{header}</h3>
</div>
)
}
@@ -115,15 +113,15 @@ const Hero = (props: Props) => {
backgroundSize: '65%',
backgroundPosition: '120% 50%',
}}
className="py-16 lg:py-36 bg-gray-50 dark:bg-dark-800 bg-no-repeat"
className="py-16 bg-no-repeat lg:py-36 bg-gray-50 dark:bg-scale-300"
>
<div className="container mx-auto px-8 lg:px-28 py-20 h-full grid grid-cols-12 gap-4 items-center text-dark-300">
<div className="col-span-12 md:col-span-9 lg:col-span-8 xl:col-span-6 text-dark-400 dark:text-white">
<div className="container grid items-center h-full grid-cols-12 gap-4 px-8 py-20 mx-auto lg:px-28 text-dark-300">
<div className="col-span-12 md:col-span-9 lg:col-span-8 xl:col-span-6 text-scale-900 dark:text-white">
<p className="mb-10 text-4xl">Supabase is an open source Firebase alternative.</p>
<p className="text-2xl">
Today, we're moving to <span className="text-brand-700">Beta</span>
Today, we're moving to <span className="text-brand-900">Beta</span>
</p>
<time itemProp="datePublished" dateTime="2020-12-03" className="opacity-50 text-sm">
<time itemProp="datePublished" dateTime="2020-12-03" className="text-sm opacity-80">
Published December 3rd, 2020
</time>
</div>
@@ -134,88 +132,86 @@ const Hero = (props: Props) => {
const Introduction = () => {
return (
<div className="bg-white dark:bg-dark-900">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-4 text-dark-300">
<div className="col-span-12 sm:col-span-9 xl:col-span-8">
<p>
<span className="text-2xl block text-black">
After the launch of our{' '}
<a
href="https://news.ycombinator.com/item?id=23319901"
target="_blank"
className="text-brand-700 hover:text-brand-800"
>
Alpha
</a>{' '}
Program in June,
</span>
</p>
</div>
<div className="col-span-12 sm:col-span-9 xl:col-span-6 text-base mb-20 text-dark-400">
<p>
we've been fortunate to work with thousands of early adopters on improving both our Open
Source, and Hosted offerings. Companies like{' '}
<div className="container grid grid-cols-12 gap-4 px-8 py-20 mx-auto lg:px-28 text-dark-300">
<div className="col-span-12 sm:col-span-9 xl:col-span-8">
<p>
<span className="block text-2xl text-black">
After the launch of our{' '}
<a
href="/blog/2020/12/02/case-study-xendit"
href="https://news.ycombinator.com/item?id=23319901"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
Xendit
</a>
,{' '}
<a
href="/blog/2020/12/02/case-study-monitoro"
target="_blank"
className="text-brand-700 hover:text-brand-800"
>
Monitoro
</a>
, and{' '}
<a
href="/blog/2020/12/02/case-study-tayfa"
target="_blank"
className="text-brand-700 hover:text-brand-800"
>
TAYFA
Alpha
</a>{' '}
are using Supabase to ship more products, faster.
</p>
</div>
Program in June,
</span>
</p>
</div>
<div className="col-span-12 mb-20 text-base sm:col-span-9 xl:col-span-6 text-dark-400">
<p>
we've been fortunate to work with thousands of early adopters on improving both our Open
Source, and Hosted offerings. Companies like{' '}
<a
href="/blog/2020/12/02/case-study-xendit"
target="_blank"
className="text-brand-900 hover:text-brand-900"
>
Xendit
</a>
,{' '}
<a
href="/blog/2020/12/02/case-study-monitoro"
target="_blank"
className="text-brand-900 hover:text-brand-900"
>
Monitoro
</a>
, and{' '}
<a
href="/blog/2020/12/02/case-study-tayfa"
target="_blank"
className="text-brand-900 hover:text-brand-900"
>
TAYFA
</a>{' '}
are using Supabase to ship more products, faster.
</p>
</div>
<div className="col-span-12 text-base mb-10">
<p className="w-60 pb-2 border-b-2 border-dark-200 text-dark-400 dark:border-dark-400">
Alpha Program in Numbers
</p>
</div>
<div className="col-span-12 mb-10 text-base">
<p className="pb-2 border-b-2 w-60 border-dark-200 text-dark-400 dark:border-dark-400">
Alpha Program in Numbers
</p>
</div>
<div
id="alphaNumbers"
className="col-span-12 grid grid-cols-12 gap-y-12 lg:gap-y-20 mb-20 gap-x-3 sm:gap-x-0"
>
{AlphaNumbers.map((stat: any, idx: number) => (
<div
key={`stat_${idx}`}
className="col-span-6 sm:col-span-4 grid grid-cols-8 sm:grid-cols-12 gap-x-1 md:gap-x-0 xl:gap-x-6 items-center"
>
<div className="col-span-4 sm:col-span-4 md:col-span-3 xl:col-span-2">
<div className="w-12 h-12 rounded-md bg-dark-700 flex items-center justify-center dark:bg-white">
{stat.icon}
</div>
</div>
<div className="col-span-7 sm-col-span-8 md:col-span-9 xl:col-span-10 text-dark-400">
<p className="text-5xl lg:text-6xl">
<CountUp triggerAnimOnScroll={true} referenceElId="alphaNumbers">
{stat.value}
</CountUp>
{stat.unit && <span className="text-2xl ml-1">{stat.unit}</span>}
</p>
</div>
<div className="col-span-12 sm:col-span-9 md:col-span-10 col-start-0 sm:col-start-5 md:col-start-4 xl:col-start-3">
<p className="text-xs lg:text-base text-dark-300 dark:text-dark-400">{stat.name}</p>
<div
id="alphaNumbers"
className="grid grid-cols-12 col-span-12 mb-20 gap-y-12 lg:gap-y-20 gap-x-3 sm:gap-x-0"
>
{AlphaNumbers.map((stat: any, idx: number) => (
<div
key={`stat_${idx}`}
className="grid items-center grid-cols-8 col-span-6 sm:col-span-4 sm:grid-cols-12 gap-x-1 md:gap-x-0 xl:gap-x-6"
>
<div className="col-span-4 sm:col-span-4 md:col-span-3 xl:col-span-2">
<div className="flex items-center justify-center w-12 h-12 rounded-md bg-scale-1200 dark:bg-white">
{stat.icon}
</div>
</div>
))}
</div>
<div className="col-span-7 sm-col-span-8 md:col-span-9 xl:col-span-10 text-dark-400">
<p className="text-5xl lg:text-6xl">
<CountUp triggerAnimOnScroll={true} referenceElId="alphaNumbers">
{stat.value}
</CountUp>
{stat.unit && <span className="ml-1 text-2xl">{stat.unit}</span>}
</p>
</div>
<div className="col-span-12 sm:col-span-9 md:col-span-10 col-start-0 sm:col-start-5 md:col-start-4 xl:col-start-3">
<p className="text-xs lg:text-base text-dark-300 dark:text-dark-400">{stat.name}</p>
</div>
</div>
))}
</div>
</div>
)
@@ -224,22 +220,22 @@ const Introduction = () => {
const TableOfContents = (props: any) => {
const { scrollTo } = props
return (
<div className="bg-gray-50 dark:bg-dark-700 text-dark-400 dark:text-dark-200">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-4">
<div className="col-span-12 text-base mb-10">
<div className="bg-gray-50 dark:bg-scale-300 text-dark-400 dark:text-dark-200">
<div className="container grid grid-cols-12 gap-4 px-8 py-20 mx-auto lg:px-28">
<div className="col-span-12 mb-10 text-base">
<p className="text-2xl text-black dark:text-white">
Supabase <span className="text-brand-600 dark:text-brand-700">Beta</span> is starting
Supabase <span className="text-brand-900 dark:text-brand-900">Beta</span> is starting
now.
</p>
</div>
<div className="col-span-12 grid grid-cols-12 gap-y-10">
<div className="grid grid-cols-12 col-span-12 gap-y-10">
{IntroductionSegments.map((segment: any, segmentIdx: number) => (
<div key={`introSegment_${segmentIdx}`} className="col-span-12 grid grid-cols-12">
<div key={`introSegment_${segmentIdx}`} className="grid grid-cols-12 col-span-12">
<div className="col-span-12 mb-10 sm:col-span-5 xl:col-span-4 sm:mb-0">
<p>{segment.description}</p>
</div>
<div className="hidden sm:block sm:col-span-1 xl:col-span-2" />
<div className="col-span-12 sm:col-span-6 flex flex-col">
<div className="flex flex-col col-span-12 sm:col-span-6">
{segment.chapters.map((chapter: any, idx: number) => (
<div
key={`section_select_${idx}`}
@@ -247,7 +243,7 @@ const TableOfContents = (props: any) => {
onClick={() => scrollTo(chapter.key)}
>
<p className="font-mono text-xs text-dark-300 dark:text-dark-400">{`0${chapter.no}`}</p>
<p className="ml-4 transition text-base border-b border-gray-400 hover:text-black dark:hover:text-white">
<p className="ml-4 text-base transition border-b border-gray-400 hover:text-black dark:hover:text-white">
{chapter.name}
</p>
</div>
@@ -308,18 +304,18 @@ const Performance = () => {
const multiplier = (metric.stats[0].value / metric.stats[1].value).toFixed(1)
return (
<div key={`${metric.key}`} className="mb-10 text-dark-400 dark:text-dark-300">
<p className="sm:w-36 pb-2 mb-4">{metric.title}</p>
<p className="pb-2 mb-4 sm:w-36">{metric.title}</p>
<div className="flex flex-col sm:flex-row sm:items-center">
<div className="w-full sm:w-5/6">
{metric.stats.map((stat: any, idx: number) => (
<div key={`metric_${metric.key}_${idx}`} className="flex items-center">
<p className="w-20 lg:w-24 border-r py-2 pr-4 mr-4 text-left sm:text-right">
<p className="w-20 py-2 pr-4 mr-4 text-left border-r lg:w-24 sm:text-right">
{stat.name}
</p>
<Bar
color={
stat.name === 'Supabase'
? 'bg-brand-600 dark:bg-brand-700'
? 'bg-brand-900 dark:bg-brand-800'
: 'bg-dark-300 dark:bg-dark-400'
}
finalPercentage={Math.ceil((stat.value / maxValue) * 100)}
@@ -328,9 +324,9 @@ const Performance = () => {
</div>
))}
</div>
<div className="text-left sm:w-1/6 sm:text-right flex flex-col">
<div className="flex flex-col text-left sm:w-1/6 sm:text-right">
<p className="text-6xl text-dark-700 dark:text-dark-100">{multiplier}x</p>
<p className="text-sm -mt-2">more {metric.key}s per second</p>
<p className="-mt-2 text-sm">more {metric.key}s per second</p>
</div>
</div>
</div>
@@ -342,11 +338,11 @@ const Performance = () => {
return (
<div id="performance" className="bg-white dark:bg-dark-800">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-300">
<SectionHeader sectionNumber={1} header="Performance" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10 items-center">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid items-center grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
We started Supabase to give developers a web-ready database that is delightful to use,
without sacrificing speed and scale. Postgres makes this possible, handling massive
@@ -358,7 +354,7 @@ const Performance = () => {
<a
href="https://elixir-lang.org/"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
Elixir
</a>{' '}
@@ -366,7 +362,7 @@ const Performance = () => {
<a
href="https://github.com/supabase/realtime"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
Realtime engine
</a>
@@ -374,7 +370,7 @@ const Performance = () => {
<a
href="https://postgrest.org/en/v7.0.0/"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
PostgREST
</a>{' '}
@@ -386,7 +382,7 @@ const Performance = () => {
<a
href="https://github.com/supabase/benchmarks/"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
benchmarks
</a>{' '}
@@ -397,7 +393,7 @@ const Performance = () => {
<div className="col-span-12 mt-10 mb-10">
<ComparisonChart />
</div>
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
Benchmarks were run from a neutral host (Digital Ocean Droplet 4 GB Memory / 80 GB
Disk / SGP1 - Ubuntu 20.04 (LTS) x64) against a table/collection pre-populated with 1
@@ -424,12 +420,12 @@ const Performance = () => {
const Security = () => {
return (
<div id="security" className="bg-gray-50 dark:bg-dark-700">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-200">
<div id="security" className="bg-gray-50 dark:bg-scale-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-200">
<SectionHeader sectionNumber={2} header="Security" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
As an infrastructure provider, security has been a priority from day one. While we had
to resolve brute force attacks on our customers' databases, we internally run pen
@@ -439,7 +435,7 @@ const Security = () => {
Approaching the launch of our Beta period, we worked with security advisors and
specialists globally to enforce new measures and processes:
</p>
<ul className="list-disc list-outside ml-6">
<ul className="ml-6 list-disc list-outside">
<li className="mb-5">
Employed DigitalXRAID to run a full Pen Test on both our internal and customer
infrastructure. We immediately patched one medium priority issue and are currently
@@ -459,7 +455,7 @@ const Security = () => {
<a
href="https://snyk.io/"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
Snyk
</a>{' '}
@@ -472,7 +468,7 @@ const Security = () => {
<a
href="https://github.com/PostgREST/postgrest/pull/1600#issuecomment-735257952"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
now uses
</a>{' '}
@@ -490,16 +486,16 @@ const Reliability = () => {
const { basePath } = useRouter()
return (
<div id="reliability" className="bg-white dark:bg-dark-800">
<div className="container mx-auto px-8 lg:px-28 py-12 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-300 ">
<div className="container grid grid-cols-12 px-8 py-12 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-300 ">
<SectionHeader sectionNumber={3} header="Reliability" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-5">
During Alpha we experienced 2 availability incidents, neither affecting customer
access to their data. These were:
</p>
<ul className="list-disc list-outside ml-6 mb-10">
<ul className="mb-10 ml-6 list-disc list-outside">
<li className="mb-5">
A third-party CDN API outage. As a result, subdomains were not created for new
projects.
@@ -519,7 +515,7 @@ const Reliability = () => {
<a
href="https://status.supabase.com"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
https://status.supabase.com
</a>{' '}
@@ -542,12 +538,12 @@ const Reliability = () => {
const NewFeaturesAndIntegrations = () => {
const { basePath } = useRouter()
return (
<div id="newFeaturesAndIntegrations" className="bg-gray-50 dark:bg-dark-700">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-200">
<div id="newFeaturesAndIntegrations" className="bg-gray-50 dark:bg-scale-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-200">
<SectionHeader sectionNumber={4} header="New Features & Integrations" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-5">
If you're new to Supabase, here's a few of the things you get when you choose us as
your backend.
@@ -563,7 +559,7 @@ const NewFeaturesAndIntegrations = () => {
<a
href="/docs/reference/javascript/auth-signup"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
JavaScript
</a>{' '}
@@ -571,7 +567,7 @@ const NewFeaturesAndIntegrations = () => {
<a
href="/docs/gotrue/server/about#endpoints"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
HTTP
</a>
@@ -593,7 +589,7 @@ const NewFeaturesAndIntegrations = () => {
<a
href="/docs/guides/client-libraries#realtime-changes"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
subscribe to changes in your database
</a>{' '}
@@ -612,7 +608,7 @@ const NewFeaturesAndIntegrations = () => {
<a
href="/docs/reference/javascript/select#query-foreign-tables"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
querying from multiple tables
</a>{' '}
@@ -620,7 +616,7 @@ const NewFeaturesAndIntegrations = () => {
<a
href="/docs/reference/javascript/rpc"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
invoke complex functions
</a>
@@ -628,7 +624,7 @@ const NewFeaturesAndIntegrations = () => {
</p>
</li>
<li className="mb-10">
<p className="w-44 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
<p className="pb-2 mb-2 border-b-2 w-44 border-dark-200 dark:border-dark-400">
Quickstart Templates
</p>
<p>
@@ -667,18 +663,18 @@ const NewFeaturesAndIntegrations = () => {
const BetaPricing = () => (
<div id="betaPricing" className="bg-white dark:bg-dark-800">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-300">
<SectionHeader sectionNumber={5} header="Beta Pricing" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">For up to date pricing info see: https://supabase.com/pricing</p>
<p className="mb-10">
We're working closely with many open source projects, infrastructure providers, and of
course our Alpha Users, to provide a predictable and sustainable pricing model.
</p>
<p className="mb-5">Our key aims going into this exercise were:</p>
<ul className="list-disc list-outside ml-6 mb-10">
<ul className="mb-10 ml-6 list-disc list-outside">
<li className="mb-5">
To continue offering free Supabase instances for Students, Hobbyists, and Early
Adopters
@@ -693,7 +689,7 @@ const BetaPricing = () => (
</li>
</ul>
<p className="mb-10">We are committing to the following initiatives:</p>
<ul className="list-decimal list-outside ml-6 mb-10">
<ul className="mb-10 ml-6 list-decimal list-outside">
<li className="mb-5">
All Alpha Users will receive credits equivalent of 2 years of base tier usage. These
will automatically be credited to your account if you signed up prior to December
@@ -709,7 +705,7 @@ const BetaPricing = () => (
<a
href="mailto:rory@supabase.io"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
rory@supabase.io
</a>
@@ -722,7 +718,7 @@ const BetaPricing = () => (
</ul>
<p>
The Supabase Base Tier is now called the Supabase Pro tier as per the{' '}
<a href="https://supabase.com/pricing" className="text-brand-700 hover:text-brand-800">
<a href="https://supabase.com/pricing" className="text-brand-900 hover:text-brand-900">
pricing page
</a>
.
@@ -734,12 +730,12 @@ const BetaPricing = () => (
)
const OpenSource = () => (
<div id="openSource" className="bg-gray-50 dark:bg-dark-700">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-200">
<div id="openSource" className="bg-gray-50 dark:bg-scale-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-200">
<SectionHeader sectionNumber={6} header="Open Source" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
Great software is multi generational and stretches beyond any single company.
</p>
@@ -753,7 +749,7 @@ const OpenSource = () => (
<a
href="https://github.com/sponsors/supabase/"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
sponsorship
</a>{' '}
@@ -765,7 +761,7 @@ const OpenSource = () => (
<a
href="/blog/2020/12/02/supabase-striveschool"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
partnering with Strive School
</a>{' '}
@@ -778,7 +774,7 @@ const OpenSource = () => (
<a
href="mailto:rory@supabase.io"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
rory@supabase.io
</a>{' '}
@@ -789,7 +785,7 @@ const OpenSource = () => (
<a
href="https://github.com/supabase"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
our GitHub.
</a>{' '}
@@ -802,10 +798,10 @@ const OpenSource = () => (
const FundingPartners = () => (
<div id="fundingPartners" className="bg-white dark:bg-dark-800">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-300">
<SectionHeader sectionNumber={7} header="Funding Partners" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
Building a platform that can offer all the amazing features of Firebase will take
resources - more than most open source tools. Ours will be a long journey and it will
@@ -825,7 +821,7 @@ const FundingPartners = () => (
<a
href="https://twitter.com/supabase"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
Twitter
</a>{' '}
@@ -838,12 +834,12 @@ const FundingPartners = () => (
)
const ScalingOurTeam = () => (
<div id="scalingOurTeam" className="bg-gray-50 dark:bg-dark-700">
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-200">
<div id="scalingOurTeam" className="bg-gray-50 dark:bg-scale-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-200">
<SectionHeader sectionNumber={8} header="Scaling Our Team" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
We are extremely proud of our team. We're a mix of 11 engineers, from 8 different
countries. Half of the team are previous founders - collectively we've founded 15
@@ -862,7 +858,7 @@ const ScalingOurTeam = () => (
<a
href="mailto:work@supabase.io"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
work@supabase.io
</a>
@@ -877,20 +873,20 @@ const ScalingOurTeam = () => (
const WhatsNext = () => (
<div
id="whatsNext"
className="border-b-2 border-gray-50 bg-white dark:bg-dark-800 dark:border-dark-800"
className="bg-white border-b-2 border-gray-50 dark:bg-dark-800 dark:border-dark-800"
>
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-y-10 text-dark-400 dark:text-dark-300">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto lg:px-28 gap-y-10 text-dark-400 dark:text-dark-300">
<SectionHeader sectionNumber={9} header="What's Next" />
<div className="col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8 mb-10">
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base">
<div className="grid grid-cols-12 col-span-12 mb-10 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-5">
If you watch our repos you'll see that development never stops, we ship every day (and
night!). We're constantly improving Supabase based on user feedback. Alongside
performance, security, and reliability improvements, here are some new features we're
working on for 2021:
</p>
<ul className="list-disc list-outside ml-6 mb-10">
<ul className="mb-10 ml-6 list-disc list-outside">
<li className="mb-5">Storage - Making blob storage easier than ever before</li>
<li className="mb-5">
Functions - write and deploy serverless functions that have access to your Supabase
@@ -923,7 +919,7 @@ const WhatsNext = () => (
<a
href="mailto:support@supabase.io"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
support@supabase.io
</a>{' '}
@@ -931,7 +927,7 @@ const WhatsNext = () => (
<a
href="https://github.com/supabase/supabase/discussions"
target="_blank"
className="text-brand-700 hover:text-brand-800"
className="text-brand-900 hover:text-brand-900"
>
discussion
</a>{' '}
@@ -943,6 +939,8 @@ const WhatsNext = () => (
</div>
)
const SUPABASE_AUTHOR_INDEX = 12
const Beta = (props: Props) => {
const [menuOpen, setMenuOpen] = useState<boolean>(false)
const { basePath } = useRouter()
@@ -989,7 +987,7 @@ const Beta = (props: Props) => {
//
// to do: author urls should be internal in future
// currently we have external links to github profiles
authors: [authors['supabase'].author_url],
authors: [authors[SUPABASE_AUTHOR_INDEX].author_url],
tags: ['beta'],
},
images: [
@@ -1001,7 +999,7 @@ const Beta = (props: Props) => {
/>
<Container>
<div className="sticky inset-0 z-50">
<div className="shadow-lg py-5 px-5 xl:px-20 bg-dark-800 flex items-center justify-between">
<div className="flex items-center justify-between px-5 py-5 shadow-lg xl:px-20 bg-scale-1200 dark:bg-scale-300">
<Link href="/">
<a>
<img className="h-5" src={`${basePath}/images/logo-dark.png`} />
+42 -33
View File
@@ -10,7 +10,7 @@ import { getSortedPosts, getAllCategories } from '~/lib/posts'
import authors from 'lib/authors.json'
import DefaultLayout from '~/components/Layouts/Default'
import { Typography, Tabs } from '@supabase/ui'
import { Tabs } from '@supabase/ui'
import PostTypes from '~/types/post'
import BlogListItem from '~/components/Blog/BlogListItem'
@@ -86,7 +86,7 @@ function Blog(props: any) {
]}
/>
<DefaultLayout>
<div className="bg-white dark:bg-dark-800 overflow-hidden py-12">
<div className="overflow-hidden py-12">
<div className="container mx-auto px-8 sm:px-16 xl:px-20 mt-16">
<div className="mx-auto ">
{props.blogs.slice(0, 1).map((blog: any, i: number) => (
@@ -96,7 +96,7 @@ function Blog(props: any) {
</div>
</div>
<div className="border-t dark:border-dark">
<div className="border-t border-scale-600">
<div className="container mx-auto px-8 sm:px-16 xl:px-20 mt-16">
<div className="mx-auto ">
<div className="grid grid-cols-12">
@@ -119,7 +119,7 @@ function Blog(props: any) {
className="col-span-12 md:col-span-12 lg:col-span-6 xl:col-span-4 mb-16"
key={idx}
>
<BlogListItem blog={blog} />
<BlogListItem post={blog} />
</div>
))}
</ol>
@@ -132,53 +132,62 @@ function Blog(props: any) {
function FeaturedThumb(blog: PostTypes) {
// @ts-ignore
const author = blog.author ? authors[blog.author] : authors['supabase']
const authorArray = blog.author.split(',')
const author = []
for (let i = 0; i < authorArray.length; i++) {
// @ts-ignore
author.push(
authors.find((authors: any) => {
// @ts-ignore
return authors.author_id === authorArray[i]
})
)
}
return (
<div key={blog.slug} className="cursor-pointer w-full">
<a href={`/blog/${blog.url}`}>
<a className="grid lg:grid-cols-2 gap-8 lg:gap-16">
<div className="relative overflow-auto w-full h-96 border dark:border-dark rounded-lg">
<div className="relative overflow-auto w-full h-96 border rounded-lg">
<Image
src={`/images/blog/` + (blog.thumb ? blog.thumb : blog.image)}
layout="fill"
objectFit="cover"
/>
</div>
<div className="flex flex-col space-y-4">
<div className="flex space-x-2">
<Typography.Text type="secondary">{blog.date}</Typography.Text>
<Typography.Text type="secondary">•</Typography.Text>
<Typography.Text type="secondary">{blog.readingTime}</Typography.Text>
<div className="flex flex-col space-y-2">
<div className="text-scale-900 flex space-x-2 text-sm">
<p>{blog.date}</p>
<p>•</p>
<p>{blog.readingTime}</p>
</div>
<div>
<Typography.Title level={2}>{blog.title}</Typography.Title>
<Typography.Text className="m-0" type="secondary">
<span className="text-xl">{blog.description}</span>
</Typography.Text>
<h2 className="h2">{blog.title}</h2>
<p className="p text-xl">{blog.description}</p>
</div>
{author && (
<div className="flex space-x-3 items-center">
{author.author_image_url && (
<div className="relative overflow-auto w-10 h-10">
<Image
src={author.author_image_url}
className="rounded-full"
layout="fill"
objectFit="cover"
/>
{author.map((author: any) => {
return (
<div className="flex space-x-3 items-center">
{author.author_image_url && (
<div className="relative overflow-auto w-10 h-10">
<Image
src={author.author_image_url}
className="rounded-full"
layout="fill"
objectFit="cover"
/>
</div>
)}
<div className="flex flex-col">
<span className="m-0 text-sm text-scale-1200">{author.author}</span>
<span className="m-0 text-xs text-scale-900">{author.position}</span>
</div>
)}
<div className="flex flex-col">
<Typography.Text>{author.author}</Typography.Text>
<Typography.Text type="secondary" small>
{author.position}
</Typography.Text>
</div>
</div>
)}
)
})}
</div>
</a>
</a>
@@ -1,4 +1,4 @@
.toc a {
/* .toc a {
text-decoration: none !important;
font-weight: 400 !important;
}
@@ -43,4 +43,4 @@
.article > div > pre {
padding: 0;
background: none;
}
} */
+110 -125
View File
@@ -1,4 +1,4 @@
import { Badge, Card, Divider, IconChevronLeft, IconFile, Space, Typography } from '@supabase/ui'
import { Badge, Card, Divider, IconChevronLeft, IconFile, Space } from '@supabase/ui'
import matter from 'gray-matter'
import authors from 'lib/authors.json'
import hydrate from 'next-mdx-remote/hydrate'
@@ -88,8 +88,19 @@ export async function getStaticProps({ params }: any) {
function BlogPostPage(props: any) {
// @ts-ignore
const author = props.blog.author ? authors[props.blog.author] : authors['supabase']
const content = hydrate(props.blog.content, { components })
const authorArray = props.blog.author.split(',')
const author = []
for (let i = 0; i < authorArray.length; i++) {
author.push(
// @ts-ignore
authors.find((authors: string) => {
// @ts-ignore
return authors.author_id === authorArray[i]
})
)
}
const { basePath } = useRouter()
@@ -102,31 +113,26 @@ function BlogPostPage(props: any) {
return (
<Link href={`/blog/${post.url}`} as={`/blog/${post.url}`}>
<div className={className}>
<Card className="cursor-pointer" hoverable>
<Space direction="vertical">
<div className="cursor-pointer border border-scale-500 p-6 transition rounded hover:bg-scale-100 dark:hover:bg-scale-300">
<div className="space-y-4">
<div>
<Typography.Text>{label}</Typography.Text>
<p className="text-sm text-scale-900">{label}</p>
</div>
<div>
<Typography.Title level={4}>{post.title}</Typography.Title>
<Typography.Text>{post.date}</Typography.Text>
<h4 className="text-lg text-scale-1200">{post.title}</h4>
<p className="small">{post.date}</p>
</div>
<div>
{post.tags.map((tag: string) => {
return <Badge key={`category-badge-${tag}`}>{tag}</Badge>
})}
</div>
</Space>
</Card>
</div>
</div>
</div>
</Link>
)
}
const toc = props.blog.toc && (
<Space direction="vertical" size={8} className="py-8 lg:py-0">
<div className="space-y-8 py-8 lg:py-0">
<div>
<Space>
<div className="space-x-2">
{props.blog.tags.map((tag: string) => {
return (
<a href={`/blog/tags/${tag}`} key={`category-badge-${tag}`}>
@@ -134,17 +140,17 @@ function BlogPostPage(props: any) {
</a>
)
})}
</Space>
</div>
</div>
<div>
<Typography.Text type="secondary">Table of contents</Typography.Text>
<Typography>
<div className={blogStyles['toc']}>
<p className="text-scale-1200">On this page</p>
<div>
<div className={[blogStyles['toc'], 'prose prose-toc'].join(' ')}>
<ReactMarkdown plugins={[gfm]}>{props.blog.toc.content}</ReactMarkdown>
</div>
</Typography>
</div>
</div>
</Space>
</div>
)
return (
@@ -181,7 +187,6 @@ function BlogPostPage(props: any) {
<DefaultLayout>
<div
className="
bg-white dark:bg-dark-800
container px-8 sm:px-16 xl:px-20 mx-auto
py-16
"
@@ -189,63 +194,67 @@ function BlogPostPage(props: any) {
<div className="grid grid-cols-12 gap-4">
<div className="col-span-12 lg:col-span-2 mb-2">
{/* Back button */}
<Typography.Text type="secondary">
<p>
<a
href={'/blog'}
className="hover:text-gray-900 dark:hover:text-white cursor-pointer flex items-center"
className="transition text-sm text-scale-900 hover:text-scale-1200 cursor-pointer flex items-center"
>
<IconChevronLeft style={{ padding: 0 }} />
Back
</a>
</Typography.Text>
</p>
</div>
<div className="col-span-12 lg:col-span-12 xl:col-span-10">
{/* Title and description */}
<div className="mb-16 space-y-8 max-w-5xl">
<div className="space-y-4">
<Typography.Text type="success">Blog post</Typography.Text>
<Typography.Title>{props.blog.title}</Typography.Title>
<div className="flex space-x-3">
<Typography.Text>{props.blog.date}</Typography.Text>
<Typography.Text type="secondary">•</Typography.Text>
<Typography.Text>
{generateReadingTime(props.blog.content.renderedOutput)}
</Typography.Text>
<p className="text-brand-900">Blog post</p>
<h1 className="h1">{props.blog.title}</h1>
<div className="text-scale-900 flex space-x-3 text-sm">
<p>{props.blog.date}</p>
<p>•</p>
<p>{generateReadingTime(props.blog.content.renderedOutput)}</p>
</div>
{author && (
<div className="mt-6 mb-8 lg:mb-0 w-max">
<Link href={author.author_url}>
<a className="cursor-pointer">
<Space size={4}>
{author.author_image_url && (
<div className="w-10">
<Image
src={author.author_image_url}
className="rounded-full border dark:border-dark"
width="100%"
height="100%"
layout="responsive"
/>
<div className="flex">
{author.map((author: any) => {
return (
<div className="mt-6 mb-8 mr-4 lg:mb-0 w-max">
<Link href={author.author_url}>
<a className="cursor-pointer">
<div className="flex gap-3 items-center">
{author.author_image_url && (
<div className="w-10">
<Image
src={author.author_image_url}
className="rounded-full border dark:border-dark"
width="100%"
height="100%"
layout="responsive"
/>
</div>
)}
<div className="flex flex-col">
<span className="text-sm text-scale-1200 mb-0">
{author.author}
</span>
<span className="text-xs mb-0 text-scale-900">
{author.position}
</span>
</div>
</div>
)}
<Space direction="vertical" size={0}>
<Typography.Text>{author.author}</Typography.Text>
<Typography.Text type="secondary" small>
{author.position}
</Typography.Text>
</Space>
</Space>
</a>
</Link>
</div>
)}
</a>
</Link>
</div>
)
})}
</div>
</div>
</div>
<div className="grid grid-cols-12 lg:gap-16 xl:gap-8">
{/* Content */}
<div className="col-span-12 lg:col-span-7 xl:col-span-7">
{props.blog.thumb && (
<div className="relative overflow-auto w-full h-96 mb-8 border dark:border-gray-600">
<div className="relative overflow-auto w-full h-96 mb-8 border rounded">
<Image
src={'/images/blog/' + props.blog.thumb}
layout="fill"
@@ -253,19 +262,19 @@ function BlogPostPage(props: any) {
/>
</div>
)}
<article className={blogStyles['article']}>
<Typography>{content}</Typography>
<article className={[blogStyles['article'], 'prose prose-docs'].join(' ')}>
{content}
</article>
<div className="text-gray-900 dark:text-white">Share with your friends</div>
<div className="flex space-x-4 mt-4">
<div>
<div className="py-16">
<div className="text-sm text-scale-900 dark:text-scale-1000">
Share this article
</div>
<div className="flex space-x-4 items-center mt-4">
<Link
href={`https://twitter.com/share?text=Checkout%20@supabase's%20new%20blog%20post%20%7C%20${props.blog.title}&url=https://supabase.com/blog/${props.blog.slug}`}
passHref
href={`https://twitter.com/share?text=${props.blog.title}&url=https://supabase.com/blog/${props.blog.slug}`}
>
<a
target="_blank"
className="text-gray-300 hover:text-gray-400 dark:hover:text-gray-400"
>
<a target="_blank" className="text-scale-900 hover:text-scale-1200">
<svg
height="26"
width="26"
@@ -275,21 +284,17 @@ function BlogPostPage(props: any) {
>
<path
d="m154.729 400c185.669 0 287.205-153.876 287.205-287.312 0-4.37-.089-8.72-.286-13.052a205.304 205.304 0 0 0 50.352-52.29c-18.087 8.044-37.55 13.458-57.968 15.899 20.841-12.501 36.84-32.278 44.389-55.852a202.42 202.42 0 0 1 -64.098 24.511c-18.42-19.628-44.644-31.904-73.682-31.904-55.744 0-100.948 45.222-100.948 100.965 0 7.925.887 15.631 2.619 23.025-83.895-4.223-158.287-44.405-208.074-105.504a100.739 100.739 0 0 0 -13.668 50.754c0 35.034 17.82 65.961 44.92 84.055a100.172 100.172 0 0 1 -45.716-12.63c-.015.424-.015.837-.015 1.29 0 48.903 34.794 89.734 80.982 98.986a101.036 101.036 0 0 1 -26.617 3.553c-6.493 0-12.821-.639-18.971-1.82 12.851 40.122 50.115 69.319 94.296 70.135-34.549 27.089-78.07 43.224-125.371 43.224a204.9 204.9 0 0 1 -24.078-1.399c44.674 28.645 97.72 45.359 154.734 45.359"
fill-rule="nonzero"
fillRule="nonzero"
/>
</svg>
</a>
</Link>
</div>
<div>
<Link
href={`https://www.linkedin.com/shareArticle?url=https://supabase.com/blog/${props.blog.slug}&title=Checkout%20@supabase's%20new%20blog%20post%20%7C%20${props.blog.title}`}
passHref
href={`https://www.linkedin.com/shareArticle?url=https://supabase.com/blog/${props.blog.slug}&title=${props.blog.title}`}
>
<a
target="_blank"
className="text-gray-300 hover:text-gray-400 dark:hover:text-gray-400"
>
<a target="_blank" className="text-scale-900 hover:text-scale-1200">
<svg
width="20"
height="20"
@@ -302,26 +307,6 @@ function BlogPostPage(props: any) {
</a>
</Link>
</div>
<button
onClick={() => copyLink()}
className="text-gray-300 hover:text-gray-400 dark:hover:text-gray-400"
>
<svg
className="h-6 w-6"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</button>
</div>
<div className="grid lg:grid-cols-1 gap-8 py-8">
<div>
@@ -336,40 +321,40 @@ function BlogPostPage(props: any) {
</div>
{/* Sidebar */}
<div className="col-span-12 lg:col-span-5 xl:col-span-3 xl:col-start-9 space-y-8">
<Space direction="vertical" size={8} className="lg:mb-16 lg:top-16 lg:sticky">
<div className="space-y-8 lg:mb-16 lg:top-16 lg:sticky">
<div className="hidden lg:block">{toc}</div>
<div>
<div className="mb-4">
<Typography.Text type="secondary">Related articles</Typography.Text>
<p className="text-sm text-scale-1200">Related articles</p>
</div>
<Space direction="vertical">
<div className="space-y-3">
{props.relatedPosts.map((post: any) => (
<Link href={`/blog/${post.url}`} as={`/blog/${post.url}`}>
<div>
<Typography.Text className="cursor-pointer">
<Space>
<IconFile size={'small'} style={{ minWidth: '1.2rem' }} />
<span className="hover:text-gray-900 dark:hover:text-white">
<p className="cursor-pointer">
<div className="flex gap-2">
<div className="text-scale-900">
<IconFile size={'small'} style={{ minWidth: '1.2rem' }} />
</div>
<span className="text-sm text-scale-1100 hover:text-gray-1200">
{post.title}
</span>
</Space>
</Typography.Text>
</div>
</p>
<Divider light className="mt-2" />
</div>
</Link>
))}
<Link href={`/blog`} as={`/blog`}>
<div>
<Typography.Text type="secondary">
<span className="hover:text-gray-900 dark:hover:text-white cursor-pointer">
View all posts
</span>
</Typography.Text>
</div>
</Link>
</Space>
<div className="mt-2">
<Link href={`/blog`} passHref>
<a className="text-sm text-scale-1100 hover:text-scale-1200 cursor-pointer">
View all posts
</a>
</Link>
</div>
</div>
</div>
</Space>
</div>
</div>
</div>
</div>
@@ -396,32 +381,32 @@ function BlogPostPage(props: any) {
<Space direction="vertical" size={8} className="lg:mb-16 lg:top-16 lg:sticky">
<div className="hidden lg:block">{toc}</div>
<div>
<Typography.Title className="mb-4" level={5}>
<h5 className="mb-4">
Related articles
</Typography.Title>
</h5>
<Space direction="vertical">
{props.relatedPosts.map((post: any) => (
<Link href={`/blog/${post.url}`} as={`/blog/${post.url}`}>
<div>
<Typography.Text className="cursor-pointer">
<p className="cursor-pointer">
<Space>
<IconFile size={'small'} style={{ minWidth: '1.2rem' }} />
<span className="hover:text-gray-900 dark:hover:text-white">
{post.title}
</span>
</Space>
</Typography.Text>
</p>
<Divider light className="mt-2" />
</div>
</Link>
))}
<Link href={`/blog`} as={`/blog`}>
<div>
<Typography.Text type="secondary">
<p>
<span className="hover:text-gray-900 dark:hover:text-white cursor-pointer">
View all posts
</span>
</Typography.Text>
</p>
</div>
</Link>
</Space>
+8 -10
View File
@@ -5,8 +5,6 @@ import Link from 'next/link'
import DefaultLayout from '~/components/Layouts/Default'
import BlogListItem from '~/components/Blog/BlogListItem'
import PostTypes from '~/types/post'
import BlogHeader from '~/components/Blog/BlogHeader'
import { Typography } from '@supabase/ui'
export async function getStaticProps({ params }: any) {
const posts = getSortedPosts('_blog', 0, [params.tag])
@@ -37,18 +35,18 @@ function TagBlogsPage(props: Props) {
<>
<NextSeo title={`Blog | ${tag}`} description="Latest news from the Supabase team." />
<DefaultLayout>
<div className="container mx-auto px-8 sm:px-16 xl:px-20 py-16">
<div className="container px-8 py-16 mx-auto sm:px-16 xl:px-20">
<div className="flex space-x-1">
<Typography.Text type="secondary" className="cursor-pointer">
<p className="cursor-pointer">
<Link href="/blog">Blog</Link>
</Typography.Text>
<Typography.Text type="secondary">/</Typography.Text>
<Typography.Text>{`${tag}`}</Typography.Text>
</p>
<p>/</p>
<p>{`${tag}`}</p>
</div>
<ol className="grid grid-cols-12 py-16 gap-8 lg:gap-16">
<ol className="grid grid-cols-12 gap-8 py-16 lg:gap-16">
{blogs.map((blog: PostTypes, idx: number) => (
<div className="col-span-12 md:col-span-12 lg:col-span-6 xl:col-span-4 mb-16">
<BlogListItem blog={blog} key={idx} />
<div className="col-span-12 mb-16 md:col-span-12 lg:col-span-6 xl:col-span-4">
<BlogListItem post={blog} key={idx} />
</div>
))}
</ol>
+12 -17
View File
@@ -1,17 +1,14 @@
import Container from 'components/Container'
import { createClient } from '@supabase/supabase-js'
import Layout from '~/components/Layouts/Default'
import CTABanner from 'components/CTABanner/index'
import { Button, Typography, IconDownload } from '@supabase/ui'
import { Button, IconDownload } from '@supabase/ui'
import Link from 'next/link'
import { useRouter } from 'next/router'
import SectionContainer from '~/components/Layouts/SectionContainer'
import { NextSeo } from 'next-seo'
const { Title, Text } = Typography
const Index = () => {
// base path for images
const router = useRouter()
@@ -38,15 +35,13 @@ const Index = () => {
<Layout>
<Container>
<SectionContainer className="pb-0 md:pb-0 lg:pb-0">
<div className="grid lg:grid-cols-2">
<div>
<Title>Brand assets</Title>
<Title level={3}>Download official Supabase logos</Title>
<Text>
All Supabase trademarks, logos, or other brand elements can never be modified or
used for any other purpose other than to represent Supabase Inc.
</Text>
</div>
<div className="max-w-xl">
<h1 className="text-5xl text-scale-1200">Brand assets</h1>
<p className="text-2xl text-scale-1200">Download official Supabase logos</p>
<p className="text-sm text-scale-1200">
All Supabase trademarks, logos, or other brand elements can never be modified or
used for any other purpose other than to represent Supabase Inc.
</p>
</div>
</SectionContainer>
<SectionContainer>
@@ -61,14 +56,14 @@ const Index = () => {
<div className="col-span-12 lg:col-span-7 flex items-center">
<div className="p-16">
<div>
<Typography.Title level={2}>Supabase logos</Typography.Title>
<Typography.Text>
<h1 className="text-4xl">Supabase logos</h1>
<p className="text-sm">
<p>
Download Supabase official logos, including as SVG's, in both light and dark
theme.
</p>
<p>Do not use any other color for the wordmark.</p>
</Typography.Text>
</p>
<form method="get" action={`/brand-assets/supabase-logos.zip`}>
<Button htmlType="submit" type="default" iconRight={<IconDownload />}>
Download logo kit
+28 -39
View File
@@ -24,7 +24,6 @@ import {
IconLinkedin,
IconTwitter,
Space,
Typography,
} from '@supabase/ui'
import { NextSeo } from 'next-seo'
@@ -76,9 +75,9 @@ const Header = () => {
mx-auto px-6 lg:px-16 xl:px-20 relative py-16 sm:py-18 md:py-24 lg:py-24
text-center"
>
<Typography.Title>
<h1 className="text-5xl">
Join one of the world's fastest growing open source communities.
</Typography.Title>
</h1>
</div>
</>
)
@@ -104,17 +103,19 @@ const Team = () => {
<SectionHeader title="Team" paragraph={<div className=""></div>} />
<div className="grid grid-cols-2 md:grid-cols-12">
<div className="col-span-8 ">
<Typography.Text>
<p>
<p className="text-lg">
Supabase is fully remote, with a strong affinity for open source maintainers and
ex-Founders. Our engineering team is made up of developers from AWS, Google,
Palantir, Stripe, and other YC companies.
</p>
</Typography.Text>
</p>
</div>
<div className=" md:text-right pt-8 md:mt-0 col-span-4">
<a href="https://about.supabase.com/careers">
<Button size="medium">Join the team</Button>
<Button size="medium" className="text-white">
Join the team
</Button>
</a>
</div>
</div>
@@ -133,12 +134,12 @@ const Team = () => {
</div>
<div className="flex flex-col justify-center space-y-2">
<div>
<Typography.Title level={5} className="mb-0">
<h5 className="mb-0">
{x.name}
</Typography.Title>
<Typography.Text type="secondary" className="mb-0">
</h5>
<p className="mb-0">
{x.department}
</Typography.Text>
</p>
</div>
<div className="flex space-x-2 text-gray-300 dark:text-gray-500">
{x.twitter && (
@@ -179,12 +180,12 @@ const Community = () => {
title={'Community'}
paragraph={
<>
<Typography.Text>
<p>
<p className="text-lg">
Join one of the world's fastest growing open source communities. Let's build
together.
</p>
</Typography.Text>
</p>
</>
}
/> */}
@@ -216,19 +217,15 @@ const Community = () => {
/>
</div>
<div>
<Typography.Title level={1} className="mb-0">
{x.stat}
</Typography.Title>
<Typography.Text type="secondary">{x.statLabel}</Typography.Text>
<h1 className="mb-0 text-4xl">{x.stat}</h1>
<p className="text-sm">{x.statLabel}</p>
</div>
</div>
))}
</div>
<div className="space-y-8">
<div className="max-w-3xl">
<Typography.Text>
With developer signups from the world's leading brands.
</Typography.Text>
<p className="text-sm">With developer signups from the world's leading brands.</p>
</div>
<ImageGrid images={CompaniesData} />
@@ -246,12 +243,12 @@ const Investors = () => {
title="Our investors"
paragraph={
<>
<Typography.Text>
<p>
<p className="text-lg">
We've raised over $36 million in funding, backed by some of the world's leading
investors.
</p>
</Typography.Text>
</p>
</>
}
/>
@@ -264,7 +261,7 @@ const Investors = () => {
className="
col-span-1
flex justify-center content-end items-center
bg-gray-50 dark:bg-gray-700
bg-gray-50 dark:bg-scale-400
h-32"
>
<div className="relative overflow-auto w-full h-8">
@@ -285,7 +282,7 @@ const Investors = () => {
</div>
))}
</div>
<Typography.Title level={3}>Individual investors</Typography.Title>
<h2>Individual investors</h2>
<div className="mt-5 mx-auto grid gap-5 grid-cols-2 lg:grid-cols-4 lg:max-w-none">
{InvestorData.filter((x) => x.lead === false)
.sort((a, b) => a.name.localeCompare(b.name))
@@ -295,12 +292,8 @@ const Investors = () => {
<div className="flex flex-col justify-center space-y-2">
<div>
<Typography.Title level={5} className="mb-0">
{x.name}
</Typography.Title>
<Typography.Text type="secondary" className="mb-0">
{x.title}
</Typography.Text>
<h1 className="mb-0 text-base">{x.name}</h1>
<p className="mb-0 text-xs">{x.title}</p>
</div>
</div>
</div>
@@ -319,13 +312,11 @@ const Press = () => {
<div className="mt-5 mx-auto grid gap-5 lg:grid-cols-2 lg:max-w-none">
{PressData.filter((x) => x.type == 'article').map((x) => (
<a href={x.href} key={x.href} target="_blank">
<Card key={`press_${x.href}`} hoverable>
<Card key={`press_${x.href}`} hoverable className="bg-white dark:bg-scale-400">
<Space className="justify-between h-40" direction="vertical">
<div>
<Typography.Text small type="secondary">
{x.type.toUpperCase()}
</Typography.Text>
<Typography.Title level={3}>{x.title}</Typography.Title>
<p>{x.type.toUpperCase()}</p>
<h3>{x.title}</h3>
</div>
</Space>
</Card>
@@ -335,13 +326,11 @@ const Press = () => {
<div className="mt-5 mx-auto grid gap-5 sm:grid-cols-2 lg:grid-cols-4 lg:max-w-none">
{PressData.filter((x) => x.type == 'podcast').map((x) => (
<a href={x.href} key={x.href} target="_blank">
<Card key={`press_${x.href}`} hoverable>
<Card key={`press_${x.href}`} hoverable className="bg-white dark:bg-scale-400">
<Space className="justify-between h-40" direction="vertical">
<div>
<Typography.Text small type="secondary">
{x.type.toUpperCase()}
</Typography.Text>
<Typography.Title level={3}>{x.title}</Typography.Title>
<p>{x.type.toUpperCase()}</p>
<h3>{x.title}</h3>
</div>
</Space>
</Card>
+61 -104
View File
@@ -1,13 +1,4 @@
import {
Badge,
Button,
IconArrowUpRight,
IconShuffle,
IconX,
Space,
Tabs,
Typography,
} from '@supabase/ui'
import { Badge, Button, IconArrowUpRight, IconShuffle, IconX, Space, Tabs } from '@supabase/ui'
// data
import ApiExamplesData from 'data/products/database/api-examples'
import ExtensionsExamplesData from 'data/products/database/extensions-examples'
@@ -110,64 +101,43 @@ function Database() {
<SectionContainer>
<div className="grid grid-cols-12">
<div className="mb-10 lg:mb-0 col-span-12 lg:col-span-3">
<Typography.Text>
<p className="mb-4">
<Space>
<ProductIcon icon={Solutions['database'].icon} />
<IconX />
<img
src={`${basePath}/images/product/database/postgresql-icon.svg`}
width={30}
/>
</Space>
</p>
</Typography.Text>
<Typography.Title level={4}>Just Postgres</Typography.Title>
<Typography.Text>
<p className="text-base lg:text-lg">
Every Supabase project is a dedicated Postgres database.
</p>
<p>
100% portable. Bring your existing Postgres database, or migrate away at any time.
</p>
</Typography.Text>
<div className="mb-4 flex items-center space-x-2">
<ProductIcon icon={Solutions['database'].icon} />
<IconX />
<img src={`${basePath}/images/product/database/postgresql-icon.svg`} width={30} />
</div>
<h4 className="h4">Just Postgres</h4>
<p className="p">Every Supabase project is a dedicated Postgres database.</p>
<p className="p text-sm">
100% portable. Bring your existing Postgres database, or migrate away at any time.
</p>
</div>
<div className="mb-10 lg:mb-0 col-span-12 lg:col-span-3 lg:col-start-5">
<Typography.Text>
<p className="mb-4">
<Space>
<ProductIcon icon={Solutions['database'].icon} />
<IconX />
<ProductIcon icon={Solutions['authentication'].icon} />
</Space>
</p>
</Typography.Text>
<Typography.Title level={4}>Built-in Auth</Typography.Title>
<Typography.Text>
<p className="text-base lg:text-lg">
Leveraging PostgreSQL's proven Row Level Security.
</p>
<p>
Integrated with JWT authentication which controls exactly what your users can
access.
</p>
</Typography.Text>
<div className="mb-4 flex items-center space-x-2">
<ProductIcon icon={Solutions['database'].icon} />
<IconX />
<ProductIcon icon={Solutions['authentication'].icon} />
</div>
<h4 className="h4">Built-in Auth</h4>
<p className="p">Leveraging PostgreSQL's proven Row Level Security.</p>
<p className="p text-sm">
Integrated with JWT authentication which controls exactly what your users can
access.
</p>
</div>
<div className="col-span-12 lg:col-span-3 lg:col-start-9">
<Typography.Text>
<p className="mb-4">
<Space>
<ProductIcon icon={Solutions['database'].icon} />
<IconX />
<ProductIcon icon={'M13 10V3L4 14h7v7l9-11h-7z'} />
</Space>
</p>
</Typography.Text>
<Typography.Title level={4}>Realtime enabled</Typography.Title>
<Typography.Text>
<p className="text-base lg:text-lg">Data-change listeners over websockets.</p>
<p>Subscribe and react to database changes, milliseconds after they happen.</p>
</Typography.Text>
<div className="mb-4 flex items-center space-x-2">
<ProductIcon icon={Solutions['database'].icon} />
<IconX />
<ProductIcon icon={'M13 10V3L4 14h7v7l9-11h-7z'} />
</div>
<h4 className="h4">Realtime enabled</h4>
<p className="p">Data-change listeners over websockets.</p>
<p className="p text-sm">
Subscribe and react to database changes, milliseconds after they happen.
</p>
</div>
</div>
</SectionContainer>
@@ -176,12 +146,11 @@ function Database() {
<SectionContainer className="text-center md:pb-0 lg:pb-0">
<div className="grid grid-cols-12">
<div className="col-span-12 lg:col-span-8 lg:col-start-3">
<Typography.Title level={2}>Easy to use dashboard</Typography.Title>
<Typography.Text>
<p className="text-base lg:text-lg">
The simplicity of a Table Editor, or the power of a SQL editor. Your choice.
</p>
</Typography.Text>
<h2 className="h3">Easy to use dashboard</h2>
<p className="p">
The simplicity of a Table Editor, or the power of a SQL editor. Your choice.
</p>
</div>
</div>
</SectionContainer>
@@ -228,9 +197,7 @@ function Database() {
handle="@Elsolo244"
key="@Elsolo244"
img_url={`${basePath}/images/twitter-profiles/v6citnk33y2wpeyzrq05_400x400.jpeg`}
quote="Where has
@supabase
been all my life? 😍"
quote="Where has @supabase been all my life? 😍"
/>,
]}
/>
@@ -267,24 +234,16 @@ function Database() {
]}
footer={[
<div className="grid grid-cols-12" key={0}>
<div className="mt-0 col-span-12 lg:col-span-6 xl:col-span-12 xl:mb-8">
<Space>
<Typography.Text type="secondary">
<p className="m-0">Libraries coming soon:</p>{' '}
</Typography.Text>
<Badge dot={false} color="blue">
Python
</Badge>
<Badge dot={false} color="blue">
Dart
</Badge>
<Badge dot={false} color="blue">
C#
</Badge>
<Badge dot={false} color="blue">
Kotlin
</Badge>
</Space>
<div className="mt-0 col-span-12 lg:col-span-6 xl:col-span-12 xl:mb-8 flex">
<p>
<p className="m-0">Libraries coming soon:</p>
</p>
<div className="space-x-1 ml-1">
<Badge dot={false}>Python</Badge>
<Badge dot={false}>Dart</Badge>
<Badge dot={false}>C#</Badge>
<Badge dot={false}>Kotlin</Badge>
</div>
</div>
<div className="col-span-12 lg:col-span-6 xl:col-span-10 hidden xl:block" key={1}>
{/* <TweetCard
@@ -320,23 +279,21 @@ function Database() {
<SectionContainer className="lg:py-48">
<div className="grid grid-cols-12 lg:gap-16">
<div className="col-span-12 lg:col-span-6 xl:col-span-5 mb-8">
<Typography.Title level={2}>Extend your database</Typography.Title>
<Typography.Text className="block mb-8">
<p className="lg:text-lg">Supabase works natively with Postgres extensions.</p>
<p>
Choose from a huge collection of Postgres extensions, enabled with a single click.
</p>
</Typography.Text>
<h2 className="h3">Extend your database</h2>
<p className="p">Supabase works natively with Postgres extensions.</p>
<p className="p">
Choose from a huge collection of Postgres extensions, enabled with a single click.
</p>
<FeatureColumn
title="40+ preinstalled extensions"
text="We only show a few of the extensions supported by supabase here, but we preinstall many more that you can use right away."
/>
<Link href="/docs/guides/database" as="/docs/guides/database">
<a>
<Button size="small" className="mt-4" type="default" icon={<IconArrowUpRight />}>
Explore documentation
</Button>
</a>
<Link href="/docs/guides/database" passHref>
<Button as="a" size="small" type="default" icon={<IconArrowUpRight />}>
Explore documentation
</Button>
</Link>
</div>
<div className="mt-8 lg:mt-0 col-span-12 lg:col-span-6 lg:col-start-7">
+2 -15
View File
@@ -9,7 +9,7 @@ import AdminAccess from 'components/AdminAccess/index'
import CaseStudies from 'components/CaseStudies/index'
import CTABanner from 'components/CTABanner/index'
import SectionContainer from '~/components/Layouts/SectionContainer'
import TwitterSocialProof from '~/components/Sections/TwitterSocialProof'
import TwitterSocialSection from '~/components/TwitterSocialSection'
// Import Swiper styles if swiper used on page
import 'swiper/swiper.min.css'
@@ -23,20 +23,7 @@ const Index = ({}: Props) => {
<Container>
<Hero />
<Features />
<div className="relative">
<div className="section--masked">
<div className="section--bg-masked">
<div className="section--bg border-t border-b border-gray-100 dark:border-gray-600"></div>
</div>
<div className="section-container pt-12 pb-0">
<div className="overflow-x-hidden">
<SectionContainer className="mb-0 pb-8">
<TwitterSocialProof />
</SectionContainer>
</div>
</div>
</div>
</div>
<TwitterSocialSection />
<BuiltExamples />
<MadeForDevelopers />
<AdminAccess />
+230
View File
@@ -0,0 +1,230 @@
import { createClient } from '@supabase/supabase-js'
import { IconChevronLeft, IconExternalLink } from '@supabase/ui'
import { marked } from 'marked'
import { GetStaticPaths, GetStaticProps } from 'next'
import { NextSeo } from 'next-seo'
import Link from 'next/link'
import { Swiper, SwiperSlide } from 'swiper/react'
import 'swiper/swiper.min.css'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import { Partner } from '~/types/partners'
import Error404 from '../404'
import Image from 'next/image'
const supabase = createClient(
'https://obuldanrptloktxcffvn.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzNzY1NjAxNSwiZXhwIjoxOTUzMjMyMDE1fQ.0sfp_Njf7l4g-nOCF5a1TQE11rPqtz8Y10uctIetkBA'
)
function Partner({ partner }: { partner: Partner }) {
if (!partner) return <Error404 />
return (
<>
<NextSeo
title={`${partner.title} | Works With Supabase`}
description={partner.description}
openGraph={{
title: `${partner.title} | Works With Supabase`,
description: partner.description,
url: `https://supabase.com/partners/${partner.slug}`,
images: [
{
url: partner.images[0] ?? partner.logo,
},
],
}}
/>
<DefaultLayout>
<SectionContainer>
<div className="max-w-5xl col-span-12 mx-auto mb-2 space-y-12 lg:col-span-2">
{/* Back button */}
<Link href={`/partners/${partner.type === 'technology' ? 'integrations' : 'experts'}`}>
<a className="flex items-center transition-colors cursor-pointer text-scale-1200 hover:text-scale-1000">
<IconChevronLeft style={{ padding: 0 }} />
Back
</a>
</Link>
<div className="flex items-center space-x-4">
<Image
layout="fixed"
width={56}
height={56}
className="rounded-full bg-scale-400 w-14 h-14 flex-shrink-f0"
src={partner.logo}
alt={partner.title}
/>
<h1 className="h1" style={{ marginBottom: 0 }}>
{partner.title}
</h1>
</div>
<div
className="py-6 bg-scale-300"
style={{ marginLeft: 'calc(50% - 50vw)', marginRight: 'calc(50% - 50vw)' }}
>
<Swiper
initialSlide={0}
spaceBetween={0}
slidesPerView={4}
speed={300}
// slidesOffsetBefore={300}
centerInsufficientSlides={true}
breakpoints={{
320: {
slidesPerView: 1,
},
720: {
slidesPerView: 2,
},
920: {
slidesPerView: 3,
},
1024: {
slidesPerView: 4,
},
1208: {
slidesPerView: 5,
},
}}
>
{partner.images.map((image: any, i: number) => {
return (
<SwiperSlide key={i}>
<div className="relative block ml-3 mr-3 cursor-move overflow-hidden rounded-md">
<Image
layout="responsive"
objectFit="contain"
width={1460}
height={960}
src={image}
alt={partner.title}
/>
</div>
</SwiperSlide>
)
})}
</Swiper>
</div>
<div className="grid gap-3 lg:grid-cols-4 space-y-16 lg:space-y-0">
<div className="lg:col-span-3">
<h2
className="text-scale-1200"
style={{ fontSize: '1.5rem', marginBottom: '1rem' }}
>
Overview
</h2>
<div className="prose" dangerouslySetInnerHTML={{ __html: partner.overview }} />
</div>
<div>
<h2
className="text-scale-1200"
style={{ fontSize: '1.5rem', marginBottom: '1rem' }}
>
Details
</h2>
<div className="divide-y text-scale-1200">
<div className="flex items-center justify-between py-2">
<span className="text-scale-900">Developer</span>
<span className="text-scale-1200">{partner.developer}</span>
</div>
<div className="flex items-center justify-between py-2">
<span className="text-scale-900">Category</span>
<Link
href={`/partners/${
partner.type === 'technology' ? 'integrations' : 'experts'
}#${partner.category.toLowerCase()}`}
>
<a className="transition-colors text-brand-900 hover:text-brand-800">
{partner.category}
</a>
</Link>
</div>
<div className="flex items-center justify-between py-2">
<span className="text-scale-900">Website</span>
<a
href={partner.website}
target="_blank"
rel="noreferrer"
className="transition-colors text-brand-900 hover:text-brand-800"
>
{new URL(partner.website).host}
</a>
</div>
<div className="flex items-center justify-between py-2">
<span className="text-scale-900">Documentation</span>
<a
href={partner.docs}
target="_blank"
rel="noreferrer"
className="transition-colors text-brand-900 hover:text-brand-800"
>
<span className="flex items-center space-x-1">
<span>Learn</span>
<IconExternalLink size="small" />
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</SectionContainer>
</DefaultLayout>
</>
)
}
// This function gets called at build time
export const getStaticPaths: GetStaticPaths = async () => {
const { data: slugs } = await supabase.from<Partner>('partners').select('slug')
const paths: {
params: { slug: string }
locale?: string | undefined
}[] =
slugs?.map(({ slug }) => ({
params: {
slug,
},
})) ?? []
return {
paths,
fallback: 'blocking',
}
}
// This also gets called at build time
export const getStaticProps: GetStaticProps = async ({ params }) => {
let { data: partner } = await supabase
.from<Partner>('partners')
.select('*')
.eq('slug', params!.slug as string)
.single()
if (!partner) {
return {
notFound: true,
}
}
// Parse markdown
partner.overview = marked.parse(partner.overview)
return {
props: { partner },
revalidate: 18000, // In seconds - refresh every 5 hours
}
}
export default Partner
+146
View File
@@ -0,0 +1,146 @@
import { createClient } from '@supabase/supabase-js'
import { IconArrowRight, IconSearch, Input, Select } from '@supabase/ui'
import { NextSeo } from 'next-seo'
import Link from 'next/link'
import { useRouter } from 'next/router'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import BecomeAPartner from '~/components/Partners/BecomeAPartners'
import PartnerLinkBox from '~/components/Partners/PartnerLinkBox'
import { Partner } from '~/types/partners'
import TileGrid from '../../../components/Partners/TileGrid'
const supabase = createClient(
'https://obuldanrptloktxcffvn.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzNzY1NjAxNSwiZXhwIjoxOTUzMjMyMDE1fQ.0sfp_Njf7l4g-nOCF5a1TQE11rPqtz8Y10uctIetkBA'
)
export async function getStaticProps() {
const { data: partners } = await supabase
.from<Partner>('partners')
.select('*')
.eq('approved', true)
.eq('type', 'expert')
.order('category')
.order('title')
return {
props: {
partners,
},
revalidate: 18000, // In seconds - refresh every 5 hours
}
}
interface Props {
partners: Partner[]
}
function ExpertPartnersPage(props: Props) {
const { partners } = props
const partnersByCategory: { [category: string]: Partner[] } = {}
partners.map(
(p) => (partnersByCategory[p.category] = [...(partnersByCategory[p.category] ?? []), p])
)
const router = useRouter()
const meta_title = 'Find an expert'
const meta_description = `Find an expert to help build your next idea.`
return (
<>
<NextSeo
title={meta_title}
description={meta_description}
openGraph={{
title: meta_title,
description: meta_description,
url: `https://supabase.com/partners/experts`,
images: [
{
url: `https://supabase.com${router.basePath}/images/product/database/database-og.jpg`, // TODO
},
],
}}
/>
<DefaultLayout>
<SectionContainer className="space-y-12">
<div>
<h1 className="h1">{meta_title}</h1>
<h2 className="text-xl text-scale-900">{meta_description}</h2>
</div>
<div className="grid grid-cols-12 lg:gap-16 xl:gap-32">
<div className="col-span-3">
{/* Horizontal link menu */}
<div className="space-y-6">
{/* Search Bar */}
<div className="space-y-4">
<div className="text-sm text-scale-900 mb-2">Explore more</div>
<PartnerLinkBox
title="Integrations"
color="blue"
description="Extend and automate your workflow by using integrations for your favorite tools."
href={`/partners/integrations`}
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11 4a2 2 0 114 0v1a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-1a2 2 0 100 4h1a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-1a2 2 0 10-4 0v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-3a1 1 0 00-1-1H4a2 2 0 110-4h1a1 1 0 001-1V7a1 1 0 011-1h3a1 1 0 001-1V4z"
/>
</svg>
}
/>
<PartnerLinkBox
title="Become a partner"
color="brand"
description="Fill out a quick 30 second form to apply to become a partner"
href={`/partners/integrations#become-a-partner`}
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
}
/>
</div>
</div>
</div>
<div className="col-span-9">
{/* Partner Tiles */}
<div className="grid">
{partners.length ? (
<TileGrid partnersByCategory={partnersByCategory} hideCategories={true} />
) : (
<h2 className="h2">No Partners Found</h2>
)}
</div>
</div>
</div>
{/* Become a partner form */}
</SectionContainer>
<BecomeAPartner supabase={supabase} />
</DefaultLayout>
</>
)
}
export default ExpertPartnersPage
+244
View File
@@ -0,0 +1,244 @@
import { createClient } from '@supabase/supabase-js'
import { IconArrowRight, IconLoader, IconSearch, Input, Select } from '@supabase/ui'
import { NextSeo } from 'next-seo'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React, { useEffect, useState } from 'react'
import { useDebounce } from 'use-debounce'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import BecomeAPartner from '~/components/Partners/BecomeAPartners'
import PartnerLinkBox from '~/components/Partners/PartnerLinkBox'
import { Partner } from '~/types/partners'
import TileGrid from '../../../components/Partners/TileGrid'
const supabase = createClient(
'https://obuldanrptloktxcffvn.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzNzY1NjAxNSwiZXhwIjoxOTUzMjMyMDE1fQ.0sfp_Njf7l4g-nOCF5a1TQE11rPqtz8Y10uctIetkBA'
)
export async function getStaticProps() {
const { data: partners } = await supabase
.from<Partner>('partners')
.select('*')
.eq('approved', true)
.eq('type', 'technology')
.order('category')
.order('title')
return {
props: {
partners,
},
// TODO: consider using Next.js' On-demand Revalidation with Supabase function hooks instead
revalidate: 18000, // In seconds - refresh every 5 hours
}
}
interface Props {
partners: Partner[]
}
function IntegrationPartnersPage(props: Props) {
const { partners: initialPartners } = props
const [partners, setPartners] = useState(initialPartners)
const allCategories = Array.from(new Set(initialPartners.map((p) => p.category)))
const partnersByCategory: { [category: string]: Partner[] } = {}
partners.forEach(
(p) => (partnersByCategory[p.category] = [...(partnersByCategory[p.category] ?? []), p])
)
const router = useRouter()
const meta_title = 'Find an Integration'
const meta_description = `Use your favorite tools with Supabase.`
const [search, setSearch] = useState('')
const [debouncedSearchTerm] = useDebounce(search, 300)
const [isSearching, setIsSearching] = useState(false)
useEffect(() => {
const searchPartners = async () => {
setIsSearching(true)
let query = supabase
.from<Partner>('partners')
.select('*')
.eq('approved', true)
.order('category')
.order('title')
if (search.trim()) {
query = query
// @ts-ignore
.textSearch('tsv', `${search.trim()}`, {
type: 'websearch',
config: 'english',
})
}
const { data: partners } = await query
return partners
}
if (search.trim() === '') {
setIsSearching(false)
setPartners(initialPartners)
return
}
searchPartners().then((partners) => {
if (partners) {
setPartners(partners)
}
setIsSearching(false)
})
}, [debouncedSearchTerm, router])
return (
<>
<NextSeo
title={meta_title}
description={meta_description}
openGraph={{
title: meta_title,
description: meta_description,
url: `https://supabase.com/partners`,
images: [
{
url: `https://supabase.com${router.basePath}/images/product/database/database-og.jpg`, // TODO
},
],
}}
/>
<DefaultLayout>
<SectionContainer className="space-y-16">
<div>
<h1 className="h1">{meta_title}</h1>
<h2 className="text-xl text-scale-900">{meta_description}</h2>
</div>
{/* Title */}
<div className="grid lg:grid-cols-12 space-y-12 lg:space-y-0 md:gap-8 lg:gap-16 xl:gap-16">
<div className="lg:col-span-4 xl:col-span-3">
{/* Horizontal link menu */}
<div className="space-y-6">
{/* Search Bar */}
<Input
size="small"
icon={<IconSearch />}
placeholder="Search..."
type="text"
// className="md:w-1/2"
value={search}
onChange={(e) => setSearch(e.target.value)}
actions={
isSearching && (
<span className="mr-1 text-white animate-spin">
<IconLoader />
</span>
)
}
/>
<div className="hidden lg:block">
<div className="text-sm text-scale-900 mb-2">Categories</div>
<div className="space-y-1">
{allCategories.map((category) => (
<button
key={category}
onClick={() => router.push(`#${category.toLowerCase()}`)}
className="block text-base text-scale-1100"
>
{category}
</button>
))}
</div>
</div>
<div className="space-y-4">
<div className="text-sm text-scale-900 mb-2">Explore more</div>
<div className="grid grid-cols-2 gap-8 lg:grid-cols-1">
<PartnerLinkBox
title="Experts"
color="blue"
description="Explore our certified Supabase agency experts that build with Supabase"
href={`/partners/experts`}
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="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"
/>
</svg>
}
/>
<PartnerLinkBox
href={`/partners/integrations#become-a-partner`}
title="Become a partner"
color="brand"
description="Fill out a quick 30 second form to apply to become a partner"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
}
/>
</div>
</div>
{/* <div className="flex flex-col space-y-1 sm:flex-row sm:space-y-0 sm:space-x-6">
<Link href={`/partners/integrations`}>
<a className="text-scale-1200">INTEGRATIONS</a>
</Link>
<Link href={`/partners/experts`}>
<a className="transition-colors text-brand-900 hover:text-brand-800">EXPERTS</a>
</Link>
<Link href={`/partners/integrations#become-a-partner`}>
<a className="flex items-center space-x-1 transition-colors text-brand-900 hover:text-brand-800">
BECOME A PARTNER <IconArrowRight />
</a>
</Link>
</div> */}
</div>
</div>
<div className="lg:col-span-8 xl:col-span-9">
{/* Partner Tiles */}
<div className="grid space-y-10">
{partners.length ? (
<TileGrid partnersByCategory={partnersByCategory} />
) : (
<h2 className="h2">No Partners Found</h2>
)}
</div>
</div>
</div>
{/* Become a partner form */}
</SectionContainer>
<BecomeAPartner supabase={supabase} />
</DefaultLayout>
</>
)
}
export default IntegrationPartnersPage
+283 -335
View File
@@ -4,7 +4,7 @@ import Link from 'next/link'
import DefaultLayout from '~/components/Layouts/Default'
import { PricingTableRowDesktop, PricingTableRowMobile } from '~/components/PricingTableRow'
import { Accordion, Badge, Button, Divider, Space, Typography } from '@supabase/ui'
import { Accordion, Button } from '@supabase/ui'
import pricing from '~/data/Pricing.json'
import pricingFaq from '~/data/PricingFAQ.json'
@@ -21,6 +21,33 @@ export default function IndexPage() {
const meta_description =
'Explore Supabase fees and pricing information. Find our competitive pricing tiers, with no hidden pricing. We have generous free tiers for those getting started, and Pay As You Go for those scaling up.'
const MobileHeaders = ({
// title,
description,
priceDescription,
price,
tier,
}: {
description: string
priceDescription: string
price: string
tier: string
}) => {
return (
<div className="px-4">
<h2 className="text-base font-normal text-scale-1200">{tier}</h2>
<span className="h1">${price}</span>
<p className="p">{priceDescription}</p>
<p className="p">{description}</p>
<Link href="https://app.supabase.io" passHref>
<Button as="a" type="default" size="medium" block>
Get started
</Button>
</Link>
</div>
)
}
return (
<DefaultLayout>
<NextSeo
@@ -37,371 +64,286 @@ export default function IndexPage() {
],
}}
/>
<div className="bg-white dark:bg-gray-800">
<div className="container mx-auto px-6 lg:px-16 xl:px-20 relative pt-24 md:pt-24 lg:pt-24">
<div className="text-center">
<Typography.Title>Predictable pricing, no surprises</Typography.Title>
<Typography.Text>
<p className="text-lg">
Start with a hobby project, collaborate with a team, and scale to millions of users.
</p>
</Typography.Text>
<div className="grid grid-cols-12 gap-8 mt-16">
{/* <div className="col-span-12 lg:col-span-6">
<div className="rounded border border-green-500 dark:border-green-900 bg-green-500 bg-opacity-10 grid grid-cols-6">
<div className="p-6 col-span-6">
<Space className="mb-4">
<Typography.Title level={3} className="flex gap-2">
Special Beta Pricing
</Typography.Title>
<Badge dot>Limited time offer</Badge>
</Space>
<Typography.Text>
<p className="lg:text-lg mb-2">
Lock into permanent beta pricing for 2 years if you upgrade today.
</p>
</Typography.Text>
</div>
</div>
</div> */}
{/* <div className="col-span-12 lg:col-span-6">
<div className="rounded border border-gray-200 dark:border-gray-600 grid grid-cols-12">
<img
src={`${basePath}/images/t-shirt-promo.jpg`}
className="col-span-6 h-30 object-cover"
/>
<div className="p-6 lg:col-span-6">
<Typography.Title level={4}>Free tshirt</Typography.Title>
<Typography.Text>
We are giving away free tshirts to anyone who signs up for the Beta Pro plan
</Typography.Text>
</div>
</div>
</div> */}
</div>
</div>
<div className="container relative px-6 pt-24 mx-auto lg:px-16 xl:px-20 md:pt-24 lg:pt-24">
<div className="text-center">
<h1 className="h1">Predictable pricing, no surprises</h1>
<p className="p">
Start with a hobby project, collaborate with a team, and scale to millions of users.
</p>
</div>
</div>
<div className="bg-white dark:bg-gray-800">
<div className="container mx-auto px-0 lg:px-16 xl:px-20 relative py-16 sm:py-18 md:py-24 lg:py-24">
{/* <!-- xs to lg --> */}
<div className="container relative px-0 py-16 mx-auto lg:px-16 xl:px-20 sm:py-18 md:py-24 lg:py-24">
{/* <!-- xs to lg --> */}
<div className=" lg:hidden">
{/* Free - Mobile */}
<div className="px-4">
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Free</h2>
<p className="mt-4">
<span className="text-4xl font-normal text-gray-900 dark:text-white">$0</span>
<Typography.Text type="secondary">/project /month</Typography.Text>
</p>
<p className="my-4 text-sm text-gray-500">
Perfect for hobby projects and experiments.
</p>
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button type="outline" size="medium" block>
Get started
</Button>
</a>
</Link>
</div>
<div className=" lg:hidden">
{/* Free - Mobile */}
<PricingTableRowMobile
category={pricing.database}
tier={'free'}
icon={Solutions['database'].icon}
/>
<PricingTableRowMobile
category={pricing.auth}
tier={'free'}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowMobile
category={pricing.storage}
tier={'free'}
icon={Solutions['storage'].icon}
/>
<PricingTableRowMobile category={pricing.dashboard} tier={'free'} />
<PricingTableRowMobile category={pricing.support} tier={'free'} />
<MobileHeaders
tier="Free"
price={'0'}
priceDescription={'/project /month'}
description={'Perfect for hobby projects and experiments'}
/>
{/* Pro - Mobile */}
<div className="px-4 mt-16">
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Pro</h2>
<p className="mt-4">
<span className="text-4xl font-normal text-gray-900 dark:text-white">$25</span>
<Typography.Text type="secondary">/project /month</Typography.Text>
</p>
<p className="my-4 text-sm text-gray-500">
Everything you need to scale your project into production.
</p>
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button type="outline" size="medium" block>
Get started
</Button>
</a>
</Link>
</div>
<PricingTableRowMobile
category={pricing.database}
tier={'free'}
icon={Solutions['database'].icon}
/>
<PricingTableRowMobile
category={pricing.auth}
tier={'free'}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowMobile
category={pricing.storage}
tier={'free'}
icon={Solutions['storage'].icon}
/>
<PricingTableRowMobile category={pricing.dashboard} tier={'free'} />
<PricingTableRowMobile category={pricing.support} tier={'free'} />
<PricingTableRowMobile
category={pricing.database}
tier={'pro'}
icon={Solutions['database'].icon}
/>
<PricingTableRowMobile
category={pricing.auth}
tier={'pro'}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowMobile
category={pricing.storage}
tier={'pro'}
icon={Solutions['storage'].icon}
/>
<PricingTableRowMobile category={pricing.dashboard} tier={'pro'} />
<PricingTableRowMobile category={pricing.support} tier={'pro'} />
{/* Pro - Mobile */}
<MobileHeaders
tier="Pro"
price={'25'}
priceDescription={'/project /month'}
description={'Everything you need to scale your project into production'}
/>
{/* Enterprise - Mobile */}
<div className="px-4 mt-16">
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
Pay as you go
</h2>
<p className="mt-4">
<span className="text-4xl font-normal text-gray-900 dark:text-white">$25</span>
<Typography.Text type="secondary">/project /month plus usage costs</Typography.Text>
</p>
<p className="my-4 text-sm text-gray-500">
Designated support team, account manager and technical specialist.
</p>
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button type="outline" size="medium" block>
Get started
</Button>
</a>
</Link>
</div>
<PricingTableRowMobile
category={pricing.database}
tier={'pro'}
icon={Solutions['database'].icon}
/>
<PricingTableRowMobile
category={pricing.auth}
tier={'pro'}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowMobile
category={pricing.storage}
tier={'pro'}
icon={Solutions['storage'].icon}
/>
<PricingTableRowMobile category={pricing.dashboard} tier={'pro'} />
<PricingTableRowMobile category={pricing.support} tier={'pro'} />
<PricingTableRowMobile
category={pricing.database}
tier={'enterprise'}
icon={Solutions['database'].icon}
/>
<PricingTableRowMobile
category={pricing.auth}
tier={'enterprise'}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowMobile
category={pricing.storage}
tier={'enterprise'}
icon={Solutions['storage'].icon}
/>
<PricingTableRowMobile category={pricing.dashboard} tier={'enterprise'} />
<PricingTableRowMobile category={pricing.support} tier={'enterprise'} />
</div>
{/* Enterprise - Mobile */}
<MobileHeaders
tier="Pay as you go"
price={'25'}
priceDescription={'/project /month plus usage costs'}
description={'Designated support team, account manager and technical specialist'}
/>
{/* <!-- lg+ --> */}
<div className="hidden lg:block">
<table className="w-full h-px table-fixed">
<caption className="sr-only">Pricing plan comparison</caption>
<thead className="thead--plans sticky z-10 top-[62px]">
<tr>
<th
className="relative bg-white dark:bg-gray-800 pb-4 px-6 text-sm font-medium text-gray-900 dark:text-white text-left"
scope="col"
>
<span className="sr-only">Feature by</span>
<span>Plans</span>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-gray-200 dark:bg-gray-600"
style={{ height: '1px' }}
></div>
</th>
<PricingTableRowMobile
category={pricing.database}
tier={'enterprise'}
icon={Solutions['database'].icon}
/>
<PricingTableRowMobile
category={pricing.auth}
tier={'enterprise'}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowMobile
category={pricing.storage}
tier={'enterprise'}
icon={Solutions['storage'].icon}
/>
<PricingTableRowMobile category={pricing.dashboard} tier={'enterprise'} />
<PricingTableRowMobile category={pricing.support} tier={'enterprise'} />
</div>
<th
className=" bg-white dark:bg-gray-800 w-1/4 pb-4 px-6 text-left font-medium"
scope="col"
>
<Typography.Title level={4}>Free</Typography.Title>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-gray-200 dark:bg-gray-600"
style={{ height: '1px' }}
></div>
</th>
{/* <!-- lg+ --> */}
<div className="hidden lg:block">
<table className="w-full h-px table-fixed">
<caption className="sr-only">Pricing plan comparison</caption>
<thead
className="
thead--plans
sticky
z-10
top-[62px]
border-b
border-scale-700
dark:border-scale-400
bg-scale-200
"
>
<tr>
<th
className="relative px-6 pb-4 text-sm font-normal text-left text-scale-900"
scope="col"
>
<span className="sr-only">Feature by</span>
<span>Plans</span>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-scale-200 dark:bg-scale-300"
style={{ height: '1px' }}
></div>
</th>
<th
className=" bg-white dark:bg-gray-800 w-1/4 pb-4 px-6 text-lg leading-6 font-medium text-gray-900 text-left"
scope="col"
>
<Typography.Title level={4}>Pro</Typography.Title>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-gray-200 dark:bg-gray-600"
style={{ height: '1px' }}
></div>
</th>
<th
className="w-1/4 px-6 pb-4 text-sm font-normal text-left text-scale-1200"
scope="col"
>
<span>Free</span>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-scale-200 dark:bg-scale-300"
style={{ height: '1px' }}
></div>
</th>
<th
className=" bg-white dark:bg-gray-800 w-1/4 pb-4 px-6 text-lg leading-6 font-medium text-gray-900 text-left"
scope="col"
>
<Typography.Title level={4}>Pay as you go</Typography.Title>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-gray-200 dark:bg-gray-600"
style={{ height: '1px' }}
></div>
</th>
</tr>
</thead>
<tbody className="border-gray-200 dark:border-gray-600 divide-y divide-gray-200 dark:divide-gray-700">
<tr className="divide-x dark:divide-gray-700">
<th
className="py-8 px-6 text-sm font-medium text-gray-900 dark:text-white text-left align-top"
scope="row"
>
Pricing
</th>
<th
className="w-1/4 px-6 pb-4 text-sm font-normal leading-6 text-left text-scale-1200"
scope="col"
>
<span>Pro</span>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-scale-200 dark:bg-scale-300"
style={{ height: '1px' }}
></div>
</th>
<td className="h-full py-8 px-6 align-top">
<div className="relative h-full table">
<p>
<span className="text-4xl font-normal text-gray-900 dark:text-white">
$0
</span>
<Typography.Text> /project /month</Typography.Text>
</p>
<p className="mt-4 mb-16 ">
<Typography.Text>
Perfect for hobby projects and experiments.
</Typography.Text>
</p>
<div className="absolute bottom-0">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="outline">
Get started
</Button>
</a>
</Link>
</div>
</div>
</td>
<th
className="w-1/4 px-6 pb-4 text-sm font-normal leading-6 text-left text-scale-1200"
scope="col"
>
<span>Pay as you go</span>
<div
className="absolute bottom-0 left-0 h-0.25 w-full bg-scale-200 dark:bg-scale-300"
style={{ height: '1px' }}
></div>
</th>
</tr>
</thead>
<tbody className="divide-y border-scale-700 dark:border-scale-400 divide-scale-700 dark:divide-scale-400">
<tr className="divide-x divide-scale-700 dark:divide-scale-400">
<th
className="px-6 py-8 text-sm font-medium text-left align-top text-scale-900 dark:text-white"
scope="row"
>
Pricing
</th>
<td className="h-full py-8 px-6 align-top">
<div className="relative h-full table">
<p>
<span className="text-4xl font-normal text-gray-900 dark:text-white">
$25
</span>
<Typography.Text> /project /month</Typography.Text>
</p>
<p className="mt-4 mb-16 ">
<Typography.Text>
Everything you need to scale your project into production.
</Typography.Text>
</p>
<div className="absolute bottom-0">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="outline">
Get started
</Button>
</a>
</Link>
</div>
</div>
</td>
<td className="h-full px-6 py-8 align-top">
<div className="relative table h-full">
<span className="h1 text-scale-1200">$0</span>
<p className="p"> /project /month</p>
<td className="h-full py-8 px-6 align-top">
<div className="relative h-full table">
<p>
<span className="text-4xl font-normal text-gray-900 dark:text-white">
$25
</span>
<Typography.Text> /project /month plus usage costs</Typography.Text>
</p>
<p className="mt-4 mb-16 ">
<Typography.Text>
Designated support team, account manager and technical specialist.
</Typography.Text>
</p>
<div className="absolute bottom-0">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="outline">
Get started
</Button>
</a>
</Link>
</div>
</div>
</td>
</tr>
<p className="text-sm p">Perfect for hobby projects and experiments</p>
<PricingTableRowDesktop
category={pricing.database}
icon={Solutions['database'].icon}
/>
<PricingTableRowDesktop
category={pricing.auth}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowDesktop
category={pricing.storage}
icon={Solutions['storage'].icon}
/>
<PricingTableRowDesktop category={pricing.dashboard} />
<PricingTableRowDesktop category={pricing.support} />
</tbody>
<tfoot>
<tr className="border-t border-gray-200 dark:border-gray-600">
<th className="sr-only" scope="row">
Choose your plan
</th>
<td className="pt-5 px-6">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="outline" block>
<Button size="medium" type="default">
Get started
</Button>
</a>
</Link>
</td>
</div>
</td>
<td className="h-full px-6 py-8 align-top">
<div className="relative table h-full">
<span className="h1 text-scale-1200">$25</span>
<p className="p"> /project /month</p>
<p className="text-sm p">
Everything you need to scale your project into production
</p>
<td className="pt-5 px-6">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="outline" block>
<Button size="medium" type="default">
Get started
</Button>
</a>
</Link>
</td>
</div>
</td>
<td className="pt-5 px-6">
<Link href="mailto:support@supabase.io" as="mailto:support@supabase.io">
<td className="h-full px-6 py-8 align-top">
<div className="relative table h-full">
<span className="h1 text-scale-1200">$25</span>
<p className="p"> /project /month plus usage costs</p>
<p className="text-sm p">
Designated support team, account manager and technical specialist
</p>
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="outline" block>
Contact us
<Button size="medium" type="default">
Get started
</Button>
</a>
</Link>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</td>
</tr>
<PricingTableRowDesktop
category={pricing.database}
icon={Solutions['database'].icon}
/>
<PricingTableRowDesktop
category={pricing.auth}
icon={Solutions['authentication'].icon}
/>
<PricingTableRowDesktop category={pricing.storage} icon={Solutions['storage'].icon} />
<PricingTableRowDesktop category={pricing.dashboard} />
<PricingTableRowDesktop category={pricing.support} />
</tbody>
<tfoot>
<tr className="border-t border-gray-200 dark:border-gray-600">
<th className="sr-only" scope="row">
Choose your plan
</th>
<td className="px-6 pt-5">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="default" block>
Get started
</Button>
</a>
</Link>
</td>
<td className="px-6 pt-5">
<Link href="https://app.supabase.io" as="https://app.supabase.io">
<a>
<Button size="medium" type="default" block>
Get started
</Button>
</a>
</Link>
</td>
<td className="px-6 pt-5">
<Link href="mailto:support@supabase.io" as="mailto:support@supabase.io">
<a>
<Button size="medium" type="default" block>
Contact us
</Button>
</a>
</Link>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div className="bg-gray-50 dark:bg-gray-800 border-t dark:border-gray-600">
<div className="container mx-auto px-6 lg:px-16 xl:px-20 relative py-16 sm:py-18 md:py-24 lg:py-24">
<Typography.Title level={2}>Frequently asked questions</Typography.Title>
<Typography className="w-5/12 mb-4">
<div className="border-t dark:border-scale-600">
<div className="container relative px-6 py-16 mx-auto lg:px-16 xl:px-20 sm:py-18 md:py-24 lg:py-24">
<h2 className="h3">Frequently asked questions</h2>
<p className="max-w-sm mb-4 text-base p">
Can&apos;t find the answer to your question, ask someone in the community either on our
Discord or GitHub.
</Typography>
</p>
<Link href="https://discord.supabase.com">
<a>
<Button type="default" className="mr-2" size="small">
@@ -411,19 +353,25 @@ export default function IndexPage() {
</Link>
<Link href="https://github.com/supabase/supabase/discussions">
<a>
<Button size="small">GitHub</Button>
<Button size="small" className="text-white">
GitHub
</Button>
</a>
</Link>
<div className="mt-16">
<div className="grid grid-cols-2 gap-y-10 gap-x-10">
{pricingFaq.map((faq) => {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-y-10 gap-x-10">
{pricingFaq.map((faq, i) => {
return (
<div>
<Accordion bordered>
<Accordion.Item label={faq.question}>
<Typography>
<ReactMarkdown>{faq.answer}</ReactMarkdown>
</Typography>
{/* @ts-ignore */}
<Accordion
type="bordered"
openBehaviour="multiple"
size="medium"
className=" text-scale-900 dark:text-white"
>
<Accordion.Item header={faq.question} id={`faq--${i.toString()}`}>
<ReactMarkdown>{faq.answer}</ReactMarkdown>
</Accordion.Item>
</Accordion>
</div>
+92 -133
View File
@@ -1,15 +1,4 @@
import {
Badge,
Button,
IconArrowUpRight,
IconBarChart,
IconCheckSquare,
IconShuffle,
IconWifi,
IconX,
Space,
Typography,
} from '@supabase/ui'
import { Badge, Button, IconArrowUpRight, IconShuffle, IconWifi, IconX, Space } from '@supabase/ui'
import ApiExamples from 'data/products/storage/api-examples'
import DashboardViewData from 'data/products/storage/dashboard-carousel.json'
import StoragePermissionsData from 'data/products/storage/permissions-examples'
@@ -93,114 +82,88 @@ function StoragePage() {
<SectionContainer>
<div className="grid grid-cols-12">
<div className="mb-10 lg:mb-0 col-span-12 lg:col-span-3">
<Typography.Text>
<p className="mb-4">
<Space>
<ProductIcon icon={Solutions['storage'].icon} />
<IconX />
<ProductIcon icon={Solutions['authentication'].icon} />
<IconX />
<ProductIcon icon={Solutions['database'].icon} />
</Space>
</p>
</Typography.Text>
<Typography.Title level={4}>Interoperable</Typography.Title>
<Typography.Text>
<p className="text-lg">
Integrates well with the rest of Supabase ecosystem, including Auth and Postgres.
</p>
{/* <p>
Familiar and easy to use permissions mean that your content is secure and accessible
only to the right users
</p> */}
</Typography.Text>
<div className="mb-4 flex items-center space-x-2">
<ProductIcon icon={Solutions['storage'].icon} />
<IconX />
<ProductIcon icon={Solutions['authentication'].icon} />
<IconX />
<ProductIcon icon={Solutions['database'].icon} />
</div>
<h4 className="h4">Interoperable</h4>
<p className="p">
Integrates well with the rest of Supabase ecosystem, including Auth and Postgres.
</p>
</div>
<div className="mb-10 lg:mb-0 col-span-12 lg:col-span-3 lg:col-start-5">
<Typography.Text>
<p>
<Space>
<ProductIcon
icon={
'M16 8v8m-4-5v5m-4-2v2m-2 4h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z'
}
/>
</Space>
</p>
</Typography.Text>
<Typography.Title level={4}>Lightning fast</Typography.Title>
<Typography.Text>
<p className="text-lg">
Thin API server layer that leverages Postgres' permissions and performance.
</p>
{/* <p>
1.3x faster than XXX, your content can be delivered anywhere in the world at the
speed you expect.
</p> */}
</Typography.Text>
<div className="mb-4 flex items-center space-x-2">
<ProductIcon
icon={
'M16 8v8m-4-5v5m-4-2v2m-2 4h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z'
}
/>
</div>
<h4 className="h4">Lightning fast</h4>
<p className="p">
Thin API server layer that leverages Postgres' permissions and performance.
</p>
</div>
<div className="col-span-12 lg:col-span-3 lg:col-start-9">
<Typography.Text>
<p>
<Space>
<ProductIcon
icon={
'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z'
}
/>
</Space>
</p>
</Typography.Text>
<Typography.Title level={4}>Dependable</Typography.Title>
<Typography.Text>
<p className="text-lg">Enterprise-level scalability and durability.</p>
{/* <p>A durability rating of 99.999999999%.</p> */}
</Typography.Text>
<div className="mb-4 flex items-center space-x-2">
<ProductIcon
icon={
'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z'
}
/>
</div>
<h4 className="h4">Dependable</h4>
<p className="p">Enterprise-level scalability and durability.</p>
</div>
</div>
</SectionContainer>
<SectionContainer className="pt-16">
<div className="mb-16 block">
<Typography.Title>Sleek dashboard for managing your media</Typography.Title>
<Typography.Text>
<p className="text-lg">
A complete Object Explorer so that any of your team can use.
</p>
<p>
Drag and drop uploading, moving objects, and multiple object selection. As easy as
working on your desktop.
</p>
</Typography.Text>
<h2 className="h3">Sleek dashboard for managing your media</h2>
<p className="p text-lg">
A complete Object Explorer so that any of your team can use.
</p>
<p className="p">
Drag and drop uploading, moving objects, and multiple object selection. As easy as
working on your desktop.
</p>
</div>
<ImageCarousel
// @ts-ignore
content={DashboardViewData}
footer={[
<Typography.Title level={4} key="title">
Check out our example app
</Typography.Title>,
// !! Update this example !!
<ExampleCard
key="example"
type={'example'}
products={['database', 'auth', 'storage']}
title={'Profile management example'}
description={
'Update a user account with public profile information, including uploading a profile image.'
}
author={'supabase'}
author_url={'https://github.com/supabase'}
author_img={'https://avatars.githubusercontent.com/u/54469796'}
repo_name={'nextjs-ts-user-management'}
repo_url={
'https://github.com/supabase/supabase/tree/master/examples/nextjs-ts-user-management'
}
vercel_deploy_url={
'https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fnextjs-ts-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-ts-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management'
}
demo_url={''}
/>,
]}
footer={
<>
<span key="check-out" className="p block mb-4">
Check out our example app
</span>
<ExampleCard
key="example"
type={'example'}
products={['database', 'auth', 'storage']}
title={'Profile management example'}
description={
'Update a user account with public profile information, including uploading a profile image.'
}
author={'supabase'}
author_url={'https://github.com/supabase'}
author_img={'https://avatars.githubusercontent.com/u/54469796'}
repo_name={'nextjs-ts-user-management'}
repo_url={
'https://github.com/supabase/supabase/tree/master/examples/nextjs-ts-user-management'
}
vercel_deploy_url={
'https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fnextjs-ts-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-ts-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management'
}
demo_url={''}
/>
</>
}
/>
</SectionContainer>
@@ -231,7 +194,7 @@ function StoragePage() {
title="Transformations"
text="Resize and compress your media before you serve it."
/>
<Badge color="blue">Coming soon</Badge>
<Badge>Coming soon</Badge>
</div>
</div>,
]}
@@ -253,34 +216,30 @@ function StoragePage() {
<SectionContainer>
<div className="grid grid-cols-12 lg:gap-16">
<div className="col-span-12 lg:col-span-5 mb-8">
<Typography.Title level={2}>
<h2 className="h3">
Integrates natively <br />
with Supabase Auth
</Typography.Title>
<Typography.Text>
<p className="">
Using Postgres Row Level Security to create Object access rules.
</p>
<p>
Storage Authorization is built around Postgres so that you can use any
combination of SQL, Postgres functions, and even your own metadata to
write policies.
</p>
</Typography.Text>
<Link
href="/docs/reference/javascript/storage-createbucket"
as="/docs/reference/javascript/storage-createbucket"
>
<a>
<Button
size="small"
type="default"
className="mt-4"
icon={<IconArrowUpRight />}
>
Explore documentation
</Button>
</a>
</h2>
<p className="p">
Using Postgres Row Level Security to create Object access rules.
</p>
<p className="p">
Storage Authorization is built around Postgres so that you can use any
combination of SQL, Postgres functions, and even your own metadata to write
policies.
</p>
<Link href="/docs/reference/javascript/storage-createbucket" passHref>
<Button
as="a"
size="small"
type="default"
className="mt-4"
icon={<IconArrowUpRight />}
>
Explore documentation
</Button>
</Link>
</div>
<div className="col-span-12 lg:col-span-6 lg:col-start-7">
+49 -62
View File
@@ -11,7 +11,6 @@ import {
IconX,
Space,
Tabs,
Typography,
IconGitHub,
Divider,
IconMail,
@@ -61,18 +60,18 @@ const Index = ({}: Props) => {
<Container>
<SectionContainer className="">
<div className="text-center">
<Typography.Title level={2}>Get Support</Typography.Title>
<Typography.Text>
<h2>Get Support</h2>
<p>
<p className="lg:text-lg">
Need a bit of help? We're here for you. Check out our current issues, GitHub
discussions, or get email support.
</p>
</Typography.Text>
</p>
</div>
<div className="grid sm:grid-cols-3 md:grid-cols-12 gap-5 mt-16">
<div className="col-span-12 lg:col-span-6 xl:col-span-4">
<div
className="h-40 bg-white dark:bg-gray-700
className="h-40 bg-white dark:bg-scale-400
border-t border-r border-l border-gray-100 dark:border-gray-600
p-5
flex flex-col justify-between
@@ -80,10 +79,8 @@ const Index = ({}: Props) => {
"
>
<div className="mb-4">
<Typography.Title level={5} className="mb-1">
GitHub Issues
</Typography.Title>
<Typography.Text className="block">
<h5 className="mb-1">GitHub Issues</h5>
<p className="block">
<p>
Have a general issue or bug that you've found? We'd love to hear about it in
our GitHub issues. This can be feature requests too!
@@ -91,37 +88,35 @@ const Index = ({}: Props) => {
<p>
<span className="font-bold">Use this for:</span> Bugs and other issues
</p>
</Typography.Text>
</p>
</div>
</div>
<Divider light />
<Divider type="horizontal" light orientation="center" />
<div>
<div
className="
bg-white dark:bg-gray-800
bg-white dark:bg-scale-400
border-b border-r border-l border-gray-100 dark:border-gray-600
p-5
p-5 pt-10
flex flex-col justify-between
rounded rounded-t-none"
>
<Space className="">
<Link
href="https://github.com/supabase/supabase/issues"
as="https://github.com/supabase/supabase/issues"
>
<a target="_blank">
<Button size="medium" type="default" iconRight={<IconGitHub />}>
Go To Issues
</Button>
</a>
</Link>
</Space>
<Link
href="https://github.com/supabase/supabase/issues"
as="https://github.com/supabase/supabase/issues"
>
<a target="_blank">
<Button size="medium" type="default" iconRight={<IconGitHub />}>
Go To Issues
</Button>
</a>
</Link>
</div>
</div>
</div>
<div className="col-span-12 lg:col-span-6 xl:col-span-4">
<div
className="h-40 bg-white dark:bg-gray-700
className="h-40 bg-white dark:bg-scale-400
border-t border-r border-l border-gray-100 dark:border-gray-600
p-5
flex flex-col justify-between
@@ -129,10 +124,8 @@ const Index = ({}: Props) => {
"
>
<div className="mb-4">
<Typography.Title level={5} className="mb-1">
GitHub Discussions
</Typography.Title>
<Typography.Text className="block">
<h5 className="mb-1">GitHub Discussions</h5>
<p className="block">
<p>
For help and questions about best practices, join our GitHub discussions.
Browse and ask questions.
@@ -140,38 +133,36 @@ const Index = ({}: Props) => {
<p>
<span className="font-bold">Use this for:</span> General questions
</p>
</Typography.Text>
</p>
</div>
</div>
<Divider light />
<Divider type="horizontal" light orientation="center" />
<div>
<div
className="
bg-white dark:bg-gray-800
bg-white dark:bg-scale-400
border-b border-r border-l border-gray-100 dark:border-gray-600
p-5
p-5 pt-10
flex flex-col justify-between
rounded rounded-t-none"
>
<Space className="">
<Link
href="https://github.com/supabase/supabase/discussions"
as="https://github.com/supabase/supabase/discussions"
>
<a target="_blank">
<Button size="medium" type="default" iconRight={<IconMessageCircle />}>
Join The Discussion
</Button>
</a>
</Link>
</Space>
<Link
href="https://github.com/supabase/supabase/discussions"
as="https://github.com/supabase/supabase/discussions"
>
<a target="_blank">
<Button size="medium" type="default" iconRight={<IconMessageCircle />}>
Join The Discussion
</Button>
</a>
</Link>
</div>
</div>
</div>
<div className="col-span-12 lg:col-span-6 xl:col-span-4">
<div
className="h-40 bg-white dark:bg-gray-700
className="h-40 bg-white dark:bg-scale-400
border-t border-r border-l border-gray-100 dark:border-gray-600
p-5
flex flex-col justify-between
@@ -179,10 +170,8 @@ const Index = ({}: Props) => {
"
>
<div className="mb-4">
<Typography.Title level={5} className="mb-1">
Email Support
</Typography.Title>
<Typography.Text className="block">
<h5 className="mb-1">Email Support</h5>
<p className="block">
<p>
We offer email based support. If you need SLAs, guaranteed response times,
or have an issue, please contact us here.
@@ -191,26 +180,24 @@ const Index = ({}: Props) => {
<span className="font-bold">Use this for:</span> Issues or questions
specific to you
</p>
</Typography.Text>
</p>
</div>
</div>
<Divider light />
<Divider type="horizontal" light orientation="center" />
<div>
<div
className="
bg-white dark:bg-gray-800
bbg-white dark:bg-scale-400
border-b border-r border-l border-gray-100 dark:border-gray-600
p-5
p-5 pt-10
flex flex-col justify-between
rounded rounded-t-none"
>
<Space className="">
<a href="mailto:support@supabase.io">
<Button size="medium" type="default" iconRight={<IconMail />}>
Email Support
</Button>
</a>
</Space>
<a href="mailto:support@supabase.io">
<Button size="medium" type="default" iconRight={<IconMail />}>
Email Support
</Button>
</a>
</div>
</div>
</div>
+4 -1
View File
@@ -1,3 +1,6 @@
module.exports = {
plugins: ['tailwindcss', 'postcss-preset-env'],
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
+66
View File
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://supabase.com/404</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/_app</loc>
<changefreq>weekly</changefreq>
@@ -54,6 +60,12 @@
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/partners/[slug]</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2020/05/01/supabase-alpha-april-2020</loc>
<changefreq>weekly</changefreq>
@@ -499,4 +511,58 @@
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2021/12/03/launch-week-three-friday-five-more-things</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2021/12/03/pg-graphql</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2021/12/03/supabase-holiday-hackdays-hackathon</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2021/12/15/beta-november-2021-launch-week-recap</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2021/12/17/holiday-hackdays-winners-2021</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2022/01/20/product-hunt-golden-kitty-awards-2021</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2022/01/20/supabase-beta-december-2021</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2022/02/22/supabase-beta-january-2022</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/2022/03/08/audit</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
</urlset>
+51
View File
@@ -0,0 +1,51 @@
create type partner_type as enum ('technology', 'expert');
create table partner_contacts (
id bigint generated by default as identity primary key,
type partner_type not null,
company text not null,
country text not null,
details text,
email text not null unique,
first text not null,
last text not null,
phone text,
size int,
title text,
website text not null,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
alter table partner_contacts enable row level security;
create policy "Enable public insert" on partner_contacts for insert with check (true);
create table partners (
id bigint generated by default as identity primary key,
slug text not null unique,
type partner_type not null,
category text not null,
developer text not null,
title text not null,
description text not null,
logo text not null,
images text[],
overview text not null,
website text not null,
docs text not null,
contact bigint references partner_contacts not null,
approved boolean default false,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
tsv tsvector generated always as (
setweight(to_tsvector('english', title), 'A')
|| setweight(to_tsvector('english', description), 'B')
|| setweight(to_tsvector('english', overview), 'C')
|| setweight(to_tsvector('english', category), 'D')
|| setweight(to_tsvector('english', slug), 'D')
) stored;
);
alter table partners enable row level security;
create policy "Enable public read access" on partners for select using (true);
+66 -46
View File
@@ -1,18 +1,6 @@
@import 'tailwindcss/base';
/* Write your own custom base styles here */
/* Start purging... */
@import 'tailwindcss/components';
/* Stop purging. */
/* Write you own custom component styles here */
/* Start purging... */
@import 'tailwindcss/utilities';
/* Stop purging. */
/* Your own custom utilities */
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'custom-font';
@@ -21,6 +9,7 @@
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'custom-font';
src: url(/fonts/custom/CustomFont-Medium.woff2) format('woff2'),
@@ -29,76 +18,108 @@
font-style: normal;
}
/*
Typography
*/
html {
scroll-behavior: smooth;
}
h1,
body {
@apply bg-scale-200;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* h1,
h2,
h3,
h4,
h5,
h6 {
h6 { */
/*
Typography
*/
.h1:not(.prose *):not(.overwrite),
.h2:not(.prose *):not(.overwrite),
.h3:not(.prose *):not(.overwrite),
.h4:not(.prose *):not(.overwrite),
.h5:not(.prose *):not(.overwrite),
.h6:not(.prose *):not(.overwrite) {
@apply text-scale-1200;
margin-bottom: 16px;
}
h1,
h2 {
.h1:not(.prose *):not(.overwrite) {
@apply text-4xl;
}
.h2:not(.prose *):not(.overwrite) {
@apply text-3xl;
}
.h3:not(.prose *):not(.overwrite) {
@apply text-2xl;
}
.h4:not(.prose *):not(.overwrite) {
@apply text-xl;
}
.h5:not(.prose *):not(.overwrite) {
@apply text-xl;
}
.h1:not(.prose *):not(.overwrite),
.h2:not(.prose *):not(.overwrite) {
line-height: 1.2 !important;
}
@media only screen and (max-width: 960px) {
h1 {
.h1:not(.prose *):not(.overwrite) {
font-size: 2.2rem !important;
}
h2 {
.h2:not(.prose *):not(.overwrite) {
font-size: 2rem !important;
}
}
@media only screen and (max-width: 720px) {
h1 {
.h1:not(.prose *):not(.overwrite) {
font-size: 2rem !important;
}
h2 {
.h2:not(.prose *):not(.overwrite) {
font-size: 1.8rem !important;
}
}
@media only screen and (max-width: 520px) {
h1 {
.h1:not(.prose *):not(.overwrite) {
font-size: 1.8rem !important;
}
h2 {
.h2:not(.prose *):not(.overwrite) {
font-size: 1.6rem !important;
}
}
p {
.p:not(.prose *):not(.overwrite) {
@apply text-scale-1100;
margin-bottom: 16px;
}
.small:not(.prose *):not(.overwrite) {
@apply text-xs text-scale-900;
}
.sbui-border-fix select {
@apply border-solid;
border-width: 1px;
}
html {
scroll-behavior: smooth;
}
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@apply bg-white dark:bg-gray-800;
}
.section--masked {
/* overflow: hidden; */
}
::selection {
@apply bg-green-300;
background-color: #6ee7b7;
color: #333 !important;
}
@@ -110,7 +131,7 @@ body {
}
.section--bg {
@apply bg-gray-50 dark:bg-gray-800;
@apply bg-scale-100 dark:bg-scale-200;
position: relative;
height: 100%;
width: 100%;
@@ -139,12 +160,12 @@ body {
@apply border border-gray-50 dark:border-gray-700;
}
a {
/* a {
@apply hover:text-black dark:hover:text-white;
}
} */
.sbui-tabs--alt div[role='tablist'] .sbui-btn-primary {
@apply bg-gray-800 text-white dark:bg-gray-600 dark:text-white;
@apply bg-gray-800 text-white dark:bg-scale-300 dark:text-white;
text-shadow: none;
font-weight: 400;
}
@@ -158,7 +179,7 @@ a {
@apply border-gray-900 dark:border-white;
}
/* override position of tabs */
.dashboard-tabs > div > div > div > div {
.dashboard-tabs > div > div {
justify-content: center;
}
@@ -206,7 +227,6 @@ table .thead--plans th {
.pricing-table__product-row {
/* @apply py-4; */
position: sticky;
border-collapse: separate;
top: 0;
z-index: 5;
}
+206 -109
View File
@@ -1,121 +1,221 @@
module.exports = {
purge: ['./components/**/*.tsx', './pages/**/*.tsx', './_blog/*.mdx'],
const ui = require('./ui.config.js')
const svgToDataUri = require('mini-svg-data-uri')
module.exports = ui({
mode: 'JIT',
purge: [
'./components/**/*.tsx',
'./pages/**/*.tsx',
'./_blog/*.mdx',
// purge styles from supabase ui theme
'./node_modules/@supabase/ui/dist/config/default-theme.js',
],
darkMode: 'class', // 'media' or 'class'
mode: 'jit',
// mode: 'jit',
theme: {
borderColor: (theme) => ({
...theme('colors'),
DEFAULT: '#f0f2f5',
dark: theme('colors.gray.600', 'currentColor'),
DEFAULT: theme('colors.scale.300', 'currentColor'),
dark: theme('colors.scale.1200', 'currentColor'),
}),
divideColor: (theme) => ({
...theme('colors'),
DEFAULT: '#f0f2f5',
dark: theme('colors.gray.600', 'currentColor'),
DEFAULT: theme('colors.scale.300', 'currentColor'),
dark: theme('colors.scale.600', 'currentColor'),
}),
extend: {
screens: {
sm: '640px',
// => @media (min-width: 640px) { ... }
md: '768px',
// => @media (min-width: 768px) { ... }
lg: '1024px',
// => @media (min-width: 1024px) { ... }
xl: '1280px',
// => @media (min-width: 1280px) { ... }
'2xl': '1536px',
// => @media (min-width: 1536px) { ... }
},
colors: {
'gray-light': '#7B7F86',
'gray-dark': '#7B7F86',
'accent-1': '#FAFAFA',
'accent-2': '#EAEAEA',
'accent-7': '#333',
success: '#0070f3',
cyan: '#79FFE1',
brand: {
100: '#82dab0',
200: '#69d3a0',
300: '#50cb90',
400: '#C5F1DD',
500: '#9FE7C7',
600: '#65D9A5',
700: '#3ECF8E',
800: '#38BC81',
900: '#10633E',
typography: ({ theme }) => ({
// Removal of backticks in code blocks for tailwind v3.0
// https://github.com/tailwindlabs/tailwindcss-typography/issues/135
DEFAULT: {
css: {
'code::before': {
content: '""',
},
'code::after': {
content: '""',
},
'--tw-prose-body': theme('colors.scale[1100]'),
'--tw-prose-headings': theme('colors.scale[1200]'),
'--tw-prose-lead': theme('colors.scale[1100]'),
'--tw-prose-links': theme('colors.brand[900]'),
'--tw-prose-bold': theme('colors.scale[1100]'),
'--tw-prose-counters': theme('colors.scale[1100]'),
'--tw-prose-bullets': theme('colors.scale[900]'),
'--tw-prose-hr': theme('colors.scale[500]'),
'--tw-prose-quotes': theme('colors.scale[1100]'),
'--tw-prose-quote-borders': theme('colors.scale[500]'),
'--tw-prose-captions': theme('colors.scale[700]'),
'--tw-prose-code': theme('colors.scale[1200]'),
'--tw-prose-pre-code': theme('colors.scale[900]'),
'--tw-prose-pre-bg': theme('colors.scale[400]'),
'--tw-prose-th-borders': theme('colors.scale[500]'),
'--tw-prose-td-borders': theme('colors.scale[200]'),
'--tw-prose-invert-body': theme('colors.scale[200]'),
'--tw-prose-invert-headings': theme('colors.white'),
'--tw-prose-invert-lead': theme('colors.scale[500]'),
'--tw-prose-invert-links': theme('colors.white'),
'--tw-prose-invert-bold': theme('colors.white'),
'--tw-prose-invert-counters': theme('colors.scale[400]'),
'--tw-prose-invert-bullets': theme('colors.scale[600]'),
'--tw-prose-invert-hr': theme('colors.scale[700]'),
'--tw-prose-invert-quotes': theme('colors.scale[100]'),
'--tw-prose-invert-quote-borders': theme('colors.scale[700]'),
'--tw-prose-invert-captions': theme('colors.scale[400]'),
// the following are typography overrides
// examples can be seen here —> https://github.com/tailwindlabs/tailwindcss-typography/blob/master/src/styles.js
// reset all header font weights
'h1, h2, h3, h4, h5': {
fontWeight: '400',
},
h2: {
fontWeight: '400',
},
p: {
fontWeight: '400',
},
a: {
fontWeight: '400',
},
pre: {
background: 'none',
padding: 0,
marginBottom: '32px',
},
},
},
// Joshen TODO: At the end just rearrange the values
dark: {
100: '#eeeeee',
200: '#e0e0e0',
300: '#bbbbbb',
400: '#666666',
500: '#444444',
600: '#2a2a2a',
700: '#1f1f1f',
800: '#181818',
900: '#0f0f0f',
toc: {
css: {
ul: {
'list-style-type': 'none',
'padding-left': 0,
margin: 0,
li: {
'padding-left': 0,
},
a: {
display: 'block',
'text-decoration': 'none',
fontSize: '0.8rem',
fontWeight: '200',
color: theme('colors.scale[1100]'),
'&:hover': {
color: theme('colors.scale[1200]'),
},
'font-weight': '400',
},
// margin: 0,
ul: {
'list-style-type': 'none',
li: {
marginTop: '0.2rem',
marginBottom: '0.2rem',
'padding-left': '0 !important',
'margin-left': '0.5rem',
},
a: {
fontWeight: '200',
color: theme('colors.scale[1000]'),
'&:hover': {
color: theme('colors.scale[1200]'),
},
},
},
},
},
},
gray: {
100: '#eeeeee',
200: '#e0e0e0',
300: '#bbbbbb',
400: '#666666',
500: '#444444',
600: '#2a2a2a',
700: '#1f1f1f',
800: '#181818',
900: '#0f0f0f',
},
},
spacing: {
28: '7rem',
},
letterSpacing: {
tighter: '-.04em',
},
lineHeight: {
tight: 1.2,
},
fontSize: {
'5xl': '2.5rem',
'6xl': '2.75rem',
'7xl': '4.5rem',
'8xl': '6.25rem',
},
boxShadow: {
'light-small': '0px 4px 8px 2px rgba(107, 114, 128, 0.08)',
small: '0 5px 10px rgba(0, 0, 0, 0.12)',
medium: '0 8px 30px rgba(0, 0, 0, 0.12)',
override: '0px 0px 0px rgba(0, 0, 0, 0)',
},
}),
// screens: {
// sm: '640px',
// // => @media (min-width: 640px) { ... }
// md: '768px',
// // => @media (min-width: 768px) { ... }
// lg: '1024px',
// // => @media (min-width: 1024px) { ... }
// xl: '1280px',
// // => @media (min-width: 1280px) { ... }
// '2xl': '1536px',
// // => @media (min-width: 1536px) { ... }
// },
// colors: {
// 'gray-light': '#7B7F86',
// 'gray-dark': '#7B7F86',
// 'accent-1': '#FAFAFA',
// 'accent-2': '#EAEAEA',
// 'accent-7': '#333',
// success: '#0070f3',
// cyan: '#79FFE1',
// // brand: {
// // 100: '#82dab0',
// // 200: '#69d3a0',
// // 300: '#50cb90',
// // 400: '#C5F1DD',
// // 500: '#9FE7C7',
// // 600: '#65D9A5',
// // 700: '#3ECF8E',
// // 800: '#38BC81',
// // 900: '#10633E',
// // },
// // Joshen TODO: At the end just rearrange the values
// dark: {
// 100: '#eeeeee',
// 200: '#e0e0e0',
// 300: '#bbbbbb',
// 400: '#666666',
// 500: '#444444',
// 600: '#2a2a2a',
// 700: '#1f1f1f',
// 800: '#181818',
// 900: '#0f0f0f',
// },
// // gray: {
// // 100: '#eeeeee',
// // 200: '#e0e0e0',
// // 300: '#bbbbbb',
// // 400: '#666666',
// // 500: '#444444',
// // 600: '#2a2a2a',
// // 700: '#1f1f1f',
// // 800: '#181818',
// // 900: '#0f0f0f',
// // },
// },
// spacing: {
// 28: '7rem',
// },
// letterSpacing: {
// tighter: '-.04em',
// },
// lineHeight: {
// tight: 1.2,
// },
// fontSize: {
// '5xl': '2.5rem',
// '6xl': '2.75rem',
// '7xl': '4.5rem',
// '8xl': '6.25rem',
// },
// boxShadow: {
// 'light-small': '0px 4px 8px 2px rgba(107, 114, 128, 0.08)',
// small: '0 5px 10px rgba(0, 0, 0, 0.12)',
// medium: '0 8px 30px rgba(0, 0, 0, 0.12)',
// override: '0px 0px 0px rgba(0, 0, 0, 0)',
// },
fontFamily: {
sans: [
'custom-font',
'BlinkMacSystemFont',
'-apple-system',
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
'Helvetica',
'Arial',
'sans-serif',
],
sans: ['custom-font', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'],
mono: ['Source Code Pro', 'Menlo', 'monospace'],
},
stroke: (theme) => ({
white: theme('colors.white'),
black: theme('colors.black'),
// stroke: (theme) => ({
// white: theme('colors.white'),
// black: theme('colors.black'),
// }),
backgroundImage: (theme) => ({
squiggle: `url("${svgToDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 3" enable-background="new 0 0 6 3" width="6" height="3" fill="${theme(
'colors.yellow.400'
)}"><polygon points="5.5,0 2.5,3 1.1,3 4.1,0"/><polygon points="4,0 6,2 6,0.6 5.4,0"/><polygon points="0,2 1,3 2.4,3 0,0.6"/></svg>`
)}")`,
}),
},
},
@@ -126,8 +226,5 @@ module.exports = {
// height: ['hover'],
// },
// },
plugins: [],
corePlugins: {
preflight: true,
},
}
plugins: [require('@tailwindcss/typography')],
})
+29
View File
@@ -0,0 +1,29 @@
export interface Partner {
id: number
slug: string
type: 'technology' | 'expert'
category: string
developer: string
title: string
description: string
logo: string
images: string[]
overview: string
website: string
docs: string
approved: boolean
}
export interface PartnerContact {
type: 'technology' | 'expert'
company: string
country: string
details?: string
email: string
first: string
last: string
phone?: string
size?: number
title?: string
website: string
}
+12 -10
View File
@@ -1,23 +1,25 @@
import Author from './author'
type PostTypes = {
slug: string
slug?: string
type: 'casestudy' | 'blog'
title: string
date: string
coverImage: string
author: Author
excerpt: string
ogImage: {
date?: string
coverImage?: string
author?: Author
excerpt?: string
ogImage?: {
url: string
}
content: string
content?: string
thumb: string
image: string
readingTime: string
image?: string
readingTime?: string
description: string
url: string
tags: []
tags?: []
logo?: string
hideAuthor?: boolean
}
export default PostTypes
+375
View File
@@ -0,0 +1,375 @@
const deepMerge = require('deepmerge')
const forms = require('@tailwindcss/forms')
const plugin = require('tailwindcss/plugin')
const radixUiColors = require('@radix-ui/colors')
const brandColors = require('./default-colors')
const { default: flattenColorPalette } = require('tailwindcss/lib/util/flattenColorPalette')
// console.log(Object.keys(radixUiColors))
// generates fixed scales
// based on the root/light mode version
const fixedOptions = ['scale', 'scaleA', 'brand']
function radixColorKeys() {
let keys = Object.keys(radixUiColors)
/**
* Filter array items based on search criteria (query)
*/
function filterItems(arr, query) {
return arr.filter(function (el) {
return el.toLowerCase().indexOf(query.toLowerCase()) == -1
})
}
keys = filterItems(keys, 'Dark')
// console.log('radixColorKeys', keys)
return keys
}
function generateColorClasses() {
const brandColors = ['brand', 'scale', 'scaleA']
const colors = [...radixColorKeys(), ...brandColors]
let mappedColors = {}
// generate the shape of the colors object
colors.map((x) => {
// create empty obj for each color
mappedColors[x] = {}
// create empty obj for each fixed color
if (
fixedOptions.some(function (v) {
return x.indexOf(v) >= 0
})
) {
mappedColors[`${x}-fixed`] = {}
}
})
colors.map((x) => {
for (let index = 0; index < 12; index++) {
const step = index + 1
mappedColors[x][step * 100] = `var(--colors-${x.toLowerCase()}${step})`
if (
fixedOptions.some(function (v) {
return x.indexOf(v) >= 0
})
) {
// console.log(x)
mappedColors[`${x}-fixed`][step * 100] = `var(--colors-fixed-${x}${step})`
}
}
})
return mappedColors
}
const colorClasses = generateColorClasses()
/*
* generateCssVariables()
*
* generate the CSS variables for tailwind to use
*
*/
function generateCssVariables() {
// potential options
// { fixedOptions, brandColors }
let rootColors = {}
let darkColors = {}
const radixArray = Object.values(radixUiColors)
const brandArray = Object.values(brandColors)
function generateColors(colors, index, colorSet) {
const key = Object.keys(colorSet)[index]
if (key.includes('Dark')) {
darkColors = { ...darkColors, ...colors }
} else {
rootColors = { ...rootColors, ...colors }
// generate an optional 'fixed' scale of colors
if (
fixedOptions.some(function (v) {
return key.indexOf(v) >= 0
})
) {
rootColors.fixed = { ...rootColors?.fixed, ...colors }
}
}
}
radixArray.map((x, i) => {
generateColors(x, i, radixUiColors)
})
brandArray.map((x, i) => {
generateColors(x, i, brandColors)
})
return {
root: { ...rootColors },
dark: { ...darkColors },
}
}
const variables = generateCssVariables()
// console.log(variables)
const uiConfig = {
theme: {
variables: {
DEFAULT: {
width: {
listbox: '320px',
},
colors: { ...variables.root },
},
'.dark': {
colors: { ...variables.dark },
},
},
extend: {
// dropdown extensions
transformOrigin: {
// tailwind class for this is `origin-dropdown`
dropdown: 'var(--radix-dropdown-menu-content-transform-origin)',
popover: 'var(--radix-popover-menu-content-transform-origin);',
},
width: {
listbox: 'var(--width-listbox);',
},
keyframes: {
fadeIn: {
'0%': { transform: 'scale(0.95)', opacity: 0 },
'100%': { transform: 'scale(1)', opacity: 1 },
},
fadeOut: {
'0%': { transform: 'scale(1)', opacity: 1 },
'100%': { transform: 'scale(0.95)', opacity: 0 },
},
overlayContentShow: {
'0%': { opacity: 0, transform: 'translate(0%, -2%) scale(.96)' },
'100%': { opacity: 1, transform: 'translate(0%, 0%) scale(1)' },
},
overlayContentHide: {
'0%': { opacity: 1, transform: 'translate(0%, 0%) scale(1)' },
'100%': { opacity: 0, transform: 'translate(0%, -2%) scale(.96)' },
},
dropdownFadeIn: {
'0%': { transform: 'scale(0.95)', opacity: 0 },
'100%': { transform: 'scale(1)', opacity: 1 },
},
dropdownFadeOut: {
'0%': { transform: 'scale(1)', opacity: 1 },
'100%': { transform: 'scale(0.95)', opacity: 0 },
},
fadeInOverlayBg: {
'0%': { opacity: 0 },
'100%': { opacity: 0.75 },
},
fadeOutOverlayBg: {
'0%': { opacity: 0.75 },
'100%': { opacity: 0 },
},
slideDown: {
'0%': { height: 0, opacity: 0 },
'100%': {
height: 'var(--radix-accordion-content-height)',
opacity: 1,
},
},
slideUp: {
'0%': { height: 'var(--radix-accordion-content-height)', opacity: 1 },
'100%': { height: 0, opacity: 0 },
},
slideDownNormal: {
'0%': { height: 0, opacity: 0 },
'100%': {
height: 'inherit',
opacity: 1,
},
},
slideUpNormal: {
'0%': { height: 'inherit', opacity: 1 },
'100%': { height: 0, opacity: 0 },
},
panelSlideLeftOut: {
'0%': { transform: 'translateX(-100%)', opacity: 0 },
'100%': {
transform: 'translate-x-0',
opacity: 1,
},
},
panelSlideLeftIn: {
'0%': { transform: 'translate-x-0', opacity: 1 },
'100%': { transform: 'translateX(-100%)', opacity: 0 },
},
panelSlideRightOut: {
'0%': { transform: 'translateX(100%)', opacity: 0 },
'100%': {
transform: 'translate-x-0',
opacity: 1,
},
},
panelSlideRightIn: {
'0%': { transform: 'translate-x-0', opacity: 1 },
'100%': { transform: 'translateX(100%)', opacity: 0 },
},
},
animation: {
'fade-in': 'fadeIn 300ms',
'fade-out': 'fadeOut 300ms',
'dropdown-content-show': 'overlayContentShow 100ms cubic-bezier(0.16, 1, 0.3, 1)',
'dropdown-content-hide': 'overlayContentHide 100ms cubic-bezier(0.16, 1, 0.3, 1)',
'overlay-show': 'overlayContentShow 300ms cubic-bezier(0.16, 1, 0.3, 1)',
'overlay-hide': 'overlayContentHide 300ms cubic-bezier(0.16, 1, 0.3, 1)',
'fade-in-overlay-bg': 'fadeInOverlayBg 300ms',
'fade-out-overlay-bg': 'fadeOutOverlayBg 300ms',
'slide-down': 'slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1)',
'slide-up': 'slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1)',
'slide-down-normal': 'slideDownNormal 300ms cubic-bezier(0.87, 0, 0.13, 1)',
'slide-up-normal': 'slideUpNormal 300ms cubic-bezier(0.87, 0, 0.13, 1)',
'panel-slide-left-out': 'panelSlideLeftOut 200ms cubic-bezier(0.87, 0, 0.13, 1)',
'panel-slide-left-in': 'panelSlideLeftIn 250ms cubic-bezier(0.87, 0, 0.13, 1)',
'panel-slide-right-out': 'panelSlideRightOut 200ms cubic-bezier(0.87, 0, 0.13, 1)',
'panel-slide-right-in': 'panelSlideRightIn 250ms cubic-bezier(0.87, 0, 0.13, 1)',
// tailwind class for this is `animate-dropdownFadeIn`
dropdownFadeIn: 'dropdownFadeIn 0.1s ease-out',
// tailwind class for this is `animate-dropdownFadeOut`
dropdownFadeOut: 'dropdownFadeOut 0.1s ease-out',
},
colors: {
...colorClasses,
'hi-contrast': `var(--colors-fixed-scale12)`,
'lo-contrast': `var(--colors-fixed-scale1)`,
},
},
},
plugins: [
require('@mertasan/tailwindcss-variables'),
function ({ addUtilities, addVariant }) {
// addVariant('data-open', '&:[data-state=open]')
addUtilities({
".dropdown-content[data-state='open']": {
animation: 'fadeIn 50ms ease-out',
},
".dropdown-content[data-state='closed']": {
animation: 'fadeOut 50ms ease-in',
},
"[data-state='open'] .accordion-content-animation": {
animation: 'slideDown 200ms ease-out',
},
"[data-state='closed'] .accordion-content-animation": {
animation: 'slideUp 200ms ease-in',
},
'.text-code': {
margin: '0 0.2em',
padding: '0.2em 0.4em 0.1em',
background: 'hsla(0, 0%, 58.8%, 0.1)',
border: '1px solid hsla(0, 0%, 39.2%, 0.2)',
borderRadius: '3px',
},
})
addVariant('data-open-parent', '[data-state="open"] &')
addVariant('data-closed-parent', '[data-state="closed"] &')
addVariant('data-open', '&[data-state="open"]')
addVariant('data-closed', '&[data-state="closed"]')
addVariant('data-show', '&[data-state="show"]')
addVariant('data-hide', '&[data-state="hide"]')
addVariant('data-checked', '&[data-state="checked"]')
addVariant('data-unchecked', '&[data-state="unchecked"]')
addVariant('aria-expanded', '&[aria-expanded="true"]')
// addVariant('parent-data-open', '[data-state="open"]&')
},
function ({ matchUtilities, theme }) {
matchUtilities(
{
highlight: (value) => ({ boxShadow: `inset 0 1px 0 0 ${value}` }),
},
{ values: flattenColorPalette(theme('backgroundColor')), type: 'color' }
),
matchUtilities(
{
subhighlight: (value) => ({
boxShadow: `inset 0 -1px 0 0 ${value}`,
}),
},
{
values: flattenColorPalette(theme('backgroundColor')),
type: 'color',
}
),
matchUtilities(
{
bordershadow: (value) => {
return {
boxShadow: `
var(--colors-blacka1) 0px 0px 0px 0px,
var(--colors-blacka1) 0px 0px 0px 0px,
var(--colors-blacka8) 0px 1px 1px 0px,
${value} 0px 0px 0px 1px,
var(--colors-blacka1) 0px 0px 0px 0px,
var(--colors-blacka1) 0px 0px 0px 0px,
rgb(64 68 82 / 8%) 0px 2px 5px 0px;
`,
}
},
},
{
values: flattenColorPalette(theme('backgroundColor')),
type: 'color',
}
)
},
require('tailwindcss-radix')(),
forms,
],
}
function arrayMergeFn(destinationArray, sourceArray) {
return destinationArray.concat(sourceArray).reduce((acc, cur) => {
if (acc.includes(cur)) return acc
return [...acc, cur]
}, [])
}
/**
* Merge Supabase UI and Tailwind CSS configurations
* @param {object} tailwindConfig - Tailwind config object
* @return {object} new config object
*/
function wrapper(tailwindConfig) {
let purge
if (Array.isArray(tailwindConfig.purge)) {
purge = {
content: tailwindConfig.purge,
}
} else {
purge = tailwindConfig.purge
}
return deepMerge({ ...tailwindConfig, purge }, uiConfig, {
arrayMerge: arrayMergeFn,
})
}
module.exports = wrapper