# Data Visualization — Agent Style Guide

Guidelines for generating scientific graphs consistent with this design system's visual language, color palette, typography, and layout conventions. This file defines **global rules** that apply to every chart type. Chart-specific rules live in dedicated files linked in the [Chart Index](#chart-index) below.

---

## Table of Contents

1. [Overview](#overview)
2. [Color System](#color-system)
3. [Typography](#typography)
4. [Axes & Gridlines](#axes--gridlines)
5. [Legends](#legends)
6. [Spacing & Layout](#spacing--layout)
7. [What to Never Do](#what-to-never-do)
8. [Quick Checklist](#quick-checklist)
9. [Chart Index](#chart-index)

---

## Overview

Every graph produced in this system must follow a unified visual style: soft, accessible colors drawn from the design system's palette, clean typographic hierarchy using Figtree, minimal axis decoration, and consistent treatment of legends. This guide is the single source of truth for all visualization decisions. Chart-specific files extend these rules — they never override them.

---

## Color System

### Available Palettes

All graph colors must be drawn from the design system's named color scales. The primary three are **Blue**, **Pink**, and **Orange**. When a graph requires more than three categories, expand to the additional scales listed below.

**Priority order for multi-series graphs:**
1. Pink
2. Blue
3. Orange
4. *(additional scales as needed — see below)*

### Core Three Scales

#### Blue
| Stop | Hex |
|------|-----|
| 50 | `#F1FEFF` |
| 100 | `#E0F1F4` |
| 200 | `#BFDFE6` |
| 300 | `#9FCEDB` |
| 400 | `#6BAFC4` |
| 500 | `#047897` |
| 600 | `#025F78` |
| 700 | `#054F6A` |
| 800 | `#044055` |
| 900 | `#03303F` |

#### Pink
| Stop | Hex |
|------|-----|
| 50 | `#FFF9FF` |
| 100 | `#FAD0F5` |
| 200 | `#EAC3E4` |
| 300 | `#DC9ED3` |
| 400 | `#CB62BB` |
| 500 | `#A81696` |
| 600 | `#8A1079` |
| 700 | `#6C0B5E` |
| 800 | `#55094B` |
| 900 | `#3E0638` |

#### Orange
| Stop | Hex |
|------|-----|
| 50 | `#FFF9F5` |
| 100 | `#FAEADE` |
| 200 | `#EED4BF` |
| 300 | `#DBB397` |
| 400 | `#CC8F66` |
| 500 | `#9A5E2A` |
| 600 | `#74431A` |
| 700 | `#563010` |
| 800 | `#3D2009` |
| 900 | `#281204` |

---

### Extended Palette — Additional Color Scales

> **Design team note:** The system currently supports three color families (blue, pink, orange). For graphs with up to 14 categories, the following additional scales must be generated following the [Color Scale Generation guidelines](../color/color-scale-generation.md). Until those scales are formally added to the design system, generate new scales using that document's process, anchoring on these seed hues:

| Scale Name | Seed Hue Direction | Notes |
|---|---|---|
| Teal | Blue-green | Distinct from Blue; avoid cyan drift |
| Purple | Red-violet | Distinct from Pink; avoid blue drift |
| Green | Yellow-green | Muted, natural tone |
| Yellow | Warm yellow | Use with caution — contrast is limited at light stops |
| Red | True red | Distinct from Pink; avoid orange drift |
| Lime | Bright green | Use sparingly |
| Indigo | Deep blue-purple | Dark, distinct from Blue |
| Coral | Warm red-orange | Bridges Orange and Red |
| Sage | Muted olive-green | Neutral, background-safe |
| Slate | Cool grey-blue | Neutral; good for secondary series |
| Amber | Deep warm yellow | More contrast than Yellow |

When generating scales for the above, follow all rules in the Color Scale Generation guide: 9 stops, correct contrast ratios, no hue drift.

---

### How to Apply Color to Graph Elements

**For all filled shapes** (bars, arcs, pie segments, area fills):
- **Fill:** Use the **100** stop of the color scale
- **Stroke:** Use the **300** stop of the color scale
- **Stroke weight:** Always **1px**. Never use any other value for shape strokes.
- **Stroke corners:** Rounded (match the element's border-radius)

**For line graphs:**
- **Line stroke:** Use the **300** stop of the color scale
- **Stroke weight on lines:** 2px
- **Inflection point circle fill:** Use the **100** stop
- **Inflection point circle stroke:** Use the **300** stop

**For text/labels inside filled shapes** (if present):
- Use the system's **secondary foreground color (`--fg-secondary`)** token

**Try to stay within Blue, Pink, and Orange** for graphs with three or fewer series. Expand to additional scales only when needed.

---

## Typography

All text in graphs uses the **Figtree** typeface family. Never substitute another font. If Figtree is not available locally, download the hosted font files and register them before generating any chart. See the [Typography Guide](../typography/typography-guide.md#hosted-font-files) for download URLs and installation instructions for both CSS and matplotlib.

| Element | Weight | Notes |
|---|---|---|
| Graph title | Figtree SemiBold | Centered above the chart area |
| Axis subtitles / labels | Figtree Medium | Applied to axis name labels (e.g., "AUROC", "Protein bound (%)") |
| Tick labels, legend labels, data labels | Figtree Regular | All informational/annotation text |

Text that pertains to axes, legends, and annotations (i.e., anything *about* the graph, not *within* the data) uses the system's **secondary foreground color (`--fg-secondary`)** token.

---

## Axes & Gridlines

### Color Tokens
- **Axis spines / axis lines:** Use `border-strong` color token
- **Gridlines:** Use a lighter grey token
- **Never use black** for any axis, spine, or grid element

### Axis Lines & Spines
- Show axis lines only where they serve a spatial reference function
- For charts with a **categorical axis**, hide the axis line and tick marks entirely on that axis
- Grid lines run lightly across the chart; do not use heavy or dark grid strokes
- **Axis lines extend 8px beyond the last tick/gridline** on the open end(s). For a horizontal x-axis, extend past the last tick to the right. For a vertical y-axis, extend past the top tick upward.

### Tick Marks
- **Only render tick marks when they carry information that no other element already provides.** If gridlines already mark the position of a value, tick marks are redundant and should be omitted.
- Do not render tick marks on categorical axes
- When tick marks are present on error bars or annotations, they must always be paired with an explicit label (e.g., `90.0 ± 2`).

### Quantitative Axis Values
- Axis values must fall on **clean, evenly spaced increments**. Snap the axis maximum and step size to a "nice" round number.
- Derive the target tick count from the axis length (approximately one tick per 55px), then round to the nearest clean step.
- Do not render the gridline or tick label at the zero baseline when it would visually duplicate the axis origin.

### Categorical Axes
For any chart where one axis is categorical:
- **Remove** the axis spine
- **Remove** all tick marks
- Labels float freely beside their corresponding data
- The label connects to its data purely through spatial alignment

---

## Legends

### Placement
- Always place the legend **below the chart**, beneath the bottom axis or chart boundary
- Never place the legend inside the chart area where it could overlap data
- Exception: For donut/pie charts, the legend may be placed to the right of the chart if vertical space is constrained

### Legend Item Format
Each legend entry consists of:
1. A **color swatch** — square (approximately 14×14px), slightly rounded corners (4px radius), filled with the **100** stop of the series color, stroked with the **300** stop at 1px weight.
2. A **text label** to the right of the swatch — Figtree Regular, secondary foreground color (`--fg-secondary`)

Arrange legend items horizontally in a single row when space allows. Wrap to a second row only if necessary.

### Centering
The legend group must be centered over the **plot area** (not the full SVG container). Compute the total width of all items — accounting for actual label character lengths, not a fixed per-item spacing — then offset from the plot area center. Use approximately 6.5px per character at font size 11, plus 14px for the swatch, 6px swatch-to-label gap, and 16px between items.

---

## Spacing & Layout

- **Chart title:** Centered over the **plot area** (the region where data is drawn), not the full container width. Account for any left offset from axis labels or category label columns.
- **Axis titles:** Placed along the axis they describe; the y-axis title should be rotated 90° and vertically centered along the axis. The y-axis title must have clear separation from the tick value labels — set `MARGIN.left` wide enough so the title and tick values never touch. A minimum of **~13px of clear space** between the right edge of the rotated title and the left edge of the nearest tick label is required. As a rule of thumb: `MARGIN.left ≥ title_font_size + 8 + max_tick_label_width + 8`.
- **Chart margins:** Maintain generous padding on all sides — the chart should never feel cramped.
- **Legend:** Sits below the chart with clear separation from the bottom axis. Maintain visible breathing room between the axis subtitle and the legend.

---

## What to Never Do

- **Never use black** for axes, spines, gridlines, or labels. Use `border-strong` and grey tokens.
- **Never place the legend inside the chart** where it may overlap data.
- **Never use stops 50–400 as text colors.** These are background-only tones.
- **Never render a categorical axis spine or tick marks.** Labels float freely.
- **Never round the corners of a bar where it meets a visible axis.** Round only the free ends.
- **Never use a font other than Figtree** for any text element in the chart.
- **Never skip the stroke on filled shapes.** All bars, arcs, and segments must have a 300-stop stroke at 1px.
- **Never use a stroke weight other than 1px** for any shape stroke or annotation line.
- **Never drift outside the color family** when applying scales.
- **Never render a chart element that carries no information.**
- **Never let bars touch the quantitative axis boundary.** Always include padding.
- **Never use uneven or non-round axis increments.** Snap to clean values.

---

## Quick Checklist

Before finalizing any graph, confirm:

- [ ] All colors are drawn from the design system's named color scales
- [ ] Filled shapes use 100 fill and 300 stroke at 1px weight
- [ ] Line graph lines use 300 stop; inflection markers use 100 fill and 300 stroke
- [ ] Categorical axes have no spine and no tick marks
- [ ] Bars never touch the quantitative axis boundary — padding is maintained
- [ ] Legend is placed below the chart (or right of donut), not inside the chart area
- [ ] Legend swatches are square (14×14px), 100 fill, 300 stroke, 4px rounded corners
- [ ] Legend is centered over the plot area using dynamic per-item widths
- [ ] Graph title uses Figtree SemiBold, centered over the plot area
- [ ] Axis subtitle uses Figtree Medium, slightly larger than tick label text
- [ ] Tick labels, legend labels, and annotation text use Figtree Regular in secondary foreground color (`--fg-secondary`)
- [ ] No black used on any axis, spine, grid, or label element
- [ ] Axis lines extend 8px beyond the last tick on their open end(s)
- [ ] Axis values use clean, evenly spaced increments; zero gridline is not duplicated
- [ ] Tick marks are only rendered when they add information not already shown by another element
- [ ] Error bars (if present) use the 300 stop and always include a `value ± error` text label

---

## Chart Index

Each chart type has its own file with specific rules, layout details, and space for notes.

| Chart Type | File |
|---|---|
| Bar Chart (grouped & horizontal) | [bar-chart.md](./charts/bar-chart.md) |
| Line Graph | [line-graph.md](./charts/line-graph.md) |
| Donut / Pie Chart | [donut-chart.md](./charts/donut-chart.md) |
| Kaplan-Meier Survival Curve | [kaplan-meier.md](./charts/kaplan-meier.md) |
| Forest Plot | [forest-plot.md](./charts/forest-plot.md) |
| Waterfall Chart | [waterfall-chart.md](./charts/waterfall-chart.md) |
| Data Table | [table.md](./charts/table.md) |

When generating a chart type not listed above, extrapolate from the global rules in this document. The core principles — soft fills, 300 strokes, no categorical axis spines, Figtree typography, bottom legends — apply universally.

---

## Matplotlib Style Sheet & Color Utilities

When generating plots with Matplotlib (or libraries built on it like seaborn), use the provided style sheet and color module to ensure output matches this design system automatically.

### Files

| File | Purpose |
|---|---|
| [`fractal.mplstyle`](./fractal.mplstyle) | Matplotlib style sheet — sets fonts, color cycle, axes, grid, legend, and figure defaults via rcParams |
| [`fractal_colors.py`](./fractal_colors.py) | Python module — all color palettes as dicts, fill/stroke helpers, sequential/diverging/qualitative colormaps |

### How to Apply

```python
# Option 1 — one-call setup (loads style + registers colormaps)
from fractal_colors import apply_fractal_style
apply_fractal_style()

# Option 2 — style sheet only
import matplotlib.pyplot as plt
plt.style.use('path/to/fractal.mplstyle')
```

### When to Use What

- **Simple line/scatter/bar plots**: The `.mplstyle` alone is sufficient — the color cycle, fonts, spines, grid, and legend placement are already configured.
- **Grouped bar charts**: Use `styled_bars()` from `fractal_colors.py` to get the correct 100-stop fill + 300-stop stroke pairs per series.
- **Heatmaps / continuous color**: Use the registered sequential colormaps (`fractal_blue`, `fractal_pink`, etc.) or `fractal_div` for diverging data.
- **Categorical color by name**: Access palettes directly — e.g., `PINK[500]` for the anchor tone, `BLUE[100]` for a fill, `BLUE[300]` for a stroke.

### What the Style Sheet Does NOT Cover

The `.mplstyle` sets global defaults but cannot enforce every rule in this guide. When generating plots, still manually ensure:

- Categorical axes have **no spine and no tick marks** (set `ax.spines['bottom'].set_visible(False)` and `ax.tick_params(bottom=False)` as needed)
- Axis lines **extend 8px beyond the last tick** on open ends (use `ax.spines['left'].set_bounds()` or clip adjustments)
- Legend is **centered over the plot area**, not the full figure
- Filled shapes use the **100-stop fill / 300-stop stroke at 1px** convention (use `fill_stroke_pair()` or `styled_bars()`)
- Line graphs use **300-stop colors** for line strokes, not the default 500-stop cycle — override with `color=BLUE[300]` or set `axes.prop_cycle` to `CYCLE_300` from `fractal_colors.py`
