From 5ff03b2971e57276e372d6821d2edcb2de00dd2f Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Wed, 8 Oct 2025 12:14:03 +1000 Subject: [PATCH] feat: custom bucket icons (#39280) * chore: separate interfaces per bucket type * chore: reuse existing bucket creation modal for files * chore: allow external trigger for new bucket modal * chore: minor fixes * chore: full-size empty state * chore: files other tabs * fix: show tab bar when on file subroute * chore: use existing table component to list buckets * fix: tests * fix: build error * chore: remove future bucket type components from PR * fix: dependency * Couple of refactors to simplify implementation * Fix TS * chore: move s3 settings out to sidebar * fix: minor nits * fix: reuse existing loading spinners from schema visualiser * fix: loading for files * fix: prevent loading of table for zero buckets * chore: improved loading state * chore: shimmer on table * chore: copywriting on S3 * chore: improve empty states * fix: deprecated input instructions * fix: filter/search bucket table * chore: tidy * fix: tests * first commit * improve documentation and fix icon build error * change svg kebab case to camel case for React * copy nits * fix svg codeblock color * add bucket (normal) icon * add bucket icon to policies * add bucket icon to other storage bucket surfaces * fix spacing on empty state * fix inconsistent icons in home sections Make 1:1 with what we have in the sidebar with optically-correct strokeWidth. * fix icons Like the previous commit, this makes sure we use the same product icons as in the sidebar. * clearer bucket add icon naming * remove unused layouts * fix naming collision * Tinnnnyyy nit --------- Co-authored-by: Joshen Lim --- apps/design-system/README.md | 2 +- apps/design-system/content/docs/icons.mdx | 104 ++++++++++++++- apps/design-system/styles/mdx.css | 2 +- .../interfaces/Home/ProjectUsage.tsx | 11 +- .../HomeNew/ProjectUsageSection.tsx | 9 +- .../interfaces/Storage/EmptyBucketState.tsx | 12 +- .../StoragePoliciesBucketRow.tsx | 7 +- .../src/building/generateIconFiles.mjs | 17 +-- packages/icons/README.md | 123 +++--------------- packages/icons/__registry__/index.tsx | 20 +++ packages/icons/src/createSupabaseIcon.ts | 31 ++++- packages/icons/src/icons/bucket-add.ts | 71 ++++++++++ packages/icons/src/icons/bucket.ts | 49 +++++++ packages/icons/src/icons/index.ts | 2 + .../src/raw-icons/_example-template.json | 6 - packages/icons/src/raw-icons/bucket-add.svg | 7 + packages/icons/src/raw-icons/bucket.svg | 5 + 17 files changed, 330 insertions(+), 148 deletions(-) create mode 100644 packages/icons/src/icons/bucket-add.ts create mode 100644 packages/icons/src/icons/bucket.ts delete mode 100644 packages/icons/src/raw-icons/_example-template.json create mode 100644 packages/icons/src/raw-icons/bucket-add.svg create mode 100644 packages/icons/src/raw-icons/bucket.svg 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 ( -