---
title: "Advanced: Specification Objects"
---
```{r}
#| include: false
library(webforest)
```
::: {.callout-note}
## When to Use This Page
**Most users should start with [`forest_plot()`](forest_plot.qmd)**, which accepts all the same arguments and is simpler for typical workflows.
Use `web_spec()` when you need to:
- Build plot specifications programmatically (e.g., in functions or loops)
- Reuse the same specification with different rendering options
- Inspect or serialize the specification object
- Work with multi-effect plots requiring explicit `web_effect()` definitions
:::
## Overview
The `web_spec()` function creates a `WebSpec` object—an intermediate representation that captures all the data, mappings, and styling for a forest plot. This object can then be rendered with `forest_plot()` or `webtable()`.
```{r}
#| eval: false
# Two equivalent approaches:
# Direct (recommended for most cases)
forest_plot(data,
point = "hr", lower = "lower", upper = "upper", label = "study",
scale = "log", null_value = 1
)
# Via WebSpec (for programmatic use)
spec <- web_spec(data,
point = "hr", lower = "lower", upper = "upper", label = "study",
scale = "log", null_value = 1
)
forest_plot(spec)
```
---
## Core Specification
### web_spec {#web_spec}
Create a specification object for forest plot rendering.
```r
web_spec(
data,
point,
lower,
upper,
label = NULL,
label_header = "Study",
group = NULL,
columns = NULL,
scale = c("linear", "log"),
null_value = NULL,
axis_label = NULL,
effects = NULL,
annotations = NULL,
title = NULL,
subtitle = NULL,
caption = NULL,
footnote = NULL,
row_bold = NULL,
row_italic = NULL,
row_color = NULL,
row_bg = NULL,
row_badge = NULL,
row_icon = NULL,
row_indent = NULL,
row_type = NULL,
row_emphasis = NULL,
row_muted = NULL,
row_accent = NULL,
marker_color = NULL,
marker_shape = NULL,
marker_opacity = NULL,
marker_size = NULL,
theme = web_theme_default(),
interaction = web_interaction()
)
```
### Required Arguments
| Argument | Description |
|----------|-------------|
| `data` | A data.frame, data.table, or tibble |
| `point` | Column name for point estimates |
| `lower` | Column name for lower confidence bounds |
| `upper` | Column name for upper confidence bounds |
### Optional Data Arguments
| Argument | Description |
|----------|-------------|
| `label` | Column name for row labels |
| `label_header` | Header text for the label column (default: `"Study"`) |
### Grouping
| Argument | Description |
|----------|-------------|
| `group` | Grouping specification (see below) |
The `group` argument supports three modes:
1. **Single column**: `group = "category"` — flat grouping by one column
2. **Hierarchical**: `group = c("region", "country")` — nested grouping (region > country)
3. **Explicit**: `group = list(web_group(...))` — full control over group definitions
### Display Arguments
| Argument | Description |
|----------|-------------|
| `columns` | List of column specifications (use `col_*()` helpers) |
| `scale` | Scale type: `"linear"` or `"log"` |
| `null_value` | Reference value (default: 0 for linear, 1 for log) |
| `axis_label` | Label for the x-axis |
| `title` | Plot title |
| `subtitle` | Plot subtitle |
| `caption` | Plot caption |
| `footnote` | Plot footnote |
### Row Styling Arguments
All row styling arguments accept **either a column name (string) or a formula expression (`~ ...`)**.
| Argument | Value Type | Description |
|----------|------------|-------------|
| `row_type` | string | Row type: `"header"`, `"data"`, `"summary"`, `"spacer"` |
| `row_bold` | logical | `TRUE`/`FALSE` for bold text |
| `row_italic` | logical | `TRUE`/`FALSE` for italic text |
| `row_color` | string | CSS color strings (e.g., `"#1a365d"`) |
| `row_bg` | string | Background color strings |
| `row_badge` | string | Badge text to display |
| `row_icon` | string | Emoji or unicode icons |
| `row_indent` | integer | Indentation levels (0, 1, 2, ...) |
| `row_emphasis` | logical | `TRUE`/`FALSE` for emphasis styling (bold + primary color) |
| `row_muted` | logical | `TRUE`/`FALSE` for muted styling (lighter, reduced prominence) |
| `row_accent` | logical | `TRUE`/`FALSE` for accent styling (theme accent color) |
**Formula expressions** let you compute styling from your data without pre-creating columns:
```{r}
#| eval: false
# Column name approach (traditional)
data <- data |> mutate(is_sig = pval < 0.05)
web_spec(data, ..., row_bold = "is_sig")
# Formula expression approach (NSE)
web_spec(data, ..., row_bold = ~ pval < 0.05)
```
Formulas can reference any column in your data:
```{r}
#| eval: false
# Simple condition
row_bold = ~ pval < 0.05
# Multiple conditions
row_emphasis = ~ pval < 0.05 & hr < 0.8
# Conditional colors
row_color = ~ ifelse(pval < 0.05, "#16a34a", "#71717a")
# Complex logic with case_when
row_color = ~ case_when(
upper < 1 ~ "#16a34a", # Significant benefit
lower > 1 ~ "#dc2626", # Significant harm
TRUE ~ "#71717a" # Non-significant
)
```
### Marker Styling Arguments
All marker styling arguments accept **either a column name (string) or a formula expression (`~ ...`)**.
| Argument | Value Type | Description |
|----------|------------|-------------|
| `marker_color` | string | CSS color strings for marker fill |
| `marker_shape` | string | Shape: `"square"`, `"circle"`, `"diamond"`, `"triangle"` |
| `marker_opacity` | numeric | Opacity values from 0 to 1 |
| `marker_size` | numeric | Size multipliers (1 = default size) |
**Example with formula expressions:**
```{r}
#| eval: false
forest_plot(data, ...,
marker_shape = ~ ifelse(design == "RCT", "circle", "square"),
marker_color = ~ ifelse(pval < 0.05, "#16a34a", "#94a3b8"),
marker_opacity = ~ ifelse(pval < 0.01, 1, 0.7)
)
```
### Advanced Arguments
| Argument | Description |
|----------|-------------|
| `effects` | List of `web_effect()` objects for multi-effect plots |
| `annotations` | List of annotation objects (`forest_refline()`, `forest_annotation()`) |
| `theme` | Theme object (default: `web_theme_default()`) |
| `interaction` | Interaction settings (default: `web_interaction()`) |
---
## Group Specification
### web_group {#web_group}
Define a row group with optional nesting. Use this for explicit control over group structure when automatic grouping from column values isn't sufficient.
```r
web_group(id, label = id, parent = NULL, collapsed = FALSE)
```
| Argument | Description |
|----------|-------------|
| `id` | Unique identifier for the group (must match values in your data) |
| `label` | Display label (defaults to id) |
| `parent` | Parent group ID for creating nested hierarchies |
| `collapsed` | Whether the group starts collapsed (`TRUE`/`FALSE`) |
**Example: Explicit Group Definitions**
```{r}
#| eval: false
# Data with group column
data <- data.frame(
study = c("Trial A", "Trial B", "Trial C", "Trial D"),
group_id = c("cardio", "cardio", "renal", "renal"),
hr = c(0.72, 0.85, 0.91, 0.78),
lower = c(0.55, 0.70, 0.75, 0.60),
upper = c(0.95, 1.03, 1.10, 1.00)
)
# Explicit groups with custom labels
web_spec(data,
point = "hr", lower = "lower", upper = "upper", label = "study",
group = list(
web_group("cardio", label = "Cardiovascular Outcomes"),
web_group("renal", label = "Renal Outcomes", collapsed = TRUE)
)
)
```
---
## Effect Specification
### web_effect {#web_effect}
Define a single effect for multi-effect forest plots. Multi-effect plots display multiple intervals per row, useful for comparing different analyses (e.g., unadjusted vs. adjusted) or different outcomes.
```r
web_effect(point, lower, upper, label = NULL, color = NULL, shape = NULL, opacity = NULL)
```
| Argument | Description |
|----------|-------------|
| `point` | Column name for point estimates |
| `lower` | Column name for lower bounds |
| `upper` | Column name for upper bounds |
| `label` | Display label for legend (defaults to point column name) |
| `color` | Override color for this effect (CSS color string) |
| `shape` | Override shape: `"square"`, `"circle"`, `"diamond"`, `"triangle"` |
| `opacity` | Override opacity (0–1) |
**Example: Comparing Unadjusted and Adjusted Estimates**
```{r}
#| eval: false
data <- data.frame(
study = c("Trial A", "Trial B", "Trial C"),
# Unadjusted estimates
hr_unadj = c(0.75, 0.82, 0.88),
lo_unadj = c(0.58, 0.68, 0.72),
hi_unadj = c(0.97, 0.99, 1.07),
# Adjusted estimates
hr_adj = c(0.72, 0.79, 0.85),
lo_adj = c(0.55, 0.65, 0.70),
hi_adj = c(0.94, 0.96, 1.03)
)
web_spec(data,
point = "hr_unadj", lower = "lo_unadj", upper = "hi_unadj",
label = "study",
effects = list(
web_effect("hr_unadj", "lo_unadj", "hi_unadj",
label = "Unadjusted", color = "#64748b"),
web_effect("hr_adj", "lo_adj", "hi_adj",
label = "Adjusted", color = "#2563eb")
),
scale = "log", null_value = 1
) |>
forest_plot()
```
---
## Interaction Specification
### web_interaction {#web_interaction}
Configure interactive features for the rendered widget.
```r
web_interaction(
tooltip_fields = NULL,
show_filters = FALSE,
show_legend = TRUE,
enable_sort = TRUE,
enable_collapse = TRUE,
enable_select = TRUE,
enable_hover = TRUE,
enable_resize = TRUE,
enable_export = TRUE,
enable_themes = "default"
)
```
| Argument | Description | Default |
|----------|-------------|---------|
| `tooltip_fields` | Column names to show in hover tooltip | `NULL` (no tooltip) |
| `show_filters` | Show filter panel | `FALSE` |
| `show_legend` | Show legend | `TRUE` |
| `enable_sort` | Enable column sorting | `TRUE` |
| `enable_collapse` | Enable group collapsing | `TRUE` |
| `enable_select` | Enable row selection | `TRUE` |
| `enable_hover` | Enable hover effects | `TRUE` |
| `enable_resize` | Enable column resizing | `TRUE` |
| `enable_export` | Enable download/export button | `TRUE` |
| `enable_themes` | Theme menu: `"default"`, `NULL`, or list of `WebTheme` | `"default"` |
### Tooltips
Tooltips are opt-in. Specify column names to display when hovering over interval markers:
```r
web_interaction(
tooltip_fields = c("n", "events", "pvalue")
)
```
The tooltip displays the row label, point estimate with CI, and each specified field.
### Presets
```r
# Read-only: hover effects only, no interactive features
web_interaction_minimal()
# Static: no interactive features at all (for publications)
web_interaction_publication()
```
---
## Programmatic Construction Patterns
### Building Specs in Functions
```{r}
#| eval: false
create_forest_spec <- function(data, effect_col, ci_cols, ...) {
web_spec(data,
point = effect_col,
lower = ci_cols[1],
upper = ci_cols[2],
...
)
}
# Use it
spec <- create_forest_spec(my_data, "hr", c("lower", "upper"),
label = "study", scale = "log", null_value = 1
)
forest_plot(spec)
```
### Reusing Specs with Different Renderings
```{r}
#| eval: false
# Create base specification once
base_spec <- web_spec(data,
point = "hr", lower = "lower", upper = "upper", label = "study",
columns = list(col_n("n"), col_interval("HR (95% CI)")),
scale = "log", null_value = 1
)
# Render with different themes
forest_plot(base_spec, theme = web_theme_jama())
forest_plot(base_spec, theme = web_theme_lancet())
# Render as table only
webtable(base_spec)
# Export to file
save_plot(forest_plot(base_spec), "forest.svg")
```
### Inspecting Specifications
```{r}
#| eval: false
spec <- web_spec(data, point = "hr", lower = "lower", upper = "upper")
# Print summary
print(spec)
# Convert to data frame
as.data.frame(spec)
# Access components
spec@data # Original data
spec@theme # Theme object
spec@columns # Column specifications
```
---
## See Also
- [forest_plot()](forest_plot.qmd) — Primary function for creating plots (recommended)
- [Styling Functions](styling.qmd) — Apply styling with `set_*()` functions
- [Columns](columns.qmd) — Column type specifications
- [Themes](themes.qmd) — Theme customization