How to style a SelectBox?

In a page there are a number of SelectBoxes.

The first one is a substitute of a navbar to choose a special page in the menu; the other ones to choose parameters.

(Why a SelectBox as a navbar?!? Long story short: I cannot use st.tabs because I need to set st.session_state on click; a proper navbar is missing in Streamlit; streamlit_option_menu is not an option: it randomly resets to the first option; multiple st.Buttons to be used as a navbar must be inserted into st.columns and their horizontal positioning is quite ugly; it is not possible to call a JS function to apply a style when clicking st.Button; and so on and on)

I would like to style the first SelectBox leaving the other ones untuoched. Alas it is not possible to apply a class to a Streamlit component so I cannot apply a dedicated CSS.

Here I hoped to inject a simple CSS, but it selects BOTH SelectBoxes, not only the first one:

st.markdown("""
	<style>
	.stSelectbox:first-of-type > div[data-baseweb="select"] > div {
	      background-color: fuchsia;
    	      padding: 10px;
	}
	</style>
""", unsafe_allow_html=True)

import streamlit as st
chosen1 = st.selectbox("Menu 1", ["Option1.1", "Option1.2", "Option1.3"], key="option1")
chosen2 = st.selectbox("Menu 2", ["Option2.1", "Option2.2", "Option2.3"], key="option2")

Any idea about a workaround?

(**PLEASE, add class and/or id! They would solve a number of issues that are stealing away the best days of my life…)

Probably an obvious question, but have you considered a multipage app for navigation Create a multipage app - Streamlit Docs?

In terms of selective styling, it’s terribly ugly, and probably not very future proof, but if you really need to select exactly one arbitrary element, browser developer tools (at least in Chrome) can give you a CSS selector that is specific enough

In this case, it told me that the selectbox could be targeted with this hideous selector

    #root > div:nth-child(1) > div.withScreencast > div > div > div > section.main.css-uf99v8.e1g8pov65 > div.block-container.css-1y4p8pa.e1g8pov64 > div:nth-child(1) > div > div:nth-child(2) > div > div > div

Hideous as it is, it seems to work

In the future, we are definitely considering ways to make customized styling easier.

1 Like

Another option is to inject a short js script with st.components.v1.html to do the styling:

import streamlit as st

def color_selectbox(n_element:int, color:str):

    js = f'''
    <script>
    // Find all the selectboxes
    var selectboxes = window.parent.document.getElementsByClassName("stSelectbox");
    
    // Select one of them
    var selectbox = selectboxes[{n_element}];
    
    // Select only the selection div
    var selectregion = selectbox.querySelector('[data-baseweb="select"]');
    
    // Modify the color
    selectregion.style.backgroundColor = '{color}';
    </script>
    '''
    st.components.v1.html(js, height=0)

chosen1 = st.selectbox("Menu 1", ["Option1.1", "Option1.2", "Option1.3"], key="option1")
color_selectbox(0, 'pink')

chosen2 = st.selectbox("Menu 2", ["Option2.1", "Option2.2", "Option2.3"], key="option2")
color_selectbox(1, 'yellow')
2 Likes

Yes, I considered a multipage app with tabs and so on, but the problem in my case is quite simple: I cannot change the app structure and need to find a quick-and-dirt solution to replace streamlit-option-menu that doesn’t work properly anymore.
I developed a (quite complex) app locally, it works quite well, and then deployed it on a remote server using docker. The server is slow to answer and to provide the frontend elements, so streamlit-option-menu sometimes doesn’t appear at all, and sometimes it reads the first option instead of the chosen one after an app reset.

The behavior is erratic so I assume it is due to timings because the libraries’ versions are the same.
I see that every time streamlit-option-menu is used there is a GET requesting for it. If this GET is not answered fast the page is completed without the menu :slightly_frowning_face: and it is not present at all.

I tried to replace it with a similar looking combination of horizontal buttons, but at no avail, as missing id and class in st.Button definition I cannot isolate any feature that is unique for this menu only. Even the attributes like div[data-baseweb="select"] here

st.markdown("""
	<style>
	.stSelectbox > div[data-baseweb="select"] > div {
		background-color: fuchsia;
    	padding: 10px;
	}
	
	</style>
""", unsafe_allow_html=True)

don’t work, because

  1. the structure is deeply nested, and CSS doesn’t let me go back to the parents/grandparents to change the st.Selectbox without using jQuery or JS.
    If I knew well enough JS I wouldn’t use Streamlit at all…
  2. Up to now every change in CSS styling even with deep and most complete element definitions like this
div [data-testid=stHorizontalBlock] [data-testid=column]:nth-of-type({st.session_state.option_menu_choice}) button {
  ...
}

invariably modify the aspect of every similar element in the page. I couldn’t find a way to select only the first stHorizontalBlock in the page because ALL of them are considered first.
3) Selecting classes and attributes doesn’t guarantee any compatibility with future releases.

All could be very easily solved adding id and/or class to every Streamlit component, and frankly I don’t understand why something so simple has not be introduced yet.

Out of sheer frustration I chose SelectBoxes instead, but I need to change their style otherwise they are quite unnoticeable in the page.

Thank you for your help!

1 Like

It looks like you nailed it.
I couldn’t find a way to inject JS to select a single element, but now I see how it works.

Thank you!

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.