{"spec_id":"gantt-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' gantt-basic: Basic Gantt Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 93/100 | Created: 2026-09-05\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ragg)\nlibrary(scales)\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\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# --- Data ---------------------------------------------------------------\n# Product launch project plan: phases grouped by workstream.\ntasks <- tibble::tribble(\n  ~task,                     ~start,       ~end,         ~workstream,\n  \"Market research\",         \"2026-01-05\", \"2026-01-23\", \"Discovery\",\n  \"Stakeholder interviews\",  \"2026-01-12\", \"2026-01-30\", \"Discovery\",\n  \"Requirements spec\",       \"2026-01-26\", \"2026-02-13\", \"Discovery\",\n  \"UX wireframes\",           \"2026-02-09\", \"2026-03-06\", \"Design\",\n  \"Visual design system\",    \"2026-02-23\", \"2026-03-20\", \"Design\",\n  \"Usability testing\",       \"2026-03-16\", \"2026-03-27\", \"Design\",\n  \"Backend architecture\",    \"2026-03-02\", \"2026-03-27\", \"Engineering\",\n  \"API development\",         \"2026-03-16\", \"2026-04-24\", \"Engineering\",\n  \"Frontend build\",          \"2026-03-30\", \"2026-05-08\", \"Engineering\",\n  \"Integration testing\",     \"2026-05-04\", \"2026-05-22\", \"Engineering\",\n  \"Beta rollout\",            \"2026-05-18\", \"2026-06-05\", \"Launch\",\n  \"Marketing campaign\",      \"2026-05-25\", \"2026-06-19\", \"Launch\",\n  \"General availability\",    \"2026-06-08\", \"2026-06-19\", \"Launch\"\n) %>%\n  mutate(\n    start = as.Date(start),\n    end   = as.Date(end),\n    task  = factor(task, levels = rev(task))\n  )\n\ntoday <- as.Date(\"2026-04-10\")\n\n# --- Plot -----------------------------------------------------------------\nn_tasks <- length(levels(tasks$task))\n\n# Progress-to-date shading: the full task duration renders faded, while the\n# portion already elapsed (start -> min(end, today)) is overlaid at full\n# opacity, so in-flight and completed work read as darker than upcoming work.\nstarted <- tasks %>%\n  filter(start <= today) %>%\n  mutate(progress_end = pmin(end, today))\n\np <- ggplot(tasks, aes(y = task, x = start, xend = end, color = workstream)) +\n  geom_segment(aes(xend = end, yend = task), linewidth = 7,\n               lineend = \"round\", alpha = 0.35) +\n  geom_segment(data = started,\n               aes(xend = progress_end, yend = task),\n               linewidth = 7, lineend = \"round\", alpha = 1,\n               show.legend = FALSE) +\n  geom_vline(xintercept = today, color = INK_SOFT,\n             linewidth = 0.6, linetype = \"dashed\") +\n  annotate(\"text\", x = today, y = n_tasks + 0.9,\n           label = \"Today\", color = INK_SOFT, size = 3, hjust = -0.15) +\n  scale_color_manual(values = IMPRINT_PALETTE) +\n  scale_x_date(date_labels = \"%b %Y\", date_breaks = \"1 month\",\n               expand = expansion(mult = c(0.02, 0.08))) +\n  scale_y_discrete(expand = expansion(add = c(0.6, 1.3))) +\n  guides(color = guide_legend(override.aes = list(linewidth = 5, alpha = 1))) +\n  labs(\n    title = \"Product Launch Plan · gantt-basic · r · ggplot2 · anyplot.ai\",\n    x = \"Timeline\", y = NULL, color = \"Workstream\"\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.major.x = element_line(color = alpha(INK, 0.18), linewidth = 0.25),\n    panel.grid.major.y = element_blank(),\n    panel.grid.minor  = element_blank(),\n    axis.title        = element_text(color = INK, size = 10),\n    axis.text.x       = element_text(color = INK_SOFT, size = 8),\n    axis.text.y       = element_text(color = INK_SOFT, size = 8),\n    axis.ticks        = element_blank(),\n    plot.title        = element_text(color = INK, size = 12, face = \"plain\"),\n    legend.position     = \"top\",\n    legend.background   = element_rect(fill = PAGE_BG, color = NA),\n    legend.text         = element_text(color = INK_SOFT, size = 8, margin = margin(l = 4, r = 14)),\n    legend.title        = element_text(color = INK, size = 10, margin = margin(r = 10)),\n    legend.key          = element_rect(fill = PAGE_BG, color = NA),\n    legend.key.spacing.x = unit(0.4, \"cm\"),\n    legend.margin       = margin(b = 6),\n    plot.margin         = margin(12, 20, 8, 8)\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"}