Data✨ Premium

Morphing Pie Chart

Conic-gradient pie chart whose slices glide from one data set to the next; the legend recomputes the percentages on every change.

CSS@propertyMorph

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Data has 28 effects, including 1 free. Explore the category →

3 usage examples

A community garden's plots, season after season — Morphing Pie Chart example 1

① A community garden's plots, season after season

WhenThe home page of a community garden: the plot map on the left, on the right the season name, the vegetables / flowers / herbs / fallow pie and two buttons. "Next season" (or a click anywhere in the scene) turns the slices towards the following season.
WhyFour states in a row tell a cycle; seeing fallow grow in winter and vegetables return in summer is understood without reading a single number.
Settings--mpie-size:132px, colours --mpie-c1:#16a34a; --mpie-c2:#ec4899; --mpie-c3:#84cc16; --mpie-c4:#b7791f, olive-brown legend on cream, caption moved to the top of the column (order:-1 on .mpie-caption), transparent background.
Where a home's electricity comes from, hour by hour — Morphing Pie Chart example 2

② Where a home's electricity comes from, hour by hour

WhenA solar installation app: the "Where your electricity comes from" card splits consumption between panels, battery and grid. Every 1.4 s the scene moves to another time of day (morning, midday, evening, night) and the tip of the moment changes with it.
WhyThree moving slices make the shift from solar to battery to grid visible, which an hourly table does not show at a glance.
SettingsOnly three slices, --mpie-size:118px, --mpie-c1:#f59e0b; --mpie-c2:#10b981; --mpie-c3:#64748b, track #e2e8f0, shadow removed, --mpie-speed:1s.
Round points in the corner of an online card table — Morphing Pie Chart example 3

③ Round points in the corner of an online card table

WhenDuring a four-player card game, a small panel in the top-right corner shows how the round's points are shared. At the end of each round (every 1.6 s in the scene), the slices redistribute and the round number moves on.
WhyPlayers look at their cards, not at a table: a 64 px pie moving in the corner tells who dominated the round without interrupting play.
Settings--mpie-size:64px, legend and disc side by side without wrapping, bright colours on green felt, track rgba(255,255,255,.12), --mpie-speed:.9s.

How it works

The .mpie-pie disc is a six-colour conic-gradient whose boundaries are variables: --mpie-s1 to --mpie-s6, the running total of the slices. Declared with @property as <percentage>, they can be animated: when they change, the borders between slices rotate smoothly (1.2 s) instead of jumping.

The data is the legend itself: each <li class="mpie-slice" data-value="…"> is one slice (2 to 6). The script divides each value by the total, sets the cumulative boundaries on the disc and writes the rounded percentage next to the label; rounding is distributed so the displayed total is always 100%. Values can be raw counts (plots, points, kWh).

To switch to another data set, change the data-value attributes: one MutationObserver per instance re-runs the calculation. The .mpie-caption text under the chart ("State A") is free text; update it at the same time to name the new state.

The first time the chart scrolls into view (150 ms later), every boundary starts at 0% and glides to its value together: the disc, empty at first (--mpie-track colour), fills clockwise, with all slices growing at once and already in their final proportions.

Accessibility

  • The legend carries the data: it is a real <ul> named by aria-label ("Breakdown"), each item reads "label, percentage". The disc is aria-hidden as it adds nothing more for a screen reader.
  • Colour and text: each legend dot uses its slice colour; the written percentage avoids having to compare close hues (pink and red, for example).
  • Reduced motion: under prefers-reduced-motion: reduce, the slice transition is turned off, the disc goes straight to the new state and is full on first display.
  • Silent changes: the legend is not an aria-live region. When users trigger the change themselves (a "Next season" button), focus stays on their button and they can reread the list.

Browser compatibility

conic-gradient, @property (for the glide), aspect-ratio, MutationObserver, IntersectionObserver. Measured in Chromium 151, Firefox 153 and WebKit 26.5: 150 ms after a change the first boundary sits between the old and new value in all three engines; two independent pies, 390 px, white background, no JS error.

Chrome 88+✓ Full (measured)
Firefox 128+✓ Full (measured); from 89 to 127, slices change without gliding
Safari 16.4+✓ WebKit engine measured, not checked in real Safari
Edge 88+✓ Full (Chromium engine)
Safari iOS 16.4+✓ Not checked on a real device
Chrome Android 88+✓ Full (Chromium engine)

Without @property the pie is correct but switches states without animation. Without JavaScript the disc stays empty and the legend shows the labels without percentages.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0246-root">
  <div class="mpie-wrap">
    <div class="mpie-pie" aria-hidden="true"></div>
    <ul class="mpie-legend" aria-label="Breakdown">
      <li class="mpie-slice" data-value="30">Part A</li>
      <li class="mpie-slice" data-value="25">Part B</li>
      <li class="mpie-slice" data-value="25">Part C</li>
      <li class="mpie-slice" data-value="20">Part D</li>
    </ul>
  </div>
  <p class="mpie-caption">State A</p>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
data-value on each .mpie-slice (HTML) 30, 25, 25, 20 Slice value (any unit, the script computes the percentages). Changing it makes the pie glide.
Number of slices (HTML) 4 From 2 to 6 .mpie-slice items; any beyond six are ignored.
.mpie-caption (HTML) "State A" Free text under the chart, to update along with the data; remove it if not needed.
--mpie-c1 to --mpie-c6 (CSS) #8b5cf6, #06b6d4, #f43f5e, #f59e0b, #10b981, #ec4899 Slice and legend dot colours, in list order.
--mpie-track (CSS) rgba(255,255,255,.08) Colour of the empty disc, visible before it fills.
--mpie-size (CSS) 150px Disc diameter; it never exceeds the container width.
--mpie-speed (CSS) 1.2s Duration of the glide between two states.
--mpie-text, --mpie-accent (CSS) 70% white, #a78bfa Legend and state text colours.
--mpie-bg, --mpie-min-height (CSS) #0a0a0f, 300px Root background and minimum height.

FAQ

Keep your states in an array and apply them on demand: root.querySelectorAll('.mpie-slice').forEach((li, i) => li.setAttribute('data-value', state[i])), then change the .mpie-caption text. Hook that function to your own button, a timer or a tab.
No. The script adds the values up and computes each share; you can feed hours, euros or votes. The displayed percentages are rounded so that their total is exactly 100.
Yes, by adding a mask to .mpie-pie: mask-image: radial-gradient(closest-side, transparent 55%, #000 56%) (plus its -webkit-mask-image form). The centre becomes transparent and your page background shows through.