Code
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_default())Themes control the visual appearance of forest plots—colors, typography, spacing, and shapes. You can use preset themes for common publication styles, or customize every aspect using a fluent pipe-based API.
Each theme is designed with a specific context in mind:
| Component | Function | Key Properties |
|---|---|---|
| Colors | set_colors() |
background, foreground, primary, interval, interval_line |
| Typography | set_typography() |
font_family, font_size_, font_weight_, header_font_scale |
| Spacing | set_spacing() |
row_height, header_height, padding, container_padding, cell_padding_x/y |
| Shapes | set_shapes() |
point_size, summary_height, line_width, effect_colors, marker_shapes |
| Axis | set_axis() |
range_min/max, tick_count, tick_values, gridlines, symmetric |
| Layout | set_layout() |
plot_position, row_border, container_border |
| Group Headers | set_group_headers() |
level*_font_size, level*_background, indent_per_level |
| Effects | set_effect_colors(), set_marker_shapes() |
Multi-effect default colors/shapes |
Colors serve different purposes in tabviz:
| Category | Purpose | Colors |
|---|---|---|
| Structural | Define the canvas | background, foreground, border |
| Semantic | Convey meaning through styling | primary, accent, muted |
| Data | Visualize data elements | interval, interval_line, summary_fill |
The palette defines what colors look like. Semantic styles apply those colors to specific elements:
┌─────────────────────────────────────────────────────────────────┐
│ PALETTE (set_colors) STYLES (row_*/column) │
│ ───────────────────── ──────────────────── │
│ accent: "#8b5cf6" ───────► row_accent, cell accent │
│ muted: "#94a3b8" ───────► row_muted, cell muted │
│ foreground: "#333" ───────► row_emphasis (+ bold) │
│ primary: "#0891b2" ───────► group headers, hover │
└─────────────────────────────────────────────────────────────────┘
Key insight: Changing accent in the theme automatically updates all row_accent and cell accent styling.
The same semantic vocabulary works at both levels:
| Style | Effect | Row Parameter | Cell Parameter |
|---|---|---|---|
| Emphasis | Bold + foreground color | row_emphasis |
emphasis in web_col() |
| Muted | Muted color (de-emphasized) | row_muted |
muted in web_col() |
| Accent | Accent color (highlighted) | row_accent |
accent in web_col() |
Row-level parameters accept column names with TRUE/FALSE values. Cell-level parameters in web_col() also accept column names.
tabviz includes 9 preset themes designed for different use cases. All themes can be customized using a fluent API.
Clean, modern default with subtle cyan accents and system fonts.
JAMA journal style: maximum density, pure black & white, Arial font, compact rows (18px). Optimized for print.
Elegant academic theme with Lancet navy blue (#00407A), warm off-white background, gold accents, and serif typography (Georgia).
Bold, vibrant design with Inter font, bright blue (#3B82F6) accents, larger elements (36px rows), and rounded corners.
Oversized theme for slides and posters: extra large fonts (1-1.25rem), thick lines (2.5px), oversized markers (12px), and tall rows (44px).
Catppuccin Mocha-inspired dark mode with comfortable low-contrast colors for extended viewing.
Academic publication-ready with pure black & white styling and Georgia serif font. Classic, authoritative look.
Cochrane systematic review style: teal accents (#0099CC), Arial font, very compact layout (20px rows).
Modify any preset theme using pipe-friendly set_*() functions:
custom_theme <- web_theme_jama() |>
set_colors(primary = "#0066cc", interval_line = "#0066cc") |>
set_spacing(row_height = 24) |>
set_axis(gridlines = TRUE, gridline_style = "dotted")
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = custom_theme)All set_*() functions follow the same pattern:
set_colors(
background = "#ffffff", # Background color
foreground = "#1a1a1a", # Primary text color
primary = "#2563eb", # Primary accent for highlights
secondary = "#64748b", # Secondary text/UI color
accent = "#8b5cf6", # Accent color for emphasis
muted = "#94a3b8", # Muted/disabled text color
border = "#e2e8f0", # Border color for dividers
interval = "#2563eb", # Marker fill color
interval_line = "#475569", # Confidence interval line color
summary_fill = "#2563eb", # Summary diamond fill
summary_border = "#1d4ed8" # Summary diamond border
)| Color | Where It’s Used |
|---|---|
background |
Plot container background, table cells |
foreground |
Primary text: study labels, data values, column headers; row_emphasis text |
primary |
Row group header backgrounds (at reduced opacity), alternating row stripes, hover/selection highlights |
secondary |
Subtitles, less prominent UI text |
accent |
row_accent text styling; annotation labels |
muted |
Footnotes, captions; row_muted text styling |
border |
Row dividers, container borders, table grid lines |
interval |
Confidence interval marker fill (squares, circles, diamonds) |
interval_line |
CI whisker lines connecting lower to upper bounds |
summary_fill |
Summary row diamond fill color |
summary_border |
Summary diamond outline/stroke |
Row styling colors: Use row_emphasis, row_muted, or row_accent parameters to apply these colors to specific rows. See Row Styling for details.
interval and primary to the same value for a cohesive lookinterval_line than interval for subtle depthmarker_color in effect_forest()set_typography(
font_family = "system-ui, sans-serif", # CSS font-family string
font_size_sm = "0.75rem", # Small text (badges, counts)
font_size_base = "0.875rem", # Default text size
font_size_lg = "1rem", # Large text (headers)
font_weight_normal = 400, # Normal weight
font_weight_medium = 500, # Medium weight
font_weight_bold = 600, # Bold weight
header_font_scale = 1.05 # Header cell font scale relative to base
)set_spacing(
row_height = 28, # Data row height in pixels
header_height = 36, # Header row height in pixels
padding = 12, # Padding around forest plot SVG
container_padding = 0, # Left/right padding for outer container
cell_padding_x = 10, # Horizontal cell padding
cell_padding_y = 4, # Vertical cell padding
axis_gap = 12 # Gap between table and x-axis
)| Context | Recommended Height |
|---|---|
| Compact/print | 18-22px |
| Standard web | 28-32px |
| Touch/mobile | 36-40px |
| Presentation | 40-48px |
set_shapes(
point_size = 6, # Point marker radius in pixels
summary_height = 10, # Summary diamond height
line_width = 1.5, # Confidence interval line width
border_radius = 4, # Container border radius
effect_colors = NULL, # Default colors for multi-effect visualizations
marker_shapes = c("square", "circle", "diamond", "triangle")
)For visualizations with multiple effects per row (forest plots, bar charts, boxplots, violin plots), colors cycle through theme-defined effect_colors:
Effects without explicit color in their specification use these theme defaults in order, cycling if there are more effects than colors.
Each theme includes a curated 5-color effect_colors palette:
| Theme | Effect Colors | Style |
|---|---|---|
| default | #0891b2 #16a34a #f59e0b #ef4444 #8b5cf6 |
Balanced, accessible |
| modern | #3b82f6 #22c55e #f59e0b #ef4444 #8b5cf6 |
Vibrant blue-first |
| jama | #1a1a1a #4a4a4a #7a7a7a #9a9a9a #bababa |
Grayscale for print |
| lancet | #00468b #ed0000 #42b540 #0099b4 #925e9f |
Journal-inspired reds/blues |
| nature | #e64b35 #4dbbd5 #00a087 #3c5488 #f39b7f |
Warm coral palette |
| cochrane | #0c4da2 #dd5129 #1a8a4f #6d4e92 #e89a47 |
Strong primaries |
| dark | #89b4fa #a6e3a1 #fab387 #f38ba8 #cba6f7 |
Pastel for dark mode |
| minimal | #64748b #94a3b8 #cbd5e1 #475569 #334155 |
Subtle slate tones |
| presentation | #2563eb #16a34a #ea580c #dc2626 #7c3aed |
High-contrast for slides |
set_axis(
range_min = NULL, # Minimum axis value (NULL = auto)
range_max = NULL, # Maximum axis value (NULL = auto)
tick_count = 5, # Target number of ticks
tick_values = NULL, # Custom tick positions (overrides tick_count)
gridlines = FALSE, # Show vertical gridlines
gridline_style = "solid",# "solid", "dashed", or "dotted"
symmetric = NULL, # Force symmetric axis around null (NULL = auto)
include_null = TRUE, # Always include null value in range
null_tick = TRUE # Always show tick at null value
)Use symmetric = TRUE when comparing benefit vs harm magnitudes. This makes a HR of 0.5 and 2.0 appear equidistant from 1.0 on log scale.
Control the hierarchical styling for nested row groups (h1/h2/h3-style visual hierarchy):
set_group_headers(
level1_font_size = "0.9375rem",
level1_font_weight = 600,
level1_background = NULL, # NULL = 15% primary opacity
level1_border_bottom = FALSE,
level2_font_size = "0.875rem",
level2_font_weight = 500,
level2_italic = FALSE,
level2_background = NULL, # NULL = 10% primary opacity
level2_border_bottom = FALSE,
level3_font_size = "0.875rem",
level3_font_weight = 400,
level3_background = NULL, # NULL = 6% primary opacity
level3_border_bottom = FALSE,
indent_per_level = 16 # Indentation per nesting level (px)
)When levelN_background = NULL, the theme automatically computes a subtle background from the primary color at reduced opacity. Override with explicit hex colors for full control.
For extensive customization, use web_theme() to extend a base theme:
publication_theme <- web_theme(
name = "my_journal",
colors = list(
primary = "#1a365d",
interval = "#1a365d",
interval_line = "#2d3748"
),
typography = list(
font_family = "Georgia, serif",
font_size_base = "0.8rem"
),
spacing = list(
row_height = 22
),
base_theme = web_theme_minimal() # Start from minimal
)See Themes Reference for complete parameter documentation.
---
title: "Themes"
---
```{r}
#| include: false
library(tabviz)
# Sample data for examples
meta_data <- data.frame(
study = c("Smith 2020", "Jones 2021", "Lee 2022", "Chen 2023", "Park 2024"),
hr = c(0.72, 0.85, 0.91, 0.68, 0.79),
lower = c(0.55, 0.70, 0.75, 0.52, 0.61),
upper = c(0.95, 1.03, 1.10, 0.89, 1.02)
)
```
Themes control the visual appearance of forest plots—colors, typography, spacing, and shapes. You can use preset themes for common publication styles, or customize every aspect using a fluent pipe-based API.
::: {.callout-tip}
## Theme Design Philosophy
Each theme is designed with a specific context in mind:
- **Publication themes** (JAMA, Lancet, Nature): Journal-specific styling for submissions
- **Presentation themes** (modern, presentation, dark): Optimized for screen viewing
- **Utility themes** (default, minimal, cochrane): Clean and adaptable
:::
## Component Summary
| Component | Function | Key Properties |
|-----------|----------|----------------|
| Colors | `set_colors()` | background, foreground, primary, interval, interval_line |
| Typography | `set_typography()` | font_family, font_size_*, font_weight_*, header_font_scale |
| Spacing | `set_spacing()` | row_height, header_height, padding, container_padding, cell_padding_x/y |
| Shapes | `set_shapes()` | point_size, summary_height, line_width, effect_colors, marker_shapes |
| Axis | `set_axis()` | range_min/max, tick_count, tick_values, gridlines, symmetric |
| Layout | `set_layout()` | plot_position, row_border, container_border |
| Group Headers | `set_group_headers()` | level*_font_size, level*_background, indent_per_level |
| Effects | `set_effect_colors()`, `set_marker_shapes()` | Multi-effect default colors/shapes |
## Color Architecture
Colors serve different purposes in tabviz:
| Category | Purpose | Colors |
|----------|---------|--------|
| **Structural** | Define the canvas | `background`, `foreground`, `border` |
| **Semantic** | Convey meaning through styling | `primary`, `accent`, `muted` |
| **Data** | Visualize data elements | `interval`, `interval_line`, `summary_fill` |
### How Palette Colors Connect to Styling
The palette defines **what colors look like**. Semantic styles **apply those colors** to specific elements:
```
┌─────────────────────────────────────────────────────────────────┐
│ PALETTE (set_colors) STYLES (row_*/column) │
│ ───────────────────── ──────────────────── │
│ accent: "#8b5cf6" ───────► row_accent, cell accent │
│ muted: "#94a3b8" ───────► row_muted, cell muted │
│ foreground: "#333" ───────► row_emphasis (+ bold) │
│ primary: "#0891b2" ───────► group headers, hover │
└─────────────────────────────────────────────────────────────────┘
```
**Key insight**: Changing `accent` in the theme automatically updates all `row_accent` and cell `accent` styling.
::: {.callout-note}
## Semantic Styles at Row and Cell Level
The same semantic vocabulary works at both levels:
| Style | Effect | Row Parameter | Cell Parameter |
|-------|--------|---------------|----------------|
| **Emphasis** | Bold + foreground color | `row_emphasis` | `emphasis` in `web_col()` |
| **Muted** | Muted color (de-emphasized) | `row_muted` | `muted` in `web_col()` |
| **Accent** | Accent color (highlighted) | `row_accent` | `accent` in `web_col()` |
Row-level parameters accept column names with TRUE/FALSE values. Cell-level parameters in `web_col()` also accept column names.
:::
## Preset Themes
tabviz includes 9 preset themes designed for different use cases. All themes can be customized using a fluent API.
::: {.panel-tabset}
### Default
Clean, modern default with subtle cyan accents and system fonts.
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_default())
```
### JAMA
JAMA journal style: maximum density, pure black & white, Arial font, compact rows (18px). Optimized for print.
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_jama())
```
### Lancet
Elegant academic theme with Lancet navy blue (#00407A), warm off-white background, gold accents, and serif typography (Georgia).
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_lancet())
```
### Modern
Bold, vibrant design with Inter font, bright blue (#3B82F6) accents, larger elements (36px rows), and rounded corners.
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_modern())
```
### Presentation
Oversized theme for slides and posters: extra large fonts (1-1.25rem), thick lines (2.5px), oversized markers (12px), and tall rows (44px).
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_presentation())
```
### Dark
Catppuccin Mocha-inspired dark mode with comfortable low-contrast colors for extended viewing.
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_dark())
```
### Minimal
Academic publication-ready with pure black & white styling and Georgia serif font. Classic, authoritative look.
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_minimal())
```
### Cochrane
Cochrane systematic review style: teal accents (#0099CC), Arial font, very compact layout (20px rows).
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_cochrane())
```
### Nature
Nature journal family styling: Nature blue (#1976D2), Helvetica Neue font, tight typography, and precise aesthetic.
```{r}
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = web_theme_nature())
```
:::
## Customizing Themes
Modify any preset theme using pipe-friendly `set_*()` functions:
```{r}
custom_theme <- web_theme_jama() |>
set_colors(primary = "#0066cc", interval_line = "#0066cc") |>
set_spacing(row_height = 24) |>
set_axis(gridlines = TRUE, gridline_style = "dotted")
forest_plot(meta_data, point = "hr", lower = "lower", upper = "upper",
label = "study", null_value = 1, scale = "log",
theme = custom_theme)
```
::: {.callout-note}
## Modifier Pattern
All `set_*()` functions follow the same pattern:
1. Take a theme object as the first argument
2. Accept named parameters for properties to change
3. Return a modified theme object
4. Can be chained with pipes
```r
web_theme_default() |>
set_colors(primary = "red") |>
set_spacing(row_height = 32) |>
set_axis(gridlines = TRUE)
```
:::
## Theme Components
### Colors
```r
set_colors(
background = "#ffffff", # Background color
foreground = "#1a1a1a", # Primary text color
primary = "#2563eb", # Primary accent for highlights
secondary = "#64748b", # Secondary text/UI color
accent = "#8b5cf6", # Accent color for emphasis
muted = "#94a3b8", # Muted/disabled text color
border = "#e2e8f0", # Border color for dividers
interval = "#2563eb", # Marker fill color
interval_line = "#475569", # Confidence interval line color
summary_fill = "#2563eb", # Summary diamond fill
summary_border = "#1d4ed8" # Summary diamond border
)
```
::: {.callout-note}
## Color Usage Reference
| Color | Where It's Used |
|-------|-----------------|
| `background` | Plot container background, table cells |
| `foreground` | Primary text: study labels, data values, column headers; `row_emphasis` text |
| `primary` | Row group header backgrounds (at reduced opacity), alternating row stripes, hover/selection highlights |
| `secondary` | Subtitles, less prominent UI text |
| `accent` | `row_accent` text styling; annotation labels |
| `muted` | Footnotes, captions; `row_muted` text styling |
| `border` | Row dividers, container borders, table grid lines |
| `interval` | Confidence interval marker fill (squares, circles, diamonds) |
| `interval_line` | CI whisker lines connecting lower to upper bounds |
| `summary_fill` | Summary row diamond fill color |
| `summary_border` | Summary diamond outline/stroke |
**Row styling colors**: Use `row_emphasis`, `row_muted`, or `row_accent` parameters to apply these colors to specific rows. See [Row Styling](row-styling.qmd) for details.
:::
::: {.callout-tip}
## Semantic Color Tips
- Set `interval` and `primary` to the same value for a cohesive look
- Use a slightly darker `interval_line` than `interval` for subtle depth
- For conditional coloring (e.g., significant vs non-significant), add a color column to your data and map it with `marker_color` in `effect_forest()`
:::
### Typography
```r
set_typography(
font_family = "system-ui, sans-serif", # CSS font-family string
font_size_sm = "0.75rem", # Small text (badges, counts)
font_size_base = "0.875rem", # Default text size
font_size_lg = "1rem", # Large text (headers)
font_weight_normal = 400, # Normal weight
font_weight_medium = 500, # Medium weight
font_weight_bold = 600, # Bold weight
header_font_scale = 1.05 # Header cell font scale relative to base
)
```
### Spacing
```r
set_spacing(
row_height = 28, # Data row height in pixels
header_height = 36, # Header row height in pixels
padding = 12, # Padding around forest plot SVG
container_padding = 0, # Left/right padding for outer container
cell_padding_x = 10, # Horizontal cell padding
cell_padding_y = 4, # Vertical cell padding
axis_gap = 12 # Gap between table and x-axis
)
```
::: {.callout-note}
## Row Height Guidelines
| Context | Recommended Height |
|---------|-------------------|
| Compact/print | 18-22px |
| Standard web | 28-32px |
| Touch/mobile | 36-40px |
| Presentation | 40-48px |
:::
### Shapes
```r
set_shapes(
point_size = 6, # Point marker radius in pixels
summary_height = 10, # Summary diamond height
line_width = 1.5, # Confidence interval line width
border_radius = 4, # Container border radius
effect_colors = NULL, # Default colors for multi-effect visualizations
marker_shapes = c("square", "circle", "diamond", "triangle")
)
```
### Multi-Effect Color Defaults
For visualizations with multiple effects per row (forest plots, bar charts, boxplots, violin plots), colors cycle through theme-defined `effect_colors`:
```{r}
#| eval: false
# Set custom effect colors and shapes for multi-effect visualizations
custom_theme <- web_theme_default() |>
set_effect_colors(c("#2563eb", "#dc2626", "#16a34a")) |>
set_marker_shapes(c("square", "circle", "diamond"))
forest_plot(data, ..., theme = custom_theme)
```
Effects without explicit `color` in their specification use these theme defaults in order, cycling if there are more effects than colors.
#### Built-in Theme Palettes
Each theme includes a curated 5-color `effect_colors` palette:
```{r}
#| echo: false
#| output: asis
theme_colors <- data.frame(
Theme = c("default", "modern", "jama", "lancet", "nature", "cochrane", "dark", "minimal", "presentation"),
Colors = c(
"`#0891b2` `#16a34a` `#f59e0b` `#ef4444` `#8b5cf6`",
"`#3b82f6` `#22c55e` `#f59e0b` `#ef4444` `#8b5cf6`",
"`#1a1a1a` `#4a4a4a` `#7a7a7a` `#9a9a9a` `#bababa`",
"`#00468b` `#ed0000` `#42b540` `#0099b4` `#925e9f`",
"`#e64b35` `#4dbbd5` `#00a087` `#3c5488` `#f39b7f`",
"`#0c4da2` `#dd5129` `#1a8a4f` `#6d4e92` `#e89a47`",
"`#89b4fa` `#a6e3a1` `#fab387` `#f38ba8` `#cba6f7`",
"`#64748b` `#94a3b8` `#cbd5e1` `#475569` `#334155`",
"`#2563eb` `#16a34a` `#ea580c` `#dc2626` `#7c3aed`"
),
Style = c(
"Balanced, accessible",
"Vibrant blue-first",
"Grayscale for print",
"Journal-inspired reds/blues",
"Warm coral palette",
"Strong primaries",
"Pastel for dark mode",
"Subtle slate tones",
"High-contrast for slides"
)
)
knitr::kable(theme_colors, col.names = c("Theme", "Effect Colors", "Style"))
```
### Axis
```r
set_axis(
range_min = NULL, # Minimum axis value (NULL = auto)
range_max = NULL, # Maximum axis value (NULL = auto)
tick_count = 5, # Target number of ticks
tick_values = NULL, # Custom tick positions (overrides tick_count)
gridlines = FALSE, # Show vertical gridlines
gridline_style = "solid",# "solid", "dashed", or "dotted"
symmetric = NULL, # Force symmetric axis around null (NULL = auto)
include_null = TRUE, # Always include null value in range
null_tick = TRUE # Always show tick at null value
)
```
::: {.callout-tip}
## When to Use Symmetric Axes
Use `symmetric = TRUE` when comparing benefit vs harm magnitudes. This makes a HR of 0.5 and 2.0 appear equidistant from 1.0 on log scale.
:::
### Layout
```r
set_layout(
plot_position = "right", # "left" or "right"
row_border = TRUE, # Show row borders
row_border_style = "solid", # "solid", "dashed", "dotted"
container_border = FALSE, # Show border around container
container_border_radius = 8 # Corner radius in pixels
)
```
### Group Headers
Control the hierarchical styling for nested row groups (h1/h2/h3-style visual hierarchy):
```r
set_group_headers(
level1_font_size = "0.9375rem",
level1_font_weight = 600,
level1_background = NULL, # NULL = 15% primary opacity
level1_border_bottom = FALSE,
level2_font_size = "0.875rem",
level2_font_weight = 500,
level2_italic = FALSE,
level2_background = NULL, # NULL = 10% primary opacity
level2_border_bottom = FALSE,
level3_font_size = "0.875rem",
level3_font_weight = 400,
level3_background = NULL, # NULL = 6% primary opacity
level3_border_bottom = FALSE,
indent_per_level = 16 # Indentation per nesting level (px)
)
```
::: {.callout-note}
## Automatic Background Colors
When `levelN_background = NULL`, the theme automatically computes a subtle background from the primary color at reduced opacity. Override with explicit hex colors for full control.
:::
## Creating Custom Themes
For extensive customization, use `web_theme()` to extend a base theme:
```{r}
#| eval: false
publication_theme <- web_theme(
name = "my_journal",
colors = list(
primary = "#1a365d",
interval = "#1a365d",
interval_line = "#2d3748"
),
typography = list(
font_family = "Georgia, serif",
font_size_base = "0.8rem"
),
spacing = list(
row_height = 22
),
base_theme = web_theme_minimal() # Start from minimal
)
```
See [Themes Reference](../reference/themes.qmd) for complete parameter documentation.