Basic Span Plot (Highlighted Region) — ggplot2

A span plot highlights a specific region of interest on a chart using a shaded rectangular area that spans the full height or width of the plot. Vertical spans mark ranges along the x-axis (e.g., time periods), while horizontal spans mark ranges along the y-axis (e.g., value thresholds). The semi-transparent fill allows underlying data to remain visible while drawing attention to the highlighted region.

Basic Span Plot (Highlighted Region) rendered with ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' span-basic: Basic Span Plot (Highlighted Region)
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 92/100 | Created: 2026-07-25

library(ggplot2)
library(ragg)

set.seed(42)

# --- Theme tokens -----------------------------------------------------------
THEME       <- Sys.getenv("ANYPLOT_THEME", "light")
PAGE_BG     <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
INK         <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
INK_SOFT    <- if (THEME == "light") "#4A4A44" else "#B8B7B0"
NEUTRAL     <- INK  # theme-adaptive anchor for the span highlight (reference region)
AMBER       <- "#DDCC77"  # semantic anchor: warning / caution threshold band
IMPRINT_PALETTE <- c("#009E73", "#C475FD", "#4467A3", "#BD8233",
                     "#AE3030", "#2ABCCD", "#954477", "#99B314")

# ggplot2 does not expose per-element grid alpha, so blend INK toward PAGE_BG
# to get a faint tint instead of passing the full-opacity ink color.
mix_color <- function(c1, c2, amount) {
  m <- col2rgb(c1) * (1 - amount) + col2rgb(c2) * amount
  rgb(m[1, ], m[2, ], m[3, ], maxColorValue = 255)
}
GRID_COLOR <- mix_color(PAGE_BG, INK, 0.18)

# --- Data --------------------------------------------------------------------
dates <- seq(as.Date("2000-01-01"), as.Date("2012-12-01"), by = "month")
n     <- length(dates)

recessions <- tibble::tibble(
  start = as.Date(c("2001-03-01", "2007-12-01")),
  end   = as.Date(c("2001-11-01", "2009-06-01")),
  label = c("2001", "2008")
)

recession_flag <- rep(0, n)
for (i in seq_len(nrow(recessions))) {
  recession_flag <- recession_flag +
    as.numeric(dates >= recessions$start[i] & dates <= recessions$end[i])
}

monthly_step <- ifelse(recession_flag > 0,
                        rnorm(n, mean = -0.9, sd = 1.0),
                        rnorm(n, mean = 0.5, sd = 1.0))
market_index <- tibble::tibble(
  date  = dates,
  index = 100 + cumsum(monthly_step)
)

# Horizontal span: caution zone whenever the index falls below its starting
# baseline of 100 (demonstrates the spec's "horizontal" direction alongside
# the vertical recession spans above).
baseline_zone <- tibble::tibble(
  xmin = min(dates),
  xmax = max(dates),
  ymin = floor(min(market_index$index)) - 1,
  ymax = 100
)

# --- Title (scaled for length per anyplot title-fontsize rule) --------------
plot_title <- "Market Index · span-basic · r · ggplot2 · anyplot.ai"
title_size <- round(12 * min(1.0, 67 / nchar(plot_title)))
title_size <- max(title_size, 8)

# --- Plot ---------------------------------------------------------------------
p <- ggplot(market_index, aes(x = date, y = index)) +
  geom_rect(
    data        = baseline_zone,
    aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
    inherit.aes = FALSE,
    fill        = AMBER,
    alpha       = 0.2
  ) +
  geom_rect(
    data        = recessions,
    aes(xmin = start, xmax = end, ymin = -Inf, ymax = Inf),
    inherit.aes = FALSE,
    fill        = NEUTRAL,
    alpha       = 0.25
  ) +
  geom_text(
    data        = recessions,
    aes(x = start + (end - start) / 2, y = Inf, label = label),
    inherit.aes = FALSE,
    vjust       = 1.6,
    size        = 2.6,
    color       = INK_SOFT
  ) +
  annotate(
    "text",
    x     = dates[round(n * 0.97)],
    y     = (baseline_zone$ymin + baseline_zone$ymax) / 2,
    label = "Below baseline",
    hjust = 1,
    size  = 2.6,
    color = INK_SOFT
  ) +
  geom_line(color = IMPRINT_PALETTE[1], linewidth = 1.0) +
  scale_x_date(date_breaks = "2 years", date_labels = "%Y") +
  labs(
    title = plot_title,
    x     = "Year",
    y     = "Market Index Value"
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.background   = element_rect(fill = PAGE_BG, color = PAGE_BG),
    panel.background  = element_rect(fill = PAGE_BG, color = NA),
    panel.grid.major.x = element_blank(),
    panel.grid.major.y = element_line(color = GRID_COLOR, linewidth = 0.3),
    panel.grid.minor  = element_blank(),
    panel.border      = element_blank(),
    axis.line         = element_line(color = INK_SOFT),
    axis.ticks        = element_blank(),
    axis.title        = element_text(color = INK, size = 10),
    axis.text         = element_text(color = INK_SOFT, size = 8),
    plot.title        = element_text(color = INK, size = title_size)
  )

# --- Save ---------------------------------------------------------------------
ggsave(
  filename = sprintf("plot-%s.png", THEME),
  plot     = p,
  device   = ragg::agg_png,
  width    = 8,
  height   = 4.5,
  units    = "in",
  dpi      = 400
)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/span-basic/ggplot2/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "span-basic",
  "language": "r",
  "library": "ggplot2",
  "page": "https://anyplot.ai/span-basic/r/ggplot2",
  "hub": "https://anyplot.ai/span-basic",
  "code_json": "https://api.anyplot.ai/specs/span-basic/ggplot2/code",
  "spec_json": "https://api.anyplot.ai/specs/span-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/span-basic/r/ggplot2/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/span-basic/r/ggplot2/plot-dark.png",
  "quality_score": 92.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Basic Span Plot (Highlighted Region) on anyplot.ai.

Other implementations