Skip to contents

vellumwidget turns a vellum scene, or more usually a vellumplot plot, into a self-contained, client-side interactive HTML widget. There is one verb, as_widget(), and it works the same in the RStudio/Positron viewer, a knitr/Quarto document, and a Shiny app. No Shiny server and no round-trip are required; every interaction runs in the browser.

Interactivity is declared in the grammar: a mark’s data_id, tooltip, and hover_group arguments flow through the scene as per-element metadata that the widget reads. A plot that declares none still renders as a static (but embeddable) SVG.

library(vellumplot)
df <- data.frame(
  wt = mtcars$wt, mpg = mtcars$mpg,
  model = rownames(mtcars), cyl = factor(mtcars$cyl)
)

Hover: tooltips and highlighting

Map tooltip and data_id and hover a point: it shows a tooltip and dims the others. data_id is the join key; tooltip is what the box says.

vplot(df) |>
  mark_point(x = wt, y = mpg, color = cyl, tooltip = model, data_id = model) |>
  as_widget()

HTML tooltips

Tooltip text is rendered as safe HTML: build a multi-line, formatted string in tooltip = (here with glue) using <b> and <br>. Data values are escaped, and only inert tags (<b>, <i>, <br>, <span>) are honoured, so there is no injection risk. Style the box with tooltip_style.

df$label <- glue::glue("<b>{df$model}</b><br>{df$mpg} mpg · {df$wt}k lbs")
vplot(df) |>
  mark_point(x = wt, y = mpg, tooltip = label, data_id = model) |>
  as_widget(tooltip_style = list(background = "#1d3557", fontsize = "13px"))

Select and brush

Click a mark to select it (every mark sharing its data_id toggles together); drag a rectangle to brush-select. select_mode = "single" makes a click replace the selection instead of toggling.

vplot(df) |>
  mark_point(x = wt, y = mpg, data_id = model, selected_color = "#e63946") |>
  as_widget()

Pan, zoom, and the toolbar

Zoom with the mouse wheel, drag to pan (toggle brush/pan on the toolbar). On a touch device, drag pans and a two-finger pinch zooms; with the widget focused, the arrow keys pan, +/- zoom, and 0 resets. The on-hover toolbar adds zoom-to-selection, reset, SVG/PNG download, copy-to-clipboard (where supported), and fullscreen.

Export captures the current view, so a zoomed-in region exports as shown. Set the download name and a hi-res PNG scale:

vplot(df) |>
  mark_point(x = wt, y = mpg, data_id = model) |>
  as_widget(export_filename = "mtcars", export_scale = 2)

Accessibility

Accessibility is on by default (a11y = TRUE). The widget announces itself as an interactive chart, and every mark is focusable: Tab into the chart, use the arrow keys to move between marks (each announced through a polite live region), Enter/Space to select, and Escape to leave traversal mode. A visually-hidden data table lists every mark for screen-reader users. The chart’s accessible name and description come from the plot’s title and alt text (which vellumplot sets automatically) or an explicit as_widget(alt =).

See the vellumplot Accessibility article for the full cross-package story (alt text, accessible SVG/PDF, and this widget).

Linked views

Widgets sharing a group link client-side: selecting or brushing in one highlights the same data keys in the others, with no Shiny and no crosstalk. Selection projects by hover_group when the marks declare it.

p <- vplot(df) |> mark_point(x = wt, y = mpg, data_id = model, hover_group = cyl)
htmltools::tagList(
  as_widget(p, group = "cars", width = 320, height = 240),
  as_widget(vplot(df) |> mark_point(x = wt, y = mpg, data_id = model, hover_group = cyl),
            group = "cars", width = 320, height = 240)
)

For interop with plotly / leaflet / DT and crosstalk’s filter_* controls, pass a crosstalk::SharedData (or a group name) to crosstalk = instead.

Styling

hover_color, selected_color, and dim_opacity set the widget-wide theme; per-mark hover_color / selected_color declared in vellumplot override them for that mark. tooltip_style themes the tooltip box (above).

vplot(df) |>
  mark_point(x = wt, y = mpg, data_id = model) |>
  as_widget(hover_color = "seagreen", selected_color = "firebrick", dim_opacity = 0.15)

Shiny

vellumwidgetOutput() / renderVellumwidget() embed a widget in a Shiny app, and the widget reports the user’s selection, clicks, hovers, and brush back to the server as reactive inputs. See the Shiny article and ?vellumwidget-shiny.