0.109.0 - Refactor charts to use shared primitives and add Histogram - #122
Merged
Merged
Conversation
…outs, Histogram and BarList Apps no longer have to hand-roll chart sizing, colours, tooltips, histograms and ranked bar lists (the /admin/traces dashboard in schemavaults/auth#235 built all five itself). Charts size to their container - width="auto" on BarChart, LineChart, ScatterPlot, Sparkline and the new Histogram (PieChart: diameter="auto", capped at its size preset). The chart measures its root with a ResizeObserver and redraws on resize. - Until the first measurement it renders an empty box at its height, so the server HTML holds no pixel width and no tick labels (no jump from a guessed width, no time-zone hydration mismatch). The root sets contain: inline-size so the drawing never holds its container open. - Exported building blocks: useMeasuredWidth(fallbackWidth) and <ResponsiveChart height>{(width) => ...}</ResponsiveChart>. - A numeric width keeps its pixel size. Chart colour palette - Default series colours are the chart palette (--chart-1 ... --chart-8, --chart-other from @schemavaults/theme 0.29.0), assigned in slot order and never cycled: a ninth series (a fourth on ScatterPlot) is "other". Every bar of a BarChart shares slot 1. Each reference carries the light hex as its var() fallback, so this works on theme 0.28.0 too (dark mode picks up its own steps once the theme is bumped). - seriesOrder (LineChart, ScatterPlot) and segmentOrder (PieChart) make colour follow the id, so filtering a series out never repaints the rest. - New colour ids chart-1 ... chart-8, other and brand; "default" now means slot 1 (it was the brand blue, now the explicit "brand"). Hover readouts - Shared ChartTooltip / ChartTooltipRow (HTML, value first, series keyed by a short line, clamped inside the chart, flips below near the top), ChartLegend and a visually hidden table view on every chart. - LineChart: crosshair that snaps to the nearest x with dots on every series and one readout for all of them; arrow keys, Home/End, Escape. Legend for two or more series. formatX / formatY / formatTooltip, xTickValues. - ScatterPlot: nearest point within 24px (hitRadius), yScale="log" with decade ticks (zero on the bottom decade), per-point group emphasis, reference lines above the points with haloed labels, memoized points layer (group fading is a scoped style rule), so pointer moves on 5,000 points touch only the readout. - BarChart: the whole column is the hover/focus target, other bars dim, arrow keys; optional value axis with nice ticks (showValueAxis); formatValue / formatTooltip. - PieChart: segment readout (value and share), arrow keys, optional legend. - Nice ticks inside the domain (niceTicks, default on) and label thinning so axes stay readable at 360px. - Mark rules: bars at most 24px thick (maxBarThickness) with a rounded free end and square baseline, 2px surface gaps, markers r >= 4 with a 2px ring, area fills as a light wash, loading state that keeps the previous render at reduced opacity. New components - Histogram: pre-binned buckets on a count axis, bucket-edge labels thinned to ~52px, touching bars, the tallest labelled, an overflow bucket in grey with a caption, hover/keyboard readout of range, count and share. - binValues(values, { scale, minBinWidth }): linear nice-width bins with a long tail past the p95 folded into an overflow bucket, or 1-2-5 log edges; unit tests (bun run test:unit, new CI job). - BarList: ranked horizontal bars in plain HTML for long names, truncated labels with the full text in title, right-aligned values, shared max, onSelect toggle buttons with aria-pressed. Stories cover every new behaviour with play tests (keyboard and pointer readouts, auto width, stable colours when filtering, 5,000 points without touching the points layer, truncation at phone width), plus a Theme/Chart Palette page. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HJCAfCNitnFTnnTMYTPiCd
- binValues could loop forever when the bin width fell below the values' precision (epoch milliseconds a tenth of a ms apart); edges are now start + k*width rounded to the width's precision, and the walk stops if an edge fails to move forward. It also folded a phantom "long tail" when the data sat below zero: the tail test now measures from the minimum. New tests cover both. - LineChart crashed when points had no explicit x and a formatX was given: nice ticks landed on fractional indices. Index-mode ticks are whole numbers now, and a formatter returning a non-string skips the tick. - niceTicks defaults off for an axis given an xTickFormatter / yTickFormatter, so existing formatters keep their evenly spaced ticks. - Screen readers: the visual tooltip is aria-hidden, and each chart keeps an always-mounted, visually hidden live region (new ChartLiveRegion) that announces keyboard moves only; a region mounted with its text is not read out, and hovers would flood it. - ChartTooltip positions itself before every paint, so a readout whose size changed never shows a frame at the old width. - useMeasuredWidth measures clientWidth minus padding, ignoring CSS transforms (a chart in a dialog that is zooming in). - LineChart keeps the crosshair on its x value across data refreshes; Bar, Pie and Histogram treat a stale index as nothing active. - Arrow keys move between clickable bars / pie segments, skipping ones that aren't clickable; PieChart segments keep their colour slot from their position in `segments` even when an earlier one is zero. - ScatterPlot trend lines on a log scale are fitted on log10(y). - BarChart docs: bar.className styles the painted shape, which doesn't take the pointer; use data-[active=true]: for hover styling. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HJCAfCNitnFTnnTMYTPiCd
0.29.0 adds the chart palette tokens (--chart-1 ... --chart-8 and --chart-other, with separate light and dark values) that the charts' default series colours read. With it installed, dark mode draws the dark steps instead of the light fallbacks the colour classes carry for older themes. No component changes are needed. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HJCAfCNitnFTnnTMYTPiCd
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
jalexw
pushed a commit
that referenced
this pull request
Sep 26, 2026
Brings in the chart upgrades from #122: the `chart-primitives` module (the `--chart-1`..`--chart-8` categorical palette, scales, tooltips, legends, responsive sizing), the new Histogram and BarList components, the upgrades to BarChart / LineChart / PieChart / ScatterPlot / Sparkline, the `test:unit` script and its CI job, and @schemavaults/theme 0.29.0. Conflicts resolved: - src/components/ui/index.ts - both sides appended exports after ./scatter-plot. Kept both; ./treemap now follows ./histogram and ./bar-list. - package.json - main shipped 0.109.0 independently, so this branch moves to 0.110.0 rather than reusing a published version. TreeMap is unaffected by the theme bump: 0.29.0 adds the `--chart-*` tokens without removing `--schemavaults-brand-blue`, `--warning` or `--destructive`, which is what its palette maps resolve against. Verified on the merge result: typecheck, typecheck:storybook, lint and build clean; `bun run test:unit` 14/14; Storybook suite 136 suites / 1165 tests passing. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Px7WyxT4i2wTrj5ZvabCr6
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This release introduces a new
Histogramcomponent and refactors all existing chart components (ScatterPlot, LineChart, BarChart, PieChart) to use a shared set of chart primitives. The refactor centralizes color management, formatting, tooltips, legends, and responsive sizing logic, reducing duplication and improving consistency across the chart library.Key Changes
New Components
Histogram- A new histogram/distribution chart component with linear and log scales, configurable binning, and overflow bucket supportBarList- A ranked list of horizontal bars for displaying sorted metricsNew Chart Primitives (
src/components/ui/chart-primitives/)chart-colors.ts- Centralized color palette management with support for 8 series slots, status colors, and a de-emphasis "other" color. Replaces inline color mappings in each chart.chart-format.ts- Shared number formatting (thousands separators, float precision)chart-scale.ts- Framework-free geometry and tick helpers (nice steps, axis generation, label thinning, log decade ticks)chart-tooltip.tsx- Reusable floating tooltip component with positioning logicchart-legend.tsx- Reusable legend component for multi-series chartschart-data-table.tsx- Accessible data table for chart readoutsuse-chart-width.ts- Hook for responsive chart sizing (width="auto"support)responsive-chart.tsx- Container component for responsive chartsChartPalette.stories.tsx- Storybook reference for the color paletteChart Component Updates
All chart components now:
chart-colors.tsuseChartWidthhook for responsive sizing withwidth="auto"supportTooltipContextinterfaces for custom tooltip renderingseriesOrderprop to control color slot assignment by series idScatterPlot:
yScaleprop for log-scale support with decade ticksgroupprop to fade unrelated groups on hoverScatterPlotTooltipContextfor custom tooltipsLineChart:
seriesOrderprop for stable color assignmentLineChartTooltipValuecontext interfaceBarChart:
BarChartTooltipContextfor custom tooltipsPieChart:
Utility Additions
bin-values.ts- Framework-free histogram binning logic with linear and log scale support, including unit testsuse-measured-width.ts- Hook for measuring element width and responding to resize eventsSparkline- Updated to use new chart color systemTesting & CI
bun run test:unitcommand for framework-free unit testsUnit-Testsjob to GitHub Actions workflowDocumentation
ChartPalette.stories.tsxas a reference for the color systemCLAUDE.mdwith unit test commandImplementation Details
--chart-1through--chart-8,--chart-other) with fallback values for older theme versionswidth="auto"to fill their container and redraw on resize, with a placeholder during serverhttps://claude.ai/code/session_01HJCAfCNitnFTnnTMYTPiCd