{"spec_id":"bar-stacked-labeled","library":"makie","language":"julia","code":"# anyplot.ai\n# bar-stacked-labeled: Stacked Bar Chart with Total Labels\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 90/100 | Created: 2026-08-26\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 SaaS revenue split by product line ($M, illustrative).\nquarters      = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"]\nproduct_lines = [\"Subscriptions\", \"Services\", \"Hardware\", \"Support\"]\n\nrevenue = [\n    5.2 2.1 0.9 0.5\n    5.8 2.3 0.8 0.5\n    6.5 2.6 0.9 0.6\n    7.3 3.0 0.9 0.7\n]\n\nn_quarter = length(quarters)\nn_product = length(product_lines)\n\nx      = repeat(1:n_quarter, inner = n_product)\nstack  = repeat(1:n_product, outer = n_quarter)\nheight = vec(permutedims(revenue))\n\ntotals = vec(sum(revenue, dims = 2))\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-stacked-labeled · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    xlabel            = \"Quarter\",\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    ygridvisible      = true,\n    ygridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.12),\n    yminorgridvisible = false,\n)\n\nbarplot!(\n    ax, x, height;\n    stack       = stack,\n    color       = stack,\n    colormap    = IMPRINT_PALETTE[1:n_product],\n    width       = 0.6,\n    strokewidth = 0,\n)\n\nax.xticks = (1:n_quarter, quarters)\n\n# --- Total labels: genuinely bold and larger than any other text, directly\n# above the tallest segment of each stack. `rich()` mixes weight/size within\n# one label -- the dollar figure is bold and oversized, the trailing \"M\"\n# unit is smaller and softer, echoing typographic hierarchy from financial\n# dashboards.\nfor (i, total) in enumerate(totals)\n    text!(\n        ax, Point2f(i, total);\n        text   = rich(\n            rich(\"\\$$(round(total, digits = 1))\"; font = :bold, fontsize = 19, color = INK),\n            rich(\"M\"; fontsize = 14, color = INK_SOFT),\n        ),\n        offset = (0, 8),\n        align  = (:center, :bottom),\n    )\nend\n\nLegend(\n    fig[1, 2], [PolyElement(color = c) for c in IMPRINT_PALETTE[1:n_product]], product_lines;\n    labelcolor      = INK_SOFT,\n    labelsize       = 13,\n    framevisible    = false,\n    backgroundcolor = PAGE_BG,\n)\n\n# Fix the y-range last so the label headroom above the tallest bar survives\n# any autolimits reset triggered by the legend/text calls above.\nylims!(ax, 0, maximum(totals) * 1.16)\n\n# --- Save ---------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}