ApexCharts Donut Chart with Live Center Total — Free Snippet

ApexCharts Donut Chart with Live Center Total · Charts · Plain HTML, CSS & JS · Live preview

CategoryCharts

What's included

Features

Live centre total
Sums only visible slices via seriesTotals.
Accessible custom legend
Buttons with aria-pressed and amounts.
Slice-click toggling
dataPointSelection shares the legend's toggle.
Non-colour hidden state
Opacity plus strike-through.
Last-slice guard
Prevents an empty ring and a $0 total.
Clean click behaviour
Expand and active filters disabled.
Currency formatting
Consistent in centre, tooltip and legend.
Responsive layout
Legend moves below the chart on small screens.

About this UI Snippet

ApexCharts Donut Chart — A Centre Total That Updates When You Filter

Screenshot of the ApexCharts Donut Chart with Live Center Total snippet rendered live

A donut chart's centre is prime space, and the most useful thing to put there is usually the total. The interesting part is keeping that total honest: when someone hides "Housing" to see how the rest of the budget splits, the centre should show the sum of what is still visible, not the original grand total.

The total formatter runs on every redraw

plotOptions.pie.donut.labels.total shows a label in the hole when nothing is hovered. Its formatter receives the chart context w and reads w.globals.seriesTotals, which only includes slices that are currently shown. Because ApexCharts re-runs the formatter after every toggle, the centre stays correct without any manual bookkeeping.

A custom legend made of real buttons

The built-in legend is hidden and replaced with a list of <button> elements that show each category's amount. Each button carries aria-pressed, is keyboard focusable, and uses both opacity and strike-through for the hidden state, so the state is not conveyed by colour alone.

Slices and legend share one toggle

Clicking a slice fires events.dataPointSelection, which calls the same toggle() as the legend buttons. chart.toggleSeries(name) hides or shows a slice by its label. The default click effects (expandOnClick and the active-state filter) are disabled so a click only means "toggle".

Guarding the last slice

If every slice were hidden, the donut would be empty and the total would read $0. toggle() refuses to hide the last visible category.

Hover still works

Hovering a slice temporarily replaces the total with that category's name and amount, then the total returns.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste the snippet into an AI assistant like Claude and ask why seriesTotals gives a filtered total and how the custom legend and slice clicks share one toggle function. Ask it to add a percentage-of-visible column to the legend, a "reset" button, or a comparison ring showing last month. It can also review the legend for screen reader announcements when a category is hidden.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a budget donut chart with ApexCharts (loaded from a CDN) in plain HTML, CSS and JavaScript.

Requirements:
- Six categories with names, amounts and colours.
- A donut with a large hollow and a centre label that shows the total of the currently visible slices when idle, and the hovered category and amount on hover, formatted as currency.
- Hide the built-in legend and build an accessible legend of buttons showing colour, name and amount, with aria-pressed reflecting visibility and a strike-through for hidden categories.
- Toggling a category from the legend or by clicking its slice hides or shows that slice and updates the centre total.
- Prevent hiding the last visible category.
- Disable slice expansion on click and stack the legend under the chart on narrow screens.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="adn-wrap">
  <div class="adn-card">
    <div class="adn-title">Monthly Budget</div>
    <div class="adn-sub">Click a category to include or exclude it — the centre total follows</div>
    <div class="adn-body">
      <div id="adnChart"></div>
      <ul class="adn-legend" id="adnLegend" aria-label="Budget categories"></ul>
    </div>
  </div>
</div>

Step by step

How to Use

  1. 1
    Load ApexChartsInclude apexcharts.min.js from the CDN.
  2. 2
    Paste the snippetA budget donut renders with a custom legend beside it.
  3. 3
    Toggle a categoryClick its legend row or its slice; the centre total updates.
  4. 4
    Hover a sliceThe centre shows that category and amount while hovered.
  5. 5
    Use your dataEdit CATS with names, values and colours.

Real-world uses

Common Use Cases

Personal finance apps
Budgets and spending breakdowns.
Cloud cost dashboards
Spend by service with exclusions.
Traffic sources
Channels with a filtered total.
Survey results
Answer shares with categories toggled off.
Storage usage
File types summing to used space.
Related: Donut Chart (vanilla)
Build it without a library in Donut Chart.
Related: ApexCharts Radial Bar Goal Rings
For progress against targets see ApexCharts Radial Bar Goal Rings.

Got questions?

Frequently Asked Questions

Enable plotOptions.pie.donut.labels.show and labels.total.show, then give total a label and formatter. The formatter receives the chart context; sum w.globals.seriesTotals to get the total of visible slices.

ApexCharts calls the total formatter again after every redraw, and seriesTotals excludes hidden slices. That makes the centre a filtered total rather than a fixed grand total.

Call chart.toggleSeries with the slice's label, or hideSeries and showSeries for explicit control. For pie and donut charts the label identifies the slice.

A custom legend can show amounts, be made of real buttons with aria-pressed, and match your design. It also lets you add rules such as preventing the last slice from being hidden.

Set plotOptions.pie.expandOnClick to false, and set states.active.filter.type to 'none' to remove the darkening effect on the selected slice.