Skip to content

Releases: ganapativs/microcharts

@microcharts/react@0.8.0

Choose a tag to compare

@github-actions github-actions released this 22 Jul 18:41
6f0adc2

Minor Changes

  • #50
    7e25243 Thanks
    @ganapativs! - Robustness, consistency and legibility pass across all 106 charts.
    Most of this is invisible if your charts already looked right; the parts that aren't are listed under "behaviour
    changes" below.

    Fixed: charts that could hang, crash or vanish. These were all reachable from ordinary props, and each now has a
    regression test.

    • TapeGauge never finished rendering when value was large enough (about 1e16 and up) that its tick step fell below
      one ULP — the tick loop stopped advancing and spun until the tab died. Ticks are now counted, not accumulated, and
      capped.
    • TraceFold hung forever on a parent graph containing a cycle (a span parented to itself, or any two-span loop).
      The critical-path walk now tracks what it has visited.
    • Marks silently disappeared whenever a scale's domain span was infinite — including finite data whose arithmetic
      overflows, such as a Waterfall running total past 1e308. An infinite span yields a slope of exactly 0, which
      passed the old finite check and then produced NaN coordinates. scaleLinear now validates the span itself.
    • PictogramRow's total, Progress's segments, Thermometer's ticks and FoldedDayBand's bins allocated
      one node per unit with no ceiling, so a raw count passed by mistake could exhaust memory. All four now saturate.
    • Charts derived bounds with Math.max(...array), which throws RangeError past roughly 125k elements. Every such
      site now uses a stack-safe fold, so a long series renders instead of crashing.
    • CyclePlot threw on period={NaN} (an empty input field yields exactly that), HeartbeatBlip divided by a zero or
      non-finite window and could put "in the last NaN minutes" into its accessible name, and ActivityGrid accepted an
      unvalidated domain and painted fill-opacity="NaN".

    Fixed: accessibility and inline seating.

    • Labels sitting on a solid mark (HeatCell's upper steps, TimeInRange zones, EventTimeline spans) were rendering
      in body ink instead of on-fill ink — dark-on-dark on the strongest cells. The stylesheet always won over the fill
      attribute these charts were setting, so the intended colour never applied.
    • A decorative interactive chart (summary={false} or live={false}) lost its inline baseline seat, which also
      detached its readout chip and hit box from the mark by up to a seat's height. The live region — which carries the
      seat — now stays mounted and is silenced by emptying it.
    • Visible readout chips and idle labels on MicroDonut, GradeProfile, GardenGrid, QuantileDots and
      EventTimeline composed English inline instead of going through the strings contract, so they stayed English in a
      localized app.

    Behaviour changes worth knowing.

    • Interactive charts now claim only the horizontal touch gesture (touch-action: pan-y pinch-zoom) rather than all
      gestures, so a chart no longer traps the page scroll under a finger; a scroll that takes over now clears the
      highlight.
    • HeatCell and HeatStrip build their step colour by mixing the accent into the band rather than ramping opacity,
      so the steps stay distinct on dark surfaces. The fill moved to the stylesheet, which restores the high-contrast
      (forced-colors) mapping that an inline fill was overriding.
    • DotPlot, Dumbbell and Slope scale their label type and row-name budget with the chart's size instead of using
      a fixed 6-unit font and 6-character cap, so figure-scale charts read properly. Slope steps its type down until the
      labels fit rather than dropping them.

    Added.

    • strings on the static Sparkline, SparkBar and MusicStaff. Their generated accessible name was English-only
      with no override short of writing the whole summary yourself.
    • labelAt on StackedArea, so endpoint share labels can track a scrubbed column.

    Breaking (pre-1.0), types only. SummaryStrings gained quantileDotsChip, quantileDotsOdds and
    timelineFallback. If you supply your own strings object for QuantileDots or EventTimeline, add those three
    templates; every other consumer is unaffected.

@microcharts/react@0.7.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 16:57
21dc605

Minor Changes

  • #46
    5310b1d Thanks
    @ganapativs! - Breaking (pre-1.0 API freeze): prop-name consistency pass. Same
    prop name must mean the same thing across the catalog. Collisions found in the pre-1.0 audit are renamed; behaviour is
    unchanged — pass the new name, get identical output.

    Chart Old New Why
    MusicStaff range mode range is the peer-band recipe on BenchmarkStrip
    PercentileTrace bands showBands bands is qualitative thresholds on Bullet
    FoldedDayBand bands percentiles ⇑; avoided levels (already DepthWedge / GradedBand)
    Waterfall start open start collided with PolarClock / ActivityGrid; avoided from (Delta)
    PolarClock start origin
    ActivityGrid start anchor
    OrbitStatus alert threshold Decision cutoffs spell threshold elsewhere
    CyclePlot trend: "line" | "none" trend?: boolean Matches MicroScatter

    Also in this release: chart polish (label defaults, annotation hosts, motion safety); interactive scrub/HOP
    re-render wins (picker SVG cache, Sparkline memo overlays, EnsembleGhosts DOM hop); size sync tolerates over-budget
    size-limit --json; marketing band ~2–7 kB interactive · ~1–4 kB static.

    Non-breaking: MicroDatum.formatted + readout on interactive pickers (hide in-chart chip, render the chart's
    own display string elsewhere); empty-state seat on 11 charts; LiveRegion on dice-pips / tally-marks / hourglass;
    data-mc-ink on Slope + forced-colors for data-mc-status; Delta / Bullet / ActivityGrid / TokenConfidence English
    via strings-*; core clamp / lastFinite reuse; docs registry + MDX synced (catalog.json / llms surfaces
    regenerate); gallery collections + comparison/SEO doc pages; CI splits core/dom/browser projects.

    Examples microsites migrate after publish — not in this release.

@microcharts/react@0.6.0

Choose a tag to compare

@github-actions github-actions released this 20 Jul 04:13
499e0f5

Minor Changes

  • #42
    86dcf8e Thanks
    @ganapativs! - Breaking (pre-1.0 API freeze): remove props that were declared but
    never read.
    Each of these was accepted by the type signature and documented, but had no effect on rendering,
    geometry, or the accessible summary — passing one did nothing. They are gone rather than fixed where the chart had no
    number and no announcement to apply them to.

    Removed outright:

    • DualSparklinecompareLabel. Never reached the summary or any announcement; the compare series is described
      positionally.
    • DualWindowMeterdamping. Documented as "ballistics for the live entry", but the interactive entry never read
      it; its motion comes from the shared entrance engine.

    Narrowed to the interactive entry only (still available on …/interactive, removed from the static default export,
    where they did nothing):

    • PercentileTraceunit. Names the reading in the hover/focus announcement; the static entry announces
      percentiles, not individual readings.
    • CalendarStrip, EventRaster, MicroScatter, TokenConfidencelocale (and format on the latter three).
      These statics render lane names, marks and text, never a formatted number; only the readout does.

    Wired up instead of removed:

    • ConfusionGridformat / locale now reach the accuracy gutter label, which previously hard-coded
      Math.round(v * 100) + "%". Percent formatting goes through the shared cached makeFormatter, so the one number
      this chart renders is locale-aware and honors a custom format. Default output is unchanged. The right-hand gutter
      is measured from the produced string, so a locale that widens the label still fits inside the viewBox.
  • #42
    86dcf8e Thanks
    @ganapativs! - One interaction contract across the catalog. 103 of 106 charts now
    share a single picker kernel, so a consumer's handler reads the same on a Sparkline, an ActivityGrid or a MicroDonut.

    This is a pre-1.0 minor with breaking changes — permitted under semver for a 0.x package, but read the Breaking
    section below before upgrading.

    Picker charts (84 multi-unit entries) gain four props on their …/interactive entry:

    • onActive(datum | null) — the hovered / keyboard-focused unit changed
    • onSelect(datum | null) — a unit was activated by click, tap, Enter or Space
    • selectedIndex / defaultSelectedIndex — controlled and uncontrolled selection

    The payload is one shape everywhere: MicroDatum = { index, value, label? }. index identifies the navigable unit
    (documented per chart — a data index where the chart is 1:1 with data, otherwise a unit position). value is that
    unit's primary encoded number, which may be derived — a segment's share, a run's duration, a signed gap, a bin's
    count — and is null where the unit encodes nothing.

    Behavior, everywhere: pointer scrub sets the active unit; click or tap selects and pins it (surviving blur);
    Enter/Space select; Escape clears; re-selecting the same unit clears it; arrows rove on both axes;
    Home/End jump to the ends. On touch, a tap pins and a drag scrubs. Still one listener on the wrapper and pure math
    — never a listener per data point.

    Scalar charts (19 single-unit entries) take the lean half: onSelect only, with { index: 0, value, label? }.
    There is nothing to rove between and no pinned state, so they don't pretend otherwise.

    Deliberately excluded, and documented as such: MinimapStrip is a viewport-window slider and keeps its
    onWindowChange([lo, hi]) range payload; TokenConfidence moves real focus to per-token spans so a screen reader
    reads the text in flow; WindBarb ships no interactive entry at all.

    Breaking

    • Removed onPointFocus (Sparkline, SparkBar, and Waveform's two-argument (index, fraction) form) and
      onRunFocus (StreakSpark). Use onActive, which reports { index, value, label? }. Waveform's fraction has
      no equivalent — the datum reports the bucket index and its peak.
    • PictogramRow's interactive strings prop now takes PictogramStrings, which adds a pictogramUnit template
      so roving announces each unit (it previously announced nothing — an accessibility gap). A custom ScalarStrings
      object needs that one template added. The static entry is unchanged.
    • Charts whose interactive entry hand-rolled geometry that disagreed with their static now mirror the static exactly.
      This corrects rendered output where the two had drifted — most visibly EventRaster (its interactive entry rendered
      43% shorter than its static) and Hypnogram (default size and row-label gutter). Overlays, focus rings and
      hit-tests now land on the marks.
    • Six charts that silently ignored ArrowUp/ArrowDown now accept them as prev/next, matching the rest of the
      catalog.

    Fixed

    • Nine interactive entries dropped consumer children, silently discarding annotations (<Threshold>, <Marker>,
      …).
    • TreeRings' pinned mark had its stroke width overridden by the stylesheet.
    • SproutRow ignored summary={false}; CometTrail mis-indexed its readout when the series contained non-finite
      values; ControlStrip had an index/value misalignment on gappy data; BalanceBeam ignored
      width/height/shape.
    • Honeycomb announced bare numerals to screen readers; it now uses a proper honeycombCell template.
    • CyclePlot's within-slot drill-down (/ over the observations in the focused slot) is restored.
  • #42
    86dcf8e Thanks
    @ganapativs! - Interactive charts now forward className and style to their
    wrapper and size the inner SVG from one shared FILL constant, so the SVG always fills the interactive <span>
    hover math, crosshairs, and focus rings stay locked to the cursor instead of drifting when the chart is scaled by a
    consumer. Passing style merges (it no longer clobbers the wrapper's display/position/line-height), and
    className composes with the chart's own class. No change at rest — static output is byte-identical.

    Formatters are also hardened against IEEE float noise: makeFormatter snaps its input to 12 significant digits before
    handing it to Intl or a custom format function, so internally-derived values (value - target
    -3.5999999999999943) render clean without touching real precision.

  • #42
    86dcf8e Thanks
    @ganapativs! - Prop-name grammar freeze. Non-negotiable
    #4 says the same prop name means the same thing on every chart —
    an API audit before 1.0 found several names carrying two meanings across the catalog. Each is now spelled one way.

    This is a pre-1.0 minor with breaking renames — permitted under semver for a 0.x package. Every rename below is
    mechanical: pass the new name, get identical output. No rendering, geometry, or accessibility behaviour changed.

    Chart Old New Why
    ABStrips labels seriesLabels labels is a boolean show/hide toggle on 11 other charts
    ShiftHistogram labels seriesLabels
    SpreadBand labels seriesLabels
    Waterfall total totalBar total is a number denominator on 5 other charts
    ChangePoint max maxItems max is a scale denominator on Progress (mirrors <progress>)
    DataDiff max maxItems
    ParetoStrip max maxItems
    CalibrationStrip variant mode 13 charts already spelled this mode
    Hypnogram variant mode
    MinimapStrip variant mode
    Ohlc variant mode
    StackedArea variant mode
    Waveform variant mode
    WindBarb variant mode
    DataDiff sort order sort/order were two names for one concept
    MiniBar sort order ⇑ ...
Read more

@microcharts/react@0.5.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 07:16
6823429

Minor Changes

  • #40
    427c25a Thanks
    @ganapativs! - defineTheme() gains a strokeWidth token — pin the base data
    stroke weight (--mc-stroke-width) alongside the other geometry/type tokens. A number is stringified as-is (not
    twinned into the dark variant). Additive and identity at its default.

Patch Changes

  • #40
    427c25a Thanks
    @ganapativs! - Floor charts now seat their baseline flush with the box bottom so they
    align on the text baseline when rendered inline: SparkBar (bar mode), StackedArea, DepthWedge, and GradeProfile. These
    marks are flat fill edges (crispEdges rects / stroke-free, dot-free areas), so filling to the box bottom bleeds
    nothing; the top pad is untouched. SparkBar win-loss keeps its symmetric mid-line inset. No API change.

@microcharts/react@0.4.0

Choose a tag to compare

@github-actions github-actions released this 15 Jul 15:36
69ad3b6

Minor Changes

  • #38 0796194 Thanks @ganapativs! - Pre-launch quality pass — expanded annotation hosting, correctness fixes, and internal
    consolidation. All changes are backward-compatible at their defaults; existing charts render
    identically.

    Annotations on 17 value-series charts (was 2). Threshold, TargetZone, Marker, and
    Callout (from @microcharts/react/annotations) can now be passed as children to Sparkline,
    SparkBar, MiniBar, CyclePlot, CitySkyline, ChangePoint, DualSparkline, SpreadBand, ForecastCone,
    ControlStrip, QueueDepth, BurnChart, Waterfall, PercentileTrace, RetentionCurve, WinProbWorm, and
    ErrorBudget. Every host resolves annotation coordinates through the same shared path, so label
    sizing and containment are identical across charts.

    Correctness fixes:

    • weekGrid no longer crashes on a NaN week count or hangs on Infinity — non-finite inputs
      clamp to a single row.
    • divergingStack no longer emits NaN segments when neutralIndex is out of range — an
      out-of-range index is treated as "no neutral".
    • stepOpacity no longer divides by zero at steps === 1 (was Infinity).
    • The interactive MinimapStrip slider always has an accessible name, even when summary={false}
      and no title is given (falls back to the localized viewport sentence).
    • Consumer-supplied annotation labels are now truncated to fit the chart's box, closing the one
      text path that could paint outside the viewBox.
    • The trend-percentage in generated summaries now routes through the locale formatter, so
      non-ASCII-digit locales no longer mix numeral systems in one sentence.

    Summary strings (i18n):

    • TokenConfidence's empty-state string moved from the shared noData key to its own noTokens
      key on SummaryStrings, so it is no longer silently overridden by other charts' noData. If
      you supply custom strings to TokenConfidence, rename noDatanoTokens. Rendered output is
      unchanged ("No tokens.").
    • compass8 is now canonically lowercase across WindBarb and StationGlyph (WindBarb capitalizes
      its sentence-initial octant in its own template), fixing lowercase sentence starts when both
      charts' strings were merged. Standalone output is unchanged.
    • heartbeatWindow now reports clean multiples of an hour as hours ("2 hours") instead of minutes
      ("120 minutes").

    Internally, duplicated label-sizing, live-region, gutter, and summary-resolution logic was
    consolidated into shared helpers, and several unused internal exports were removed. No public
    export, prop, or chart was added or removed.

  • #38 0796194 Thanks @ganapativs! - Deepen the theming surface — all additive, backward-compatible, and identity at
    their defaults (no visual change to existing charts):

    • defineTheme() — a new opt-in @microcharts/react/theme subpath (zero
      runtime deps, ~2 kB gzip, tree-shaken when unused). Give it one brand accent
      and it derives a harmonized, colour-blind-safe categorical palette and
      hand-tuned-style dark twins in-house in OKLCH — never moving the
      positive/negative hues off their CVD-safe split. Extend a preset, pin any
      token, pass an explicit palette, or extend() a variant; returns
      vars / style / css(selector) (with a prefers-color-scheme: dark block).
    • New tokens--mc-font-numeric (a dedicated face for figures, tracks
      --mc-font by default), --mc-label-weight, and --mc-density (one scalar
      that scales stroke weight, label size, and small-multiple gap together for
      compact vs comfortable layouts; the plot box is untouched).
    • colors prop — per-instance series colours on the categorical charts
      (SegmentedBar, StackedArea, PartitionStrip, Hypnogram, MicroDonut),
      overriding --mc-cat-* just there.
    • print and eink presets — output-context token bundles for paper and
      grayscale e-paper, answering data-mc-theme / data-mc-preset like the rest.

    The MicrochartCommonProps shared-grammar type is now enforced as a single
    source of truth: a guard test holds every chart's shared props to its canonical
    types.

Patch Changes

  • #38 0796194 Thanks @ganapativs! - Ship a minified styles.css. @microcharts/react/styles.css (and the
    per-chart styles/*.css escape-hatch files) now resolve to minified copies in
    dist/ — the shared stylesheet drops from ~8.7 kB to ~2.8 kB gzip. The
    minifier only strips comments and whitespace (no rule merging), so @layer
    membership and cascade order are unchanged. The repo-root styles.css stays the
    unminified source of truth; no API or visual changes.

  • #38 0796194 Thanks @ganapativs! - Improve npm and search discoverability: expand keywords, add a funding link
    (GitHub Sponsors), and a structured author (name/email/url). Metadata only — no
    API, runtime, or visual changes.

@microcharts/react@0.3.0

Choose a tag to compare

@github-actions github-actions released this 14 Jul 19:40
d7f8e31

Minor Changes

  • #23 c5854a0 Thanks @ganapativs! - Deepen the theming surface — all additive, backward-compatible, and identity at
    their defaults (no visual change to existing charts):

    • defineTheme() — a new opt-in @microcharts/react/theme subpath (zero
      runtime deps, ~2 kB gzip, tree-shaken when unused). Give it one brand accent
      and it derives a harmonized, colour-blind-safe categorical palette and
      hand-tuned-style dark twins in-house in OKLCH — never moving the
      positive/negative hues off their CVD-safe split. Extend a preset, pin any
      token, pass an explicit palette, or extend() a variant; returns
      vars / style / css(selector) (with a prefers-color-scheme: dark block).
    • New tokens--mc-font-numeric (a dedicated face for figures, tracks
      --mc-font by default), --mc-label-weight, and --mc-density (one scalar
      that scales stroke weight, label size, and small-multiple gap together for
      compact vs comfortable layouts; the plot box is untouched).
    • colors prop — per-instance series colours on the categorical charts
      (SegmentedBar, StackedArea, PartitionStrip, Hypnogram, MicroDonut),
      overriding --mc-cat-* just there.
    • print and eink presets — output-context token bundles for paper and
      grayscale e-paper, answering data-mc-theme / data-mc-preset like the rest.

    The MicrochartCommonProps shared-grammar type is now enforced as a single
    source of truth: a guard test holds every chart's shared props to its canonical
    types.

Patch Changes

  • #23 c5854a0 Thanks @ganapativs! - Ship a minified styles.css. @microcharts/react/styles.css (and the
    per-chart styles/*.css escape-hatch files) now resolve to minified copies in
    dist/ — the shared stylesheet drops from ~8.7 kB to ~2.8 kB gzip. The
    minifier only strips comments and whitespace (no rule merging), so @layer
    membership and cascade order are unchanged. The repo-root styles.css stays the
    unminified source of truth; no API or visual changes.

  • #23 c5854a0 Thanks @ganapativs! - Improve npm and search discoverability: expand keywords, add a funding link
    (GitHub Sponsors), and a structured author (name/email/url). Metadata only — no
    API, runtime, or visual changes.

@microcharts/react@0.2.1

Choose a tag to compare

@github-actions github-actions released this 13 Jul 18:05
d21b4d8

Patch Changes

  • 3955472: Fix subpath resolution on StackBlitz and some CDNs. Every export now includes a
    default condition alongside import, so imports like
    @microcharts/react/sparkline/interactive resolve on loose resolvers that don't
    request the import condition (they previously failed with "file does not
    exist"). Also ship a minified dist — smaller install, and correct bytes for
    consumers that import the ESM directly (Deno, CDNs). No API changes.

@microcharts/react@0.2.0

Choose a tag to compare

@github-actions github-actions released this 13 Jul 16:40
543be24

Minor Changes

  • ef2df66: Ship the full catalog: 106 stable chart types (34 core, 26 decision, 23 expressive, 23
    frontier), each with a static React Server Component entry and an /interactive twin, an
    auto-generated accessible summary, opt-in mount animation via @microcharts/react/motion, and
    a CI-gated size budget. Zero runtime dependencies; one shared styles.css. First public release
    usable end to end.

@microcharts/react@0.1.0

Choose a tag to compare

@github-actions github-actions released this 09 Jul 16:28
c6f4c49

Minor Changes

  • 69eb408: Batch 1 wave 1 — four scalar glyph charts, each with a static RSC-safe entry and an
    interactive /interactive entry:

    • TrendArrow (./trend-arrow) — direction glyph (arrow/triangle/chevron), flatBand
      noise floor, showValue gutter, positive polarity.
    • StatusDot (./status-dot) — five paired shape+color states (ok/warn/error/off/busy),
      pulse halo, extensible states vocabulary.
    • HeatCell (./heat-cell) — one calibrated color step (shared 5-step ramp), shared
      shape cell vocabulary, optional centered value label.
    • Progress (./progress) — zero-anchored bar + direct percent label, segments
      stepped mode, honest >100% clamp (label carries the truth), burn-down wording.

    Also: EN locale dictionary is now composed from per-shape modules (EN_SERIES +
    EN_SCALAR) so each chart bundles only its own summary templates; new ScalarStrings
    keys scalarDir, flatChange, status, level, progress, remaining, stepsDone.

  • 16a3914: Batch 1 wave 2 — five tick/cell strip charts, each with static + /interactive entries:

    • RugStrip (./rug-strip) — raw observations as ticks, density via opacity tiers,
      highlight for "you are here", never downsamples.
    • MiniBar (./mini-bar) — zero-anchored categorical bars, honest data order, sort,
      highlight, signed data with positive polarity.
    • PictogramRow (./pictogram-row) — countable units (●●●○○), true fractional units as
      circular segments (no clipPath ids), renderPoint escape hatch.
    • Seismogram (./seismogram) — event density/intensity ticks, barcode mode,
      spike-preserving max-per-bucket downsampling, summaries always from raw values.
    • HeatStrip (./heat-strip) — 1×N calibrated intensity cells sharing ActivityGrid's
      vocabulary; empty ≠ zero; density-adaptive gaps.

    Kernel fix: scaleLinear now treats denormal-span domains as degenerate (the slope
    overflowed to ±Infinity and 0 × Infinity poisoned coordinates with NaN). New summary
    template modules: EN_CATEGORY, EN_DIST, EN_SLOTS (slot-empty announcements — also
    fixes the latent "No data.." double period in ActivityGrid's null-cell announcement).

  • 7bb9ee1: Batch 1 wave 3 — five categorical/relationship charts, each with static + /interactive
    entries:

    • DotPlot (./dot-plot) — named values on one scale; stem flips to a zero-anchored
      magnitude read; deterministic label drop-out and coincident-dot de-overlap.
    • Dumbbell (./dumbbell) — hollow→filled before/after per row; positive valence for
      changes (documented: drop it for ranges).
    • PairedBars (./paired-bars) — actual vs reference on ONE zero-anchored domain;
      grouped or overlay-ghost modes; ref muted by opacity AND width.
    • Slope (./slope) — two aligned columns, one y-domain; neutral until positive;
      greedy endpoint-label dedup; dashed "incomplete" stubs for missing ends.
    • MicroScatter (./micro-scatter) — 2-D position + Pearson r in the summary (claim and
      evidence travel together); linear-only least-squares trend; focal point.

    New summary template modules: EN_PAIRED (fromTo/pairs/slopes…), EN_SCATTER
    (count + relationship tiers). Pluralization fixes in categories/pairs templates.

  • 0c92adc: Batch 1 wave 4 — the annotations layer + seven part-to-whole/staged charts:

    • @microcharts/react/annotations<Threshold>, <TargetZone>, <Marker celebrate?>,
      <Callout> as declarative children; hosts pay only a tiny walker (mark renderers ship
      with this entry). Sparkline/SparkBar retrofitted as hosts.
    • SegmentedBar (./segmented-bar) — composition with a labeled Other rollup +
      largest-remainder percents.
    • HistogramStrip (./histogram-strip) — ≤ 12 uniform bins from raw observations;
      value highlight.
    • MicroBox (./micro-box) — five-number box; min-max or tukey whiskers; refuses fake
      boxes (< 5 raw values → dots; non-monotonic stats → refused).
    • ProgressRing (./progress-ring) — fixed 12-o'clock arc, butt caps, sweep
      countdown mode; threshold-crossing announcements.
    • MicroDonut (./micro-donut) — ≤ 4 wedges + Other; decorative ornament mode.
    • Funnel (./funnel) — stepped columns + retained-share slats; rate-of-first mode;
      inversion notes.
    • LikertStrip (./likert-strip) — diverging valence bar; split/omit neutral (never
      silently dropped); ends/net labels.

    New EN_COMPOSITION string module + distribution templates (binAt, distribution,
    fiveNum, boxStat). Sparkline budget 3.65/4.65 kB pending sign-off (annotations walker).

  • 15b0a12: Batch 1 wave 5 — six composed-series charts:

    • Waterfall (./waterfall) — signed deltas bridging into a total; zero-anchored total
      bar on by default; positive="down" for cost bridges.
    • BumpStrip (./bump-strip) — rank trajectory on an inverted ordinal scale (#1 on
      top); change dots; "#5" → "#1" end labels; null = unranked gap.
    • DualSparkline (./dual-sparkline) — series vs benchmark on ONE shared domain;
      dashed/thinner/neutral reference; coincident-endpoint dedup. curve="step" renders
      as linear (documented — keeps the entry inside the 3 kB hard cap).
    • StackedArea (./stacked-area) — share-normalized stack, ≤ 3 series;
      variant="ridge" skin; order="asc".
    • Ohlc (./ohlc) — candle/bars price action, ≤ 20 periods rendered (never averaged);
      valence + geometry encode direction.
    • Horizon (./horizon) — 2–3 fold horizon band; mirror/offset modes; authored
      baseline; summaries read the unfolded values.

    EN_COMPOSED split into per-chart string modules (EN_FLOW/EN_VS/EN_STACK/
    EN_OHLC) so no chart pays for another family's templates. Horizon domain prop is
    now actually wired to the geometry (was silently ignored).

  • cf601c5: Batch 1 wave 6 — the two date-structured charts, completing the core-29:

    • CalendarStrip (./calendar-strip) — the last few weeks day by day on a real
      weeks × 7 UTC calendar grid; value days step the shared intensity ramp, zero days
      show the track, no-record days render as outlines (empty ≠ zero), future days are
      blank. 2-D keyboard nav announces real calendar days.
    • EventTimeline (./event-timeline) — spans + point-event diamonds on one linear
      time axis; window clipping is flat-cut honest; coverage merges intervals (overlaps
      never double-count); authored now tick.

    New core: cached makeDateFormatter (UTC-forced for calendar charts) + EN_CALENDAR/
    EN_TIMELINE string modules. core/calendar split into calendar (day parsing) and
    calendar-grid (week-grid/year math) so ActivityGrid stops carrying grid math it never
    calls. Fixed: normalizeShares could emit a negative share when the float remainder
    landed on a denormal-tiny entry (property-test counterexample) — remainder now folds
    into the largest share.

  • 425f933: Batch 2 wave 1 — five decision micrographs, each with a static RSC-safe entry and an
    interactive /interactive entry:

    • CoverageStrip (./coverage-strip) — presence/absence on a time strip; null (no
      measurement) is hollow, 0 (a measured zero) is filled, so absence never masquerades
      as zero. expected makes trailing missingness count; mode="intensity" shades measured
      cells; label="percent".
    • BenchmarkStrip (./benchmark-strip) — a focal dot against the peers' empirical
      quantile bands; the stated percentile uses a mid-rank rule; small samples fall back to
      min–max. range, median, label, positive.
    • PercentileLadder (./percentile-ladder) — p50/p90/p99 as graduated ticks on a
      zero-anchored track; scale="log" renders an in-chart log tag (never silent);
      ps, label, dots.
    • GradedBand (./graded-band) — nested central intervals graded by opacity, never a bar
      from zero; levels (1–3), value dot, softEdge, label="median".
    • IconArray (./icon-array) — one rate made countable in a fixed N-unit grid with the
      denominator visible; no partial-unit fills (sub-unit rates are flagged); of (10/20/100),
      label, shape, positive.

    Also: new per-family summary-string modules EN_COVERAGE, EN_QUANTILE, EN_FREQ (each
    chart bundles only its own templates) and the corresponding SummaryStrings keys.

  • 425f933: Batch 2 wave 2 — BurnChart (./burn-chart), static + /interactive entries:

    • BurnChart — a dashed plan line (full length to the deadline), the solid
      actual line to today, a today tick, and a dotted projection whose slope
      is a linear fit over the last max(2, ⌈today/3⌉) actual points — provisional by
      construction, never a smoothed or optimistic curve. label="gap" states the
      signed schedule landing vs the deadline (e.g. +2 d), colored by valence with
      the sign in text. mode="down" | "up", projection={false} for retrospectives.
      A flattened recent burn never reaches zero: no landing, and the summary says
      "not finishing at the current pace" outright.

    New EN_BURN summary module (burn, burnNoPlan, burnLanding, burnFlatlined,
    burnAt, burnAtProjected, burnRemain/burnDone).

  • 425f933: Batch 2 wave 2 — ControlStrip (./control-strip), static + /interactive entries:

    • ControlStrip — a Shewhart individuals control chart: the band is center ± 3σ̂
      where σ̂ = mean moving range ÷ 1.128 (the individuals estimator, stated —
      sample SD is not used, it inflates limits under drift). In-control points are bare
      vertices; only out-of-control points are marked (ringed, negative). limits="sigma" | "percentile" (empirical p0.135/p99.865 for skew), baseline for a golden-period
      center, rules="we" for the enumerated Western Electric subset (WE-1/2/4, no rule
      fires silently), dots="all" for sparse series. Fewer than 10 points → dashed band
      • "limits provisional"; ze...
Read more