Vize

Chart Scale

Headless, dependency-free, d3-compatible scales with Intl tick formatting.

Package @vizejs/ui/chart-scale
Maturity stable
Own the source vize lib pull chart-scale
Requires —
Aliases scale, d3 scale, axis scale, linear scale, time scale, band scale
Covers d3-scale, d3-array ticks, d3-time, Intl.NumberFormat, Intl.DateTimeFormat

API

discreteKey

Identity key for discrete values; dates compare by timestamp like d3's InternMap.

function discreteKey(value: DiscreteValue): DiscreteKey

scaleOrdinal

Create an immutable ordinal scale. Unlike d3, unknown values never mutate the domain; without an unknown output they cycle through the range in the order they are first seen by this scale instance.

function scaleOrdinal<Domain extends DiscreteValue, Output>( options: OrdinalScaleOptions<Domain, Output>, ): OrdinalScale<Domain, Output>

scaleBand

Create an immutable band scale for categorical positions such as bars.

function scaleBand<Domain extends DiscreteValue>( options: BandScaleOptions<Domain> = {}, ): BandScale<Domain>
const x = scaleBand({ domain: ["a", "b", "c"], range: [0, 300], paddingInner: 0.1 });
x("b"); // band start
x.bandwidth; // band width

scalePoint

Create an immutable point scale: evenly spaced positions with zero bandwidth.

function scalePoint<Domain extends DiscreteValue>( options: BandScaleOptions<Domain> = {}, ): PointScale<Domain>

createContinuousCore

Build the forward and inverse mappings shared by every continuous scale.

function createContinuousCore(config: { readonly domain: readonly number[]; readonly range: readonly number[]; readonly clamp: boolean; readonly round: boolean; readonly unknown: number; readonly transform: Unary; readonly untransform: Unary; }): ContinuousCore

scaleLinear

Create an immutable linear scale.

function scaleLinear(options: ContinuousScaleOptions = {}): LinearScale
const y = scaleLinear({ domain: [0, 120], range: [300, 0], nice: true });
y(60); // 150

scalePow

Create an immutable power scale. Use exponent: 0.5 for area-proportional encodings such as bubble radii.

function scalePow(options: PowScaleOptions = {}): PowScale

scaleSqrt

Create a square-root scale, scalePow with exponent: 0.5.

function scaleSqrt(options: ContinuousScaleOptions = {}): PowScale

scaleLog

Create an immutable logarithmic scale. Ticks and nice domains snap to powers of the base, and tickFormat blanks minor ticks on dense axes like d3.

function scaleLog(options: LogScaleOptions = {}): LogScale

d3ScaleVectors

const d3ScaleVectors

numberTickFormat

Build a localized formatter for ticks of [start, stop]. Precision follows d3's precisionFixed(tickStep), so adjacent ticks are always distinguishable without trailing noise; with the default en-US locale the output equals d3's ",f" format except that Intl uses an ASCII hyphen-minus rather than −.

function numberTickFormat( start: number, stop: number, count: number, options: NumberTickFormatOptions = {}, ): (value: number) => string

defaultTimeTickFormats

Default Intl.DateTimeFormat options per time tick granularity.

const defaultTimeTickFormats: Readonly< Record<TimeTickGranularity, Intl.DateTimeFormatOptions> >

timeTickFormat

Build a granularity-aware time formatter. granularity decides which calendar unit a tick represents (as d3's multi-scale time format does); each unit is formatted with Intl.DateTimeFormat in timeZone, so labels are localized and identical on server and client.

function timeTickFormat( timeZone: string, granularity: (date: Date) => TimeTickGranularity, options: TimeTickFormatOptions = {}, ): (date: Date) => string

ticks

Return about count human-friendly values (multiples of 1, 2, or 5 times a power of ten) inside [start, stop], in the same direction as the input.

function ticks(start: number, stop: number, count: number): number[]

tickIncrement

Tick increment for [start, stop]. Positive values are the step; negative values are the inverse step (-10 means 0.1), which avoids float error.

function tickIncrement(start: number, stop: number, count: number): number

tickStep

Signed distance between adjacent ticks for [start, stop].

function tickStep(start: number, stop: number, count: number): number

niceExtent

Extend [start, stop] outward to tick-aligned values, iterating like d3's linear.nice until the tick increment stabilizes. Direction is preserved.

function niceExtent(start: number, stop: number, count = 10): [number, number]

decimalExponent

Decimal exponent of value, as in scientific notation (0.05 → -2).

function decimalExponent(value: number): number

precisionFixed

Fraction digits needed to distinguish ticks step apart (d3 precisionFixed).

function precisionFixed(step: number): number

bisectRight

Index of the first element greater than value in an ascending array.

function bisectRight( values: readonly number[], value: number, lo = 0, hi = values.length, ): number

createZoneClock

Create a wall-clock converter for timeZone.

function createZoneClock(timeZone: string): ZoneClock

zoneIntervals

Build (and cache) the calendar intervals for timeZone.

function zoneIntervals(timeZone: string): ZoneIntervals

timeTickInterval

Choose the tick interval d3's timeTickInterval would pick for [start, stop].

function timeTickInterval( intervals: ZoneIntervals, start: number, stop: number, count: number, tickStep: (start: number, stop: number, count: number) => number, ): TimeInterval | null

scaleTime

Create an immutable, calendar-aware time scale. Ticks, nice domains, and labels are computed in timeZone (default "UTC"), never in the host's local zone, so server rendering and hydration agree. With "UTC" the ticks equal d3's scaleUtc; with an IANA zone they follow the same calendar rules as d3's local scaleTime would in that zone.

function scaleTime(options: TimeScaleOptions = {}): TimeScale
const x = scaleTime({ domain: [start, end], range: [0, 640], timeZone: "Asia/Tokyo" });
const format = x.tickFormat({ locale: "ja-JP" });
x.ticks(6).map(format);

Behavior

Normative input -> outcome table for @vizejs/ui/chart-scale: linear, pow, sqrt, log, time, band, point, and ordinal scales, tick generation, nice domains, and Intl-based tick formatting. Every row is proven by the named test in chart-scale.test.ts or chart-scale-ssr.test.ts; compile-only guarantees live in chart-scale.types.test-d.ts.

The numeric algorithms are ports of d3-array 3, d3-scale 4, and d3-time 3 (ISC licensed). Parity is asserted against fixed vectors in scale-d3-vectors.ts, generated once by running the same inputs through d3 under TZ=UTC, TZ=America/New_York, and TZ=Asia/Tokyo. There is no runtime or test dependency on d3. Scales are immutable callable objects: nice() and with() return copies.

# Input Outcome Proven by
K1 ticks, tickIncrement, tickStep identical to d3-array, including reversed, tiny, huge, and degenerate extents ticks, tickIncrement, and tickStep match d3-array exactly
K2 linear map / invert / clamp / round identical to d3 including piecewise domains, descending domains, and zero-width domains linear scales map, invert, clamp, round, and tick like d3
K3 nice iterates to a stable tick increment like d3; copies, never mutates nice domains iterate to a stable tick increment like d3
K4 number tickFormat Intl.NumberFormat with d3 precisionFixed digits; locale and percent options number tick formats match d3 ',f' precision through Intl
K5 pow / sqrt signed power transform and linear ticks identical to d3 pow and sqrt scales follow d3's signed power transform
K6 log map, invert, ticks, nice, negative domains, and d3's minor-label blanking log scales map, tick, nice, and blank minor labels like d3
K7 band / point step, bandwidth, padding, align, round, and reversed ranges identical to d3 band and point scales lay out categories like d3
K8 ordinal range cycling like d3, but unknown values never mutate the domain ordinal scales cycle the range without mutating the domain
K9 UTC time ticks / nice / label granularity identical to d3 scaleUtc from milliseconds to centuries UTC time ticks, nice domains, and label granularity match d3 scaleUtc
K10 zoned time ticks timeZone reproduces d3 local scaleTime under that TZ, including DST transitions zoned time ticks match d3 local scaleTime run under that TZ
K11 time map / invert / tickFormat Intl.DateTimeFormat labels per granularity, in the scale's zone and locale time scales map dates, invert, and format ticks with Intl in their zone
K12 with, options, frozen domains immutable copies that round-trip their options scales are immutable and expose their options
K13 SSR on hosts in different time zones byte-identical markup, because ticks and labels never read the host zone scale-driven markup is byte-identical across requests and host time zones
K14 hydration scale-driven markup hydrates without warnings hydrates scale-driven markup without mismatch warnings

Intl renders negative numbers with an ASCII hyphen-minus where d3 uses −; that is the only intentional difference in number labels.