---
title: "Getting Started with MAIDR"
author: "JooYoung Seo"
description: "Install the maidr R package and make a first ggplot2 or Base R plot accessible with keyboard navigation, screen reader text, braille and sonification using show(), save_html() and maidr_on()."
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting Started with MAIDR}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

## Introduction to MAIDR

**MAIDR** (Multimodal Access and Interactive Data Representation) is an R package that makes data visualizations accessible to users with visual impairments. It converts ggplot2 and Base R plots into interactive, accessible formats with:

- **Keyboard navigation** - Explore data using arrow keys
- **Screen reader support** - Full ARIA labels and descriptions
- **Sonification** - Hear data patterns through sound
- **HTML/SVG output** - Accessible charts that open in any browser

MAIDR helps data scientists and researchers create inclusive visualizations that everyone can explore, regardless of visual ability.

## Installation

Install the development version from GitHub:

```{r install, eval=FALSE}
# Install the released version from CRAN
install.packages("maidr")

# Or the development version from GitHub:
# install.packages("devtools")
devtools::install_github("xability/r-maidr")
```

## Basic Workflow

MAIDR works with two main functions:

1. **`show()`** - Display an interactive plot in RStudio Viewer or browser
2. **`save_html()`** - Save a plot as an HTML file, with the MAIDR.js library in a `lib/` folder beside it

### Quick Example: ggplot2 Bar Chart

```{r ggplot2-example}
library(maidr)
library(ggplot2)

# Create sample data
sales_data <- data.frame(
  Product = c("A", "B", "C", "D"),
  Sales = c(150, 230, 180, 290)
)

# Create a bar chart
p <- ggplot(sales_data, aes(x = Product, y = Sales)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  labs(
    title = "Product Sales by Category",
    x = "Product",
    y = "Sales Amount"
  ) +
  theme_minimal()

# Display interactively
show(p)

# Or save as HTML file
save_html(p, "sales_chart.html")
```

### Quick Example: Base R Plot

MAIDR also works with Base R plotting functions:

```{r base-r-example}
library(maidr)

# Create a simple barplot
categories <- c("A", "B", "C", "D")
values <- c(150, 230, 180, 290)

barplot(
  values,
  names.arg = categories,
  col = "steelblue",
  main = "Product Sales by Category",
  xlab = "Product",
  ylab = "Sales Amount"
)

# Note: For Base R plots, call show() with NO arguments
# after creating the plot
show()
```

## How maidr hooks into your session

- **Console.** `library(maidr)` is all it takes. Printing a ggplot2 object,
  by typing `p` or `print(p)`, opens it in the maidr viewer; `show(p)` is the
  explicit form. Base R plotting calls are recorded, and `show()` with no
  argument opens the recorded chart. `save_html()` writes either kind to a
  file.
- **R Markdown and Quarto.** Call `maidr_on()` once in a setup chunk. It
  installs the knitr hooks that turn every plot the document draws into an
  accessible chart; `library(maidr)` alone does not install them.
- **Shiny.** Put `maidr_output()` in the UI and `render_maidr()` in the
  server; see `vignette("shiny-integration", package = "maidr")`.
- **Turning it off.** `maidr_off()` stops interception for the session and
  `maidr_on()` starts it again. `options(maidr.ggplot2 = FALSE)` leaves
  ggplot2 printing alone, `options(maidr.base_r = FALSE)` stops recording
  Base R calls, and `options(maidr.auto_show = FALSE)`, in `.Rprofile` to
  make it permanent, turns everything off. See `?"maidr-options"`.
- **What gets masked.** Attaching maidr puts its own copies of the Base R
  plotting functions, and of `methods::show()`, ahead of the originals; R
  lists them at `library(maidr)`. Each records the call and passes through
  to the original, and `show()` hands anything that is not a plot back to
  `methods::show()`. In a script or a package call `maidr::show()` by name,
  and attach vioplot, wordcloud or quantmod *before* maidr, or their own
  functions mask the wrappers and their charts go unrecorded. See
  [`?"base-r-wrappers"`](https://r.maidr.ai/reference/base-r-wrappers.html).

## Interactive htmlwidgets: plotly, highcharter and echarts4r

`maidr_htmlwidget()` takes an interactive chart drawn by plotly, highcharter
or echarts4r and returns it with MAIDR attached. MAIDR reads the chart in the
browser, through the adapter for the library that draws it, so the widget
keeps working everywhere an htmlwidget does: the viewer,
`htmlwidgets::saveWidget()`, this document, and Shiny, where a re-rendered
chart is read again.

```{r htmlwidget-examples}
library(maidr)

# plotly, including ggplotly()
plotly::plot_ly(mtcars, x = ~wt, y = ~mpg, type = "scatter", mode = "markers") |>
  maidr_htmlwidget()

# highcharter
highcharter::hchart(mtcars, "scatter", highcharter::hcaes(wt, mpg)) |>
  maidr_htmlwidget()

# echarts4r
mtcars |>
  echarts4r::e_charts(wt) |>
  echarts4r::e_scatter(mpg) |>
  maidr_htmlwidget()

# In Shiny, wrap the widget inside its own render function
# output$chart <- plotly::renderPlotly(maidr_htmlwidget(plotly::plot_ly(...)))
```

An echarts4r chart is switched to ECharts' SVG renderer, which MAIDR needs to
highlight the mark being read. While an echarts4r chart shows its legend, the
visual highlight is off; audio, text and braille are not affected.

## Offline vs CDN Usage

By default, `show()` and `save_html()` use the bundled MAIDR.js library, so the result works offline. `save_html()` writes the library to a `lib/` folder beside the file, and the two have to be shared together (zip the folder that holds both): an `.html` sent on its own loads no MAIDR.js and shows a plain, inaccessible chart. Widgets, knitr documents and Shiny apps auto-detect internet availability and use the CDN when online. You can control this behavior with the `use_cdn` parameter:

```{r use-cdn-example}
library(maidr)
library(ggplot2)

p <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_bar(stat = "identity")

# Default - bundled files, works offline
show(p)

# Force CDN (requires internet when viewing; loads the latest MAIDR.js)
show(p, use_cdn = TRUE)

# Force bundled/local files (works offline)
show(p, use_cdn = FALSE)
```

The same parameter works with `save_html()`:

```{r save-html-cdn}
# One self-contained file; needs internet whenever it is viewed
save_html(p, "plot_cdn.html", use_cdn = TRUE)

# The file plus a lib/ folder beside it; works offline
save_html(p, "plot_offline.html", use_cdn = FALSE)
```

The CDN paths load the **latest published MAIDR.js**, not the copy bundled
with the package, the same as the Python binding. The first CDN document in an
R session asks jsDelivr (falling back to the npm registry) which version that
is, allowing 3 seconds, and every document in the session names that exact
version. When the lookup cannot be made -- offline, or blocked -- nothing
fails: the document names the bundled version, the copy `use_cdn = FALSE`
would serve.
Documents rendered with `use_cdn = FALSE` make no network request at all.

To load a fixed version from the CDN instead, pin it with an option or the
`MAIDR_CDN_VERSION` environment variable (the option wins). A pinned document
makes no lookup:

```{r cdn-version}
# The version bundled with this package
options(maidr.cdn_version = "bundled")

# A particular release
options(maidr.cdn_version = "4.9.0")

# Back to the latest
options(maidr.cdn_version = NULL)
```

`maidr.cdn_timeout` (or `MAIDR_CDN_TIMEOUT`) changes the time allowed for the
lookup. See `?"maidr-options"` for the details.

**When to use `use_cdn = FALSE`:**
- Viewing offline, or sharing with readers who may not have internet access:
  send the file together with its `lib/` folder (zip the two), never the
  `.html` alone
- Ensuring reproducibility with a specific MAIDR.js version (or pin one on
  the CDN with `maidr.cdn_version`)

**When to use `use_cdn = TRUE`:**
- Attaching or uploading a single file, for readers who will be online
- Loading the newest MAIDR.js without updating the R package

### The DotPad SDK

One thing an offline document still fetches: the SDK for the
[DotPad tactile display](https://maidr.ai/docs/TACTILE_DISPLAY.html). maidr.js
does not bundle it (its licence does not permit redistribution) and imports the
vendor's copy from jsDelivr the first time a reader connects a DotPad. The
document renders, sonifies and brailles without the network; only that first
connect needs it.

To keep the DotPad offline as well, serve the SDK yourself and tell maidr where
it is before rendering. Options and environment variables of the same names
both work; an option wins when both are set:

```{r dotpad-sdk, eval = FALSE}
options(
  maidr.dotpad_sdk_url = "https://intranet.example/dotpad/DotPadSDK-3.0.3.js",
  # Only if the braille engine (liblouis) is not in lib/ beside the module
  maidr.dotpad_asset_base_url = "https://intranet.example/dotpad/lib/"
)

save_html(p, "plot_offline.html", use_cdn = FALSE)
```

Every document maidr produces (`show()`, `save_html()`, widgets, knitr and
Shiny) then declares `window.MAIDR_DOTPAD_SDK_URL` and
`window.MAIDR_DOTPAD_ASSET_BASE_URL` ahead of maidr.js, and the CDN is never
asked for the SDK. See `?"maidr-options"` for the details.

## Exploring Accessible Plots

When you open a MAIDR plot, you can explore it using:

### Keyboard Navigation

<!-- maidr-keys:start -->
| Key | Action |
|---|---|
| **Tab** | Focus the chart; **Shift + Tab** leaves it |
| **Left / Right** | Move between data points |
| **Up / Down** | Move between series, stacked segments, heat map rows or box plot sections, on a chart that has them |
| **Page Up / Page Down** | Switch between the layers of a chart that has several |
| **B** | Toggle braille mode |
| **T** | Toggle text mode |
| **S** | Toggle sonification |
| **R** | Toggle review mode |
| **C** | Toggle high contrast mode |
| **L**, then **X**, **Y** or **T** | Announce the x axis label, the y axis label or the title |
| **Space** | Repeat the current sound |
| **Ctrl + /** (**Cmd + /** on macOS) | Show or hide the full keyboard shortcut help |

Every other shortcut, including autoplay, jumping to the ends, the command palette, settings and the AI chat, is on the [MAIDR controls reference](https://maidr.ai/docs/CONTROLS.html).
<!-- maidr-keys:end -->

### Screen Reader Announcements

MAIDR plots include:

- Plot titles and descriptions
- Axis labels and ranges
- Data point values
- Navigation instructions

### Data Sonification

Plots can be heard through:

- Pitch mapping (higher values = higher pitch)
- Volume changes
- Different tones for different series

## Quarto reveal.js Slides

A `revealjs` deck needs nothing special from this package: call
`maidr_on()` once in a setup chunk, as in any other Quarto or R Markdown
document, and every plot the deck draws becomes an accessible MAIDR chart.

A chart on a `revealjs` slide is keyboard reachable on its own: **Tab** moves
into it, the arrow keys explore it, and **Shift+Tab** hands focus back to the
slide, so **Space** advances the deck again. None of that needs configuring.

What does need attention is a reveal.js behavior that has nothing to do with
MAIDR. reveal.js keeps the slides on either side of the current one rendered so
that transitions stay smooth, and marking them `hidden` does not take them out
of the tab order — reveal's own inline style overrides the attribute. On a
deck with a chart on every slide, a single **Tab** can therefore land on an
off-screen slide's chart rather than the one in front of the reader. This is
[hakimel/reveal.js#1587](https://github.com/hakimel/reveal.js/issues/1587),
open since 2016.

The fix is now on reveal.js `master`, which marks every slide but the current
one `inert`. It has not reached a published release yet, and Quarto carries its
own copy of reveal.js — Quarto 1.10 ships 5.1.0 — so it will arrive in a
Quarto release some time after reveal.js cuts one. Nothing will need to change
in your deck when it does.

Until then,
[quarto-revealjs-a11y](https://github.com/mcanouil/quarto-revealjs-a11y) does
the same thing for a Quarto deck. Add it once per project:

``` bash
quarto add mcanouil/quarto-revealjs-a11y
```

and enable it in the deck's front matter:

``` yaml
format:
  revealjs:
    revealjs-plugins:
      - a11y
```

Use **0.2.3 or newer**. Earlier versions took off-slide elements out of the tab
order by setting `tabindex="-1"` on them and could not find them again to put
them back, which left the chart on the *current* slide unreachable as well.

With the extension enabled, each slide gives one **Tab** to its own chart and
**Shift+Tab** back out. The extension also adds a skip link ahead of the
slides, so Shift+Tab lands there rather than on the slide element itself;
either way **Space** still moves to the next slide.

## Supported Plot Types

MAIDR supports a comprehensive range of visualizations:

### Basic Plot Types
- Bar charts (simple, grouped/dodged, stacked)
- Pie charts — ggplot2 via `geom_col()`/`geom_bar()` + `coord_polar("y")`;
  Base R via `pie()`
- Histograms
- Scatter plots
- Line plots (single and multi-line)
- Step plots — `geom_step()` in ggplot2, `plot(type = "s")` / `plot(type = "S")`
  in Base R — for values that are piecewise constant, such as a sleep-stage
  hypnogram
- Box plots
- Violin plots (ggplot2 `geom_violin()`)
- Candlestick (OHLC) charts — ggplot2 via {tidyquant} (with optional
  `geom_ma()` moving-average overlays and a patchwork volume sub-panel);
  Base R via `quantmod::chartSeries()`, with its `addVo()` volume panel read
  as a bar layer (other TA overlays such as `addSMA()` and `addEMA()` are
  not supported and fall back to native graphics)
- Heatmaps
- Contour plots (Base R `contour()`)
- Density/smooth curves

See the [Heat Map and Candlestick Examples](https://r.maidr.ai/articles/examples-heatmap-candlestick.html)
article for the full candlestick + MA + volume pipeline and the Base R support matrix.

### Advanced Plot Types
- **Faceted plots** - `facet_wrap()` and `facet_grid()` in ggplot2
- **Multi-panel layouts** - patchwork for ggplot2, `par(mfrow/mfcol)` for Base R
- **Multi-layered plots** - Combine multiple geoms (e.g., histogram + density)

### Experimental Plot Types

Every type above is stable. maidr also reads a longer list of charts as
prototypes: none has been through a user study, and each may change without
a deprecation period. Here and in the rest of the docs an experimental type
is marked **[experimental]** after its name; a type with no mark is stable.
Among them:

- ggplot2: area and stacked area charts [experimental], contour plots via
  `geom_contour()` [experimental], error bars [experimental], Gantt charts
  via `maidr_gantt()` [experimental], hexbin plots [experimental] and ROC
  curves via `maidr_roc()` [experimental]
- Base R: correlograms [experimental], Q-Q plots [experimental], radar charts
  via `stars()` [experimental], mosaic plots [experimental], violin plots via
  `vioplot::vioplot()` [experimental] and word clouds [experimental]

The full list is under "Experimental Plot Types" in the
[README](https://r.maidr.ai/#experimental-plot-types).

## Next Steps

- **[Shiny Integration](shiny-integration.html)** - Use MAIDR in Shiny apps
- **Package documentation** - Run `?maidr::show` for function details
- **Run examples** - Try `maidr::run_example()` to see all available plot types

## Example Gallery

### Histogram

```{r histogram-example}
library(maidr)
library(ggplot2)

# Normal distribution
hist_data <- data.frame(values = rnorm(1000, mean = 100, sd = 15))

p <- ggplot(hist_data, aes(x = values)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black") +
  labs(
    title = "Distribution of Test Scores",
    x = "Score",
    y = "Frequency"
  ) +
  theme_minimal()

show(p)
```

### Scatter Plot

```{r scatter-example}
library(maidr)
library(ggplot2)

# Create sample data
scatter_data <- data.frame(
  height = rnorm(50, 170, 10),
  weight = rnorm(50, 70, 8),
  gender = sample(c("Male", "Female"), 50, replace = TRUE)
)

p <- ggplot(scatter_data, aes(x = height, y = weight, color = gender)) +
  geom_point(size = 3, alpha = 0.7) +
  labs(
    title = "Height vs Weight",
    x = "Height (cm)",
    y = "Weight (kg)"
  ) +
  theme_minimal()

show(p)
```

### Line Plot

```{r line-example}
library(maidr)
library(ggplot2)

# Time series data
months <- month.abb[1:12]
temperature <- c(5, 7, 12, 18, 22, 26, 28, 27, 23, 17, 11, 6)

temp_data <- data.frame(
  Month = factor(months, levels = months),
  Temperature = temperature
)

p <- ggplot(temp_data, aes(x = Month, y = Temperature, group = 1)) +
  geom_line(color = "red", linewidth = 1.5) +
  geom_point(color = "darkred", size = 3) +
  labs(
    title = "Average Monthly Temperature",
    x = "Month",
    y = "Temperature (°C)"
  ) +
  theme_minimal()

show(p)
```

## Tips for Creating Accessible Plots

1. **Use clear titles** - Describe what the plot shows
2. **Label axes properly** - Include units of measurement
3. **Choose distinct colors** - Ensure good contrast
4. **Add legends** - Explain what colors/shapes mean
5. **Keep it simple** - Avoid overcrowded visualizations

## Getting Help

- Run `?maidr::show` for function documentation
- Report a bug or ask for a feature at [xability/r-maidr/issues](https://github.com/xability/r-maidr/issues)
- Read the full documentation: `help(package = "maidr")`

## Learn More

- **Accessibility standards**: [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1)
- **MAIDR website**: More examples and tutorials
- **Research papers**: Understanding multimodal data representation
