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 containing load_dynamic_theme() that routes theme names to their respective modules based on the THEME configuration variable.
  • config.py: Defines AVAIL_THEMES (the list populating the UI dropdown) and the default THEME variable read at startup.
  • themes/gui_toolbar.py: Builds the theme selection interface using the AVAIL_THEMES configuration.
  • themes/common.py: Provides get_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/ implementing adjust_theme() returning a gr.themes.* object and exposing advanced_css.
  • Register the name in config.py's AVAIL_THEMES list to make it selectable in the UI.
  • Update the dispatcher in themes/theme.py to 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →