---
title: "Content Collections: Deep Dive"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Content Collections: Deep Dive}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5
)
```

This vignette goes deep into content collections - the first layer of dashboardr's architecture. For a quick overview, see `vignette("getting-started")`.

Throughout this vignette, we use the [General Social Survey](https://kjhealy.github.io/gssr/) as example data. Click below to see the data loading code.

<details>
<summary>📂 **Data Setup** (click to expand)</summary>

```{r setup, message=FALSE, warning=FALSE}
library(dashboardr)
library(dplyr)
library(gssr)
library(haven)

# Load GSS data
data(gss_all)

# Latest wave only (for most examples)
gss <- gss_all %>%
  select(year, age, sex, race, degree, happy, polviews, wtssps,
         # Continuous variables for scatter plots
         educ, childs,
         # Confidence in institutions (for multi-question stackedbar example)
         confinan, conbus, coneduc, confed, conmedic) %>%
  filter(year == max(year, na.rm = TRUE)) %>%
  # Filter to substantive responses for core variables
  filter(
    happy %in% 1:3,        # very happy, pretty happy, not too happy
    polviews %in% 1:7,     # extremely liberal to extremely conservative
    !is.na(age), !is.na(sex), !is.na(race), !is.na(degree)
  ) %>%
  # Convert to factors with proper labels
  mutate(
    happy = droplevels(as_factor(happy)),
    polviews = droplevels(as_factor(polviews)),
    degree = droplevels(as_factor(degree)),
    sex = droplevels(as_factor(sex)),
    race = droplevels(as_factor(race)),
    # Confidence variables: convert non-substantive (IAP, don't know, etc.) to NA
    # Then use drop_na_vars = TRUE in visualizations
    confinan = ifelse(confinan %in% 1:3, confinan, NA),
    conbus = ifelse(conbus %in% 1:3, conbus, NA),
    coneduc = ifelse(coneduc %in% 1:3, coneduc, NA),
    confed = ifelse(confed %in% 1:3, confed, NA),
    conmedic = ifelse(conmedic %in% 1:3, conmedic, NA),
    # Convert to labeled factors
    confinan = factor(confinan, levels = 1:3, labels = c("a great deal", "only some", "hardly any")),
    conbus = factor(conbus, levels = 1:3, labels = c("a great deal", "only some", "hardly any")),
    coneduc = factor(coneduc, levels = 1:3, labels = c("a great deal", "only some", "hardly any")),
    confed = factor(confed, levels = 1:3, labels = c("a great deal", "only some", "hardly any")),
    conmedic = factor(conmedic, levels = 1:3, labels = c("a great deal", "only some", "hardly any"))
  )

# Full time series (for timeline examples)
gss_timeline <- gss_all %>%
  select(year, happy) %>%
  filter(happy %in% 1:3, !is.na(year)) %>%
  mutate(happy = droplevels(as_factor(happy)))
```

</details>

```{r, include = FALSE}
library(dashboardr)
```

## 📦 What Content Collections Store

A content collection is a container that holds visualizations, content blocks, interactive inputs, and layout helpers. Here's what you can add:

<table style="width:100%; border-collapse: collapse; margin: 20px 0;">
<thead>
<tr style="background: #f8f9fa; border-bottom: 2px solid #dee2e6;">
<th style="padding: 12px; text-align: left;">Category</th>
<th style="padding: 12px; text-align: left;">Types</th>
<th style="padding: 12px; text-align: left;">Functions</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 12px; vertical-align: top;">
<strong><a href="#visualization-types">Visualizations</a></strong>
</td>
<td style="padding: 12px; vertical-align: top;">
Bar, Stacked Bar, Histogram, Density, Boxplot, Timeline, Heatmap, Scatter, Treemap, Map
</td>
<td style="padding: 12px; vertical-align: top;">
<code>add_viz()</code>, <code>add_vizzes()</code>
</td>
</tr>
<tr style="border-bottom: 1px solid #dee2e6; background: #fafafa;">
<td style="padding: 12px; vertical-align: top;">
<strong><a href="#content-blocks">Content Blocks</a></strong>
</td>
<td style="padding: 12px; vertical-align: top;">
Text, Callouts, Cards, Accordions, Quotes, Badges, Metrics, Value Boxes, Code, Images, Videos, iframes, HTML
</td>
<td style="padding: 12px; vertical-align: top;">
<code>add_text()</code>, <code>add_callout()</code>, <code>add_card()</code>, <code>add_accordion()</code>, <code>add_quote()</code>, <code>add_badge()</code>, <code>add_metric()</code>, <code>add_value_box()</code>, <code>add_code()</code>, <code>add_image()</code>, <code>add_html()</code>
</td>
</tr>
<tr style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 12px; vertical-align: top;">
<strong><a href="#tables-and-custom-charts">Tables &amp; Custom Charts</a></strong>
</td>
<td style="padding: 12px; vertical-align: top;">
gt Tables, Reactable, DT DataTables, Basic Tables, Custom Highcharter Charts
</td>
<td style="padding: 12px; vertical-align: top;">
<code>add_gt()</code>, <code>add_reactable()</code>, <code>add_DT()</code>, <code>add_table()</code>, <code>add_hc()</code>
</td>
</tr>
<tr style="border-bottom: 1px solid #dee2e6; background: #fafafa;">
<td style="padding: 12px; vertical-align: top;">
<strong>Interactive Inputs</strong> <em>(see <a href="advanced-features.html">Advanced Features</a>)</em>
</td>
<td style="padding: 12px; vertical-align: top;">
Dropdowns, Checkboxes, Sliders, Radio buttons
</td>
<td style="padding: 12px; vertical-align: top;">
<code>add_input_row()</code>, <code>add_input()</code>
</td>
</tr>
<tr style="background: #fafafa;">
<td style="padding: 12px; vertical-align: top;">
<strong><a href="#layout-helpers">Layout Helpers</a></strong>
</td>
<td style="padding: 12px; vertical-align: top;">
Dividers, Spacers, Pagination markers
</td>
<td style="padding: 12px; vertical-align: top;">
<code>add_divider()</code>, <code>add_spacer()</code>, <code>add_pagination()</code>
</td>
</tr>
</tbody>
</table>

Everything is stored in a unified `items` list, and you can inspect it anytime:

```{r}
content <- create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education") %>%
  add_text("## Analysis", "", "Key findings:") %>%
  add_callout(paste0("Sample size: ", nrow(gss)), type = "note")

print(content)
```

```{r}
preview(content)
```


## ⚙️ The Defaults System

The defaults system lets you **set once, use many times**. Instead of repeating the same parameters for every visualization, you define them once in `create_content()` and they automatically apply to all `add_viz()` calls.

### Why Use Defaults?

Without defaults, you'd need to repeat parameters for every chart:

```r
# Repetitive - don't do this!
content %>%
  add_viz(x_var = "age", type = "bar", color_palette = c("#3498DB"), bar_type = "percent") %>%
  add_viz(x_var = "sex", type = "bar", color_palette = c("#3498DB"), bar_type = "percent") %>%
  add_viz(x_var = "race", type = "bar", color_palette = c("#3498DB"), bar_type = "percent")
```

With defaults, you set them once and they flow through:

```r
# Clean - do this!
create_content(data = gss, type = "bar", color_palette = c("#3498DB"), bar_type = "percent") %>%
  add_viz(x_var = "age") %>%
  add_viz(x_var = "sex") %>%
  add_viz(x_var = "race")
```
  
### Override Hierarchy

Parameters are resolved in this order (later wins):

1. **Collection defaults** - set in `create_content()`
2. **Individual viz settings** - set in `add_viz()`

This means you can set sensible defaults for most charts, then override specific ones as needed:

```{r}
# Default: horizontal percent bars in blue
content <- create_content(
  data = gss,
  type = "bar",
  bar_type = "percent",
  horizontal = TRUE,
  color_palette = c("#3498DB")
) %>%
  # Uses all defaults
  add_viz(x_var = "degree", title = "Education") %>%
  # Override: vertical instead of horizontal
  add_viz(x_var = "race", title = "Race (vertical)", horizontal = FALSE) %>%
  # Override: count instead of percent
  add_viz(x_var = "sex", title = "Sex (count)", bar_type = "count")

print(content)
```

```{r}
content %>% preview()
```

### Any Parameter Can Be a Default

**Any parameter** you can pass to `add_viz()` can be set as a default in `create_content()`. Common ones include:

| Parameter | Description | Example Values |
|-----------|-------------|----------------|
| `type` | Visualization type | `"bar"`, `"histogram"`, `"stackedbar"`, `"timeline"` |
| `color_palette` | Colors for charts | `c("#3498DB", "#E74C3C")` |
| `bar_type` | Bar chart display | `"count"`, `"percent"`, `"mean"` |
| `horizontal` | Flip chart orientation | `TRUE`, `FALSE` |
| `weight_var` | Survey weight column | `"wtssps"` |
| `drop_na_vars` | Remove NA values | `TRUE`, `FALSE` |
| `stacked_type` | Stacked bar display | `"count"`, `"percent"` |
| `bins` | Histogram bins | `20`, `30`, `50` |
| `x_label` | Custom x-axis label | `"Age (years)"` |
| `y_label` | Custom y-axis label | `"Number of Respondents"` |
| `tooltip_suffix` | Text after tooltip values | `"%"`, `" people"` |
| `error_bars` | Error bar type (for `bar_type = "mean"`) | `"none"`, `"sd"`, `"se"`, `"ci"` |
| `value_var` | Numeric variable for means | `"score"`, `"income"` |

See the [Visualization Types](#visualization-types) section for all parameters available for each chart type.

## 🏷️ Custom Labels and Tooltips

Every visualization supports custom axis labels and tooltip formatting. These help make your charts clearer and more professional.

### Axis Labels

By default, `dashboardr` uses your variable names as axis labels. Override them with `x_label` and `y_label`:
 
```{r}
# Without custom labels (uses variable names)
create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education Levels") %>%
  preview()
```

```{r}
# With custom labels (more polished)
create_content(data = gss, type = "bar") %>%
  add_viz(
    x_var = "degree", 
    title = "Education Levels",
    x_label = "Highest Degree Earned",
    y_label = "Number of Respondents"
  ) %>%
  preview()
```

For stacked charts, you can also set `stack_label` to customize the legend title:

```{r}
create_content(data = gss, type = "stackedbar") %>%
  add_viz(
    x_var = "degree", 
    stack_var = "sex",
    title = "Education by Gender",
    x_label = "Highest Degree",
    y_label = "Count",
    stack_label = "Gender"
  ) %>%
  preview()
```

### Tooltip Customization

Tooltips are the interactive popups that appear when users hover over data points. Customize them with prefix and suffix options:

| Parameter | Description | Example |
|-----------|-------------|---------|
| `tooltip_prefix` | Text before the value | `"Count: "`, `"$"` |
| `tooltip_suffix` | Text after the value | `"%"`, `" respondents"` |
| `x_tooltip_suffix` | Text after x-axis value | `" years"`, `" USD"` |

```{r}
# Add units to tooltips
create_content(data = gss, type = "histogram") %>%
  add_viz(
    x_var = "age", 
    title = "Age Distribution",
    x_label = "Age",
    y_label = "Count",
    tooltip_suffix = " respondents",
    x_tooltip_suffix = " years old"
  ) %>%
  preview()
```

```{r}
# Percentage tooltips for stacked bars
create_content(data = gss, type = "stackedbar") %>%
  add_viz(
    x_var = "happy", 
    stack_var = "sex",
    title = "Happiness by Gender",
    stacked_type = "percent",
    y_label = "Percentage",
    tooltip_suffix = "%"
  ) %>%
  preview()
```


## ➕ Combining Collections

### The + Operator

Merge collections while preserving structure:

```{r}
demographics <- create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education", tabgroup = "Demo")

attitudes <- create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "happy", title = "Happiness", tabgroup = "Attitudes")

combined <- demographics + attitudes
print(combined)
```


### combine_viz()

Same as `+` but in pipe-friendly form:

```{r}
all_content <- demographics %>%
  combine_viz(attitudes)

print(all_content)
```

## 📊 Visualization Types {#visualization-types}

dashboardr supports 11 visualization types. Each is optimized for different data patterns. For detailed documentation on each type, see the individual vignettes:
- [Bar Charts](bar_vignette.html) | [Stacked Bars](stackedbar_vignette.html)
- [Histograms](histogram_vignette.html) | [Density Plots](density_vignette.html) | [Box Plots](boxplot_vignette.html)
- [Timelines](timeline_vignette.html) | [Heatmaps](heatmap_vignette.html) | [Scatter Plots](scatter_vignette.html)
- [Treemaps](treemap_vignette.html) | [Maps](map_vignette.html)

### Bar Charts

Bar charts are the workhorse of categorical data. Use them when you want to compare counts or proportions across categories.

The simplest bar chart counts occurrences of each category:

```{r}
create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education Levels") %>%
  preview()
```

Often you'll want percentages instead of raw counts. Set `bar_type = "percent"` to show proportions that sum to 100%:

```{r}
create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "race", title = "Race Distribution", bar_type = "percent") %>%
  preview()
```

To compare categories across groups, add `group_var`. This creates side-by-side bars:

```{r}
create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", group_var = "sex", title = "Education by Sex") %>%
  preview()
```

For long category labels, flip to horizontal with `horizontal = TRUE`:

```{r}
create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education (Horizontal)", horizontal = TRUE) %>%
  preview()
```

#### Means with Error Bars

For numeric outcomes, use `bar_type = "mean"` with `error_bars` to show statistical uncertainty:
  
```{r}
# Sample data with numeric scores
score_data <- data.frame(
  group = rep(c("Control", "Treatment"), each = 50),
  score = c(rnorm(50, 70, 10), rnorm(50, 78, 12))
)

create_content(data = score_data, type = "bar") %>%
  add_viz(
    x_var = "group",
    value_var = "score",
    bar_type = "mean",
    error_bars = "ci",           # "sd", "se", or "ci"
    title = "Mean Scores (95% CI)"
  ) %>%
  preview()
```

| Error Type | Parameter | Shows |
|------------|-----------|-------|
| Standard Deviation | `error_bars = "sd"` | Spread of individual values |
| Standard Error | `error_bars = "se"` | Precision of mean estimate |
| Confidence Interval | `error_bars = "ci"` | Range likely to contain true mean |

See the [Bar Chart vignette](bar_vignette.html) for more options including grouped means and error bar styling.

### Stacked Bars

Stacked bars show composition - how a whole breaks down into parts. Use `stack_var` to specify what fills each bar.

This shows the happiness distribution within each education level as raw counts:

```{r}
create_content(data = gss, type = "stackedbar") %>%
  add_viz(x_var = "degree", stack_var = "happy", title = "Happiness by Education") %>%
  preview()
```

For easier comparison across groups of different sizes, use `stacked_type = "percent"`. Now each bar sums to 100%, making patterns clearer:

```{r}
create_content(data = gss, type = "stackedbar") %>%
  add_viz(x_var = "degree", stack_var = "happy", title = "Happiness by Education (%)",
          stacked_type = "percent") %>%
  preview()
```

Horizontal stacked bars work well when you have many categories or long labels:

```{r}
create_content(data = gss, type = "stackedbar") %>%
  add_viz(x_var = "degree", stack_var = "happy", title = "Horizontal Stacked",
          stacked_type = "percent", horizontal = TRUE) %>%
  preview()
```

See the [Stacked Bar vignette](stackedbar_vignette.html) for more options.

### Histograms

Histograms show the distribution of continuous variables. The `bins` parameter controls granularity - more bins show more detail, fewer bins show smoother patterns.

```{r}
create_content(data = gss, type = "histogram") %>%
  add_viz(x_var = "age", title = "Age Distribution", bins = 25,
          x_label = "Age (years)", y_label = "Frequency") %>%
  preview()
```

See the [Histogram vignette](histogram_vignette.html) for more options.

### Density Plots

Density plots show smooth estimates of the distribution of a continuous variable. They're useful when you want a cleaner view of the distribution shape compared to histograms.

```{r}
create_content(data = gss, type = "density") %>%
  add_viz(x_var = "age", title = "Age Distribution (Density)",
          x_label = "Age (years)") %>%
  preview()
```

Add `group_var` to compare distributions across groups:

```{r}
create_content(data = gss, type = "density") %>%
  add_viz(x_var = "age", group_var = "sex", 
          title = "Age Distribution by Sex") %>%
  preview()
```

See the [Density vignette](density_vignette.html) for more options.

### Box Plots

Box plots (also called box-and-whisker plots) show the distribution of a numeric variable across categories. They display the median, quartiles, and outliers, making it easy to compare distributions.

```{r}
create_content(data = gss, type = "boxplot") %>%
  add_viz(x_var = "degree", y_var = "age", 
          title = "Age Distribution by Education Level",
          x_label = "Highest Degree", y_label = "Age") %>%
  preview()
```

See the [Box Plot vignette](boxplot_vignette.html) for more options.

### Multiple Stacked Bars (Likert Scales)

When you have multiple survey questions with the same response scale (like Likert items), use `type = "stackedbar"` with `x_vars` to display them together for comparison. This is perfect for "confidence in institutions" batteries or agreement scales.

Use `x_vars` for the question columns and `x_var_labels` for readable labels. Note: These confidence questions use a split-ballot design, so ~1/3 of respondents have NA (question not asked). We use `drop_na_vars = TRUE` to exclude those:

```{r}
create_content(data = gss, type = "stackedbar", drop_na_vars = TRUE) %>%
  add_viz(
    x_vars = c("confinan", "conbus", "coneduc", "confed", "conmedic"),
    x_var_labels = c("Banks & financial institutions", 
                     "Major companies",
                     "Education",
                     "Federal government",
                     "Medicine"),
    title = "Confidence in Institutions",
    stacked_type = "percent",
    horizontal = TRUE
  ) %>%
  preview()
```

See the [Stacked Bar vignette](stackedbar_vignette.html) for more options including multi-question mode.

### Timeline

Timeline charts track how responses change over time. You need a time variable (`time_var`) and a response variable (`y_var`). Unlike other chart types, timelines need data spanning multiple time periods.

```{r}
create_content(data = gss_timeline, type = "timeline") %>%
  add_viz(
    time_var = "year",
    y_var = "happy",
    title = "Happiness Over Time",
    chart_type = "line"
  ) %>%
  preview()
```

Use `chart_type = "stacked_area"` for a stacked area chart, which emphasizes cumulative patterns:

```{r}
create_content(data = gss_timeline, type = "timeline") %>%
  add_viz(
    time_var = "year",
    y_var = "happy",
    title = "Happiness Trends (Stacked Area)",
    chart_type = "stacked_area"
  ) %>%
  preview()
```

See the [Timeline vignette](timeline_vignette.html) for more options.

### Heatmap

Heatmaps visualize how a numeric value varies across two categorical dimensions, displayed as a color-coded grid. Each cell shows the aggregated value for that combination of categories.

**Required parameters:**

| Parameter | Description |
|-----------|-------------|
| `x_var` | Categorical variable for columns |
| `y_var` | Categorical variable for rows |
| `value_var` | Numeric variable to aggregate and color by |

**How it works:** The heatmap automatically aggregates `value_var` for each unique combination of `x_var` and `y_var`. **By default, it calculates the mean** - so if you have 50 people with a bachelor's degree who are "very happy", the cell shows their average age. You can change this with `agg_fun` (e.g., `agg_fun = median` for medians).

```{r}
create_content(data = gss, type = "heatmap") %>%
  add_viz(
    x_var = "degree",
    y_var = "happy",
    value_var = "age",
    title = "Average Age by Education & Happiness",
    color_palette = c("#FFF5F0", "#67000D"),
    label_decimals = 0,
    weight_var = "wtssps"
  ) %>%
  preview()
```

**Key optional parameters:**

| Parameter | Description | Default |
|-----------|-------------|---------|
| `agg_fun` | Aggregation function (e.g., `mean`, `median`, `sum`) | `mean` |
| `label_decimals` | Decimal places for cell labels and tooltips | `1` |
| `color_palette` | Two colors for gradient (low, high) | `c("#FFFFFF", "#7CB5EC")` |
| `color_min` / `color_max` | Fixed color scale bounds | Auto from data |
| `x_order` / `y_order` | Custom category ordering | Auto |
| `data_labels_enabled` | Show values in cells | `TRUE` |
| `weight_var` | Column for weighted aggregation | `NULL` |

See the [Heatmap vignette](heatmap_vignette.html) for more options.

### Scatter Plots

Scatter plots show relationships between two numeric variables. Each point represents one observation. Key parameters:

- `x_var` - Variable for horizontal axis
- `y_var` - Variable for vertical axis  
- `color_var` - Optional grouping variable for colored points
- `size_var` - Optional variable to control point sizes
- `show_trend` - Add a trend line (`TRUE`/`FALSE`)
- `alpha` - Point transparency (0-1)

```{r}
# Filter to respondents with valid education and children data
scatter_data <- gss %>%
  filter(!is.na(educ), !is.na(childs))

create_content(data = scatter_data, type = "scatter") %>%
  add_viz(
    x_var = "educ",
    y_var = "childs",
    color_var = "sex",
    title = "Education vs Number of Children",
    x_label = "Years of Education",
    y_label = "Number of Children",
    alpha = 0.4
  ) %>%
  preview()
```

See the [Scatter Plot vignette](scatter_vignette.html) for more options.

### Treemap

Treemaps display hierarchical data as nested rectangles. The size of each rectangle represents its value - larger rectangles mean larger values. Great for showing composition and proportions across many categories.

**Important**: Treemaps require pre-aggregated data with a value column. You typically need to `count()` or `summarize()` your data first.

Key parameters:

- `group_var` - Primary grouping variable (creates the rectangles)
- `subgroup_var` - Optional secondary grouping for hierarchical treemaps
- `value_var` - Numeric column that determines rectangle size
- `color_var` - Optional variable for coloring (defaults to group_var)

```{r}
# Treemaps need pre-aggregated data
degree_counts <- gss %>%
  count(degree, name = "n")

create_content(data = degree_counts, type = "treemap") %>%
  add_viz(
    group_var = "degree",
    value_var = "n",
    title = "Education Distribution"
  ) %>%
  preview()
```

For hierarchical treemaps, add `subgroup_var`:

```{r}
# Two-level hierarchy: degree within sex
degree_sex_counts <- gss %>%
  count(sex, degree, name = "n")

create_content(data = degree_sex_counts, type = "treemap") %>%
  add_viz(
    group_var = "sex",
    subgroup_var = "degree",
    value_var = "n",
    title = "Education by Sex"
  ) %>%
  preview()
```

See the [Treemap vignette](treemap_vignette.html) for more options.

### Map

Geographic maps (choropleths) display data across regions using color intensity. Each region is shaded based on a numeric value, making it easy to see geographic patterns at a glance.

**Key parameters:**

| Parameter | Description |
|-----------|-------------|
| `value_var` | Numeric column for color intensity |
| `join_var` | Column with geographic codes (must match map's internal codes) |
| `map_type` | Map geography: `"custom/world"`, `"countries/us/us-all"`, etc. |
| `color_palette` | Two colors for gradient (low, high) |
| `tooltip_vars` | Variables to show in hover tooltips |

**Important**: Maps require geographic identifier codes that match the map's internal region codes:

- **World maps**: Use 2-letter ISO country codes (`iso2c`): "US", "DE", "FR", etc.
- **US state maps**: Use postal codes: "CA", "NY", "TX", etc.

```{r}
# Load gapminder data and add ISO country codes
library(gapminder)
library(countrycode)

# Get latest year and convert country names to ISO codes
map_data <- gapminder %>%
  filter(year == max(year)) %>%
  mutate(iso2c = countrycode(country, "country.name", "iso2c")) %>%
  filter(!is.na(iso2c))
```

Now create a world map showing life expectancy:

```{r}
create_content(data = map_data, type = "map") %>%
  add_viz(
    value_var = "lifeExp",
    join_var = "iso2c",
    map_type = "custom/world",
    title = "Life Expectancy by Country (2007)",
    color_palette = c("#fee5d9", "#a50f15")
  ) %>%
  preview()
```



**Available map types:**

| Map Type | Region | Join Key |
|----------|--------|----------|
| `"custom/world"` | World countries | `iso2c` (2-letter ISO) |
| `"custom/world-highres"` | World (high resolution) | `iso2c` |
| `"countries/us/us-all"` | US states | Postal codes ("CA", "NY") |
| `"countries/de/de-all"` | German states | State codes |
| `"custom/europe"` | European countries | `iso2c` |

See the [Map vignette](map_vignette.html) for more options.

## 📁 Organizing with Tabgroups

Tabgroups create tabbed interfaces in your dashboard:

```{r}
content <- create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education", tabgroup = "demographics") %>%
  add_viz(x_var = "race", title = "Race", tabgroup = "demographics") %>%
  add_viz(x_var = "happy", title = "Happiness", tabgroup = "attitudes") %>%
  add_viz(x_var = "polviews", title = "Politics", tabgroup = "attitudes")

print(content)
```


### Nested Tabgroups

For complex dashboards, you can create **multi-level tab hierarchies** using `/` as a separator:

```{r}
nested <- create_content(data = gss, type = "bar") %>%
  # Top-level: "Demographics" with two sub-tabs
  add_viz(x_var = "degree", title = "Education Level", 
          tabgroup = "Demographics/Education") %>%
  add_viz(x_var = "race", title = "Race Distribution", 
          tabgroup = "Demographics/Race") %>%
  add_viz(x_var = "age", title = "Age Distribution", 
          tabgroup = "Demographics/Age") %>%
  # Top-level: "Attitudes" with sub-tabs
  add_viz(x_var = "happy", title = "General Happiness", 
          tabgroup = "Attitudes/Wellbeing") %>%
  add_viz(x_var = "polviews", title = "Political Views", 
          tabgroup = "Attitudes/Politics")

print(nested)
```

```{r}
preview(nested)
```


### Custom Tab Labels

Replace tabgroup IDs with readable labels using `set_tabgroup_labels()`:

```{r}
labeled <- create_content(data = gss, type = "bar") %>%
  add_viz(x_var = "degree", title = "Education", tabgroup = "demo") %>%
  add_viz(x_var = "happy", title = "Happiness", tabgroup = "attitudes") %>%
  set_tabgroup_labels(
    demo = "Demographics",
    attitudes = "Attitudes & Values"
  )

print(labeled)
```

```{r}
labeled %>% preview()
```

## 📝 Content Blocks

Content blocks add non-visualization elements to your collections.

### Text Blocks

`add_text()` is a flexible function for adding markdown content. It has several smart features:

**Multiple arguments become lines** - Pass as many strings as you want, and they join with newlines:

```{r}
create_content() %>%
  add_text(
    "## Survey Overview",
    "",
    "The General Social Survey (GSS) has been conducted since 1972.",
    "It covers demographics, attitudes, and social behaviors."
  ) %>%
  preview()
```

**Use empty strings for spacing** - An empty `""` creates a paragraph break:

```{r}
create_content() %>%
  add_text(
    "## Section One",
    "",
    "First paragraph of content.",
    "",
    "## Section Two",
    "",
    "Second paragraph with a gap above."
  ) %>%
  preview()
```

**Full Markdown support** - Headers, lists, bold, italic, links, and more:

```{r}
text_example <- create_content() %>%
  add_text(
    "## Key Findings",
    "",
    "This analysis reveals **three important patterns**:",
    "",
    "1. Education correlates with happiness",
    "2. Age distribution is *roughly normal*",
    "3. Political views show [polarization](https://en.wikipedia.org/wiki/Political_polarization)",
    "",
    "### Methodology Note",
    "",
    "> Data weighted using GSS survey weights"
  )

print(text_example)
```

```{r}
text_example %>% preview()
```

**Works standalone or in pipes** - Call it directly or chain it:

```r
# Standalone - returns a content block
block <- add_text("# Title")

# In a pipe - adds to existing collection
content %>% add_text("More content here")
```

### Callouts

Callouts draw attention to specific information with colored boxes and icons. Use them to highlight notes, tips, warnings, or critical information that readers shouldn't miss.

**Five types for different purposes:**

| Type | Use for | Color |
|------|---------|-------|
| `note` | Additional context, background info | Blue |
| `tip` | Helpful suggestions, best practices | Green |
| `warning` | Potential issues, things to watch out for | Yellow |
| `caution` | Proceed carefully, possible problems | Orange |
| `important` | Critical information, must-read | Red |

```{r}
callout_gallery <- create_content() %>%
  add_callout("Notes provide additional context or information.", type = "note", title = "Note") %>%
  add_callout("Tips offer helpful suggestions to improve workflow.", type = "tip", title = "Pro Tip") %>%
  add_callout("Warnings alert users to potential issues.", type = "warning", title = "Warning") %>%
  add_callout("Caution indicates something that needs careful attention.", type = "caution", title = "Caution") %>%
  add_callout("Important highlights critical information.", type = "important", title = "Important")

print(callout_gallery)
```

```{r}
callout_gallery %>% preview()
```

**Custom titles are optional** - If you omit `title`, the type name is used:

```{r}
create_content() %>%
  add_callout("Sample size is smaller than recommended for this analysis.", type = "warning") %>%
  preview()
```

**Practical example** - Combine callouts with visualizations:

```{r}
create_content(data = gss, type = "bar") %>%
  add_callout("This chart shows unweighted counts. See methodology for weighted estimates.", type = "note") %>%
  add_viz(x_var = "degree", title = "Education Distribution") %>%
  add_callout("Education data may reflect selection bias in survey response rates.", type = "tip", title = "Interpretation Tip") %>%
  preview()
```

### Cards

Styled content containers with optional titles:

```{r}
card_example <- create_content() %>%
  add_card(
    title = "Key Finding",
    text = "Education level shows a strong positive correlation with reported happiness levels across all demographic groups."
  ) %>%
  add_card(
    text = "Cards without titles work too. Great for quick content blocks."
  )

print(card_example)
```

```{r}
card_example %>% preview()
```

### Accordions

Collapsible sections for supplementary or detailed content:

```{r}
accordion_example <- create_content() %>%
  add_accordion(
    title = "Click to expand: Methodology",
    text = "This analysis uses weighted survey data from NORC. Sample size: 21,788 respondents."
  ) %>%
  add_accordion(
    title = "Click to expand: Data Sources",
    text = "General Social Survey (GSS), collected annually since 1972."
  )

print(accordion_example)
```

```{r}
accordion_example %>% preview()
```

### Quotes

Block quotes with optional attribution:

```{r}
quote_example <- create_content() %>%
  add_quote(
    quote = "The only true wisdom is in knowing you know nothing.",
    attribution = "Socrates"
  )

print(quote_example)
```

```{r}
quote_example %>% preview()
```

### Badges

Inline status indicators:

```{r}
badge_example <- create_content() %>%
  add_text("Status indicators:") %>%
  add_badge("Complete", color = "success") %>%
  add_badge("In Progress", color = "warning") %>%
  add_badge("Not Started", color = "danger") %>%
  add_badge("Info", color = "info") %>%
  add_badge("Primary", color = "primary") %>%
  add_badge("Secondary", color = "secondary")

print(badge_example)
```

```{r}
badge_example %>% preview()
```

### Metrics

Single KPI value boxes:

```{r}
metric_example <- create_content() %>%
  add_metric(
    title = "Total Respondents",
    value = "21,788",
    icon = "ph:users"
  )

print(metric_example)
```

```{r}
metric_example %>% preview()
```

### Value Boxes

Custom-styled value boxes with branding:

```{r}
value_box_example <- create_content() %>%
  add_value_box(
    title = "Revenue",
    value = "$1.2M",
    bg_color = "#27AE60"
  )

print(value_box_example)
```

```{r}
value_box_example %>% preview()
```

### Value Box Rows

Multiple value boxes in a responsive row:

```{r}
value_row_example <- create_content() %>%
  add_value_box_row() %>%
    add_value_box(title = "Users", value = "12,345", bg_color = "#3498DB") %>%
    add_value_box(title = "Sessions", value = "45,678", bg_color = "#9B59B6") %>%
    add_value_box(title = "Conversion", value = "3.2%", bg_color = "#E74C3C") %>%
  end_value_box_row()

print(value_row_example)
```

```{r}
value_row_example %>% preview()
```

### Code Blocks

Syntax-highlighted code snippets:

```{r}
code_example <- create_content() %>%
  add_code(
    code = "library(dashboardr)\n\ncreate_content(data = df) %>%\n  add_viz(type = 'bar', x_var = 'category')",
    language = "r"
  )

print(code_example)
```

```{r}
code_example %>% preview()
```

### Images

Add images with optional captions. **Use full URLs** for images to ensure they display on GitHub Pages:

```{r}
image_example <- create_content() %>%
  add_image(
    src = "https://favstats.github.io/dashboardr/reference/figures/logo.svg",
    alt = "dashboardr Logo",
    caption = "The dashboardr package logo",
    width = "200px"
  )

print(image_example)
image_example %>% preview()
```

> **Tip**: Always use absolute URLs (starting with `https://`) for images in vignettes. Local file paths like `"logo.png"` work during development but won't display on GitHub Pages or pkgdown sites.

### Videos

For local video files, use `add_video()` - but note these won't work on GitHub Pages unless hosted elsewhere:

```{r, eval=FALSE}
# Local video (works locally, not on GitHub)
create_content() %>%
  add_video(
    src = "presentation.mp4",
    caption = "Project overview video"
  )
```

For video content, use YouTube or Vimeo embeds via `add_iframe()` - this is more reliable than direct video files:

```{r}
# YouTube embed (recommended approach)
video_example <- create_content() %>%
  add_iframe(
    src = "https://www.youtube.com/embed/dQw4w9WgXcQ",
    height = "315px"
  )

print(video_example)
video_example %>% preview()
```



### iframes

Embed external content like interactive maps, dashboards, or other web pages:

```{r}
iframe_example <- create_content() %>%
  add_iframe(
    src = "https://www.openstreetmap.org/export/embed.html?bbox=-0.1%2C51.5%2C0.0%2C51.52",
    height = "300px"
  )

print(iframe_example)
iframe_example %>% preview()
```

### Raw HTML

Insert custom HTML when needed:

```{r}
html_example <- create_content() %>%
  add_html('<div style="background: linear-gradient(to right, #667eea, #764ba2); color: white; padding: 20px; border-radius: 8px; text-align: center;"><h3>Custom HTML Block</h3><p>Style anything with raw HTML!</p></div>')

print(html_example)
```

```{r}
html_example %>% preview()
```

## 📊 Tables and Custom Charts

Beyond visualizations created with `add_viz()`, you can embed tables and custom charts directly into content collections.

### gt Tables

Use `add_gt()` to embed publication-quality tables created with the [gt package](https://gt.rstudio.com/):
                              
```{r, warning = FALSE}
# Create a summary table
summary_data <- gss %>%
  group_by(degree) %>%
  summarise(
    n = n(),
    mean_age = round(mean(age, na.rm = TRUE), 1),
    .groups = "drop"
  )

gt_example <- create_content() %>%
  add_text("### Summary Statistics by Education") %>%
  add_gt(
    gt::gt(summary_data) %>%
      gt::cols_label(degree = "Education", n = "Count", mean_age = "Mean Age")
  )

print(gt_example)
```

```{r, warning = FALSE}
gt_example %>% preview()
```

You can also pass a data frame directly - it will be converted to a gt table automatically:

```{r, warning = FALSE}
simple_gt <- create_content() %>%
  add_gt(head(summary_data, 3), caption = "Top 3 Education Levels")

simple_gt %>% preview()
```

### Reactable Tables

For interactive tables with sorting and filtering, use `add_reactable()`:

```{r}
reactable_example <- create_content() %>%
  add_text("### Interactive Summary Table") %>%
  add_reactable(
    reactable::reactable(summary_data, searchable = TRUE, striped = TRUE)
  )

print(reactable_example)
```

```{r}
reactable_example %>% preview()
```

### DT DataTables

For feature-rich interactive tables, use `add_DT()`:

```{r}
dt_example <- create_content() %>%
  add_text("### DataTable with Search and Pagination") %>%
  add_DT(summary_data, options = list(pageLength = 5))

print(dt_example)
```

```{r}
dt_example %>% preview()
```

### Basic Tables

For simple tables without dependencies, use `add_table()`:

```{r}
table_example <- create_content() %>%
  add_table(head(summary_data, 3), caption = "Sample Data")

print(table_example)
```

```{r}
table_example %>% preview()
```

### Custom Highcharter Charts

When you need a visualization that goes beyond what `add_viz()` offers, use `add_hc()` to embed any [highcharter](https://jkunst.com/highcharter/) chart:

```{r}
library(highcharter)

# Create a custom chart
custom_chart <- highchart() %>%
  hc_chart(type = "pie") %>%
  hc_title(text = "Education Distribution") %>%
  hc_add_series(
    name = "Count",
    data = list(
      list(name = "Less than HS", y = sum(gss$degree == "less than high school")),
      list(name = "High School", y = sum(gss$degree == "high school")),
      list(name = "Junior College", y = sum(gss$degree == "junior college")),
      list(name = "Bachelor", y = sum(gss$degree == "bachelor")),
      list(name = "Graduate", y = sum(gss$degree == "graduate"))
    )
  )

hc_example <- create_content() %>%
  add_text("### Custom Pie Chart") %>%
  add_hc(custom_chart)

print(hc_example)
```

```{r}
hc_example %>% preview()
```

This is useful for:

- Chart types not available in `add_viz()` (pie charts, gauges, etc.)
- Highly customized visualizations
- Integrating existing highcharter code

## 📈 Working with Pre-aggregated Data

While dashboardr was designed with survey data in mind (where you typically count or aggregate responses), it works equally well with data that's already aggregated.

### Bar Charts with Pre-aggregated Data

Use the `y_var` parameter to specify a column containing pre-computed values:

```{r}
# Pre-aggregated data (e.g., from a database or API)
country_stats <- data.frame(
  country = c("USA", "Germany", "France", "UK", "Japan"),
  population_millions = c(331, 83, 67, 67, 126),
  gdp_trillions = c(21.4, 3.8, 2.6, 2.8, 5.1)
)

# Use y_var to plot pre-aggregated values directly
preagg_bar <- create_content(data = country_stats) %>%
  add_viz(
    type = "bar",
    x_var = "country",
    y_var = "population_millions",  # Pre-computed values
    title = "Population by Country (Millions)",
    x_order = c("USA", "Japan", "Germany", "UK", "France")
  )

preagg_bar %>% preview()
```

This also works for grouped bar charts:

```{r}
# Pre-aggregated grouped data
quarterly_sales <- data.frame(
  quarter = rep(c("Q1", "Q2", "Q3", "Q4"), each = 2),
  region = rep(c("North", "South"), 4),
  revenue = c(100, 80, 120, 95, 150, 110, 130, 100)
)

grouped_preagg <- create_content(data = quarterly_sales) %>%
  add_viz(
    type = "bar",
    x_var = "quarter",
    group_var = "region",
    y_var = "revenue",
    title = "Quarterly Revenue by Region"
  )

grouped_preagg %>% preview()
```

### Stacked Bars with Pre-aggregated Data

The `y_var` parameter works the same way for stacked bar charts:

```{r}
# Pre-aggregated stacked data
satisfaction_data <- data.frame(
  department = rep(c("Sales", "Engineering", "HR"), each = 3),
  rating = rep(c("Satisfied", "Neutral", "Dissatisfied"), 3),
  count = c(45, 30, 10, 60, 25, 15, 35, 20, 5)
)

stacked_preagg <- create_content(data = satisfaction_data) %>%
  add_viz(
    type = "stackedbar",
    x_var = "department",
    stack_var = "rating",
    y_var = "count",  # Pre-computed counts
    title = "Employee Satisfaction by Department"
  )

stacked_preagg %>% preview()
```

### Histograms with Pre-aggregated Data

For pre-binned histogram data:

```{r}
# Pre-binned data (e.g., from a reporting system)
age_bins <- data.frame(
  age_group = c("18-25", "26-35", "36-45", "46-55", "56-65", "65+"),
  count = c(150, 280, 320, 290, 210, 180)
)

hist_preagg <- create_content(data = age_bins) %>%
  add_viz(
    type = "histogram",
    x_var = "age_group",
    y_var = "count",  # Pre-computed bin counts
    title = "Age Distribution"
  )

hist_preagg %>% preview()
```

### Timelines with Pre-aggregated Data

Use `agg = "none"` to skip aggregation for timeline data:

```{r}
# Pre-aggregated time series
yearly_metrics <- data.frame(
  year = c(2020, 2021, 2022, 2023),
  value = c(1250, 1380, 1420, 1510)
)

timeline_preagg <- create_content(data = yearly_metrics) %>%
  add_viz(
    type = "timeline",
    time_var = "year",
    y_var = "value",
    agg = "none",  # Use values directly, no aggregation
    title = "Yearly Performance Trend"
  )

timeline_preagg %>% preview()
```

### Heatmaps with Pre-aggregated Data

Use `pre_aggregated = TRUE` to skip the aggregation step:

```{r}
# Pre-computed heatmap values (one row per cell)
correlation_data <- data.frame(
  var1 = rep(c("Age", "Income", "Education"), each = 3),
  var2 = rep(c("Age", "Income", "Education"), 3),
  correlation = c(1.0, 0.35, 0.28, 0.35, 1.0, 0.52, 0.28, 0.52, 1.0)
)

heatmap_preagg <- create_content(data = correlation_data) %>%
  add_viz(
    type = "heatmap",
    x_var = "var1",
    y_var = "var2",
    value_var = "correlation",
    pre_aggregated = TRUE,  # Skip aggregation
    title = "Correlation Matrix",
    color_min = -1,
    color_max = 1
  )

heatmap_preagg %>% preview()
```



### Treemaps with Pre-aggregated Data

Use `pre_aggregated = TRUE` to use values directly without summing:

```{r}
# Pre-aggregated hierarchical data
budget_data <- data.frame(
  category = c("Marketing", "Marketing", "Engineering", "Engineering", "Operations"),
  subcategory = c("Digital", "Events", "Frontend", "Backend", "Support"),
  amount = c(50000, 30000, 80000, 120000, 45000)
)

treemap_preagg <- create_content(data = budget_data) %>%
  add_viz(
    type = "treemap",
    group_var = "category",
    subgroup_var = "subcategory",
    value_var = "amount",
    pre_aggregated = TRUE,  # Use amounts directly
    title = "Budget Allocation"
  )

treemap_preagg %>% preview()
```

## 🔲 Layout Helpers

### Dividers

Visual separators between content sections:
 
```{r}
divider_content <- create_content() %>%
  add_text("## Section One", "", "Content for the first section.") %>%
  add_divider() %>%
  add_text("## Section Two", "", "Content for the second section.")

print(divider_content)
```

```{r}
divider_content %>% preview()
```

### Spacers

Add vertical spacing between elements:

```{r}
spacer_example <- create_content() %>%
  add_text("Content above") %>%
  add_spacer(height = "3rem") %>%
  add_text("Content below (after 3rem spacer)")

print(spacer_example)
```

```{r}
spacer_example %>% preview()
```

## 👁️ Previewing Content

Use `preview()` to quickly check your visualizations and content blocks during development. It automatically validates your visualization specs and catches errors like missing parameters or invalid column names before rendering:

```{r, eval=FALSE}
# Quick preview (fast, no Quarto)
content %>% preview()

# Full Quarto preview (slower, full features)
content %>% preview(quarto = TRUE)

# Save to specific location
content %>% preview(path = "my_preview.html")
```

### Limitations of Preview

> **Important:** `preview()` is a simplified rendering for quick checks during development. It does **not** show the final dashboard appearance.

**What preview shows well:**

- Individual visualizations and their data
- Basic tabgroup organization
- Content blocks (text, callouts, cards, etc.)
- Simple nested tabs

**What preview does NOT support:**

- Full dashboard navigation and layout
- Interactive inputs (dropdowns, sliders, checkboxes)
- Complex multi-page structures
- Dashboard theming and styling
- Sidebar navigation
- Mobile responsiveness

**For a real view of your dashboard**, you must run `generate_dashboard()` and open the resulting Quarto site:

```r
dashboard <- create_dashboard("My Dashboard") %>%

  add_page(my_page)

# Generate the full dashboard
generate_dashboard(dashboard)

# Then open _site/index.html in your browser
```

Think of `preview()` as a "sanity check" for your charts and content, not a representation of the final product.

## ➡️ Next Steps

Once you have content, add it to a page:

```r
create_page("Analysis", data = gss) %>%
  add_content(demographics) %>%
  add_content(attitudes)
```

See `vignette("pages")` for page creation details.
