Injecting CSS After the Plugin Stylesheet (Module/Plugin Authors)
If you are building a theme and just want your cart-override.scss to load after the plugin CSS, use registerCssFile with depends instead — it requires no PHP and no Craft module.
This approach is for Craft module or plugin authors who need to inject CSS programmatically — for example, a shipping plugin that adds a parcel-picker widget inside the checkout, or a loyalty module that adds reward-point styling to the cart totals.
ThemeService exposes the EVENT_REGISTER_THEMES event. Listening to it lets you register a theme override: an extra CSS file appended to any existing built-in theme. It loads as the last stylesheet on the page, so its rules win unconditionally.
Prerequisites
You need a Craft module or plugin to listen to the event. If your project does not have one yet, follow the Craft CMS module guide first.
Step 1 — Create the override stylesheet
Place your override CSS somewhere Craft can serve it. A common location is under web/assets/:
web/
└── assets/
└── theme/
├── cart-override.css
└── checkout-override.css
Example cart-override.css:
/* Loaded after cart-modern.css — these rules always win */
.cart-items-wrapper .list-item {
transition: background 0.15s ease;
}
.cart-items-wrapper .list-item:hover {
background: #f9fafb;
}
.cart-summary-sidebar .btn-primary {
letter-spacing: 0.05em;
text-transform: none;
}
Step 2 — Register the override in your module
In your module's init() method, listen to EVENT_REGISTER_THEMES and populate $event->themeOverrides:
use yui\craft\services\ThemeService;
use yui\craft\events\RegisterThemeEvent;
use yui\craft\support\EventManager;
class MyModule extends \craft\base\Module
{
public function init(): void
{
parent::init();
EventManager::listen(
ThemeService::class,
ThemeService::EVENT_REGISTER_THEMES,
function (RegisterThemeEvent $event) {
// Override the 'modern' built-in theme
$event->themeOverrides['modern'] = [
'cartCss' => '@webroot/assets/theme/cart-override.css',
'checkoutCss' => '@webroot/assets/theme/checkout-override.css',
];
}
);
}
}
The key in themeOverrides must match the active theme value configured in the plugin settings (default, modern, bold, minimal, or hyva).
If you want the override to apply regardless of which built-in theme is active, register the same paths for every theme key:
foreach (['default', 'modern', 'bold', 'minimal', 'hyva'] as $theme) {
$event->themeOverrides[$theme] = [
'cartCss' => '@webroot/assets/theme/cart-override.css',
'checkoutCss' => '@webroot/assets/theme/checkout-override.css',
];
}
How load order works
When the cart or checkout page is rendered, CartAsset / CheckoutAsset calls ThemeService::getThemeCssFiles(), which builds the list in this order:
- Built-in theme CSS (e.g.
cart-modern.css) - Custom theme CSS (only if you registered a new theme — see Register a Custom Theme)
- Override CSS ← your file goes here
Craft registers them in that order via the asset bundle, so your override is literally the last <link> tag for cart/checkout styles. Standard CSS cascade applies; your rules win.
Specificity still matters within your override file
Even though your file loads last, an equally specific selector from the plugin CSS earlier in the same <link> chain can conflict. To be safe, match or slightly raise specificity for rules you know you are overriding:
/* Plugin uses: .cart-items-wrapper .list-item { ... } — specificity (0,2,0) */
/* Match it exactly — last declaration wins because load order */
.cart-items-wrapper .list-item {
background: #f9fafb;
}
/* Or add one class to be safe */
.cart-wrapper .cart-items-wrapper .list-item {
background: #f9fafb;
}
Using Aliases
Craft aliases work in the CSS path strings:
| Alias | Resolves to |
|---|---|
@webroot | The web root directory |
@web | The base URL of the web root |
@vendor | The vendor directory |
You can also use absolute filesystem paths, but aliases are more portable.
When to use this approach instead of CSS variables
Use the event override when:
- You need to override a property that has no
--cart-*/--checkout-*variable - You need to change pseudo-element styles (
:before,:after,::placeholder) - You are adding net-new selector blocks (e.g. custom loading skeletons)
- You are integrating a third-party shipping widget that injects its own markup inside
.checkout-wrapper
For simple color/spacing/font changes, CSS custom properties remain the better choice — they are more declarative and easier to maintain.