Search code examples
rshinybslib

Determine foreground / background colour in bslib app with dark/light mode


I am experimenting with bslib's dark/light mode feature. I would like to replot a base R graphic depending on the dark/light mode. In particular, I want to set the fg and bg options to par().

A quick search brought up the functions bs_current_theme and bs_get_variables:

current_theme <- bslib::bs_current_theme()
bg <- bslib::bs_get_variables(current_theme, "body-bg")[["body-bg"]]

However, the value of bg seems not to change after toggle_dark_mode() is called. Here is an example app that prints bg every time the dark/light mode button is pressed:

ui <- bslib::page_navbar(
  title = "Dark Mode Demo",
  theme = bslib::bs_theme(version=5),
  bslib::nav_panel(
    title = "Base Graphics",
    bslib::card(
      bslib::card_header("Mosaic"),
      shiny::plotOutput("base_mosaic")
    )
  ),
  bslib::nav_spacer(),
  bslib::nav_item(
    bslib::input_dark_mode(id = "theme_toggle", mode = "light")
  )
)

server <- function(input, output, session) {
  # dark / light mode
  shiny::observeEvent(input$theme_toggle, {
    bslib::toggle_dark_mode(mode=input$theme_toggle)
    current_theme <- bslib::bs_current_theme()
    message("bg=", bslib::bs_get_variables(current_theme, "body-bg")[["body-bg"]])
    par(bg = bslib::bs_get_variables(current_theme, "body-bg")[["body-bg"]]) 
  })
  output$base_mosaic <- shiny::renderPlot({ 
    plot(1:10)
  })
}

shiny::shinyApp(ui, server)

What is the best way to determine the current background and foreground (=text) color in bslib?


Solution

  • Here is a possibility using a small custom message handler: It gets the current value of the Bootstrap variable --bs-body-bg and sends it to Shiny:

    enter image description here

    library(shiny)
    library(bslib)
    
    ui <- page_navbar(
      tags$head(
        tags$script("
          Shiny.addCustomMessageHandler('get_bg', function(value) {
            var bg = getComputedStyle(document.body).getPropertyValue('--bs-body-bg');
            Shiny.setInputValue('current_bg', {bg});
          });
        ")
      ),
      title = "Dark Mode Demo",
      theme = bs_theme(version=5),
      nav_panel(
        title = "Base Graphics",
        card(
          card_header("Mosaic"),
          plotOutput("base_mosaic")
        )
      ),
      nav_spacer(),
      nav_item(
        input_dark_mode(id = "theme_toggle", mode = "light")
      )
    )
    
    server <- function(input, output, session) {
      # dark / light mode
      observeEvent(input$theme_toggle, {
        toggle_dark_mode(mode=input$theme_toggle)
        
        session$sendCustomMessage("get_bg", "")
        req(input$current_bg)
        message("bg=", input$current_bg)
      })
      output$base_mosaic <- renderPlot({ 
        par(bg = input$current_bg)
        plot(1:10)
      })
    }
    
    shinyApp(ui, server)