Appearance
Child Themes
Introduction
A child theme takes everything it does not have from a base theme: page components, styles, images, migrations and the base theme's service provider. It is the natural way to build language or regional copies of a site, or a seasonal variant, without forking the theme.
The rule that keeps this maintainable: the base theme never knows its children. A child declares its base; the base's code needs no change when a child appears.
Declaring a child theme
The smallest child theme is just a composer.json that maps the new theme to its base:
json
{
"name": "acme/my-theme-fr",
"description": "French copy of the Acme theme.",
"require": {
"acme/my-theme": "^1.0"
},
"extra": {
"laravel": {
"themes": {
"MyThemeFr": "MyTheme"
}
},
"kitloom": {
"theme": { "name": "Acme — Français" }
}
}
}Without a package — a directory in themes/ — declare it in the config instead:
php
// config/kitloom-themes.php
return [
'all' => ['MyThemeFr'],
'base' => ['MyThemeFr' => 'MyTheme'],
];kitloom-themes.base also overrides the base declared by a package. A theme without a package and without a base entry inherits from the default theme (APP_THEME).
What is inherited
| What | How |
|---|---|
| Service provider | The base theme's provider runs when the base or any of its children is active — ThemeManager::isActiveOrBase(). The child needs no provider unless it adds behaviour. |
| Page components (Vue) | The @ alias resolves a file from the child first, then from the base. Import base files explicitly with @base. |
| Styles | virtual:theme-css is the child's resources/css/app.css, or the base's if the child has none. |
| Views (Blade) | The child's resources/views come first. |
| Migrations | Base theme's first, then the child's. |
| Images and fonts | Copied per theme into public/images/{Theme} and public/fonts/{Theme} — a child brings its own, or references the base's. |
Overriding one component
To change only the post page in the French copy, put a single file in the child:
text
my-theme-fr/
├── composer.json
└── resources/js/Pages/Pages/Post/Index.vueEvery other component still comes from MyTheme. Inside the override you can reuse base components (assuming the base page exposes a footer slot):
vue
<script setup>
import BasePost from '@base/Pages/Pages/Post/Index.vue';
</script>
<template>
<BasePost v-bind="$attrs">
<template #footer>Lire aussi…</template>
</BasePost>
</template>A provider of its own
The base theme's provider already runs for every child, so a child usually needs no provider. If a child must add bindings of its own, keep them small and guard them with the child's name — app(ThemeManager::class)->isActive('MyThemeFr') — rather than extending the base provider: an extending provider would run the base theme's registration a second time.
Planned
A dedicated base class for child-theme providers — inheriting the base theme's behaviour and adding the child's own components — is planned.