003 / RECHARTS / LINE

Twinline Compare

Recharts

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

PLOTCN/REGISTRY/LINE-TWIN-COMPARE/SOURCE
pnpm dlx shadcn@latest add @plotcn/line-twin-compare

Checking public registry…

View local registry JSON
REGISTRY direct URL·ENGINE Recharts·FILES 1·DEPENDENCIES 6

Copied as source into your project (requires recharts).

× 340Container width
Measuring preview...
RECHARTS · SVG · 0 × 340pxMotion enabled · ResizeObserver

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).
TSX
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 MultiSeriesLine or StackedArea when 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 SignalLine or PulseLine for 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.

PLOTCN/REGISTRY/LINE-TWIN-COMPARE/SOURCE
pnpm dlx shadcn@latest add @plotcn/line-twin-compare

Checking public registry…

View local registry JSON
REGISTRY direct URL·ENGINE Recharts·FILES 1·DEPENDENCIES 6

Copied 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.

TypeScript
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:

  1. Independent Missing Points: If one series has an observation at time TT while the other is missing, the missing series renders a clean visual gap (missingValuePolicy="gap"). It is never coerced to zero.
  2. Safe Delta Computation: Delta is evaluated only when both primary and reference values are finite numbers at the same point.
  3. Zero Reference Protection: When the reference series is 0, percentage delta cannot be mathematically computed (Δ0\frac{\Delta}{0}). Rather than crashing with Infinity or NaN, the tooltip cleanly indicates "Unavailable".

Component Props

Core Configuration

Prop Type Default Description
PropTypeDefaultDescription
datareadonly TData[][]Readonly array of observation records. The caller's array is never mutated.
xKeykeyof TData & stringrequiredProperty name representing the horizontal X axis coordinate.
primaryKeykeyof TData & stringrequiredProperty name representing the primary series numeric value.
referenceKeykeyof TData & stringrequiredProperty name representing the reference benchmark series numeric value.
primaryLabelstring"Primary"Human-readable label for the primary series displayed in legend and tooltip.
referenceLabelstring"Reference"Human-readable label for the reference series displayed in legend and tooltip.
heightnumber | string340Container 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.
invertDeltabooleanfalseWhen 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
PropTypeDefaultDescription
primaryColorstring"var(--chart-1)"Stroke color for the primary series.
referenceColorstring"var(--chart-2)"Stroke color for the reference series.
showGridbooleantrueRenders subtle horizontal dashed reference dividers (var(--chart-grid)).
showLegendbooleantrueRenders series legend with solid vs dashed indicator styles.
showXAxisbooleantrueRenders horizontal domain tick labels.
showYAxisbooleantrueRenders vertical value tick labels on the shared scale.
showDeltaInTooltipbooleantrueDisplays computed difference pill at the bottom of the hover tooltip.
motionboolean | { duration: number }trueReveal animation (350ms). Automatically disabled under prefers-reduced-motion.

Accessibility & States

Prop Type Default Description
PropTypeDefaultDescription
titlestring"Twinline Comparison Chart"Accessible name announced by screen readers for the <figure> region.
descriptionstringundefinedExtended contextual description for assistive technologies.
loadingbooleanfalseDisplays neutral loading state without fake data while preserving layout footprint.
errorError | string | nullnullActionable error banner with optional retry trigger.
unavailableboolean | string | nullfalseUnavailability notice (e.g. historical data retention limitations).
onRetry() => voidundefinedCallback invoked when user clicks the retry button in the error state.
03 / Component API & Styling

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.

Generated Usage Code (Live Props):
<TwinlineCompare
  data={data}
  xKey="date"
  series={{
    primary: { key: "current", label: "Current" },
    reference: { key: "previous", label: "Previous" },
  }}
/>
Interactive Prop Preview Lab
primaryColorstring

Color token for the primary solid series stroke.

Select value to preview live:var(--chart-1, #10b981)
Active: primaryColor="var(--chart-1, #10b981)"Default: "var(--chart-1, #10b981)"
referenceColorstring

Color token for the reference dashed series stroke.

Select value to preview live:var(--chart-2, #94a3b8)
Active: referenceColor="var(--chart-2, #94a3b8)"Default: "var(--chart-2, #94a3b8)"
deltaType"both" | "percentage" | "absolute"

Format of delta calculation in the comparative tooltip.

Select value to preview live:
Active: deltaType="both"Default: "both"
curve"monotone" | "linear" | "step"

Line interpolation across observations. Monotone for continuous trends; step for scheduled tiers.

Select value to preview live:
Active: curve="monotone"Default: "monotone"
showLegendboolean

Whether to render the series comparison legend with solid vs dashed line indicators.

Select value to preview live:
Active: showLegend={true}Default: true
showGridboolean

Whether to render subtle horizontal background reference gridlines.

Select value to preview live:
Active: showGrid={true}Default: true
heightnumber | string

Container height in pixels or standard CSS dimension strings.

Select value to preview live:
Active: height={340}Default: 340
missingValuePolicy"gap" | "connect"

Handling of null or undefined observations. 'gap' preserves visual breaks; 'connect' bridges adjacent points.

Select value to preview live:
Active: missingValuePolicy="gap"Default: "gap"
All Properties (23)
Component properties
PropertyTypeDefaultRequiredDescription
dataReq
readonly TData[][]Yes

Readonly array of comparative observation records. Caller data is never mutated.

Best for: Comparative dataset

xKeyReq
keyof TData & stringYes

Property name for the horizontal X-axis domain (e.g. date, month, or sprint).

Best for: Domain coordinates

keyof TData & string"current"No

Field name for the primary metric series to plot with solid emphasis.

Best for: Current/actual metric

keyof TData & string"previous"No

Field name for the reference baseline series to plot with dashed styling.

Best for: Prior/benchmark metric

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

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

"both" | "percentage" | "absolute""both"No

Format of delta calculation in the comparative tooltip.

Best for: Comparative inspection detail

"monotone" | "linear" | "step""monotone"No

Line interpolation across observations. Monotone for continuous trends; step for scheduled tiers.

Best for: Interpolation style

booleantrueNo

Whether to render the series comparison legend with solid vs dashed line indicators.

Best for: Multi-series clarity

booleantrueNo

Whether to render subtle horizontal background reference gridlines.

Best for: Grid density control

number | string340No

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

[number, number] | ["auto", "auto"]"auto"No

Explicit Y-axis numeric domain spanning both series, or 'auto' unified scale calculation.

booleantrueNo

Whether to display the horizontal X-axis tick labels.

booleantrueNo

Whether to display the unified vertical Y-axis scale.

boolean | { duration?: number }trueNo

Synchronized dual-line reveal animation (350ms). Automatically disabled under prefers-reduced-motion.

string"Twinline Compare"No

Accessible name announced to screen-readers for the chart figure region.

stringundefinedNo

Long-form context describing what the comparative trend communicates.

booleanfalseNo

Renders a neutral loading skeleton preserving container footprint without fake data.

Error | string | nullnullNo

Renders an actionable error state banner with optional retry trigger.

boolean | string | nullfalseNo

Renders a metric unavailability notice (e.g. permission restriction or retention limit).

04 / Cookbook & States

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.

<TwinlineCompare data={revenueData} xKey="month" primaryKey="actual2024" referenceKey="prior2023" primaryLabel="2024 Actual" referenceLabel="2023 Prior" />

Relative Percentage Variance

Focuses comparative tooltip inspection strictly on percentage delta (+25.0%) without clutter.

<TwinlineCompare data={latencyData} xKey="period" primaryKey="canary" referenceKey="baseline" deltaType="percentage" />

Scheduled Rate Tiers vs Observed

Discrete step interpolation comparing actual billing usage against contracted rate limits.

<TwinlineCompare data={tierData} xKey="hour" primaryKey="usage" referenceKey="limit" curve="step" primaryColor="#0ea5e9" referenceColor="#71717a" />

Compact Overview Widget

Condensed 200px tile comparison with clean legend and unified scale for executive summary cards.

<TwinlineCompare data={metrics} xKey="month" primaryKey="mrr" referenceKey="target" showYAxis={false} height={200} />
Lifecycle & Exception States
01. Loading State

Skeletons indicate runtime fetch or pending data queries.

02. Empty Data State

Handles empty collections ([]) gracefully without crashing.

03. Error State

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.
05 / Responsive Lab

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.

Desktop
>= 1024px

Full comparative tooltip with signed delta and percentage change, explicit legend with solid vs dashed indicator.

Tablet
640px - 1023px

Thinned X-axis labels, preserved line continuity, compact margins, tooltip pinned within viewport boundaries.

Mobile
< 640px

Edge-to-edge scrub inspection, compact legend pills, touch-first scrub interaction.

Tablet Preview (768px Container Constraint)
Mobile Preview (390px Container Constraint)

Accessibility & Keyboard Navigation

Twinline Compare conforms to WCAG 2.1 AAA and Section 508 accessibility guidelines:

  1. Keyboard Operable: Pressing Tab focuses the chart region with a prominent var(--chart-focus) ring.
  2. 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.
  3. Screen Reader Announcement: A live region announces both primary and reference observations along with the time label.
  4. Factual Figure Summary: An invisible <figcaption> provides an automated quantitative overview summarizing total observations, primary range, reference range, and final values without marketing spin.
06 / Assistive Technology

Accessibility & Navigation Standards

Screen-reader figure region with quantitative delta summary (net change across both primary and benchmark series).

Semantic Role & Landmark

Container mounts as region with explicit assistive label.

Color-Independent Legibility

Primary line is solid 2.5px while reference line is dashed 1.8px (dasharray 4 3), ensuring full clarity without relying on color.

Screen Reader Summary

Embeds visually hidden summary (.sr-only) declaring: “VoiceOver and NVDA announce primary vs reference series values and comparative delta without SVG node traversal.

Reduced Motion Support

Automatically suppresses stroke draw animation when user requests reduced motion.

Keyboard Interaction Model
Keyboard interaction model
KeyAction
TabFocus comparative chart region with prominent focus ring
ArrowLeftStep to previous comparative observation
ArrowRightStep to next comparative observation
HomeJump to first observation point
EndJump to latest observation point
EscapeClear active inspection state

Data Safety Guarantee

  1. Zero Fabricated Fallback Data: Twinline Compare will never synthesize fake comparison curves when inputs are missing.
  2. Finite Number Enforcement: Non-finite values (NaN, Infinity, -Infinity) are cleanly caught before calculation, preventing corrupt SVG path operations.
  3. Shared Scale Integrity: The vertical scale is calculated over the combined extent of both series, guaranteeing accurate visual comparison without misleading axis scaling.
  4. Zero Reference Division Protection: Prevents NaN% or Infinity% by falling back to "Unavailable" when the baseline reference is zero.
07 / Source Anatomy

Internal Architecture & File Dependencies

Source-first ownership model. Inspect the exact component call tree, dependencies, and full implementation below.

Component Architecture Call Tree
TwinlineCompare(Root figure wrapper)
└──ChartContainer[CSS token bridge]

Scoped CSS variables for axes, grid, and crosshairs without global pollution.

Involved Source Files & Registry Assets
components/charts/recharts/line-twin-compare.tsx
Primary TwinlineCompare comparative visualization component
components/charts/shared/chart-container.tsx
CSS variable token bridge and container wrapper
components/charts/shared/chart-tooltip.tsx
Tabular numerical inspection card
components/charts/shared/chart-legend.tsx
Solid vs dashed comparison legend
components/charts/shared/chart-state.tsx
Loading, empty, and error fallback states
components/charts/shared/use-chart-reduced-motion.ts
prefers-reduced-motion media query hook
registry/recharts/line-twin-compare.tsx
"use client"import * as React from "react"import {  ResponsiveContainer,  LineChart,  Line,  XAxis,  YAxis,  Tooltip,  CartesianGrid,  Legend,  type TooltipProps,} from "recharts"import { useChartReducedMotion } from "../shared/use-chart-reduced-motion"import { ChartLegend } from "../shared/chart-legend"import { ChartContainer } from "../shared/chart-container"import {  ChartLoadingState,  ChartEmptyState,  ChartErrorState,  ChartUnavailableState,} from "../shared/chart-state"import { cn } from "@/lib/utils"/* -------------------------------------------------------------------------- *//*  Type Definitions                                                          *//* -------------------------------------------------------------------------- */export type CurveType = "monotone" | "linear" | "step" | "natural"export type DeltaType = "absolute" | "percentage" | "both"export interface TwinlineSeriesConfig<TData extends Record<string, unknown> = Record<string, unknown>> {  key: keyof TData & string  label?: string  color?: string}export interface TwinlineCompareProps<TData extends Record<string, unknown> = Record<string, unknown>> {  /**   * The array of data observations to visualize.   * Accepts a readonly array and will not mutate caller data.   */  data: readonly TData[]  /**   * Key for the horizontal axis domain (e.g. date, month, or ordered category).   */  xKey: keyof TData & string  /**   * Key for the primary (dominant / current period) series.   */  primaryKey: keyof TData & string  /**   * Key for the reference (benchmark / previous period) series.   */  referenceKey: keyof TData & string  /**   * Human-readable label for the primary series.   * Default: "Primary"   */  primaryLabel?: string  /**   * Human-readable label for the reference series.   * Default: "Reference"   */  referenceLabel?: string  /**   * CSS color or token for the primary series.   * Default: "var(--chart-1)"   */  primaryColor?: string  /**   * CSS color or token for the reference series.   * Default: "var(--chart-2)"   */  referenceColor?: string  /**   * Curve interpolation algorithm.   * Default: "monotone"   */  curve?: CurveType  /**   * Height of the chart container in pixels or standard CSS string.   * Default: 340   */  height?: number | string  /**   * How delta is computed and displayed in the synchronized tooltip.   * "absolute" | "percentage" | "both"   * Default: "both"   */  deltaType?: DeltaType  /**   * Whether to invert delta semantics (e.g. for latency/errors where higher is worse).   * When inverted, negative delta is green/positive and positive delta is red/negative.   * Default: false   */  invertDelta?: boolean  /**   * Custom formatter for metric values.   */  valueFormatter?: (value: number) => string  /**   * Custom formatter for horizontal axis labels.   */  xFormatter?: (value: any) => string  /**   * Explicit numeric domain for the shared Y-axis [min, max].   * If omitted, safe domain spanning both series is calculated automatically.   */  domain?: [number, number] | ["auto", "auto"]  /**   * Handling of missing / null / undefined values.   * "gap" leaves a visual gap; "connect" bridges adjacent points.   * Default: "gap"   */  missingValuePolicy?: "gap" | "connect"  /**   * Whether to display Cartesian background grid lines.   * Default: true   */  showGrid?: boolean  /**   * Whether to render the series legend.   * Default: true   */  showLegend?: boolean  /**   * Whether to show horizontal category axis.   * Default: true   */  showXAxis?: boolean  /**   * Whether to show vertical value axis.   * Default: true   */  showYAxis?: boolean  /**   * Whether to render delta context in the synchronized hover tooltip.   * Default: true   */  showDeltaInTooltip?: boolean  /**   * Enable or disable entry and update transitions.   * Default: true   */  motion?: boolean | { duration?: number }  /**   * Accessible title announced by screen readers.   * Default: "Twinline Comparison Chart"   */  title?: string  /**   * Optional long-form description for assistive technologies.   */  description?: string  /**   * Loading state indicator.   */  loading?: boolean  /**   * Error state indicator or Error instance.   */  error?: Error | string | null  /**   * Unavailable state indicator.   */  unavailable?: boolean | string  /**   * Callback invoked when the user clicks retry in the error state.   */  onRetry?: () => void  /**   * Custom content overrides for state placeholders.   */  emptyContent?: React.ReactNode  errorContent?: React.ReactNode  loadingContent?: React.ReactNode  /**   * Additional CSS classes applied to the root figure element.   */  className?: string}/* -------------------------------------------------------------------------- *//*  Pure Data Safety & Domain Algorithms                                      *//* -------------------------------------------------------------------------- */function isFiniteNumber(val: unknown): val is number {  return typeof val === "number" && Number.isFinite(val)}/** * Calculates a single honest shared Y-domain spanning BOTH series. * Prevents zero-height scales and enforces identical units and scaling. */function calculateSharedDomain(  data: readonly Record<string, unknown>[],  primaryKey: string,  referenceKey: string,  explicitDomain?: [number, number] | ["auto", "auto"]): [number, number] | ["auto", "auto"] {  if (explicitDomain) {    return explicitDomain  }  const validValues: number[] = []  for (const item of data) {    const p = item[primaryKey]    const r = item[referenceKey]    if (isFiniteNumber(p)) validValues.push(p)    if (isFiniteNumber(r)) validValues.push(r)  }  if (validValues.length === 0) {    return [0, 100]  }  const min = Math.min(...validValues)  const max = Math.max(...validValues)  if (min === max) {    if (min === 0) return [-1, 1]    if (min > 0) return [Math.floor(min * 0.9), Math.ceil(min * 1.1)]    return [Math.floor(min * 1.1), Math.ceil(min * 0.9)]  }  const span = max - min  const pad = span * 0.06  const safeMin = min >= 0 ? Math.max(0, Math.floor(min - pad)) : Math.floor(min - pad)  const safeMax = Math.ceil(max + pad)  return [safeMin, safeMax]}/** * Normalizes dataset without mutating caller data: * - Drops invalid non-finite numbers * - Converts missing points to null for truthful gap rendering */function normalizeTwinlineData<TData extends Record<string, unknown>>(  data: readonly TData[],  xKey: string,  primaryKey: string,  referenceKey: string): Record<string, unknown>[] {  const normalized: Record<string, unknown>[] = []  for (let i = 0; i < data.length; i++) {    const raw = data[i]    const rawPrimary = raw[primaryKey]    const rawReference = raw[referenceKey]    const xVal = raw[xKey] ?? `Point ${i + 1}`    const cleanPrimary = isFiniteNumber(rawPrimary) ? rawPrimary : null    const cleanReference = isFiniteNumber(rawReference) ? rawReference : null    normalized.push({      ...raw,      [xKey]: xVal,      [primaryKey]: cleanPrimary,      [referenceKey]: cleanReference,    })  }  return normalized}/* -------------------------------------------------------------------------- *//*  Synchronized Comparison Tooltip Component                                 *//* -------------------------------------------------------------------------- */interface ComparisonTooltipContentProps {  active?: boolean  payload?: readonly { dataKey?: string | number; value?: any; [key: string]: any }[]  label?: React.ReactNode  primaryKey: string  referenceKey: string  primaryLabel: string  referenceLabel: string  primaryColor: string  referenceColor: string  valueFormatter?: (value: number) => string  deltaType: DeltaType  invertDelta?: boolean  showDelta?: boolean  isCompact?: boolean}function ComparisonTooltipContent({  active,  payload,  label,  primaryKey,  referenceKey,  primaryLabel,  referenceLabel,  primaryColor,  referenceColor,  valueFormatter,  deltaType,  invertDelta = false,  showDelta = true,  isCompact = false,}: ComparisonTooltipContentProps) {  if (!active || !payload || payload.length === 0) {    return null  }  const primaryItem = payload.find((p) => p.dataKey === primaryKey)  const referenceItem = payload.find((p) => p.dataKey === referenceKey)  const primaryVal = primaryItem?.value  const referenceVal = referenceItem?.value  const hasPrimary = isFiniteNumber(primaryVal)  const hasReference = isFiniteNumber(referenceVal)  const fmt = valueFormatter ?? ((v: number) => v.toLocaleString())  // Delta calculation  let absoluteDelta: number | null = null  let percentageDelta: number | null = null  let deltaState: "positive" | "negative" | "neutral" | "unavailable" = "unavailable"  if (hasPrimary && hasReference) {    absoluteDelta = primaryVal - referenceVal    if (referenceVal !== 0) {      percentageDelta = ((primaryVal - referenceVal) / Math.abs(referenceVal)) * 100    }    if (absoluteDelta > 0) {      deltaState = invertDelta ? "negative" : "positive"    } else if (absoluteDelta < 0) {      deltaState = invertDelta ? "positive" : "negative"    } else {      deltaState = "neutral"    }  }  const formatDeltaString = () => {    if (absoluteDelta === null) return "Unavailable"    const sign = absoluteDelta > 0 ? "+" : absoluteDelta < 0 ? "−" : ""    const absAbs = Math.abs(absoluteDelta)    if (deltaType === "absolute") {      return `${sign}${fmt(absAbs)}`    }    if (deltaType === "percentage") {      if (percentageDelta === null) return `${sign}${fmt(absAbs)}`      const pctSign = percentageDelta > 0 ? "+" : percentageDelta < 0 ? "−" : ""      return `${pctSign}${Math.abs(percentageDelta).toFixed(1)}%`    }    // "both"    if (percentageDelta !== null) {      const pctSign = percentageDelta > 0 ? "+" : percentageDelta < 0 ? "−" : ""      return `${sign}${fmt(absAbs)} (${pctSign}${Math.abs(percentageDelta).toFixed(1)}%)`    }    return `${sign}${fmt(absAbs)}`  }  return (    <div      className={cn(        "plotcn-interactive-tooltip z-50 rounded-lg border border-[var(--chart-tooltip-border)] bg-[var(--chart-tooltip-background)] shadow-md backdrop-blur-md select-none",        isCompact          ? "min-w-[120px] max-w-[180px] p-1.5 text-[10px]"          : "min-w-[200px] max-w-[280px] p-2.5 text-xs"      )}    >      <div        className={cn(          "tooltip-header font-mono font-medium text-[var(--chart-tooltip-muted)] truncate",          isCompact ? "mb-1 text-[10px]" : "mb-2 text-[11px]"        )}      >        {label}      </div>      <div className={isCompact ? "space-y-1" : "space-y-1.5"}>        {/* Primary Row */}        <div className={cn("tooltip-row flex items-center justify-between", isCompact ? "gap-1.5" : "gap-3")}>          <div className="flex items-center gap-1.5 min-w-0">            <span              className={cn("rounded-full shrink-0", isCompact ? "h-1.5 w-1.5" : "h-2 w-2")}              style={{ backgroundColor: primaryColor }}            />            <span className="font-medium text-[var(--chart-tooltip-foreground)] truncate">              {primaryLabel}            </span>          </div>          <span className="font-mono font-semibold text-[var(--chart-tooltip-foreground)] shrink-0">            {hasPrimary ? fmt(primaryVal) : "—"}          </span>        </div>        {/* Reference Row */}        <div className={cn("tooltip-row flex items-center justify-between", isCompact ? "gap-1.5" : "gap-3")}>          <div className="flex items-center gap-1.5 min-w-0">            <span              className={cn("rounded-full shrink-0", isCompact ? "h-1.5 w-1.5" : "h-2 w-2")}              style={{ backgroundColor: referenceColor }}            />            <span className="font-medium text-[var(--chart-tooltip-foreground)] truncate">              {referenceLabel}            </span>          </div>          <span className="font-mono font-semibold text-[var(--chart-tooltip-foreground)] shrink-0">            {hasReference ? fmt(referenceVal) : "—"}          </span>        </div>        {/* Delta Row */}        {showDelta && (          <div className="mt-2 flex items-center justify-between border-t border-[var(--chart-tooltip-border)] pt-1.5 text-[11px]">            <span className="text-[var(--chart-tooltip-muted)]">Delta</span>            <span              className={cn(                "font-mono font-medium",                deltaState === "positive" && "text-emerald-500 dark:text-emerald-400",                deltaState === "negative" && "text-rose-500 dark:text-rose-400",                deltaState === "neutral" && "text-[var(--chart-tooltip-muted)]",                deltaState === "unavailable" && "text-[var(--chart-tooltip-muted)] italic"              )}            >              {formatDeltaString()}            </span>          </div>        )}      </div>    </div>  )}/* -------------------------------------------------------------------------- *//*  Component Implementation                                                  *//* -------------------------------------------------------------------------- */export function TwinlineCompare<TData extends Record<string, unknown> = Record<string, unknown>>({  data = [],  xKey,  primaryKey,  referenceKey,  primaryLabel = "Primary",  referenceLabel = "Reference",  primaryColor = "var(--chart-1)",  referenceColor = "var(--chart-2)",  curve = "monotone",  height = 340,  deltaType = "both",  invertDelta = false,  valueFormatter,  xFormatter,  domain,  missingValuePolicy = "gap",  showGrid = true,  showLegend = true,  showXAxis = true,  showYAxis = true,  showDeltaInTooltip = true,  motion = true,  title = "Twinline Comparison Chart",  description,  loading = false,  error = null,  unavailable = false,  onRetry,  emptyContent,  errorContent,  loadingContent,  className,}: TwinlineCompareProps<TData>) {  const reducedMotion = useChartReducedMotion()  const containerId = React.useId().replace(/[:]/g, "")  const titleId = `twinline-title-${containerId}`  const descId = `twinline-desc-${containerId}`  const summaryId = `twinline-summary-${containerId}`  const [activeIndex, setActiveIndex] = React.useState<number | null>(null)  // Normalized safe data and shared honest domain  const safeData = normalizeTwinlineData(data, xKey, primaryKey, referenceKey)  const safeDomain = calculateSharedDomain(safeData, primaryKey, referenceKey, domain)  // Motion config  const isAnimated = motion !== false && !reducedMotion  const animationDuration =    typeof motion === "object" && motion?.duration !== undefined ? motion.duration * 1000 : 350  // Factual quantitative screen-reader summary (Strictly truthful, no marketing adjectives)  const factualSummary = React.useMemo(() => {    if (safeData.length === 0) return "No comparison data observations recorded."    const primaryValues = safeData      .map((d) => d[primaryKey])      .filter((v): v is number => isFiniteNumber(v))    const referenceValues = safeData      .map((d) => d[referenceKey])      .filter((v): v is number => isFiniteNumber(v))    if (primaryValues.length === 0 && referenceValues.length === 0) {      return "No valid numeric observations recorded for comparison."    }    const fmt = valueFormatter ?? ((n: number) => n.toLocaleString())    const pMin = primaryValues.length > 0 ? fmt(Math.min(...primaryValues)) : "none"    const pMax = primaryValues.length > 0 ? fmt(Math.max(...primaryValues)) : "none"    const pEnd = primaryValues.length > 0 ? fmt(primaryValues[primaryValues.length - 1]) : "none"    const rMin = referenceValues.length > 0 ? fmt(Math.min(...referenceValues)) : "none"    const rMax = referenceValues.length > 0 ? fmt(Math.max(...referenceValues)) : "none"    const rEnd = referenceValues.length > 0 ? fmt(referenceValues[referenceValues.length - 1]) : "none"    return `Comparative time-series visualizing ${safeData.length} observations. ${primaryLabel} spans from ${pMin} to ${pMax} ending at ${pEnd}. ${referenceLabel} spans from ${rMin} to ${rMax} ending at ${rEnd}. Both series share a single uniform vertical scale.`  }, [safeData, primaryKey, referenceKey, primaryLabel, referenceLabel, valueFormatter])  if (error) {    if (errorContent) return <div className={cn("w-full", className)} style={{ height }}>{errorContent}</div>    return (      <div className={cn("w-full", className)} style={{ height }}>        <ChartErrorState          title="Unable to load comparative data"          description={typeof error === "string" ? error : error.message}          onRetry={onRetry}        />      </div>    )  }  if (unavailable) {    return (      <div className={cn("w-full", className)} style={{ height }}>        <ChartUnavailableState          title="Comparison unavailable"          description={typeof unavailable === "string" ? unavailable : "Metrics are unavailable for this selection."}        />      </div>    )  }  if (loading) {    if (loadingContent) return <div className={cn("w-full", className)} style={{ height }}>{loadingContent}</div>    return (      <div className={cn("w-full", className)} style={{ height }}>        <ChartLoadingState          title="Loading comparative visualization…"          description="Synchronizing primary and reference datasets"        />      </div>    )  }  if (data.length === 0) {    if (emptyContent) return <div className={cn("w-full", className)} style={{ height }}>{emptyContent}</div>    return (      <div className={cn("w-full", className)} style={{ height }}>        <ChartEmptyState          title="No comparative observations"          description="Observations will appear when both series are recorded."        />      </div>    )  }  // Keyboard navigation across observations  const handleKeyDown = (e: React.KeyboardEvent) => {    if (safeData.length === 0) return    if (e.key === "ArrowRight") {      e.preventDefault()      setActiveIndex((prev) => (prev === null ? 0 : Math.min(safeData.length - 1, prev + 1)))    } else if (e.key === "ArrowLeft") {      e.preventDefault()      setActiveIndex((prev) => (prev === null ? safeData.length - 1 : Math.max(0, prev - 1)))    } else if (e.key === "Home") {      e.preventDefault()      setActiveIndex(0)    } else if (e.key === "End") {      e.preventDefault()      setActiveIndex(safeData.length - 1)    } else if (e.key === "Escape") {      e.preventDefault()      setActiveIndex(null)    }  }  return (    <figure      role="region"      aria-labelledby={titleId}      aria-describedby={description ? descId : summaryId}      tabIndex={0}      onKeyDown={handleKeyDown}      onBlur={() => setActiveIndex(null)}      className={cn(        "group relative flex flex-col w-full min-w-0 max-w-full outline-none focus-visible:ring-2 focus-visible:ring-[var(--chart-focus)] rounded-xl transition-all overflow-hidden",        className      )}      style={{ height }}    >      <figcaption className="sr-only">        <h3 id={titleId}>{title}</h3>        {description && <p id={descId}>{description}</p>}        <p id={summaryId}>{factualSummary}</p>      </figcaption>      <ChartContainer className="w-full h-full min-w-0 max-w-full overflow-hidden">        <ResponsiveContainer          width="100%"          height="100%"          minWidth={0}          minHeight={0}          initialDimension={{ width: 320, height: typeof height === "number" ? height : 340 }}        >          <LineChart            data={safeData}            margin={{ top: 14, right: 16, left: showYAxis ? -16 : 10, bottom: showXAxis ? 6 : 6 }}          >            {showGrid && (              <CartesianGrid                strokeDasharray="3 3"                vertical={false}                stroke="var(--chart-grid)"              />            )}            <XAxis              hide={!showXAxis}              dataKey={xKey as any}              tickLine={false}              axisLine={false}              tick={{ fontSize: 11, fill: "var(--chart-axis)" }}              tickFormatter={xFormatter}              dy={6}            />            <YAxis              hide={!showYAxis}              domain={safeDomain as any}              tickLine={false}              axisLine={false}              tick={{ fontSize: 11, fill: "var(--chart-axis)" }}              tickFormatter={valueFormatter ? (v) => valueFormatter(Number(v)) : undefined}              dx={-4}            />            <Tooltip              content={                <ComparisonTooltipContent                  primaryKey={primaryKey}                  referenceKey={referenceKey}                  primaryLabel={primaryLabel}                  referenceLabel={referenceLabel}                  primaryColor={primaryColor}                  referenceColor={referenceColor}                  valueFormatter={valueFormatter}                  deltaType={deltaType}                  invertDelta={invertDelta}                  showDelta={showDeltaInTooltip}                  isCompact={typeof height === "number" ? height <= 260 : false}                />              }              cursor={{                stroke: "var(--chart-crosshair)",                strokeDasharray: "3 3",                strokeWidth: 1.2,              }}            />            {showLegend && <Legend content={<ChartLegend />} />}            {/* Reference Series: Rendered First (Behind Primary), Dashed, Muted */}            <Line              type={curve}              dataKey={referenceKey}              name={referenceLabel}              stroke={referenceColor}              strokeWidth={1.8}              strokeDasharray="4 4"              dot={                safeData.length === 1                  ? { r: 4, fill: referenceColor, stroke: "var(--chart-background)", strokeWidth: 1.5 }                  : false              }              activeDot={{                r: 4.5,                fill: referenceColor,                stroke: "var(--chart-background)",                strokeWidth: 2,              }}              connectNulls={missingValuePolicy === "connect"}              isAnimationActive={isAnimated}              animationDuration={animationDuration}              animationEasing="ease-out"            />            {/* Primary Series: Rendered on Top, Solid, Prominent Stroke */}            <Line              type={curve}              dataKey={primaryKey}              name={primaryLabel}              stroke={primaryColor}              strokeWidth={2.5}              dot={                safeData.length === 1                  ? { r: 5, fill: primaryColor, stroke: "var(--chart-background)", strokeWidth: 2 }                  : false              }              activeDot={{                r: 5.5,                fill: primaryColor,                stroke: "var(--chart-background)",                strokeWidth: 2.5,              }}              connectNulls={missingValuePolicy === "connect"}              isAnimationActive={isAnimated}              animationDuration={animationDuration}              animationEasing="ease-out"            />          </LineChart>        </ResponsiveContainer>      </ChartContainer>      {/* Visual Indicator for Screen-Reader Exploration */}      <div className="sr-only" aria-live="polite">        {activeIndex !== null && safeData[activeIndex] && (          <span>            Point {activeIndex + 1} of {safeData.length}: {String(safeData[activeIndex][xKey])}.{" "}            {primaryLabel} is {String(safeData[activeIndex][primaryKey] ?? "missing")}.{" "}            {referenceLabel} is {String(safeData[activeIndex][referenceKey] ?? "missing")}.          </span>        )}      </div>    </figure>  )}