Diverging Bar Chart — Chart.js

A diverging bar chart displays bars extending in opposite directions from a central baseline, typically at zero. This visualization is ideal for comparing positive and negative values, showing responses above and below a neutral point, or contrasting opposing categories. Different colors distinguish positive from negative values, making it easy to identify magnitude and direction at a glance.

Diverging Bar Chart rendered with Chart.js

Renders

JavaScript source (Chart.js)

// anyplot.ai
// bar-diverging: Diverging Bar Chart
// Library: chartjs 4.4.7 | JavaScript 22.23.2
// Quality: 95/100 | Created: 2026-08-18

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ----------------------------------------
// Employee engagement survey: net agreement score (% agree − % disagree) per
// topic, sorted descending so the pattern (strong positives to strong
// negatives) reads at a glance.
const topics = [
  "Career Growth",
  "Work-Life Balance",
  "Team Collaboration",
  "Remote Flexibility",
  "Job Security",
  "Training & Development",
  "Management Support",
  "Compensation",
];
const netScores = [62, 48, 31, 14, -8, -19, -33, -45];

const positiveColor = t.palette[0]; // #009E73 brand green — sentiment: agree
const negativeColor = t.palette[4]; // #AE3030 matte red — sentiment: disagree

// --- Mount -------------------------------------------------------------------
const canvas = document.createElement("canvas");
document.getElementById("container").appendChild(canvas);

// --- Title (fontsize scaled against this spec's own mandated-title length) --
// Only shrink once the descriptive prefix pushes the full title past 1.8x the
// bare mandated suffix ("bar-diverging · javascript · chartjs · anyplot.ai");
// short spec-ids like this one keep the full base size instead of being
// over-shrunk against a generic fixed-length reference.
const mandatedTitle = "bar-diverging · javascript · chartjs · anyplot.ai";
const title = `Employee Engagement Survey · ${mandatedTitle}`;
const titleFontSize = Math.round(26 * Math.min(1, (mandatedTitle.length * 1.8) / title.length));

// --- End-of-bar value labels (custom Chart.js plugin, canvas-drawn) ---------
// Gives every bar an explicit numeric focal point (e.g. Career Growth +62,
// Compensation -45) instead of relying on axis reading alone.
const valueLabelsPlugin = {
  id: "valueLabels",
  afterDatasetsDraw(chart) {
    const { ctx } = chart;
    const meta = chart.getDatasetMeta(0);
    ctx.save();
    ctx.font = "600 13px sans-serif";
    ctx.textBaseline = "middle";
    meta.data.forEach((bar, i) => {
      const value = netScores[i];
      ctx.fillStyle = value >= 0 ? positiveColor : negativeColor;
      ctx.textAlign = value >= 0 ? "left" : "right";
      ctx.fillText(`${value > 0 ? "+" : ""}${value}`, bar.x + (value >= 0 ? 8 : -8), bar.y);
    });
    ctx.restore();
  },
};

// --- Chart ---------------------------------------------------------------
new Chart(canvas, {
  type: "bar",
  data: {
    labels: topics,
    datasets: [
      {
        label: "Net agreement",
        data: netScores,
        backgroundColor: netScores.map((v) => (v >= 0 ? positiveColor : negativeColor)),
        borderWidth: 0,
        borderRadius: 3,
      },
    ],
  },
  plugins: [valueLabelsPlugin],
  options: {
    indexAxis: "y",
    responsive: true,
    maintainAspectRatio: false,
    animation: false,
    layout: { padding: { top: 20, right: 36, bottom: 16, left: 12 } },
    plugins: {
      title: { display: true, text: title, color: t.ink, font: { size: titleFontSize, weight: "500" } },
      legend: { display: false },
    },
    scales: {
      x: {
        min: -70,
        max: 70,
        title: { display: true, text: "Net Agreement Score (%)", color: t.ink, font: { size: 15 } },
        ticks: { color: t.inkSoft, font: { size: 13 } },
        grid: {
          color: (ctx) => (ctx.tick.value === 0 ? t.ink : t.grid),
          lineWidth: (ctx) => (ctx.tick.value === 0 ? 2 : 1),
        },
        border: { display: false },
      },
      y: {
        ticks: { color: t.inkSoft, font: { size: 14 } },
        grid: { display: false },
        border: { display: false },
      },
    },
  },
});

Part of Diverging Bar Chart on anyplot.ai.

Other implementations