Components · Data display
Chart
BetaCharts using the chart palette, such as weekly volume.
- Source
components/ui/chart.tsx - shadcn/ui Chart
- Figma: not linked yet (Q13)
Up from 42 in week 1 to 61 in week 6, with a dip in week 4.
Show data table
| Week | Completed workouts |
|---|---|
| Week 1 | 42 |
| Week 2 | 48 |
| Week 3 | 51 |
| Week 4 | 47 |
| Week 5 | 55 |
| Week 6 | 61 |
When to use
- To show a trend or a comparison that a number alone can't: completed workouts per week, revenue per month, check-in rate by program.
- On dashboards and reports, next to the number the chart explains.
When not to use
- For one number: show it large, in a Card, with the change in text ("+12% on last month").
- When coaches need exact values to act on, such as invoices: use a Table or Data table.
- For more than five series: group the rest as "Other", or split the chart.
- For progress toward one goal: use Progress.
Anatomy
The Chart component is shadcn's thin wrapper around Recharts (3.8, installed). ChartContainer maps each series in a ChartConfig to a CSS variable (--color-workouts), sizes the chart and styles axes, grid and cursor with tokens. ChartTooltipContent and ChartLegendContent are styled tooltip and legend contents. The chart itself is plain Recharts: BarChart, LineChart, XAxis, Bar.
Up from 42 to 51, with a dip in week 4.
Completed workouts
Show data table
- 1. Title: what is measured, over what period
- 2. Summary: one sentence with the takeaway
- 3. Legend with text, in palette order
- 4. Plot: axes, gridlines and marks, always left to right
- 5. Text alternative: a data table
Variants
The default above is a bar chart: one bar per period, for counts. A line chart shows a trend over time, here with two series.
Subscriptions grew every month, from SAR 12,400 to SAR 16,950. One-off programs stayed between SAR 1,800 and SAR 3,100.
- Subscriptions
- One-off programs
Show data table
| Month | Subscriptions | One-off programs |
|---|---|---|
| May | SAR 12,400 | SAR 2,100 |
| Jun | SAR 13,100 | SAR 1,800 |
| Jul | SAR 13,900 | SAR 2,600 |
| Aug | SAR 14,600 | SAR 2,200 |
| Sep | SAR 15,800 | SAR 3,100 |
| Oct | SAR 16,950 | SAR 2,900 |
| Chart | Use for |
|---|---|
| Bar | Counts per period or per category: workouts per week, clients per program. Bars start at zero. |
| Line | A trend over time with many points: revenue, body weight, check-in rate. |
| Stacked bar | Parts of a total per period, with up to three parts. |
Every chart uses the theme's chart palette, var(--chart-1) to var(--chart-5), in that order. Put the colors in the ChartConfig as CSS variables, never as hex, so the chart follows SuperCoach and Coach:
const config = {
subscriptions: { label: "Subscriptions", color: "var(--chart-1)" },
programs: { label: "One-off programs", color: "var(--chart-2)" },
} satisfies ChartConfig;
Use the color key. The theme key is for light and dark values and stays unused until there is a dark theme (DECISIONS #13).
States
| State | What changes | Token |
|---|---|---|
| Rest | Axis labels muted, gridlines light | fill-muted-foreground, stroke-border/50 |
| Hover or keyboard focus on a point | Cursor band or line, tooltip with the values | fill-muted, stroke-border; tooltip bg-background, border-border/50, shadow-xl |
| Loading | A Skeleton the size of the chart, with the title already shown | — |
| No data | An Empty state in place of the plot: "No workouts logged yet" | — |
Usage guidelines
- Title first, in plain words: "Completed workouts per week, all clients".
- Then one sentence with the takeaway, so coaches don't have to work it out: "Up from 42 in week 1 to 61 in week 6".
- Bars start at zero. Lines may start elsewhere, but label the axis.
- Use the palette in order;
chart-1is the main series. Don't use status colors for series; keepsuccessanddestructivefor good and bad. - Don't animate on every data load. Recharts animates on mount; that is enough.
Monthly revenue in SAR, May to October 2026
Subscriptions grew every month, from SAR 12,400 to SAR 16,950.
Revenue
Content guidelines
- Titles say the measure, the unit and the period: "Monthly revenue in SAR, May to October 2026".
- Axis labels are short: "Week 1", "Oct", "12k". Don't repeat the unit on every tick; put it in the title.
- Legend labels match the column names in the data table.
- Numbers use Western digits in both languages (QUESTIONS Q22). Amounts follow Content › Money: "SAR 12,400", "12400 ر.س".
Accessibility
| Key | Does |
|---|---|
| Tab | Moves focus to the chart (with accessibilityLayer) |
| Arrow left / Arrow right | Moves between data points and shows the tooltip for each |
| Tab or Escape | Leaves the chart |
- Name and summary. Wrap the chart in a
<figure>with a<figcaption>title, and pointaria-describedbyat the summary sentence. Screen readers announce the title, then the summary. accessibilityLayer. Set it on every Recharts chart. It makes the chart's SVG focusable (tabIndex="0",role="application") and lets the arrow keys step through the points, showing the tooltip for each. Recharts 3 turns it on by default; set it anyway so the intent is visible in code. Also pass the title to the chart'stitleprop: it becomes the SVG's<title>, so the focusable chart has a name ("Completed workouts per week, all clients, application").- Data table. A
<details>with "Show data table" under the chart gives every value as text, in a real table with headers. It is the alternative for screen readers, for coaches with color vision deficiency, and for anyone who needs the exact number. - Legend with text. Every color has a label, and for two series the line style differs too (WCAG 1.4.1).
- Contrast. Marks need 3:1 against the background; see the Q25 note above. Axis labels use
muted-foreground(4.5:1 or more).
RTL
- The plot does not mirror. Time runs left to right in both directions, so wrap the chart in
dir="ltr", as the Data visualization foundation does. Recharts draws in fixed SVG coordinates; without the wrapper, the SVG text inherits right-to-left direction, which flipstext-anchor, and the Y-axis labels slide into the plot. - Inside the wrapper the arrow keys keep their visual meaning: Arrow right moves to the next (later) point.
- The title, summary, legend and data table follow the page direction: in Arabic the legend starts on the right.
- Axis labels are in the reading language ("الأسبوع 1", "أكتوبر") with Western digits.
ChartTooltipContent formats values with Western digits in every locale (toLocaleString("en-US"), QUESTIONS Q22), so the tooltip matches the axis and the table. Upstream used the browser's locale, which showed Arabic-Indic digits on Arabic browsers.
Tokens used
Read from components/ui/chart.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.
| Token | Utilities | SuperCoach | Coach |
|---|---|---|---|
| --background | bg-background | white | white |
| --border | stroke-border/50 stroke-border border-border/50 | neutral-200 | neutral-200 |
| --foreground | text-foreground | neutral-950 | neutral-950 |
| --muted | fill-muted | neutral-100 | neutral-100 |
| --muted-foreground | fill-muted-foreground text-muted-foreground | neutral-600 | neutral-600 |
- Radius
rounded-lg - Radius
rounded-sm - Shadow
shadow-xl - Type
font-medium - Type
leading-none - Type
text-xs
The series colors are not in this list because they come from the ChartConfig: --chart-1 to --chart-5, documented on Data visualization.
Props
<ChartContainer>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| config | ChartConfig | — |
| children | React.ComponentProps< typeof RechartsPrimitive.ResponsiveContainer >["children"] | — |
| initialDimension? | { width: number height: number } | INITIAL_DIMENSION |
<ChartTooltipContent>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| hideLabel? | boolean | false |
| hideIndicator? | boolean | false |
| indicator? | "line" | "dot" | "dashed" | "dot" |
| nameKey? | string | — |
| labelKey? | string | — |
<ChartLegendContent>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| hideIcon? | boolean | false |
| nameKey? | string | — |
Replaces in coach-admin
From the read-only audit of coach-admin: 63 style values, 78 uses.
coach-admin draws charts with Chart.js (src/components/Charts/Chart.js, and the Coach dashboards in src/GroupDashboard), with colors written in the chart options. Chart.js's own default colors are excluded from the audit. Moving to this component replaces those colors with the chart palette.
Classes and selectors
.chart-wrapper8.chart-total4.chart-card4.chart-empty-state-icon4.chart-sub-title-empty-state3.chart-empty-state2.chart-comparison-percentage-up2.chart-sub-title2.chart-comparison-percentage-down2.chart-legend li, .bar-legend li, .line-legend li, .pie-lege2.chart-comparison-percentage2.chart-comparison-icon2
Files
src/components/Charts/Chart.js29src/views/User/components/client-details/tabs-content/overview/charts/client-progress-chart.scss7src/components/Charts/GetUserProgressChart.scss3
The inventory lists the first 12 locations of each value, so file counts are a lower bound.