Advanced Customization

Introduction

This vignette dives deeper into BFHtheme for users who need more than the out-of-the-box defaults. You will learn how to:

  • fine-tune theme parameters and override individual elements,
  • craft bespoke colour palettes and gradients,
  • add BFH-branded annotations such as footers, logos, and colour bars,
  • organise multi-panel figures, and
  • optimise fonts and performance for production workflows.
Customisation area Key functions
Theme tweaks theme_bfh_*(), theme()
Colours & palettes bfh_cols(), bfh_pal(), scale_*_bfh_*()
Branding elements add_bfh_logo(), add_bfh_footer()
Layout helpers bfh_combine_plots(), bfh_save()
library(BFHtheme)
library(ggplot2)

Theme Customization

Modifying Base Theme Parameters

All BFH themes expose the standard ggplot2 base arguments. Adjust them to fit specific deliverables—larger fonts for accessibility, a fixed typeface for compliance, or heavier lines for print.

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  labs(title = "Custom Base Size") +
  theme_bfh(
    base_size = 14,           # Larger text
    base_family = "Arial",    # Specific font
    base_line_size = 0.6,     # Thicker lines
    base_rect_size = 0.6      # Thicker rectangles
  )

Overriding Theme Elements

Layer additional ggplot2 theme calls on top of the BFH defaults to fine-tune individual components such as titles, panels, or grids.

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  labs(title = "Custom Theme Elements") +
  theme_bfh() +
  theme(
    plot.title = element_text(color = bfh_cols("hospital_blue"), size = 18),
    axis.title.x = element_text(face = "bold"),
    panel.grid.major = element_line(color = "grey90", linewidth = 0.3)
  )

Legend Customisation

Legends inherit BFH styling but can be repositioned or reformatted to suit the layout.

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  labs(
    title = "Custom Legend Position",
    color = "Cylinders"
  ) +
  scale_color_bfh() +
  theme_bfh() +
  theme(
    legend.position = "top",
    legend.direction = "horizontal",
    legend.box = "horizontal"
  )

Colour Palette Customisation

Using Different Palettes

Switch palettes dynamically to emphasise different aspects of the brand.

p <- ggplot(mtcars, aes(x = factor(cyl), fill = factor(cyl))) +
  geom_bar() +
  labs(title = "Hospital Blues Palette") +
  theme_bfh()

p + scale_fill_bfh(palette = "hospital_blues")

Reversing Palettes

Reverse the palette order when a gradient needs to flow in the opposite direction.

ggplot(mtcars, aes(x = wt, y = mpg, color = hp)) +
  geom_point(size = 3) +
  labs(title = "Reversed Colour Scale") +
  scale_color_bfh_continuous(palette = "blues", reverse = TRUE) +
  theme_bfh()

Creating Custom Palettes

Combine individual colours or build a bespoke gradient with bfh_pal() to match department-specific requirements.

# Create a custom palette with specific colours
custom_colors <- c(
  bfh_cols("hospital_primary"),
  bfh_cols("regionh_grey"),
  bfh_cols("regionh_primary")
)

ggplot(mtcars, aes(x = factor(cyl), fill = factor(cyl))) +
  geom_bar() +
  labs(title = "Custom Colour Selection") +
  scale_fill_manual(values = custom_colors) +
  theme_bfh()
#> Warning: No shared levels found between `names(values)` of the manual scale and the
#> data's fill values.
#> No shared levels found between `names(values)` of the manual scale and the
#> data's fill values.

Working with Colour Functions

Use bfh_pal() to generate smooth gradients or truncate palettes for a precise number of categories.

# Generate colour ramp
blues_pal <- bfh_pal("hospital_blues")

# Use in continuous scale
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
  geom_tile() +
  scale_fill_gradientn(colours = blues_pal(100)) +
  theme_bfh()

Plot Enhancements

Extend a finished plot with branded components and annotations to convey context, ownership, or emphasis without redesigning the underlying chart.

Adding BFH Branding

Anchor a plot with official branding using add_bfh_footer():

p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  theme_bfh()

add_bfh_footer(
  p,
  text = "Bispebjerg og Frederiksberg Hospital - 2024",
  color = bfh_cols("hospital_primary")
)

Multi-Panel Figures

Combine multiple plots with a shared legend for consistent storytelling:

library(patchwork)

p1 <- ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point() +
  labs(title = "Weight vs MPG") +
  scale_color_bfh() +
  theme_bfh()

p2 <- ggplot(mtcars, aes(hp, mpg, color = factor(cyl))) +
  geom_point() +
  labs(title = "Horsepower vs MPG") +
  scale_color_bfh() +
  theme_bfh()

# Combine with shared legend
p1 + p2 + plot_layout(guides = "collect") &
  theme(legend.position = "bottom")

Alternatively, call the BFH helper directly:

plots <- list(p1, p2)
bfh_combine_plots(plots, ncol = 2, legend_position = "bottom")

Axis Customisation

Formatting Axes

Tidy up axes with breaks, limits, and units that match your narrative:

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  labs(
    title = "Custom Axis Formatting",
    x = "Vehicle Weight (1000 lbs)",
    y = "Fuel Efficiency (MPG)"
  ) +
  scale_y_continuous(
    breaks = seq(10, 35, 5),
    limits = c(10, 35)
  ) +
  theme_bfh()

Date Axes

For time-series data, combine date scales with BFH colours:

library(lubridate)

date_data <- data.frame(
  date = seq(as.Date("2024-01-01"), by = "month", length.out = 12),
  value = cumsum(rnorm(12, 10, 3))
)

ggplot(date_data, aes(x = date, y = value)) +
  geom_line(color = bfh_cols("hospital_primary"), linewidth = 1) +
  labs(
    title = "Monthly Trends",
    x = "Month",
    y = "Value"
  ) +
  scale_x_date(
    date_breaks = "2 months",
    date_labels = "%b %Y"
  ) +
  theme_bfh()

Text and Labels

Typography and copy tone are central to the BFH identity. These helpers keep labels consistent while still giving you room for context-specific annotations.

Uppercase Labels

Use bfh_labs() to align subtitles, axis titles, and captions with the uppercase convention while preserving the main title casing:

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  bfh_labs(
    title = "Fuel efficiency analysis",      # Title unchanged
    subtitle = "Motor trend data",            # Converted to uppercase
    x = "weight (1000 lbs)",                  # Converted to uppercase
    y = "miles per gallon",                   # Converted to uppercase
    caption = "Source: Motor Trend, 1974"     # Converted to uppercase
  ) +
  theme_bfh()

Uppercase Axis Tick Labels

For complete typographic consistency, use the position scale functions to convert axis tick labels (the actual values on the axes) to uppercase. This is particularly useful for date labels and categorical data:

# Discrete/categorical data with uppercase labels
ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_boxplot() +
  scale_x_discrete_bfh() +  # "4" becomes "4", but categories like "low" become "LOW"
  bfh_labs(
    title = "Fuel efficiency by cylinder count",
    x = "cylinders",
    y = "miles per gallon"
  ) +
  theme_bfh()


# Date data with uppercase month labels
df <- data.frame(
  date = seq.Date(as.Date("2023-01-01"), as.Date("2023-12-31"), by = "month"),
  value = rnorm(12, mean = 100, sd = 10)
)

ggplot(df, aes(x = date, y = value)) +
  geom_line(color = bfh_cols("hospital_primary"), linewidth = 1) +
  scale_x_date_bfh(date_labels = "%b %Y") +  # "Jan 2023" becomes "JAN 2023"
  scale_y_continuous_bfh() +
  bfh_labs(
    title = "Monthly trends",
    x = "month",
    y = "value"
  ) +
  theme_bfh()

The available position scale functions with uppercase support are:

  • scale_x_continuous_bfh() / scale_y_continuous_bfh() - For numeric axes
  • scale_x_discrete_bfh() / scale_y_discrete_bfh() - For categorical data
  • scale_x_date_bfh() / scale_y_date_bfh() - For date axes
  • scale_x_datetime_bfh() / scale_y_datetime_bfh() - For datetime axes

All functions accept a custom labels argument if you need different formatting.

Advanced: Integration with scales Package

The BFH position scales integrate seamlessly with the scales package for even smarter formatting. You can use breaks_pretty() for intelligent break positioning and any label_*() function—output is automatically uppercased:

library(scales)

# Use label_date_short() for compact, hierarchical labels
ggplot(df, aes(x = date, y = value)) +
  geom_line(color = bfh_cols("hospital_primary"), linewidth = 1) +
  scale_x_date_bfh(
    breaks = breaks_pretty(n = 8),           # Smart break positioning
    labels = label_date_short()              # Compact labels (auto-uppercased)
  ) +
  bfh_labs(
    title = "Compact date labels with breaks_pretty",
    x = "date",
    y = "value"
  ) +
  theme_bfh()


# Combine custom formatting with breaks
ggplot(df, aes(x = date, y = value)) +
  geom_line(color = bfh_cols("hospital_blue"), linewidth = 1) +
  scale_x_date_bfh(
    breaks = breaks_width("2 months"),       # Break every 2 months
    labels = label_date("%B")                # Full month names (uppercased)
  ) +
  bfh_labs(
    title = "Full month names with custom breaks",
    x = "month",
    y = "value"
  ) +
  theme_bfh()

Useful scales functions:

  • breaks_pretty(n) - Pretty breaks with desired count
  • breaks_width("2 weeks") - Breaks at regular intervals
  • label_date("%b %Y") - Custom date format
  • label_date_short() - Compact hierarchical labels
  • label_time() - Time formatting

Best Practice: label_date_short() for Space Efficiency

The label_date_short() function is particularly powerful for time series with many data points. It creates a hierarchical labeling system that only shows what has changed, dramatically reducing horizontal space requirements:

# Standard labels - repetitive and space-consuming
ggplot(df, aes(x = date, y = value)) +
  geom_line(color = bfh_cols("hospital_primary"), linewidth = 1) +
  scale_x_date_bfh(
    date_breaks = "2 months",
    labels = label_date("%b %Y")  # "JAN. 2023", "MAR. 2023", etc.
  ) +
  bfh_labs(title = "Standard labels", x = "date", y = "value") +
  theme_bfh() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))  # Rotation needed!


# label_date_short() - compact and hierarchical
ggplot(df, aes(x = date, y = value)) +
  geom_line(color = bfh_cols("hospital_primary"), linewidth = 1) +
  scale_x_date_bfh(
    date_breaks = "2 months",
    labels = label_date_short()  # Shows year only when it changes
  ) +
  bfh_labs(title = "Compact hierarchical labels", x = "date", y = "value") +
  theme_bfh()  # No rotation needed!

Recommended combinations based on data duration:

# Short time series (< 1 year)
scale_x_date_bfh()  # Default is fine

# Medium time series (1-3 years)
scale_x_date_bfh(
  breaks = breaks_pretty(n = 6),
  labels = label_date_short()
)

# Long time series (> 3 years)
scale_x_date_bfh(
  breaks = breaks_pretty(n = 8),
  labels = label_date_short()
)

# Weekly data
scale_x_date_bfh(
  breaks = breaks_width("2 weeks"),
  labels = label_date_short()
)

Custom Text Elements

Layer additional annotations for storytelling while staying within the BFH colour system:

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  annotate(
    "text",
    x = 4.5, y = 30,
    label = "High efficiency zone",
    color = bfh_cols("hospital_primary"),
    size = 4
  ) +
  annotate(
    "rect",
    xmin = 1.5, xmax = 3.5,
    ymin = 25, ymax = 35,
    alpha = 0.1,
    fill = bfh_cols("hospital_blue")
  ) +
  labs(title = "Annotated Plot") +
  theme_bfh()

Faceting

Facets inherit BFH defaults, but subtle tweaks can make multi-panel layouts feel polished and balanced.

Facet Customisation

Adjust strip backgrounds and text to reinforce brand colours:

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point(color = bfh_cols("hospital_primary")) +
  facet_wrap(~cyl, labeller = labeller(cyl = function(x) paste(x, "cylinders"))) +
  labs(title = "Fuel Efficiency by Cylinder Count") +
  theme_bfh() +
  theme(
    strip.background = element_rect(fill = bfh_cols("hospital_light_blue1")),
    strip.text = element_text(color = bfh_cols("hospital_primary"), face = "bold")
  )

Export and File Formats

Produce publication-ready figures while keeping file sizes manageable.

High-Resolution Export

Use bfh_save() presets for common outputs, or override the size when you need an exact width and height.

p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  theme_bfh()

# High-resolution PNG
bfh_save("figure.png", p, preset = "report_full", dpi = 600)

# Vector format for editing
bfh_save("figure.pdf", p, preset = "report_full")

# Custom dimensions
bfh_save("figure.png", p, width = 10, height = 8, units = "cm", dpi = 300)

Dimension Presets

Query the recommended sizes directly from the helper for reproducible reporting:

# Get dimensions for different output types (internal function)
report_dims <- BFHtheme:::get_bfh_dimensions(type = "report", format = "standard")
print(report_dims)
#> $width
#> [1] 7
#> 
#> $height
#> [1] 5

presentation_dims <- BFHtheme:::get_bfh_dimensions(type = "presentation", format = "wide")
print(presentation_dims)
#> $width
#> [1] 12
#> 
#> $height
#> [1] 6.75

Performance Optimisation

Keep your workflow responsive when working with fonts or large datasets.

Font Caching

Font detection results are cached for speed. Clear or refresh the cache after adding or removing fonts mid-session:

# Font detection is cached automatically
get_bfh_font()  # First call: detects font
get_bfh_font()  # Subsequent calls: uses cache

# Clear cache if needed
clear_bfh_font_cache()

# Force re-detection
get_bfh_font(force_refresh = TRUE)

Large Datasets

High-volume data can strain rendering. Consider these approaches:

# Use alpha for overplotting
ggplot(large_data, aes(x, y)) +
  geom_point(alpha = 0.3) +
  theme_bfh()

# Use geom_hex or geom_bin2d
ggplot(large_data, aes(x, y)) +
  geom_hex() +
  scale_fill_bfh_continuous(palette = "blues") +
  theme_bfh()

# Reduce point size
ggplot(large_data, aes(x, y)) +
  geom_point(size = 0.5) +
  theme_bfh()

Wrap-Up

  • Revisit vignette("getting-started") for the foundational workflow.
  • Explore vignette("theming") when you need hospital vs Region H branding.
  • Consult the function reference (help(package = "BFHtheme")) for parameter details.