You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在bslib应用的明暗模式中确定前景与背景颜色?

问题描述

我正在试验bslib的明暗模式功能,希望根据明暗模式重绘R基础图形,具体是设置par()的fg和bg参数。

通过搜索找到bs_current_theme和bs_get_variables函数:

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

但调用toggle_dark_mode()后,bg的值似乎并未改变。以下是每次点击明暗模式按钮时打印bg值的示例应用:

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)

请问在bslib中确定当前背景与前景(文本)颜色的最佳方法是什么?


解决方案

核心问题

bs_current_theme()获取的是后端初始化的静态主题对象,而bslib的明暗模式切换是前端通过修改CSS变量实时生效的,并不会同步修改后端的主题对象,所以直接用它拿不到更新后的颜色值。此外,你之前的代码里把par()设置放在observeEvent中是无效的——每次renderPlot执行时,图形参数都会被重置,必须在renderPlot内部设置。

两种可靠实现方法

方法1:根据模式预定义颜色(适合默认主题)

利用input$theme_toggle返回的当前模式("light"或"dark"),提前定义对应模式的颜色值(匹配Bootstrap 5默认配色),直接在绘图时切换:

server <- function(input, output, session) {
  # 预定义Bootstrap 5默认明暗模式的颜色
  theme_colors <- list(
    light = list(bg = "#ffffff", fg = "#212529"),
    dark = list(bg = "#212529", fg = "#ffffff")
  )
  
  shiny::observeEvent(input$theme_toggle, {
    bslib::toggle_dark_mode(mode = input$theme_toggle)
  })
  
  output$base_mosaic <- shiny::renderPlot({ 
    current_mode <- input$theme_toggle
    # 在绘图前设置图形参数
    par(bg = theme_colors[[current_mode]]$bg, fg = theme_colors[[current_mode]]$fg)
    plot(1:10)
  })
}

方法2:从前端CSS变量读取实时颜色(适合自定义主题)

如果使用了自定义主题,预定义颜色可能不匹配,这时候可以通过JS获取页面实时生效的CSS变量值,传给Shiny后端:

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")
  ),
  # 添加自定义JS脚本,监听主题切换并传递颜色值
  tags$script(HTML('
    // 页面加载时传递初始颜色
    document.addEventListener("DOMContentLoaded", () => {
      sendThemeColors();
    });
    
    // 监听主题切换事件,传递更新后的颜色
    document.addEventListener("shiny:value", (event) => {
      if (event.name === "theme_toggle") {
        sendThemeColors();
      }
    });
    
    // 封装获取并传递颜色的函数
    function sendThemeColors() {
      const root = getComputedStyle(document.documentElement);
      const bg = root.getPropertyValue("--bs-body-bg").trim();
      const fg = root.getPropertyValue("--bs-body-color").trim();
      Shiny.setInputValue("live_theme_colors", {bg, fg});
    }
  '))
)

server <- function(input, output, session) {
  shiny::observeEvent(input$theme_toggle, {
    bslib::toggle_dark_mode(mode = input$theme_toggle)
  })
  
  output$base_mosaic <- shiny::renderPlot({ 
    # 等待颜色值传递过来
    req(input$live_theme_colors)
    # 设置图形参数
    par(bg = input$live_theme_colors$bg, fg = input$live_theme_colors$fg)
    plot(1:10)
  })
}

shiny::shinyApp(ui, server)

总结

  • 若使用默认Bootstrap主题,方法1简单高效,无需额外JS代码
  • 若使用自定义主题或动态修改主题变量,方法2能准确获取实时生效的颜色
  • 必须在renderPlot内部设置par()参数,确保每次绘图都使用最新的颜色配置

内容的提问来源于stack exchange,提问作者Karsten W.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:25:59