Automatic code hover with ch_hover()
Source:vignettes/codehover_automatic.Rmd
codehover_automatic.RmdSince version 1.0.0, ch_hover() is the main entry point
of codehover. Give it ggplot code and it:
- splits the code at every top-level
+; - evaluates each cumulative step;
- renders one image per step;
- 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() |
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") |
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() |
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() |
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() |
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) anddpicontrol 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 globalaes()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.