013 / RECHARTS / AREA
Percent Stream Area
100% normalized stacked area chart for tracking how each contributor's share of the visible whole evolves over an ordered domain.
- SPEC
- #013
- ENGINE
- Recharts
- FAMILY
- Area
- RENDERER
- svg
- STATUS
- preview
Installation
Checking public registry…
View local registry JSONCopied as source into your project (requires recharts).
Overview
Percent Stream Area is the composition-specialized chart of the Plotcn Area family. It normalizes multiple additive series so that their combined visual height strictly equals 100% of the visible whole at every observation point across an ordered domain.
The primary analytical question answered by Percent Stream Area is:
"How is the composition of the whole changing over time?"
The secondary analytical question is:
"What underlying raw values produced those proportional shares?"
Unlike general stacked area charts that communicate changes in absolute scale, Percent Stream Area deliberately removes total-size information from its geometry. A period with 1,000 total requests and a period with 10,000 total requests produce visually identical geometry if their constituent platform shares are both 50% / 30% / 20%.
import { PercentStreamArea } from "@/components/charts/recharts/area-percent-stream"const trafficData = [ { month: "Jan", web: 500, ios: 300, android: 200 }, { month: "Feb", web: 5000, ios: 3000, android: 2000 }, { month: "Mar", web: 4800, ios: 3200, android: 2000 }, { month: "Apr", web: 4000, ios: 3600, android: 2400 }, { month: "May", web: 3600, ios: 3700, android: 2700 }, { month: "Jun", web: 3300, ios: 3900, android: 2800 },]export function MonthlyTrafficMix() { return ( <PercentStreamArea data={trafficData} xKey="month" series={[ { key: "web", label: "Web" }, { key: "ios", label: "iOS" }, { key: "android", label: "Android" }, ]} showLegend interactiveLegend lockableTooltip valueFormatter={(v) => `${v.toLocaleString()} req/s`} showGrid /> )}Area-Family Positioning
The Plotcn Area family provides three distinct analytical instruments:
| Consideration | Prism Area (011) | Stack Flow Area (012) | Percent Stream Area (013) |
|---|---|---|---|
| Primary Question | "How much magnitude exists relative to baseline?" | "How do parts and total magnitude change?" | "How does the composition of the whole evolve?" |
| Input Structure | Single series (series={...}) | Multiple additive series (series={[...]}) | Multiple additive series (series={[...]}) |
| Scale Domain | Absolute values () | Absolute stack sum () | Strictly normalized () |
| Baseline Rule | Configurable ("zero", "domain-min") | Fixed at zero baseline () | Fixed at zero percentage () |
| Total Magnitude | Explicitly visible in height | Explicitly visible in top silhouette | Deliberately removed from geometry |
| Unit Constraint | One quantitative unit | Shared additive unit | Shared additive unit before normalization |
Composition Model & Mathematical Normalization
Geometry Represents Relative Shares; Total Magnitude Is Deliberately Removed
1. Governing Mathematical Model
For all valid visible series at observation :
For each individual series :
2. Normalization Is Derived Data
Plotcn never mutates caller data. Your original records (e.g. { month: "Jan", web: 500, ios: 300, android: 200 }) remain completely untouched. Normalization occurs in a pure derived step, converting raw counts into internal percentage shares () for SVG rendering while retaining original quantities for tooltips and screen readers.
3. High Floating-Point Precision
Internal shares retain complete 64-bit floating-point precision throughout path generation. Even if display rounding formats individual rows as 33.3% + 33.3% + 33.3% = 99.9%, the underlying stacked SVG geometry terminates precisely at the 100% boundary.
Missing & Zero-Total Semantics
In 100% normalized composition, Plotcn enforces a strict distinction across three analytical states:
| Scenario | Raw Observations | Resolved State | Tooltip Readout | Geometry Behavior |
|---|---|---|---|---|
| Known Zero | web: 80, ios: 20, android: 0 | valid | 0.0% (raw 0) | Valid 0-height layer; remaining layers stack to 100% |
| Missing Contributor | web: 80, ios: null, android: 20 | incomplete | — (Composition Incomplete) | Honest break (gap); no deceptive subtotal normalization |
| Zero Total | web: 0, ios: 0, android: 0 | zero-total | share unavailable (raw 0) | Zero stacked height; no divide-by-zero, NaN%, or Infinity% |
1. Known Zero Is a Valid Measurement ()
A contributor recording 0 is not missing. It represents a truthful, known contribution of zero. The series contributes 0 height, and the remaining series normalize accurately.
2. Missing Is Not Zero (null ≠ 0)
Under the default missingValuePolicy="gap", an absent contributor (null or undefined) is unknown. Plotcn never normalizes known partial subtotals to 100% (e.g., calculating when iOS is missing), because doing so would fabricate a false total and misrepresent market share. Instead, the geometry breaks cleanly, and the tooltip reports Composition Incomplete.
3. Zero Total Is Mathematically Undefined ()
When all visible contributors record zero, calculating percentage shares would divide by zero. Plotcn handles this gracefully: zero total is classified explicitly as "zero-total", tooltips report raw zeros with share unavailable, and screen readers announce that percentages are undefined.
Legend & Visible-Series Re-Normalization
Toggling a Series Changes the Normalization Basis (Visible Total = 100%)
Denominator = 50 + 30 + 20 = 100. Each raw contribution maps directly to its initial share.
Denominator = 50 + 30 = 80. Remaining layers re-normalize to 100% without color shifts.
When an interactive legend is enabled, clicking a series toggles its visibility. Plotcn implements Model A: Visible-Series Normalization:
- Dynamic Visible Basis: Hiding a series recalculates the normalization denominator across remaining visible contributors:
- Stable Identity Preservation: Hiding a series never shifts or reassigns color tokens. If
Webis assigned--chart-1,iOS--chart-2, andAndroid--chart-3, hidingiOSleavesAndroidbound to--chart-3. - Single Visible Series: If only one visible series remains, its share truthfully normalizes to across all positive observations.
- All Series Hidden: If all series are toggled off, Percent Stream Area renders an accessible, recoverable prompt ("All Series Hidden") with active legend buttons to restore visibility without page reloading.
Data & Series Contracts
Percent Stream Area accepts an ordered array of data records:
type TrafficMixDatum = { month: string | Date web: number | null ios: number | null android: number | null}Series Configuration Object
interface PercentStreamSeries<TData> { key: NumericKeyOf<TData> label: string color?: string valueFormatter?: (value: number) => string}Contract Requirements
- Same Unit Requirement: All contributors must share the identical physical, operational, or financial unit (e.g. all requests, all users, or all dollars). Do not stack disparate metrics like revenue and latency.
- Non-Negative Values: In V1, additive composition requires non-negative quantities (). If negative values are detected, Plotcn halts rendering and presents a truthful
ChartErrorStateexplaining that signed values violate 100% composition semantics. Negative values are never silently clamped.
Installation
Install Percent Stream Area directly into your project using the shadcn CLI:
Checking public registry…
View local registry JSONCopied as source into your project (requires recharts).
Component Props
| Property | Type | Default | Required | Description |
|---|---|---|---|---|
data | readonly TData[] | [] | Required | Readonly array of observation records. Caller data is never mutated. |
xKey | keyof TData & string | — | Required | Property name on data records for horizontal domain coordinates. |
series | readonly PercentStreamSeries<TData>[] | — | Required | Canonical array of additive series. Stack order is strictly bottom-to-top. |
height | number | string | 320 | Optional | Container height in pixels or CSS dimension string. |
curve | "monotone" | "linear" | "step" | "monotone" | Optional | Curve interpolation algorithm for layer boundaries. |
fillOpacity | number | 0.75 | Optional | Layer fill opacity between 0 and 1. |
gradientMode | "none" | "vertical-fade" | "none" | Optional | Decorative gradient treatment. Never encodes data certainty. |
selectionColor | string | "var(--chart-selection)" | Optional | Accent color for pinned crosshair and inspection markers. |
showGrid | boolean | true | Optional | Whether to render subtle horizontal Cartesian grid reference lines. |
showXAxis | boolean | true | Optional | Whether to render horizontal category scale. |
showYAxis | boolean | true | Optional | Whether to render vertical percentage ticks (0%, 25%, 50%, 75%, 100%). |
showLegend | boolean | true | Optional | Whether to render the series identity legend. |
interactiveLegend | boolean | true | Optional | Enables pointer and keyboard series visibility toggles. |
lockableTooltip | boolean | true | Optional | Enables persistent tooltip locking via click or Enter/Space. |
missingValuePolicy | "gap" | "zero" | "gap" | Optional | Handling of missing values: 'gap' marks incomplete; 'zero' substitutes 0. |
animation | "draw" | "fade" | "none" | "draw" | Optional | Entry reveal animation. Bypassed automatically under reduced motion. |
shareFormatter | (share: number) => string | ${share.toFixed(1)}% | Optional | Formatter for normalized percentage readouts in tooltips. |
valueFormatter | (value: number) => string | n.toLocaleString() | Optional | Formatter for raw underlying quantities in tooltips and tables. |
title | string | "Percent Stream Area Chart" | Optional | Accessible title for screen readers. |
description | string | undefined | Optional | Accessible description detailing 100% normalized composition. |
Keyboard Navigation Reference
Percent Stream Area provides a single focused entry point for Cartesian exploration:
| Key | Control Target | Action |
|---|---|---|
| → / ArrowRight | Plot Inspection | Inspect next chronological observation |
| ← / ArrowLeft | Plot Inspection | Inspect previous chronological observation |
| Home | Plot Inspection | Jump inspection to the first observation |
| End | Plot Inspection | Jump inspection to the latest observation |
| Enter or Space | Plot Inspection | Lock or unlock persistent inspection at the active coordinate |
| Escape | Plot Inspection | Dismiss locked inspection and release pinned tooltip |
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.
<PercentStreamArea
data={data}
xKey="date"
seriesKey="value"
/>Curve interpolation algorithm shared across all stacked area boundaries.
curve="monotone"Default: "monotone"Opacity of normalized area layers (0.1 to 1.0) ensuring layers remain distinguishable.
fillOpacity={0.75}Default: 0.75Gradient fill treatment: "none" (solid translucent) or "vertical-fade" (restrained top-to-bottom fade).
gradientMode="none"Default: "none"Whether to render subtle horizontal Cartesian grid reference lines.
showGrid={true}Default: trueWhether to render the series identity legend.
showLegend={true}Default: trueWhether legend items can be clicked/keyboard-activated to toggle layer visibility and re-normalize the visible whole.
interactiveLegend={true}Default: trueWhether clicking or pressing Enter/Space pins the currently inspected X datum.
lockableTooltip={true}Default: trueHandling of missing observations: 'gap' marks composition incomplete; 'zero' substitutes 0.
missingValuePolicy="gap"Default: "gap"| Property | Type | Default | Required | Description |
|---|---|---|---|---|
dataReq | readonly TData[] | [] | Yes | Readonly array of observation records. Caller data is never mutated or sorted in place. |
xKeyReq | keyof TData & string | — | Yes | Property name on data records for horizontal domain coordinates. |
seriesReq | readonly PercentStreamSeries<TData>[] | [] | Yes | Ordered array of additive series definitions. Geometry stacks from series[0] at bottom to series[n-1] at top. |
heightOpt | number | string | 320 | No | Container height in pixels or standard CSS dimension strings. |
curveOpt | "monotone" | "linear" | "step" | "monotone" | No | Curve interpolation algorithm shared across all stacked area boundaries. |
fillOpacityOpt | number | 0.75 | No | Opacity of normalized area layers (0.1 to 1.0) ensuring layers remain distinguishable. |
gradientModeOpt | "none" | "vertical-fade" | "none" | No | Gradient fill treatment: "none" (solid translucent) or "vertical-fade" (restrained top-to-bottom fade). |
string | "var(--chart-selection)" | No | Accent color for the locked crosshair and selection pin. | |
showGridOpt | boolean | true | No | Whether to render subtle horizontal Cartesian grid reference lines. |
showXAxisOpt | boolean | true | No | Whether to render the horizontal category scale. |
showYAxisOpt | boolean | true | No | Whether to render the vertical percentage scale (0%–100%). |
showLegendOpt | boolean | true | No | Whether to render the series identity legend. |
boolean | true | No | Whether legend items can be clicked/keyboard-activated to toggle layer visibility and re-normalize the visible whole. | |
boolean | true | No | Whether clicking or pressing Enter/Space pins the currently inspected X datum. | |
"gap" | "zero" | "gap" | No | Handling of missing observations: 'gap' marks composition incomplete; 'zero' substitutes 0. | |
animationOpt | "draw" | "fade" | "none" | "draw" | No | Entry reveal animation mode. |
(share: number) => string | (s) => `${s.toFixed(1)}%` | No | Custom formatter for normalized percentage shares in tooltips. | |
(value: number) => string | n.toLocaleString() | No | Custom formatter for raw underlying quantities in tooltips and tables. | |
titleOpt | string | "Percent Stream Area Chart" | No | Accessible heading announced to screen readers. |
descriptionOpt | string | undefined | No | Accessible descriptive summary announced to screen readers. |
Component Variants & Edge States
Production cookbooks showcasing configuration variants alongside verified handling of loading, empty data, and network error states.
Traffic Mix Evolution
Tracking client platform share evolution over six months while underlying raw traffic scales 10x.
Subscription Tier Mix
Monthly customer account mix across Free, Pro, and Enterprise tiers.
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
Percent Stream Area preserves full 100% normalized layer composition and deterministic color mappings across all viewport widths without dropping series.
Full horizontal percentage ticks (0%, 25%, 50%, 75%, 100%), inline interactive legend, and spacious multi-column tooltip.
Adaptive wrapped interactive legend, thinned domain ticks, and continuous 100% normalized geometry.
Compact percentage Y-axis, compact stacked tooltip, scrollable legend. Series are never silently dropped.
Accessibility & Navigation Standards
Single keyboard tab stop on root figure with ArrowLeft, ArrowRight, Home, End, Enter, and Escape shortcuts. Separate standard button tab stops for interactive legend toggles.
Container mounts as region with explicit assistive label.
Deterministic series order, distinct stroke boundaries, interactive fill swatches, and structured data table provide accessible non-color differentiation.
Embeds visually hidden summary (.sr-only) declaring: “Figure element announces chart title and 100% normalized composition summary. Live region announces inspected coordinate, visible shares, and raw quantities.”
All entrance reveal animations and restacking transitions immediately bypass when prefers-reduced-motion is detected.
| Key | Action |
|---|---|
| ArrowRight | Inspect next chronological observation across visible series. |
| ArrowLeft | Inspect previous chronological observation across visible series. |
| Home | Jump inspection directly to the first observation. |
| End | Jump inspection directly to the final observation. |
| Enter / Space | Lock or unlock persistent inspection at the active coordinate. |
| Escape | Release locked selection and dismiss active inspection tooltip. |
| Tab | Move focus to interactive series visibility controls in the legend. |
Data Safety Checklist
- ✓ Total Magnitude Deliberately Removed: Geometry strictly communicates relative shares; total volume changes are not visible in polygon height.
- ✓ Input Immutability: Caller data arrays and series configuration objects are never mutated.
- ✓ Missing Contributor Never Coerced to 0%: Under default
gappolicy, missing data breaks the stack honestly (null ≠ 0). - ✓ Zero Total Handled Safely: avoids divide-by-zero,
NaN%, orInfinity%. - ✓ Non-Negative Enforcement: Negative values trigger a descriptive error state rather than silent clamping.
- ✓ Stable Series Identity: Canonical series index determines palette tokens; toggling series in the legend never reassigns colors.
- ✓ Floating-Point Precision: Internal geometry calculations use full precision to reach the exact 100% boundary.
- ✓ Responsive Integrity: Narrow viewports simplify labels and ticks; they never silently remove series to save space.
Related Charts
- Stack Flow Area: Multi-series additive stacking for communicating both individual contributions and changing total magnitude.
- Prism Area: Single-series magnitude visualization anchored to an explicit baseline.
- Multi-Signal Line: Multi-series trend comparison without area filling or composition constraints.
Internal Architecture & File Dependencies
Source-first ownership model. Inspect the exact component call tree, dependencies, and full implementation below.
Handles container dimension measurement and CSS token scoping
Coordinates coordinate scales, 0%–100% Y-axis, Cartesian grid, and Area polygon rendering with stackId
Interactive button controls with rectangular area swatches and visible-series re-normalization