name: moonshine-appearance-v4 description: Use when working on MoonShine v4 layouts, custom layouts, menus, color palettes, icons, assets, custom pages, dark mode, branding, or Blade templates.
MoonShine v4 Appearance
Covers layout templates, menu configuration, color palette system, icons, asset management, dark mode, branding, and Blade integration.
CRITICAL v4 changes from v3
- CompactLayout removed -- extend
AppLayoutdirectly and use$contentSimpled,$contentCenteredproperties instead. - NEW palette system --
PurplePaletteis the default; palette classes replace direct color config. Colors now use OKLCH format. - MenuItem parameter swap --
$filleris now the first parameter,$labelis second and optional (auto-derived fromgetLabel()/getTitle()). - Profile component --
$withBorderparameter removed. - Component control -- new boolean properties
$sidebar,$topBar,$bottomBar,$mobileModeon layout. - BottomBar -- new component for bottom navigation, auto-displays top menu.
- Mobile Mode -- new
$mobileModeproperty for mobile-optimized layouts.
1. Layout basics
MoonShine v4 publishes app/MoonShine/Layouts/AppLayout.php at install. All custom layouts extend AppLayout.
Hierarchy
BaseLayout (abstract)
-> AppLayout (standard sidebar layout)
-> BlankLayout (bare bones -- Head + Body only)
-> LoginLayout (authentication page)
Choosing a layout
// config/moonshine.php
'layout' => \App\MoonShine\Layouts\MoonShineLayout::class,
// Or via MoonShineServiceProvider
$config->layout(\App\MoonShine\Layouts\CustomLayout::class);
Extending AppLayout
namespace App\MoonShine\Layouts;
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\UI\Components\Layout\Layout;
final class MoonShineLayout extends AppLayout
{
protected function getFooterMenu(): array
{
return [
'https://example.com' => 'Custom link',
];
}
protected function getFooterCopyright(): string
{
return 'My Company';
}
public function build(): Layout
{
return parent::build();
}
}
Override individual component methods
protected function getHeadComponent(bool $withAssetsFragment = true): Head { /* ... */ }
protected function getLogoComponent(): Logo { /* ... */ }
protected function getSidebarComponent(): Sidebar { /* ... */ }
protected function getHeaderComponent(): Header { /* ... */ }
protected function getTopBarComponent(): Topbar { /* ... */ }
protected function getFooterComponent(): Footer { /* ... */ }
protected function getProfileComponent(): Profile { /* ... */ }
protected function getContentComponents(): array { /* ... */ }
protected function getLogo(bool $small = false): string { /* ... */ }
protected function getHomeUrl(): string { /* ... */ }
Content layout properties
// Remove border and background from content block
protected bool $contentSimpled = true; // default false
// Center content in a fixed-width container
protected bool $contentCentered = true; // default false
2. Component control (NEW in v4)
Control layout components via boolean properties -- no need to override build().
final class MoonShineLayout extends AppLayout
{
protected bool $sidebar = true; // enabled by default
protected bool $topBar = false; // disabled by default
protected bool $bottomBar = false; // disabled by default
protected bool $mobileMode = false; // disabled by default
}
- Sidebar -- set
$sidebar = falseto disable. - TopBar -- set
$topBar = trueto enable. If used with Sidebar, TopBar must come first inbuild(). - BottomBar -- set
$bottomBar = trueto enable. Auto-displays top menu. - Mobile Mode -- set
$mobileMode = truefor compact styles, hidden Burger, auto BottomBar.
Layout slots
protected function sidebarSlot(): array
{
return [Search::make()->enabled()];
}
protected function sidebarTopSlot(): array
{
return [Notifications::make()];
}
protected function topBarSlot(): array
{
return [/* custom components */];
}
See references/layouts.md for the full layout API.
3. Creating custom layouts
php artisan moonshine:layout MyLayout
Creates app/MoonShine/Layouts/MyLayout.php.
Page-specific layouts
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
}
// Or via attribute
use MoonShine\Core\Attributes\Layout;
#[Layout(MyLayout::class)]
class CustomPage extends Page {}
4. Menu system
The menu is declared in the layout's menu() method.
MenuItem -- NEW parameter order in v4
MenuItem::make(
Closure|MenuFillerContract|string $filler, // FIRST: resource, page, URL, or closure
Closure|string $label = null, // SECOND: optional, auto-derived from getTitle()
string $icon = null,
Closure|bool $blank = false
)
Quick example
use MoonShine\MenuManager\MenuItem;
use MoonShine\MenuManager\MenuGroup;
use MoonShine\MenuManager\MenuDivider;
protected function menu(): array
{
return [
MenuGroup::make('Content', [
MenuItem::make(ArticleResource::class),
MenuItem::make(CategoryResource::class, icon: 'tag'),
])->icon('newspaper'),
MenuDivider::make(),
MenuItem::make(DashboardPage::class, 'Dashboard', 'home'),
MenuItem::make('https://example.com', 'External', blank: true),
];
}
Key menu features
- MenuItem -- links to Resource, Page, URL, or Closure. Label auto-derived from filler.
- MenuGroup -- groups items with optional icon.
- MenuDivider -- visual separator with optional label.
- badge() -- show counts:
->badge(fn() => Comment::count()) - canSee() -- conditional display:
->canSee(fn() => auth()->user()->isAdmin()) - translatable() -- i18n:
->translatable('menu') - whenActive() -- custom active state logic.
- icon() -- Heroicons or custom SVGs.
- onlyIcon() -- display icon without text label, shows tooltip on hover.
- changeButton() -- modify the underlying ActionButton.
Autoloaded menu
protected function menu(): array
{
return $this->autoloadMenu();
// or with icon-only mode:
// return $this->autoloadMenu(onlyIcons: true);
}
Attributes for autoload: #[SkipMenu], #[Group('name', 'icon')], #[Order(1)], #[CanSee(method: 'methodName')], #[Badge('new')].
See references/menu-system.md for the full menu API.
5. Color palette system (NEW in v4)
MoonShine v4 uses palette classes instead of direct color arrays. The default is PurplePalette.
Setting a palette
// In layout
final class MoonShineLayout extends AppLayout
{
protected ?string $palette = CorporatePalette::class;
}
// In config/moonshine.php
'palette' => \MoonShine\ColorManager\Palettes\PurplePalette::class,
// Programmatically in ServiceProvider
$colors->palette(new CorporatePalette());
Available built-in palettes
PurplePalette (default), CyanPalette, GrayPalette, GreenPalette, HalloweenPalette, LimePalette, NeutralPalette, OrangePalette, PinkPalette, RetroPalette, RosePalette, SkyPalette, SpringPalette, TealPalette, ValentinePalette, WinterPalette, YellowPalette.
Creating a custom palette
namespace App\MoonShine\Palettes;
use MoonShine\Contracts\ColorManager\PaletteContract;
final class CorporatePalette implements PaletteContract
{
public function getColors(): array
{
return [
'primary' => 'oklch(65% 0.18 264)',
'base' => [
'default' => '0 0 0',
50 => 'oklch(98% 0.02 250)',
],
];
}
public function getDarkColors(): array
{
return [
'primary' => 'oklch(60% 0.17 264)',
'base' => [
'default' => '0.24 0 0',
50 => 'oklch(98% 0.02 250)',
],
];
}
}
ColorManager override in layout
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('oklch(65% 0.18 264)')
->secondary('oklch(70% 0.14 230)')
->successBg('oklch(63.9% 0.218 142.495)')
->errorBg('oklch(58.9% 0.214 26.855)');
// Dark theme
$colorManager
->set('body', '0.2 0.0168 274.32', dark: true);
}
See references/colors.md for the full color API.
6. Icons
MoonShine v4 uses Heroicons with four sets: Outline (default), Solid (s.), Mini (m.), Compact (c.).
->icon('cog') // Outline (default)
->icon('s.cog') // Solid
->icon('m.cog') // Mini
->icon('c.cog') // Compact
Custom icon sets via $path parameter or $custom mode with raw HTML/SVG.
See references/icons.md for icon details.
7. Assets
Add CSS/JS to a layout via the assets() method:
use MoonShine\AssetManager\Css;
use MoonShine\AssetManager\Js;
use MoonShine\AssetManager\InlineCss;
protected function assets(): array
{
return [
...parent::assets(),
Css::make('/css/custom.css'),
Js::make('/js/custom.js')->defer(),
InlineCss::make(':root { --spacing: 0.15rem; }'),
];
}
Vite integration
use Illuminate\Support\Facades\Vite;
protected function assets(): array
{
return [
$this->getMainThemeJs(),
Css::make(Vite::asset('resources/css/app.css')),
Js::make(Vite::asset('resources/js/app.js')),
];
}
Asset types: Js, Css, InlineCss, InlineJs, Raw.
See references/assets-branding.md for the full asset and branding API.
8. Dark mode
Always-dark theme
protected function isAlwaysDark(): bool
{
return true;
}
Disable theme switcher (light only)
protected function hasThemes(): bool
{
return false;
}
The ThemeSwitcher component is included by default in Sidebar and TopBar.
Force dark mode on custom components
Sidebar, TopBar, and MobileBar are styled dark by default. Force dark on custom additions:
$this->getSidebarComponent()->class('dark'),
$this->getTopBarComponent()->class('dark'),
9. Branding
Logo
protected function getLogo(bool $small = false): string
{
return $small ? '/images/logo-small.png' : '/images/logo.png';
}
Or via config:
// config/moonshine.php
'logo' => '/images/logo.png',
'logo_small' => '/images/logo-small.png',
Favicon
protected function getFaviconComponent(): Favicon
{
return parent::getFaviconComponent()->customAssets([
'apple-touch' => '/favicon/apple-touch-icon.png',
'32' => '/favicon/favicon-32x32.png',
'16' => '/favicon/favicon-16x16.png',
'safari-pinned-tab' => '/favicon/safari-pinned-tab.svg',
]);
}
Footer
protected function getFooterMenu(): array
{
return ['https://docs.example.com' => 'Docs'];
}
protected function getFooterCopyright(): string
{
return sprintf('© %d My Company', now()->year);
}
10. Blade templates
MoonShine v4 supports pure Blade layouts using <x-moonshine::layout.*> components.
<x-moonshine::layout>
<x-moonshine::layout.html :with-alpine-js="true" :with-themes="true">
<x-moonshine::layout.head>
<x-moonshine::layout.meta name="csrf-token" :content="csrf_token()"/>
<x-moonshine::layout.favicon />
<x-moonshine::layout.assets>
@vite(['resources/css/main.css', 'resources/js/app.js'], 'vendor/moonshine')
</x-moonshine::layout.assets>
</x-moonshine::layout.head>
<x-moonshine::layout.body>
<x-moonshine::layout.wrapper>
<x-moonshine::layout.sidebar :collapsed="true">
<!-- sidebar content -->
</x-moonshine::layout.sidebar>
<x-moonshine::layout.div class="layout-page">
<x-moonshine::layout.header>
<x-moonshine::layout.burger/>
</x-moonshine::layout.header>
<x-moonshine::layout.content>
Your content
</x-moonshine::layout.content>
</x-moonshine::layout.div>
</x-moonshine::layout.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>
Fragments
Layout parts live inside Fragment components for AJAX updates:
sidebar-top-- logo and theme switchsidebar-content-- side menu contenttopbar-logo-- logo in top menutopbar-menu-- top menu itemstopbar-actions-- action block in top menuassets-- styles and scripts
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
return JsonResponse::make()->events([
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),
]);
11. Cross-references
- references/layouts.md -- full layout API, component control, slots, build method
- references/menu-system.md -- full menu API, autoload, attributes
- references/colors.md -- palette system, ColorManager methods, color tokens
- references/icons.md -- Heroicons sets, custom icons, icon component
- references/assets-branding.md -- AssetManager, Vite, branding, custom build
- moonshine-setup-v4 -- Installation, configuration, routing
- moonshine-components-v4 -- UI components used inside layouts
- moonshine-resources-v4 -- ModelResource classes for menu items
- moonshine-fields-v4 -- Field types used within page components