mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
docs(design-system): design system improvements (#40196)
* font smoothing * remove sidebar * fix typo * data grid empty state * basic data grid * remove figma * empty state docs
This commit is contained in:
1 parent
dee40fe1bf
commit
40f7abc2cd
15 files changed
+176
-148
No files matched your search
@@ -64,7 +64,7 @@ With that out of the way, there are several parts of this design system that nee
|
||||
- `registry/charts.ts`: chart components
|
||||
- `registry/fragments.ts`: fragment components
|
||||
|
||||
You may need to rebuild the design system’s registry. You can do that via:
|
||||
You will probably need to rebuild the design system’s registry after making new additions. You can do that via:
|
||||
|
||||
```bash
|
||||
cd apps/design-system
|
||||
|
||||
@@ -2315,13 +2315,35 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"empty-state-initial-state": {
|
||||
name: "empty-state-initial-state",
|
||||
"empty-state-initial-state-presentational": {
|
||||
name: "empty-state-initial-state-presentational",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/empty-state-initial-state")),
|
||||
component: React.lazy(() => import("@/registry/default/example/empty-state-initial-state-presentational")),
|
||||
source: "",
|
||||
files: ["registry/default/example/empty-state-initial-state.tsx"],
|
||||
files: ["registry/default/example/empty-state-initial-state-presentational.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"empty-state-initial-state-informational": {
|
||||
name: "empty-state-initial-state-informational",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/empty-state-initial-state-informational")),
|
||||
source: "",
|
||||
files: ["registry/default/example/empty-state-initial-state-informational.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"empty-state-zero-items-data-grid": {
|
||||
name: "empty-state-zero-items-data-grid",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/empty-state-zero-items-data-grid")),
|
||||
source: "",
|
||||
files: ["registry/default/example/empty-state-zero-items-data-grid.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
|
||||
@@ -35,16 +35,6 @@ export const docsConfig: DocsConfig = {
|
||||
href: '/docs/icons',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Figma',
|
||||
href: '/docs/figma',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Changelog',
|
||||
href: '/docs/changelog',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
title: Changelog
|
||||
description: Latest updates and announcements.
|
||||
toc: false
|
||||
---
|
||||
|
||||
## 2nd Oct 2025 - Update button styles
|
||||
|
||||
- UI Patterns section added for higher-level guidance
|
||||
- [`Navigation`](/design-system/docs/ui-patterns/navigation) UI pattern added
|
||||
- [`NavMenu`](/design-system/docs/components/nav-menu) component added
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/39188)
|
||||
|
||||
## 1st Sep 2025 - Update button styles
|
||||
|
||||
- [`Color usage`](/design-system/docs/color-usage) page updated
|
||||
- Color tokens improved for accessibility
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/38343)
|
||||
|
||||
## 18th Oct 2024 - Update multi-select
|
||||
|
||||
Improved Multi Select with combobox, badge limit, and more examples.
|
||||
|
||||
- [Multi Select](/design-system/components/multi-select)
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/29695)
|
||||
|
||||
## 12th June 2024 - Button styles updated
|
||||
|
||||
- [`<Button />`](/design-system/docs/components/button) styles updated
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/27055)
|
||||
|
||||
## 5th June 2024 - More Modal and Dialog examples
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/26844)
|
||||
|
||||
## 3rd June 2024 - Add scroll example to NavigationMenu
|
||||
|
||||
- [Added scroll example](https://supabase-design-system.vercel.app/design-system/docs/components/navigation-menu#with-horizontal-scroll) to [`<NavigationMenu />`](/design-system/docs/components/navigation-menu)
|
||||
|
||||
## 25th May 2024 - TreeView component added
|
||||
|
||||
- [`<TreeView />`](/design-system/docs/components/tree-view) component added.
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/26821)
|
||||
|
||||
## 24th May 2024 - FormItemLayout updated, and InfoTooltip added
|
||||
|
||||
- [`<FormItemLayout />`](/design-system/docs/fragments/form-item-layout) now supports new props, `BeforeLabel` and `AfterLabel`
|
||||
- [`<InfoTooltip />`](/design-system/docs/fragments/info-tooltip) added to easily add information tooltips
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/26712)
|
||||
|
||||
## 24th May 2024 - Added Admonition component
|
||||
|
||||
- [Admonition](/design-system/docs/fragments/admonition)
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/26710)
|
||||
|
||||
## 24th May 2024 - New components and docs
|
||||
|
||||
### New components added
|
||||
|
||||
- [Multi Select](/design-system/components/multi-select)
|
||||
|
||||
[PR](https://github.com/supabase/supabase/pull/26719)
|
||||
|
||||
### New components documented
|
||||
|
||||
- [Radio Group Card](/design-system/docs/components/radio-group-card)
|
||||
- [Radio Group Stacked](/design-system/docs/components/radio-group-stacked)
|
||||
- [Info Tooltip](/design-system/docs/components/info-tooltip)
|
||||
- [Admonition](/design-system/docs/components/admonition)
|
||||
|
||||
### New colors
|
||||
|
||||
`bg-surface-75` tailwind color fixed for Dark theme
|
||||
|
||||
## 22nd May 2024 - Introducing Design System
|
||||
|
||||
We're introducing [Supabase Design System](/design-system), a home for Design related resources for Supabase
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
title: Figma
|
||||
description: Every component recreated in Figma. With customizable props, typography and icons.
|
||||
---
|
||||
|
||||
The Figma UI Kit is open sourced by [Pietro Schirano](https://twitter.com/skirano).
|
||||
|
||||
<AspectRatio ratio={12 / 9} className="w-full mt-4">
|
||||
<iframe
|
||||
src="https://www.figma.com/embed?embed_host=share&url=https%3A%2F%2Fwww.figma.com%2Fdesign%2FWCja3lpEj1DeunV1d5zu5D%2FDesign-System%3Fnode-id%3D520%253A1715%26t%3Dz8Quu7PADvSqD0Ss-1"
|
||||
className="h-full w-full overflow-hidden rounded-lg border bg-surface-100"
|
||||
></iframe>
|
||||
</AspectRatio>
|
||||
|
||||
## Additional resources
|
||||
|
||||
### Figma Diagram Kit
|
||||
|
||||
<AspectRatio ratio={12 / 9} className="w-full mt-4">
|
||||
<iframe
|
||||
src="https://www.figma.com/embed?embed_host=share&url=https%3A%2F%2Fwww.figma.com%2Fdesign%2Fd6EV8a9G2yLht01AayAkmJ%2FDiagram-Kit-(Variables)%3Fnode-id%3D47%253A7265%26t%3D3JsjisJgJqD08wGG-1"
|
||||
className="h-full w-full overflow-hidden rounded-lg border bg-surface-100"
|
||||
></iframe>
|
||||
</AspectRatio>
|
||||
|
||||
## Grab a copy
|
||||
|
||||
https://www.figma.com/community/file/1203061493325953101
|
||||
@@ -3,35 +3,60 @@ title: Empty states
|
||||
description: Convey the absence of data and provide clear instruction for what to do about it.
|
||||
---
|
||||
|
||||
At a minimum, empty states convey the fact that there is nothing to list, perform, or display on the current page. They should also provide a clear call to action for the user to take.
|
||||
Empty states convey the fact that there is nothing to list, perform, or display on the current page. **Ideally**, they also provide a clear action for the user to take.
|
||||
|
||||
## Missing route
|
||||
|
||||
Users may accidentally navigate to a non-existent dynamic route, such as a non-existent bucket in [Storage](https://supabase.com/dashboard/project/_/storage) or a non-existent table in the [Table Editor](https://supabase.com/dashboard/project/_/editor). In these cases, follow the pattern of a centered [Admonition](../fragments/admonition) as shown below..
|
||||
Users may accidentally navigate to a non-existent dynamic route, such as a non-existent bucket in [Storage](https://supabase.com/dashboard/project/_/storage) or a non-existent table in the [Table Editor](https://supabase.com/dashboard/project/_/editor). In these cases, follow the pattern of a centered [Admonition](../fragments/admonition) as shown below.
|
||||
|
||||
<ComponentPreview name="empty-state-missing-route" peekCode wide />
|
||||
|
||||
## Zero results
|
||||
## No data
|
||||
|
||||
Tabular information without results—or perhaps no data to begin with—should have an empty state that matches the larger presentation.
|
||||
There are two ways an empty state may be displayed in cases where there is no data:
|
||||
|
||||
For instance, a [Table](../components/table) may just display a single row just like it would if it had data. Dulling the TableHead text color and removing the TableCell hover state can further reinforce the lack of usable data.
|
||||
- **Zero results**: no data after a search or filter
|
||||
- **Initial state**: no data to begin with
|
||||
|
||||
### Zero results
|
||||
|
||||
Data-heavy presentations without results should have an empty state that broadly matches the state when there is data. This makes the transition between the two states more seamless.
|
||||
|
||||
#### Table
|
||||
|
||||
A [Table](../components/table) instance with zero results should display a single row. Dulling the TableHead text color and removing the TableCell hover state can further reinforce the lack of usable data.
|
||||
|
||||
<ComponentPreview name="empty-state-zero-items-table" peekCode wide />
|
||||
|
||||
The treatment for other layouts, such as the list of users in [Authentication](https://supabase.com/dashboard/project/_/auth/users), should match their own general styling.
|
||||
#### DataGrid
|
||||
|
||||
## Initial state
|
||||
[DataGrid](../components/data-table) typically spans the full height and width of a container. A classic example is [Users](https://supabase.com/dashboard/project/_/auth/users), which (as it sounds) displays a list of the project’s registered users. Any instance with zero results should display a more prominent empty with a clear title, description, and supporting illustration.
|
||||
|
||||
Perhaps the user has not yet created any data yet. They might be a feature for the first time. In these cases, the empty state should provide the briefest information about the lack of data, putting more focus on the value proposition and primary action.
|
||||
<ComponentPreview name="empty-state-zero-items-data-grid" peekCode wide />
|
||||
|
||||
<ComponentPreview name="empty-state-initial-state" peekCode wide />
|
||||
Other DataGrid instances include [Cron Jobs](https://supabase.com/dashboard/project/_/integrations/cron/jobs) and [Queues](https://supabase.com/dashboard/project/_/integrations/queues).
|
||||
|
||||
Keep in mind that this empty state will likely appear after a visual loading state. Consider layout shift and button placement during and after the transition.
|
||||
### Initial state
|
||||
|
||||
Perhaps the user has not yet created any data. The presentation of this empty state depends on the context of the list and the type of data it contains.
|
||||
|
||||
#### Presentational
|
||||
|
||||
The user may be learning about a feature for the first time, and this experience benefits from user education or onboarding. In cases like these, the empty state should put more focus on the feature’s value proposition and an action the user can take.
|
||||
|
||||
<ComponentPreview name="empty-state-initial-state-presentational" peekCode wide />
|
||||
|
||||
#### Informational
|
||||
|
||||
Or perhaps the list type is data-heavy or does not benefit from additional information. In these cases, the empty state should provide show the initial state in the same presentation as the list when there is data, much like the [zero results](#zero-results) scenario.
|
||||
|
||||
<ComponentPreview name="empty-state-initial-state-informational" peekCode wide />
|
||||
|
||||
Keep in mind that empty states will likely appear after a visual loading state. Consider layout shift and button placement during and after the transition.
|
||||
|
||||
## Components
|
||||
|
||||
There is not yet a shared empty state UI component. The context and needs for each placement differ enough to warrant custom components for each placement. That said, we should aim to make these as consistent as possible over time. See the below examples that might share common logic in a future centralized component.
|
||||
There is not yet a shared empty state UI component. The context and needs for each placement differ enough to warrant custom components for each placement.
|
||||
|
||||
## External references
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"next-contentlayer2": "0.4.6",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "catalog:",
|
||||
"react-data-grid": "7.0.0-beta.41",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-docgen": "^7.0.3",
|
||||
"react-dom": "catalog:",
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Plus } from 'lucide-react'
|
||||
import { Button, Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
|
||||
|
||||
export default function EmptyStateZeroItemsTable() {
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4 w-full">
|
||||
<Button className="w-fit self-end" type="primary" size="tiny" icon={<Plus size={14} />}>
|
||||
New table
|
||||
</Button>
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-foreground-muted">Table name</TableHead>
|
||||
<TableHead className="text-foreground-muted">Date created</TableHead>
|
||||
<TableHead />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow className="[&>td]:hover:bg-inherit">
|
||||
<TableCell colSpan={3}>
|
||||
<p className="text-sm text-foreground">No tables yet</p>
|
||||
<p className="text-sm text-foreground-lighter">Create a table to get started</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
import { Button } from 'ui'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { BucketAdd } from 'icons'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
export default function EmptyStateInitialState() {
|
||||
export default function EmptyStateInitialStatePresentational() {
|
||||
return (
|
||||
<aside className="border border-dashed w-full bg-surface-100 rounded-lg px-4 py-10 flex flex-col gap-y-4 items-center text-center gap-1 text-balance">
|
||||
<div className="flex flex-col gap-3 items-center text-center">
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
|
||||
const bucketId = 'user_avatars'
|
||||
|
||||
@@ -14,7 +14,14 @@ export default function EmptyStateMissingRoute() {
|
||||
description={`${bucketId ? `The bucket “${bucketId}”` : 'This bucket'} doesn’t seem to exist.`}
|
||||
>
|
||||
<Button asChild type="default" className="mt-2">
|
||||
<Link href="/">Head back</Link>
|
||||
<Link
|
||||
href="/"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
Head back
|
||||
</Link>
|
||||
</Button>
|
||||
</Admonition>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Users } from 'lucide-react'
|
||||
import DataGrid, { Column } from 'react-data-grid'
|
||||
import 'react-data-grid/lib/styles.css'
|
||||
import { cn } from 'ui'
|
||||
|
||||
export default function EmptyStateZeroItemsDataGrid() {
|
||||
const columns: Column<{ id: string; name: string; email: string }>[] = [
|
||||
{ key: 'name', name: 'Display name', minWidth: 200, resizable: true },
|
||||
{ key: 'email', name: 'Email', minWidth: 250, resizable: true },
|
||||
{ key: 'phone', name: 'Phone', minWidth: 150, resizable: true },
|
||||
]
|
||||
|
||||
const rows: { id: string; name: string; email: string }[] = []
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col relative min-h-[400px] rounded-md border overflow-hidden">
|
||||
<DataGrid
|
||||
className="flex-grow border-t-0 bg-dash-canvas"
|
||||
rowHeight={44}
|
||||
headerRowHeight={36}
|
||||
columns={columns}
|
||||
rows={rows}
|
||||
rowKeyGetter={(row: { id: string; name: string; email: string }) => row.id}
|
||||
rowClass={() => {
|
||||
return cn(
|
||||
'bg-surface-200 cursor-pointer',
|
||||
'[&>.rdg-cell]:border-box [&>.rdg-cell]:outline-none [&>.rdg-cell]:shadow-none',
|
||||
'[&>.rdg-cell:first-child>div]:ml-8'
|
||||
)
|
||||
}}
|
||||
renderers={{
|
||||
noRowsFallback: (
|
||||
<div className="absolute top-20 px-6 flex flex-col items-center justify-center w-full gap-y-2">
|
||||
<Users className="text-foreground-lighter" strokeWidth={1} />
|
||||
<div className="text-center">
|
||||
<p className="text-foreground">No users in your project</p>
|
||||
<p className="text-foreground-light">
|
||||
There are currently no users who signed up to your project
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Card, Table, TableBody, TableHead, TableHeader, TableRow, TableCell } from 'ui'
|
||||
import { Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
|
||||
|
||||
export default function EmptyStateZeroItemsTable() {
|
||||
return (
|
||||
@@ -14,8 +14,10 @@ export default function EmptyStateZeroItemsTable() {
|
||||
<TableBody>
|
||||
<TableRow className="[&>td]:hover:bg-inherit">
|
||||
<TableCell colSpan={3}>
|
||||
<p className="text-sm text-foreground">No tables yet</p>
|
||||
<p className="text-sm text-foreground-lighter">Connect a table from your database</p>
|
||||
<p className="text-sm text-foreground">No results found</p>
|
||||
<p className="text-sm text-foreground-lighter">
|
||||
Your search for “test” did not return any results
|
||||
</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
|
||||
@@ -1274,8 +1274,18 @@ export const examples: Registry = [
|
||||
files: ['example/empty-state-zero-items-table.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'empty-state-initial-state',
|
||||
name: 'empty-state-initial-state-presentational',
|
||||
type: 'components:example',
|
||||
files: ['example/empty-state-initial-state.tsx'],
|
||||
files: ['example/empty-state-initial-state-presentational.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'empty-state-initial-state-informational',
|
||||
type: 'components:example',
|
||||
files: ['example/empty-state-initial-state-informational.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'empty-state-zero-items-data-grid',
|
||||
type: 'components:example',
|
||||
files: ['example/empty-state-zero-items-data-grid.tsx'],
|
||||
},
|
||||
]
|
||||
@@ -37,6 +37,8 @@
|
||||
/* font-feature-settings: "rlig" 1, "calt" 1; */
|
||||
font-synthesis-weight: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+3
@@ -259,6 +259,9 @@ importers:
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 18.3.1
|
||||
react-data-grid:
|
||||
specifier: 7.0.0-beta.41
|
||||
version: 7.0.0-beta.41(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-day-picker:
|
||||
specifier: ^9.11.1
|
||||
version: 9.11.1(react@18.3.1)
|
||||
|
||||
Reference in new issue
Block a user