{"spec_id":"bar-grouped","library":"makie","language":"julia","code":"# anyplot.ai\n# bar-grouped: Grouped Bar Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 94/100 | Created: 2026-08-05\n\nusing CairoMakie\nusing Colors\n\n# --- Theme tokens -------------------------------------------------------\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\n\n# --- Data -----------------------------------------------------------------\n# Quarterly hardware/software/services revenue ($M) across five regions.\ncategories = [\"N. America\", \"Europe\", \"Asia-Pacific\", \"Latin America\", \"MEA\"]\ngroups     = [\"Hardware\", \"Software\", \"Services\"]\n\n# rows = categories, cols = groups\n# Asia-Pacific breaks the otherwise-monotonic Hardware > Software > Services\n# ranking (Software leads there), keeping the comparison less predictable.\nrevenue = [\n    42.0 28.0 15.0\n    35.0 31.0 18.0\n    20.0 29.0 12.0\n    18.0 14.0  9.0\n    12.0  9.0  6.0\n]\n\nn_cat = length(categories)\nn_grp = length(groups)\n\nx      = repeat(1:n_cat, inner = n_grp)\ndodge  = repeat(1:n_grp, outer = n_cat)\nheight = vec(permutedims(revenue))\ncolors = IMPRINT_PALETTE[repeat(1:n_grp, outer = n_cat)]\n\n# --- Plot -------------------------------------------------------------------\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = \"bar-grouped · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    xlabel            = \"Region\",\n    ylabel            = \"Revenue (\\$M)\",\n    xlabelsize        = 14,\n    ylabelsize        = 14,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelsize    = 13,\n    yticklabelsize    = 12,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridvisible      = false,\n    ygridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    yminorgridvisible = false,\n)\n\nbarplot!(\n    ax, x, height;\n    dodge     = dodge,\n    color     = colors,\n    width     = 0.85,\n    dodge_gap = 0.03,\n    gap       = 0.25,\n)\n\nax.xticks = (1:n_cat, categories)\n\n# --- Data storytelling: per-region totals, leading region called out -------\ntotals      = vec(sum(revenue, dims = 2))\nmax_per_cat = vec(maximum(revenue, dims = 2))\ntop_idx     = argmax(totals)\n\nfor i in 1:n_cat\n    text!(\n        ax, Point2f(i, max_per_cat[i] + 1.5);\n        text     = \"\\$$(round(Int, totals[i]))M\",\n        align    = (:center, :bottom),\n        fontsize = 12,\n        color    = INK_SOFT,\n    )\nend\n\ntext!(\n    ax, Point2f(top_idx, max_per_cat[top_idx] + 5.5);\n    text     = \"▲ leading region\",\n    align    = (:center, :bottom),\n    fontsize = 12,\n    color    = INK,\n    font     = :bold,\n)\n\n# Set explicitly *after* every plot element (bars + text) has been added, so\n# no later autolimits recompute can shrink the headroom the labels need.\nylims!(ax, 0, maximum(revenue) + 10)\n\nlegend_elements = [\n    PolyElement(color = IMPRINT_PALETTE[i], strokecolor = INK, strokewidth = 1)\n    for i in 1:n_grp\n]\nLegend(\n    fig[1, 2], legend_elements, groups, \"Product Line\";\n    labelcolor      = INK_SOFT,\n    titlecolor      = INK,\n    framevisible    = false,\n    backgroundcolor = PAGE_BG,\n)\n\ncolgap!(fig.layout, 1, 24)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}