Applying custom CSS to manually created containers

Summary

I am trying to add custom outline to containers used in my app. I am familiar with the technique of applying custom CSS styles through markdown and have no issue with that. However, I am struggling to only apply styles to containers created with st.container() without affecting all the other containers that Streamlit generates on its own (e.g. when using tabs). Sadly, manually created containers have no “data-testid” attribute, unlike metrics and some other widgets. That would’ve helped resolve the issue.

Basically, my question narrows down to this:
How can I add a CSS outline to the container I am creating manually without affecting other widgets?
Let this be an example application:

st.write('text outside the container')
with st.container():
       st.write('text inside the container')

This be a CSS style code:

[data-testid="some_container_tag"] {
    outline: 2px solid red;
    border-radius: 2px;
} 

And this be an expected result:
image

1 Like

Hi @Chay,

This is pretty convoluted, but here’s one method that seems to work:

import streamlit as st

st.write("text outside the container")
with st.container():
    st.write("text inside the container")

st.write("More text outside the container")


st.markdown(
    """
<style>
    div[data-testid="stVerticalBlock"] div[style*="flex-direction: column;"] div[data-testid="stVerticalBlock"] {
        border: 1px solid red;
    }
</style>
""",
    unsafe_allow_html=True,
)
2 Likes

Your idea is magnificent! Although, I would make some minor changes to the structure of the css selector so that it does not affect st.columns() grid :wink:

Turns out that the st.columns() method generates a similar hierarchy of divs. Luckily, there is a difference! Unlike those that compose a container, these divs are not direct children. So, a specific descendant-child order would make our thingy more precise :upside_down_face: This allows us to play with containers without affecting other widgets (as far as I can tell).

Little example here:

    with st.container():
        st.text('interesting content')
        st.text('in a potentially ')
        st.text('very stylish container')

    col1, col2, col3 = st.columns(3)
    col1.write('cool column box 1')
    col2.write('cool column box 2')
    col3.write('cool column box 3')
/* Style columns */
[data-testid="column"] {
    box-shadow: rgb(0 0 0 / 20%) 0px 2px 1px -1px, rgb(0 0 0 / 14%) 0px 1px 1px 0px, rgb(0 0 0 / 12%) 0px 1px 3px 0px;
    border-radius: 15px;
    padding: 5% 5% 5% 10%;
} 

/* Style containers */
[data-testid="stVerticalBlock"] > [style*="flex-direction: column;"] > [data-testid="stVerticalBlock"] {
    border: 20px groove red;
}

6 Likes

That’s excellent!

to who it may concern:
if you want to style a specific element you can create a css element with an id and then style it using the css “has” property. It works directly in edge and chrome, in firefox (v 109) you have to enable the css has selector in the config first.

    st.write('''<style>
    [data-testid="stHorizontalBlock"]:has(div.PortMarker) [data-testid="stMarkdownContainer"] p { 
        margin: 0px 0px 0.2rem; 
        color: #ff0000;
    }        
    </style>''', unsafe_allow_html=True)


    with st.container():
        INcol1, INcol2 = st.columns(2) 
        with INcol1:
                st.write('Test 1')
                st.write("""<div class='PortMarker'/>""", unsafe_allow_html=True)
        with INcol2:
                st.write('Test 2')
1 Like