Styling with theme(), element_text(), and guides()
Recall, that ggplot2 graphics are built in layers. The first layer is the data and the mapping of data to aesthetics. The second layer is the scales and guides that control how the data-mapped aesthetics are displayed. The third layer is the theme, which controls the non-data ink on the canvas.
Layers in ggplot2
Data Mapping (aes) ───► What data is plotted and mapped
Scales / Guides ───► How data-mapped aesthetics translate and display
Themes / Elements ───► The canvas styling (non-data ink)
This document focuses on the third layer, the theme, and how to control it with theme(), element_text(), and guides().
1. theme()
theme() controls all non-data visual elements of the plot: it never touches data points, lines, bars, or coordinates, and instead styles the frame, grid lines, panel backgrounds, margins, and legend positioning.
Rule of thumb: if a visual property is derived directly from data values (e.g., coloring points by vehicle class), it belongs in aes() or scale_*(). If it’s a fixed stylistic choice across the whole plot (e.g., background color, legend position), it belongs in theme().
2. element_text()
Inside theme(), text elements (titles, subtitles, axis labels, tick marks, legend titles) can’t take raw strings or plain numbers — ggplot2 uses constructor functions to define their appearance. element_text() handles typography.
Related constructors:
element_text(): typography and font stylingelement_rect(): borders, fills, and backgroundselement_line(): grid lines and axis tick markselement_blank(): removes the element completely
Key arguments to element_text():
| Argument | Description | Values / Examples |
|---|---|---|
face |
Font style/weight | "bold", "italic", "plain" |
size |
Text size in points | Numeric (e.g., 12, 14) |
color |
Font color | Hex codes ("#2b2b2b") or names ("gray30") |
angle |
Text rotation | Degrees (0 to 360, e.g., 45 for crowded x-axes) |
hjust / vjust |
Horizontal / vertical alignment | 0 (left/bottom), 0.5 (center), 1 (right/top) |
family |
Font family/typeface | "sans", "serif", "mono", or custom fonts |
theme(
plot.title = element_text(face = "bold", size = 16, hjust = 0),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10)
)3. guides()
While theme() manages non-data ink, guides() controls how legends and colorbars behave, wrap, and render.
The distinction:
theme(legend.position = "bottom")controls where the legend container sits on the canvas.guides(color = guide_legend(nrow = 2))controls how the keys inside that legend are laid out.guides(fill = "none")removes a specific legend entirely without dropping the aesthetic mapping.
Common use cases:
- Wrapping long discrete legends into multiple rows/columns with
guide_legend(nrow = 2). - Fixing point visibility in the legend (
override.aes): if points are drawn with low opacity (alpha = 0.3) or small size (size = 1), they can be hard to see in the legend key.override.aesadjusts the legend keys without changing the plot data. - Shaping continuous colorbars with
guide_colorbar(barwidth = unit(12, "lines")).
Reference:
| Function | Scope | Question it answers | Example |
|---|---|---|---|
theme() |
Canvas / frame | Where is this non-data element placed or styled? | Moving the legend box, dropping grid lines |
element_text() |
Typography | How should this specific label look? | Rotating tick labels, bolding titles |
guides() |
Legend keys | How should the legend contents be arranged? | Stacking legend keys into 2 rows, boosting legend alpha |