---
title: "Theming"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Theming}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE, purl = FALSE)
```
bslibdash ships a single brand theme, [`brand_bs_theme()`], that every
page constructor applies by default. This vignette shows how to use it
as-is, tweak individual Bootstrap variables, add your own SCSS, swap to
a different Bootswatch preset, and tune the theme interactively with
`bslib::bs_themer()`.
For the building blocks underneath, see the
[bslib theming articles](https://rstudio.github.io/bslib/articles/theming/).
## The default theme
`brand_bs_theme()` returns a `bslib::bs_theme()` built on Bootstrap 5
(`bootswatch = "bootstrap"`) with the bslibdash brand colour and typography
defaults. Component CSS is not baked into the theme - each bslibdash component
attaches its own theme-aware SCSS via `bslib::bs_dependency_defer()`, so
swapping `theme` does not drop bslibdash styles. Key variable defaults:
| Variable | Value |
|-----------------|--------------------------------|
| `primary` | `#0460A9` |
| `body-color` | `#111827` |
| `border-radius` | `.75rem` |
| `font-family` | System UI stack |
| `enable-shadows`| `TRUE` (subtle, layered) |
## Overriding theme variables
Use `bslib::bs_add_variables()` to override individual Bootstrap Sass
variables and pass the result to any page constructor:
```{r purl=FALSE}
library(shiny)
library(bslibdash)
theme <- brand_bs_theme() |>
bslib::bs_add_variables(
primary = "#8B0000",
`body-color` = "#1F2937",
`font-size-base` = "0.95rem"
)
ui <- dashboardPage(
header = dashboardHeader(title = "Maroon dashboard"),
sidebar = dashboardSidebar(),
body = dashboardBody(),
theme = theme
)
```
Component status colours (`primary`, `success`, ...) automatically pick
up the new variable values, so cards, buttons, badges and value boxes
re-skin together.
## Adding custom SCSS
Add bespoke rules with `bslib::bs_add_rules()`:
```{r purl=FALSE}
theme <- brand_bs_theme() |>
bslib::bs_add_rules("
.app-header { background: linear-gradient(90deg, #0460A9, #003a6e); }
.app-footer { font-size: 0.85rem; }
")
```
## Switching the Bootswatch preset
```{r purl=FALSE}
theme <- bslib::bs_theme(version = 5, bootswatch = "flatly") |>
bslib::bs_add_variables(primary = "#0460A9")
dashboardPage(
header = dashboardHeader(title = "Flatly + brand blue"),
sidebar = dashboardSidebar(),
body = dashboardBody(h2("Flatly + brand blue")),
theme = theme
)
```
Note: bslibdash component CSS is attached by the components themselves,
so any `bslib::bs_theme()` - including a plain `bslib::bs_theme(version = 5)`
- still renders the package's custom classes (`.bslibdash-value-box`,
`.bslibdash-info-box`, `.app-sidebar`, ...). Use `brand_bs_theme()` when you
want the bslibdash brand defaults; use your own `bs_theme()` when you do not.
## Real-time theming
`bslib::bs_themer()` works against bslibdash pages out of the box - call it
once from your server to interactively tune colours and typography
during development:
```{r purl=FALSE}
server <- function(input, output, session) {
bslib::bs_themer()
}
```
See the bslib
[real-time theming article](https://rstudio.github.io/bslib/articles/theming/index.html#real-time)
for the underlying widget.
## Dynamic theming
`session$setCurrentTheme()` swaps the active `bs_theme()` at runtime
without a page reload, which makes it easy to let users flip between
light/dark variants or any other named presets. Because each bslibdash
component attaches its SCSS via `bslib::bs_dependency_defer()`, the
components automatically recompile against the new theme:
```{r purl=FALSE}
library(shiny)
library(bslib)
library(bslibdash)
themes <- list(
light = brand_bs_theme(),
dark = bs_theme(
version = 5,
bg = "#0B0F19",
fg = "#E5E7EB",
primary = "#60A5FA"
)
)
ui <- dashboardPage(
title = "Theme switcher",
header = dashboardHeader(
rightUi = radioButtons(
"mode", NULL,
choices = c("light", "dark"),
inline = TRUE
)
),
sidebar = dashboardSidebar(
sidebarMenu(
id = "sidebar",
menuItem("Overview", tabName = "overview", icon = icon("house"))
)
),
body = dashboardBody(
fluidRow(
valueBox(1200, "Total sales", icon = icon("graph-up"), color = "success"),
valueBox(53, "Open issues", icon = icon("exclamation-triangle"), color = "warning"),
valueBox(8, "Critical alerts", icon = icon("bell"), color = "danger")
),
fluidRow(
infoBox(title = "CPU usage", value = "48%",
subtitle = "Average over last 5 min",
icon = icon("cpu"), color = "primary"),
infoBox(title = "Queue length", value = 17,
subtitle = "Waiting jobs",
icon = icon("inboxes"), color = "warning")
),
fluidRow(
box(title = "Status", width = 6, status = "primary",
"All systems nominal."),
box(title = "Notes", width = 6, status = "info",
collapsible = TRUE,
"Toggle between light and dark in the header to see the theme swap.")
)
),
theme = themes$light
)
server <- function(input, output, session) {
observeEvent(input$mode, {
session$setCurrentTheme(themes[[input$mode]])
})
}
shinyApp(ui, server)
```
Use the same pattern to expose any other curated set of presets (for
example, a Bootswatch picker) - just build the `bs_theme()` objects
ahead of time and hand the chosen one to `setCurrentTheme()`. The dark
variant above is built with `bs_theme(bg = ..., fg = ..., primary = ...)`
rather than `bs_add_variables()` on the brand theme because the former
makes bslib rederive a coherent palette (greys, borders, link colours,
secondary/tertiary backgrounds) from `bg`/`fg`, while the latter only
patches the named variables. For the underlying mechanics, see
[`?bslib::bs_dependency_defer`](https://rstudio.github.io/bslib/reference/bs_dependency.html).