Files
Danny White d067e81a69 fix(ui): align primary colours across text, buttons, and controls (#50697)
## Problem

Primary colour serves readable text and selected controls, but those
uses need different shades. Light mode needs darker text, while dark
mode needs a deeper button fill. Fixed brand green on interactive chrome
also prevents a custom primary hue from carrying through the interface.
Some slider tracks and selected text are hard to read.

## Solution

- Keep `--primary` for accessible text and small selected indicators.
Use `--primary-solid` for button fills, which need a deeper shade in
dark mode.
- Add `--primary-bright` for focus rings, selected control chrome, chart
accents, and other interactive highlights. It follows `--primary-hue`;
`brand-*` stays fixed for Supabase identity.
- Make slider troughs clearer and text selection translucent with theme
foreground text.
- Document the split in the design-system colour guide.

| Before | After |
| --- | --- |
| <img width="980" height="244" alt="Before: light mode primary
controls"
src="https://github.com/user-attachments/assets/dfae325d-0dfe-4231-8bcd-3f89c4b9d793"
/> | <img width="982" height="204" alt="After: light mode primary
controls"
src="https://github.com/user-attachments/assets/5fdcb531-a6e3-4549-8a13-9d9a5ebe6e20"
/> |
| <img width="610" height="120" alt="Before: slider track"
src="https://github.com/user-attachments/assets/04f768e0-51e8-4d06-9b97-c52f4a34f122"
/> | <img width="622" height="126" alt="After: slider track"
src="https://github.com/user-attachments/assets/95127f4e-13dc-4f0f-b63c-cf5d70a28b42"
/> |
| <img width="652" height="512" alt="Before: dark mode controls"
src="https://github.com/user-attachments/assets/3f88de66-90cc-40ee-8cf1-b5f4eb87b09a"
/> | <img width="658" height="498" alt="After: dark mode controls"
src="https://github.com/user-attachments/assets/906bec30-6ca1-4614-9fb3-6cf5e5feec22"
/> |

## Review instructions

1. Compare light and dark mode in the [colour usage
guide](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/color-usage#primary-and-brand-colors).
Check primary ink, primary-solid, primary-bright, and fixed brand
swatches.
2. In Studio, open the ‘new table’ sheet in [Table
Editor](https://studio-staging-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/dashboard/project/_/editor).
Tab through the new table sheet's fields and toggles. Check the focus
rings, selected controls, and the sheet's edges in both themes. You do
not need to save a table.
3. Select text in Studio in both themes, including a link or
primary-coloured label. The selection and text should remain legible.
4. Check the
[Field](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/field)
Price Range slider: the unused track should remain visible in both
themes. The selected field card border should follow primary-bright.
5. Check the
[Button](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/button)
and [Radio
Group](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/radio-group)
previews. In dark mode, `primary` button fill should be deeper than
primary
[text](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/color-usage#text);
selected radios should remain readable.
2026-09-24 09:56:31 +10:00

305 lines
23 KiB
Plaintext

---
title: Charts
description: Composable charts for Reports, Dashboards and other visualizations.
links:
doc: https://recharts.github.io/en-US/
source:
recharts: true
---
<ComponentPreview name="chart-composed-demo" peekCode wide />
## About
Charts are an integral part of Observability at Supabase. Charts aim to be composable and reusable for frictionless setup.
Our charts use a combination of our own presentational components and [Recharts](https://recharts.github.io/en-US/) for extensibility.
## Best Practices
1. **Use provided chart types first**: Always try to use the default provided charts first, otherwise passing Recharts compoennts to `<ChartContent>` is possible but not recommended to avoid complexity.
2. **Use the `useChart` context to show loading and disabled states**: The `useChart` context provides boolean flags for child components to show `isLoading` and `isDisabled` states.
3. **Keep it simple**: Try to avoid abstracting the chart content too much. These components should cover most of your presentational needs.
## Color
Series colors come from eight categorical slots, `--chart-1` through `--chart-8`, defined in
`packages/config/css/charts.css`. Assign them in order and never cycle: a ninth series folds
into "Other" or becomes small multiples. Each slot has a matching `-fill` token. Slots resolve
per theme, so pass `var(--chart-n)` and never branch on light/dark in code. Adjacent slots
alternate hue families and clear colorblind separation in both themes.
Reference lines use `--chart-reference`. Headroom, idle and unused capacity use `--chart-muted`.
Directional pairs use `--chart-in` / `--chart-out` so read and write keep the same hue across
charts.
Status colors (`--chart-status-success`, `-warning`, `-destructive`, each with a `-muted` tier)
are reserved for state and always ship with an icon or label. Never use one as a series color:
amber on a neutral metric reads as a problem. Warm hues are otherwise limited to tomato, slot 5,
because no amber or yellow step is legible on the dark surface.
<ComponentPreview name="chart-palette" wide />
Every slot stacked together, to check adjacent segments stay separable in both themes.
<ComponentPreview name="chart-palette-stress" wide />
## Examples
### Basic Chart Types
<ComponentPreview name="chart-composed-basic" wide />
### Chart States
<ComponentPreview name="chart-composed-states" wide />
### Charts as Standalone Metrics
<ComponentPreview name="chart-composed-metrics" wide />
### Charts with Actions
<ComponentPreview name="chart-composed-actions" wide />
### Chart with Table
<ComponentPreview name="chart-composed-table" wide />
You can pass through our [Table](/docs/components/table) component to the `ChartFooter` and expect it to be styled correctly. There is no additional need to style the table, `ChartFooter` will handle the styling for you.
## API
### Chart
The root container component that provides chart context to all child components.
| Prop | Type | Default | Description |
| ------------ | -------------------------------------- | ------- | ------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Chart child components |
| `isLoading` | `boolean` | `false` | Shows loading state in child components via the `useChart` context |
| `isDisabled` | `boolean` | `false` | Disables chart interactions via the `useChart` context |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartCard
A card wrapper for the chart. Can be used as a Card component or as a slot.
| Prop | Type | Default | Description |
| ----------- | -------------------------------------- | ------- | ----------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Chart content |
| `asChild` | `boolean` | - | Render as child component (uses Slot) - ideal for removing the `Card` wrapper |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartHeader
Container for chart header content like title and actions.
| Prop | Type | Default | Description |
| ----------- | -------------------------------------- | ---------- | ------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header content (typically ChartTitle, ChartActions, or ChartMetric) |
| `align` | `'start' \| 'center'` | `'center'` | Alignment of header content |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartTitle
Displays the chart title with optional tooltip.
| Prop | Type | Default | Description |
| ----------- | -------------------------------------- | ------- | ------------------------------------- |
| `children` | `React.ReactNode` | - | Title text |
| `tooltip` | `string` | - | Tooltip text shown on help icon hover |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartActions
Renders action buttons or links in the chart header.
| Prop | Type | Default | Description |
| ----------- | -------------------------------------- | ------- | ---------------------------------------------------------- |
| `actions` | `ChartAction[]` | - | Array of action objects (see ChartAction type below) |
| `children` | `React.ReactNode` | - | Custom action content (takes precedence over actions prop) |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
#### ChartAction Type
| Property | Type | Description |
| ----------- | -------------------- | ----------------------------------------------- |
| `label` | `string` | Accessible label for the action |
| `icon` | `React.ReactNode` | Icon component to display |
| `onClick` | `() => void` | Click handler for button actions |
| `href` | `string` | URL for link actions |
| `type` | `'button' \| 'link'` | Action type (auto-detected if href is provided) |
| `className` | `string` | Additional CSS classes |
### ChartMetric
Displays a metric value with optional status indicator.
| Prop | Type | Default | Description |
| ----------- | ---------------------------------------------------- | --------- | --------------------------------------------------- |
| `label` | `string` | - | Metric label text |
| `value` | `string \| number \| null \| undefined` | - | Metric value to display |
| `diffValue` | `string \| number \| null \| undefined` | - | Differential value to display (shown as +/- change) |
| `status` | `'positive' \| 'negative' \| 'warning' \| 'default'` | - | Status indicator color |
| `align` | `'start' \| 'end'` | `'start'` | Alignment of the metric |
| `tooltip` | `string` | - | Tooltip text shown on help icon hover |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartContent
Container for the main chart visualization. Handles loading and empty states.
| Prop | Type | Default | Description |
| ----------------- | -------------------------------------- | ------- | -------------------------------------- |
| `children` | `React.ReactNode` | - | Chart visualization content |
| `isEmpty` | `boolean` | `false` | Whether to show empty state |
| `emptyState` | `React.ReactNode` | - | Custom empty state component |
| `loadingState` | `React.ReactNode` | - | Custom loading state component |
| `disabledState` | `React.ReactNode` | - | Custom disabled state component |
| `disabledActions` | `ChartAction[]` | - | Actions to show when chart is disabled |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartEmptyState
Pre-built empty state component for charts.
| Prop | Type | Default | Description |
| ------------- | -------------------------------------- | ------- | ------------------------ |
| `title` | `string` | - | Empty state title |
| `description` | `string` | - | Empty state description |
| `icon` | `React.ReactNode` | - | Optional icon to display |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartLoadingState
Pre-built loading state component. Takes no props.
### ChartFooter
Container for footer content below the chart.
| Prop | Type | Default | Description |
| ----------- | -------------------------------------- | ------- | ---------------------- |
| `children` | `React.ReactNode` | - | Footer content |
| `className` | `string` | - | Additional CSS classes |
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | - | All standard div props |
### ChartConfig
Type definition for chart configuration used with Recharts components.
```tsx
type ChartConfig = {
[key: string]: {
label?: React.ReactNode
icon?: React.ComponentType
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<'light' | 'dark', string> }
)
}
```
| Property | Type | Description |
| -------- | ----------------------------------- | -------------------------------------------------------- |
| `label` | `React.ReactNode` | Label for the data series |
| `icon` | `React.ComponentType` | Icon component for the data series |
| `color` | `string` | Single color value (mutually exclusive with theme) |
| `theme` | `Record<'light' \| 'dark', string>` | Theme-aware color values (mutually exclusive with color) |
### ChartLine
Line chart component for displaying time-series data.
| Prop | Type | Default | Description |
| ------------------- | --------------------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------- |
| `data` | `ChartLineTick[]` | - | Array of data points with timestamp |
| `dataKey` | `string` | - | Key in data object to plot |
| `config` | `ChartConfig?` | - | Chart configuration for styling |
| `onLineClick` | `(datum: ChartLineTick, tooltipData?: CategoricalChartState) => void` | - | Click handler for line points |
| `DateTimeFormat` | `string` | `'MMM D, YYYY, hh:mma'` | Date format for tooltips and labels |
| `isFullHeight` | `boolean` | `false` | Whether chart should take full height |
| `className` | `string` | - | Additional CSS classes |
| `color` | `string` | `'var(--primary-bright)'` | Line color |
| `hoverColor` | `string` | `'hsl(var(--brand-500))'` | Line color on hover |
| `chartHighlight` | `ChartHighlight` | - | Highlight selection configuration |
| `updateDateRange` | `(from: string, to: string) => void` | - | Callback when date range is updated via highlight |
| `highlightActions` | `ChartHighlightAction[]` | - | Actions to show when area is highlighted |
| `syncId` | `string` | - | ID to sync multiple charts |
| `showHighlightArea` | `boolean` | `true` | Whether to show highlight area |
| `cursor` | `string` | - | Cursor style (defaults to 'crosshair' if chartHighlight provided) |
| `showGrid` | `boolean` | `false` | Whether to show grid lines |
| `showYAxis` | `boolean` | `false` | Whether to show Y-axis |
| `YAxisProps` | `object` | - | Additional Y-axis props (tick, tickFormatter, width, etc.) |
| `strokeWidth` | `number` | `1.5` | Line stroke width |
#### ChartLineTick Type
| Property | Type | Description |
| --------------- | ------------------ | ---------------------------- |
| `timestamp` | `string` | Timestamp for the data point |
| `[key: string]` | `string \| number` | Additional data fields |
#### ChartHighlight Type
| Property | Type | Description |
| ----------------- | ------------------------------------------------------------- | ------------------------------------ |
| `handleMouseDown` | `(e: { activeLabel?: string; coordinates?: string }) => void` | Mouse down handler |
| `handleMouseMove` | `(e: { activeLabel?: string; coordinates?: string }) => void` | Mouse move handler |
| `handleMouseUp` | `(e: { chartX?: number; chartY?: number }) => void` | Mouse up handler |
| `coordinates` | `{ left?: string; right?: string }` | Highlight area coordinates |
| `clearHighlight` | `() => void?` | Optional function to clear highlight |
#### ChartHighlightAction Type
| Property | Type | Description |
| ------------ | -------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `id` | `string` | Unique identifier for the action |
| `label` | `string \| ((ctx: { start: string; end: string; clear: () => void }) => string)` | Action label or function that returns label |
| `icon` | `ReactNode?` | Optional icon component |
| `isDisabled` | `(ctx: { start: string; end: string; clear: () => void }) => boolean?` | Optional function to determine if action is disabled |
| `onSelect` | `(ctx: { start: string; end: string; clear: () => void }) => void` | Action selection handler |
### ChartBar
Bar chart component for displaying time-series data.
| Prop | Type | Default | Description |
| ------------------- | -------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------- |
| `data` | `ChartBarTick[]` | - | Array of data points with timestamp |
| `dataKey` | `string` | - | Key in data object to plot |
| `config` | `ChartConfig?` | - | Chart configuration for styling |
| `onBarClick` | `(datum: ChartBarTick, tooltipData?: CategoricalChartState) => void` | - | Click handler for bars |
| `DateTimeFormat` | `string` | `'MMM D, YYYY, hh:mma'` | Date format for tooltips and labels |
| `isFullHeight` | `boolean` | `false` | Whether chart should take full height |
| `className` | `string` | - | Additional CSS classes |
| `color` | `string` | `'var(--primary-bright)'` | Bar color |
| `hoverColor` | `string` | `'var(--primary-bright-hover)'` | Bar color on hover |
| `chartHighlight` | `ChartHighlight` | - | Highlight selection configuration |
| `updateDateRange` | `(from: string, to: string) => void` | - | Callback when date range is updated via highlight |
| `highlightActions` | `ChartHighlightAction[]` | - | Actions to show when area is highlighted |
| `syncId` | `string` | - | ID to sync multiple charts |
| `showHighlightArea` | `boolean` | `true` | Whether to show highlight area |
| `cursor` | `string` | - | Cursor style (defaults to 'crosshair' if chartHighlight provided) |
| `showGrid` | `boolean` | `false` | Whether to show grid lines |
| `showYAxis` | `boolean` | `false` | Whether to show Y-axis |
| `YAxisProps` | `object` | - | Additional Y-axis props (tick, tickFormatter, width, etc.) |
#### ChartBarTick Type
| Property | Type | Description |
| --------------- | ------------------ | ---------------------------- |
| `timestamp` | `string` | Timestamp for the data point |
| `[key: string]` | `string \| number` | Additional data fields |