aiRA Data Visualisation

aiRA data visualisation companion v0.2

The charts, tables and dashboards aiRA draws, specified the way the aiRA Design System specifies everything else. This release covers the core chart set, the specialist charts, the analytics settings and table patterns Power BI users expect, and the three parts every chart shares.

Every colour, type style, space and radius on this page is a design-system token, read from the live site. Where a chart needs something the system does not have, it is marked Proposed and collected under Proposed tokens. The written rules behind these specs, including integrity, freshness, layouts and platforms, live in the foundations draft.

Toggle Light and Dark in the top bar: every chart re-resolves its tokens. Hover or tap any mark for its tooltip, or tab to a chart and use the arrow keys.

How to use this page

Each block follows the design system's component shape: a live example, when to use it, anatomy with tokens, settings, states, accessibility, copy, do and don't, and an engineering handoff.

If you areRead
The aiRA agent choosing a chartThe chooser in the foundations draft, then the block here. Use when and Don't use when are decision rules, not suggestions.
A designer speccing a screenThe example and anatomy. Every part names its token.
An engineerSwitch to Developer view for settings and the handoff snippet. Names marked Proposed are not in the library yet.

Rules every block on this page follows

  • The title is the chart's name. What it shows ("Daily redemptions"), never the finding. The finding goes in aiRA's answer next to the chart, and in annotations and highlights on it.
  • Trust line on every chart: the table aiRA queried and the time the query ran, in the org timezone. Nothing else.
  • Direct labels first. A key only for colour scales, size, collisions and small multiples.
  • Never red on chart marks. Down is --aira-delta-negative orange; red stays for errors.
  • View as table on every chart. Card actions appear on hover and keyboard focus, and in a menu on touch; no value lives only in a tooltip.
  • One colour means indigo. A single series uses --aira-viz-1; channel colours only appear when channels are compared.
  • Hover dims the rest. The active mark keeps its colour and the others fade. No outlines.
  • Org locale for every number: this page's demo org is Indian, so lakh, crore and ₹.

Chart card

ProposedUpdated Design system owner · Punya Shetty

The frame every chart sits in: title, subtitle, optional key, plot, trust line and actions. It is a composition of AiraCard, so no chart ever needs its own header.

Example

Redemptions by store
Redemptions · count · 1–23 Sep 2026 · all tiers

Column chart. HSR Layout led with 18,420 redemptions, then Indiranagar 15,230, Koramangala 11,910, Whitefield 9,640, Jayanagar 7,120 and Malleshwaram 5,310. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Hover the card for its actions. Resize the window: below 480px the columns become horizontal bars, as the chooser requires for narrow space. One highlight (HSR Layout) in --aira-viz-1, the rest in context grey. Every bar is labelled, so there is no value axis.

Use when

Use when

Any chart, on any surface: chat answer, artifact panel, dashboard tile.

Don't use when

A KPI tile. AiraMetricTile has its own anatomy.

Anatomy

PartTypeColourRule
Title--aira-t-label-md--aira-text-primaryThe chart's name: what it shows, as a short noun phrase. Never the finding. One line
Subtitle--aira-t-caption--aira-text-tertiaryMeasure · unit · period · filters
Actions.iconbtn sm; CSV is a small tertiary button--aira-text-tertiaryView as table, copy, CSV, full screen, in that order. They appear on hover and keyboard focus, and stay while the table view is open; on touch they sit in a menu. Borderless, scoped to this chart
KeySee Chart keyOnly where the key rules allow
PlotMarks, axes, annotations
Trust line--aira-t-caption--aira-text-tertiarySource: the table aiRA queried (write_db_100783.points_aira) · As of time in org timezone. Nothing else. Never optional
Surface--aira-surface-artifact-card-bg--aira-radius-md, 1px --aira-border-default, padding --aira-sp-4, parts spaced --aira-sp-3

Behaviour

ActionWhat happens
Hover or focus a markThe other marks fade to --aira-proposed-viz-dim. The tooltip opens beside the mark and never leaves the card: above it, then below, then beside, pushed back inside the card's edges.
View as tableThe plot swaps for its table in place; the button becomes View as chart.
CopyCopies the data as tab-separated text. The copy icon becomes a tick in --aira-text-success for 1.5 seconds, then reverts. No text note on the page. If copying is blocked, a toast says to select the table instead.
CSVDownloads this chart's data as a CSV file. The button always names the format.
Full screenThe same chart, wider and taller, in a window over the page: title, subtitle, key, plot and trust line. Tooltips work inside it. It keeps the card's actions (view as table, copy, CSV) in its header next to close; View as table swaps the chart for its table, never both at once. Escape or the close button returns focus to the card.
On touchThe actions sit in a "…" menu on the card: view as table, copy, Download CSV, Open full screen. Tapping the card also opens full screen, whose own "…" menu has the same actions except full screen.

States

  • Loading: AiraSkeleton in the plot's shape with "Drawing this chart…"; title and subtitle already shown.
  • No data: the plot area holds a message and a next step, never a blank chart.
  • Error: "Couldn't draw this chart", the reason in plain language ("The query took too long to finish."), and Try again. No codes.
  • Old snapshot: the card offers Re-run when the result is over 24 hours old.

Accessibility

  • The plot is one tab stop with role="img", named by the title and described by a text summary.
  • Arrow keys move between values; each is announced ("3 of 6. Koramangala, 11,910 redemptions").
  • View as table is a real button with aria-pressed.

Do / don't

Do

Name the chart for what it shows: "Redemptions by store".

Don't

Write the finding as the title: "HSR Layout led September redemptions". The finding goes in aiRA's answer.

Do

Say the period is partial when it is.

Don't

Drop the trust line to save space.

Do

Confirm a copy with a tick on the button.

Don't

Write "Copied as a table" under the chart.

Engineering handoff

AiraChartCardBuilt with AiraCard · proposed API, to confirm against @aira/react/charts
<AiraChartCard
  title="Redemptions by store"
  subtitle="Redemptions · count · 1–23 Sep 2026 · all tiers"
  source="write_db_100783.redemptions_aira"   // the table aiRA queried
  asOf={queryRanAt}            // formatted in org timezone
  table={rows}                 // powers "View as table" and copy
>
  <AiraBarChart data={rows} xKey="store" yKey="redemptions" highlight="HSR Layout" />
</AiraChartCard>

Chart key

ProposedUpdated Design system owner · Punya Shetty

A key makes the reader look away, remember a colour and look back. So the first choice is always a direct label; the key is the fallback, and it has three forms.

Direct labels (the default)

Active members by tier
Active members · count · Mar–Aug 2026 · by tier

Line chart of active members by tier. Silver rose from 11,200 to 12,400, Gold from 6,800 to 8,000, Platinum from 2,100 to 2,600. Use the arrow keys to move between values.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

Names sit at the line ends in text colour; the dot carries the series colour. Tiers take locked sequential steps by rank (Silver seq-4, Gold seq-5, Platinum seq-6).

Inline key (paired and stacked charts)

EmailPushLine series

A row between subtitle and plot. 8px square swatch for areas, 12×2px line for lines. Label text is neutral, never the series colour.

Scale key (colour scales, always shown)

Under 20%40%60% or more

Heatmaps, cohort grids and filled maps. Low and high ends labelled, and the middle on diverging scales.

When a key appears

CaseKey
One series; bars and columns; donut, waffle, treemap; KPI tiles; sparklinesNone
Several lines; stacked bars; paired bars and dumbbellsDirect labels, or an inline key in the subtitle
Heatmaps, cohort grids, filled mapsScale key, always
Bubble sizeSize key, always
Different kinds of marks together: bars with a line, a shaded band behind a line, coloured bars beside grey onesName every mark type on the chart: a direct label where it fits ("Expected range" on the band), otherwise an inline key
Direct labels would collideInline key
Small multiplesOne shared key for the set

Rules

  • Top-left, between subtitle and plot, in a row. Never at the right: aiRA's surfaces are narrow.
  • In data order (stack order, or lines by last value). Never alphabetical.
  • Clicking an item may highlight that series; it works from the keyboard and never replaces View as table.

Engineering handoff

AiraChartKeyBuilt with AiraChartCard · proposed API, to confirm against @aira/react/charts
<AiraChartKey items={[
  { label: "Email", color: "var(--aira-cat-email)" },
  { label: "Push",  color: "var(--aira-cat-push)" },
]} />
<AiraChartKey scale={{ steps: seqTokens(6), low: "Under 20%", high: "60% or more" }} />

Chart tooltip

ProposedUpdated Design system owner · Punya Shetty

Adds detail on hover, tap or keyboard focus. It never holds the only copy of a value: anything essential is already on the chart or in View as table.

Example

Aug 2026
Silver12,400
Gold8,000
Platinum2,600
vs Jul: +3.4% total

Every chart on this page uses the live version. Hover, tap, or tab to a chart and press the arrow keys.

Anatomy

PartTypeColourRule
Header--aira-t-label-sm--aira-text-primaryThe category or date, with year
Row--aira-t-caption + value in --aira-t-label-smName --aira-text-secondary, value --aira-text-primarySwatch, name, value. Full precision, not compact
Note--aira-t-caption--aira-text-tertiaryOptional: comparison, range level, group size
Surface--aira-bg-surface, --aira-border-default--aira-radius-sm, --aira-shadow-md, padding --aira-sp-2 / --aira-sp-3

Behaviour

InputBehaviour
HighlightThe active mark stays at full colour; every other mark in the chart fades to 35% (Proposed --aira-proposed-viz-dim). No outline
MouseShows on hover with --aira-motion-fast, no delay. Beside the mark, flipping at the edges, never covering it
TouchTap a mark to show; tap elsewhere to dismiss. Placed above the finger
KeyboardArrow keys move between marks; the tooltip follows and the value is announced. Escape hides
LinesOne tooltip per period listing every series, with a 1px --aira-viz-baseline crosshair
Limit6 rows; beyond that the top 5 and "and 4 more"

Engineering handoff

AiraChartTooltipBuilt with AiraTooltip · proposed API, to confirm against @aira/react/charts
// Built on the AiraTooltip surface; chart blocks render it for you.
<AiraBarChart tooltip={{ rows: ["redemptions"], note: "1–23 Sep 2026" }} … />

Bar chart

Stable blockProposed settingsUpdated Design system owner · Punya Shetty

The workhorse. Length on a shared baseline is the most accurately read encoding there is, so when the question is "how do these compare", the answer is a bar chart unless something rules it out. AiraBarChart ships today; the variants below are its settings.

Column and horizontal

Redemptions by store
Redemptions · count · 1–23 Sep 2026 · all tiers

Column chart. HSR Layout led with 18,420 redemptions, then Indiranagar 15,230, Koramangala 11,910, Whitefield 9,640, Jayanagar 7,120 and Malleshwaram 5,310. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Columns for up to 12 short categories or for periods; horizontal bars for long labels or more than 12 categories. This example switches by itself below 480px.

Paired

Redemptions by store, Q2 and Q1
Redemptions · count · Q2 vs Q1 2026 · top 4 stores
Q2 2026Q1 2026

Paired bar chart. HSR Layout 52,400 in Q2 against 46,100 in Q1; Indiranagar 43,000 against 39,800; Koramangala 33,900 against 31,200; Whitefield 27,300 against 26,900. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

The current period is indigo; the comparison period is context grey, so the eye reads "now" first. Two groups only; more becomes a dot plot or small multiples.

100% stacked

Redemption share by channel and tier
Share of redemptions · % · August 2026 · by tier and channel
WhatsAppPushEmailSMS

100% stacked bar. WhatsApp share of redemptions is 38% for Platinum, 41% Gold, 44% Silver and 47% Base, the largest in every tier. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Four channels compared, so channel colours apply here. A single-series bar is always indigo.

Variance

Sales against plan by store
Sales vs plan · % · August 2026 · by store

Variance bar chart of sales against plan: HSR Layout +8.2%, Jayanagar +4.6%, Indiranagar +2.1%, Koramangala −4.6%, Whitefield −11.8%. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

Comparing values across categories, or totals across a few periods. The default answer to "which is biggest".

Don't use when

A trend over many periods: use a line. Two or three parts of a whole with a headline total: a donut.

Variants

VariantWhenStatus
ColumnUp to 12 short categories, or up to 12 periodsStable
HorizontalLong labels or more than 12 categoriesStable
PairedTwo values per categoryProposed setting
StackedTotals and their make-up, 3 to 5 segmentsProposed setting
100% stackedShares compared across groupsProposed setting
VarianceDifference from plan or last yearProposed setting
Lollipop15 or more items, where solid bars crowdProposed setting
With a same-unit lineActual bars with a target or average lineProposed setting

Anatomy

PartTokenRule
Bar--aira-viz-1; context grey for the rest when one bar is the story70% of its slot, 40px at most; square at zero, 2px radius at the value end
Baseline1px --aira-viz-baselineAlways at zero
Value labels--aira-t-caption, --aira-text-secondary; highlighted bar in --aira-t-label-sm primaryWith 12 bars or fewer label every bar and drop the value axis
Category labels--aira-t-caption, secondaryNever rotated: switch to horizontal instead; truncate past 24 characters
Gridlines1px --aira-viz-gridOnly when there is a value axis
Segments1px surface gapStacked variants

Settings

PropValuesRule
orientationvertical · horizontal · autoAuto switches by label length, count and width
highlightcategoryThat bar in indigo, the rest context grey
compare{ key, label }Paired: the comparison period in context grey
stacknone · stacked · percentPercent labels each segment over 36px wide; text token by contrast
variance{ baseline: "plan" | "last year" }Bars grow from a 1px --aira-text-secondary zero line; delta colours with signs; sorted high to low
betterhigher · lowerColour follows good or bad

Do / don't

Do

Sort by value unless the categories have their own order (time, tier).

Don't

Start the value axis above zero. A bar's length is its value.

Do

Keep one colour for one series: indigo.

Don't

Put a number on every bar and keep the value axis as well.

Engineering handoff

AiraBarChartBuilt with recharts · proposed API, to confirm against @aira/react/charts
<AiraBarChart data={stores} xKey="store" yKey="redemptions" orientation="auto" highlight="HSR Layout" />
<AiraBarChart data={stores} xKey="store" yKey="q2" compare={{ key: "q1", label: "Q1 2026" }} />
<AiraBarChart data={tiers} xKey="tier" series={channels} stack="percent" orientation="horizontal" />
<AiraBarChart data={stores} xKey="store" yKey="vsPlan" variance={{ baseline: "plan" }} better="higher" />

Line chart

Stable blockProposed settingsUpdated Design system owner · Punya Shetty

The default for anything over time. AiraLineChart ships today; forecast, anomaly markers and moving average are its new settings.

Lines with direct labels

Active members by tier
Active members · count · Mar–Aug 2026 · by tier

Line chart of active members by tier. Silver rose from 11,200 to 12,400, Gold from 6,800 to 8,000, Platinum from 2,100 to 2,600. Use the arrow keys to move between values.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

Forecast

Monthly redemptions with forecast
Redemptions · count · Sep 2025–Aug 2026 actual, Sep–Nov 2026 forecast · based on 12 months with a festive-season pattern

Line chart with forecast. Monthly redemptions rose from 31,000 in September 2025 to 42,000 in August 2026. The forecast is 43,000 in September, 47,000 in October and 52,000 in November, likely between 45,000 and 59,000. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

More settings, each with its own example: anomaly markers, moving average and reference lines.

Use when

Use when

A measure over time, especially more than 12 periods, or several series over the same periods.

Don't use when

Categories with no order: a line between them implies a connection. Use a bar.

Anatomy

PartTokenRule
Line--aira-viz-1, 2px, round joinsFour lines at most; then highlight and grey or small multiples
Highlighted line2.5pxThe one the story is about
Context linesContext grey, 1.5px
Points8px with 1px surface ringOnly with 12 points or fewer, on the highlighted line, or the last point
Direct labels--aira-t-label-sm, secondary, at line endsThe dot carries the colour; the text stays neutral
Crosshair1px --aira-viz-baselineSnaps to the nearest period; one tooltip lists every series
Partial periodProposed patternThe last, incomplete period is marked, never shown as a drop

Settings

PropValuesRule
forecast{ points, low, high }Dashed 2px line in the series colour, joined to the last actual point; band in --aira-seq-2
todayboolean1px --aira-viz-baseline vertical line labelled "Today"
anomaliesmax 3See Anomaly markers
movingAverage{ window }See Moving average
referenceLines[{ kind, value }]See Reference lines
baselinezero · autoAuto may start above zero; the axis shows where it starts

Rules

  • At least two full cycles of history before forecasting; horizon no longer than a third of the history shown.
  • aiRA's answer states the forecast range, not only the point.
  • Size the chart so the main slope sits near 45°.

Engineering handoff

AiraLineChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraLineChart data={months} xKey="month" series={tiers} directLabels />
<AiraLineChart data={months} xKey="month" yKey="redemptions" forecast={{ points: next3, low: "p10", high: "p90" }} today />

Bullet chart

ProposedUpdated Design system owner · Punya Shetty

Actual against a target, in a single row per item. It replaces the gauge: same answer, a fraction of the space, and rows compare against each other.

Example

Redemption rate against target
Redemption rate · % of active members · August 2026 · by tier

Bullet chart. Platinum 46% against a 45% target; Gold 38% against 40%; Silver 29% against 35%; Base 18% against 20%. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

A value has a target, a plan or a benchmark, for one item or several.

Don't use when

There is no target: use a bar. Or the question is about change over time: use a line with a target line.

Anatomy

PartTokenRule
Bar--aira-viz-112px tall, square at zero, 2px radius at the end
Target mark--aira-text-primary2px wide, 20px tall, centred on the bar
Value--aira-t-label-sm, --aira-text-primaryRight of the plot
Against target--aira-delta-* with sign"+1 pt" or "−6 pts": points, since both are rates
Axis--aira-viz-grid, --aira-viz-baseline at zero3 or 4 ticks
Bands (optional)ProposedPoor, fair, good ranges behind the bar; to be designed

Settings

PropValues
datarows
valueKey, targetKeyfield names
betterhigher · lower, so colour follows good or bad
unitpercent · number · currency

Copy

  • Title: the chart's name, "Redemption rate against target". aiRA's answer carries the finding: "Only Platinum is above its target."
  • Rates compared with rate targets are points, not percent.

Do / don't

Do

Sort by tier order when tiers have one, otherwise by distance from target.

Don't

Draw a gauge because someone asked for one. Say "Showing this as a bullet chart; gauges take more space for the same number."

Engineering handoff

AiraBulletChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraBulletChart data={tiers} labelKey="tier" valueKey="rate" targetKey="target" unit="percent" better="higher" />

Waterfall chart

ProposedUpdated Design system owner · Punya Shetty

Explains what moved a total from one value to another. The standard answer to "why did revenue change?"

Example

Revenue change, July to August
Revenue · ₹ lakh · August vs July 2026 · all stores

Waterfall chart. July revenue 42.0 lakh. New members added 2.4 lakh, returning members 1.9 lakh, lapsed members took away 0.8 lakh and discounts 0.4 lakh, ending at 45.1 lakh in August. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

The value axis starts at ₹36L because a waterfall reads the steps, not the totals from zero. The start and end bars say so with their labels; this is the one bar-like chart where a non-zero start is honest.

Use when

Use when

A total changed between two points and the parts of the change are known: revenue bridge, points liability, member count.

Don't use when

The parts overlap or do not add up to the change. Then show a sorted bar of drivers and say they overlap.

Anatomy

PartTokenRule
Start and end bars--aira-viz-1From the axis floor, labelled with their totals
Up step--aira-delta-positiveFloating, label with + sign
Down step--aira-delta-negativeFloating, label with − sign. Never red
Connector1px dashed --aira-viz-baselineFrom each bar's end to the next bar's start
OrderIncreases first, largest first, then decreases, largest first

States

  • More than 7 steps: group the smallest into "Other changes".
  • A step of zero stays, labelled "0", so the list is complete.

Do / don't

Do

Check that start plus steps equals end, and say so in aiRA's answer when rounding means it does not.

Don't

Colour increases green when an increase is bad, such as points liability. Colour follows good or bad.

Engineering handoff

AiraWaterfallChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraWaterfallChart start={{ label: "July", value: 42.0e5 }} end={{ label: "August", value: 45.1e5 }} steps={drivers} better="higher" unit="currency" />

Funnel chart

ProposedUpdated Design system owner · Punya Shetty

Campaign stages from sent to redeemed, as aligned bars on one baseline with the conversion between steps. Never trapezoids: the shape hides the real drop-offs.

Example

Diwali 2× points campaign funnel
Diwali 2× points campaign · WhatsApp · members · 1–20 Sep 2026

Funnel. Sent 1,20,000; delivered 1,14,600, 95.5% of sent; opened 71,050, 62% of delivered; clicked 18,470, 26% of opened; redeemed 6,280, 34% of clicked. Use the arrow keys to move between values.

Source: write_db_100783.campaigns_aira · As of 23 Sep 2026, 10:42 IST

One series, so one colour: aiRA indigo, --aira-viz-1. The channel is named in the subtitle; channel colours only appear when several channels are compared.

Use when

Use when

Members move through ordered stages and each stage is a subset of the one before.

Don't use when

Stages are not subsets (people can skip one). Use a bar chart of each stage and say so.

Anatomy

PartTokenRule
Stage label--aira-t-caption, --aira-text-secondaryLeft column
Bar--aira-viz-1All bars start on one baseline. One series takes indigo even when it is one channel
Count--aira-t-label-sm, --aira-text-primaryFull value; compact under 420px
Step rate--aira-t-caption, --aira-text-tertiary"62% of delivered", under each bar after the first

Copy

  • Title: the campaign and "funnel". The end-to-end rate or the biggest drop goes in aiRA's answer.
  • Stage names in the customer's words, not event names.

Do / don't

Do

Show the attribution window in the subtitle.

Don't

Compare two campaigns in one funnel. Use small multiples, or a table of step rates.

Engineering handoff

AiraFunnelChartBuilt with recharts · proposed API, to confirm against @aira/react/charts
<AiraFunnelChart stages={stages} labelKey="stage" valueKey="members" />

Heatmap and cohort grid

ProposedUpdated Design system owner · Punya Shetty

Two categories and one measure, shown as a grid of cells coloured by value. The cohort variant is the retention triangle every loyalty team asks for.

Example: cohort grid

Retention by join month
Members still transacting · % of join-month cohort · Apr–Aug 2026 cohorts
Under 20%60% or more

Cohort grid. Month-one retention was 52% for April joiners, 55% May, 49% June, 58% July and 61% August. April joiners fell to 31% by month five. Use the arrow keys to move between values.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

Cell text switches between --aira-text-primary and --aira-text-inverse by computed contrast on each step, in each theme. Empty cells are months that have not happened yet, not missing data.

Use when

Use when

Rows and columns are both categories (day by hour, store by month, join month by months since).

Don't use when

One of the axes is continuous and the exact values matter: use a line or small multiples.

Variants

VariantRows × columnsPalette
HeatmapAny two categoriesSequential; diverging when there is a meaningful middle
Cohort gridJoin period × periods sinceSequential, triangle shape
Calendar heatmapWeeks × weekdaysSequential
RFM gridRecency × frequencySequential, segment names printed

Anatomy

PartTokenRule
Cell--aira-seq-1 to -61px surface gap between cells. Six bins, stated in the key
Value--aira-t-captionPrinted in every cell that fits; text token chosen by contrast
Scale keySee Chart keyAlways shown
Row and column labels--aira-t-captionRows --aira-text-secondary, columns --aira-text-tertiary

Do / don't

Do

Say what 100% means in the subtitle ("% of join-month cohort").

Don't

Use a rainbow or two unrelated hues for a sequential measure.

Engineering handoff

AiraHeatmapBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraHeatmap variant="cohort" data={cohorts} rowKey="cohort" colKey="month" valueKey="retained" bins={6} unit="percent" />

Scatter chart

ProposedUpdated Design system owner · Punya Shetty

Two measures across many items, to see whether they move together and which items stand apart. The bubble variant adds a third measure as size.

Example

Basket size and days since last purchase
Average basket (₹) vs days since last purchase · sample of 72 Gold members

Scatter chart of 72 Gold members. Basket size falls as days since last purchase rise. 12 members with baskets over 6,000 rupees have not bought in more than 60 days. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Highlight and grey: the members the annotation names are in --aira-viz-1, everyone else in context grey. Dots are 6px with a 1px surface ring so overlaps stay visible.

Use when

Use when

Many items, two measures, and the question is about relationship or outliers.

Don't use when

One measure is time: use a line. Fewer than about 15 items: use a table or dot plot.

Anatomy

PartTokenRule
Dot--aira-viz-1 or context grey6px, 1px --aira-surface-artifact-card-bg ring. Opacity only past 200 points
Hit area16px radius, bigger than the dot
Axes--aira-viz-gridBoth titled, units in the title
Annotation--aira-t-caption, --aira-text-secondaryNames the group that matters, with a 1px leader
Trend line1px --aira-text-tertiaryOnly when asked for or clearly real; never implies cause
Bubble sizeSize keyArea, not radius, scales with value

Copy

  • The highlighted group is named in an annotation on the chart ("12 high spenders lapsing"), not in the title.
  • Always show n. Describe association: "bigger baskets go with more recent purchases", never "recency drives spend".

Engineering handoff

AiraScatterChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraScatterChart data={members} xKey="daysSince" yKey="basket" highlight={(m) => m.daysSince >= 60 && m.basket >= 6000} sampleSize={72} />

Dot plot and dumbbell

ProposedUpdated Design system owner · Punya Shetty

Values as dots on a shared scale. The dumbbell joins two values per row, so the gap is the thing you see.

Example: dumbbell

Open rate by tier: email and push
Open rate · % of delivered · August 2026 · by tier and channel

Dumbbell chart of open rates. Platinum email 42%, push 58%, gap 16 points. Gold 36% and 49%. Silver 31% and 38%. Base 22% and 24%. Use the arrow keys to move between values.

Source: write_db_100783.campaigns_aira · As of 23 Sep 2026, 10:42 IST

Channel colours are locked: email is always --aira-cat-email, push always --aira-cat-push. The key is two direct labels on the first row.

Use when

Use when

Two values per category and the gap matters, especially with more than 8 categories where paired bars crowd.

Don't use when

More than two values per row: use small multiples.

Anatomy

PartTokenRule
DotSeries or channel token10px, 1px surface ring
Connector2px --aira-viz-baselineDumbbell only
Gap label--aira-t-label-sm, --aira-text-primaryRight column, in points for rates
OrderBy gap, or by tier order where tiers have one
AxisMay start above zero: dots have no length

Engineering handoff

AiraDotPlotBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraDotPlot variant="dumbbell" data={tiers} labelKey="tier" series={[{ key: "email", channel: "email" }, { key: "push", channel: "push" }]} unit="percent" />

Donut and pie charts

ProposedUpdated Design system owner · Punya Shetty

Use sparingly. Angle is one of the weakest encodings people read, so a donut or pie is only for two or three parts when the rough proportion is the point. Past three parts it becomes a sorted bar, a 100% stacked bar or a waffle. The donut is preferred: its centre holds the headline total.

Example

Redemptions by reward type
Redemptions by reward type · share · August 2026

Donut chart of 42,100 redemptions: points 54%, voucher 28%, tier upgrade 18%. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

Two or three parts, the rough split is the point, and there is a total worth showing.

Don't use when

Four or more parts, or parts that are close in size: angle is too weak to compare them. Use a sorted bar.

Anatomy

PartTokenRule
Segments--aira-viz-1, -2, -3 in order of size1.5px surface gap; start at 12 o'clock, clockwise, largest first
Labels--aira-t-label-sm, --aira-text-secondaryName and share beside each segment. No key
Centre--aira-t-h2 + --aira-t-captionThe total, compact, with its unit word
HoleInner radius about 62% of outer

Rules carried from the design system

  • Two or three slices only. The agent never draws a fourth; it draws a sorted bar and says why in one line.
  • Never 3D, never an exploded (pulled-out) slice, never a second ring.
  • A single insight per chart.
  • Pie only when there is no total worth showing; otherwise the donut.

Do / don't

Do

Say so in aiRA's answer when shares do not sum to 100% after rounding.

Don't

Use a pie for close values such as 34%, 33% and 33%. Nobody can see the difference; use a bar.

Engineering handoff

AiraDonutChartBuilt with recharts · proposed API, to confirm against @aira/react/charts
<AiraDonutChart data={mix} labelKey="type" valueKey="share" total={{ value: 42100, label: "redemptions" }} />

Test result

ProposedUpdated Design system owner · Punya Shetty

The result of an A/B or campaign test: each variant's difference from control, with its likely range. It says plainly when a result could be chance.

Example

Reminder test results
Redemption rate · difference vs control · points · 1–14 Sep 2026

Test result. Variant B, a 7 pm reminder, lifted redemption by 2.1 points, likely between 1.2 and 3.0: a clear result. Variant C, a 9 am reminder, lifted it by 0.6 points, likely between minus 0.4 and 1.6: could be chance. Use the arrow keys to move between values.

Source: write_db_100783.campaigns_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

Any randomised comparison of a variant with a control.

Don't use when

A before-and-after comparison without a control group. That is a change over time, not a test; use a line and describe association.

States

StateMarkCopy
Clear resultRange fully on one side of zero; --aira-delta-*"Variant B lifted redemption by 2.1 pts (likely 1.2 to 3.0 pts)"
Could be chanceRange crosses zero; context grey"No clear winner yet. The difference could be chance."
Still runningContext grey, progress in the subtitle"Test running, day 5 of 14. Results so far are not reliable."
Too few membersContext grey"1,240 members per group so far; about 4,000 needed to be sure."

Anatomy

PartTokenRule
Zero line1px --aira-text-secondaryStronger than gridlines: it is the "no effect" line
Range2px line with end capsThe 80% likely range; the level is in the tooltip
Point10px dotThe measured lift
Verdict--aira-t-label-smRight column: the lift, or "Could be chance"

Copy

  • Say "likely range", not "confidence interval"; "could be chance", not "not statistically significant". Technical terms go in the tooltip and the SQL tab.
  • Never declare a winner early because a running result looks good.

Engineering handoff

AiraTestResultBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraTestResult metric="redemption rate" unit="points" variants={[{ name: "B · 7 pm reminder", lift: 2.1, low: 1.2, high: 3.0 }, …]} groupSize={12400} status="complete" />

Slope chart

ProposedUpdated Design system owner · Punya Shetty

Change between exactly two points, for many items at once. The angle of each line is the change.

Example

Tier share
Share of active members · % · Q1 vs Q2 2026 · by tier

Slope chart of tier share. Platinum rose from 18% to 22%, Gold from 34% to 38%. Silver fell from 28% to 25% and Base from 20% to 15%. Use the arrow keys to move between values.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

All lines are indigo: direction is already carried by the slope and the signed label. Colour by up or down only when up is always good; here a shrinking Base share is good news too.

Use when

Use when

Exactly two points in time (or two conditions) and several items.

Don't use when

Three or more points: use a line or bump chart. One item: a KPI tile with a delta.

Anatomy

PartTokenRule
Lines--aira-viz-1, 2pxHighlight and grey when one item is the story
End dots8px, 1px surface ring
LabelsLeft --aira-t-caption; right --aira-t-label-sm, --aira-text-primaryName and value at both ends; signed change on the right; labels spaced at least 15px
Axes1px --aira-viz-baselineTwo vertical rules, period labels on top

Do / don't

Do

Leave at least 14px between labels on each side; nudge colliding labels apart.

Don't

Use a slope chart when the two points are months apart and something happened between them. Show the line.

Engineering handoff

AiraSlopeChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraSlopeChart data={tiers} labelKey="tier" from={{ key: "q1", label: "Q1 2026" }} to={{ key: "q2", label: "Q2 2026" }} unit="percent" />

Bump chart

ProposedUpdated Design system owner · Punya Shetty

Rank over time. It replaces Power BI's ribbon chart, whose ribbons hide the rank changes they are meant to show.

Example

Store rank by sales
Rank by sales · 1 = highest · Mar–Aug 2026 · top 5 stores

Bump chart of store rank. HSR Layout ranked first every month. Whitefield rose to third in June and fell to fifth in July and August. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

Rank matters more than value, for up to 10 items over time.

Don't use when

The gaps between items matter: rank hides that first may be far ahead. Use a line.

Anatomy

PartTokenRule
Highlighted line--aira-viz-1, 2.5pxThe item the story is about
Other linesContext grey, 1.5px
DotsEvery periodSo each rank is readable
Labels--aira-t-label-sm at the right endNames in final rank order
Rank axis1 at the topSay "1 = highest" in the subtitle

Engineering handoff

AiraBumpChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraBumpChart data={ranks} xKey="month" seriesKey="store" rankKey="rank" highlight="Whitefield" />

Box plot

ProposedUpdated Design system owner · Punya Shetty

Compares how values spread across groups: the middle half, the median, the range and the outliers.

Example

Basket size by tier
Basket size · ₹ · August 2026 · by tier

Box plot of basket size. Platinum median 5,600 rupees, middle half 4,200 to 7,100. Gold median 4,000. Silver median 2,900. Base median 1,900. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Most readers have not been taught box plots, so the first row labels its median in words, and the tooltip says "middle half" rather than "interquartile range".

Use when

Use when

Comparing spreads across 2 to 8 groups with enough values each (30 or more).

Don't use when

Few values, or readers unfamiliar with the form: use a strip plot, which shows every value.

Anatomy

PartTokenRule
BoxFill --aira-seq-2, 1.5px --aira-viz-1 outlineThe middle half
Median2px --aira-text-primaryLabelled on the first row
Whiskers1.5px --aira-viz-1 with capsRule stated in the tooltip
OutliersHollow 6px dots

Engineering handoff

AiraBoxPlotBuilt with d3 · proposed API, to confirm against @aira/react/charts
<AiraBoxPlot data={baskets} groupKey="tier" valueKey="basket" unit="currency" />

Strip plot

ProposedUpdated Design system owner · Punya Shetty

Every value as a dot, grouped by category, with the median marked. The honest alternative to a box plot when readers need to see the actual people.

Example

Days between purchases by tier
Days between purchases · 40 members per tier · Jun–Aug 2026

Strip plot of days between purchases. Median 12 days for Platinum, 19 Gold, 28 Silver and 41 Base. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

Up to about 300 values in total, when individual values matter.

Don't use when

Thousands of values: dots merge. Use a box plot or histogram.

Anatomy

PartTokenRule
Dot--aira-viz-1, 6px, 1px surface ringRandom vertical jitter within the row
Median2px --aira-text-primary tick
AxisTitled, units in the title

Engineering handoff

AiraStripPlotBuilt with d3 · proposed API, to confirm against @aira/react/charts
<AiraStripPlot data={members} groupKey="tier" valueKey="daysBetween" median />

Waffle chart

ProposedUpdated Design system owner · Punya Shetty

A single percentage made tangible: 100 squares, one per percent. Reads more naturally than a pie for "how many of every hundred".

Example

Active members who redeemed
Active members who redeemed · % · August 2026

Waffle chart: 38 of 100 squares filled. 38% of active members redeemed in August. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Use when

Use when

One share of a whole, for an audience that reads counts better than percentages.

Don't use when

Comparing several shares: use a bar. Values under 1%: squares cannot show them.

Anatomy

PartTokenRule
Filled square--aira-viz-1Filled left to right, top to bottom
Empty square--aira-viz-grid
Gap3px
Label--aira-t-h2 + captionThe value beside the grid

Engineering handoff

AiraWaffleChartBuilt with d3 · proposed API, to confirm against @aira/react/charts
<AiraWaffleChart value={38} label="of active members redeemed" />

Treemap

ProposedUpdated Design system owner · Punya Shetty

Parts of a whole in a hierarchy, as nested rectangles. For broad patterns, not exact comparison: area is a weak encoding.

Example

Spend by category
Spend by category · % of total · August 2026

Treemap of spend. Apparel 32% (men 14%, women 12%, kids 6%), beauty 18% (skin care 10%, make-up 8%), grocery 14%, home 12%, tech 8%, pharmacy 8%, other 8%. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Six categories take viz-1 to viz-6 by size; the seventh becomes "Other" in context grey. Sub-categories share their parent's colour, split by 2px surface gaps.

Use when

Use when

Many parts, two levels at most, and the question is "what dominates".

Don't use when

Exact comparison between similar parts: use a sorted bar.

Anatomy

PartTokenRule
Rectangles--aira-viz-1 to -6 by top-level categorySquarified layout, largest top-left
Gaps2px surface
Labels--aira-t-label-sm + shareOnly in rectangles over 64 × 36px; the rest in the tooltip
OtherContext greyPast six categories

Engineering handoff

AiraTreemapBuilt with d3 · proposed API, to confirm against @aira/react/charts
<AiraTreemap data={categories} labelKey="name" valueKey="share" childrenKey="kids" />

Pareto chart

ProposedUpdated Design system owner · Punya Shetty

Concentration: how few items make most of the total. Bars and the cumulative line share one axis, % of total, so there is no second axis.

Example

Share of sales by store
Share of sales · % of total · August 2026 · 12 stores
Store's share: the stores making 80% of salesStore's share: other storesRunning total

Pareto chart of 12 stores. The top 6 stores make 80.4% of sales; HSR Layout alone makes 21.9%. Use the arrow keys to move between values.

Source: write_db_100783.transactions_aira · As of 23 Sep 2026, 10:42 IST

Three mark types, so a key names all three: bars, the grey bars and the running-total line. The classic Pareto chart has two axes; showing bars as % of total puts both on one scale, which keeps the no-dual-axis rule without losing anything.

Use when

Use when

"Which few items matter most": stores, SKUs, members, complaint reasons.

Don't use when

Items are roughly equal: the chart just shows a straight line. Say so in a sentence instead.

Anatomy

PartTokenRule
Bars--aira-viz-1 for the items reaching 80%; context grey for the restSorted high to low
Cumulative line1.5px --aira-text-secondaryNeutral, so it does not compete with the bars
80% line1px dashed --aira-text-secondaryLabelled "80% of sales"
AxisOne, 0 to 100%Never a second axis

Engineering handoff

AiraParetoChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraParetoChart data={stores} labelKey="store" valueKey="sales" threshold={80} />

Sankey chart

ProposedUpdated Design system owner · Punya Shetty

Movement between states. For loyalty, the question it answers best is who moved up and down a tier.

Example

Tier movement
Members · Q1 to Q2 2026 · by tier

Sankey of tier migration. 4,200 members moved up a tier and 1,700 moved down; 44,200 stayed in their tier. Use the arrow keys to move between values.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

Flows are coloured by what they mean: up in --aira-delta-positive, down in --aira-delta-negative, staying in context grey. Tier nodes keep their locked steps.

Use when

Use when

Up to 8 states on each side, and the flows between them are the story.

Don't use when

More states, or more than two steps: use a migration matrix table, which stays readable.

Anatomy

PartTokenRule
NodesLocked tier steps, 12px wideSame order on both sides
FlowsDelta colours by direction; context grey for no changeWidth = members
Labels--aira-t-caption, secondaryTier and total outside each node
HoverDims every other flowThe tooltip names from, to and count

Engineering handoff

AiraSankeyChartBuilt with d3 · proposed API, to confirm against @aira/react/charts
<AiraSankeyChart flows={migration} sourceKey="from" targetKey="to" valueKey="members" order={tierOrder} colorBy="direction" />

Maps

ProposedUpdated Design system owner · Punya Shetty

Where a pattern is geographic. Regions use a tile-grid map by default: one equal square per state, in a rough geographic layout.

Example: tile-grid map

Redemption rate by state
Redemption rate · % of active members · August 2026 · by state
Under 17%45% or more

Tile-grid map of redemption rate by state. Karnataka 46% and Tamil Nadu 43% lead; Meghalaya 12%, Bihar 14% and Assam 15% trail. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Why tiles: every state gets the same size, so Rajasthan does not outweigh Kerala just by area, and there are no boundaries to get wrong. Maps published in India must show the official national boundaries; a generic map library often does not.

Use when

Use when

The geography itself is the point: regional clusters, coverage gaps, catchments.

Don't use when

A "where" question a sorted bar of regions answers just as well, which is most of them.

Anatomy

PartTokenRule
Tile--aira-seq-1 to -6, 2px radius, 4px gapPale steps get a 1px --aira-viz-baseline outline
Tile textState code in --aira-t-label-sm, value in captionText token chosen by contrast
Scale keyAlwaysBins stated
ValuesRates onlyNever totals: a total map just shows where people live

Variants

VariantWhenStatus
Tile-grid mapRegions, states, zonesDefault
Filled boundary mapOnly with approved official boundary data from the productAllowed, needs approved data
Store dot mapStore locations inside a city, dot size by value, size keyAllowed, needs approved base map

Do / don't

Do

Show full state names in the tooltip and table.

Don't

Use a free web map tile service with unknown boundary depiction for Indian orgs.

Engineering handoff

AiraMapBuilt with d3 · proposed API, to confirm against @aira/react/charts
<AiraMap variant="tiles" layout="india-states" data={states} idKey="code" valueKey="rate" unit="percent" />

Anomaly markers

Stable blockProposed settingUpdated

A line chart setting that marks values outside their expected range. Finding the unusual day is exactly what an analyst like aiRA should do; the chart shows the what, and aiRA's prose offers the possible why.

Example

Daily redemptions
Redemptions · count · per day · August 2026 · all stores

Line chart of daily redemptions in August with the expected range. 18 August had 3,220 redemptions, 2.3 times expected. 26 August had 560, 0.4 times expected. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

The band is --aira-seq-1. Anomalies are a filled 12px dot in the series colour, with a 2px surface ring and a factual label. Never red: an unusual day is information, not an error.

Rules

  • Mark at most three anomalies per chart; more means the expected range is wrong.
  • Labels state the fact ("2.3× expected"). A possible cause goes in aiRA's prose, worded as possible ("A preview SMS went out that morning").
  • State how "expected" was worked out in the band's tooltip.
PropValues
expected{ low, high } per point, or "auto"
anomaliesmax 3, each with a label

Engineering handoff

AiraLineChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraLineChart data={days} xKey="day" yKey="redemptions" expected={{ low: "p10", high: "p90" }} anomalies={flagged} />

Reference lines and error bars

Stable blocksProposed settingsUpdated

What Power BI's analytics pane adds: target, average, median and percentile lines, and error bars for values measured on a sample.

Example

NPS by tier
Net promoter score · August 2026 survey · by tier

Column chart of NPS with likely ranges. Platinum 52, likely 46 to 58, clearly above the target of 45. Gold 44, likely 39 to 49, could be either side. Silver 38 and Base 29 are below. Use the arrow keys to move between values.

Source: write_db_100783.survey_responses_aira · As of 23 Sep 2026, 10:42 IST

Only the bar whose whole range clears the target is indigo. Gold's value is close to target, but its range crosses it, so it stays grey.

Line styles

LineStyleLabel
Target1px dashed --aira-text-secondary"Target 45", right end
Average1px dotted --aira-text-tertiary"Average 41"
Median1px dotted --aira-text-tertiary"Median 38"
Percentile1px dotted --aira-text-tertiary"Top 10%: 62"
Min and maxNot drawn by defaultSay them in the tooltip; lines at the extremes mostly add clutter
Error bars1.5px --aira-text-secondary with 12px capsLevel in the tooltip

Rules

  • Two reference lines per chart at most.
  • Error bars on any value from a sample: surveys, tests, estimates.
  • Say "likely range" in the interface.

Engineering handoff

AiraBarChartBuilt with recharts · proposed API, to confirm against @aira/react/charts
<AiraBarChart data={tiers} xKey="tier" yKey="nps" errorBars={{ low: "low", high: "high" }} referenceLines={[{ kind: "target", value: 45 }, { kind: "average", value: 41 }]} />

Moving average and calculations

Stable blockProposed settingUpdated

Daily loyalty data swings with the week. A moving average shows the trend underneath without hiding the real days.

Example

Daily redemptions with 7-day average
Redemptions · count · per day with 7-day average · Jun–Aug 2026

Line chart of daily redemptions from June to August with a 7-day moving average rising from about 1,300 to about 1,500. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

The daily line stays, thin and in context grey; the average is the emphasis. A moving average is always labelled with its window.

Power BI visual calculations, and when each is honest

CalculationUse it forRule
Moving averageSmoothing weekly or daily swingsName the window ("7-day average"); window at least one full cycle
Running totalProgress toward a period goalSubtitle says "cumulative since 1 Jan"; never presented as growth, because it can only go up
Percent of totalSharesSay "share" and the total it is a share of
RankLeader boardsUse a bump chart over time; show the gap to the next item in the tooltip
Period over periodDeltasAlways name the comparison; like for like on partial periods

Engineering handoff

AiraLineChartBuilt with recharts d3 · proposed API, to confirm against @aira/react/charts
<AiraLineChart data={days} xKey="day" yKey="redemptions" movingAverage={{ window: 7 }} showRaw="context" />

Result tables

ProposedUpdated Design system owner · Punya Shetty

How aiRA shows query results as tables in the artifact panel, whether it returns one table or several. The format, tabs and behaviour are the Artifact-preview page's. Alignment follows this page's rule instead: numbers right, words left.

Example

Member IDTierPoints issuedPoints redeemedLast transaction

Switch tables with the tabs, page through the 62,76,905 rows, and change the page size. The member rows are sample data.

Use when

Use when

A query returns rows: one table, or several tables from one cell.

Don't use when

A handful of numbers that answer the question. Use a chart, a KPI tile or a sentence in the answer.

Anatomy

PartTypeColourRule
Cell barOnly when there are two or more tables. Holds Export all to FTP, which covers every table in the cell. No count of tables
Tabs--aira-t-label-mdSelected --aira-text-link with a 3px --aira-border-brand underline; others --aira-text-tertiaryOne per table, named by the table. Hover is a neutral --aira-border-strong underline that never outweighs the selected tab. Too many tabs scroll sideways, and the edge clips the last one
Table toolbar--aira-t-body-sm--aira-text-tertiary for the countInside the table's own panel, so its actions read as this table's: "Total 62,76,905 rows" in the org locale, then CSV, copy and Export to FTP
Header--aira-t-label-md (14/20 medium)--aira-text-primary on --aira-bg-surface-alt1px --aira-border-default below. Padding --aira-sp-3 × --aira-sp-5. Stays in place while rows scroll. Sentence case
Cells--aira-t-body-sm (13/20)--aira-text-primaryPadding --aira-sp-4 × --aira-sp-5. 1px --aira-border-subtle between rows. Hover --aira-bg-hover. No zebra, no colour, no bars in cells
BodyThe table sits flat in the panel, with no border or box around it. It is at most 560px tall and scrolls inside; wide tables scroll sideways
Footer--aira-t-label-sm--aira-text-secondary; current page --aira-text-link with a --aira-border-brand outlineOnly when the rows run past one page. 10 / page menu (10, 20, 50, 100; a custom menu, never a native select) on the left, pages with ‹ › and … on the right

Alignment

Column holdsAlignExamples
Words, names, labelsLeftTier, store, campaign
Dates and timesLeft18 Sep 2026
IDs and codesLeftM-104233
Numbers, currency, percentagesRight, with tabular numerals12,400 · ₹2.4L · 23.12%
HeadingsSame as their column"Points issued" sits right over its numbers

The Artifact-preview page left-aligns every column. This page's rule wins: right-aligned figures line up their place values, so they can be compared down the column.

Behaviour

  • One table: no tabs and no cell bar. The table toolbar sits directly above the table.
  • Several tables: the first tab is the table that answers the question. Each tab keeps its own page and page size when you switch away and back.
  • Paging starts at 10 rows. Changing the page size goes back to page 1, and every page change scrolls the table to its top.
  • CSV downloads every row of that table. Copy copies the rows on screen as tab-separated text, and its icon becomes a tick for 1.5 seconds.
  • Charts and tables together: the charts come first, then the tables in tabs below them (see Chart preview panel).

States

  • Loading: skeleton rows under the real header.
  • No rows: "No rows match this query" with the filter or period that caused it, and a way to change it.
  • Error: plain language and Try again. No codes.

Accessibility

  • Tabs are a tablist; the arrow keys move between tabs.
  • Pages are real buttons; the current page carries aria-current="page".
  • The page-size menu is a listbox that works from the keyboard: arrows, Enter and Escape.
  • Every icon button has an accessible name that includes the table, such as "Copy Points by member".

Do / don't

Do

Keep each table's actions in its own toolbar.

Don't

Put per-table actions on the tab bar, where they read as covering every table.

Do

Show the full row count, in the org locale.

Don't

Write "showing the first 8". The pager says where you are.

Scorecard

ProposedUpdated Design system owner · Punya Shetty

Several metrics against their targets in one view: Power BI's goals visual, built from a table, bullet bars and sparklines. The standard format for a loyalty programme's monthly review.

Example

Programme goals
Loyalty programme goals · targets from the 2026 plan · status against target
GoalValueTargetProgress6-month trendStatus
Active members50,10052,000Behind · 96% of target
Redemption rate38%40%Behind · −2 pts
Repeat purchase rate31%30%Ahead · +1 pt
Tier upgrades (quarter)4,2003,500Ahead · 120% of target
Points liability (lower is better)₹42.2Lunder ₹45.0LOn track · ₹2.8L headroom
Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

Status is plain text with the gap, not a coloured chip: table cells stay monochrome. The one visual cue per row is the bullet bar, with the target as a dark tick.

Use when

Use when

3 to 10 goals reviewed together, each with a target.

Don't use when

Metrics without targets: a KPI tile row. One goal: a bullet chart.

Anatomy

ColumnContentRule
GoalMetric name, with "(lower is better)" where it appliesText, left
Value and targetOrg-locale numbersRight-aligned
ProgressBullet bar: --aira-viz-1 bar, --aira-text-primary target tickOne scale per row, stated in the tooltip
TrendSparkline, 1.5px --aira-viz-1, last point markedSame period for every row
StatusAhead · On track · Behind, plus the gapPlain text, never colour alone

Engineering handoff

AiraScorecardBuilt with AiraDataTable AiraSparkline · proposed API, to confirm against @aira/react/charts
<AiraScorecard goals={goals} columns={["value", "target", "progress", "trend", "status"]} />

Matrix

ProposedUpdated Design system owner · Punya Shetty

Values across two dimensions with an expandable row hierarchy, as Power BI's stepped matrix does. Every figure is a number, right-aligned; there are no bars inside cells.

Example

Redemptions by zone and store
Redemptions · count · Jun–Aug 2026 · by zone and store
Zone and storeJunJulAugTotalShare of total
South40,12042,86045,2101,28,19071.0%
HSR Layout16,98017,64018,42053,04029.4%
Indiranagar13,42014,39015,23043,04023.8%
Koramangala9,72010,83011,56032,11017.8%
West16,34017,41018,64052,39029.0%
Total56,46060,27063,8501,80,580100%
Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST

Group rows collapse and expand with a borderless chevron button. Shares are plain right-aligned percentages: a table is for exact values, so bars stay out of the cells. When the pattern matters more than the numbers, use a chart.

Rules

  • Group and total rows in --aira-fw-medium; child rows indented by --aira-sp-12.
  • Two levels of grouping at most in a chat answer; three on a dashboard.
  • No data bars in cells: the percentage alone. If the pattern matters more than the numbers, show a chart instead.
  • Conditional colour only as a heatmap table (see Heatmap); never traffic-light cells.

Engineering handoff

AiraDataTableBuilt with @tanstack/react-table · proposed API, to confirm against @aira/react/charts
<AiraDataTable data={rows} groupBy={["zone"]} expandable columns={[…, { key: "share", unit: "percent" }]} />

Chart preview panel

ProposedUpdated Design system owner · Punya Shetty

How several charts, or charts with tables, appear in the preview panel beside the chat on desktop web. Every chart is its own chart card; the panel only arranges the cards. Try every case in the live prototype. Mobile rules come later.

Example

Store and tier activity

Redemptions by store
Redemptions · count · 1–23 Sep 2026 · all tiers

Column chart of redemptions by store. HSR Layout led with 18,420. Use the arrow keys to move between values.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST
Active members by tier
Active members · count · Mar–Aug 2026 · by tier

Line chart of active members by tier. Silver rose from 11,200 to 12,400, Gold from 6,800 to 8,000, Platinum from 2,100 to 2,600. Use the arrow keys to move between values.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

The panel at the width it opens at, 610px. Hover a card for its actions. With five or more charts, a Charts menu joins the toolbar.

Where charts appear

SurfaceWhat it holds
Chat answerOne chart at most, after one or two sentences with the finding.
Artifact card in the chatWhen an answer has more than one chart, or charts with tables: the artifact's name with its version underneath. Clicking it opens the preview panel.
Preview panelThe charts and tables of one answer, arranged as below.
DashboardThe same chart cards in a grid (see Dashboards).

Layout

PanelColumnsRule
Default, beside the chat: about 610px, up to 780pxOneCards stack in one column you scroll. Two cards side by side would be about 280px each, too narrow for a 12-month axis.
Widened: the chat is hidden and content is 900px or moreTwoCharts marked half width pair up: short comparisons, rankings, tier bars. The first chart and every trend over time stay full width.
PhoneWaiting on the mobile rules.

How many charts

ChartsToolbarNotes
1Preview / Code onlyThe card's own CSV button covers the download.
2–4Preview / Code · Download all as CSVDownload all gives a .zip with one CSV file for each chart and table. Its tooltip says so.
5–8Adds a Charts menuA text menu with one arrow and no leading icon. It lists the chart names; picking one scrolls to that card.
More than 8aiRA offers to build a dashboard instead, or to show the four that matter most.

No count label anywhere: no "5 charts", no "2 charts · 2 tables". The stacked cards already show it.

Order and content

  • The chart that answers the question comes first, full width, then the supporting charts.
  • Charts before tables. Tables go below the charts in underline tabs, in the Result tables format.
  • Every card keeps its full anatomy: title, subtitle with the period, trust line. There is no shared "data from" line for the whole panel.
  • One chart per card. A chart with two y-axes becomes two cards on the same months. A grid of subplots becomes one card per chart.
  • Preview / Code: Code shows the code aiRA ran. It returns chart data for the product to draw, never an image of the charts.

States

  • Drawing: each card shows its title and subtitle, a skeleton in the plot's shape and "Drawing this chart…", and fills in as its own query finishes.
  • One chart fails: only that card changes. It reads "Couldn't draw this chart", then "The query took too long to finish.", then a Try again button. The other cards are untouched.

Do / don't

Do

Give every chart its own card, with its own actions and trust line.

Don't

Send a matplotlib image with several charts drawn into it. Nothing in it can be hovered, read by a screen reader or downloaded as data.

Do

Name the format on every download: "Download all as CSV", "CSV".

Don't

Label a button "Download" or "Download all" and leave people guessing what they get.

Dashboards

ProposedUpdated Design system owner · Punya Shetty

A dashboard is chart cards in a grid, re-run live when it opens. Each tile follows the Chart card block exactly: the same title, subtitle, trust line, actions, hover and full screen as in the preview panel. These rules arrange the tiles.

Reading order

  • Header: title, period, filters and the as-of time.
  • Row 1: two to four KPI tiles.
  • Row 2: the main trend, full width. This is the chart that answers the dashboard's main question.
  • Row 3: breakdowns, two or three across.
  • Row 4: the detail table, in the Result tables format.

Grid

TileColumns at lg and xlUse
KPI3 (four across) or 4 (three across)Single numbers
Full12The main trend, detail tables
Half6Two related breakdowns side by side
Third4Three small breakdowns
  • 12 columns at lg and xl, 8 at md, 4 at sm.
  • Gap between tiles --aira-sp-4; page padding --aira-sp-6.
  • Tiles in a row share one height, and their plots share one baseline, so axes line up.
  • No chart tile narrower than 4 columns.

Consistency across the page

RuleWhy
One period for the whole dashboard, stated once in the header. A tile that differs says so in its subtitleReaders assume tiles are comparable
The same category keeps the same colour in every tileOtherwise Gold is indigo in one chart and green in the next
Charts meant to be compared share a scaleTwo bars of equal height must mean equal values
One comparison basis for all deltas: previous period or last yearMixed bases make the KPI row contradict itself
Every tile keeps its own trust lineTiles can come from different tables

Limits

  • Up to 6 charts visible in one screen at lg, and up to 12 on a dashboard.
  • More than that: group them into tabs, one question per tab.
  • One highlight colour per dashboard for "this is the story".

Filters and cross-highlighting

  • Dashboard filters sit in the header and apply to every tile. Applied filters stay visible as chips, with clear all.
  • A tile with its own filter names it in its subtitle ("Gold tier only").
  • Changing a filter re-runs the queries and updates every as-of time.
  • Clicking a bar highlights that item in every tile; it does not filter. The highlight shows as a removable chip in the header, and Escape clears it.
  • Asking a follow-up about a data point opens a new chat, never a conversation inside the dashboard.

Mobile

ProposedUpdated Design system owner · Punya Shetty

On iOS and Android, aiRA runs in the phone's browser with the same code as desktop; a native app comes later. Every chart is the same chart card as on web, with the same title, subtitle, plot, key and trust line. Only the controls around the card change for touch. Try it in the live prototype.

Example

Store and tier activity

Redemptions by store
Redemptions · count · 1–23 Sep 2026 · all tiers

Bar chart of redemptions by store. HSR Layout led with 18,420.

Source: write_db_100783.redemptions_aira · As of 23 Sep 2026, 10:42 IST
Active members by tier
Active members · count · Mar–Aug 2026 · by tier

Line chart of active members by tier.

Source: write_db_100783.members_aira · As of 23 Sep 2026, 10:42 IST

Tap a chart to open it full screen. Touch and hold to read exact values.

A 375px phone. Tap a card to open it full screen, or its "…" for the menu.

What stays the same

  • The chart card's anatomy, titles, colours, keys and trust line.
  • The Result tables format, alignment by column type, and monochrome cells.
  • Loading, error and empty states, word for word.
  • The visible size and shape of every button. Only the tap area grows (see Touch targets).

What changes on touch

PartWebMobile
Panel headerTitle left; widen and close at the rightTitle centred; close at the top right. No widen
Preview / CodeShownNot shown
ToolbarIn the panel toolbarA row under the header: Charts menu on the left (five charts or more), Download all as CSV on the right (Download CSV for one chart)
ColumnsOne, or two when widenedAlways one
Card actionsOn hover and keyboard focusA "…" button on each card opens a bottom sheet: View as table, Copy data, Download CSV, Open full screen, Cancel
Opening full screenThe full-screen buttonTap the card, or Open full screen in its menu. There is always a visible way in, never only the gesture
Full screenA window over the pageFills the phone. "…", a divider and close at the top right. The chart is taller; View as table fills the screen with the first column fixed
Reading valuesHoverTouch and hold a mark, slide to move along the chart, lift to clear after 1.5 seconds. A hint under the cards says so
Charts menuA dropdownA bottom sheet listing the chart names
TablesCSV, copy and Export to FTPCSV and copy; Export to FTP stays on desktop. With more than three columns the first column stays fixed, and a note says how many columns scroll sideways. The pager shows fewer page numbers
Copy confirmationTick on the copy buttonTick on the "…" button that opened the menu

Touch targets

  • Buttons look exactly as on web: 28px on cards, 32px in headers, the same radius and fill.
  • Each gets an invisible 44px tap area around it. Neighbouring buttons sit at least 12px apart so tap areas never overlap.
  • No hover on touch. A tapped button shows only a pressed state (--aira-bg-pressed) while the finger is down, so it never stays grey.
  • Sheets and full screen respect the safe areas (--aira-safe-*) for the notch and the home indicator.

Charts at phone width

  • Twelve labelled columns do not fit, so a column chart becomes horizontal bars when its card is narrower than 480px. The data and labels stay the same.
  • The month axis shows every second or third month, and the year again wherever it changes.
  • Half-width pairing never applies: every chart is full width.
  • Direct labels stay; when line ends would collide, they keep at least 15px apart.

Do / don't

Do

Keep the chart card identical to web, and change only the controls around it.

Don't

Draw a separate mobile chart, or drop the trust line to save space.

Do

Give every gesture a visible control too: tap to open full screen, and Open full screen in the menu.

Don't

Put close on the left because a platform sheet does. It sits at the top right on every platform.

Power BI coverage

Every visual on Microsoft's Power BI visualizations overview, checked 23 Sep 2026, and where it lives in aiRA.

Power BIIn aiRAStatus
Bar and column chartsBar chartStable block
Line chartsLine chartStable block
Area chartsLine chart, area settingStable block
Combo chartsBar chart with a same-unit line; different units become two stacked chartsRule
Ribbon chartsBump chartThis release
Waterfall chartsWaterfall chartCore release
Pie and donutDonut chart (2 or 3 parts)Core release
TreemapsTreemapThis release
Funnel chartsFunnel chartCore release
Scatter, bubble, dot plotScatter chart, dot plotCore release
TablesData tableStable block
MatrixMatrix with expandable rowsThis release
MapsTile-grid map; boundary and dot maps with approved dataThis release
CardsKPI tile (AiraMetricTile)Stable block
KPI visualKPI tile with target, or bullet chartCore release
GaugeBullet chartCore release
GoalsScorecardThis release
Decomposition treeDrill sequence of sorted bars, each "Ask about this" opening a new chatRule
Key influencersSorted driver bars, described as association, not causeNext release
Smart narrativeaiRA's finding title and proseRule
Anomaly detectionAnomaly markers on the line chartThis release
SlicersFilter bar (on the design system's watchlist)Watchlist
Q&A visualThe aiRA chat itselfRule
Tooltips and report-page tooltipsChart tooltip; one sparkline at most, anything bigger is "Ask about this"Core release
Analytics pane: reference lines, forecast, error barsReference lines, error bars, forecast settingThis release
Visual calculationsMoving average and calculation rulesThis release
Small multiplesSmall multiples wrapperNext release
Conditional formattingHeatmap table, or the one allowed delta colour. No data bars in cellsThis release
ThemesaiRA tokens; no per-report themesRule
Image, text box, shapes, buttons, navigators, Power Apps, paginated, R and PythonNot charts. aiRA generates dashboard scaffolding to each customer's requestOut of scope

Proposed tokens

What this page needs that the design system does not have. On this page they are named --aira-proposed-*, so they can never collide with or redefine a real token.

Proposed tokenLightDarkWhy
--aira-proposed-viz-context--aira-slate-300--aira-slate-600Context grey for highlight and grey. The site uses raw --aira-slate-300, which has no dark value
--aira-proposed-div-neg-1 to -3#FBD5BE · #F08A4B · #C2410C#4A2210 · #E0793A · #FB923COrange negative side of the diverging palette, replacing red. Starting values, built out from --aira-delta-negative
Stroke widths2 · 2.5 · 1.5pxsameStandard, emphasis and context lines (the site's current values)
Bar radius2pxsameValue end of bars (the site's current value)
--aira-proposed-viz-dim0.35sameHow far other marks fade while one is hovered or focused
Partial-period patternto designto designMarks the incomplete last period on live data

Orange diverging ramp, as proposed

Much lowerNo changeMuch higher

Divergences found

Places where the live design system disagrees with itself or with the rules on this page. Each needs a decision from the design system owner.

#WhatMeasuredSuggested fix
1Dark-mode categorical neighbours look alikeviz-1 vs viz-2: 6.3 with normal vision (15 needed), 0.8 for red-green colour blindness · viz-5 vs viz-6: 13.0 · viz-2 vs viz-3: 4.0 for tritanRe-step viz-2 and viz-6 in dark
2Push and In-app channel colours look alikecat-push vs cat-inapp: 6.9 light, 5.3 dark, with normal visionMove In-app away from indigo
3Four chart colours are below 3:1 on light surfacesviz-2 2.57, viz-4 2.30, cat-whatsapp 2.35, cat-email 2.93Darken the light values; until then, label every mark
4Diverging palette uses red for negatives--aira-div-neg-* are red stepsOrange ramp above
5Dark negative diverging steps out of orderneg-2 #F58B8B is lighter than neg-3 #F55959Fixed by the orange ramp
6Chart labels below the type rampSite examples use 10px and 11pxUse --aira-t-caption (12px)
7KPI example hard-codes currencyvalue="₹14.2L"Pass the number; the formatter applies org locale
9Title rule: the site says "the title is the takeaway"aiRA titles name the chart ("Daily redemptions")Deliberate aiRA override: the finding goes in aiRA's answer and annotations
10Channel colours: the site locks a channel to its colour everywhereaiRA draws a single series in indigo, even for one channelDeliberate aiRA override: channel colours only when channels are compared
8Web channel colour is a greycat-web reads as grey and competes with context greyGive Web a hue, or reserve grey for context only

Colour differences measured with a colour-blindness simulation validator (OKLab ΔE × 100); contrast is WCAG contrast against --aira-bg-surface.