Streamlit-elements issue: Pie Chart Tooltip text color not showing in dark mode

When streamlit app is in dark mode, the tooltip of the nivo Pie chart displays white text on white background instead of black (or any discernible color) on white or white text on dark background.

What I tried

  • I tried to add a tooltip by examining the live nivo Pie chart react code but I couldn’t implement it. (look for tooltip/custom tooltip example)
  • I tried using the CSS hack injected through st.markdown() but it doesn’t work in case of an iframe, which is the case for streamlit-elements nivo pie chart, its not possible to reach the element by class or id to affect the text color.

Preview

streamlit-elements-nivo-pie-chart-tooltip

Code

import streamlit as st
from streamlit_elements import elements, mui, html
from streamlit_elements import nivo

with elements("nivo_pie_chart"):

    DATA = [
    { "id": "css", "label": "css", "value": 58, "color": "hsl(309, 70%, 50%)" },
    { "id": "php", "label": "php", "value": 582, "color": "hsl(229, 70%, 50%)" },
    { "id": "ruby", "label": "ruby", "value": 491, "color": "hsl(78, 70%, 50%)" },
    { "id": "scala", "label": "scala", "value": 254, "color": "hsl(278, 70%, 50%)" },
    { "id": "stylus", "label": "stylus", "value": 598, "color": "hsl(273, 70%, 50%)" }
    ]

    with mui.Box(sx={"height": 500}):
        nivo.Pie(
            data=DATA,
            margin={"top": 100, "right": 100, "bottom": 100, "left": 100},
            innerRadius=0.5,
            padAngle=0.7,
            cornerRadius=3,
            activeOuterRadiusOffset=8,
            borderWidth=1,
            borderColor={"from": "color", "modifiers": [["darker", 0.8]]},
            arcLinkLabelsSkipAngle=10,
            arcLinkLabelsTextColor="grey",
            arcLinkLabelsThickness=2,
            arcLinkLabelsColor={"from": "color"},
            arcLabelsSkipAngle=10,
            arcLabelsTextColor={"from": "color", "modifiers": [["darker", 4]]},
            defs=[
                {
                    "id": "dots",
                    "type": "patternDots",
                    "background": "inherit",
                    "color": "rgba(255, 255, 255, 0.3)",
                    "size": 4,
                    "padding": 1,
                    "stagger": True,
                },
                {
                    "id": "lines",
                    "type": "patternLines",
                    "background": "inherit",
                    "color": "rgba(255, 255, 255, 0.3)",
                    "rotation": -45,
                    "lineWidth": 6,
                    "spacing": 10,
                },
            ],
            fill=[
                {"match": {"id": "ruby"}, "id": "dots"},
                {"match": {"id": "php"}, "id": "dots"},
                {"match": {"id": "scala"}, "id": "lines"},
                {"match": {"id": "css"}, "id": "dots"},
                {"match": {"id": "stylus"}, "id": "lines"},
            ],
            legends=[
                {
                    "anchor": "bottom",
                    "direction": "row",
                    "justify": False,
                    "translateX": 0,
                    "translateY": 56,
                    "itemsSpacing": 0,
                    "itemWidth": 100,
                    "itemHeight": 18,
                    "itemTextColor": "#999",
                    "itemDirection": "left-to-right",
                    "itemOpacity": 1,
                    "symbolSize": 18,
                    "symbolShape": "circle",
                    "effects": [
                        {"on": "hover", "style": {"itemTextColor": "#000"}}
                    ],
                }
            ],
        )

Config

  • Streamlit 1.27.2
  • Streamlit-elements 0.1.0
  • Python 3.10.2
1 Like

Hi @IndigoWizard :wave:

Thank you for your question!

I don’t think it’s possible to achieve what you’re after for charts depending on the Streamlit Elements component.

I’m cc’ing @okld, who’s the creator of this library and may be able to confirm or help with a workaround.

Best,
Charly

1 Like

having the same issue here, also tried the custom tooltip to workaround this, but also could not implement it. Although the default tooltip would be enough for me if it worked properly in dark mode. I am glad i found this thread because I didnt realize it worked in light mode, i thought there was something wrong in my code

I think I found the solution. Try to add the following to your mui.Box. it worked for me

with mui.Box(sx={“height”: 500}):

        # Rest of code
        # Update the tooltip text color
        theme={
            "tooltip": {
                "container": {
                    "color": "#999"  # Change this to the desired text color
                }
            }
        }
    )
2 Likes

Interesting workaround, @joseluizmm!

@IndigoWizard, would it work for what you’re after?

1 Like

@Charly_Wargnier @joseluizmm
Sorry for not updating the thread but my computer was down so I couldn’t share any advancements or what I found so far. Yes, I did find a similar solution to affect the styling properties of the tooltip that I tried to incorporate in my app, but it seems it’s not possible to change the stayling based on the current theme.

I tried to get the current theme mode of the app to change the tooltip styling accordingly from the app’s current session state but it appears that it’s not possible in streamlit to access the current theme info (see: Detect if the app is in Light/Dark mode at runtime · Issue #5009 · streamlit/streamlit · GitHub) (or it’s just me who don’t know how to implement it if it’s possible in streamlit now).
So it works only with the “local” theme change but not the app’s actual theme change, making the tooltip styling kinda stuck on a specific color (half-measure but not that bad).

code sample

import streamlit as st
from streamlit_elements import elements, mui
from streamlit_elements import nivo


# setting a session state variable to track the current mode
if 'mode' not in st.session_state:
    st.session_state.mode = "light"

# function to change tooltip colors based on app theme
def theme_check():
    if st.session_state.mode == "dark":
        return {
            "background": "#0e1117",  # Mui box dark background
            "textColor": "#fafafa",
            "tooltip": {
                "container": {
                    "background": "#262730",  # Dark tooltip background
                    "color": "#fff",  # white text color in dark mode
                    "border": "solid 3px #F0F2F6",
                    "border-radius": "8px",
                    "padding": 5,
                }
            }
        }
    else:
        return {
            "background": "#fff",  # Light tooltip background
            "textColor": "#262730",
            "tooltip": {
                "container": {
                    "background": "#F0F2F6",  # Light background for tooltip
                    "color": "#31333F",  # Text color in light mode
                    "border": "solid 3px #262730",
                    "border-radius": "8px",

                }
            }
        }

# toggle mode button
if st.button("Toggle Mode"):
    st.session_state.mode = "dark" if st.session_state.mode == "light" else "light"

# data
DATA_PIE = [
    { "id": "css", "label": "css", "value": 58, "color": "hsl(309, 70%, 50%)" },
    { "id": "php", "label": "php", "value": 582, "color": "hsl(229, 70%, 50%)" },
    { "id": "ruby", "label": "ruby", "value": 491, "color": "hsl(78, 70%, 50%)" },
    { "id": "scala", "label": "scala", "value": 254, "color": "hsl(278, 70%, 50%)" },
    { "id": "stylus", "label": "stylus", "value": 598, "color": "hsl(273, 70%, 50%)" }
]

# nivo element with nivo pie chart
with elements("nivo_pie_chart"):
    with mui.Box(sx={"height": 500}):
        nivo.Pie(
            data=DATA_PIE,
            margin={"top": 100, "right": 100, "bottom": 100, "left": 100},
            innerRadius=0.5,
            padAngle=0.7,
            cornerRadius=3,
            activeOuterRadiusOffset=8,
            borderWidth=1,
            borderColor={"from": "color", "modifiers": [["darker", 0.8]]},
            arcLinkLabelsSkipAngle=10,
            arcLinkLabelsTextColor="grey",
            arcLinkLabelsThickness=2,
            arcLinkLabelsColor={"from": "color"},
            arcLabelsSkipAngle=10,
            arcLabelsTextColor={"from": "color", "modifiers": [["darker", 4]]},
            theme=theme_check(), # Render the nivo.Pie component with the defined theme
            defs=[
                {
                    "id": "dots",
                    "type": "patternDots",
                    "background": "inherit",
                    "color": "rgba(255, 255, 255, 0.3)",
                    "size": 4,
                    "padding": 1,
                    "stagger": True,
                },
                {
                    "id": "lines",
                    "type": "patternLines",
                    "background": "inherit",
                    "color": "rgba(255, 255, 255, 0.3)",
                    "rotation": -45,
                    "lineWidth": 6,
                    "spacing": 10,
                },
            ],
            fill=[
                {"match": {"id": "ruby"}, "id": "dots"},
                {"match": {"id": "php"}, "id": "dots"},
                {"match": {"id": "scala"}, "id": "lines"},
                {"match": {"id": "css"}, "id": "dots"},
                {"match": {"id": "stylus"}, "id": "lines"},
            ],
            legends=[ # legend bellow the pie chart
                {
                    "anchor": "bottom",
                    "direction": "row",
                    "justify": False,
                    "translateX": 0,
                    "translateY": 56,
                    "itemsSpacing": 0,
                    "itemWidth": 100,
                    "itemHeight": 18,
                    "itemTextColor": "#999",
                    "itemDirection": "left-to-right",
                    "itemOpacity": 1,
                    "symbolSize": 18,
                    "symbolShape": "circle",
                    "effects": [
                        {"on": "hover", "style": {"itemTextColor": "#000"}}
                    ],
                }
            ],
        )

preview

nivo-theme-test-0

Also, the styling of the nivo chart in streamlit-elements isn’t obvious, to find the current solution I just relied on luck tbh, later I found some guidance with this issue Theming for components · Issue #308 · plouc/nivo · GitHub and tried to guess the rest from there as the nivo documentation don’t cover everything or is very ambiguous about things (same applies to streamlit-elements package, which is not maintained, the whole GitHub profile is inactive since over a year so the package and other Okld streamlit packages / repos don’t offer any help).

Anyway, I"ll post this sample for anyone who might need some help in the future with the package, you can consider it as a solution I guess. (please ignore the ugly styling, its just for the purpose of demoing whats possible)

code sample:

import streamlit as st
from streamlit_elements import elements, mui
from streamlit_elements import nivo

# Define your data
DATA_PIE = [
    { "id": "css", "label": "css", "value": 58, "color": "hsl(309, 70%, 50%)" },
    { "id": "php", "label": "php", "value": 582, "color": "hsl(229, 70%, 50%)" },
    { "id": "ruby", "label": "ruby", "value": 491, "color": "hsl(78, 70%, 50%)" },
    { "id": "scala", "label": "scala", "value": 254, "color": "hsl(278, 70%, 50%)" },
    { "id": "stylus", "label": "stylus", "value": 598, "color": "hsl(273, 70%, 50%)" }
]

# Render the nivo.Pie component with the defined theme
with elements("nivo_pie_chart"):
    with mui.Box(sx={"height": 500}):
        nivo.Pie(
            data=DATA_PIE,
            margin={"top": 100, "right": 100, "bottom": 100, "left": 100},
            innerRadius=0.5,
            padAngle=0.7,
            cornerRadius=3,
            activeOuterRadiusOffset=8,
            borderWidth=1,
            borderColor={"from": "color", "modifiers": [["darker", 0.8]]},
            arcLinkLabelsSkipAngle=10,
            arcLinkLabelsTextColor="grey",
            arcLinkLabelsThickness=2,
            arcLinkLabelsColor={"from": "color"},
            arcLabelsSkipAngle=10,
            arcLabelsTextColor={"from": "color", "modifiers": [["darker", 4]]},
            defs=[
                {
                    "id": "dots",
                    "type": "patternDots",
                    "background": "inherit",
                    "color": "rgba(255, 255, 255, 0.3)",
                    "size": 4,
                    "padding": 1,
                    "stagger": True,
                },
                {
                    "id": "lines",
                    "type": "patternLines",
                    "background": "inherit",
                    "color": "rgba(255, 255, 255, 0.3)",
                    "rotation": -45,
                    "lineWidth": 6,
                    "spacing": 10,
                },
            ],
            fill=[
                {"match": {"id": "ruby"}, "id": "dots"},
                {"match": {"id": "php"}, "id": "dots"},
                {"match": {"id": "scala"}, "id": "lines"},
                {"match": {"id": "css"}, "id": "dots"},
                {"match": {"id": "stylus"}, "id": "lines"},
            ],
            theme={
                # "background": "#FFFFFF",  # background color of the whole chart box
                "tooltip": {
                    "container": { # container within the tooltip
                        "background": "white",  # background of the tooltip inside container
                        # "color": "red",      # text color within the container
                        "fontSize": 20,
                        "font-family": "sans-serif",
                        "padding": 2,
                        "border": "solid 4px blue",
                        "border-radius": 8
                    },
                    "basic": { # the box within the container within the tooltip
                        "whiteSpace": "pre",
                        "display": "flex",
                        "flex-direction": "row",
                        "alignItems": "center",
                        "justify-content": "space-around",
                        "background": "linear-gradient(90deg, rgba(71,53,156,1) 0%, rgba(103,53,156,1) 52%, rgba(156,53,150,1) 100%)",
                        "margin": 3,
                        "padding": 5,
                        "width": 160,
                        "height": 50,
                        "color": "white",
                    },
                },
                "legends": { # Related to Legends options bellow
                    "text": {
                        "fontSize": "1.1em",
                    }
                },
            },
            legends=[
                {
                    "anchor": "bottom",
                    "direction": "row",
                    "justify": False,
                    "translateX": 0,
                    "translateY": 90,
                    "itemsSpacing": 0,
                    "itemWidth": 100,
                    "itemHeight": 18,
                    "itemTextColor": "#999",
                    "itemDirection": "left-to-right",
                    "itemOpacity": 1,
                    "symbolSize": 20,
                    "symbolShape": "circle",
                    "effects": [
                        {"on": "hover", "style": {"itemTextColor": "#fafafa"}}
                    ],
                }
            ],
        )

Note:

  • Certain options are weird to style, like the legend option has text property that needs to be styled in theme instead of legends but the hover effect on text is styled within legends…
  • There is no indication in the docs on what “basic” actually is for, I just fiddled with it and it appears to affect the inside container within the container within the tooltip…

preview

nivo-theme-test-1

Hey @IndigoWizard - Glad to see that you’ve found a solution to your issue, albeit a partial one!

Thank you also for sharing the code; hopefully, it will benefit others facing similar challenges.

Best,
Charly

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