Loading...
Loading...
Advanced theming for Shiny apps using bslib and Bootstrap 5. Use when customizing app appearance with bs_theme(), Bootswatch themes, custom colors, typography, brand.yml integration, Bootstrap Sass variables, custom Sass/CSS rules, dark mode and color modes, dynamic theme switching, real-time theming, theme inspection, or making R plots match the app theme with thematic.
npx skill4agent add posit-dev/skills shiny-bslib-themingpage_sidebar(
theme = bs_theme(), # "shiny" preset by default — polished, not plain Bootstrap
...
)page_sidebar(
theme = bs_theme(preset = "zephyr"), # or "cosmo", "minty", "darkly", etc.
...
)page_sidebar(
theme = bs_theme(
version = 5,
bg = "#FFFFFF",
fg = "#333333",
primary = "#2c3e50",
base_font = font_google("Lato"),
heading_font = font_google("Montserrat")
),
...
)_brand.yml_brand.ymlbs_theme()brand.ymlbs_theme(brand = FALSE) # Disable auto-discovery
bs_theme(brand = TRUE) # Require _brand.yml (error if not found)
bs_theme(brand = "path/to/brand.yml") # Explicit path"shiny"bgfgprimaryfont_google()...bs_add_variables()bs_add_rules()thematic::thematic_shiny()bs_themer()theme <- bs_theme(preset = "minty") |>
bs_theme_update(
primary = "#1a9a7f",
base_font = font_google("Lato")
) |>
bs_add_rules("
.card { box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
")sass::sass_bundle()bs_theme(
version = version_default(),
preset = NULL, # "shiny" (default for BS5+), "bootstrap", or Bootswatch name
..., # Bootstrap Sass variable overrides
brand = NULL, # brand.yml: NULL (auto), TRUE (require), FALSE (disable), or path
bg = NULL, fg = NULL,
primary = NULL, secondary = NULL,
success = NULL, info = NULL, warning = NULL, danger = NULL,
base_font = NULL, code_font = NULL, heading_font = NULL,
font_scale = NULL, # Scalar multiplier for base font size (e.g., 1.5 = 150%)
bootswatch = NULL # Alias for preset
)bs_theme_update(theme, ...)is_bs_theme(x)bs_theme()preset = "shiny"preset = "bootstrap"builtin_themes()bootswatch_themes()"zephyr""cosmo""minty""flatly""litera""darkly""cyborg""simplex""sketchy"| Parameter | Description |
|---|---|
| Background color |
| Foreground (text) color |
| Primary brand color (links, nav active states, input focus) |
| Default for action buttons |
| Positive/success states (typically green) |
| Informational content (typically blue-green) |
| Warnings (typically yellow) |
| Errors/destructive actions (typically red) |
bs_theme(
bg = "#202123", fg = "#B8BCC2",
primary = "#EA80FC", secondary = "#48DAC6"
)bgfgprimarybgfghtmltools::parseCssColors()base_fontheading_fontcode_fontfont_scale1.5font_collection()local = TRUEbs_theme(
base_font = font_google("Roboto"),
heading_font = font_google("Montserrat"),
code_font = font_google("Fira Code")
)font_google("Crimson Pro", wght = "200..900")font_google("Raleway", wght = c(300, 400, 700))bs_theme(
base_font = font_collection(
font_google("Lato", local = FALSE),
"Helvetica Neue", "Arial", "sans-serif"
)
)font_link("Crimson Pro",
href = "https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@200..900")@font-facefont_face(
family = "Crimson Pro",
style = "normal",
weight = "200 900",
src = "url(fonts/crimson-pro.woff2) format('woff2')"
)font_collection(font_google("Lato"), "Helvetica Neue", "Arial", "sans-serif")bs_theme()theme <- bs_add_variables(
bs_theme(preset = "sketchy", primary = "orange"),
"body-bg" = "#EEEEEE",
"font-family-base" = "monospace",
"font-size-base" = "1.4rem",
"btn-padding-y" = ".16rem"
).where | When to use |
|---|---|
| Set variable defaults with |
| Reference other Bootstrap variables (e.g., |
| Placed after all rules. Rarely needed. |
# This fails in bs_theme() because $secondary isn't defined yet:
# bs_theme("progress-bar-bg" = "$secondary")
# Use bs_add_variables with .where = "declarations" instead:
bs_theme() |>
bs_add_variables("progress-bar-bg" = "$secondary", .where = "declarations")theme <- bs_theme(primary = "#007bff") |>
bs_add_rules("
.custom-card {
background: mix($bg, $primary, 95%);
border: 1px solid $primary;
padding: $spacer;
@include media-breakpoint-up(md) {
padding: $spacer * 2;
}
}
")bs_add_rules(sass::sass_file("www/custom.scss"))lighten()darken()mix()rgba()color-contrast()@include media-breakpoint-up()@include box-shadow()@include border-radius()theme |>
bs_add_functions("@function my-tint($color) { @return mix(white, $color, 20%); }") |>
bs_add_rules(".highlight { background: my-tint($primary); }")sass::sass_bundle()my_extension <- sass::sass_layer(
defaults = list("my-var" = "red !default"),
rules = ".my-class { color: $my-var; }"
)
theme <- bs_theme() |> bs_bundle(my_extension)bs_theme(...)bs_add_variables()bs_theme(
"border-radius" = "0.5rem",
"card-border-radius" = "1rem",
"card-bg" = "lighten($bg, 5%)",
"navbar-bg" = "$primary",
"link-color" = "$primary",
"font-size-base" = "1rem",
"spacer" = "1rem",
"btn-padding-y" = ".5rem",
"btn-padding-x" = "1rem",
"input-border-color" = "#dee2e6"
)--bs-*data-bs-themedata-bs-themeinput_dark_mode()toggle_dark_mode()session$setCurrentTheme()bs_theme()thematiclibrary(thematic)
thematic_shiny(font = "auto") # Call before shinyApp()
shinyApp(ui, server)font = "auto"bs_theme()bs_themer()library(ggplot2)
theme_set(theme_minimal())bslib-page-dashboardpage_sidebar()page_sidebar(
class = "bslib-page-dashboard",
title = "My Dashboard",
sidebar = sidebar(...),
...
)page_navbar()nav_panel()page_navbar()page_navbar(
title = "Analytics",
nav_panel("Dashboard", class = "bslib-page-dashboard",
layout_column_wrap(...)
),
nav_panel("Report",
# No dashboard class — standard white background for prose/reports
...
)
)bslib::bs_theme_preview() # Default theme
bslib::bs_theme_preview(bs_theme(preset = "darkly")) # Custom themewith_themer = TRUEshiny::runApp()run_with_themer(shinyApp(ui, server))
run_with_themer("path/to/app")server <- function(input, output, session) {
bs_themer() # Add during development, remove for production
# ...
}bs_theme()runtime: shinybs_dependency_defer()vars <- c("body-bg", "body-color", "primary", "border-radius")
bs_get_variables(bs_theme(), varnames = vars)
bs_get_variables(bs_theme(preset = "darkly"), varnames = vars)bs_get_contrast(bs_theme(), c("primary", "dark", "light"))bs_theme()bs_theme(version = 5)font_collection()bs_get_contrast()theme.R# theme.R
app_theme <- function() {
bs_theme(
version = 5,
primary = "#2c3e50",
base_font = font_google("Lato"),
heading_font = font_google("Montserrat", wght = c(400, 700))
) |>
bs_add_rules(sass::sass_file("www/custom.scss"))
}