ui library styles (#34500)

* ui library styles

* fix command

* mobile home

* home

* sidebar

* Fix the URL to the quickstart.

* Fix the install route.

* Fix the command url.

* Fix the command again.

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
Saxon Fletcher
2025-03-28 22:41:18 +10:00
committed by GitHub
parent 5eb57a4fa2
commit 82749a7bf6
12 changed files with 144 additions and 82 deletions

View File

@@ -81,7 +81,7 @@ export default async function DocPage({ params }: DocPageProps) {
const toc = await getTableOfContents(doc.body.raw)
return (
<main className="relative lg:gap-10 xl:grid xl:grid-cols-[1fr_200px] pr-6 lg:py-8">
<main className="relative lg:gap-10 xl:grid xl:grid-cols-[1fr_200px] pr-6 lg:py-20">
<div className="mx-auto w-full min-w-0 max-w-4xl">
<div className="mb-4 flex items-center space-x-1 text-sm text-foreground-muted">
<div className="overflow-hidden text-ellipsis whitespace-nowrap">Docs</div>
@@ -89,7 +89,7 @@ export default async function DocPage({ params }: DocPageProps) {
<div className="text-foreground-lighter">{doc.title}</div>
</div>
<div className="flex items-end justify-between mb-5">
<div className="space-y-2 mb-5">
<div className="space-y-2">
<h1 className={cn('scroll-m-20 text-4xl tracking-tight')}>{doc.title}</h1>
{doc.description && (
<p className="text-lg text-foreground-light">

View File

@@ -11,16 +11,16 @@ interface AppLayoutProps {
export default function AppLayout({ children }: AppLayoutProps) {
return (
<>
<TopNavigation />
{/* <TopNavigation /> */}
{/* main container */}
<div className="">
{/* main content */}
<main className="flex-1 max-w-site mx-auto w-full border-l border-r">
<main className="flex-1 max-w-site mx-auto w-full">
{/* {children} */}
<div className="border-b">
<div className="flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
<aside className="fixed top-10 z-30 hidden h-[calc(100vh-3rem)] w-full shrink-0 md:sticky md:block border-r">
<ScrollArea className="h-full py-6 lg:py-8">
<aside className="fixed z-30 top-0 hidden h-screen w-full shrink-0 md:sticky md:block bg-black/10 border-r border-muted/50">
<ScrollArea className="h-full">
<SideNavigation />
</ScrollArea>
</aside>

View File

@@ -1,22 +1,24 @@
import { BlockPreview } from '@/components/block-preview'
import { ComponentPreview } from '@/components/component-preview'
import { Button } from '@/registry/default/components/ui/button'
import Link from 'next/link'
// Horizontal grid line component
const HorizontalGridLine = () => <div className="col-span-12 h-px bg-border/30" />
export default function Home() {
return (
<main className="relative lg:-ml-10">
<div className="mx-auto w-full min-w-0 flex flex-col gap-16">
{/* Component Showcase with Grid */}
<div className="relative z-10 h-full w-full overflow-y-auto">
{/* Top fade effect */}
<div className="sticky top-0 left-0 right-0 h-12 -mb-12 bg-gradient-to-b from-background to-transparent z-30"></div>
{/* Grid Container */}
<div className="relative">
{/* Grid Lines - Vertical (Columns) */}
{Array.from({ length: 13 }).map((_, i) => (
<div
key={`col-line-${i}`}
className="absolute top-0 bottom-0 w-px bg-border/30 z-10"
className="absolute top-0 bottom-0 w-px bg-border/30 z-10 first:hidden last:hidden"
style={{
left: `${(i / 12) * 100}%`,
height: '100%',
@@ -24,35 +26,16 @@ export default function Home() {
/>
))}
{/* Grid Lines - Horizontal (Rows) */}
<div
className="absolute left-0 right-0 h-px bg-border/30 z-10"
style={{ top: '0px' }}
></div>
<div
className="absolute left-0 right-0 h-px bg-border/30 z-10"
style={{ top: '200px' }}
></div>
<div
className="absolute left-0 right-0 h-px bg-border/30 z-10"
style={{ top: '440px' }}
></div>
<div
className="absolute left-0 right-0 h-px bg-border/30 z-10"
style={{ top: '740px' }}
></div>
<div
className="absolute left-0 right-0 h-px bg-border/30 z-10"
style={{ top: '1040px' }}
></div>
{/* Grid Content */}
<div className="grid grid-cols-12 gap-0 relative z-20">
<div className="grid grid-cols-12 gap-0 relative z-20 pb-32">
{/* Heading Section */}
<div data-grid-item className="col-start-3 col-span-8 pt-8 pb-8">
<div
data-grid-item
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 pt-8 pb-8"
>
<div className="flex flex-col gap-8 justify-start pt-32">
<div className="max-w-2xl">
<h1 className="text-4xl text-foreground mb-3 font-semibold tracking-tight">
<h1 className="text-4xl text-foreground mb-3 font-medium tracking-tight">
UI Blocks for Supabase Projects
</h1>
<h2 className="text-lg text-foreground-light mb-4">
@@ -61,7 +44,7 @@ export default function Home() {
Source. Open Code.
</h2>
<Button variant="secondary" size="lg" className="mt-4">
Get Started
<Link href="/docs/getting-started/quickstart">Get Started</Link>
</Button>
</div>
</div>
@@ -70,91 +53,95 @@ export default function Home() {
{/* Password-based Authentication */}
<div
data-grid-item
className="col-start-3 col-span-8 pt-16 pb-4 text-xs uppercase font-mono text-muted-foreground tracking-wider"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 pt-16 pb-6 text-xs uppercase font-mono text-foreground-light tracking-wider relative"
>
Password-based Authentication
</div>
<HorizontalGridLine />
<div
data-grid-item
className="col-start-3 col-span-8 hover:shadow-xl transition-shadow"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 relative"
>
<div className="w-full shadow-lg">
<div className="-mt-4">
<BlockPreview name="password-based-auth/sign-up" />
</div>
</div>
<HorizontalGridLine />
{/* Realtime Cursors */}
<div
data-grid-item
className="col-start-3 col-span-8 pt-16 pb-4 text-xs uppercase font-mono text-muted-foreground tracking-wider"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 pt-16 pb-6 text-xs uppercase font-mono text-foreground-light tracking-wider relative"
>
Realtime Cursors
</div>
<HorizontalGridLine />
<div
data-grid-item
className="col-start-3 col-span-8 hover:shadow-xl transition-shadow"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 relative"
>
<div className="w-full shadow-lg flex rounded-lg overflow-hidden">
<div className="-mt-4 flex rounded-lg overflow-hidden">
<BlockPreview name="realtime-cursor" isPair />
<BlockPreview name="realtime-cursor" isPair />
</div>
</div>
<HorizontalGridLine />
{/* Dropzone */}
<div
data-grid-item
className="col-start-3 col-span-8 pt-16 pb-4 text-xs uppercase font-mono text-muted-foreground tracking-wider"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 pt-16 pb-6 text-xs uppercase font-mono text-foreground-light tracking-wider relative"
>
File Upload
</div>
<HorizontalGridLine />
<div
data-grid-item
className="col-start-3 col-span-8 hover:shadow-xl transition-shadow"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 relative"
>
<div className="w-full shadow-lg">
<div className="-mt-4 -mb-12">
<ComponentPreview name="dropzone-demo" showCode={false} />
</div>
</div>
<HorizontalGridLine />
{/* Current User Avatar */}
<div
data-grid-item
className="col-start-3 col-span-8 pt-16 pb-4 text-xs uppercase font-mono text-muted-foreground tracking-wider"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 pt-16 pb-6 text-xs uppercase font-mono text-foreground-light tracking-wider relative"
>
Current User Avatar
</div>
<HorizontalGridLine />
<div
data-grid-item
className="col-start-3 col-span-8 hover:shadow-xl transition-shadow"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 relative"
>
<div className="w-full shadow-lg">
<div className="-mt-4 -mb-12">
<ComponentPreview name="current-user-avatar-preview" showCode={false} />
</div>
</div>
<HorizontalGridLine />
{/* Realtime Avatar Stack */}
<div
data-grid-item
className="col-start-3 col-span-8 pt-16 pb-4 text-xs uppercase font-mono text-muted-foreground tracking-wider"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 pt-16 pb-6 text-xs uppercase font-mono text-foreground-light tracking-wider relative"
>
Realtime Avatar Stack
</div>
<HorizontalGridLine />
<div
data-grid-item
className="col-start-3 col-span-8 hover:shadow-xl transition-shadow"
className="col-start-2 col-span-10 md:col-start-3 md:col-span-8 relative"
>
<div className="w-full shadow-lg">
<div className="-mt-4 -mb-12">
<ComponentPreview name="realtime-avatar-stack-preview" showCode={false} />
</div>
</div>
{/* Empty row at bottom for padding */}
<div data-grid-item className="col-span-12 h-16"></div>
<HorizontalGridLine />
</div>
</div>
{/* Bottom fade effect */}
<div className="sticky bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-background to-transparent z-30"></div>
</div>
</div>
</main>

View File

@@ -3,12 +3,9 @@ import { OpenInV0Button } from '@/components/open-in-v0-button'
interface BlockItemProps {
name: string
title: string
description: string
src: string
}
export const BlockItem = ({ name, description }: BlockItemProps) => {
export const BlockItem = ({ name }: BlockItemProps) => {
return (
<div className="mt-4">
<Command name={name} highlight />

View File

@@ -55,7 +55,7 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button
type="outline"
className={cn(
`relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-foreground-muted shadow-none sm:pr-12 md:w-40 lg:w-64
`relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-foreground-muted shadow-none sm:pr-12
hover:border-foreground-muted hover:bg-surface-100 hover:text-foreground-lighter
`
)}

View File

@@ -1,8 +1,7 @@
'use client'
import { CommandCopyButton } from './command-copy-button'
import { motion } from 'framer-motion'
import { cn } from '../lib/utils'
import { CommandCopyButton } from './command-copy-button'
interface CommandCopyProps {
name: string
@@ -11,10 +10,10 @@ interface CommandCopyProps {
export function Command({ name, highlight }: CommandCopyProps) {
const command = `npx shadcn@latest add ${
process.env.VERCEL_TARGET_ENV === 'production'
process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV === 'production'
? `https://supabase.com`
: process.env.VERCEL_TARGET_ENV === 'preview'
? `https://${process.env.VERCEL_PROJECT_PREVIEW_URL}`
: process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV === 'preview'
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`
: 'http://localhost:3004'
}${process.env.NEXT_PUBLIC_BASE_PATH ?? ''}/r/${name}.json`

View File

@@ -3,6 +3,9 @@
import NavigationItem from '@/components/side-navigation-item'
import { aiEditorsRules, componentPages, gettingStarted } from '@/config/docs'
import { useFramework } from '@/context/framework-context'
import Link from 'next/link'
import { ThemeSwitcherDropdown } from './theme-switcher-dropdown'
import { CommandMenu } from './command-menu'
function SideNavigation() {
const { framework: preferredFramework } = useFramework()
@@ -33,8 +36,66 @@ function SideNavigation() {
})
return (
<nav className="min-w-[220px]">
<div className="pb-10 space-y-0.5">
<nav className="flex flex-col h-full min-w-[220px]">
<div className="p-6">
<div className="flex items-start justify-between">
<Link href="/">
<svg
xmlns="http://www.w3.org/2000/svg"
width="109"
height="113"
viewBox="0 0 109 113"
fill="none"
className="w-6 h-6 mb-4"
>
<path
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0625L99.1935 40.0625C107.384 40.0625 111.952 49.5226 106.859 55.9372L63.7076 110.284Z"
fill="url(#paint0_linear)"
/>
<path
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0625L99.1935 40.0625C107.384 40.0625 111.952 49.5226 106.859 55.9372L63.7076 110.284Z"
fill="url(#paint1_linear)"
fill-opacity="0.2"
/>
<path
d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z"
fill="#3ECF8E"
/>
<defs>
<linearGradient
id="paint0_linear"
x1="53.9738"
y1="54.9738"
x2="94.1635"
y2="71.8293"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#249361" />
<stop offset="1" stop-color="#3ECF8E" />
</linearGradient>
<linearGradient
id="paint1_linear"
x1="36.1558"
y1="30.5779"
x2="54.4844"
y2="65.0804"
gradientUnits="userSpaceOnUse"
>
<stop />
<stop offset="1" stop-opacity="0" />
</linearGradient>
</defs>
</svg>
</Link>
<ThemeSwitcherDropdown />
</div>
<Link href="/" className="mb-4 block">
<h1>Supabase UI Library</h1>
</Link>
{/* <TopNavigationSearch /> */}
<CommandMenu />
</div>
<div className="pb-6">
<div className="font-mono uppercase text-xs text-foreground-lighter/75 mb-2 px-6 tracking-widest">
{gettingStarted.title}
</div>
@@ -42,7 +103,7 @@ function SideNavigation() {
<NavigationItem item={item} key={`${item.href}-${i}`} />
))}
</div>
<div className="pb-10 space-y-2">
<div className="pb-6">
<div className="font-mono uppercase text-xs text-foreground-lighter/75 mb-2 px-6 tracking-widest">
Blocks
</div>
@@ -54,7 +115,7 @@ function SideNavigation() {
</div>
</div>
<div className="pb-10 space-y-0.5">
<div className="pb-6 flex-1">
<div className="font-mono uppercase text-xs text-foreground-lighter/75 mb-2 px-6 tracking-widest">
{aiEditorsRules.title}
</div>

View File

@@ -2,7 +2,7 @@ import { siteConfig } from '@/config/site'
export function SiteFooter() {
return (
<footer className="py-6 md:px-8 md:py-0 mx-auto border-r border-l border-b w-full max-w-site">
<footer className="py-6 md:px-8 md:py-0 mx-auto w-full max-w-site">
<div className="flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
<p className="text-balance text-center text-sm leading-loose text-foreground-muted md:text-left">
Built by{' '}

View File

@@ -70,7 +70,7 @@ const ThemeSwitcherDropdown = () => {
}
></Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="end">
<DropdownMenuContent className="w-56" align="start">
<DropdownMenuLabel>Theme</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup

View File

@@ -63,37 +63,37 @@ export const componentPages: Record<
title: 'Client',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
commandItemLabel: 'Supabase Client',
href: '/docs/client',
href: '/docs/nextjs/client',
},
'password-based-auth': {
title: 'Password-Based Auth',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
commandItemLabel: 'Password-Based Auth',
href: '/docs/password-based-auth',
href: '/docs/nextjs/password-based-auth',
},
dropzone: {
title: 'Dropzone',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
commandItemLabel: 'Dropzone (File Upload)',
href: '/docs/dropzone',
href: '/docs/nextjs/dropzone',
},
'realtime-cursor': {
title: 'Realtime Cursor',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
commandItemLabel: 'Realtime Cursor',
href: '/docs/realtime-cursor',
href: '/docs/nextjs/realtime-cursor',
},
'current-user-avatar': {
title: 'Current User Avatar',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
commandItemLabel: 'Current User Avatar',
href: '/docs/current-user-avatar',
href: '/docs/nextjs/current-user-avatar',
},
'realtime-avatar-stack': {
title: 'Realtime Avatar Stack',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
commandItemLabel: 'Realtime Avatar Stack',
href: '/docs/realtime-avatar-stack',
href: '/docs/nextjs/realtime-avatar-stack',
},
}

View File

@@ -1,5 +1,5 @@
# Supabase UI Library
Last updated: 2025-03-27T11:29:28.542Z
Last updated: 2025-03-28T12:17:05.978Z
## Overview
Library of components for your project. The components integrate with Supabase and are shadcn compatible.
@@ -15,31 +15,49 @@ Library of components for your project. The components integrate with Supabase a
- Components
- [Next.js](https://supabase.com/ui/docs/nextjs/client)
- Supabase client for Next.js
- [Current User Avatar](https://supabase.com/ui/docs/nextjs/current-user-avatar)
- Supabase Auth-aware avatar
- [Dropzone (File Upload)](https://supabase.com/ui/docs/nextjs/dropzone)
- Displays a control for easier uploading of files directly to Supabase Storage
- [Password-based Auth (Next.js)](https://supabase.com/ui/docs/nextjs/password-based-auth)
- Password-based Auth block for Next.js app
- [Realtime Avatar Stack](https://supabase.com/ui/docs/nextjs/realtime-avatar-stack)
- Avatar stack in realtime
- [Realtime Cursor](https://supabase.com/ui/docs/nextjs/realtime-cursor)
- Real-time cursor sharing for collaborative applications
- [Supabase Client for React Router](https://supabase.com/ui/docs/react-router/client)
- Supabase client for React Router
- [Current User Avatar](https://supabase.com/ui/docs/react-router/current-user-avatar)
- Supabase Auth-aware avatar
- [Dropzone (File Upload)](https://supabase.com/ui/docs/react-router/dropzone)
- Displays a control for easier uploading of files directly to Supabase Storage
- [Password-based Auth (Next.js)](https://supabase.com/ui/docs/react-router/password-based-auth)
- Password-based Auth block for Next.js app
- [Realtime Avatar Stack](https://supabase.com/ui/docs/react-router/realtime-avatar-stack)
- Avatar stack in realtime
- [Realtime Cursor](https://supabase.com/ui/docs/react-router/realtime-cursor)
- Real-time cursor sharing for collaborative applications
- [React Single Page Applications](https://supabase.com/ui/docs/react/client)
- Supabase client for React Single Page Applications
- [Current User Avatar](https://supabase.com/ui/docs/react/current-user-avatar)
- Supabase Auth-aware avatar
- [Dropzone (File Upload)](https://supabase.com/ui/docs/react/dropzone)
- Displays a control for easier uploading of files directly to Supabase Storage
- [Password-based Authentication](https://supabase.com/ui/docs/react/password-based-auth)
- Password-based authentication block for React Single Page Applications
- [Realtime Avatar Stack](https://supabase.com/ui/docs/react/realtime-avatar-stack)
- Avatar stack in realtime
- [Realtime Cursor](https://supabase.com/ui/docs/react/realtime-cursor)
- Real-time cursor sharing for collaborative applications
- [Tanstack Start](https://supabase.com/ui/docs/tanstack/client)
- Supabase client for Tanstack Start
- [Current User Avatar](https://supabase.com/ui/docs/tanstack/current-user-avatar)
- Supabase Auth-aware avatar
- [Dropzone (File Upload)](https://supabase.com/ui/docs/tanstack/dropzone)
- Displays a control for easier uploading of files directly to Supabase Storage
- [Tanstack Start](https://supabase.com/ui/docs/tanstack/password-based-auth)
- Supabase client for Tanstack Start
- [Realtime Avatar Stack](https://supabase.com/ui/docs/tanstack/realtime-avatar-stack)
- Avatar stack in realtime
- [Realtime Cursor](https://supabase.com/ui/docs/tanstack/realtime-cursor)
- Real-time cursor sharing for collaborative applications

View File

@@ -128,8 +128,8 @@
"env": [
"NEXT_PUBLIC_BASE_PATH",
"NEXT_PUBLIC_APP_URL",
"VERCEL_TARGET_ENV",
"VERCEL_PROJECT_PREVIEW_URL",
"NEXT_PUBLIC_VERCEL_TARGET_ENV",
"NEXT_PUBLIC_VERCEL_BRANCH_URL",
"NODE_ENV",
"HIDE_TAILWIND_INDICATOR",
// These envs are used in the packages