# Roboto Studio chart guidelines

Data visualization on robotostudio.com: charts in posts and service pages, KPI tiles, diagrams. The globals (logo, palette, type, sharp corners, sentence case) are in [design.md](https://robotostudio.com/design.md). Printed documents use the monochrome chart system in [reports.md](https://robotostudio.com/design/reports.md); counters inside animated scenes follow [motion.md](https://robotostudio.com/design/motion.md).

## Series palette (Voltage)

Five hues, assigned to series in slot order; a single series uses slot 1. Each step flips with the page's color mode, so 2px lines hold on white and read as neon on `#141414`.

| Slot | Token | Light | Dark | Hue |
|---|---|---|---|---|
| 1 | `--chart-1` | `#0891B2` | `#22D3EE` | cyan |
| 2 | `--chart-2` | `#C026D3` | `#E879F9` | fuchsia |
| 3 | `--chart-3` | `#65A30D` | `#A3E635` | lime |
| 4 | `--chart-4` | `#EA580C` | `#FB923C` | orange |
| 5 | `--chart-5` | `#7C3AED` | `#A78BFA` | violet |

- Reference the token (`var(--chart-1)`) wherever CSS variables resolve. Use the raw hex only where they can't (SVG presentation attributes, image exports), and then pick the step for the mode you render.
- Five is the ceiling; the set was checked for color-vision-deficiency separation. A sixth series falls back to zinc-200 `#E4E4E7` and means split the chart, or rank and cut.
- Series take palette slots only: never the foreground, muted, or zinc tokens, never a new hue. Green and red are reserved for deltas and pass/fail states.

## Chart chrome

Everything that isn't data is zinc, mono, and hairline.

| Part | Spec |
|---|---|
| Frame | 1px hairline box (zinc-200 light, zinc-800 dark), 16px padding, square corners, 48px vertical margin |
| Axes and ticks | Geist Mono 10px, muted foreground; axis lines and tick marks in the border token; Y gutter 40px |
| Grid | off by default; bars get horizontal lines only at 50% opacity, lines and areas a `3 3` dashed grid in the border token |
| Legend | below the plot, centered, only with two or more series; 8px square swatch in the series color, then the label in mono, extra small, muted; a series may carry an icon (vendor logo) instead of a swatch, and a link |
| Title and caption | under the box, centered, Geist Mono, muted; title 12px (14px from the medium breakpoint), caption 12px |
| Tooltip | popover background, 1px border token, no radius, mono, 10 to 12px |
| Bars | 2px stroke in the series color over a 15% fill of it, zero radius, soft neon glow (two Gaussian blurs, 6px and 1.75px, merged under the source) |
| Lines | 1px monotone curve, no dots |
| Areas | 2px stroke over a vertical gradient from 30% to 0% of the series color |
| Pies | slices separated by a 1px border-token stroke, labeled "Name · 42%" |
| Height | 300px; 320px for pies |

The AI-visibility scoreboard band is the one chart that breaks the frame: flush to the page rails, no border, no legend, no Y gutter, glow on its area strokes. Prose charts keep the frame.

## Numbers

- `yFormat` values: `compact` (1.2K, 3.4M), `percent` (42%), `currency` (US dollars, compact). Locale is en-US.
- Tabular figures wherever numbers align in a column.
- Every number has a source and date range nearby: the caption, the surrounding paragraph, or a linked receipt. No invented figures. Case-study KPI tiles repeat values from the copy on the same page.

## MDX components

`<LineChart>`, `<BarChart>`, `<AreaChart>`, and `<PieChart>` share one prop contract and render inside the chrome above. Recharts lazy-loads; the frame, title, and caption are server-rendered so the text is crawlable.

```mdx
<LineChart
  title="Organic clicks by month"
  caption="Source: Google Search Console, Jan to Jun 2026"
  xKey="month"
  data={[
    { month: "Jan", site: 3100, competitor: 2200 },
    { month: "Feb", site: 3250, competitor: 2100 },
  ]}
  series={[
    { key: "site", label: "robotostudio.com", color: "var(--chart-1)" },
    { key: "competitor", label: "Competitor", color: "var(--chart-2)" },
  ]}
  yFormat="compact"
  showGrid
/>
```

| Prop | Meaning |
|---|---|
| `xKey` | data key for the X axis (required) |
| `data` | flat objects, one per X value (required) |
| `series` | `{ key, label, color, href?, icon? }[]` for multi-series charts |
| `yKey`, `yLabel`, `yColor` | single-series shorthand instead of `series`; always set `yColor="var(--chart-1)"`, the fallback is white |
| `title`, `caption` | mono text under the frame |
| `height` | plot height in px, default 300 |
| `showGrid` | grid lines on |
| `yFormat` | `compact`, `percent`, or `currency` |
| `innerRadius`, `outerRadius`, `showLabels` | `<PieChart>` only, defaults 0, 110, true; slices take the palette in data order |

Line for change over time with more than one series. Area for a single series over time where the volume matters. Bar for comparing or ranking categories. Pie only for parts of one whole with five or fewer slices; prefer a bar when the slices are close in size.

## KPI tiles

`<StatsBand>` is a hairline grid of cells (two or three columns, or a horizontal snap rail). Each cell is a large Geist Mono figure (36px, 48px from the medium breakpoint, tight tracking, foreground) over a small sentence-case label in zinc-600 (zinc-400 in dark mode). A cell can link to its receipt and then carries a trailing arrow. Values are strings, so write them the way they should read: "99.86%", "500k", "2 min".

## Diagrams

Author flow diagrams in Mermaid, render once to a light SVG and a dark SVG, store them on Vercel Blob, and place them with `<Diagram src srcDark alt caption width height />`. CSS swaps by color mode; the caption is mono, muted, centered. Render against the house theme, never Mermaid's stock themes:

| Variable | Light | Dark |
|---|---|---|
| Node fill | `#FAFAFA` | `#18181B` |
| Node border | `#E4E4E7` | `#3F3F46` |
| Text | `#18181B` | `#FAFAFA` |
| Line | `#A1A1AA` | `#52525B` |
| Edge label background | `#FFFFFF` | `#141414` |

Geist at 15px, transparent background, flowchart `curve: linear`, `nodeSpacing: 32`, `rankSpacing: 90`, `padding: 14`. Invert the focal node (dark fill, light text) and dash de-emphasized nodes. Hand-set `<br/>` line breaks. Prefer landscape, left-to-right flows.
