Making mods
A mod is a folder with a mod.json and the files it names: a stylesheet laid over Caspian's window, and CSS and JavaScript that run on Caspian's own pages — the new tab, history, downloads and the rest — with settings you declare and the user sets on the mod's card. Several mods run at once; each has a switch. Mods never touch websites.
mod.json
name and version are required, and at least one of style or pages. Paths are relative to the folder and must stay inside it.
{
"name": "Clock on the new tab",
"version": "1.0.0",
"author": "You",
"description": "A clock above the search box on the new tab page.",
"homepage": "https://example.com/clock",
"icon": "icon.png",
"style": "window.qss",
"pages": [ { "match": ["newtab"], "css": "clock.css", "js": "clock.js" } ],
"settings": [
{ "key": "showDate", "label": "Show the date", "type": "bool", "default": true },
{ "key": "hours", "label": "Hours", "type": "select", "options": ["12", "24"], "default": "24" }
]
}Scripts and styles on Caspian's pages
Each pages entry names the pages it applies to — newtab, history, downloads, bookmarks, readinglist, extensions, sites, about, blocked, player, welcome, error, feedback, or * for all of those. The CSS is added as a <style>; the script runs once the page has loaded, as a function that receives caspianMod:
const box = document.createElement('div');
box.className = 'modclock';
box.innerHTML = '<div class="t"></div><div class="d"></div>';
const form = document.getElementById('form'); // the new tab's search box
form.parentNode.insertBefore(box, form);
const hour12 = caspianMod.settings.hours === '12';
function tick() {
const now = new Date();
box.querySelector('.t').textContent = now.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit', hour12 });
box.querySelector('.d').textContent = caspianMod.settings.showDate ? now.toLocaleDateString([], { weekday: 'long', day: 'numeric', month: 'long' }) : '';
}
tick(); setInterval(tick, 1000);The values of the settings declared in mod.json, as the user set them. Changing one re-runs the mod's pages.
The current colours — dark, bg, bgAlt, surface, border, text, muted, accent, accentText — read from the page's CSS variables, which already carry the theme.
Open an https:, http: or caspian: link in a new tab.
get(key), set(key, value), remove(key): JSON values kept per mod.
Which mod this is and which page it is running on.
--bg, --bgAlt, --surface, --border, --text, --muted, --accent, --accentText).Settings
Declared fields show on the mod's card. Types: bool (a switch), text, number, select (with options). Keys are letters, digits and underscores.
A stylesheet over the window
style names a Qt stylesheet appended after Caspian's own (and after the theme's), so its rules win. It may use the same tokens as Caspian's theme.qss — {{bg}}, {{bgAlt}}, {{surface}}, {{border}}, {{text}}, {{textMuted}}, {{hover}}, {{pressed}}, {{accent}}, {{accentText}}, {{bgHover}} — which resolve to the current palette.
QWidget#UrlBar { border-radius: 8px; background: transparent; }
QWidget#UrlBar:hover { background: {{bgHover}}; }
QWidget#UrlBar[focused="true"] { background: {{bg}}; border: 1px solid {{accent}}; }Selectors
The widget object names in Caspian's theme.qss: QWidget#NavBar, QWidget#UrlBar, QWidget#TabStrip, QLineEdit#UrlInput, QMenu#LiquidMenu, QToolButton#NewTabButton, QWidget#BookmarksBar, QWidget#FindBar, #InfoBar and more. Qt's stylesheet syntax is CSS-like but not CSS: no variables, no calc, no !important (see Qt's style sheet reference). The tab strip, the sidebar and the address-bar suggestions paint themselves from the palette, so a stylesheet can't recolour them — a theme does that.
Trying it
Zip the folder and use Install from file… on caspian:mods; a freshly installed mod is on. While editing, Reload on the card re-reads the installed copy from %LOCALAPPDATA%\Caspian\mods\<id> on Windows (~/Library/Application Support/Caspian/mods on macOS) and reloads the pages it runs on. A mod that breaks a page or the window is switched off from the mods page, which no mod can touch.
Publishing
Upload the zip in the store's admin like an extension. The store reads mod.json, marks the listing as a mod, and signs the package; Caspian installs it onto the mods page with the verified badge.