Theming
Adding your own theme #
Drop a .css file in cast-themes/, alongside config/ and templates/. The filename is the handle, a header comment supplies the rest.
/**
* Theme Name: Midnight
* Color Scheme: dark
* Description: Near-black, for late sessions.
*/
html[data-cast-theme="midnight"] {
--body-bg: #05070d;
--text-color: #e6ecff;
}
Only Color Scheme really matters: it decides whether _dark-base.css loads ahead of your theme, and which side of Auto it sits on. The name falls back to the filename and the scheme to light, so a file with no header is still a theme. Files starting with an underscore are partials and skipped.
The file is the registration, so a theme can't exist in one environment and not another. Settings → Plugins → Cast → Themes lists what was found and any file that couldn't be read, with the reason. The folder is published to cpresources — it needn't sit in the web root, and editing a theme busts its own cache. Point elsewhere with themesPath in config/cast.php.
Bundled handles win, so a dark.css won't quietly redefine Dark for everyone already using it. To genuinely replace one, register it in code.
What it costs #
Craft's palette derives almost entirely from a --gray-*-hsl ramp plus semantic properties (--pane-bg, --text-color, …), and most themes are a page of variables. Dark themes get _dark-base.css ahead of them — the inverted ramp plus patches — so they only declare deltas. Cast also sets data-cast-scheme to light or dark, so several of your own themes can share a base the same way.
Never
@importa base from a theme. The preview screens load every theme at once, and a second import would re-declare the base after the first theme's overrides, flattening it.
The expense isn't light versus dark, and it isn't how much of the ramp you redeclare. It's how far you move the hue. Dim rewrites all fourteen ramp steps but keeps Craft's blue-grey, and needs two extra rules. Stone shifts to a warm neutral and needs nine — as does Stone Dark, base or no base. Craft and the dark base both write blues straight into rules in a few dozen places, and those don't follow a retinted ramp. The most visible is --fg-input, which every control background derives from at 25%, 30% and 50% alpha.
Move the hue and stone.css is the worked example; each patch carries a note on what was pinned and why.
Registering a theme in code #
For a plugin or module shipping its own:
use bensomething\cast\events\RegisterThemesEvent;
use bensomething\cast\models\Theme;
use bensomething\cast\services\Themes;
use yii\base\Event;
Event::on(Themes::class, Themes::EVENT_REGISTER_THEMES, function(RegisterThemesEvent $event) {
$event->themes['midnight'] = new Theme([
'handle' => 'midnight',
'name' => 'Midnight',
'colorScheme' => Theme::SCHEME_DARK,
'url' => Craft::$app->getAssetManager()->getPublishedUrl(
'@mymodule/resources', true, 'midnight.css',
),
]);
});
Use a bundled handle as the key to replace that theme.