003 / RECHARTS / LINE
Twinline Compare
Compare one primary series against one reference series with a strong visual hierarchy, synchronized inspection, precise delta context, and clean responsive behavior.
- SPEC
- #003
- ENGINE
- Recharts
- FAMILY
- Line
- RENDERER
- svg
- STATUS
- preview
Installation
Checking public registry…
View local registry JSONCopied as source into your project (requires recharts).
Overview
Twinline Compare is Plotcn's specialized two-series comparative visualization. Designed specifically for period-over-period analytics, benchmark evaluations, and target vs actual metrics, it pairs one dominant primary series with one subdued reference series over a shared, honest Cartesian scale.
Unlike casual multi-line charts where all series compete with identical visual weight, Twinline Compare establishes an unmistakable visual hierarchy:
- Primary Series: Solid curve, dominant stroke weight (
2.5px), and primary brand/theme color (var(--chart-1)). - Reference Series: Dashed curve (
4 4), lighter stroke weight (1.8px), rendered behind the primary line in a muted tone (var(--chart-2)). - Single Shared Y Scale: Strictly avoids misleading dual axes. Both series share identical baseline, domain, and unit definitions.
- Synchronized Inspection: A single hover crosshair snaps across both series simultaneously, rendering a contextual tooltip with explicit delta math (
+/−, absolute difference, and percentage delta).
import { TwinlineCompare } from "@/components/charts/recharts/line-twin-compare"export function RevenueComparisonCard() { return ( <TwinlineCompare data={revenueData} xKey="date" primaryKey="current" referenceKey="previous" primaryLabel="2024 Actual" referenceLabel="2023 Benchmark" deltaType="both" /> )}Best Suited For
Twinline Compare is engineered for:
- Period-over-Period Analytics: Current month vs previous month, Year-over-Year (YoY) performance, and quarter-to-date tracking.
- Goal & Benchmark Tracking: Actual metrics measured against quarterly forecasts, SLAs, or industry baselines.
- A/B Experimentation: Control vs Variant performance over test duration.
- Operational & System Drift: Canary vs baseline deployment error rates, response latencies, and memory footprints.
When to Avoid
- Three or More Series: Use
MultiSeriesLineorStackedAreawhen comparing 3+ independent series simultaneously. - Independent Unrelated Units: When comparing metrics with completely different units (e.g. Temperature in °C vs Humidity in %), do not force them into a single chart. Render two stacked charts instead.
- Single Series Only: Use
SignalLineorPulseLinefor uncompared operational signals.
Installation
Install Twinline Compare directly into your project via the shadcn CLI. The component source and its required dependencies (recharts and shared Plotcn primitives) will be copied directly into your repository under complete source ownership.
Checking public registry…
View local registry JSONCopied as source into your project (requires recharts).
Data Contract
Twinline Compare accepts a readonly array of observations. Each record must contain a shared horizontal domain key (date, timestamp, or ordinal label) and numeric values for both the primary and reference series.
export interface ComparisonDatum { date: string // ISO date string, formatted timestamp, or ordinal category current: number // Primary series observation (e.g. current year revenue) previous: number // Reference series observation (e.g. prior year revenue)}Truthful Missing Values & Zero References
Twinline Compare guards comparison calculations against corrupting assumptions:
- Independent Missing Points: If one series has an observation at time while the other is missing, the missing series renders a clean visual gap (
missingValuePolicy="gap"). It is never coerced to zero. - Safe Delta Computation: Delta is evaluated only when both primary and reference values are finite numbers at the same point.
- Zero Reference Protection: When the reference series is
0, percentage delta cannot be mathematically computed (). Rather than crashing withInfinityorNaN, the tooltip cleanly indicates"Unavailable".
Component Props
Core Configuration
| Prop | Type | Default | Description |
|---|---|---|---|
data | readonly TData[] | [] | Readonly array of observation records. The caller's array is never mutated. |
xKey | keyof TData & string | required | Property name representing the horizontal X axis coordinate. |
primaryKey | keyof TData & string | required | Property name representing the primary series numeric value. |
referenceKey | keyof TData & string | required | Property name representing the reference benchmark series numeric value. |
primaryLabel | string | "Primary" | Human-readable label for the primary series displayed in legend and tooltip. |
referenceLabel | string | "Reference" | Human-readable label for the reference series displayed in legend and tooltip. |
height | number | string | 340 | Container height in pixels or standard CSS string (e.g. 100%, 360px). |
curve | "monotone" | "linear" | "step" | "monotone" | Line interpolation. Use monotone for continuous trends, step for scheduled rate tiers. |
deltaType | "absolute" | "percentage" | "both" | "both" | Formatting of delta context in tooltip. |
invertDelta | boolean | false | When true, negative deltas are styled as positive (emerald) and positive deltas as negative (rose) — useful for cost, latency, or errors. |
domain | [number, number] | ["auto", "auto"] | "auto" | Explicit Y-axis bounds. Automatic mode calculates a single safe scale spanning both series. |
Visual & Appearance
| Prop | Type | Default | Description |
|---|---|---|---|
primaryColor | string | "var(--chart-1)" | Stroke color for the primary series. |
referenceColor | string | "var(--chart-2)" | Stroke color for the reference series. |
showGrid | boolean | true | Renders subtle horizontal dashed reference dividers (var(--chart-grid)). |
showLegend | boolean | true | Renders series legend with solid vs dashed indicator styles. |
showXAxis | boolean | true | Renders horizontal domain tick labels. |
showYAxis | boolean | true | Renders vertical value tick labels on the shared scale. |
showDeltaInTooltip | boolean | true | Displays computed difference pill at the bottom of the hover tooltip. |
motion | boolean | { duration: number } | true | Reveal animation (350ms). Automatically disabled under prefers-reduced-motion. |
Accessibility & States
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | "Twinline Comparison Chart" | Accessible name announced by screen readers for the <figure> region. |
description | string | undefined | Extended contextual description for assistive technologies. |
loading | boolean | false | Displays neutral loading state without fake data while preserving layout footprint. |
error | Error | string | null | null | Actionable error banner with optional retry trigger. |
unavailable | boolean | string | null | false | Unavailability notice (e.g. historical data retention limitations). |
onRetry | () => void | undefined | Callback invoked when user clicks the retry button in the error state. |
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.
<TwinlineCompare
data={data}
xKey="date"
series={{
primary: { key: "current", label: "Current" },
reference: { key: "previous", label: "Previous" },
}}
/>Color token for the primary solid series stroke.
primaryColor="var(--chart-1, #10b981)"Default: "var(--chart-1, #10b981)"Color token for the reference dashed series stroke.
referenceColor="var(--chart-2, #94a3b8)"Default: "var(--chart-2, #94a3b8)"Format of delta calculation in the comparative tooltip.
deltaType="both"Default: "both"Line interpolation across observations. Monotone for continuous trends; step for scheduled tiers.
curve="monotone"Default: "monotone"Whether to render the series comparison legend with solid vs dashed line indicators.
showLegend={true}Default: trueWhether to render subtle horizontal background reference gridlines.
showGrid={true}Default: trueContainer height in pixels or standard CSS dimension strings.
height={340}Default: 340Handling of null or undefined observations. 'gap' preserves visual breaks; 'connect' bridges adjacent points.
missingValuePolicy="gap"Default: "gap"| Property | Type | Default | Required | Description |
|---|---|---|---|---|
dataReq | readonly TData[] | [] | Yes | Readonly array of comparative observation records. Caller data is never mutated. Best for: Comparative dataset |
xKeyReq | keyof TData & string | — | Yes | Property name for the horizontal X-axis domain (e.g. date, month, or sprint). Best for: Domain coordinates |
primaryKeyOpt | keyof TData & string | "current" | No | Field name for the primary metric series to plot with solid emphasis. Best for: Current/actual metric |
referenceKeyOpt | keyof TData & string | "previous" | No | Field name for the reference baseline series to plot with dashed styling. Best for: Prior/benchmark metric |
primaryLabelOpt | string | "Primary" | No | Human-readable label for the primary series displayed in tooltips and legends. Best for: Series identification |
string | "Reference" | No | Human-readable label for the reference series displayed in tooltips and legends. Best for: Benchmark identification | |
primaryColorOpt | string | "var(--chart-1, #10b981)" | No | Color token for the primary solid series stroke. Best for: Primary brand theme |
string | "var(--chart-2, #94a3b8)" | No | Color token for the reference dashed series stroke. Best for: Benchmark stroke styling | |
deltaTypeOpt | "both" | "percentage" | "absolute" | "both" | No | Format of delta calculation in the comparative tooltip. Best for: Comparative inspection detail |
curveOpt | "monotone" | "linear" | "step" | "monotone" | No | Line interpolation across observations. Monotone for continuous trends; step for scheduled tiers. Best for: Interpolation style |
showLegendOpt | boolean | true | No | Whether to render the series comparison legend with solid vs dashed line indicators. Best for: Multi-series clarity |
showGridOpt | boolean | true | No | Whether to render subtle horizontal background reference gridlines. Best for: Grid density control |
heightOpt | number | string | 340 | No | Container height in pixels or standard CSS dimension strings. Best for: Dashboard slot sizing |
"gap" | "connect" | "gap" | No | Handling of null or undefined observations. 'gap' preserves visual breaks; 'connect' bridges adjacent points. Best for: Data safety & truthful representation | |
domainOpt | [number, number] | ["auto", "auto"] | "auto" | No | Explicit Y-axis numeric domain spanning both series, or 'auto' unified scale calculation. |
showXAxisOpt | boolean | true | No | Whether to display the horizontal X-axis tick labels. |
showYAxisOpt | boolean | true | No | Whether to display the unified vertical Y-axis scale. |
motionOpt | boolean | { duration?: number } | true | No | Synchronized dual-line reveal animation (350ms). Automatically disabled under prefers-reduced-motion. |
titleOpt | string | "Twinline Compare" | No | Accessible name announced to screen-readers for the chart figure region. |
descriptionOpt | string | undefined | No | Long-form context describing what the comparative trend communicates. |
loadingOpt | boolean | false | No | Renders a neutral loading skeleton preserving container footprint without fake data. |
errorOpt | Error | string | null | null | No | Renders an actionable error state banner with optional retry trigger. |
unavailableOpt | boolean | string | null | false | No | Renders a metric unavailability notice (e.g. permission restriction or retention limit). |
Component Variants & Edge States
Production cookbooks showcasing configuration variants alongside verified handling of loading, empty data, and network error states.
Year-Over-Year Performance
Primary 2024 performance contrasted against dashed 2023 baseline with synchronized delta calculation.
Relative Percentage Variance
Focuses comparative tooltip inspection strictly on percentage delta (+25.0%) without clutter.
Scheduled Rate Tiers vs Observed
Discrete step interpolation comparing actual billing usage against contracted rate limits.
Compact Overview Widget
Condensed 200px tile comparison with clean legend and unified scale for executive summary cards.
Skeletons indicate runtime fetch or pending data queries.
Handles empty collections ([]) gracefully without crashing.
Graceful failure banner when data source or script fails.
Responsive Behavior
Twinline Compare automatically adapts to its parent container:
- Desktop (1024px+): Spacious layout, complete legend, and comparative hover tooltip with absolute and percentage deltas.
- Tablet (640px – 1023px): Adaptive X-axis tick thinning to prevent collision, consolidated tooltip padding.
- Mobile (< 640px): Compact padding, preserved start/end axis labels, full-width touch scrub interaction, and stacked tooltip items.
Container-Driven Breakpoints
Twinline Compare scales primary and reference curves synchronously on a single honest Y-axis. The synchronized tooltip card automatically formats metric readouts and delta calculations without viewport overflow.
Full comparative tooltip with signed delta and percentage change, explicit legend with solid vs dashed indicator.
Thinned X-axis labels, preserved line continuity, compact margins, tooltip pinned within viewport boundaries.
Edge-to-edge scrub inspection, compact legend pills, touch-first scrub interaction.
Accessibility & Keyboard Navigation
Twinline Compare conforms to WCAG 2.1 AAA and Section 508 accessibility guidelines:
- Keyboard Operable: Pressing Tab focuses the chart region with a prominent
var(--chart-focus)ring. - Synchronized Point Inspection:
- → Navigates to the next time observation.
- ← Navigates to the previous time observation.
- Home Jumps to the first observation.
- End Jumps to the final observation.
- Esc Clears active point selection.
- Screen Reader Announcement: A live region announces both primary and reference observations along with the time label.
- Factual Figure Summary: An invisible
<figcaption>provides an automated quantitative overview summarizing total observations, primary range, reference range, and final values without marketing spin.
Accessibility & Navigation Standards
Screen-reader figure region with quantitative delta summary (net change across both primary and benchmark series).
Container mounts as region with explicit assistive label.
Primary line is solid 2.5px while reference line is dashed 1.8px (dasharray 4 3), ensuring full clarity without relying on color.
Embeds visually hidden summary (.sr-only) declaring: “VoiceOver and NVDA announce primary vs reference series values and comparative delta without SVG node traversal.”
Automatically suppresses stroke draw animation when user requests reduced motion.
| Key | Action |
|---|---|
| Tab | Focus comparative chart region with prominent focus ring |
| ArrowLeft | Step to previous comparative observation |
| ArrowRight | Step to next comparative observation |
| Home | Jump to first observation point |
| End | Jump to latest observation point |
| Escape | Clear active inspection state |
Data Safety Guarantee
- Zero Fabricated Fallback Data: Twinline Compare will never synthesize fake comparison curves when inputs are missing.
- Finite Number Enforcement: Non-finite values (
NaN,Infinity,-Infinity) are cleanly caught before calculation, preventing corrupt SVG path operations. - Shared Scale Integrity: The vertical scale is calculated over the combined extent of both series, guaranteeing accurate visual comparison without misleading axis scaling.
- Zero Reference Division Protection: Prevents
NaN%orInfinity%by falling back to"Unavailable"when the baseline reference is zero.
Internal Architecture & File Dependencies
Source-first ownership model. Inspect the exact component call tree, dependencies, and full implementation below.
Scoped CSS variables for axes, grid, and crosshairs without global pollution.