How to styled st.expander in streamlit and python based on value

Summary

having a dataframe that i want to display its result using st.expander widget based on the value of column type i want to styled the header of the expander.
the problem is :it doesn’t display the correct result where all the selected record will be displayed with the same style even if the df[‘type’] is different

Code snippet:

cols = st.columns(len(df))

for i ,x in enumerate(cols):

if df[i]['type'] == 'employee':
               
                font_css ='''
                    <style>
                        .streamlit-expanderHeader.st-ae.st-dj.st-ag.st-ah.st-ai.st-aj.st-bv.st-dk.st-bw.st-dl.st-dm.st-dn.st-do.st-ar.st-as.st-dp.st-dq.st-b3.st-cj.st-c5.st-dr.st-b4.st-ds.st-c3.st-c4.st-c2.st-c1{
                           font-size:30px;
                           text-align:center;     
                           color:yellow;
                           background-color :red;
                    }
                    </style>
                    
                '''
                st.write(font_css,unsafe_allow_html = True)

            if df[i]['type'] == 'supervisor':
              
                font_css ='''
                    <style>
                        .streamlit-expanderHeader.st-ae.st-dj.st-ag.st-ah.st-ai.st-aj.st-bv.st-dk.st-bw.st-dl.st-dm.st-dn.st-do.st-ar.st-as.st-dp.st-dq.st-b3.st-cj.st-c5.st-dr.st-b4.st-ds.st-c3.st-c4.st-c2.st-c1{
                           font-size:30px;
                           text-align:center;     
                           color:Green;
                    }
                    </style>
                    
                '''
                st.write(font_css,unsafe_allow_html = True)
            if df[i]['type'] == 'CEO':
               
                font_css ='''
                    <style>
                        .streamlit-expanderHeader.st-ae.st-dj.st-ag.st-ah.st-ai.st-aj.st-bv.st-dk.st-bw.st-dl.st-dm.st-dn.st-do.st-ar.st-as.st-dp.st-dq.st-b3.st-cj.st-c5.st-dr.st-b4.st-ds.st-c3.st-c4.st-c2.st-c1{
                           font-size:30px;
                           text-align:center;     
                           color:blue;
                           background-color :red;
                    }
                    </style>
                    
                '''
                st.write(font_css,unsafe_allow_html = True)
            
            

            with st.expander(f"{df[i]['name']}"):
                
            
                st.image(df[i][f'ImgPath'],width=100)

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:
the code must display multiple expanders

color:yellow; background-color :red;  if type == employe
color:Green;  if type == supervisor
color:blue;background-color :red;  if type == ceo

Actual behavior:

Explain the undesired behavior or error you see when you run the code above.
If you’re seeing an error message, share the full contents of the error message here.

Debug info

  • Streamlit version: 1.13
  • Python version:3.10
  • Using PipEnv
  • OS version: windows 10
  • Browser version: chrome 106

Hi @leb_dev!

  1. As far as I noticed for “css hacking” people are usually using st.markdown not st.write (this however, should not have an impact)
  2. This example should help in regards how to refer to st.expander label style
  3. Could you share some screens what you are seeing or what you are expecting to see?

I want to change the color of the header of the expander based on the value of the column type
for now i am able to change the color but regardless of the type all the expanders will have the same color and style.

1 Like

Ok! So there is a way
but it is quite hacky :wink: maybe someone will come with better solution*, but for now:

Lets create external styles.css file with the following contents:

.label1css {
    background-color: blue;
}

.label2css {
    background-color: red;
}

.label3css {
    background-color: green;
}

streamlit app

We will need import components for some JS trickery

import streamlit as st
import streamlit.components.v1 as components

let’s load the css file in a classic community forum way :slight_smile:

def local_css(file_name):
    with open(file_name) as f:
        st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)

local_css("style.css")

Time for some expanders:

with st.expander("label1"):
    st.write("text1")
with st.expander("label2"):
    st.write("text2")
with st.expander("label3"):
    st.write("text3")

and now the magic trick: we will use JS to find expander label text and then assign to each expander with different label different style:

components.html(
    """
<script>
Array.from(window.parent.document.querySelectorAll('div[data-testid="stExpander"] div[role="button"] p')).find(el => el.innerText === 'label1').classList.add('label1css');
Array.from(window.parent.document.querySelectorAll('div[data-testid="stExpander"] div[role="button"] p')).find(el => el.innerText === 'label2').classList.add('label2css');
Array.from(window.parent.document.querySelectorAll('div[data-testid="stExpander"] div[role="button"] p')).find(el => el.innerText === 'label3').classList.add('label3css');
console.log("test");
</script>
""",
    height=0,
    width=0,
)

Result:

* as for better sollutions - if widgets in streamlit would have independent id’s for each widget, for example “expander_1”,“expander_2”,
“expander_n” adjusting styles would be much easier. I wil probably pitch this idea to streamlit team :wink:

1 Like

this will be a greate idea it will make the styling much easier thank yo i will try your suggestion

1 Like

Hello @TomJohn , I tried to use the exact same steps that you suggested but still couldn’t get any color on those 3 labels as seen above. Could you please help?

This is the error message on my browser’s console -

Uncaught TypeError: Cannot read properties of undefined (reading ‘classList’)

Looking forward to seeking help on this one!

Thanks!

Hi @Devansh_Popat could you share your code and/or what version of streamlit you are using? :slight_smile:

1 Like

Sorry for late reply @TomJohn , My code worked. Thanks!

1 Like