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)
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)
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)