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鈥檛 implement it. (look for tooltip/custom tooltip example)
  • I tried using the CSS hack injected through st.markdown() but it doesn鈥檛 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鈥檛 think it鈥檚 possible to achieve what you鈥檙e after for charts depending on the Streamlit Elements component.

I鈥檓 cc鈥檌ng @okld, who鈥檚 the creator of this library and may be able to confirm or help with a workaround.

Best,
Charly

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={鈥渉eight鈥: 500}):

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

Interesting workaround, @joseluizmm!

@IndigoWizard, would it work for what you鈥檙e after?

@Charly_Wargnier @joseluizmm
Sorry for not updating the thread but my computer was down so I couldn鈥檛 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鈥檚 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鈥檚 current session state but it appears that it鈥檚 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鈥檚 just me who don鈥檛 know how to implement it if it鈥檚 possible in streamlit now).
So it works only with the 鈥渓ocal鈥 theme change but not the app鈥檚 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鈥檛 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鈥檛 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鈥檛 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 鈥渂asic鈥 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鈥檝e 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.