Skip to main content

Renewables vs demand

Two quantities on the same axis, and the question is where one overtakes the other. Overlapping translucent areas answer it better than two lines do, because the gap between them is the thing you're reading.

The chart

Around Easter Sunday midday the teal area climbs out of the magenta one and stays out for 1 h 45 m — seven consecutive 15-minute samples, 12:15 through 13:45 CEST, peaking at 103.6% of load. Hover through it: the crosshair snaps to whichever of the two curves is nearer the pointer, so nudging across the gap flips the pill between 42 (wind + solar) and 40 (demand) at 13:00.

The data

Real measured data, the same fixture the grid mix card draws — German generation and load over Easter weekend 2025 at 15-minute cadence, from energy-charts.info (Fraunhofer ISE), CC BY 4.0, upstream ENTSO-E / SMARD. Two of its columns matter here:

ColumnMeaning
loadTotal demand, GW — 33.58 min, 49.04 max
wind + solarThe two weather-driven sources, added, in GW

The crossing is not a rounding artefact. At its widest, wind and solar were producing 41.59 GW against 40.18 GW of load — a 1.41 GW surplus, at 13:00 on Sunday. It happened on exactly one day of the weekend, and the conditions behind it are visible in both columns: Easter Sunday had the lowest average demand of any day in 2025 (39.59 GW), and solar peaked at 38.48 GW.

Solar alone never quite gets there: its own best moment is 94.7% of load, at 13:30 on Sunday. It takes wind's 3.45 GW in that same quarter-hour on top to cross. That is why the chart adds the two rather than plotting solar by itself — a "solar vs load" chart on this weekend is a chart of a near miss.

The time axis renders in your browser's timezone; clock times here are CEST.

Build it

1. One derived column. The chart wants wind and solar as a single series, which is one collapse — the same operator the stacked card uses eight times:

const series = gridMix().collapse(
['wind', 'solar'],
'renewable',
(v) => v.wind + v.solar,
{ append: true },
);

{ append: true } keeps wind, solar and load alongside the new column, so one series still feeds the whole chart.

2. Two areas, and the order matters. Layers paint in JSX order, so the second one is on top. Put demand first: it's the larger quantity most of the time, and the layer on top is the one whose outline you want to follow.

<Layers>
<AreaChart series={series} column="load" as="out" legend="Demand" />
<AreaChart series={series} column="renewable" as="in" legend="Wind + solar" />
</Layers>

3. Let the fills stay graded. This is the exact opposite of the stacked card. Here both areas keep the default graded, translucent fill, so the overlap reads as a blend and you can see the demand curve through the renewable one. A flat opaque fill would hide it. in / out are the theme's contrasting pair (the two-colour esnet traffic roles), which map neatly onto this data: power arriving from generation, power leaving to consumers.

Nothing needs a baseline prop — both areas rest on the axis floor, which is 0 here because <YAxis min={0}> pins it.

Options to try

OptionWhat it doesReach for it when
Layer orderWhich outline is unobscuredSwap when the smaller quantity is the subject
as="in" / "out"The two contrasting area rolesAny two-quantity comparison; they carry the theme's opposed hues
cursor="crosshair"A reticle + pill on the curve nearer the pointerReading either quantity off the chart; the default "line" cursor has no pill
curve="monotone"Smooths both outlinesHourly or coarser data, where the crossing shouldn't look like a corner
baseline={0}Pins the fill floor at zero instead of the axisA quantity that can go negative — see negative prices
A third <AreaChart>Adds another band to the comparisonSplitting renewable back into wind and solar, drawn over each other

One sharp edge: cursor="crosshair" draws one reticle per row, snapped to the single nearest sample — it is not a per-series fan-out, so it never shows both numbers at once. Two values at one instant means an off-chart readout fed by <ChartContainer onTrackerChanged>, whose TrackerInfo.values carries one sample per layer.

If you want the difference rather than the overlap, that's a different chart: collapse the two columns into renewable - load and draw one area with baseline={0}, which puts surplus above the line and deficit below.

See also

  • <AreaChart> — every prop, and the baseline forms
  • Theming — the area.in / area.out roles and their fillOpacity
  • Grid mix — the same weekend as an eight-band stack, where the fills must be opaque instead
  • Negative prices — what this surplus did to the day-ahead auction
  • Storybook — the systematic knob walk