Trend · Intermediate

Skyline

Stacked volume per period as blocks, with a smaller outcome in the same unit standing in front.

Open in playground
Use when
7 to 40 periods of stacked volume and a smaller outcome in the same unit (failures, retries), as the lead card of a dashboard
Avoid when
Negative values, precise value comparison (bars are more accurate), or two values in different units (split the chart and link it with syncId)

Try it

34

Skyline

Daily tokens, last 14 days
SkylineSkyline. 4 series (Input, Cached, Output, Reasoning) stacked from 10/23 to 11/5. Peak period 11/5 at 1.5M. Front row (Failed, Retried) peaks at 10/31 with 216K, 16.4% of the back row. Use left and right arrows for periods, up and down to switch rows.Peak 1.5MFront peak 10/31 · 216K (16.4%)
Skyline data
10/23 · backback 993.8K (Reasoning 79.5K, Output 187K, Cached 317.2K, Input 410K), front 21K (Retried 10K, Failed 11K)
10/24 · backback 1.1M (Reasoning 87.9K, Output 202K, Cached 337.9K, Input 446K), front 24.5K (Retried 9.2K, Failed 15.3K)
10/25 · backback 1.1M (Reasoning 87.5K, Output 202.5K, Cached 336.2K, Input 440.9K), front 21.4K (Retried 6.1K, Failed 15.4K)
10/26 · backback 1.2M (Reasoning 92.9K, Output 221.8K, Cached 370.2K, Input 472.3K), front 17.9K (Retried 4.6K, Failed 13.3K)
10/27 · backback 1.1M (Reasoning 88.3K, Output 216.5K, Cached 366.5K, Input 456.4K), front 14.8K (Retried 5.7K, Failed 9K)
10/28 · backback 1.2M (Reasoning 92.5K, Output 226.3K, Cached 386.7K, Input 482.5K), front 18.1K (Retried 9.5K, Failed 8.6K)
10/29 · backback 1.3M (Reasoning 100.3K, Output 238.3K, Cached 405.6K, Input 519.6K), front 25.2K (Retried 12.6K, Failed 12.6K)
10/30 · backback 1.2M (Reasoning 100K, Output 230.5K, Cached 387.1K, Input 509K), front 28K (Retried 11.3K, Failed 16.7K)
10/31 · backback 1.3M (Reasoning 108.1K, Output 249K, Cached 413.6K, Input 545K), front 216K (Retried 126K, Failed 90K)
11/1 · backback 1.4M (Reasoning 111.9K, Output 264.6K, Cached 440.5K, Input 566.9K), front 23.2K (Retried 5.8K, Failed 17.3K)
11/2 · backback 1.4M (Reasoning 106.9K, Output 260.8K, Cached 439.7K, Input 550.2K), front 18.1K (Retried 6.1K, Failed 12K)
11/3 · backback 1.4M (Reasoning 112.5K, Output 276.5K, Cached 471.7K, Input 586.4K), front 20.5K (Retried 10.3K, Failed 10.3K)
11/4 · backback 1.4M (Reasoning 113K, Output 271.1K, Cached 462.6K, Input 587.5K), front 26.7K (Retried 13.8K, Failed 12.9K)
11/5 · backback 1.5M (Reasoning 121.7K, Output 282.3K, Cached 476K, Input 622.5K), front 33.7K (Retried 14.6K, Failed 19.1K)
Nov 5 was the busiest dayIntermediate

Hover over the chart, or Tab in and use the arrow keys to move between items. The numbers are sample data.

Variants

Named looks for this chart. Each one is the example above with a few options or fields changed. The CLI (ztd-charts example skyline --variant <id>) and the MCP server know the same names.

Rate strip

Daily tokens, last 14 days
Sample data

Shallow blocks

Daily tokens, last 14 days
Sample data

Without wall

Daily tokens, last 14 days
Sample data

Texture on every row

Daily tokens, last 14 days
Sample data

Code

SkylineChart.tsx
import { ChartCard, SkylineChart } from "ztd-charts";

const data = [
  {"day":"2026-10-23","Input":410000,"Cached":317223,"Output":186999,"Reasoning":79534,"Failed":11000,"Retried":10000},
  {"day":"2026-10-24","Input":446030,"Cached":337921,"Output":202044,"Reasoning":87942,"Failed":15314,"Retried":9190},
  {"day":"2026-10-25","Input":440886,"Cached":336150,"Output":202507,"Reasoning":87489,"Failed":15398,"Retried":6051},
  {"day":"2026-10-26","Input":472300,"Cached":370227,"Output":221753,"Reasoning":92899,"Failed":13276,"Retried":4626},
  {"day":"2026-10-27","Input":456358,"Cached":366468,"Output":216506,"Reasoning":88261,"Failed":9041,"Retried":5714},
  {"day":"2026-10-28","Input":482495,"Cached":386711,"Output":226337,"Reasoning":92466,"Failed":8633,"Retried":9460},
  // … 8 more rows
];

export function Example() {
  return (
    <ChartCard number="34" title="Skyline" subtitle="Daily tokens, last 14 days" takeaway="Nov 5 was the busiest day" level="intermediate" replay>
      <SkylineChart
        data={data}
        x="day"
        y={["Input","Cached","Output","Reasoning"]}
        front={["Failed","Retried"]}
        format="compact"
      />
    </ChartCard>
  );
}

Props

<SkylineChart> takes these props. Rows marked Common work the same on every chart.

31 props

Prop
dataRequiredAn array of objects. Pass CSV, REST or database rows as they areobject[]
xRequiredPeriod field (date or category)string | (d, i) => any
yRequiredBack row fields stacked into each block. An array for wide tablesstring | (d, i) => number | string[]
seriesSeries field (long tables, back row only)string | (d, i) => string
frontFront row fields: a smaller outcome in the same unit as y (wide tables only)string | (d, i) => number | string[]
depthBack row depth as a share of the period slot, from 0.1 to 0.32. Values outside are pulled into the rangenumberDefault 0.32
wallValue grid on the back wall, with its labels on the rightbooleanDefault true
textureRows that get the hatch pattern, for print and for readers who cannot tell the grays apart"front" | "all" | "none"Default "front"
frontRateFront row total as a share of the back row: "label" in a label and the tooltip, "strip" as a rate strip under the floor, false hides itanyDefault "label"
dayFirstReads dates such as "05/03/2024", where either part could be the month, day first (5 March). Without it, a clear date in the column decides (25/03/2024), otherwise month first. true also reads "05.03.24" as day.month.yearboolean
heightCommonHeight in px. Without it, each chart picks a default that fits its widthnumber
widthCommonFixed width in px. Without it, the chart fits its parentnumber
highlightCommonItem to highlight. An index, a name, an array, or "max" | "min" | "first" | "last" | "none"number | string | (number|string)[] | "max" …
formatCommonValue format. "number" | "integer" | "percent" (values are already percents: 42 → "42%") | "ratio" (0.42 → "42%") | "compact" | "currency" (KRW for Korean locales, otherwise USD; for another currency pass Intl options with currency) | "decimal" or Intl.NumberFormat options"number" | "percent" | "compact" | …
unitCommonUnit appended to values. For example "%" or "ms"string
localeCommonLocale for numbers, dates and the text a chart writes itself (tooltips, screen reader sentences). "ko" or "ko-*" switches to Korean. Falls back to the ThemeProvider localestringDefault "en-US"
animateCommonEntry and update animationbooleanDefault true
durationCommonAnimation length (ms)number
easingCommonMotion curve"spring" | "easeOut" | "easeInOut" | "linear"Default "spring"
staggerCommonDelay between items as they enter (ms)number
reducedMotionCommon"auto" follows the OS setting. true always turns motion offanyDefault "auto"
animateOnViewCommonPlay when the chart scrolls into viewbooleanDefault true
showLabelsCommonLabels drawn on the chart (values and names)booleanDefault true
tooltipCommonShow tooltips. { template: "{label} · {value} · owner {owner}" } writes them: {label}, {value}, {series} or any other column of the row. \n starts a new line, and a line with an empty value is left outtooltipDefault true
syncIdCommonCharts with the same syncId show the hovered x together (line, area, bar, candlestick). <ChartGrid sync> sets one for every chart inside itstring
ariaLabelCommonName for screen readersstring
descriptionCommonDescription for screen readers. Built from the data when omittedstring
onHoverCommonRuns when hover or keyboard focus moves to another item(e: ChartEvent | null) => void
onClickCommonRuns on click, Enter or Space(e: ChartEvent) => void
replayKeyCommonReplays the entry animation when it changesstring | number
themeCommonChanges theme tokens for this chart onlyPartial<ChartTheme>