How to Add Custom Themes to GPT Academic: A Complete Developer Guide
To add custom themes to GPT Academic, create a Python module in the themes/ directory that implements the adjust_theme() function, add the theme name to the AVAIL_THEMES list in config.py, and register the import path inside the load_dynamic_theme() dispatcher in themes/theme.py.
GPT Academic provides a modular theme system that allows developers to customize the visual appearance of the Gradio-based interface. Whether you want to adjust color schemes, inject custom CSS, or create entirely new visual layouts, the theme architecture in the binary-husky/gpt_academic repository provides a clean extension point without modifying core application code.
Understanding the Theme Architecture
The theme system follows a dispatcher pattern that loads configurations dynamically at startup. When the application initializes, it reads the THEME variable from config.py (around line 93) to determine which visual style to apply. This value is passed to the load_dynamic_theme() function in themes/theme.py (lines 17-39), which acts as a central router importing the appropriate theme module based on the name provided.
Each theme module must expose two critical components: an adjust_theme() function that returns a Gradio theme object (gr.themes.*), and an advanced_css string containing additional stylesheet rules. The UI constructs the theme selection dropdown from the AVAIL_THEMES list defined in config.py, as implemented in themes/gui_toolbar.py (line 25).
Step-by-Step Guide to Creating a Custom Theme
Step 1: Create the Theme Module
Create a new Python file inside the themes/ directory (e.g., themes/mytheme.py). This module must define the adjust_theme() function and expose the advanced_css variable:
# themes/mytheme.py
import os
import gradio as gr
from toolbox import get_conf
from themes.common import get_common_html_javascript_code
theme_dir = os.path.dirname(__file__)
def adjust_theme():
"""
Returns a Gradio theme object with custom color schemes.
"""
# Initialize with a base theme and customize hues
set_theme = gr.themes.Default(
primary_hue=gr.themes.utils.colors.purple,
secondary_hue=gr.themes.utils.colors.orange,
)
# Inject required JavaScript for GPT Academic functionality
js = get_common_html_javascript_code()
if not hasattr(gr, "RawTemplateResponse"):
gr.RawTemplateResponse = gr.routes.templates.TemplateResponse
orig_template = gr.RawTemplateResponse
def wrapped_template(*args, **kwargs):
res = orig_template(*args, **kwargs)
res.body = res.body.replace(b"</html>", f"{js}</html>".encode())
res.init_headers()
return res
gr.routes.templates.TemplateResponse = wrapped_template
return set_theme
# Load accompanying CSS file
with open(os.path.join(theme_dir, "mytheme.css"), "r", encoding="utf-8") as f:
advanced_css = f.read()
Place a corresponding CSS file at themes/mytheme.css:
/* themes/mytheme.css */
body {
background-color: #fafafa;
}
.gradio-container {
font-family: "Helvetica", sans-serif;
}
Step 2: Register the Theme in config.py
Add your theme name to the AVAIL_THEMES list in config.py so it appears in the user interface dropdown:
# config.py (around line 90)
AVAIL_THEMES = [
"Default",
"Chuanhu-Small-and-Beautiful",
"High-Contrast",
"Gstaff/Xkcd",
"NoCrypt/Miku",
"MyCustomTheme", # ← Your new theme
]
# Optionally set as default
THEME = "MyCustomTheme"
Step 3: Wire the Theme Loader
Update the load_dynamic_theme() function in themes/theme.py to import your module when the theme name matches:
# themes/theme.py - inside load_dynamic_theme()
elif THEME == "MyCustomTheme":
from .mytheme import adjust_theme, advanced_css
theme_declaration = "" # Optional HTML banner
Step 4: Add an Optional Banner
To display a custom header when your theme is active, set the theme_declaration variable in the same code block:
theme_declaration = '<h2 align="center" class="small">My Custom Theme</h2>'
Step 5: Restart the Application
Execute python main.py to reload the configuration. Your custom theme will now appear in the dropdown menu and apply your defined colors, fonts, and CSS rules when selected.
Key Source Files and Implementation Details
themes/theme.py: Central dispatcher containingload_dynamic_theme()that routes theme names to their respective modules based on theTHEMEconfiguration variable.config.py: DefinesAVAIL_THEMES(the list populating the UI dropdown) and the defaultTHEMEvariable read at startup.themes/gui_toolbar.py: Builds the theme selection interface using theAVAIL_THEMESconfiguration.themes/common.py: Providesget_common_html_javascript_code(), a utility function that returns the shared JavaScript required for core GPT Academic functionality.themes/default.py,themes/green.py: Reference implementations demonstrating standard theme structures.
Summary
- Create a module in
themes/implementingadjust_theme()returning agr.themes.*object and exposingadvanced_css. - Register the name in
config.py'sAVAIL_THEMESlist to make it selectable in the UI. - Update the dispatcher in
themes/theme.pyto import your module when the theme name matches. - Inject common JavaScript via
get_common_html_javascript_code()to ensure compatibility with GPT Academic's core features. - Restart the server to load the new configuration and make the theme available to users.
Frequently Asked Questions
Where is the theme configuration stored in GPT Academic?
The active theme is controlled by the THEME variable defined in config.py (line 93), while the available options are listed in the AVAIL_THEMES array. The UI dropdown is constructed from this array in themes/gui_toolbar.py.
What functions must a custom theme module implement?
Every theme module must expose an adjust_theme() function that returns a Gradio theme instance and an advanced_css string variable containing CSS rules. The adjust_theme() function should also inject required JavaScript using the pattern shown in themes/common.py.
Can I use custom CSS and JavaScript in my theme?
Yes. Store CSS in a separate file (e.g., mytheme.css) and load it into the advanced_css variable. For JavaScript, use the get_common_html_javascript_code() utility from themes/common.py and inject it by wrapping gr.routes.templates.TemplateResponse as demonstrated in existing theme implementations.
Why doesn't my new theme appear in the dropdown menu?
If your theme is missing from the dropdown, verify that you added the exact name to AVAIL_THEMES in config.py and included the corresponding elif branch in themes/theme.py's load_dynamic_theme() function. Both steps are required for the theme to be recognized and displayed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →