{"spec_id":"donut-nested","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// donut-nested: Nested Donut Chart\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-18\n\n//# anyplot-orientation: square\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Annual revenue ($M) by business unit (inner ring) and product line (outer ring).\nconst businessUnits = [\n  {\n    name: \"Cloud Services\",\n    color: t.palette[0],\n    products: [\n      { name: \"Compute\", value: 42 },\n      { name: \"Storage\", value: 27 },\n      { name: \"Networking\", value: 15 },\n    ],\n  },\n  {\n    name: \"Hardware\",\n    color: t.palette[1],\n    products: [\n      { name: \"Servers\", value: 24 },\n      { name: \"Laptops\", value: 19 },\n      { name: \"Peripherals\", value: 8 },\n    ],\n  },\n  {\n    name: \"Software Licensing\",\n    color: t.palette[2],\n    products: [\n      { name: \"Enterprise\", value: 33 },\n      { name: \"SMB\", value: 12 },\n    ],\n  },\n  {\n    name: \"Consulting\",\n    color: t.palette[3],\n    products: [\n      { name: \"Implementation\", value: 14 },\n      { name: \"Training\", value: 9 },\n      { name: \"Support\", value: 7 },\n    ],\n  },\n];\n\n// Inner ring: one slice per business unit, sized by the sum of its products.\nconst innerData = businessUnits.map((unit) => ({\n  name: unit.name,\n  y: unit.products.reduce((sum, product) => sum + product.value, 0),\n  color: unit.color,\n}));\n\n// Outer ring: one slice per product, tinted around its parent's hue so the\n// color family (same hue, varying lightness) reads as belonging to that unit.\n// Because both rings share the same order and total, slice boundaries line up.\nconst outerData = businessUnits.flatMap((unit) =>\n  unit.products.map((product, i) => {\n    const offset = (i - (unit.products.length - 1) / 2) * 0.18;\n    return {\n      name: product.name,\n      y: product.value,\n      color: Highcharts.color(unit.color).brighten(offset).get(),\n    };\n  }),\n);\n\n// Only the largest product-line slices earn a direct inline label; the rest\n// fall back to the legend, per the spec's \"labels on larger segments\" guidance.\nconst outerLabelThreshold = 15; // percent of the outer ring's total\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"pie\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"donut-nested · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Revenue by Business Unit and Product Line ($M)\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n    align: \"center\",\n    verticalAlign: \"bottom\",\n    layout: \"horizontal\",\n  },\n  tooltip: {\n    backgroundColor: t.elevatedBg,\n    borderColor: t.grid,\n    style: { color: t.ink, fontSize: \"13px\" },\n    pointFormat: \"<b>{point.name}</b>: ${point.y}M ({point.percentage:.1f}%)\",\n  },\n  plotOptions: {\n    series: { animation: false },\n    pie: {\n      borderWidth: 2,\n      borderColor: t.pageBg,\n      borderRadius: 3,\n    },\n  },\n  series: [\n    {\n      name: \"Business Unit\",\n      innerSize: \"38%\",\n      size: \"64%\",\n      showInLegend: false,\n      data: innerData,\n      dataLabels: {\n        enabled: true,\n        distance: -55,\n        format: \"{point.name}\",\n        style: {\n          color: t.ink,\n          fontSize: \"14px\",\n          fontWeight: \"600\",\n          textOutline: `3px ${t.pageBg}`,\n        },\n      },\n    },\n    {\n      name: \"Product Line\",\n      innerSize: \"68%\",\n      size: \"96%\",\n      showInLegend: true,\n      data: outerData,\n      dataLabels: {\n        enabled: true,\n        distance: -55,\n        formatter() {\n          return this.percentage > outerLabelThreshold ? this.point.name : null;\n        },\n        style: {\n          color: t.ink,\n          fontSize: \"11px\",\n          fontWeight: \"600\",\n          textOutline: `2px ${t.pageBg}`,\n        },\n      },\n    },\n  ],\n});\n"}