Search code examples
rshinysassnavbarbslib

Why is my bslib theme not coloring the navbar as expected?


I can't seem to get bslib style options working right. I was interested to use the pulse bootswatch for the purple (https://bootswatch.com/pulse/), but I'm not seeing it ...

library(shiny)
library(bslib)

ui <- page_navbar(
    title = "why is the background here not purple?",
    theme = bs_theme(bootswatch = "pulse")
)

server <- function(input, output, session) {
    bs_themer()
}

shinyApp(ui = ui, server = server)

Using the bs_themer in the top right of the app seems to give inconsistent results, but other bootswatches do sometimes seem to use the primary color as the header background as expected, but the pulse theme doesn't seem to work.


Solution

  • The reason why you don't see the purple color when applying the Bootswatch theme Pulse (via theme = bs_theme(bootswatch = "pulse")) is because Bootstrap and bslib handle navbar styling in different ways (despite that, it is expectable that your code should suffice for setting up the color).

    Bootstrap 5

    The docs on color schemes suggest customization by using .bg-* classes. A navbar could e.g. have the HTML setup

    <nav class="navbar bg-primary" data-bs-theme="dark">
      <!-- Navbar content -->
    </nav>
    

    and would get colored with a corresponding primary color (here the purple, this is also used in the above Bootswatch example). Additionally, data-bs-theme="dark" makes the navbar using dark colors. However, bslib does not define the navbar by this means.

    bslib

    bslib uses the variables $navbar-light-bg, $navbar-dark-bg and $navbar-bg for the background color, as introduced in the PRs rstudio/bslib#253 and rstudio/bslib#271. Additionally, bslib makes use of two Bootstrap 3 classes: .navbar-default and .navbar-inverse. In your example code (generally with bslib::page_navbar() with inverse = FALSE), one would get

    <nav class="navbar navbar-default navbar-static-top navbar-inverse" role="navigation">
      <!-- Navbar content -->
    </nav>
    

    The reason why these classes are still used is backward compatibility: shiny::navbarPage() calls bslib::page_navbar() under the hood and shiny relies on Bootstrap 3. The two classes define the color variables as follows:

    So we have to see how $navbar-light-bg and $navbar-dark-bg are defined. There is a mapping function in bslib which is used for the translation from Bootstrap 3 navbar classes to higher versions, it shows the mapping definitions, e.g.

    > bslib:::bs3compat_navbar_defaults("pulse")
    [1] ""
    > bslib:::bs3compat_navbar_defaults("flatly")
    [[1]]
    [1] "$navbar-light-bg: $primary !default;"
    
    [[2]]
    [1] "$navbar-dark-bg: $success !default;"
    

    Using "pulse" returns here "" because this Bootswatch theme was added in Bootstrap 4. Therefore, the fallback value var(--bs-dark) gets used and this is the black what you see.

    How to solve it?

    The above should make clear why both of the following not convenient approaches work ("#593196" is the primary color for Pulse):

    • Overwrite $navbar-dark-bg or $navbar-light-bg:
      theme = bs_theme(bootswatch = "pulse") |>
        bs_add_variables("navbar-dark-bg" = "#593196")
      
    • Set the background-color of .navbar-default to $primary:
      theme = bs_theme(bootswatch = "pulse") |>
        bs_add_rules(
          rules = ".navbar.navbar-default {
                    background-color: $primary !important;
                   }"
        )
      

    However, the recommended solution in the sense of Bootstrap 5 is to use appropriate classes on the navbar. What we can do is:

    • Omit the .navbar-inverse and .navbar-default classes,
    • Add the .bg-primary class,
    • Add the attribute data-bs-theme="dark".

    This can be implemented via htmltools::tagQuery() (and setting inverse = FALSE in page_navbar()):

    library(shiny)
    library(bslib)
    
    ui <- htmltools::tagQuery(page_navbar(
      title = "background is now purple",
      theme = bs_theme(bootswatch = "pulse"),
      inverse = FALSE
    ))$ 
      find(".navbar")$
      toggleClass("navbar-default bg-primary")$
      addAttrs(`data-bs-theme` = "dark")$
      allTags()
    
    shinyApp(ui = ui, server = \(...) {})
    

    We obtain the purple color, as expected:

    enter image description here


    Please note that we may expect changes in the future, see rstudio/bslib#940 (page_navbar() and navset_*() functions should provide easier access to navbar attributes).