Fully functional Themes Page including custom themes feature

I see this , this and this issues and finally come with a FINAL solution for all of these kind of ‘Theme’ related issues by creating & sharing a fully functional page.

Note that this is a full page example, you need to create a pages folder inside your project root and create a <page_name>.py file (for example themes.py ) and you should paste this code there.

Outputs

Default Dark & Light Themes (you can change your dark and light themes inside the source code and update the db/themes.json with new values)

Let’s create a custom theme:

I save this custom theme by naming it ‘green’.

db/themes.json

Show Code
import streamlit as st
import json
import os


def init():
    if "db" not in os.listdir("../"):
        os.mkdir("../db")
    if "themes.json" not in os.listdir("../db"):
        file = open("../db/themes.json", "w")
        theme = {
            "dark": {
                "bg": "black",
                "bg2": "#5591f5",
                "pr": "#c98bdb",
                "text": "white"
            },
            "light": {
                "bg": "white",
                "bg2": "#82E1D7",
                "pr": "#5591f5",
                "text": "black"
            },
            "custom": {
                "bg": "#bbbabb",
                "bg2": "#a29595",
                "pr": "#c3a07d",
                "text": "#000000"
            }
        }
        json.dump(theme, file, indent=4)
        file.close()
    if "themes" not in st.session_state.keys():
        st.session_state["themes"] = json.load(open("../db/themes.json", "r"))


def check_different(elem1, elem2):
    min = elem1 if len(elem1) < len(elem2) else elem2
    other = elem2 if min == elem1 else elem1
    for i, elem in enumerate(list(min)):
        if elem != list(other)[i]:
            return True
    return False


def theme_io():
    theme_json = st.file_uploader("load a theme JSON")

    cols = st.columns([4, 2, 4])
    with cols[0]:
        if st.button("import"):
            if theme_json is not None:
                theme["custom"] = json.load(theme_json)
                st.success("imported!")
            else:
                st.error("please upload a theme file first.")
    with cols[2]:
        st.download_button(
            label="Export",
            data=json.dumps(st.session_state["themes"]["custom"], indent=4),
            file_name='theme.json',
            mime='application/json',
        )


def custom_theme(theme):
    new_theme = st.session_state["themes"]["custom"].copy()
    new_theme["bg"] = st.color_picker("background", theme["custom"]["bg"])
    new_theme["bg2"] = st.color_picker("background secondary", theme["custom"]["bg2"])
    new_theme["pr"] = st.color_picker("primary", theme["custom"]["pr"])
    new_theme["text"] = st.color_picker("text color", theme["custom"]["text"])
    cols = st.columns([3, 1])
    with cols[0]:
        theme_name = st.text_input("Theme name")
        if st.button("save custom theme"):
            if len(theme_name) > 0:
                st.session_state["themes"].update({theme_name: new_theme})
                json.dump(st.session_state["themes"], open("../db/themes.json", "w"), indent=4)
                st.rerun()
            else:
                st.error("please enter a valid theme name!")

    with cols[1]:
        st.write("Preview:")
        st.button("Primary", type="primary")
        st.button("Secondary", type="secondary")

    return new_theme


def theme_selection():
    theme = st.session_state["themes"].copy()
    options = st.session_state["themes"].keys()
    columns = st.columns([2, 1])
    with columns[1]:
        theme_io()  # import export

    with columns[0]:
        st.selectbox("theme", options=options, key="themebutton")
        selected_theme = st.session_state["themebutton"]

        placeholder = st.empty()

        new_theme = st.session_state["themes"]["custom"].copy()
        if selected_theme == "custom":
            new_theme = custom_theme(theme)  # custom themes

        is_changed = check_different(new_theme.items(), st.session_state["themes"]["custom"].items())

        if is_changed:
            theme["custom"] = new_theme
            st.session_state["themes"]["custom"] = new_theme

        set_btn = False
        if selected_theme != "custom":
            set_btn = placeholder.button("set")

        if (set_btn) or is_changed:
            st._config.set_option(f'theme.base', "dark")
            st._config.set_option(f'theme.backgroundColor', theme[selected_theme]["bg"])
            st._config.set_option(f'theme.secondaryBackgroundColor', theme[selected_theme]["bg2"])
            st._config.set_option(f'theme.primaryColor', theme[selected_theme]["pr"])
            st._config.set_option(f'theme.textColor', theme[selected_theme]["text"])

            st.rerun()


def main():
    init()
    theme_selection()


if __name__ == '__main__':
    main()
3 Likes

I tried the application and it was great. In practical applications, I will add the function of deleting themes.:blush:

1 Like

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