CoolFace
Apppublic

whitphx/streamlit-theme-editor

sourceHugging Facemitupdated 3y agoView on Hugging Face
2likes
app.py185 linesDownload Raw Back to root
1import colorsys2 3import streamlit as st4 5import fragments6import util7from util import ThemeColor8 9 10preset_colors: list[tuple[str, ThemeColor]] = [11    ("Default light", ThemeColor(12            primaryColor="#ff4b4b",13            backgroundColor="#ffffff",14            secondaryBackgroundColor="#f0f2f6",15            textColor="#31333F",16        )),17    ("Default dark", ThemeColor(18            primaryColor="#ff4b4b",19            backgroundColor="#0e1117",20            secondaryBackgroundColor="#262730",21            textColor="#fafafa",22    ))23]24 25theme_from_initial_config = util.get_config_theme_color()26if theme_from_initial_config:27    preset_colors.append(("From the config", theme_from_initial_config))28 29default_color = preset_colors[0][1]30 31 32def sync_rgb_to_hls(key: str):33    # HLS states are necessary for the HLS sliders.34    rgb = util.parse_hex(st.session_state[key])35    hls = colorsys.rgb_to_hls(rgb[0], rgb[1], rgb[2])36    st.session_state[f"{key}H"] = round(hls[0] * 360)37    st.session_state[f"{key}L"] = round(hls[1] * 100)38    st.session_state[f"{key}S"] = round(hls[2] * 100)39 40 41def sync_hls_to_rgb(key: str):42    h = st.session_state[f"{key}H"]43    l = st.session_state[f"{key}L"]44    s = st.session_state[f"{key}S"]45    r, g, b = colorsys.hls_to_rgb(h / 360, l / 100, s / 100)46    st.session_state[key] = f"#{round(r * 255):02x}{round(g * 255):02x}{round(b * 255):02x}"47 48 49def set_color(key: str, color: str):50    st.session_state[key] = color51    sync_rgb_to_hls(key)52 53 54if 'preset_color' not in st.session_state or 'backgroundColor' not in st.session_state or 'secondaryBackgroundColor' not in st.session_state or 'textColor' not in st.session_state:55    set_color('primaryColor', default_color.primaryColor)56    set_color('backgroundColor', default_color.backgroundColor)57    set_color('secondaryBackgroundColor', default_color.secondaryBackgroundColor)58    set_color('textColor', default_color.textColor)59 60 61st.title("Streamlit color theme editor")62 63 64def on_preset_color_selected():65    _, color = preset_colors[st.session_state.preset_color]66    set_color('primaryColor', color.primaryColor)67    set_color('backgroundColor', color.backgroundColor)68    set_color('secondaryBackgroundColor', color.secondaryBackgroundColor)69    set_color('textColor', color.textColor)70 71 72st.selectbox("Preset colors", key="preset_color", options=range(len(preset_colors)), format_func=lambda idx: preset_colors[idx][0], on_change=on_preset_color_selected)73 74if st.button("🎨 Generate a random color scheme 🎲"):75    primary_color, text_color, basic_background, secondary_background = util.generate_color_scheme()76    set_color('primaryColor', primary_color)77    set_color('backgroundColor', basic_background)78    set_color('secondaryBackgroundColor', secondary_background)79    set_color('textColor', text_color)80 81 82def color_picker(label: str, key: str, default_color: str, l_only: bool) -> None:83    col1, col2 = st.columns([1, 3])84    with col1:85        color = st.color_picker(label, key=key, on_change=sync_rgb_to_hls, kwargs={"key": key})86    with col2:87        r,g,b = util.parse_hex(default_color)88        h,l,s = colorsys.rgb_to_hls(r,g,b)89        if l_only:90            if f"{key}H" not in st.session_state:91                st.session_state[f"{key}H"] = round(h * 360)92        else:93            st.slider(f"H for {label}", key=f"{key}H", min_value=0, max_value=360, value=round(h * 360), format="%d°", label_visibility="collapsed", on_change=sync_hls_to_rgb, kwargs={"key": key})94 95        st.slider(f"L for {label}", key=f"{key}L", min_value=0, max_value=100, value=round(l * 100), format="%d%%", label_visibility="collapsed", on_change=sync_hls_to_rgb, kwargs={"key": key})96 97        if l_only:98            if f"{key}S" not in st.session_state:99                st.session_state[f"{key}S"] = round(s * 100)100        else:101            st.slider(f"S for {label}", key=f"{key}S", min_value=0, max_value=100, value=round(s * 100), format="%d%%", label_visibility="collapsed", on_change=sync_hls_to_rgb, kwargs={"key": key})102 103    return color104 105 106primary_color = color_picker('Primary color', key="primaryColor", default_color=default_color.primaryColor, l_only=True)107text_color = color_picker('Text color', key="textColor", default_color=default_color.textColor, l_only=True)108background_color = color_picker('Background color', key="backgroundColor", default_color=default_color.backgroundColor, l_only=True)109secondary_background_color = color_picker('Secondary background color', key="secondaryBackgroundColor", default_color=default_color.secondaryBackgroundColor, l_only=True)110 111 112st.header("WCAG contrast ratio")113st.markdown("""114Check if the color contrasts of the selected colors are enough to the WCAG guidelines recommendation.115For the details about it, see some resources such as the [WCAG document](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) or the [MDN page](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast).""")116 117def synced_color_picker(label: str, value: str, key: str):118    def on_change():119        st.session_state[key] = st.session_state[key + "2"]120        sync_rgb_to_hls(key)121    st.color_picker(label, value=value, key=key + "2", on_change=on_change)122 123col1, col2, col3 = st.columns(3)124with col2:125    synced_color_picker("Background color", value=background_color, key="backgroundColor")126with col3:127    synced_color_picker("Secondary background color", value=secondary_background_color, key="secondaryBackgroundColor")128 129col1, col2, col3 = st.columns(3)130with col1:131    synced_color_picker("Primary color", value=primary_color, key="primaryColor")132with col2:133    fragments.contrast_summary("Primary/Background", primary_color, background_color)134with col3:135    fragments.contrast_summary("Primary/Secondary background", primary_color, secondary_background_color)136 137col1, col2, col3 = st.columns(3)138with col1:139    synced_color_picker("Text color", value=text_color, key="textColor")140with col2:141    fragments.contrast_summary("Text/Background", text_color, background_color)142with col3:143    fragments.contrast_summary("Text/Secondary background", text_color, secondary_background_color)144 145 146st.header("Config")147 148st.subheader("Config file (`.streamlit/config.toml`)")149st.code(f"""150[theme]151primaryColor="{primary_color}"152backgroundColor="{background_color}"153secondaryBackgroundColor="{secondary_background_color}"154textColor="{text_color}"155""", language="toml")156 157st.subheader("Command line argument")158st.code(f"""159streamlit run app.py \\160    --theme.primaryColor="{primary_color}" \\161    --theme.backgroundColor="{background_color}" \\162    --theme.secondaryBackgroundColor="{secondary_background_color}" \\163    --theme.textColor="{text_color}"164""")165 166 167if st.checkbox("Apply theme to this page"):168    st.info("Select 'Custom Theme' in the settings dialog to see the effect")169 170    def reconcile_theme_config():171        keys = ['primaryColor', 'backgroundColor', 'secondaryBackgroundColor', 'textColor']172        has_changed = False173        for key in keys:174            if st._config.get_option(f'theme.{key}') != st.session_state[key]:175                st._config.set_option(f'theme.{key}', st.session_state[key])176                has_changed = True177        if has_changed:178            st.experimental_rerun()179 180    reconcile_theme_config()181 182    fragments.sample_components("body")183    with st.sidebar:184        fragments.sample_components("sidebar")185