rshinybslib

Dynamically change font_scale of bslib/shiny app


In my app, I want to enable the user to change the text size (similar to the wikipedia appearance).

I thought the font_scale parameter of bs_theme would be a good place for that.

However, changing font_scale seems to have no effect. Here is a minimal example:

library(shiny)
library(bslib)

# UI
ui <- bslib::page_sidebar(
    title = "Font Size",
    fillable = FALSE,
    sidebar = bslib::sidebar(
        shiny::radioButtons(
            inputId="pref",
            label="Text Size:",
            selected = "standard",
            choiceNames = c("Small", "Standard", "Large"),
            choiceValues = c("small", "standard", "large")
        )
    ),
    shiny::markdown(
        "## Sample Markdown\n\nThis is *italic* and **bold** text. The size changes with your selection."
    )
)
      
# Server
server <- function(input, output, session) {

  text_scale <- reactive({
    switch(input$pref,
           "small" = 0.8,
           "standard" = 1.0,
           "large" = 1.2)
  })

  # no effect
  shiny::observeEvent(input$pref, {
        scale <- text_scale()
        the_theme <- bslib::bs_current_theme()
        the_theme <- bslib::bs_theme_update(the_theme, font_scale=scale)
        bslib::bs_global_set(theme = the_theme)
  })

}

shinyApp(ui = ui, server = server)

I expected / hoped that changing input$pref would change the appearance of the sample text.

Any hints how I can achieve that?


Solution

  • Concerning the theme update there is in principal no issue with your app. However, also in my opinion bslib::bs_global_set() is not really working as expected. You can instead use session$setCurrentTheme() (as introduced in Shiny 1.6), what seems to work.

    One remark concerning the font_scale parameter of bs_theme() which you try to use: Note that this is a multiplier for the current parameter. Hence, if you toggle the input to 0.8 and then 1.2, and rely on the previous theme, you finally don't have 1.2, but rather 0.8 * 1.2. This is - maybe - not what you want. I modified the code below such that one always gets the multiplier "starting from 1".

    library(shiny)
    library(bslib)
    
    ui <- page_sidebar(
      title = "Font Size",
      fillable = FALSE,
      sidebar = sidebar(
        radioButtons(
          inputId="pref",
          label="Text Size:",
          selected = "standard",
          choiceNames = c("Small", "Standard", "Large"),
          choiceValues = c("small", "standard", "large")
        )
      ),
      markdown(
        "## Sample Markdown\n\nThis is *italic* and **bold** text. The size changes with your selection."
      )
    )
    
    server <- function(input, output, session) {
      
      text_scale <- reactive({
        switch(input$pref,
               "small" = 0.8,
               "standard" = 1.0,
               "large" = 1.2)
      })
      
      currentScale <- 1
      
      observeEvent(input$pref, {
        scale <- text_scale()
        the_theme <- bs_current_theme()
        the_theme <- bs_theme_update(the_theme, font_scale = scale / currentScale)
        session$setCurrentTheme(the_theme)
        currentScale <<- scale
      })
    }
    
    shinyApp(ui = ui, server = server)