{"spec_id":"bar-heart-rate-zones","library":"echarts","language":"javascript","code":"// anyplot.ai\n// bar-heart-rate-zones: Time in Heart Rate Zones Bar Chart\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 90/100 | Created: 2026-06-14\n\nconst t = window.ANYPLOT_TOKENS;\nconst THEME = window.ANYPLOT_THEME;\n\n// Semantic zone colors mapped to Imprint palette\n// Fitness-platform convention (grey→blue→green→ochre→red) is a widely-shared\n// expectation — the semantic exception in default-style-guide.md applies here.\nconst Z1_GREY = THEME === \"light\" ? \"#6B6A63\" : \"#A8A79F\"; // Imprint muted\nconst zoneColors = [\n  Z1_GREY,    // Z1 Recovery  — muted grey (lowest intensity)\n  \"#4467A3\",  // Z2 Endurance — Imprint blue (pos 3)\n  \"#009E73\",  // Z3 Aerobic   — Imprint green (pos 1, brand)\n  \"#BD8233\",  // Z4 Threshold — Imprint ochre (pos 4, nearest orange)\n  \"#AE3030\",  // Z5 Maximum   — Imprint red (pos 5, semantic high-effort)\n];\n\n// Data — 60-minute tempo run (polarized training example)\nconst zoneNames  = [\"Z1\\nRecovery\", \"Z2\\nEndurance\", \"Z3\\nAerobic\", \"Z4\\nThreshold\", \"Z5\\nMaximum\"];\nconst hrRanges   = [\"< 111 bpm\", \"111–130 bpm\", \"130–148 bpm\", \"148–167 bpm\", \"> 167 bpm\"];\nconst minutes    = [8, 22, 15, 12, 3];\n\nconst barData = minutes.map((m, i) => ({\n  value: m,\n  itemStyle: { color: zoneColors[i] },\n}));\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n\n  title: {\n    text: \"bar-heart-rate-zones · javascript · echarts · anyplot.ai\",\n    subtext: \"60-min Tempo Run  ·  Total: 60 min  ·  Max HR: 185 bpm\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" },\n    subtextStyle: { color: t.inkSoft, fontSize: 14 },\n  },\n\n  tooltip: {\n    trigger: \"axis\",\n    axisPointer: { type: \"none\" },\n    backgroundColor: t.elevatedBg,\n    borderColor: t.grid,\n    textStyle: { color: t.ink, fontSize: 14 },\n    formatter: (params) => {\n      const idx = params[0].dataIndex;\n      return (\n        `<b>${zoneNames[idx].replace(/\\n/g, \" \")}</b><br/>` +\n        `Duration: <b>${minutes[idx]} min</b><br/>` +\n        `HR range: ${hrRanges[idx]}`\n      );\n    },\n  },\n\n  grid: {\n    left: 90,\n    right: 60,\n    top: 118,\n    bottom: 165,\n  },\n\n  xAxis: {\n    type: \"category\",\n    data: zoneNames,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 14,\n      lineHeight: 21,\n      margin: 14,\n      // Add HR boundary range as a third line below zone name\n      formatter: (value, index) => `${value}\\n{hr|${hrRanges[index]}}`,\n      rich: {\n        hr: { color: t.inkSoft, fontSize: 11, align: \"center\" },\n      },\n    },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n\n  yAxis: {\n    type: \"value\",\n    name: \"Time (minutes)\",\n    nameLocation: \"middle\",\n    nameGap: 65,\n    nameTextStyle: { color: t.ink, fontSize: 15 },\n    min: 0,\n    max: 27,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 14,\n      formatter: \"{value}\",\n    },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n\n  series: [\n    {\n      type: \"bar\",\n      data: barData,\n      barWidth: \"52%\",\n      itemStyle: { borderRadius: [4, 4, 0, 0] },\n      label: {\n        show: true,\n        position: \"top\",\n        formatter: (params) => `${params.value} min`,\n        color: t.ink,\n        fontSize: 16,\n        fontWeight: \"bold\",\n        distance: 8,\n      },\n      // Highlight Z4+Z5 as the high-intensity tempo-run signature\n      markArea: {\n        silent: true,\n        data: [\n          [\n            {\n              xAxis: \"Z4\\nThreshold\",\n              itemStyle: {\n                color: THEME === \"light\"\n                  ? \"rgba(174, 48, 48, 0.07)\"\n                  : \"rgba(174, 48, 48, 0.13)\",\n              },\n              label: {\n                show: true,\n                formatter: \"High Intensity\",\n                position: \"insideTop\",\n                color: t.inkSoft,\n                fontSize: 11,\n                fontStyle: \"italic\",\n                distance: 8,\n              },\n            },\n            { xAxis: \"Z5\\nMaximum\" },\n          ],\n        ],\n      },\n    },\n  ],\n});\n"}