019 / RECHARTS / BAR & COLUMN
Signal Bars
Canonical zero-anchored bars for discrete categorical comparison with stable ordering, grouped series, and category-centric inspection.
- SPEC
- #019
- ENGINE
- Recharts
- FAMILY
- Bar & Column
- RENDERER
- svg
- STATUS
- preview
Installation
Checking public registry…
View local registry JSONCopied as source into your project (requires recharts).
Overview
Signal Bars is Plotcn's canonical vertical bar chart for comparing discrete categories. It establishes the foundational analytical contract of the Bar family: bar length encodes quantitative magnitude from a truthful zero baseline, caller category order is preserved without automatic ranking, zero is a valid observation, missing data is not zero, and interaction resolves discrete category bands with forgiving touch targets.
The primary analytical question answered by Signal Bars is:
“How do these discrete categories compare on the same quantitative measure?”
Examples include:
- requests by region;
- revenue by product tier;
- tickets by priority;
- latency by microservice;
- incidents by category;
- tasks completed by engineering squad.
import { SignalBars } from "@/components/charts/recharts/bar-signal"const regionalTrafficData = [ { region: "North", web: 128400, mobile: 94200 }, { region: "South", web: 103800, mobile: 121300 }, { region: "East", web: 87400, mobile: 69800 }, { region: "West", web: 145100, mobile: 110600 }, { region: "Central", web: 76200, mobile: 81900 },]export function RegionalComparison() { return ( <SignalBars data={regionalTrafficData} categoryKey="region" series={[ { key: "web", label: "Web", color: "var(--chart-1)" }, { key: "mobile", label: "Mobile", color: "var(--chart-2)" }, ]} maxBarSize={48} showGrid /> )}Bar-Family Positioning
The Bar family specializes in discrete categorical comparison. Unlike continuous Line or Area charts which track movement across an ordered or temporal domain, Signal Bars treats each category as an independent discrete record:
| Consideration | Signal Line (001) | Prism Area (011) | Interactive Area (018) | Signal Bars (019) |
|---|---|---|---|---|
| Domain Type | Continuous time-series | Continuous time-series | Continuous time-series | Discrete categorical records |
| Primary Question | "How is this metric trending over time?" | "What is the accumulated volume over time?" | "What exact observation occurred at X?" | "How do these discrete categories compare?" |
| Geometric Encoding | 1 continuous polyline stroke | 1 filled area polygon | 1 filled area + crosshair + marker | Discrete rectangles rising from zero baseline |
| Baseline Requirement | Zero optional (trend-focused) | Zero recommended (magnitude) | Zero recommended | Zero mandatory (length encodes magnitude) |
| Ordering Semantics | Strictly chronological () | Strictly chronological | Strictly chronological | Caller-preserved category order (no sorting) |
| Interaction Target | Nearest-X time coordinate | Pointer hover position | Nearest-X snapping + observation lock | Category band hit region (44px+) |
Grouped Bars vs. Stacked Bars
Signal Bars V1 intentionally excludes stackMode. Grouped bars and stacked bars answer two fundamentally distinct analytical questions:
- Grouped Bars (Signal Bars): Answer "How do peer measures compare within and across categories?" All bars originate from the exact same common zero baseline, enabling direct visual length comparison.
- Stacked Bars (Dedicated Stacked Bar Component): Answer "How is a category total composed from additive parts?" Bars are stacked on top of one another, sacrificing baseline alignment for secondary segments in order to communicate whole-part composition.
Bar Magnitude Model
Governing Principle: Bar length communicates quantitative magnitude from a common baseline. Zero is therefore part of the analytical contract, not merely an axis styling choice.
In a bar chart, the visual ratio between any two bars and is geometrically determined by their lengths relative to the baseline :
When :
The visual ratio of the physical rectangles matches the true mathematical ratio of the data. Truncating the quantitative baseline () causes severe geometric distortion, exaggerating minor differences into misleading visual leaps:
Bar Magnitude Model: Zero Baseline vs. Truncated Axis
Comparison diagram illustrating why bar charts require a common zero baseline. On the left, bars start at zero, correctly representing 80 as twice the magnitude of 40. On the right, a truncated baseline starting at 35 distorts visual ratios, making 80 appear nine times taller than 40.
Because of this geometric requirement:
- Signal Bars enforces
includeZero = truecanonically. - The quantitative axis domain automatically encloses $0$ and all finite visible data points: .
- Truncating the baseline is intentionally disallowed in V1.
Category Model & Order Invariance
In Plotcn, category order is data:
The component strictly preserves the caller's input order:
- No Automatic Ranking: Signal Bars never rearranges categories from largest to smallest. Ranking semantics belong to a dedicated ranked-bar component where sorting is explicitly requested.
- No Alphabetical Sorting: Categorical records are rendered in the order provided by the consumer application (e.g. geographical sequence, workflow priority, funnel order).
- Deterministic Duplicate Handling: If duplicate category labels exist in caller data, Signal Bars preserves every record deterministically using internal index keys without silently aggregating, summing, or averaging values. A development-time console warning alerts developers to ambiguous category keys.
Category Band & Touch Hit Regions
A common usability failure in bar charts is making the interaction target match only the narrow visible rectangle. When a chart displays 16 categories on a mobile viewport, a bar might measure only 10px or 14px in width—far smaller than the 44px minimum required for reliable touch activation:
Category Band & Expanded Touch Hit Region
Diagram showing how a category band provides a 44px or wider touch/pointer target even when the visible bar rectangle is narrow, allowing effortless inspection without missing the target or blocking vertical scrolling.
Signal Bars solves this through Category Band Hit Testing:
- The interaction target is the entire categorical band enclosing the bar group.
- Pointer movement or touch anywhere within a category's horizontal slice immediately resolves that category.
- Mobile vertical scrolling remains completely unobstructed (
touch-action: pan-y), preventing touch-trapping common with naive canvas charts. - Hover never alters bar geometry (no hover growth, no Y-translation, no bouncing). Hover and focus emphasize data; they never distort it.
Grouped Peer Series Contract
Signal Bars supports one or more peer numeric series sharing the same categorical domain:
<SignalBars data={data} categoryKey="region" series={[ { key: "web", label: "Web", color: "var(--chart-1)" }, { key: "mobile", label: "Mobile", color: "var(--chart-2)" }, { key: "desktop", label: "Desktop", color: "var(--chart-3)" }, ]}/>Grouped Peer Series: Stable Order & Shared Scale
Diagram showing grouped peer series within discrete categories. Series order remains fixed across categories without dynamic reordering by value, and all series share the exact same quantitative scale.
Key Invariants for Grouped Series:
- Shared Scale & Unit: Peer series must represent the same quantitative unit (e.g. requests, users, dollars, latency) and share the exact same quantitative scale. No dual Y-axes exist in canonical Signal Bars.
- Stable Series Ordering: Within every category band, peer bars follow the exact order declared in the
seriesconfiguration array (Web \to Mobile \to Desktop). Bars are never sorted by value inside individual category groups. - Stable Theme Colors: Default series colors map deterministically from Plotcn theme tokens (
--chart-1,--chart-2,--chart-3, ...). Toggling a series in the legend never reassigns the colors of remaining series.
Signed Zero-Baseline Model
Signal Bars provides native support for signed datasets containing positive and negative values:
Signed Values: Common Baseline & Grounded Geometry
Illustration of signed bar growth. Positive bars extend upward from the zero baseline, and negative bars extend downward from the same baseline. Outer corners are subtly rounded, but the zero-facing edge remains flat and grounded.
Signed Geometry Rules:
- Common Baseline: Positive and negative bars extend in opposite directions from the exact same central zero reference line.
- No Absolute Value Conversion: Negative values are never clamped to zero or converted to absolute values.
- Asymmetric Corner Radii: Outer positive ends receive subtle rounding (
[4, 4, 0, 0]), and outer negative ends receive subtle rounding ([0, 0, 4, 4]). Zero-facing baseline edges remain completely flat and grounded, preventing visual detachment from the baseline.
Missing Values vs. Measured Zero
Signal Bars enforces a strict distinction between measured zero and unavailable data:
- Measured Zero ($0$): Represents a known observation that measured zero units. It renders at the zero baseline, and tooltips, screen readers, and data tables announce it explicitly as
0. - Missing ( / ): Indicates unavailable data. It renders no quantitative rectangle whatsoever. Tooltips display
Unavailableor—, and accessible tables reportUnavailable. - Data Sanitization: Any non-finite values (, ) are safely neutralized to before reaching SVG generation, preventing
height="NaN"ory="Infinity"console errors.
Keyboard Navigation & Traversal
Signal Bars provides an orientation-aware keyboard exploration model with a single chart tab stop:
| Key | Vertical Bars (orientation="vertical") | Horizontal Bars (orientation="horizontal") |
|---|---|---|
ArrowLeft | Move to previous category | — |
ArrowRight | Move to next category | — |
ArrowUp | — | Move to previous category |
ArrowDown | — | Move to next category |
Home | Jump to first category | Jump to first category |
End | Jump to last category | Jump to last category |
Escape | Clear active category inspection | Clear active category inspection |
Focus states use the canonical --chart-focus ring on the chart container. Active category changes trigger polite live-region announcements ("North. Category 1 of 5. Web: 128k, Mobile: 94k") without spamming users during pointer hovering.
Rendering Pipeline & Architecture
Signal Bars maintains an idiomatic Recharts SVG architecture rather than introducing heavy chart abstraction layers:
SignalBars Rendering Pipeline & Interaction Flow
Architecture diagram showing data normalization, band and quantitative domain computation, Recharts BarChart composition, and category-centric event routing to tooltip, highlight, and accessible live regions.
The rendering flow:
- Raw Categorical Records: Received as
readonly TData[]without caller mutation. - Deterministic Normalization: Identifies missing values, guards against non-finite values, and ensures stable internal index keys.
- Scale Resolution: Sets up discrete band scale for categories and quantitative domain anchored at zero .
- Recharts Composition: Renders native SVG
<BarChart>,<XAxis>,<YAxis>,<ReferenceLine y={0}>, and individual<Bar>elements. - Synchronized Output: Category band hit testing feeds active category state to synchronized multi-series tooltips, band tint highlights, accessible live regions, and off-screen structured tables.
Value Labels Policy
Signal Bars supports an optional valueLabel configuration ("none" | "auto" | "always"):
"none"(default): Cleanest categorical presentation without inline clutter."auto": Renders formatted values near the outer end of each bar when category density and bar width provide sufficient clearance."always": Always displays value labels.- Zero & Missing Safeguards: Missing values never display a value label; zero values render
0without manufacturing a fake bar.
Props Reference & Interactive Prop Explorer
Inspect every component property, customize semantic color roles live with instant visual feedback, and copy production-ready code with active prop configurations.
Colors & Appearance Configuration
Customize primary, reference, or annotation series colors. Defaults derive from Plotcn theme tokens.
<SignalBars
data={data}
categoryKey="region"
series={[
{ key: "web", label: "Web" },
{ key: "mobile", label: "Mobile" },
]}
/>Bar direction: "vertical" puts categories on horizontal X-axis; "horizontal" puts categories on vertical Y-axis.
orientation="vertical"Default: "vertical"Maximum bar thickness in pixels to prevent grotesque bar expansion when only 2-3 categories exist.
maxBarSize={48}Default: 48Pixel spacing between peer series bars within the same category group.
barGap={4}Default: 4Spacing between distinct category groups along the categorical axis.
groupGap={16}Default: 16Inline numeric value label policy. Auto displays labels when space permits without collision.
valueLabel="none"Default: "none"Whether to render subtle reference grid lines perpendicular to the quantitative axis.
showGrid={true}Default: trueWhether to render the horizontal scale ticks and axis line.
showXAxis={true}Default: trueWhether to render the vertical scale ticks and axis line.
showYAxis={true}Default: trueWhether to render the series legend. Automatically enabled for multi-series grouped charts.
showLegend={false}Default: series.length > 1Whether clicking legend items toggles individual series visibility with stable color assignment.
interactiveLegend={true}Default: trueInteraction hit-testing target: category band (forgiving 44px) or individual bar rectangle.
cursorMode="category"Default: "category"Tooltip composition: category shows all peer series together; bar shows only the hovered metric.
tooltipMode="category"Default: "category"| Property | Type | Default | Required | Description |
|---|---|---|---|---|
dataReq | readonly TData[] | [] | Yes | Readonly array of categorical records. Caller data is never mutated or reordered. |
categoryKeyReq | keyof TData & string | — | Yes | Property name on data records representing the discrete category domain. |
seriesReq | readonly SignalBarSeries<TData>[] | — | Yes | Array of peer numeric series sharing the same quantitative unit, domain, and scale. |
orientationOpt | "vertical" | "horizontal" | "vertical" | No | Bar direction: "vertical" puts categories on horizontal X-axis; "horizontal" puts categories on vertical Y-axis. |
heightOpt | number | string | 320 | No | Container height in pixels or CSS dimension string. |
maxBarSizeOpt | number | 48 | No | Maximum bar thickness in pixels to prevent grotesque bar expansion when only 2-3 categories exist. |
barGapOpt | number | 4 | No | Pixel spacing between peer series bars within the same category group. |
groupGapOpt | number | 16 | No | Spacing between distinct category groups along the categorical axis. |
valueLabelOpt | "none" | "auto" | "always" | "none" | No | Inline numeric value label policy. Auto displays labels when space permits without collision. |
showGridOpt | boolean | true | No | Whether to render subtle reference grid lines perpendicular to the quantitative axis. |
showXAxisOpt | boolean | true | No | Whether to render the horizontal scale ticks and axis line. |
showYAxisOpt | boolean | true | No | Whether to render the vertical scale ticks and axis line. |
showLegendOpt | boolean | series.length > 1 | No | Whether to render the series legend. Automatically enabled for multi-series grouped charts. |
boolean | true | No | Whether clicking legend items toggles individual series visibility with stable color assignment. | |
cursorModeOpt | "category" | "bar" | "category" | No | Interaction hit-testing target: category band (forgiving 44px) or individual bar rectangle. |
tooltipModeOpt | "category" | "bar" | "category" | No | Tooltip composition: category shows all peer series together; bar shows only the hovered metric. |
motionOpt | boolean | { duration?: number } | true | No | Animation toggle or configuration. Automatically disabled when prefers-reduced-motion is detected. |
(category: string | number) => string | — | No | Custom formatter function for axis ticks and tooltip category labels. | |
(value: number) => string | — | No | Global numeric formatter function for quantitative values across tooltips and value labels. | |
(active: ActiveCategoryDatum<TData> | null) => void | — | No | Callback invoked whenever the active inspected category changes via pointer, touch, or keyboard. |
Component Variants & Edge States
Production cookbooks showcasing configuration variants alongside verified handling of loading, empty data, and network error states.
Requests by Region (Grouped Peer Series)
Canonical multi-series grouped bar chart comparing Web and Mobile traffic across five geographic regions with stable series ordering and shared quantitative scale.
Support Tickets by Priority (Single Series)
Canonical single-series categorical comparison demonstrating truthful zero-anchored magnitude across priority tiers.
Net Operating Growth (Signed Values)
Bi-directional bars extending from a common zero baseline, demonstrating positive growth and negative contraction without absolute-value distortion.
Enterprise Teams (Horizontal Orientation)
Horizontal bar orientation providing ample layout width for lengthy categorical labels without cramped diagonal typography.
Skeletons indicate runtime fetch or pending data queries.
Handles empty collections ([]) gracefully without crashing.
Graceful failure banner when data source or script fails.
Container-Driven Breakpoints
Signal Bars preserves all categorical records, zero-anchored geometry, and category band interaction across all screen widths down to 320px, thinning axis tick labels while never dropping underlying data bars.
Full category tick density, multi-series legend, spacious category bands, and complete keyboard traversal.
Adaptive category tick reduction, compact margins, and preserved 44px touch hit regions.
Compact gutters, thinned ticks, 44px touch targets, pan-y scroll preservation, and multi-series wrapped legend.
Accessibility & Navigation Standards
Single keyboard tab stop on root figure with orientation-aware arrow navigation (Left/Right for vertical, Up/Down for horizontal), Home/End traversal, and full off-screen structured HTML table for screen readers.
Container mounts as region with explicit assistive label.
Grouped bar positions, distinct legend labels, tabular numbers, and full off-screen structured HTML table ensure non-color accessibility.
Embeds visually hidden summary (.sr-only) declaring: “Announces category name, index position, total categories, and all peer series measurements factually without editorializing.”
All entrance animations are bypassed immediately when prefers-reduced-motion is detected in system preferences.
| Key | Action |
|---|---|
| ArrowRight / ArrowDown | Inspect next category across the discrete domain. |
| ArrowLeft / ArrowUp | Inspect previous category across the discrete domain. |
| Home | Jump inspection directly to the first category. |
| End | Jump inspection directly to the last category. |
| Escape | Clear active category inspection. |
Data Safety Guarantees
Signal Bars enforces sixteen strict data safety invariants:
- ✓ Caller category order is strictly preserved
- ✓ Values are never automatically ranked or sorted
- ✓ Duplicate categories are never silently aggregated
- ✓ Zero is a real quantitative value
- ✓ Missing data is not coerced to zero
- ✓ Negative values preserve their true mathematical sign
- ✓ Non-finite numbers (, ) never reach SVG geometry
- ✓ Ordinary bar magnitude remains zero-anchored
- ✓ Grouped series share one compatible quantitative scale
- ✓ Series ordering is stable across all categories
- ✓ Series colors are stable across visibility toggles
- ✓ Hiding a series never reassigns another series' color
- ✓ Responsive tick reduction never removes underlying categories
- ✓ Keyboard navigation reaches unticked categories
- ✓ Touch targets exceed visible rectangle widths
- ✓ Hover never changes bar length, width, or position
Props Reference
| Property | Type | Default | Required | Description |
|---|---|---|---|---|
data | readonly TData[] | [] | Yes | Readonly array of categorical records. |
categoryKey | keyof TData & string | — | Yes | Property key identifying discrete categories. |
series | readonly SignalBarSeries<TData>[] | — | Yes | One or more peer numeric series sharing scale. |
orientation | "vertical" | "horizontal" | "vertical" | Optional | Bar direction: vertical (categories on X) or horizontal (categories on Y). |
height | number | string | 320 | Optional | Container height in pixels or CSS dimension. |
maxBarSize | number | 48 | Optional | Maximum bar thickness in pixels to prevent grotesque wide bars. |
barGap | number | 4 | Optional | Pixel space between peer bars in a category group. |
groupGap | number | 16 | Optional | Spacing between distinct category groups. |
valueLabel | "none" | "auto" | "always" | "none" | Optional | Inline numeric value label rendering policy. |
showGrid | boolean | true | Optional | Whether to render subtle reference grid lines. |
showXAxis | boolean | true | Optional | Whether to render the horizontal axis line and ticks. |
showYAxis | boolean | true | Optional | Whether to render the vertical axis line and ticks. |
showLegend | boolean | series.length > 1 | Optional | Whether to render series legend. |
interactiveLegend | boolean | true | Optional | Whether legend allows toggling series visibility. |
cursorMode | "category" | "bar" | "category" | Optional | Hit testing target: category band or individual bar. |
tooltipMode | "category" | "bar" | "category" | Optional | Tooltip composition: peer series together or single measure. |
motion | boolean | { duration?: number } | true | Optional | Animation toggle or configuration. Respects reduced motion. |
categoryFormatter | (category: string | number) => string | — | Optional | Custom category label formatter function. |
valueFormatter | (value: number) => string | — | Optional | Global numeric formatter fallback. |
onActiveChange | (active: ActiveCategoryDatum<TData> | null) => void | — | Optional | Callback invoked on active category inspection change. |
Related Charts
- Interactive Area (018): Inspection-specialized continuous time-series area chart with nearest-X scrubbing and persistent locking.
- Prism Area (011): Continuous volume area chart with translucent magnitude fill.
- Signal Line (001): Single-series continuous trend line chart with restrained active-point emphasis.
Internal Architecture & File Dependencies
Source-first ownership model. Inspect the exact component call tree, dependencies, and full implementation below.
Handles container dimension measurement and SVG viewBox sizing
Coordinates scales, Cartesian grid, Bar geometry, zero baseline ReferenceLine, and Tooltip