{"spec_id":"range-interval","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// range-interval: Range Interval Chart\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 86/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Annual base salary range by role, sorted by seniority (low to high midpoint).\n// Core Highcharts has no `columnrange` (that lives in the highcharts-more\n// module, which isn't loaded) — so the range is built as a stacked bar: an\n// invisible \"floor\" series holding each range's minimum, topped by a visible\n// series holding the span (max - min). The stack lands exactly on [min, max].\n// Highcharts renders stacked series in REVERSE array order by default (the\n// last series in `series[]` sits closest to zero) — so the visible span\n// series is declared FIRST and the invisible floor SECOND below, otherwise\n// the floor stacks on top and every bar appears anchored at 0.\nconst roles = [\n  \"Customer Support\",\n  \"Sales Associate\",\n  \"Marketing Specialist\",\n  \"UX Designer\",\n  \"Software Engineer\",\n  \"Data Scientist\",\n  \"Product Manager\",\n  \"Engineering Manager\",\n  \"Director of Engineering\",\n  \"VP of Engineering\",\n];\nconst minSalary = [38, 45, 50, 62, 75, 85, 90, 110, 140, 170];\nconst maxSalary = [52, 65, 72, 88, 110, 125, 130, 155, 195, 240];\n\n// The range series data holds plain `y` numbers; the original min/max values\n// are looked up by point index inside the dataLabels formatter below.\nconst rangeData = minSalary.map((low, i) => maxSalary[i] - low);\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"bar\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    spacingRight: 24,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"range-interval · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: {\n    categories: roles,\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  yAxis: {\n    min: 0,\n    max: 250,\n    tickInterval: 25,\n    endOnTick: false,\n    title: {\n      text: \"Annual base salary (US$ thousands)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    gridLineColor: t.grid,\n    labels: {\n      style: { color: t.inkSoft, fontSize: \"14px\" },\n      formatter: function () {\n        return \"$\" + this.value + \"K\";\n      },\n    },\n  },\n  legend: { enabled: false },\n  tooltip: { enabled: false },\n  plotOptions: {\n    series: { animation: false, stacking: \"normal\" },\n    bar: { borderWidth: 0, borderRadius: 3, pointPadding: 0.1, groupPadding: 0.15 },\n  },\n  series: [\n    {\n      name: \"Base salary range\",\n      data: rangeData,\n      color: t.palette[0],\n      dataLabels: {\n        enabled: true,\n        inside: true,\n        align: \"center\",\n        verticalAlign: \"middle\",\n        formatter: function () {\n          const i = this.point.index;\n          return \"$\" + minSalary[i] + \"K – $\" + maxSalary[i] + \"K\";\n        },\n        style: {\n          color: t.pageBg,\n          fontSize: \"13px\",\n          fontWeight: \"600\",\n          textOutline: \"none\",\n        },\n      },\n    },\n    {\n      name: \"Floor\",\n      data: minSalary,\n      color: \"transparent\",\n      enableMouseTracking: false,\n      showInLegend: false,\n      dataLabels: { enabled: false },\n    },\n  ],\n});\n"}