npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fuaran-ui/charts

v0.14.2

Published

Render-time Chart → Drawing lowering for Fuaran UI — the bounded, deterministic layout engine that turns a semantic ChartSpec + data rows into a canonical themed Drawing subtree (scales, ticks, axes, gridlines, legend, series geometry). The TypeScript twi

Readme

@fuaran-ui/charts

Render-time lowering to Drawing for the Fuaran UI wire format — the bounded, deterministic layout engine that turns a semantic chart spec + data rows into a canonical themed Drawing subtree (scales, ticks, axes, gridlines, legend, series geometry), and the Sparkline lowering beside it. The TypeScript twin of the F# reference lowerings.

A Chart stays a semantic wire kind; this module lowers it at render time to the closed, typed Shape vocabulary of @fuaran-ui/schema, so a chart renders as first-party inline SVG on every conformant host — no third-party charting engine.

import { lower, lowerNode } from '@fuaran-ui/charts';
import { encodeNode } from '@fuaran-ui/ops';

const spec = {
  kind: 'Bar',
  xField: 'quarter',
  yFields: ['revenue'],
  title: 'Revenue by quarter',
} as const;
const rows = [
  { quarter: 'Q1', revenue: 120 },
  { quarter: 'Q2', revenue: 150 },
];

const drawing = lower(spec, rows); // a DrawingSpec
const wire = encodeNode(lowerNode('chart-revenue', spec, rows)); // canonical JSON

Sparkline

A Sparkline carries a bare bound series and nothing else, so every host that draws one has to turn that series into geometry — and this is the ONE place this repository does it. Both renderers (@fuaran-ui/renderer and @fuaran-ui/renderer-server) call it, so they cannot draw a different picture; before it landed they each carried a hand-written copy of the same algorithm.

import { tryLowerSparkline } from '@fuaran-ui/charts';

const drawing = tryLowerSparkline([1, 2, 3, 2, 4]); // a DrawingSpec, or null

null is the nothing-to-draw case: an empty (or unresolved, and so array-coerced-to-empty) series has no polyline, and the fallback a renderer emits instead — its own hook element carrying an em-dash — is a host element rather than a Shape, so the lowering cannot express it and must not pretend to by returning an empty canvas. The goldens spell the same fact as the JSON literal null.

The geometry, over a series of n values with min and max: a viewBox="0 0 100 30" canvas; range = max - min, or 1.0 when max - min < 1e-9 so a constant series sits on its own line rather than dividing by zero; x = i / (n - 1) * 100, and 50 when n = 1 so a lone point is centred; y = 30 - (v - min) / range * 28 - 1, one unit of inset at each edge so a peak is not clipped by the stroke; round-half-up to 2 dp on both coordinates; one Polyline, stroke="currentColor", stroke-width="1.5", no fill. No title or description — a sparkline has no spec to generate an accessible summary from, so it carries no accessible name of its own.

Non-finite values are not special-cased. They propagate through that arithmetic, reach the wire as the canonical "NaN" / "Infinity" / "-Infinity" string sentinels, and render as 0 through the drawing builder's number form. The shared wire-format-fixtures/sparkline-lowering/* corpus pins every case, this one included.

Chart-kind coverage (this host)

isLowered(kind) is the dispatch authority — the render branch and the arm set can never drift apart.

| ChartKind | Lowered? | Geometry | | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Bar | ✅ yes | grouped bars; stacked: true stacks segments by per-category cumulative sum | | Line | ✅ yes | one polyline per series over band centres | | Area | ✅ yes | translucent baseline-closed bands + full-strength series edge; stacked: true stacks cumulative bands | | Scatter | ✅ yes | fixed-radius point marks on a linear (non-zero-anchored) numeric x-domain | | Pie | ✅ yes | polar single-series wedges (cubic-Bézier arcs, 12-o'clock start, clockwise); zero-value categories keep their legend row; a lone 100% category degenerates to a Circle. Multi-series or negative values refuse the geometry. The donut variant is not lowered yet (deferred in the reference implementation; this host mirrors exactly what the reference ships) | | Heatmap | ❌ no | falls back to the host placeholder (fuaran-chart-placeholder); its lowering rule lands with its own phase |

stacked: true on a kind where stacking is meaningless (Line, Scatter, Pie) is ignored — the flag only changes Bar / Area geometry.

Data-addressed annotations

ChartLowerSpec.annotations carries the closed ChartAnnotation union — a horizontal ReferenceLine at a value, a vertical EventMarker at an x address, and a shaded RangeBand over a pair on either axis. An annotation names a place in the data's coordinates and, optionally, a label; it carries no geometry and no style at all, so it survives a data change, a theme flip, a restyle and a resize.

Three rules the lowering applies, each pinned by the shared goldens:

  • An address participates in the domain it addresses, before the axis is nice-d. A target above every bar still draws and the axis moves to say so; clamping it would draw a line at a value that is not the value declared.
  • Draw order is part of the lowering, not the stylesheet. Bands sit behind everything (the grid included), lines and markers sit in front of the series, and every annotation label is painted last. In inline SVG z-order is emission order, so a host that reordered these would still emit a valid document showing a different picture.
  • A label is fit-gated and suppressed, never clipped — and the gate is asked only of a Literal, because the text behind a Bound or I18n arm is not known at lowering time. A suppressed label never suppresses its annotation.

Pie is neutralised for all three members: a polar arm has neither axis for an address to name.

Mark identity

Every data-bearing shape carries a derivation-based markId on its DrawStyle (series-field|category-key, or the series field alone for one-shape-per-series geometry such as Line / Area), stable under row reorder and data refresh (object constancy). Renderers emit it as data-fuaran-mark on the corresponding SVG element. Chrome (axes, gridlines, labels, legend) deliberately stays unstamped — its identity is structural, not data-borne.

Determinism (R2)

The layout is a byte-for-byte port of the F# reference lowering: a fixed pixel viewBox, a {1,2,5}·10ⁿ nice-tick rule, and round-half-up coordinate rounding to 2 dp, so the output depends only on the spec + data. The shared wire-format-fixtures/chart-lowering/* corpus certifies parity across the F#, TypeScript, Python and Rust hosts — every golden, the annotation family included.

Theme

Chrome + text ink is surface-relative (currentColor at a per-role opacity), never a spec wire field — a lowered chart inherits the surface's own text colour and is legible on a light or dark surface with no CSS override. Series (categorical data) colours stay hex so they read distinctly on both surfaces.

Apache-2.0.