Files
Danny White 73c9dbfa52 fix(studio): standardise custom icon weight (#48478)
## What kind of change does this PR introduce?

Bug fix and internal tooling update. Resolves FE-3472.

## What is the current behavior?

Custom Studio icons use inconsistent source stroke widths, and some
child-level styling prevents component props from overriding them. Mixed
custom and Lucide icon sets can therefore appear uneven.

## What is the new behavior?

Custom stroke icons use a root-level `stroke-width="1.5"`; fill-only
logos use `stroke="none"`. The build validates that contract and
regenerated components preserve existing exports and props.

Studio applies the same `1.5` weight across Reports categories and uses
one shared destination icon mapping in the replication selector,
destination rows and diagram.

| Before | After |
| --- | --- |
| <img width="418" height="516" alt="56398"
src="https://github.com/user-attachments/assets/6afa7042-e6be-40e7-9911-af2f61238c9d"
/> | <img width="390" height="550" alt="CleanShot 2026-07-30 at 17 12
37@2x"
src="https://github.com/user-attachments/assets/870f49cf-c8fa-40db-8be8-2eb5f264ff4a"
/> |
| <img width="510" height="734" alt="CleanShot 2026-07-30 at 17 19
28@2x"
src="https://github.com/user-attachments/assets/a5b2c088-dcd2-4907-976b-5820794d06e3"
/> | <img width="554" height="742" alt="CleanShot 2026-07-30 at 17 16
06@2x"
src="https://github.com/user-attachments/assets/ed3a77c4-5d94-4ca7-b9e4-1403b725a981"
/> |


## Testing

At 100% zoom, compare custom and Lucide icon weight in:

- Reports: **Add your first chart** and **Add block**
- Database > Replication: the destination selector, destination rows and
replication diagram
- Command menu (`⌘K`): **Search Database Tables**, **Search RLS
Policies**, **Search Edge Functions** and **Search Storage**
- Authentication > Users: right-click a user row and compare the
context-menu icons
- Database > Schema Visualizer: open a table node overflow menu
- A paused project: **Export your data > Download backups**

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added consistent destination icons across replication panels, rows,
and diagrams.
  * Updated instance health and metric icons for clearer identification.
* Standardized icon stroke weight and reduced default icon stroke
thickness.

* **Documentation**
* Clarified custom icon requirements, default properties, and validation
guidance.

* **Bug Fixes**
* Improved consistency of icon rendering across replication destinations
and reports.

* **Tests**
* Added coverage for icon SVG validation and replication destination
icon rendering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-03 17:48:50 +10:00

136 lines
5.1 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 icons use `strokeWidth={1.5}` and fill-only icons use `stroke="none"`. These defaults come from the source SVG's root attributes and can be intentionally overridden at the call site.
### 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"`
- 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.
Put shared styling like `fill`, `stroke`, `stroke-width`, `stroke-linecap`, and `stroke-linejoin` on the root `<svg>`. The build validates this contract, propagates the root attributes as the component's defaults, and converts them to camel-case for React compatibility (e.g. `strokeWidth`). Child-level stroke styling is rejected because it would 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
The icon build reports the source file and invalid attribute when an SVG does not follow the stroke or fill-only contract. Run `pnpm validate:icons` from `packages/icons` to check sources without regenerating components.