ComponentsData & statusChart
On this page
Data & status

Chart released

A chart you compose in the template: a scene that owns the axes and the colours, and marks inside it that draw your rows. The data stand beside the picture as a table.

  • Import@pacit/components/chart
  • Selectorspct-chart, pct-line, pct-axis, pct-chart-table

No APG pattern applies, and none is invented — the reasoning is under Accessibility ↓

Heat recovery on 28 October 2026
Time Supply air (°C) Fan power (W)
10/28/26, 12:00:00 AM18.2380
10/28/26, 3:00:00 AM17.7380
10/28/26, 12:00:00 PM22.4640
10/28/26, 1:00:00 PM22.6640
10/28/26, 6:00:00 PM20.6450
10/28/26, 11:00:00 PM18.4450
10/29/26, 12:00:00 AM18.3450

chart.demo.ts — the same file runs here and is highlighted under Code

Start here

Usage

One import per component, and the shortest possible use — the package itself is installed once, on the start page.

Import this component
import { PctChart, PctChartTable, PctLine } from '@pacit/components/chart';
ExportedWhat it is
PctAxisdirectiveunreleasedpct-axisA value axis the consumer configures by its unit. Without one, an axis takes its domain from the data in the window.
PctAxisLayoutinterfaceunreleasedA value axis as the scene lays it out: what a legend or a custom axis reads off axes().
PctChartcomponentunreleasedpct-chartA chart: a scene of scales and the marks drawn on them (0099). The marks — pct-line and those that follow it — stand inside it; the scene owns the scales, the window, the colours and the axes, and draws in layers: the data in one <svg>, the axes in a second, each on a compositor layer of its own, so a change of one does not repaint the other (0100).
PctChartTablecomponentunreleasedpct-chart-tableThe data of a chart as a table: the alternative a reader walks where the eye reads the picture (0103). It is the platform's <table> dressed by table[pctTable], captioned by the chart's name: a column for the position, and one per series shown, its unit in the header.
PctLinecomponentunreleasedpct-lineA series drawn as a line, a step or an area, on the chart it stands in (0099).
pctSeriesColorfunctionunreleasedReads a series' color: a whole number from 1 to 8, or its text, is a palette place; any other text is a CSS colour; anything else is no colour, and the series takes a free place.
PctSeriesSpecinterfaceunreleasedA series as a mark declares it.
PctChartScenedirectiveunreleasedThe scene as a mark sees it: what to draw with.
PctMarkdirectiveunreleasedA mark as the scene sees it.
<pct-chart #chart ariaLabel="Supply air, last 24 hours" timeZone="Europe/Warsaw" style="height: 320px">
  <pct-line [data]="readings" x="at" y="value" name="Supply air" unit="°C" curve="step" />
</pct-chart>
<pct-chart-table [chart]="chart" />
Examples

Every state, running

Each example is the component itself, not a screenshot. Open the code under any of them.

Midnight in Warsaw on 28 October 2026. */

const START = Date.UTC(2026, 9, 27, 23);

const outdoor = [ 4.1, 3.8, 3.4, 3.1, 2.9, 2.8, 3.2, 4.4, 6.1, 7.9, 9.4, 10.6, 11.3, 11.6, 11.2, 10.1, 8.6, 7.4, 6.5, 5.9, 5.4, 5, 4.7, 4.4, 4.2, ].map((value, hour) => ({ at: START + hour * HOUR, value }));

/** An area on a fixed axis

One series filled down to zero, on an axis pct-axis holds from 0 to 30 °C whatever the day brings, so two days side by side read on one scale. The fill is the series' palette colour mixed into nothing by the skin's --pct-chart-area-alpha.

API

What it reads, and what it writes back

Read from the source at build time — the types, the defaults and the lines of documentation are the library's own.

unreleased marks 19 of them: on main, and not in @pacit/components@0.2.0 — the next release names the version.

A chart: a scene of scales and the marks drawn on them (0099). The marks — pct-line and those that follow it — stand inside it; the scene owns the scales, the window, the colours and the axes, and draws in layers: the data in one <svg>, the axes in a second, each on a compositor layer of its own, so a change of one does not repaint the other (0100).

Inputs

NameTypeWhat it does
ariaLabelunreleaseddefault ''stringThe chart's name, which a reader announces with the picture, and which the data table takes as its caption. A picture with no name is a hole in the page: give this or ariaLabelledby.
ariaLabelledbyunreleaseddefault ''stringAs ariaLabel, for a name that already stands on the page: a heading over the chart.
descriptionunreleaseddefault ''stringWhat the picture shows, in a sentence or two — the trend, the range, what stands out. A reader hears it after the name.
xunreleaseddefault 'time''time' | 'linear'What the horizontal axis holds: instants (time, milliseconds since the epoch) or plain numbers (linear).
timeZoneunreleaseddefault undefinedstring | undefinedThe zone whose wall clock the time axis reads: its ticks fall on its whole hours and midnights. The runtime's own when unset — under SSR the server's, so pass it.
localeunreleaseddefault ''stringOverrides the application's LOCALE_ID for the numbers and times the axes write.
gridunreleaseddefault 'first''first' | 'all' | 'none'Which value axes draw grid lines: the first, every one, or none.
loadingunreleaseddefault falsebooleanThe data are on their way: the chart shows a skeleton of its shape and tells a reader it is busy.
windowtwo-wayunreleaseddefault nullPctWindow | nullThe stretch of the horizontal axis on show, or null for all the data have (0101). A change of the data changes it not.
hiddenSeriestwo-wayunreleaseddefault []readonly string[]The ids of the series not drawn. A hidden series keeps its colour and its axis, and adds nothing to the axis's domain.

Outputs

None of its own. The native events — (click), (focus), the form's submit — are the platform's, and two-way models above carry the rest.

What it writes on the element

The hooks a stylesheet or a test can hold. Stable by contract — the parts inventory gate fails the build when one disappears.

AttributeBound toNote
classpct-chart
aria-busyloading() || !measured() ? "true" : null
Styling

Parts to select, tokens to override

Two ways in, both stable. No class names to depend on.

Parts

In a test, PctChartHarness, PctLineHarness from @pacit/components/testing read a part by the name below and a state by its attribute — the harness gate holds the names to the package.

PartSelectorWhat it is
axis[data-pct-part="axis"]the <svg> of the axes, their lines, ticks and labels
data[data-pct-part="data"]the <svg> the marks draw into
grid[data-pct-part="grid"]the lines behind the data
message[data-pct-part="message"]the message of a chart with nothing to draw
plot[data-pct-part="plot"]the picture: the box with role="img" holding the layers
unit[data-pct-part="unit"]the unit written over a value axis
area[data-pct-part="area"]the area under a line, drawn by pct-line when area is set
series[data-pct-part="series"]a line's path, drawn by pct-line into the chart's data layer

Tokens

Every value the component reads, with the light and the dark default. Override a token at any scope and it cascades like any custom property.

TokenStyles
--pct-chart-area-alpha20%How much of the series colour fills the area under a line.
--pct-chart-axis-border{pct.border-strong}#64748bLine of an axis and its ticks.
--pct-chart-axis-fg{pct.text-muted}#334155 · #e2e8f0Labels of the ticks.
--pct-chart-axis-gap{pct.space.2}4pxGap between an axis and its labels.
--pct-chart-font-size{pct.font.size.sm}13pxFont size of the axis labels, the units and the empty message.
--pct-chart-grid-border{pct.border}#e2e8f0 · #334155Lines of the grid behind the data.
--pct-chart-message-fg{pct.text-muted}#334155 · #e2e8f0The message of a chart with nothing to draw.
--pct-chart-series-color-1{pct.series-1}#3848b7 · #708afbColour of series 1: its line, its area and its swatch.
--pct-chart-series-color-2{pct.series-2}#8f312c · #de655cColour of series 2: its line, its area and its swatch.
--pct-chart-series-color-3{pct.series-3}#029b98 · #42dcd8Colour of series 3: its line, its area and its swatch.
--pct-chart-series-color-4{pct.series-4}#af8002 · #f6b925Colour of series 4: its line, its area and its swatch.
--pct-chart-series-color-5{pct.series-5}#773c6f · #c36cb7Colour of series 5: its line, its area and its swatch.
--pct-chart-series-color-6{pct.series-6}#058066 · #1cbf9aColour of series 6: its line, its area and its swatch.
--pct-chart-series-color-7{pct.series-7}#7a73d2 · #a7a5f3Colour of series 7: its line, its area and its swatch.
--pct-chart-series-color-8{pct.series-8}#ab501f · #ea8e62Colour of series 8: its line, its area and its swatch.
--pct-chart-series-width2pxWidth of a line.
--pct-chart-unit-fg{pct.text}#0f172a · #ffffffThe unit written over a value axis.

Theming

A brand override in a few declarations. Scope it to a theme attribute, a page or one container — the component never notices.

[data-theme='brand'] {
  --pct-series-1: #0f766e;
  --pct-chart-grid-border: #ccfbf1;
  --pct-chart-series-width: 3px;
}
Heat recovery on 28 October 2026
Time Supply air (°C) Fan power (W)
10/28/26, 12:00:00 AM18.2380
10/28/26, 3:00:00 AM17.7380
10/28/26, 12:00:00 PM22.4640
10/28/26, 1:00:00 PM22.6640
10/28/26, 6:00:00 PM20.6450
10/28/26, 11:00:00 PM18.4450
10/29/26, 12:00:00 AM18.3450
Accessibility

Measured, not assumed

Pattern: none — the APG has no pattern for a chart. The scene follows the WAI tutorial on complex images: a picture role="img" with a name and a description, and the data as a table beside it. Named in the class JSDoc.

Keyboard

keyeffecttest
Tabpasses the picture by: it is a named image and no stop; the table's frame is one when it scrollsapps/sandbox-e2e/src/chart.spec.ts — "the drawing is a picture named by the chart, described, its layers hidden"

No key does anything to the chart yet: the cursor that walks it from the keyboard comes with the next phase, as the platform's range (0103).

What is measured, and what is not yet

Every line is a test in three engines or a gate on every commit. The gaps are listed the same way, each with the requirement that will close it.

MeasuredARIA pattern named in the class JSDoclibs/components/chart/src/chart.ts
MeasuredKeyboard map tested key by keythe table above — apps/sandbox-e2e/src/chart.spec.ts
Measuredaxe audit on the component's own sandbox viewapps/sandbox-e2e/src/a11y.spec.ts — /chart in SBX_ROUTES
MeasuredVisual screenshotapps/sandbox-e2e/src/visual.spec.ts — chart-area and chart-area-rtl
Measuredforced-colors: active — no state carried by colour alonelibs/components/chart/src/chart.scss, line.scss — the lines and the axes in CanvasText, no area filled, and each palette place drawn with a dash of its own (0102's second channel). The reading: apps/sandbox-e2e/src/forced-colors.spec.ts
n/aprefers-reduced-motion — duration from a tokennot applicable — no motion at all
n/aTouch target ≥ 24×24 px outrightnot applicable — nothing in a chart takes a press yet; the cursor's range arrives with its own target
MeasuredSize axis aligned to --pct-control-height-*deliberately not: a chart is not a control, and its height is the consumer's
MeasuredDensity axislibs/tokens/src/component.chart.json — the gap between an axis and its labels stands on space.2, which the compact scope re-points
MeasuredRTL — no physical properties + a dir="rtl" screenshotlogical properties only, held by check-styles; the axes run left to right in every direction (direction: ltr on the layers), the screenshot chart-area-rtl
MeasuredSSR + hydration with no NG05xxapps/sandbox-e2e/src/hydration.spec.ts — /chart in SBX_ROUTES; the server draws the skeleton, the browser draws after measuring
n/aFormsnot applicable — a chart holds no value a form owns
MeasuredParts registered in the inventorylibs/components/parts.snapshot.md — plot, data, grid, axis, unit, message, series, area
MeasuredTokens registered + an entry in contrast.policy.jsonlibs/tokens/src/contrast.policy.json — the eight series on both surfaces (UI error, 16), the tick labels, the unit and the message (AA error), the axis line and the grid (UI warn); libs/tokens/tokens.snapshot.md
MeasuredStrings through PCT_TEXTSlibs/components/core/src/texts.ts — chartEmpty, chartAllHidden, chartGap, chartOn, chartOff, chartTime, chartPosition
MeasuredEntrypoint size budgetlibs/components/size.snapshot.md — the ./chart row
MeasuredScreen-reader logRead 2026-10-30 from docs/acr/at/ — three readers, whole sandbox, and the instrument cannot reach this component: the picture is a named image and no stop, the table beside it does not scroll, so /chart walks the demo's theme, size and direction groups and then out to the browser (NVDA Size, grouping, Orca Theme · panel · light · selected radio button., VoiceOver light selected radio button, 1 of 2 Theme radio group). Nothing in the three logs reads the picture's name, its description or the table, which is what a reader reading the page would hear and is not evidence of it — the cursor's range, the next phase, is the chart's first stop (0103)
MeasuredA docs page with live examplesapps/sandbox/src/app/views/chart/ (the sandbox view). The published site: /components/chart, its demo's source in the code tab
MeasuredUnit + mutationlibs/components/chart/src/chart.spec.ts, model/*.spec.ts; libs/components/mutation.snapshot.md — the chart's files, measured by a narrow run
Evidence

What the repository holds for this component

Read at build time from the tracked snapshots, not typed here.

91.76%mutation score412 of 449 mutants killed
11 × 3e2e cases × enginesplus the shared audits: axe, forced colors, preferences, size, hydration
21colour pairs measuredlight and dark, at every token build
2visual baselinesChromium, the sandbox's own pictures
47elements in the preview2 listeners · 1 render to settle · 27296 µs in jsdom, held exactly but for the clock

Known limitations

  • No cursor, zoom, pan or legend yet. They are the next phases of the plan (1.11); the window and the hidden series are models today, so an application can already set both.
  • A series is told apart by its colour on the picture. Under forced colours each place has a dash of its own; the second channel everywhere else — a dash and a marker per series — arrives with the legend, and until then the table names every series.
  • The rows must come ordered by x. A row out of order is reported in development and drawn where it stands.
  • The width follows the container, the height is the consumer's — 16 / 9 of the width when neither height nor aspect ratio is set.
  • Labels are measured with a canvas in the axis's font. A skin that sets font-variant-numeric on an axis changes widths the canvas does not see.