{"spec_id":"area-stacked-percent","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' area-stacked-percent: 100% Stacked Area Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 95/100 | Created: 2026-09-02\n\nlibrary(ggplot2)\nlibrary(dplyr)\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\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\")\n\n# --- Data ---------------------------------------------------------------\n# Cloud infrastructure market share by vendor, quarterly, 2020-2024\nquarters <- seq(as.Date(\"2020-01-01\"), as.Date(\"2024-10-01\"), by = \"quarter\")\nvendors <- c(\"Vendor A\", \"Vendor B\", \"Vendor C\", \"Vendor D\", \"Vendor E\")\nn_periods <- length(quarters)\n\ntrend <- rbind(\n  cumsum(rnorm(n_periods, mean = 1.4, sd = 1.0)) + 30,\n  cumsum(rnorm(n_periods, mean = 0.6, sd = 1.0)) + 22,\n  cumsum(rnorm(n_periods, mean = -0.2, sd = 1.0)) + 20,\n  cumsum(rnorm(n_periods, mean = -0.9, sd = 1.0)) + 16,\n  cumsum(rnorm(n_periods, mean = -1.1, sd = 1.0)) + 12\n)\ntrend[trend < 2] <- 2\n\ndf <- expand.grid(quarter = quarters, vendor = vendors) %>%\n  arrange(vendor, quarter) %>%\n  mutate(revenue = as.vector(t(trend)))\n\ndf$vendor <- factor(df$vendor, levels = vendors)\n\n# --- Direct end-of-line labels (replaces side legend) ------------------------\n# Stacking order is reverse-factor (bottom-to-top: E, D, C, B, A), so cumulative\n# shares must be computed in that same order to land labels on the right band.\nlast_quarter <- max(df$quarter)\nlabel_df <- df %>%\n  filter(quarter == last_quarter) %>%\n  mutate(pct = revenue / sum(revenue)) %>%\n  arrange(desc(vendor)) %>%\n  mutate(ymax = cumsum(pct), ymin = ymax - pct, ymid = (ymin + ymax) / 2) %>%\n  arrange(ymid)\n\n# Nudge overlapping labels apart (thin bands, e.g. Vendor D/E, sit within a\n# text-height of each other) so labels never collide, and keep the bottom\n# label clear of the 0% axis line.\nmin_gap <- 0.06\nlabel_df$ylabel <- label_df$ymid\nlabel_df$ylabel[1] <- max(label_df$ylabel[1], 0.04)\nfor (i in 2:nrow(label_df)) {\n  if (label_df$ylabel[i] - label_df$ylabel[i - 1] < min_gap) {\n    label_df$ylabel[i] <- label_df$ylabel[i - 1] + min_gap\n  }\n}\nlabel_df$label_x <- last_quarter + 60\n\n# --- Plot -----------------------------------------------------------------\ntitle_text <- \"Cloud Market Share · area-stacked-percent · r · ggplot2 · anyplot.ai\"\ntitle_fontsize <- round(12 * min(1.0, 67 / nchar(title_text)))\n\np <- ggplot(df, aes(x = quarter, y = revenue, fill = vendor)) +\n  geom_area(position = \"fill\", color = PAGE_BG, linewidth = 0.3) +\n  scale_fill_manual(values = IMPRINT_PALETTE) +\n  geom_segment(\n    data = label_df,\n    aes(x = last_quarter, xend = label_x, y = ymid, yend = ylabel, color = vendor),\n    inherit.aes = FALSE, linewidth = 0.35\n  ) +\n  geom_text(\n    data = label_df,\n    aes(x = label_x, y = ylabel, label = vendor, color = vendor),\n    inherit.aes = FALSE, hjust = 0, size = 3, fontface = \"bold\"\n  ) +\n  scale_color_manual(values = IMPRINT_PALETTE, guide = \"none\") +\n  scale_y_continuous(labels = scales::percent_format(), expand = c(0, 0)) +\n  scale_x_date(\n    expand = expansion(mult = c(0, 0.02), add = c(0, 250)),\n    date_labels = \"%Y\", date_breaks = \"1 year\"\n  ) +\n  coord_cartesian(clip = \"off\") +\n  labs(\n    title = title_text,\n    x = \"Quarter\",\n    y = \"Market Share (%)\"\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    legend.position     = \"none\",\n    plot.background     = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background    = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid.major.y  = element_line(color = INK, linewidth = 0.2),\n    panel.grid.minor    = element_blank(),\n    panel.grid.major.x  = element_blank(),\n    axis.title          = element_text(color = INK, size = 10),\n    axis.text           = element_text(color = INK_SOFT, size = 8),\n    axis.ticks          = element_blank(),\n    plot.title          = element_text(color = INK, size = title_fontsize),\n    plot.margin         = margin(t = 5, r = 34, b = 5, l = 5)\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"}