diff --git a/apps/design-system/README.md b/apps/design-system/README.md index 0dd571441fe..96a8eaf8fef 100644 --- a/apps/design-system/README.md +++ b/apps/design-system/README.md @@ -2,7 +2,7 @@ Design resources for building consistent user experiences at Supabase. -## Getting Started +## Getting started First, make a copy of _.env.local.example_ and name it _env.local_. Then install any required packages and start the development server: diff --git a/apps/design-system/content/docs/icons.mdx b/apps/design-system/content/docs/icons.mdx index f9df03365a4..5e020a680d4 100644 --- a/apps/design-system/content/docs/icons.mdx +++ b/apps/design-system/content/docs/icons.mdx @@ -11,14 +11,108 @@ description: Icons make actions and navigation across Supabase easier. ## Tints -Destructive actions, such as deleting an API key, don’t need to be [tinted](color-usage#text) with `text-destructive` because there should be a confirmation dialog as a failsafe right after. +Use classes just like you would for [text](color-usage#text) to tint icons. For example: -## UI Icons +```jsx + +``` -We rely on Lucide icons for most of our UI icons. +Just like text, don’t tint icons with `text-destructive` for destructive actions. There should be a confirmation dialog right after which can handle the destructive styling. -## Custom Icons +## UI icons -Tap on an icon below to copy the JSX, SVG, or import path. +We rely on [Lucide](https://lucide.dev/icons/) for any standard UI icon needs. + +## Custom icons + +Create and use custom icons when Lucide doesn’t have the icon you need. Tap on an icon below to copy the JSX, SVG, or import path. + +### Usage + +```jsx +import { ReplaceCode, InsertCode, BucketAdd } from 'icons' + +function app() { + return ( + <> + + + + + ) +} +``` + +**Default props**: All icons have `strokeWidth={2}` and `size={24}` by default. Override these props as needed for your use case. + +### Adding new custom icons + +To add a new custom icon to the Supabase icon library: + +1. **Create SVG file**: Add your SVG file to `packages/icons/src/raw-icons/` with a kebab-case name (e.g., `my-new-icon.svg`). Make sure it has follows these exact requirements: + + - Exported at 24x24px with `viewBox="0 0 24 24"` + - Uses `stroke="currentColor"` for strokes (no hardcoded colors) + - Uses `fill="none"` for fills (no hardcoded colors) + - Icon content is optically centered and around 18x18px within the 24x24 frame + - Any unnecessary elements like ``, ``, and `` wrappers have been removed + - SVG structure is as simple as possible with just `` elements + +Just leave attributes like `stroke-width` as they are. The conversion to camel-case (for React compatibility) is handled by the below build process. + +2. **Build the component**: Run `npm run build:icons` from inside the `packages/icons` directory + +3. **Use the icon**: Import and use like any other icon: + + ```jsx + import { MyNewIcon } from 'icons' + ; + ``` + +### SVG design guidelines + +Icons should: + +- Always be exported 24x24px +- Have an icon inside that frame that’s around 18x18px(ish) +- Use clean, simple paths without unnecessary wrapper elements + +#### Bad example ❌ + +Notice the hardcoded colors, unnecessary backgrounds, and complex structure: + +```svg + + + + + +``` + +#### Good example ✅ + +Clean structure with `currentColor` and proper attributes: + +```svg + + + + + +``` + +{/* This is still wrong: */} + +```svg + + + + + +``` + +### Troubleshooting + +If your SVG specifies `stroke-width` attributes, they will override the component's `strokeWidth` prop. Remove stroke attributes from individual paths to let the component control them. diff --git a/apps/design-system/styles/mdx.css b/apps/design-system/styles/mdx.css index afac3b50ede..84391167b6f 100644 --- a/apps/design-system/styles/mdx.css +++ b/apps/design-system/styles/mdx.css @@ -15,7 +15,7 @@ } [data-rehype-pretty-code-fragment] { - @apply relative text-white; + @apply relative; } [data-rehype-pretty-code-fragment] code { diff --git a/apps/studio/components/interfaces/Home/ProjectUsage.tsx b/apps/studio/components/interfaces/Home/ProjectUsage.tsx index 1ee9a4ddd10..346332575d8 100644 --- a/apps/studio/components/interfaces/Home/ProjectUsage.tsx +++ b/apps/studio/components/interfaces/Home/ProjectUsage.tsx @@ -1,6 +1,7 @@ import dayjs from 'dayjs' import sumBy from 'lodash/sumBy' -import { Archive, ChevronDown, Database, Key, Zap } from 'lucide-react' +import { ChevronDown } from 'lucide-react' +import { Auth, Database, Realtime, Storage } from 'icons' import Link from 'next/link' import { useRouter } from 'next/router' import { useState } from 'react' @@ -200,7 +201,7 @@ const ProjectUsage = () => { - + } title="Database" @@ -226,7 +227,7 @@ const ProjectUsage = () => { - + } title="Auth" @@ -252,7 +253,7 @@ const ProjectUsage = () => { - + } title="Storage" @@ -278,7 +279,7 @@ const ProjectUsage = () => { - + } title="Realtime" diff --git a/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx b/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx index 67f46bdbb20..b29c45a6761 100644 --- a/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx +++ b/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx @@ -1,5 +1,5 @@ import dayjs from 'dayjs' -import { Archive, ChevronDown, Code, Database, Key, Zap } from 'lucide-react' +import { ChevronDown } from 'lucide-react' import Link from 'next/link' import { useRouter } from 'next/router' import { useMemo, useState } from 'react' @@ -77,7 +77,6 @@ type ServiceKey = 'db' | 'functions' | 'auth' | 'storage' | 'realtime' type ServiceEntry = { key: ServiceKey title: string - icon: React.ReactNode href?: string route: string enabled: boolean @@ -159,7 +158,7 @@ export const ProjectUsageSection = () => { { key: 'db', title: 'Database requests', - icon: , + href: `/project/${projectRef}/editor`, route: '/logs/postgres-logs', enabled: true, @@ -167,14 +166,12 @@ export const ProjectUsageSection = () => { { key: 'functions', title: 'Functions requests', - icon: , route: '/logs/edge-functions-logs', enabled: true, }, { key: 'auth', title: 'Auth requests', - icon: , href: `/project/${projectRef}/auth/users`, route: '/logs/auth-logs', enabled: authEnabled, @@ -182,7 +179,6 @@ export const ProjectUsageSection = () => { { key: 'storage', title: 'Storage requests', - icon: , href: `/project/${projectRef}/storage/buckets`, route: '/logs/storage-logs', enabled: storageEnabled, @@ -190,7 +186,6 @@ export const ProjectUsageSection = () => { { key: 'realtime', title: 'Realtime requests', - icon: , route: '/logs/realtime-logs', enabled: true, }, diff --git a/apps/studio/components/interfaces/Storage/EmptyBucketState.tsx b/apps/studio/components/interfaces/Storage/EmptyBucketState.tsx index 3b4e1a61a65..32a1b30ff01 100644 --- a/apps/studio/components/interfaces/Storage/EmptyBucketState.tsx +++ b/apps/studio/components/interfaces/Storage/EmptyBucketState.tsx @@ -1,3 +1,4 @@ +import { BucketAdd } from 'icons' import { CreateBucketModal } from './CreateBucketModal' import { BUCKET_TYPES } from './Storage.constants' @@ -9,10 +10,13 @@ export const EmptyBucketState = ({ bucketType }: EmptyBucketStateProps) => { const config = BUCKET_TYPES[bucketType] return ( -