Using the float component in combination with chat input to add chat buttons. Avoiding that the chat text hides behind the buttons

Hello,

following this discussion, I wanted to add some context buttons close to the chat input for our app. I tried with a single toggle button for the camera and it positioned it nicely, like this:

The problem is when there is enough text to fill up the entire chat area and then the text of the chat hides behind the icons float, specially in mobile (and we need the site to work well in mobile). Like this:

now the screenshot in mobile, the last line of the text is hidden behind the icon bar so there’s no conclusion to the story :frowning:

I’m asking @bouzidanas if his great floating component could solve this issue. Or if someone else has any idea how to?

I ran into the same problem in the discussion you linked (presented by @pierrelouisbescond) and my quick and dirty solution was just to add 1 or 2 lines of st.write("") right after all the chat messages. This adds blank space at the end that will be covered up instead of the text. It works great while using streamlit functions. I would recommend adding a comment explaining why the lines are there in case you forget in the future or to inform someone else reading the code.

A cleaner solution imo would be to add padding to the bottom of the container with the scrollbar or alternatively, setting max-height to calc(100vh - HEIGHT_OF_PROMPT_AND_BUTTONS) where HEIGHT_OF_PROMPT_AND_BUTTONS is just some fixed value should provide the necessary vertical shift. Doing the latter has the added benefit of shifting the end of the scroll bar up to where the chat portion ends which I think is probably a cleaner look.

I would try the simple solution first and then maybe later work on the CSS approach.


On another note, I noticed that on mobile, your buttons are much higher up. What is the CSS you used to place the buttons? Did you use percentage or some window scale units?

2 Likes

Hey! thanks for the answer.

I tried adding the st.write lines but it doesn’t seem to be working. For the buttons I used this:

        chat_buttons.float(
            "bottom: 6rem;background-color: var(--default-backgroundColor); padding-top: 1rem;"
        )

I was wondering also why it is so high

Scratch that, the st.writes worked fine! Although I needed 8 because the button is so high in mobile and it looks a bit weird. I notice that if I manipulate the screen size with the developer tools on the browser, the button is mostly positioned similar than on the desktop until some point and then it jumps upwards suddenly. Any idea how to handle that?

Ohhh…I just thought of a reason why! It may be the width of the button array. If the screen width gets too small, the buttons wrap to the next line.

streamlit-chat-float

Try adding max-width: 80vw to the css in the float function. Oh and disable wrap in the css too.

Also, if you need to put a bunch of st.write calls, you can use an st.container with height argument set instead of all the st.writes

1 Like

Amazing, you’re such a life saver! So many good tips

I tried disabling wrap like this

        chat_buttons.float(
            "bottom: 6.5rem;background-color: var(--default-backgroundColor); "
            "padding-top: 1rem; max-width: 80vw; white-space: nowrap"
        )

but it didn’t seem to work

1 Like

I think it is flex-wrap judging by what devtools is showing me.

Can you provide another pic of what the css you added changed?


After playing around a little bit. You can fix the problem with CSS but it is very involved because you have to basically undo what Streamlit does when the screen gets to mobile size, but only undo it for the buttons and not everything else. Streamlit turns columns into rows when the screen gets small in width and then it makes each item full width.

One trick that might work since you dont have many buttons is to put the buttons in another column like this:

[ [ button 1 , button 2 ] , [ empty space , empty space, empty space ] ]

So basically an outter 2 columns with the first column containing the buttons (in another 2 column arrangment) and the second outer column contains empty inner columns.

There might be an even easier solution, but I cant know until I see your column setup for the buttons

I take back the idea about the nested columns. It doesnt work.

Yeah, so basically, the forceful way is to undo the streamlit css for small screens and add your own and making sure the changes only target the buttons and doesnt effect the rest. Its all doable but much more involved.

I cant think of any other way atm

Here are the pics of how it looks now. I’m using this code

        chat_buttons.float(
            "bottom: 6.5rem;background-color: var(--default-backgroundColor); "
            "padding-top: 1rem; max-width: 80vw; flex-wrap: nowrap"
        )

As you can see the icon moves to the left in the mobile version which probably means stacking. I was wondering if I could detect vertical stacking happening then maybe I can arrange the icons inside a popover, like they asked here

Anyways thanks so much for all the help, you’ve helped me a lot.

Hey @Odrec,

I just saw your message. You caught me at a good time. I just had my coffee and some time to clear my thoughts.

I found a better solution to your problem. I realized that with float functions we can add any CSS we want to containers and that means we can position the contents of the containers wherever we want! So why do we even need columns to position buttons next to each other when we can use a container for each button and literally position them next to each other!! That way, we completely avoid the column behavior when the screen width is small!

Sure enough it works!

Instead of in a column. Put each button in a container.

con1 = st.container()
con2 = st.container()

with con1:
    float_parent("bottom: 6.9rem;background-color: var(--default-backgroundColor); padding-top: 0.9rem;")
    
    if st.button(...):
        # Do stuff when first button clicked

with con2:
    float_parent("margin-left: 6rem; bottom: 6.9rem;background-color: var(--default-backgroundColor); padding-top: 0.9rem;")

    if st.button(...):
        # Do stuff when second button clicked

All I did was float each button container separately and make sure to move the second button to the right by adding margin-left to the CSS and setting the value of the margin to be the width of the first button.

If I were to add a 3rd button, then I would set the left margin to the combined width of the first 2 buttons.

Now, there is no funny business with the media query changing the layout!

2 Likes

This is amazing! It works perfectly to avoid the button stacking. Thanks so much for the help @bouzidanas.

I have been fiddling with this and I’m very satisfied with the result.

The only thing I was wondering. Right now it is easy to position all the buttons starting on the left of the screen because we have margin-left 0 there and then start increasing that gradually for the rest of the bottoms like you suggested. Do you know a trick to dynamically place a button on the right side of the screen (no matter the screen size), like where the chat input ends? I would like to position the camera icon there and other controls like delete chat on the left side but I haven’t figured out how to.