Files
supabase/apps/design-system/content/docs/icons.mdx
Ivan VasilovandJoshen Lim 9fa96977be chore: Minor prettier fixes (#43849)
This PR fixes some prettier issues:
- Bump and unify all prettier versions to 3.7.3 across teh whole repo
- Bump the SQL prettier plugin
- When running `test:prettier`, check `mdx` files also
- Run the new prettier format on all files

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-03-17 11:17:42 +01:00

136 lines
5.4 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Icons
description: Icons make actions and navigation across Supabase easier.
---
## Principles
1. Paired: Icons should accompany text, as they aren’t often obvious enough on their own.
2. Clear: Icons should be legible at small sizes and unembellished. Let the text do the heavy lifting.
3. Consistent: Use the same icons for similar actions throughout Supabase. This makes the app easier to use.
## Tints
Use classes just like you would for [text](color-usage#text) to tint icons. For example:
```jsx
<BucketAdd className="text-foreground-muted" />
```
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.
## UI icons
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.
<Icons />
### Usage
```jsx
import { BucketAdd, InsertCode, ReplaceCode } from 'icons'
function app() {
return (
<>
<ReplaceCode className="text-light" strokeWidth={1} size={16} />
<InsertCode className="text-light" strokeWidth={1} size={16} />
<BucketAdd size={24} className="text-foreground-muted" />
</>
)
}
```
**Default props**: All icons default to `size={24}`. Stroke and fill defaults come from the source SVG's root attributes (e.g. `stroke-width="1"` on the root `<svg>` becomes the component's default `strokeWidth`). You can override those defaults at the call site, but any `stroke`, `fill`, or `stroke-width` set on individual child paths will still win.
### Adding new custom icons
Follow these steps 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 follows these requirements:
- Exported at 24×24px with `viewBox="0 0 24 24"`
- Uses `stroke="currentColor"` for strokes (no hardcoded colors)
- Uses `stroke-width="1.5"` (deviate based on optical weight if necessary)
- Uses `fill="none"` for fills (no hardcoded colors)
- Icon content is optically centered and around 18×18px within the 24×24 frame
- Any unnecessary elements like `<clipPath>`, `<defs>`, and `<g>` wrappers have been removed
- SVG structure is as simple as possible with just `<path>` elements
For **fill-only icons** (e.g. logos that use shapes instead of strokes), add `stroke="none"` to the root `<svg>` element. The build will propagate this so the component never renders an unwanted stroke.
Prefer putting shared styling like `fill`, `stroke`, `stroke-width`, `stroke-linecap`, and `stroke-linejoin` on the root `<svg>`. The build propagates those root attributes as the component's defaults, and also converts them to camel-case for React compatibility (e.g. `strokeWidth`). If you put those attributes on individual child paths instead, they become fixed path-level styling and will override props passed to the component.
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:
```tsx
import { MyNewIcon } from 'icons'
```
```tsx
<MyNewIcon size={16} strokeWidth={1} />
```
The icon name is automatically made available in camel-case, determined by the kebab-case input SVG. For example, `my-new-icon.svg` will become available as `MyNewIcon`.
### SVG design guidelines
Icons should:
- Always be exported 24×24px
- Have an icon inside that frame that’s around 18×18px(ish)
- Use clean, simple paths without unnecessary wrapper elements
#### Bad example ❌
Notice the hardcoded colors, unnecessary backgrounds, and complex structure:
```svg
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" fill="#1E1E1E" /> <!-- ❌ Hardcoded color -->
<path d="M..." fill="#404040" /> <!-- ❌ Hardcoded color -->
<path d="M..." stroke="#EDEDED" stroke-linecap="round" /> <!-- ❌ Hardcoded color -->
</svg>
```
#### Good example (stroke icon) ✅
Clean structure with `currentColor` and proper attributes:
```svg
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M6 7C6 4.2 8.2 2 11 2H13C15.8 2 18 4.2 18 7" />
<path d="M4.5 11H19.5" />
<path d="M6 11L6.8 20C6.9 21.1 7.9 22 9 22H12" />
</svg>
```
#### Good example (fill-only icon / logo) ✅
Note `stroke="none"` on the root to prevent unwanted strokes, and `fill="currentColor"` on each path:
```svg
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="none" xmlns="http://www.w3.org/2000/svg">
<path d="M..." fill="currentColor" />
</svg>
```
{/* This is still wrong: */}
```svg
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M6 7C6 4.2 8.2 2 11 2H13C15.8 2 18 4.2 18 7" />
<path d="M4.5 11H19.5" />
<path d="M6 11L6.8 20C6.9 21.1 7.9 22 9 22H12" />
</svg>
```
### Troubleshooting
If your SVG specifies `stroke-width` attributes on individual child paths, they will override the component's `strokeWidth` prop. Keep shared stroke attributes on the root `<svg>` and remove them from individual paths if you want consumers to control stroke weight.