Chart
Shows how a number moved over time, with exact values on hover.
Examples
Runs, 30 days
Outcomes, 14 days
Outcomes, 14 days
Type
Calls
Calls Date Agent calls Model calls Sep 1 502 721 Sep 2 495 731 Sep 3 517 764 Sep 4 461 735 Sep 5 534 793 Sep 6 580 796 Sep 7 545 815 Sep 8 599 764 Sep 9 590 693 Sep 10 563 719 Sep 11 597 730 Sep 12 610 762 Sep 13 595 721 Sep 14 589 677 Sep 15 635 645 Sep 16 592 587 Sep 17 565 545 Sep 18 686 629 Sep 19 658 524 Sep 20 665 591 Sep 21 686 643 Sep 22 642 651 Sep 23 696 643 Sep 24 702 664 Sep 25 653 704 Sep 26 706 675 Sep 27 673 677 Sep 28 688 748 Sep 29 775 757 Sep 30 760 803 linedefault
Runs by hour
Runs by hour Date Runs 00:00 58 01:00 46 02:00 56 03:00 54 04:00 71 05:00 77 06:00 71 07:00 87 08:00 105 09:00 93 10:00 98 11:00 107 12:00 116 13:00 113 14:00 101 15:00 101 16:00 87 17:00 87 18:00 79 19:00 73 20:00 58 21:00 64 22:00 50 23:00 55 bar
Runs per day: up from 1,403 to 2,010, low 775, high 2,123 Point Runs per day Point 1 1,403 Point 2 1,672 Point 3 1,572 Point 4 775 Point 5 816 Point 6 1,557 Point 7 1,707 Point 8 1,577 Point 9 1,620 Point 10 1,639 Point 11 844 Point 12 848 Point 13 1,656 Point 14 1,776 Point 15 1,671 Point 16 1,814 Point 17 1,865 Point 18 1,068 Point 19 1,139 Point 20 1,787 Point 21 1,871 Point 22 1,801 Point 23 1,892 Point 24 1,936 Point 25 1,215 Point 26 1,136 Point 27 1,904 Point 28 2,123 Point 29 1,953 Point 30 2,010 sparkline
Releases, 30 days Bucket Releases Status Sep 1 99 OK Sep 2 157 OK Sep 3 137 OK Sep 4 119 OK Sep 5 69 OK Sep 6 0 None Sep 7 0 None Sep 8 190 OK Sep 9 104 OK Sep 10 95 OK Sep 11 46 OK Sep 12 155 OK Sep 13 0 None Sep 14 0 None Sep 15 61 OK Sep 16 116 OK Sep 17 189 Warning Sep 18 154 OK Sep 19 67 OK Sep 20 0 None Sep 21 0 None Sep 22 96 OK Sep 23 92 OK Sep 24 43 OK Sep 25 179 OK Sep 26 61 OK Sep 27 0 None Sep 28 0 None Sep 29 136 OK Sep 30 189 OK status
Frame
Runs by hour
paneldefault
Runs by hour
Runs by hour Date Runs 00:00 58 01:00 46 02:00 56 03:00 54 04:00 71 05:00 77 06:00 71 07:00 87 08:00 105 09:00 93 10:00 98 11:00 107 plain
Runs: up from 1,403 to 2,010, low 775, high 2,123 Point Runs Point 1 1,403 Point 2 1,672 Point 3 1,572 Point 4 775 Point 5 816 Point 6 1,557 Point 7 1,707 Point 8 1,577 Point 9 1,620 Point 10 1,639 Point 11 844 Point 12 848 Point 13 1,656 Point 14 1,776 Point 15 1,671 Point 16 1,814 Point 17 1,865 Point 18 1,068 Point 19 1,139 Point 20 1,787 Point 21 1,871 Point 22 1,801 Point 23 1,892 Point 24 1,936 Point 25 1,215 Point 26 1,136 Point 27 1,904 Point 28 2,123 Point 29 1,953 Point 30 2,010 none
States
Reply time
Loading Reply timeloading
Reply time
Reply time Date p50 p95 p99 No runs yet empty
Reply time
Query timed outerror
Usage
When to use it
- Use
- A number over time where the shape matters: runs per day, reply time, cost.
- Use
- Two to four series compared on one axis.
- Use
- A daily health strip for an agent or a prompt release (type="status").
- Use
- A small trend beside a stat or in a table row (type="sparkline").
- Not for
- One number now. Use Stat.
- Not for
- How far a task got. Use Progress.
- Not for
- Exact values to sort and compare. Use Table.
Rules
12 rules
- Must
- Set unit to duration or count; never a valueFormat that picks a unit per value. Because a per-value unit puts 900ms and 1.2s on one axis, so the ticks no longer read as one scale.
- Must
- Render the Chart with loading and its final height; never a spinner or a 0px chart. Because a spinner or an empty box makes the dashboard jump when data lands, and hides which chart is which.
- Should
- Give each series its kind in the config so the kind's hue reads the same on every screen; a single-kind chart also passes kind for its legend mark.
- Must
- Show an error top-left in the chart's own frame with Retry; the frame keeps its height. Because a chart swapped for an alert box moves the dashboard and loses which chart failed.
- Never
- Draw a round point; the active point is a square. Because round means a person in Quartz, and a dot on a line reads as an avatar.
- Must
- Name the measure and the range in label, such as "Runs, last 30 days". Because label is the title, the table caption and the name of every point group, so "Chart" is all a screen reader hears.
- Should
- Pass empty as a sentence that names the range.
- Must
- Let the chart render its data table; never hide it with your own CSS. Because the sr-only table is the only way a screen reader gets the values, and display:none removes it.
- Never
- Fill an area with a gradient; use area, a flat wash under one line. Because a gradient fades to nothing near the axis, so low values lose their fill and overlapping areas mix into a new hue.
- Never
- Color series by index; map series to kinds or leave them ink. Because hue means a kind in Quartz, so a purple series reads as agents, and hex values skip the line step that passes 3:1.
- Should
- Draw at most 4 series in a line chart; split it or show the top 3 and an Other. Because kind-less series past two repeat dashed, so a fifth line no longer has a look of its own.
- Should
- Keep kind-less bar series to 2. Because kind-less series step from ink to ink at 66%, and bars cannot dash for a third.
Accessibility
8 checks
- The plot is a group named by label with aria-roledescription "chart".
- Each point is a button named by its x value and every series value.
- A data table with the label as caption is always in the DOM.
- Arrow keys move between points, Shift moves 7, Home and End jump, Escape hides the tooltip.
- Legend items are toggle buttons with aria-pressed.
- The tooltip is aria-hidden; the point name carries the values.
- Series differ by dash and position as well as hue, so forced colors still read.
- The frame sets aria-busy while loading; error is a status region.
Edge cases
13 edge cases
- Long label and series name
- The title wraps; x labels thin to one per 72px; legend names truncate.
- One data point
- Draws a small square; one bar fills its band.
- data is []
- The sentence top-left over a baseline at the body height; the screen-reader table stays, with one row that says it.
- Null value
- Breaks the line; never drops to zero.
- 90 status cells on 320px
- Cells share the width in whole pixels at 1px gaps.
- Right to left
- The chart mirrors: x runs from the right, the y axis sits on the right, and Left and Right follow the screen.
- Text-only zoom
- SVG axis text stays 12px; read exact values from the table switch.
- Query fails
- The reason in red and Retry sit top-left at the same height; focus moves to the title when Retry unmounts.
- Touch
- Each x value is a full-height column; use about 30 buckets on phones.
- Forced colors
- Series map to CanvasText and differ by dash and position; silhouettes map to GrayText.
- Reduced motion
- Data changes redraw in one frame; nothing tweens.
- Inside a frame
- Use frame="plain" or "none" so there is one frame, never two; a chart in a Panel's body takes "none".
- Study a chart on a working page
- Put it in a Card with frame="plain"; frame="panel" is the widget.
API
15 props
type'line' | 'bar' | 'sparkline' | 'status'defaultrequiredlabelstringdefaultrequireddataChartRow[] | (number | null)[] | StatusBarBucket[]defaultrequiredxKey, config, seriesstring, ChartConfig, string[]title, description, actionsReactNodedefaultlabelframe'panel' | 'plain' | 'none'default'panel', sparkline 'none'kind, liveKind, booleanheightnumberdefault200, status 56, sparkline 24loading, error, onRetry, retrying, emptyboolean, ReactNode, () => void, boolean, ReactNodeunit'duration' | 'count'legend, table, defaultHiddenboolean, boolean, string[]defaultlegend at 2+ seriescurve, area, zero'monotone' | 'linear' | 'step', boolean, booleandefault'monotone', false, truestackedbooleandefaultfalsekind, variant, end, sizesparklinekind, noun, range, onBucketClick, sizestatus
Tokens
18 tokens
--accent-mark- var(--ink)
--accent-line- var(--ink)
--line-accent- 2px
--chart-ink-2- oklch(var(--ink-c) / 0.66)
--accent-wash- var(--surface-selected)
--line-frame- oklch(var(--ink-c) / var(--line-frame-alpha))
--line-row- oklch(var(--ink-c) / 0.08)
--line-hair- 1px
--ink- oklch(var(--ink-c))
--type-label-sm- 400 12px/16px var(--font-sans)
--text-3- oklch(var(--ink-c) / 0.66)
--surface-skeleton- color-mix(in oklch, oklch(var(--grey-c)), oklch(var(--ink-c)) var(--skeleton-step))
--cell-quiet- oklch(var(--ink-c) / 0.07)
--elev-overlay- 0 0 0 1px var(--line-frame), 0 12px 24px -12px oklch(var(--text-on-field-c) / var(--shadow-alpha))
--radius-soft- 6px
--radius-frame- 10px
--mark-size- 8px
--chart-dash- 6 4