mirror of
https://github.com/supabase/supabase.git
synced 2026-05-06 08:56:46 -04:00
feat(design-system): add initial copy writing guide (#41307)
* feat: create entry points for copy writing docs * feat: basic page generated using the supabase writing style guide * chore: editing page * feat: more components * feat: remaining example components * chore: remove duplicate registry entry * chore: add aria label to example * chore: run prettier * fix: alphabetise getting started section * chore: rename copy writing to copywriting
This commit is contained in:
@@ -2975,5 +2975,93 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "Composed",
|
||||
chunks: []
|
||||
},
|
||||
"copy-button-verbs": {
|
||||
name: "copy-button-verbs",
|
||||
type: "components:example",
|
||||
registryDependencies: ["button"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-button-verbs")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-button-verbs.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-form-labels": {
|
||||
name: "copy-form-labels",
|
||||
type: "components:example",
|
||||
registryDependencies: ["form"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-form-labels")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-form-labels.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-error-messages": {
|
||||
name: "copy-error-messages",
|
||||
type: "components:example",
|
||||
registryDependencies: ["form"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-error-messages")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-error-messages.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-success-messages": {
|
||||
name: "copy-success-messages",
|
||||
type: "components:example",
|
||||
registryDependencies: ["form"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-success-messages")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-success-messages.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-tooltips": {
|
||||
name: "copy-tooltips",
|
||||
type: "components:example",
|
||||
registryDependencies: ["tooltip"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-tooltips")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-tooltips.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-loading-states": {
|
||||
name: "copy-loading-states",
|
||||
type: "components:example",
|
||||
registryDependencies: ["loading-state"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-loading-states")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-loading-states.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-empty-states": {
|
||||
name: "copy-empty-states",
|
||||
type: "components:example",
|
||||
registryDependencies: ["empty-state"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-empty-states")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-empty-states.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-confirmations": {
|
||||
name: "copy-confirmations",
|
||||
type: "components:example",
|
||||
registryDependencies: ["confirmation"],
|
||||
component: React.lazy(() => import("@/registry/default/example/copy-confirmations")),
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-confirmations.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
showGrid?: boolean
|
||||
showDottedGrid?: boolean
|
||||
wide?: boolean
|
||||
hideCode?: boolean
|
||||
}
|
||||
|
||||
export function ComponentPreview({
|
||||
@@ -36,6 +37,7 @@ export function ComponentPreview({
|
||||
showGrid = false,
|
||||
showDottedGrid = true,
|
||||
wide = false,
|
||||
hideCode = false,
|
||||
...props
|
||||
}: ComponentPreviewProps) {
|
||||
const [config] = useConfig()
|
||||
@@ -136,7 +138,10 @@ export function ComponentPreview({
|
||||
return (
|
||||
<div className={cn('mt-4 mb-12', wideClasses)}>
|
||||
<div
|
||||
className={cn('relative rounded-tl-md rounded-tr-md border-t border-l border-r bg-studio')}
|
||||
className={cn('relative bg-studio', {
|
||||
'rounded-tl-md rounded-tr-md border-t border-l border-r': !hideCode,
|
||||
'rounded-md border': hideCode,
|
||||
})}
|
||||
>
|
||||
{showGrid && (
|
||||
<div className="pointer-events-none absolute h-full w-full bg-[linear-gradient(to_right,hsla(var(--foreground-default)/0.02)_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]"></div>
|
||||
@@ -146,42 +151,44 @@ export function ComponentPreview({
|
||||
)}
|
||||
<div className="z-10 relative">{ComponentPreview}</div>
|
||||
</div>
|
||||
<Collapsible_Shadcn_>
|
||||
<CollapsibleTrigger_Shadcn_
|
||||
className={`
|
||||
flex
|
||||
gap-3 items-center
|
||||
w-full
|
||||
font-mono
|
||||
text-xs
|
||||
text-foreground-light
|
||||
px-4 py-4
|
||||
border border-r
|
||||
group
|
||||
data-[state=closed]:rounded-bl-md data-[state=closed]:rounded-br-md
|
||||
|
||||
`}
|
||||
>
|
||||
<ChevronRight
|
||||
className="transition-all group-data-[state=open]:rotate-90 text-foreground-lighter"
|
||||
size={14}
|
||||
/>
|
||||
View code
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_ className="transition-all">
|
||||
<div
|
||||
className={cn(
|
||||
'relative',
|
||||
'w-full rounded-md [&_pre]:my-0',
|
||||
'[&_pre]:overflow-auto',
|
||||
'[&_pre]:max-h-[320px]',
|
||||
'[&_pre]:rounded-tr-none [&_pre]:rounded-tl-none [&_pre]:border-t-transparent'
|
||||
)}
|
||||
{!hideCode && (
|
||||
<Collapsible_Shadcn_>
|
||||
<CollapsibleTrigger_Shadcn_
|
||||
className={`
|
||||
flex
|
||||
gap-3 items-center
|
||||
w-full
|
||||
font-mono
|
||||
text-xs
|
||||
text-foreground-light
|
||||
px-4 py-4
|
||||
border border-r
|
||||
group
|
||||
data-[state=closed]:rounded-bl-md data-[state=closed]:rounded-br-md
|
||||
|
||||
`}
|
||||
>
|
||||
{Code}
|
||||
</div>
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
<ChevronRight
|
||||
className="transition-all group-data-[state=open]:rotate-90 text-foreground-lighter"
|
||||
size={14}
|
||||
/>
|
||||
View code
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_ className="transition-all">
|
||||
<div
|
||||
className={cn(
|
||||
'relative',
|
||||
'w-full rounded-md [&_pre]:my-0',
|
||||
'[&_pre]:overflow-auto',
|
||||
'[&_pre]:max-h-[320px]',
|
||||
'[&_pre]:rounded-tr-none [&_pre]:rounded-tl-none [&_pre]:border-t-transparent'
|
||||
)}
|
||||
>
|
||||
{Code}
|
||||
</div>
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,31 +9,28 @@ export const docsConfig: DocsConfig = {
|
||||
sidebarNav: [
|
||||
{
|
||||
title: 'Getting Started',
|
||||
sortOrder: 'manual',
|
||||
sortOrder: 'alphabetical',
|
||||
items: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs',
|
||||
priority: true,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tailwind Classes',
|
||||
href: '/docs/tailwind-classes',
|
||||
title: 'Accessibility',
|
||||
href: '/docs/accessibility',
|
||||
items: [],
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Color Usage',
|
||||
href: '/docs/color-usage',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Typography',
|
||||
href: '/docs/typography',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Theming',
|
||||
href: '/docs/theming',
|
||||
title: 'Copywriting',
|
||||
href: '/docs/copywriting',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
@@ -42,8 +39,18 @@ export const docsConfig: DocsConfig = {
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Accessibility',
|
||||
href: '/docs/accessibility',
|
||||
title: 'Tailwind Classes',
|
||||
href: '/docs/tailwind-classes',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Theming',
|
||||
href: '/docs/theming',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Typography',
|
||||
href: '/docs/typography',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
---
|
||||
title: Copywriting
|
||||
description: A concise guide for writing UI copy in Supabase.
|
||||
---
|
||||
|
||||
Write UI copy that helps developers complete tasks quickly. Be direct, action-oriented, and respectful of developer time.
|
||||
|
||||
## Voice and tone
|
||||
|
||||
Supabase UI copy is:
|
||||
|
||||
- **Direct**: Say what something does, not what it "enables" you to do.
|
||||
- **Action-oriented**: Focus on what happens, not what we built.
|
||||
- **Technical without jargon**: Use precise terms but explain when necessary.
|
||||
- **Pragmatic**: Acknowledge tradeoffs and limitations when relevant.
|
||||
|
||||
## Buttons and actions
|
||||
|
||||
### Use verbs, not nouns
|
||||
|
||||
<ComponentPreview name="copy-button-verbs" hideCode />
|
||||
|
||||
### Be specific about outcomes
|
||||
|
||||
| Bad | Good |
|
||||
| ----------- | ---------------- |
|
||||
| "Remove" | "Delete project" |
|
||||
| "Change" | "Revoke access" |
|
||||
| "Configure" | "Enable RLS" |
|
||||
|
||||
### Match button text to the action
|
||||
|
||||
| Action | Bad | Good |
|
||||
| ----------------- | --------- | -------------- |
|
||||
| Primary action: | "Submit" | "Create table" |
|
||||
| Secondary action: | "Go back" | "Cancel" |
|
||||
|
||||
## Form labels and descriptions
|
||||
|
||||
### Labels describe the field, not the feature
|
||||
|
||||
<ComponentPreview name="copy-form-labels" hideCode />
|
||||
|
||||
| Action | Bad | Good |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------ | ---------------------------------------- |
|
||||
| Label: | "Name your table" | "Table name" |
|
||||
| Description: | "This field allows you to specify a name for your table using letters, numbers, and underscores" | "Letters, numbers, and underscores only" |
|
||||
|
||||
### Descriptions explain constraints, not concepts
|
||||
|
||||
| Bad | Good |
|
||||
| --------------------------------------------------------------- | ---------------------------------- |
|
||||
| "This ensures your table name is unique" | "Must be unique within the schema" |
|
||||
| "You can enter up to 255 characters here" | "Maximum 255 characters" |
|
||||
| "This field is required when using Row Level Security policies" | "Required for RLS policies" |
|
||||
|
||||
### Use present tense
|
||||
|
||||
| Bad | Good |
|
||||
| -------------------------------------- | --------------------------------- |
|
||||
| "Will store connection pool settings" | "Stores connection pool settings" |
|
||||
| "This will limit query execution time" | "Limits query execution time" |
|
||||
|
||||
## Error messages
|
||||
|
||||
### State what went wrong, then how to fix it
|
||||
|
||||
<ComponentPreview name="copy-error-messages" hideCode />
|
||||
|
||||
| Bad | Good |
|
||||
| ----------------------------------------- | ----------------------------------------------------- |
|
||||
| "An error occurred" | "Table name already exists. Choose a different name." |
|
||||
| "Something went wrong. Please try again." | "Invalid API key. Check your project settings." |
|
||||
|
||||
### Be specific about the problem
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------ | --------------------------------------------- |
|
||||
| "Invalid input" | "Password must be at least 8 characters" |
|
||||
| "Connection error" | "Connection failed: timeout after 30 seconds" |
|
||||
|
||||
### Avoid blame or apology
|
||||
|
||||
| Bad | Good |
|
||||
| ---------------------------- | ------------------------------------------------ |
|
||||
| "Sorry, we couldn't connect" | "Unable to connect to database" |
|
||||
| "Oops! Something went wrong" | "Table creation failed: column name is reserved" |
|
||||
|
||||
## Success messages
|
||||
|
||||
### Confirm what happened
|
||||
|
||||
<ComponentPreview name="copy-success-messages" hideCode />
|
||||
|
||||
| Bad | Good |
|
||||
| --------------------- | ---------------------------- |
|
||||
| "Success!" | "Table created successfully" |
|
||||
| "Done" | "API key revoked" |
|
||||
| "Operation completed" | "Changes saved" |
|
||||
|
||||
### Keep it brief
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------------------------------------------- | ------------------- |
|
||||
| "Your backup has been successfully restored to your database" | "Backup restored" |
|
||||
| "The migration has been applied successfully to your project" | "Migration applied" |
|
||||
|
||||
## Tooltips and help text
|
||||
|
||||
### Explain why, not what
|
||||
|
||||
<ComponentPreview name="copy-tooltips" hideCode />
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------- | ------------------------------------------------ |
|
||||
| "This is a toggle switch" | "Enables real-time subscriptions for this table" |
|
||||
| "Click to delete" | "Prevents accidental deletions" |
|
||||
|
||||
### One sentence maximum
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ |
|
||||
| "Row Level Security restricts access based on user policies. When enabled, users can only access rows that match their policy conditions." | "Restricts access based on user policies" |
|
||||
| "This setting controls the maximum number of concurrent connections that can be established to your database at any given time." | "Maximum number of concurrent connections" |
|
||||
|
||||
## Navigation and headings
|
||||
|
||||
### Use sentence case
|
||||
|
||||
| Bad | Good |
|
||||
| ----------------------- | ----------------------- |
|
||||
| "Set Up Authentication" | "Set up authentication" |
|
||||
| "Database Settings" | "Database settings" |
|
||||
| "Create New Project" | "Create new project" |
|
||||
|
||||
### Headings describe the page, not the feature
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------------ | -------------------- |
|
||||
| "Manage your API keys" | "API keys" |
|
||||
| "Configure connection pooling" | "Connection pooling" |
|
||||
| "Edit your tables" | "Table editor" |
|
||||
|
||||
## Empty states
|
||||
|
||||
### Explain what's missing, then how to add it
|
||||
|
||||
<ComponentPreview name="copy-empty-states" hideCode />
|
||||
|
||||
| Bad | Good |
|
||||
| --------------------------------- | ---------------------------------------------------------- |
|
||||
| "You don't have any tables" | "No tables yet. Create your first table to get started." |
|
||||
| "There are no API keys available" | "No API keys. Generate a key to connect your application." |
|
||||
|
||||
### Include the action
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------ | ------------------------------------------------- |
|
||||
| "No buckets found" | "No buckets yet. [Create bucket] button" |
|
||||
| "No functions available" | "No functions deployed. [Deploy function] button" |
|
||||
|
||||
## Loading states
|
||||
|
||||
### Describe what's happening
|
||||
|
||||
<ComponentPreview name="copy-loading-states" hideCode />
|
||||
|
||||
| Bad | Good |
|
||||
| ---------------- | ----------------------- |
|
||||
| "Please wait..." | "Creating table..." |
|
||||
| "Loading..." | "Loading schema..." |
|
||||
| "Processing..." | "Applying migration..." |
|
||||
|
||||
### Match the action verb
|
||||
|
||||
| Action | Bad | Good |
|
||||
| --------------------------- | ---------------- | --------------------- |
|
||||
| "Delete project" → Loading: | "Processing..." | "Deleting project..." |
|
||||
| "Save changes" → Loading: | "Please wait..." | "Saving changes..." |
|
||||
|
||||
## Confirmations and dialogs
|
||||
|
||||
### State consequences clearly
|
||||
|
||||
<ComponentPreview name="copy-confirmations" hideCode />
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| "Are you sure?" | "Delete this project? This action cannot be undone and will permanently delete all data." |
|
||||
| "This action is permanent. Continue?" | "Revoke this API key? Applications using this key will stop working immediately." |
|
||||
|
||||
### Use active voice
|
||||
|
||||
| Bad | Good |
|
||||
| ------------------------------------------------------------ | --------------------------------------------------- |
|
||||
| "All data will be removed if this project is deleted" | "Deleting this project will remove all data" |
|
||||
| "Existing connections will be broken if this key is revoked" | "Revoking this key will break existing connections" |
|
||||
|
||||
## Words to avoid
|
||||
|
||||
### Marketing language
|
||||
|
||||
| Bad | Good |
|
||||
| ---------------------------- | -------------------- |
|
||||
| "Easily create tables" | "Create tables" |
|
||||
| "Simply configure settings" | "Configure settings" |
|
||||
| "Powerful database features" | "Database features" |
|
||||
|
||||
### Vague verbs
|
||||
|
||||
| Bad | Good |
|
||||
| ---------------- | -------------------------------- |
|
||||
| "Manage tables" | "Create, edit, or delete tables" |
|
||||
| "Handle errors" | "View and resolve errors" |
|
||||
| "Work with data" | "Query and update data" |
|
||||
|
||||
## Capitalization
|
||||
|
||||
- **Sentence case** for all UI text (buttons, labels, headings)
|
||||
- **Product names:** Database, Auth, Storage, Edge Functions, Realtime, Vector
|
||||
- **Postgres**, not PostgreSQL
|
||||
- **Supabase** (capitalize except in code)
|
||||
|
||||
## Formatting
|
||||
|
||||
- **Bold for emphasis** only when necessary
|
||||
- **Inline code** for technical terms: `RLS`, `API key`, `supabase init`
|
||||
- **No italics** for emphasis
|
||||
- **No exclamation marks** unless critical (e.g., destructive actions)
|
||||
|
||||
## Quick checklist
|
||||
|
||||
Before publishing UI copy, ask:
|
||||
|
||||
- Does it use an action verb?
|
||||
- Is it specific about what happens?
|
||||
- Can a developer complete the task without reading more?
|
||||
- Does it avoid marketing language?
|
||||
- Is it in sentence case?
|
||||
- Is it one sentence or less (for labels, buttons, tooltips)?
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Registry } from './schema'
|
||||
|
||||
export const copyWriting: Registry = [
|
||||
{
|
||||
name: 'copy-button-verbs',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-button-verbs.tsx'],
|
||||
registryDependencies: ['button'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-form-labels',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-form-labels.tsx'],
|
||||
registryDependencies: ['form'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-error-messages',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-error-messages.tsx'],
|
||||
registryDependencies: ['form'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-success-messages',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-success-messages.tsx'],
|
||||
registryDependencies: ['form'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-tooltips',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-tooltips.tsx'],
|
||||
registryDependencies: ['tooltip'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-loading-states',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-loading-states.tsx'],
|
||||
registryDependencies: ['loading-state'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-empty-states',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-empty-states.tsx'],
|
||||
registryDependencies: ['empty-state'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
{
|
||||
name: 'copy-confirmations',
|
||||
type: 'components:example',
|
||||
files: ['example/copy-confirmations.tsx'],
|
||||
registryDependencies: ['confirmation'],
|
||||
category: 'Getting Started',
|
||||
subcategory: 'Copywriting',
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from 'ui'
|
||||
|
||||
export default function CopyButtonVerbs() {
|
||||
return (
|
||||
<div className="flex flex-row gap-16">
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<Button type="primary">Table creation</Button>
|
||||
<Button type="primary">Save action</Button>
|
||||
<Button type="danger">Bucket deletion</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<Button type="primary">Create table</Button>
|
||||
<Button type="primary">Save changes</Button>
|
||||
<Button type="danger">Delete bucket</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
|
||||
export default function CopyConfirmations() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-4 w-[400px]">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="flex flex-col gap-5 border border-border rounded-md p-4 bg-surface-100">
|
||||
<div className="flex flex-row items-start gap-4">
|
||||
<div className="flex flex-col gap-1 flex-1">
|
||||
<h3 className="text-sm font-medium text-foreground">Are you sure?</h3>
|
||||
<p className="text-sm text-foreground-light">
|
||||
All data will be removed if this project is deleted
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button type="default" size="tiny">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="danger" size="tiny">
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 w-[400px]">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="flex flex-col gap-5 border border-border rounded-md p-4 bg-surface-100">
|
||||
<div className="flex flex-row items-start gap-4">
|
||||
<div className="flex flex-col gap-1 flex-1">
|
||||
<h3 className="text-sm font-medium text-foreground">Delete this project?</h3>
|
||||
<p className="text-sm text-foreground-light">
|
||||
This action cannot be undone and will permanently delete all data. Deleting this
|
||||
project will remove all data.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button type="default" size="tiny">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="danger" size="tiny">
|
||||
Delete project
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import { EmptyStatePresentational } from 'ui-patterns'
|
||||
import { Key } from 'lucide-react'
|
||||
|
||||
export default function CopyEmptyStates() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="flex flex-col gap-4">
|
||||
<EmptyStatePresentational
|
||||
icon={Key}
|
||||
title="There are no API keys available"
|
||||
description=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="flex flex-col gap-4">
|
||||
<EmptyStatePresentational
|
||||
icon={Key}
|
||||
title="No API keys"
|
||||
description="Generate a key to connect your application."
|
||||
>
|
||||
<Button type="primary">Generate key</Button>
|
||||
</EmptyStatePresentational>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
'use client'
|
||||
|
||||
import { CircleAlert } from 'lucide-react'
|
||||
|
||||
export default function CopyErrorMessages() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-4 w-[400px]">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="flex flex-row items-center gap-2 border border-destructive-500 rounded-md px-4 py-2 bg-destructive-200 text-destructive shadow-md">
|
||||
<CircleAlert size={16} strokeWidth={1.5} />
|
||||
<p className="text-sm">Something went wrong. Please try again.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 w-[400px]">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="flex flex-row items-center gap-2 border border-destructive-500 rounded-md px-4 py-2 bg-destructive-200 text-destructive shadow-md">
|
||||
<CircleAlert size={16} strokeWidth={1.5} />
|
||||
<p className="text-sm">Invalid API key. Check your project settings.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import { Input_Shadcn_, Label_Shadcn_ } from 'ui'
|
||||
|
||||
export default function CopyFormLabels() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-4 w-[300px]">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="grid w-full items-center gap-1.5">
|
||||
<Label_Shadcn_ htmlFor="table-name-bad">Name your table</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="table-name-bad" placeholder="my_table" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This field allows you to specify a name for your table using letters, numbers, and
|
||||
underscores
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 w-[300px]">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="grid w-full items-center gap-1.5">
|
||||
<Label_Shadcn_ htmlFor="table-name-good">Table name</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="table-name-good" placeholder="my_table" />
|
||||
<p className="text-sm text-muted-foreground">Letters, numbers, and underscores only</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from 'ui'
|
||||
|
||||
export default function CopyLoadingStates() {
|
||||
return (
|
||||
<div className="flex flex-row gap-16">
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Button type="primary" loading>
|
||||
Please wait...
|
||||
</Button>
|
||||
<Button type="primary" loading>
|
||||
Loading...
|
||||
</Button>
|
||||
<Button type="danger" loading>
|
||||
Processing...
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Button type="primary" loading>
|
||||
Creating table...
|
||||
</Button>
|
||||
<Button type="primary" loading>
|
||||
Loading schema...
|
||||
</Button>
|
||||
<Button type="danger" loading>
|
||||
Deleting project...
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
'use client'
|
||||
|
||||
import { CheckCircle } from 'lucide-react'
|
||||
|
||||
export default function CopySuccessMessages() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-4 w-[400px]">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="flex flex-row items-center gap-2 border border-brand-500 rounded-md px-4 py-2 bg-brand-300 text-brand-600 shadow-md">
|
||||
<CheckCircle size={16} strokeWidth={1.5} />
|
||||
<p className="text-sm">Success!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 w-[400px]">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="flex flex-row items-center gap-2 border border-brand-500 rounded-md px-4 py-2 bg-brand-300 text-brand-600 shadow-md">
|
||||
<CheckCircle size={16} strokeWidth={1.5} />
|
||||
<p className="text-sm">Table created successfully</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'ui'
|
||||
import { Info } from 'lucide-react'
|
||||
|
||||
export default function CopyTooltips() {
|
||||
return (
|
||||
<div className="flex flex-row gap-20">
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Bad Example</span>
|
||||
<div className="flex flex-row gap-8 items-center">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="text"
|
||||
className="flex items-center px-1.5"
|
||||
icon={<Info size={20} strokeWidth={1.5} />}
|
||||
aria-label="More info"
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>This is a toggle switch</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs text-foreground-muted">Good Example</span>
|
||||
<div className="flex flex-row gap-8 items-center">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="text"
|
||||
className="flex items-center px-1.5"
|
||||
icon={<Info size={20} strokeWidth={1.5} />}
|
||||
aria-label="More info"
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Restricts access based on user policies</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,5 +2,6 @@ import { Registry } from '@/registry/schema'
|
||||
import { examples } from '@/registry//examples'
|
||||
import { fragments } from '@/registry/fragments'
|
||||
import { charts } from '@/registry/charts'
|
||||
import { copyWriting } from '@/registry/copy-writing'
|
||||
|
||||
export const registry: Registry = [...fragments, ...examples, ...charts]
|
||||
export const registry: Registry = [...fragments, ...examples, ...charts, ...copyWriting]
|
||||
|
||||
Reference in New Issue
Block a user