{"spec_id":"bar-stacked-percent","library":"makie","language":"julia","code":"# anyplot.ai\n# bar-stacked-percent: 100% Stacked Bar Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 92/100 | Created: 2026-08-18\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# Fixed (non-theme-adaptive) dark ink for segment labels drawn on the lighter\n# Imprint hues — the underlying fill color never changes between themes, so\n# the label color that contrasts against it shouldn't either.\nconst LABEL_DARK  = colorant\"#1A1A17\"\nconst LABEL_LIGHT = colorant\"#FFFFFF\"\n\n# --- Data -----------------------------------------------------------------\n# Global electricity generation mix (TWh, illustrative) — rows are years,\n# columns are sources. Coal share erodes as renewables scale up.\nyears   = [\"2010\", \"2013\", \"2016\", \"2019\", \"2022\", \"2025\"]\nsources = [\"Coal\", \"Natural Gas\", \"Nuclear\", \"Renewables\"]\n\n# Semantic-exception color mapping (style guide \"Semantic exception\"):\n# renewables carries a strong reader expectation of green, so brand green is\n# reassigned from the ordinal-first series to Renewables, and the remaining\n# canonical hues (positions 2-4) reflow by their own semantic fit — ochre\n# (earth/commodity) for Coal, blue (gas-flame association) for Natural Gas,\n# lavender left for Nuclear.\nsource_colors = IMPRINT_PALETTE[[4, 3, 2, 1]]\nlabel_colors  = [LABEL_DARK, LABEL_LIGHT, LABEL_DARK, LABEL_LIGHT]\n\ngeneration = [\n    42.0 23.0 13.0  8.0\n    41.0 24.0 11.0 14.0\n    38.0 25.0 10.0 20.0\n    33.0 24.0 10.0 27.0\n    27.0 23.0  9.0 35.0\n    21.0 21.0  9.0 45.0\n]\n\nn_year   = length(years)\nn_source = length(sources)\n\n# Normalize each year's row so segments sum to exactly 100%.\nrow_totals = sum(generation, dims = 2)\nshares     = 100 .* generation ./ row_totals\n\nx      = repeat(1:n_year, inner = n_source)\nstack  = repeat(1:n_source, outer = n_year)\nheight = vec(permutedims(shares))\ncolors = source_colors[repeat(1:n_source, outer = n_year)]\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-percent · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    xlabel            = \"Year\",\n    ylabel            = \"Share of Electricity Generation (%)\",\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       = colors,\n    width       = 0.65,\n    gap         = 0.25,\n    strokewidth = 0,\n)\n\nax.xticks = (1:n_year, years)\nax.yticks = (0:20:100, [\"$(t)%\" for t in 0:20:100])\n\n# --- Segment labels: percentage text where the segment has room -----------\nfor j in 1:n_year, i in 1:n_source\n    pct = shares[j, i]\n    pct < 6.0 && continue\n    y_top    = sum(shares[j, 1:i])\n    y_center = y_top - pct / 2\n    text!(\n        ax, Point2f(j, y_center);\n        text     = \"$(round(Int, pct))%\",\n        align    = (:center, :center),\n        fontsize = 12,\n        color    = label_colors[i],\n    )\nend\n\n# --- Callout: mark where renewables overtake coal --------------------------\ncrossover_x = 5  # 2022 — first year Renewables' share exceeds Coal's\nlines!(ax, [crossover_x, crossover_x], [100.0, 104.0]; color = INK_SOFT, linewidth = 1.5)\ntext!(\n    ax, Point2f(crossover_x, 106);\n    text     = \"Renewables overtake Coal\",\n    align    = (:center, :bottom),\n    fontsize = 13,\n    color    = INK,\n)\n\nLegend(\n    fig[1, 2], [PolyElement(color = c) for c in source_colors], sources;\n    labelcolor  = INK,\n    labelsize   = 13,\n    framevisible = false,\n    backgroundcolor = PAGE_BG,\n)\n\n# Fix the y-range last so no later plot/legend call can reset it via autolimits.\nylims!(ax, 0, 124)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}