skip to Main Content

I’m building a simple Shiny App, and have a decent understanding (or so I thought) of CSS and how to use it to style different UI elements (background color, size, etc.).

The issue I’m having is that I’m using renderUI() a lot in my app, because I need the inputs to be reactive to one another. When I move the selectInput() from the ui portion of the app into the server section (and use it inside renderUI()), it now seemingly ignores the CSS. What gives?

Here’s the first example, NOT using renderUI(), and the CSS (in this case, just a smaller 6px font-size for the selectInput() text) works fine:

library(shiny)

ui <- fluidPage(
  tags$head(tags$style(HTML('.selectize-input {font-size: 6px;}'))),
  shiny::selectInput("input_1", label = NULL, choices = c("a", "b", "c"))
)
server <- function(input, output, session){}
shinyApp(ui = ui, server = server)

Result:
enter image description here

Then I move the selectInput() call into a renderUI() function in the server section, and the CSS doesn’t get applied, even though I’m not changing that part at all. Shouldn’t the CSS rules still be applied to all .selectize-input elements?

library(shiny)

ui <- fluidPage(
  tags$head(tags$style(HTML('.selectize-input {font-size: 6px;}'))),
  shiny::uiOutput(outputId = "output_1")
)

server <- function(input, output, session){
  
  output$output_1 = shiny::renderUI({
    shiny::selectInput("input_1", label = NULL, choices = c("a", "b", "c"))
  })
  
}
shinyApp(ui = ui, server = server)

Result:
enter image description here

2

Answers


  1. When I run your code in RStudio I see that your css value is being overridden.

    enter image description here

    Why it is occurring in one and not the other I’m not sure.

    Perhaps instead of inline css, use a style sheet and add an additional class to your dropdown to ensure it selects? Or add further classes such as the .selectize-dropdown etc to increase its priority.

    Edit: Try this

    library(shiny)
    
    ui <- fluidPage(
      tags$head(tags$style(HTML('.selectize-input, .target {font-size: 6px;}'))),
      shiny::uiOutput(outputId = "output_1", class = "target")
    )
    
    server <- function(input, output, session){
      
      output$output_1 = shiny::renderUI({
        shiny::selectInput("input_1", label = NULL, choices = c("a", "b", "c"))
      })
      
    }
    shinyApp(ui = ui, server = server)
    
    Login or Signup to reply.
  2. In the head tag of an HTML document generally the order matters, e.g. if you have multiple stylesheets. Shiny constructs the head tag in a sense of ‘as needed’.

    • In your first example where you don’t render something inside the
      server, the head tag looks like this:

      ... 
      <link href="selectize-0.15.2/css/selectize.bootstrap3.css" rel="stylesheet"> 
      ... 
      <style> .selectize-input {font-size: 6px;}</style> 
      ...
      

      Your custom style is loaded at the end and hence
      overwrites the selectize styles.

    • However, in your second example, where you use renderUI around the
      selectInput inside the server and then put an uiOutput in the
      ui, the head tag has this order:

      ... 
      <style> .selectize-input {font-size: 6px;}</style> 
      ... 
      <link href="selectize-0.15.2/css/selectize.bootstrap3.css" rel="stylesheet" type="text/css"> 
      ... 
      

      Your custom style gets overwritten by the
      selectize styles (here: font-size: inherit). The order is
      expectable because your own styles may need to be relevant before the
      selectInput gets rendered (it is not rendered ‘immediately’).

    While it is a possibility to edit the CSS in combination with e.g. adding additional classes, this may not be the most desirable approach in particular in larger apps where you have a lot of custom styles. If it is suitable for you that in the second example the order of the head tag is the same as in the first example, one approach to set this would be like this: shiny has a function insertUI() which can be used for adding arbitrary UI elements into the app, and below the ui is an htmltools::htmlDependency() which contains the custom style without additional editing. It gets loaded after the selectize styles. There might be a more straightforward method, though.

    library(shiny)
    
    ui <- fluidPage(
      shiny::uiOutput(outputId = "output_1", class = "myClass")
    )
    
    server <- function(input, output, session){
      
      output$output_1 = shiny::renderUI({
        shiny::selectInput("input_1", label = NULL, choices = c("a", "b", "c"))
      })
      
      observe({
        insertUI(
          selector = "head",
          ui = htmltools::htmlDependency(
            name = "myCSS",
            version = "1.0",
            src = ".",
            package = "shiny",
            head = "<style> .myClass {font-size: 6px;} </style>"
          )
        )
      })
      
    }
    shinyApp(ui = ui, server = server)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search