{"spec_id":"bar-stacked-percent","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' bar-stacked-percent: 100% Stacked Bar Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 90/100 | Created: 2026-08-18\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(scales)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens ------------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\n\n# Imprint palette — categorical, theme-independent\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Fixed (theme-independent) label inks, chosen per segment fill's own\n# luminance so percentage labels stay legible regardless of page theme.\nLABEL_DARK  <- \"#1A1A17\"\nLABEL_LIGHT <- \"#FFFDF6\"\n\n# --- Data ---------------------------------------------------------------------\nfiscal_years <- c(\"FY2021\", \"FY2022\", \"FY2023\", \"FY2024\", \"FY2025\")\ncategories   <- c(\"Compute\", \"Storage\", \"Networking\", \"Support & Security\")\n\nbase_spend <- c(\n  \"Compute\"             = 24,\n  \"Storage\"             = 12,\n  \"Networking\"          = 7,\n  \"Support & Security\"  = 5\n)\ncompute_growth <- c(0, 4, 9, 15, 22)  # AI-driven compute demand outpaces other spend\n\ndf <- expand.grid(fiscal_year = fiscal_years, category = categories, stringsAsFactors = FALSE) %>%\n  mutate(\n    fiscal_year    = factor(fiscal_year, levels = fiscal_years),\n    category       = factor(category, levels = categories),\n    year_idx       = as.integer(fiscal_year),\n    spend_millions = base_spend[as.character(category)] +\n      ifelse(category == \"Compute\", compute_growth[year_idx], 0) +\n      round(rnorm(n(), mean = 0, sd = 1.4), 1)\n  ) %>%\n  group_by(fiscal_year) %>%\n  mutate(share = spend_millions / sum(spend_millions)) %>%\n  ungroup() %>%\n  mutate(label_text = ifelse(share >= 0.05, sprintf(\"%.0f%%\", share * 100), \"\"))\n\nlabel_ink <- c(\n  \"Compute\"             = LABEL_DARK,\n  \"Storage\"             = LABEL_DARK,\n  \"Networking\"          = LABEL_LIGHT,\n  \"Support & Security\"  = LABEL_DARK\n)\n\n# --- Plot ----------------------------------------------------------------------\ntitle_text <- \"Cloud Infrastructure Spend Mix · bar-stacked-percent · r · ggplot2 · anyplot.ai\"\ntitle_size <- round(12 * min(1, 67 / nchar(title_text)))\n\np <- ggplot(df, aes(x = fiscal_year, y = spend_millions, fill = category)) +\n  geom_col(position = \"fill\", width = 0.65, color = PAGE_BG, linewidth = 1.2) +\n  geom_text(\n    aes(label = label_text, color = category),\n    position  = position_fill(vjust = 0.5),\n    size      = 3.4,\n    fontface  = \"bold\",\n    show.legend = FALSE\n  ) +\n  scale_y_continuous(labels = percent_format(), expand = c(0, 0)) +\n  scale_fill_manual(values = IMPRINT_PALETTE, name = \"Spend Category\") +\n  scale_color_manual(values = label_ink, guide = \"none\") +\n  labs(\n    title = title_text,\n    x     = \"Fiscal Year\",\n    y     = \"Share of Annual Cloud Spend\"\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background     = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background     = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid           = element_blank(),\n    axis.line.x          = element_line(color = INK_SOFT, linewidth = 0.4),\n    axis.ticks           = element_blank(),\n    axis.title           = element_text(color = INK, size = 10),\n    axis.text            = element_text(color = INK_SOFT, size = 8),\n    plot.title           = element_text(color = INK, size = title_size, margin = margin(b = 14)),\n    legend.title         = element_text(color = INK, size = 10),\n    legend.text          = element_text(color = INK_SOFT, size = 8),\n    legend.key           = element_rect(fill = PAGE_BG, color = NA),\n    plot.margin          = margin(t = 16, r = 16, b = 10, l = 10)\n  )\n\n# --- Save -----------------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}