Skip to contents

Since version 1.0.0, ch_hover() is the main entry point of codehover. Give it ggplot code and it:

  1. splits the code at every top-level +;
  2. evaluates each cumulative step;
  3. renders one image per step;
  4. returns a hoverable table with CSS and JavaScript attached — no template, YAML or jQuery needed.

Basic use

ch_hover({
  ggplot(cars, aes(x = speed, y = dist)) +
    geom_point(color = "red") +
    scale_y_continuous(limits = c(0, 100)) +
    labs(title = "A ggplot for the rest of us",
         subtitle = "Hover each row to see that step") +
    theme_bw()
}, width = 4, height = 3)
ggplot(cars, aes(x = speed, y = dist)) +
geom_point(color = "red") +
scale_y_continuous(limits = c(0, 100)) +
labs(title = "A ggplot for the rest of us", subtitle = "Hover each row to see that step") +
theme_bw()
Plot after step 5 of 5: theme_bw()

Hover the rows above: each one shows the plot as it looks up to that step.

Fixed scales

By default every step shows the true output of its partial code, so axes and legends may move as layers are added. With fixed_scales = TRUE the axes, legends and panel layout are pinned from the final plot, giving a visually stable reveal (mechanism borrowed from the ggreveal package):

ch_hover({
  ggplot(mtcars, aes(x = wt, y = mpg)) +
    geom_point(aes(color = factor(cyl))) +
    geom_smooth(method = "lm")
}, fixed_scales = TRUE, width = 4, height = 3)
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point(aes(color = factor(cyl))) +
geom_smooth(method = "lm")
Plot after step 3 of 3: geom_smooth(method = "lm")

Referencing a chunk by label

To keep your source document clean, write the plot in a normal chunk with eval=FALSE and call ch_hover_chunk() with the chunk label:

ggplot(mtcars, aes(x = factor(cyl))) +
  geom_bar(fill = "steelblue") +
  coord_flip()

ggplot(mtcars, aes(x = factor(cyl))) +
geom_bar(fill = "steelblue") +
coord_flip()
Plot after step 3 of 3: coord_flip()

Layout

By default (layout = "auto") the image sits beside the code table when there is enough horizontal room and wraps below it otherwise — so wide code tables (long lines, deep indentation) push the image down, while narrow ones keep it at their side. To take control:

  • layout = "row" forces side by side; the image shrinks to fit.
  • layout = "column" forces the image below the code.
ch_hover({
  ggplot(cars, aes(speed, dist)) +
    geom_point() +
    theme_minimal()
}, layout = "column", width = 4, height = 3)
ggplot(cars, aes(speed, dist)) +
geom_point() +
theme_minimal()
Plot after step 3 of 3: theme_minimal()

Beyond the mouse

Rows are not hover-only. They also respond to:

  • tap, so the table works on phones and tablets;
  • the keyboard — Tab moves into the table, Arrow Up/Down walks the steps and Enter/Space activates the focused one.

Each step image carries alternative text. By default it is "Plot after step i of n: <code of that step>"; pass your own with alt = (one string per step, or a single string for all of them):

ch_hover({
  ggplot(cars, aes(speed, dist)) +
    geom_point() +
    theme_minimal()
},
alt = c("empty panel with speed and distance axes",
        "the same panel with one point per car",
        "final plot, minimal theme"),
caption = "Stopping distance versus speed",
width = 4, height = 3)
ggplot(cars, aes(speed, dist)) +
geom_point() +
theme_minimal()
final plot, minimal theme
Stopping distance versus speed

Theming

Everything is scoped under the .codehover class and driven by CSS custom properties, so a chunk of CSS in your document is enough to restyle the table:

.codehover {
  --codehover-highlight: #cde7ff;
  --codehover-font: monospace;
  --codehover-font-size: 0.9em;
  --codehover-tab: 2em;
}

A dark-scheme variant is applied automatically via prefers-color-scheme.

Other options

  • type = "one_row" highlights only the hovered row instead of the incremental effect.
  • path = "assets/" keeps the step PNGs on disk and references them by relative path, instead of embedding base64 (smaller HTML, but the folder must travel with the page). The images are preloaded, so hovering does not flicker.
  • initial = "first" shows the first step’s image before any interaction; the default "last" shows the finished plot. A step number also works.
  • width, height (inches) and dpi control the rendered images. They also reserve the image box, so the page does not reflow while stepping through the table.
  • A step that cannot be rendered on its own (for example after_stat() in the global aes() before any geom exists) produces a blank image and a message naming the step, instead of aborting the table.

When to use the manual API

ch_hover() handles ggplot + chains. For anything else — data-wrangling pipelines, maps assembled from several objects, arbitrary images — use the low-level ch_int() %>% ch_row() %>% ch_out() API described in the introduction vignette.