# Table Pattern & Trend Sparkline — Developer Spec

> Component spec for the updated table pattern, the new `ChartSpark` trend sparkline component, and their composition inside the `UniversalWidgetShell`. Derived from Figma file `wCdsKYnOx6hCoSOfvMT0Cu`, node `3855:13746`.

---

## 1. Trend Sparkline (`ChartSpark`)

An inline area-chart sparkline showing directional trend within a table cell.

### Variants

| Variant | Prop | Area fill | Stroke | Dot | Baseline position |
|---------|------|-----------|--------|-----|-------------------|
| Positive | `property1="positive"` | Gradient — `--content-success` to transparent | `--content-success` (`#007844`) | Top-right | Bottom (y ≈ 29px) |
| Negative | `property1="negative"` | Gradient — `--content-danger` to transparent | `--content-danger` (`#c10006`) | Bottom-right | Top (y ≈ 9px) |

### Dimensions

| Property | Value |
|----------|-------|
| **Component size** | `85 × 40px` |
| **Cell container** | `97 × 48px` (`overflow: clip`) — the sparkline sits at `(0, 0)` inside this |
| **Endpoint dot** | `4 × 4px` circle, same colour as stroke |
| **Baseline** | Dashed horizontal line, full 85px width, `1px` stroke, `rgba(0,0,0,0.10)` (neutral hairline) |

### Rendering notes

- The area fill is an SVG `<path>` with a vertical gradient from stroke colour (top) to transparent (bottom for positive, top for negative).
- The line is rendered as a separate SVG path sitting on top of the area fill — same stroke colour, no fill.
- The endpoint dot marks the **last data point** (right edge). For positive trends it sits near the top; for negative trends near the bottom.
- The baseline is a dashed reference line (period start value) — positioned opposite to the trend direction.
- The sparkline should be generated from an array of numeric values. Minimum 2 points, recommended 7–12 for the 85px width.
- Ensure `overflow: clip` on the container — the area path intentionally bleeds 1px beyond bounds.

### Accessibility

- Add `role="img"` and an `aria-label` describing the trend, e.g. `aria-label="Trend: up 12.1%"`.
- The sparkline is decorative when paired with the Delta text column — in that case `aria-hidden="true"` is acceptable.

---

## 2. Updated Table Pattern

### Architecture

```
Table
├── Header Row (Columns)
│   └── Header Cell × n
├── Body Row × n
│   ├── Text Cell (left-aligned — labels)
│   ├── Sparkline Cell (left-aligned — ChartSpark)
│   ├── Numerical Cell (right-aligned — values)
│   ├── Delta Cell (right-aligned — change indicator)
│   └── Numerical Cell (right-aligned — counts)
```

### Header Row

| Property | Token / Value |
|----------|---------------|
| Background | `var(--surface-raised)` / `#FFFFFF` |
| Border | **None** (`border-width: 0`) |
| Cell padding | `12px` all sides |
| Font | Klarheit Grotesk **Regular** (400) |
| Font size | `12px` |
| Line height | `16px` |
| Letter spacing | `-0.12px` |
| Text colour | `var(--content-subtle)` / `#61605E` |
| Alignment | Left for text columns, Right for numerical columns |

### Body Rows

| Property | Token / Value |
|----------|---------------|
| Background | `var(--surface-raised)` / `#FFFFFF` — **uniform, no striping** |
| Row border | `1px solid var(--border-tertiary)` / `#F2EFEB` — top and bottom |
| Cell padding (standard) | `12px` all sides |
| Cell padding (sparkline) | `12px` horizontal, `4px` vertical |
| Font | Klarheit Grotesk Regular (400) |
| Font size | `14px` |
| Line height | `1.5` (21px) |
| Letter spacing | `-0.14px` |
| Text colour | `var(--neutral-950)` / `#070707` |
| Font features | `'lnum' 1, 'tnum' 1` (lining numerals, tabular figures) |

### Cell Types

| Type | Alignment | Content | Notes |
|------|-----------|---------|-------|
| **Label** | Left, `align-items: flex-end` | Plain text | Product names, categories |
| **Sparkline** | Left, `align-items: flex-start` | `ChartSpark` component | Reduced vertical padding (4px) to accommodate 48px chart container |
| **Numerical** | Right, `text-align: right` | Formatted number/currency | `font-feature-settings: 'lnum' 1, 'tnum' 1` |
| **Delta** | Right, inner content centred | Icon + text | See Delta section below |

### Delta Cell

The delta indicator shows percentage change and absolute value.

| Property | Value |
|----------|-------|
| Layout | `display: flex`, `align-items: center`, `gap: 4px` |
| Icon | `16 × 16px`, Heroicons Mini |
| Icon (positive) | `arrow-up-circle`, colour `var(--content-success)` / `#007844` |
| Icon (negative) | `arrow-down-circle`, colour `var(--content-danger)` / `#C10006` |
| Text (positive) | `var(--content-success)` / `#007844` |
| Text (negative) | `var(--content-danger)` / `#C10006` |
| Text format | `{percentage}% (+/-£{absolute})` e.g. `12.1% (+£2,034)` |
| Font size | `14px` |
| Line height | `16px` (tighter than standard body) |
| Letter spacing | `-0.14px` |
| White space | `nowrap` |

### Column Widths (reference — within widget shell)

These are the Figma reference widths inside a ~602px widget. In production, use proportional/flex sizing:

| Column | Figma width | Flex behaviour |
|--------|-------------|----------------|
| Product | 118px | `flex: 0 0 auto`, min-width for label |
| Trend | 114px | `flex: 0 0 114px` — fixed to accommodate sparkline |
| Sales value | 122px | `flex: 1` — fills available space |
| Change | ~189px | `flex: 1` — fills available space |
| Fees | 104px | `flex: 0 0 auto` |

> When the table is rendered standalone (outside the widget shell), columns should use `flex: 1` with appropriate min-widths.

### Border Collapse

Cells use `margin-right: -1px` to collapse shared vertical borders. The last cell in each row omits this negative margin.

---

## 3. Changes from Current Table Pattern

These are the breaking changes from the existing table component. Call these out to the dev team as migration work.

| Property | Current (old) | New | Impact |
|----------|--------------|-----|--------|
| **Header font weight** | `700` (Bold) | `400` (Regular) | Quieter header — hierarchy shifts to content |
| **Header font size** | `14px` | `12px` | Smaller, more compact header labels |
| **Header text colour** | `#070707` (Eclipse) | `#61605E` (content-subtle) | Muted — header recedes behind data |
| **Header border** | `1px solid rgba(8,8,8,0.10)` | `none` | Borderless header row |
| **Header background** | `#FFFCF7` (canvas) | `#FFFFFF` (surface-raised) | Matches card surface instead of page canvas |
| **Row striping** | Alternating `#FAF7F2` / `#FFFCF7` | Uniform `#FFFFFF` | No zebra striping — cleaner in widget context |
| **Row border colour** | `#E6E3DF` | `#F2EFEB` (border-tertiary) | Lighter separator — more subtle |
| **Column sizing** | Equal `flex: 1` | Mixed fixed + flex | Columns can have explicit widths |
| **Tabular figures** | Not specified | `font-feature-settings: 'lnum' 1, 'tnum' 1` | All numerical cells — ensures columns align |
| **New cell: Sparkline** | n/a | `ChartSpark` embedded in cell | New cell type with reduced vertical padding |
| **New cell: Delta** | n/a | Icon + coloured percentage + absolute | Replaces plain numerical for change data |

### Backward compatibility

The old table pattern (with bold headers, striped rows, canvas-coloured header bg) remains in use on full-page report views. The new pattern is specifically designed for the constrained widget context. Both should coexist — consider a variant prop or CSS modifier class (e.g. `.table--widget`) rather than replacing the base table.

---

## 4. Figma References

| Component | Node ID | Description |
|-----------|---------|-------------|
| ChartSpark | `3855:13748` (positive), `3855:13752` (negative) | Sparkline component variants |
| New table | `3855:13756` | Table with trend sparkline columns |
| Widget Shell + table | `3855:13915` | Full composition in widget shell |
| Current/old table | `3855:14100` | Existing table pattern for comparison |

[Open in Figma](https://www.figma.com/design/wCdsKYnOx6hCoSOfvMT0Cu/Data---Insights---Visualisations?node-id=3855-13746)
