diff --git a/plots/dashboard-metrics-tiles/implementations/r/ggplot2.R b/plots/dashboard-metrics-tiles/implementations/r/ggplot2.R new file mode 100644 index 0000000000..84990730fe --- /dev/null +++ b/plots/dashboard-metrics-tiles/implementations/r/ggplot2.R @@ -0,0 +1,177 @@ +#' anyplot.ai +#' dashboard-metrics-tiles: Real-Time Dashboard Tiles +#' Library: ggplot2 3.5.1 | R 4.4.1 +#' Quality: 92/100 | Created: 2026-05-21 + +library(ggplot2) +library(dplyr) +library(scales) +library(ragg) + +set.seed(42) + +# --- Theme tokens ----------------------------------------------------------- +THEME <- Sys.getenv("ANYPLOT_THEME", "light") +PAGE_BG <- if (THEME == "light") "#FAF8F1" else "#1A1A17" +ELEVATED_BG <- if (THEME == "light") "#FFFDF6" else "#242420" +INK <- if (THEME == "light") "#1A1A17" else "#F0EFE8" +INK_SOFT <- if (THEME == "light") "#4A4A44" else "#B8B7B0" + +COL_GOOD <- "#009E73" # Okabe-Ito 1 — good / brand +COL_WARNING <- "#E69F00" # Okabe-Ito 5 — warning +COL_CRITICAL <- "#D55E00" # Okabe-Ito 2 — critical / bad + +# --- Data ------------------------------------------------------------------- +# Server health metrics snapshot (6 tiles in 3x2 grid) +metric_names <- c("CPU Usage", "Memory", "Response Time", "Disk I/O", "Throughput", "Error Rate") +value_nums <- c(45.2, 72.1, 118, 38.6, 1247, 0.82) +value_labels <- c("45.2%", "72.1%", "118 ms", "38.6%", "1,247 req/s", "0.82%") +changes <- c(-5.2, 8.1, -14.7, 3.4, 12.3, -22.5) +statuses <- c("good", "warning", "good", "good", "good", "good") +up_is_good <- c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE) + +n_metrics <- length(metric_names) +n_pts <- 24 + +status_colors <- ifelse( + statuses == "critical", COL_CRITICAL, + ifelse(statuses == "warning", COL_WARNING, COL_GOOD) +) + +change_colors <- ifelse( + (changes > 0 & !up_is_good) | (changes < 0 & up_is_good), + COL_CRITICAL, COL_GOOD +) + +arrows <- ifelse(changes > 0, "▲", "▼") +change_labels <- paste0(arrows, " ", sprintf("%.1f", abs(changes)), "%") + +metrics_df <- data.frame( + metric = factor(metric_names, levels = metric_names), + value_label = value_labels, + change_label = change_labels, + status_color = status_colors, + change_color = change_colors, + stringsAsFactors = FALSE +) + +# Generate sparkline histories (end pinned to current value, with slight trend) +spark_list <- lapply(seq_len(n_metrics), function(i) { + base <- value_nums[i] + chg <- changes[i] / 100 * base + steps <- rnorm(n_pts, mean = chg / n_pts, sd = base * 0.035) + vals <- base - chg + cumsum(steps) + vals[n_pts] <- base + data.frame( + metric = metric_names[i], + t = seq_len(n_pts), + val = vals, + status_color = status_colors[i], + stringsAsFactors = FALSE + ) +}) +spark_df <- do.call(rbind, spark_list) +spark_df$metric <- factor(spark_df$metric, levels = metric_names) + +# Normalise each sparkline to [0.15, 0.65] within the panel's y space +spark_df <- spark_df |> + group_by(metric) |> + mutate(val_norm = rescale(val, to = c(0.15, 0.65))) |> + ungroup() + +spark_end <- spark_df[spark_df$t == n_pts, ] + +# Annotation positions within the normalised [−0.18, 1.55] y range +label_df <- data.frame( + metric = metrics_df$metric, + x_mid = (n_pts + 1) / 2, + y_value = 1.35, + y_change = 1.08, + y_name = -0.06, + value_label = metrics_df$value_label, + change_label = metrics_df$change_label, + status_color = metrics_df$status_color, + change_color = metrics_df$change_color, + name_color = INK_SOFT, + stringsAsFactors = FALSE +) + +# --- Plot ------------------------------------------------------------------- +p <- ggplot() + + # Shaded area under sparkline + geom_area( + data = spark_df, + aes(x = t, y = val_norm, fill = status_color, group = metric), + alpha = 0.15, + show.legend = FALSE + ) + + # Sparkline + geom_line( + data = spark_df, + aes(x = t, y = val_norm, color = status_color, group = metric), + linewidth = 0.9, + show.legend = FALSE + ) + + # Terminal dot + geom_point( + data = spark_end, + aes(x = t, y = val_norm, color = status_color), + size = 2.0, + show.legend = FALSE + ) + + # KPI value — large, status-coloured + geom_text( + data = label_df, + aes(x = x_mid, y = y_value, label = value_label, color = status_color), + size = 7, + fontface = "bold", + show.legend = FALSE + ) + + # Change indicator with directional arrow + geom_text( + data = label_df, + aes(x = x_mid, y = y_change, label = change_label, color = change_color), + size = 3.2, + show.legend = FALSE + ) + + # Metric name label at bottom of tile + geom_text( + data = label_df, + aes(x = x_mid, y = y_name, label = metric, color = name_color), + size = 3.5, + fontface = "bold", + show.legend = FALSE + ) + + scale_color_identity() + + scale_fill_identity() + + facet_wrap(~metric, nrow = 2, ncol = 3) + + scale_y_continuous(limits = c(-0.18, 1.55), expand = c(0, 0)) + + scale_x_continuous(expand = expansion(mult = 0.05)) + + labs( + title = "Server Health · dashboard-metrics-tiles · r · ggplot2 · anyplot.ai" + ) + + theme_minimal(base_size = 8) + + theme( + plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG), + panel.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.4), + panel.grid = element_blank(), + axis.text = element_blank(), + axis.title = element_blank(), + axis.ticks = element_blank(), + strip.text = element_blank(), + plot.title = element_text(color = INK, size = 11, hjust = 0.5), + plot.margin = margin(t = 20, r = 20, b = 20, l = 20), + panel.spacing.x = unit(1.5, "lines"), + panel.spacing.y = unit(1.5, "lines") + ) + +# --- Save ------------------------------------------------------------------- +ggsave( + filename = sprintf("plot-%s.png", THEME), + plot = p, + device = ragg::agg_png, + width = 8, + height = 4.5, + units = "in", + dpi = 400 +) diff --git a/plots/dashboard-metrics-tiles/metadata/r/ggplot2.yaml b/plots/dashboard-metrics-tiles/metadata/r/ggplot2.yaml new file mode 100644 index 0000000000..bc767a6d34 --- /dev/null +++ b/plots/dashboard-metrics-tiles/metadata/r/ggplot2.yaml @@ -0,0 +1,273 @@ +library: ggplot2 +language: r +specification_id: dashboard-metrics-tiles +created: '2026-05-21T03:39:49Z' +updated: '2026-05-21T03:45:22Z' +generated_by: claude-sonnet +workflow_run: 26203610104 +issue: 3791 +language_version: 4.4.1 +library_version: 3.5.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/dashboard-metrics-tiles/r/ggplot2/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/dashboard-metrics-tiles/r/ggplot2/plot-dark.png +preview_html_light: null +preview_html_dark: null +quality_score: 92 +review: + strengths: + - Innovative use of ggplot2 facet_wrap to create a dashboard tile grid — idiomatic + and visually clean + - 'Proper semantic color coding using Okabe-Ito: #009E73 (good), #E69F00 (warning), + #D55E00 (critical/unfavorable change)' + - 'Complete tile content: large KPI value, directional change indicator with arrow, + compact sparkline with area fill and terminal dot, metric name' + - Theme-adaptive chrome fully implemented — backgrounds, INK, INK_SOFT all flip + correctly between light and dark renders + - Panel ELEVATED_BG creates a professional card-like tile appearance distinguishable + from the PAGE_BG outer surface + - All grid, axis, and strip elements correctly suppressed for a clean dashboard + look + - Clean, reproducible R code using set.seed(42), dplyr pipe, and proper modern ggplot2 + API (linewidth= not size=) + weaknesses: + - No 'critical' status tile in the example data — spec mentions critical as a status + option; add at least one critical tile (e.g. set Error Rate status to 'critical') + so all three status colors are demonstrated + - Change indicator text (size=3.2) is slightly small at canvas scale — increase + to ~3.8-4.0 to ensure readability when image is scaled to mobile width (~400 px) + - 'Memory tile area fill in dark render appears brownish/muddy — the alpha=0.15 + of #E69F00 on the dark #242420 panel background creates a desaturated khaki fill; + consider increasing alpha slightly to ~0.25 or using a lighter tint approach to + keep the fill recognizable as warning-orange in dark mode' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct per style guide. Panel interiors use elevated background (#FFFDF6). + Chrome: Title "Server Health · dashboard-metrics-tiles · r · ggplot2 · anyplot.ai" in dark ink, well-centered. Metric name labels at bottom of each tile in INK_SOFT (#4A4A44) — all readable. No axis text or grid. + Data: 6 tiles in 3×2 grid. CPU Usage (45.2%), Response Time (118ms), Disk I/O (38.6%), Throughput (1247 req/s), Error Rate (0.82%) all display in brand green (#009E73). Memory (72.1%) in warning orange (#E69F00). Change indicators use green for favorable changes and vermillion (#D55E00) for unfavorable. Sparklines with shaded area fill and terminal dot clearly visible. + Legibility verdict: PASS — all text readable, status colors well-distinguished against warm off-white. + + Dark render (plot-dark.png): + Background: Warm near-black (#1A1A17) — correct. Panel interiors use dark elevated (#242420). + Chrome: Title and metric name labels display in light ink — fully readable against dark background. No dark-on-dark text issues observed. + Data: Data colors are identical to light render — green (#009E73) for good metrics, orange (#E69F00) for Memory warning. Change indicator colors unchanged. Minor observation: Memory tile area fill (alpha=0.15 of #E69F00 on dark panel) appears as a slightly muddy brownish-gold fill compared to the cleaner golden tint in the light render, but the sparkline itself remains clearly orange and readable. + Legibility verdict: PASS — all text readable against dark background. No dark-on-dark failures. + criteria_checklist: + visual_quality: + score: 29 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: 'All font sizes explicitly set (title=11, value=7, change=3.2, name=3.5). + All readable in both themes. Minor: change indicator at size=3.2 is slightly + small for mobile scaling.' + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No overlap — KPI value, change indicator, sparkline, and metric name + all clearly separated within each tile. + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Sparklines (linewidth=0.9), terminal dots (size=2.0), shaded area + (alpha=0.15), and KPI values are all optimally visible. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Okabe-Ito palette applied semantically; green/orange/vermillion are + CVD-safe. Adequate contrast in both themes. + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Canvas correctly 3200x1800 (8in x 4.5in @ 400dpi). 3x2 grid fills + canvas well with generous margins. No canvas gate failure. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Title has descriptive prefix 'Server Health'. Tile labels serve as + metric identifiers. No axis labels needed for this chart type. + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First/primary series color is #009E73. Warning uses #E69F00 (Okabe-Ito + pos 5). Critical/#D55E00 (pos 2) used for unfavorable change indicators. + Backgrounds #FAF8F1/#1A1A17 are correct.' + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: 'Strong design: semantic status coloring, elevated card backgrounds, + intentional hierarchy with large values and subtle secondary elements. Clearly + above configured defaults.' + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 + passed: true + comment: 'Thorough refinement: grid removed, strip text removed, axis elements + removed, custom panel borders, generous panel spacing (1.5 lines). Minor: + dark mode area fill for warning tile appears muddy.' + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: 'Good visual hierarchy: Memory''s warning color immediately draws + the eye to the problem metric. Change arrows + colors communicate trend + direction effectively. Combination of value+trend+change creates comprehensive + metric context.' + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct dashboard tile layout with 6 tiles in 3x2 grid (spec-compliant + for 6 tiles). + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: 'All required tile elements present: prominent value, metric label, + mini sparkline, change indicator with directional arrow. Status color coding + implemented.' + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: KPI values, sparklines, change percentages, and status colors all + correctly mapped. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: 'Title: ''Server Health · dashboard-metrics-tiles · r · ggplot2 · + anyplot.ai'' — correct format with descriptive prefix.' + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 5 + max: 6 + passed: true + comment: 'Shows varied metrics with different units (%, ms, req/s), favorable + and unfavorable changes, and good/warning statuses. Missing: no ''critical'' + status tile to demonstrate all three status types.' + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: 'Real server health monitoring scenario with plausible values: CPU + 45%, Memory 72%, Response Time 118ms, Throughput 1247 req/s, Error Rate + 0.82%.' + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: All values are factually plausible for a web server health dashboard. + Memory at 72% correctly triggers warning. Values and units are coherent. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Clean linear structure: Imports → Theme tokens → Data → Sparklines + → Annotations → Plot → Save. No functions or classes.' + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: set.seed(42) at the top. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All four imports (ggplot2, dplyr, scales, ragg) are actually used. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean idiomatic R. No over-engineering, no fake interactivity. lapply+do.call + pattern appropriate for sparkline generation. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves as plot-{THEME}.png via ragg::agg_png. Uses modern linewidth= + not deprecated size= for lines. + library_mastery: + score: 9 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: 'Expert use of ggplot2 grammar: geom layers composited within facets, + scale_color_identity/scale_fill_identity for pre-computed colors, theme() + layering on theme_minimal(). R pipe (|>) used idiomatically.' + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: Creative use of facet_wrap as a dashboard tile grid — a clever and + distinctive ggplot2 pattern. Panel-level theming (panel.background with + ELEVATED_BG, panel border styling) to create card-like tiles is ggplot2-specific. + verdict: APPROVED +impl_tags: + dependencies: + - scales + - ragg + techniques: + - faceting + - layer-composition + - annotations + patterns: + - data-generation + - iteration-over-groups + dataprep: + - normalization + - cumulative-sum + styling: + - alpha-blending + - minimal-chrome