Layout in MoonShine is a set of components that form the structure of the admin panel page.
Each element of the page, including HTML tags, is a MoonShine component.
This provides a high degree of flexibility and customization options.
When installing MoonShine, the default template app/MoonShine/Layouts/AppLayout.php is published and registered in the configuration file.
You can:
- Modify an existing template,
- Create a new template,
- Apply different templates for various pages.
Find the complete list of components in the Components section.
As you may notice, there are a huge number of components, and for convenience, we have grouped them together so that you can conveniently override only the groups required.
use MoonShine\Laravel\Layouts\AppLayout;
final class MoonShineLayout extends AppLayout
{
protected function getFooterMenu(): array
{
return [
'https://example.com' => 'Custom link',
];
}
protected function getFooterCopyright(): string
{
return 'MoonShine';
}
public function build(): Layout
{
return parent::build();
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
final class MoonShineLayout extends AppLayout
{
// ...
protected function getFooterMenu(): array
{
return [
'https://example.com' => 'Custom link',
];
}
protected function getFooterCopyright(): string
{
return 'MoonShine';
}
public function build(): Layout
{
return parent::build();
}
}
use MoonShine\Laravel\Layouts\AppLayout;
final class MoonShineLayout extends AppLayout
{
// ...
protected function getFooterMenu(): array
{
return [
'https://example.com' => 'Custom link',
];
}
protected function getFooterCopyright(): string
{
return 'MoonShine';
}
public function build(): Layout
{
return parent::build();
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
final class MoonShineLayout extends AppLayout
{
// ...
protected function getFooterMenu(): array
{
return [
'https://example.com' => 'Custom link',
];
}
protected function getFooterCopyright(): string
{
return 'MoonShine';
}
public function build(): Layout
{
return parent::build();
}
}
use MoonShine\Laravel\Layouts\AppLayout;
final class MoonShineLayout extends AppLayout
{
// ...
protected function getFooterMenu(): array
{
return [
'https://example.com' => 'Custom link',
];
}
protected function getFooterCopyright(): string
{
return 'MoonShine';
}
public function build(): Layout
{
return parent::build();
}
}
In the example above, we have overridden the output of the footer menu and copyright using the methods getFooterMenu() and getFooterCopyright().
Available quick methods:
Override the Head component
protected function getHeadComponent(bool $withAssetsFragment = true): Head
{
return Head::make([
]);
}
protected function getHeadComponent(bool $withAssetsFragment = true): Head
{
return Head::make([
// ...
]);
}
protected function getHeadComponent(bool $withAssetsFragment = true): Head
{
return Head::make([
// ...
]);
}
protected function getHeadComponent(bool $withAssetsFragment = true): Head
{
return Head::make([
// ...
]);
}
protected function getHeadComponent(bool $withAssetsFragment = true): Head
{
return Head::make([
// ...
]);
}
Override the Logo component
protected function getLogoComponent(): Logo
{
return Logo::make(
$this->getHomeUrl(),
$this->getLogo(),
$this->getLogo(small: true),
);
}
protected function getLogoComponent(): Logo
{
return Logo::make(
$this->getHomeUrl(),
$this->getLogo(),
$this->getLogo(small: true),
);
}
protected function getLogoComponent(): Logo
{
return Logo::make(
$this->getHomeUrl(),
$this->getLogo(),
$this->getLogo(small: true),
);
}
protected function getLogoComponent(): Logo
{
return Logo::make(
$this->getHomeUrl(),
$this->getLogo(),
$this->getLogo(small: true),
);
}
protected function getLogoComponent(): Logo
{
return Logo::make(
$this->getHomeUrl(),
$this->getLogo(),
$this->getLogo(small: true),
);
}
Override the Sidebar component
protected function getSidebarComponent(): Sidebar
{
return Sidebar::make([
]);
}
protected function getSidebarComponent(): Sidebar
{
return Sidebar::make([
// ...
]);
}
protected function getSidebarComponent(): Sidebar
{
return Sidebar::make([
// ...
]);
}
protected function getSidebarComponent(): Sidebar
{
return Sidebar::make([
// ...
]);
}
protected function getSidebarComponent(): Sidebar
{
return Sidebar::make([
// ...
]);
}
Override the Header component
protected function getHeaderComponent(): Header
{
Header::make([
]);
}
protected function getHeaderComponent(): Header
{
Header::make([
// ...
]);
}
protected function getHeaderComponent(): Header
{
Header::make([
// ...
]);
}
protected function getHeaderComponent(): Header
{
Header::make([
// ...
]);
}
protected function getHeaderComponent(): Header
{
Header::make([
// ...
]);
}
Override or integrate the TopBar component
protected function getTopBarComponent(): Topbar
{
Topbar::make([
]);
}
protected function getTopBarComponent(): Topbar
{
Topbar::make([
// ...
]);
}
protected function getTopBarComponent(): Topbar
{
Topbar::make([
// ...
]);
}
protected function getTopBarComponent(): Topbar
{
Topbar::make([
// ...
]);
}
protected function getTopBarComponent(): Topbar
{
Topbar::make([
// ...
]);
}
Override the Footer component
protected function getFooterComponent(): Footer
{
Footer::make([
]);
}
protected function getFooterComponent(): Footer
{
Footer::make([
// ...
]);
}
protected function getFooterComponent(): Footer
{
Footer::make([
// ...
]);
}
protected function getFooterComponent(): Footer
{
Footer::make([
// ...
]);
}
protected function getFooterComponent(): Footer
{
Footer::make([
// ...
]);
}
Override the Profile component
protected function getProfileComponent(): Profile
{
return Profile::make();
}
protected function getProfileComponent(): Profile
{
return Profile::make();
}
protected function getProfileComponent(): Profile
{
return Profile::make();
}
protected function getProfileComponent(): Profile
{
return Profile::make();
}
protected function getProfileComponent(): Profile
{
return Profile::make();
}
Override the сontent of the Content component
protected function getContentComponents(): array
{
}
protected function getContentComponents(): array
{
// ...
}
protected function getContentComponents(): array
{
// ...
}
protected function getContentComponents(): array
{
// ...
}
protected function getContentComponents(): array
{
// ...
}
Content::make(
$this->getContentComponents()
)
Content::make(
$this->getContentComponents()
)
Content::make(
$this->getContentComponents()
)
Content::make(
$this->getContentComponents()
)
Content::make(
$this->getContentComponents()
)
Path to the logo
protected function getLogo(bool $small = false): string
{
}
protected function getLogo(bool $small = false): string
{
// ...
}
protected function getLogo(bool $small = false): string
{
// ...
}
protected function getLogo(bool $small = false): string
{
// ...
}
protected function getLogo(bool $small = false): string
{
// ...
}
URL of the main page
protected function getHomeUrl(): string
{
}
protected function getHomeUrl(): string
{
// ...
}
protected function getHomeUrl(): string
{
// ...
}
protected function getHomeUrl(): string
{
// ...
}
protected function getHomeUrl(): string
{
// ...
}
Simplified Content Layout
You can remove the border and background from the content block of the page by setting the $contentSimpled = true property in your Layout:
protected bool $contentSimpled = true;
protected bool $contentSimpled = true; // default false
protected bool $contentSimpled = true; // default false
protected bool $contentSimpled = true; // default false
protected bool $contentSimpled = true; // default false
Centered Content Layout
By default, page content takes up the entire width of the screen. To place it in a centered fixed-width container, set the $contentCentered = true property in your Layout.
protected bool $contentCentered = true;
protected bool $contentCentered = true; // default false
protected bool $contentCentered = true; // default false
protected bool $contentCentered = true; // default false
protected bool $contentCentered = true; // default false
With the help of "slots" you can quickly add components to the Sidebar or Topbar.
protected function sidebarSlot(): array
{
return [
Search::make()->enabled(),
];
}
protected function sidebarTopSlot(): array
{
return [
Notifications::make(),
];
}
protected function topBarSlot(): array
{
return [
];
}
protected function sidebarSlot(): array
{
return [
Search::make()->enabled(),
// ...
];
}
protected function sidebarTopSlot(): array
{
return [
Notifications::make(),
// ...
];
}
protected function topBarSlot(): array
{
return [
// ...
];
}
protected function sidebarSlot(): array
{
return [
Search::make()->enabled(),
// ...
];
}
protected function sidebarTopSlot(): array
{
return [
Notifications::make(),
// ...
];
}
protected function topBarSlot(): array
{
return [
// ...
];
}
protected function sidebarSlot(): array
{
return [
Search::make()->enabled(),
// ...
];
}
protected function sidebarTopSlot(): array
{
return [
Notifications::make(),
// ...
];
}
protected function topBarSlot(): array
{
return [
// ...
];
}
protected function sidebarSlot(): array
{
return [
Search::make()->enabled(),
// ...
];
}
protected function sidebarTopSlot(): array
{
return [
Notifications::make(),
// ...
];
}
protected function topBarSlot(): array
{
return [
// ...
];
}
You can also create your custom template with your own set of convenient methods for easier interaction in the future.
In the standard Layout, the Sidebar, Topbar, and Mobilebar components are designed in the dark colors.
But if you add other components to them, they will change depending on the chosen theme, which will lead to incorrect display.
To avoid this behavior, you can force them into dark mode by adding the 'dark' class.
$this->getSidebarComponent()->class('dark'),
$this->getTopBarComponent()->class('dark'),
MobileBar::make([
])->class('dark'),
$this->getSidebarComponent()->class('dark'),
$this->getTopBarComponent()->class('dark'),
MobileBar::make([
// ...
])->class('dark'),
$this->getSidebarComponent()->class('dark'),
$this->getTopBarComponent()->class('dark'),
MobileBar::make([
// ...
])->class('dark'),
$this->getSidebarComponent()->class('dark'),
$this->getTopBarComponent()->class('dark'),
MobileBar::make([
// ...
])->class('dark'),
$this->getSidebarComponent()->class('dark'),
$this->getTopBarComponent()->class('dark'),
MobileBar::make([
// ...
])->class('dark'),
To create another template, use the command:
php artisan moonshine:layout
php artisan moonshine:layout
php artisan moonshine:layout
php artisan moonshine:layout
php artisan moonshine:layout
You can learn about all supported options in the section Commands.
By default, pages use the display template AppLayout.
But you can change it to your custom template by simply replacing the value of the $layout property.
Read more about pages in the Page section.
use App\MoonShine\Layouts\MyLayout;
use MoonShine\Laravel\Pages\Page;
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
}
namespaces
use App\MoonShine\Layouts\MyLayout;
use MoonShine\Laravel\Pages\Page;
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
// ...
}
use App\MoonShine\Layouts\MyLayout;
use MoonShine\Laravel\Pages\Page;
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
// ...
}
namespaces
use App\MoonShine\Layouts\MyLayout;
use MoonShine\Laravel\Pages\Page;
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
// ...
}
use App\MoonShine\Layouts\MyLayout;
use MoonShine\Laravel\Pages\Page;
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
// ...
}
Each template can have its own set of styles and scripts defined through the assets() method.
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\AssetManager\Css;
final class MyLayout extends AppLayout
{
protected function assets(): array
{
return [
...parent::assets(),
Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),
];
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\AssetManager\Css;
final class MyLayout extends AppLayout
{
// ...
protected function assets(): array
{
return [
...parent::assets(),
Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),
];
}
// ...
}
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\AssetManager\Css;
final class MyLayout extends AppLayout
{
// ...
protected function assets(): array
{
return [
...parent::assets(),
Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),
];
}
// ...
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\AssetManager\Css;
final class MyLayout extends AppLayout
{
// ...
protected function assets(): array
{
return [
...parent::assets(),
Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),
];
}
// ...
}
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\AssetManager\Css;
final class MyLayout extends AppLayout
{
// ...
protected function assets(): array
{
return [
...parent::assets(),
Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),
];
}
// ...
}
For more detailed information, refer to the Assets section.
protected function assets(): array
{
return [
...parent::assets(),
InlineCss::make(<<<'Style'
:root {
--spacing: 0.15rem;
}
Style),
];
}
protected function assets(): array
{
return [
...parent::assets(),
InlineCss::make(<<<'Style'
:root {
--spacing: 0.15rem;
}
Style),
];
}
protected function assets(): array
{
return [
...parent::assets(),
InlineCss::make(<<<'Style'
:root {
--spacing: 0.15rem;
}
Style),
];
}
protected function assets(): array
{
return [
...parent::assets(),
InlineCss::make(<<<'Style'
:root {
--spacing: 0.15rem;
}
Style),
];
}
protected function assets(): array
{
return [
...parent::assets(),
InlineCss::make(<<<'Style'
:root {
--spacing: 0.15rem;
}
Style),
];
}
You can replace the set of favicons in a template by overriding the getFaviconComponent() method.
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
protected function getFaviconComponent(): Favicon
{
return parent::getFaviconComponent()->customAssets([
'apple-touch' => 'favicon_path',
'32' => 'favicon_path',
'16' => 'favicon_path',
'safari-pinned-tab' => 'favicon_path',
'web-manifest' => 'favicon_path',
]);
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
// ...
protected function getFaviconComponent(): Favicon
{
return parent::getFaviconComponent()->customAssets([
'apple-touch' => 'favicon_path',
'32' => 'favicon_path',
'16' => 'favicon_path',
'safari-pinned-tab' => 'favicon_path',
'web-manifest' => 'favicon_path',
]);
}
}
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
// ...
protected function getFaviconComponent(): Favicon
{
return parent::getFaviconComponent()->customAssets([
'apple-touch' => 'favicon_path',
'32' => 'favicon_path',
'16' => 'favicon_path',
'safari-pinned-tab' => 'favicon_path',
'web-manifest' => 'favicon_path',
]);
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
// ...
protected function getFaviconComponent(): Favicon
{
return parent::getFaviconComponent()->customAssets([
'apple-touch' => 'favicon_path',
'32' => 'favicon_path',
'16' => 'favicon_path',
'safari-pinned-tab' => 'favicon_path',
'web-manifest' => 'favicon_path',
]);
}
}
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
// ...
protected function getFaviconComponent(): Favicon
{
return parent::getFaviconComponent()->customAssets([
'apple-touch' => 'favicon_path',
'32' => 'favicon_path',
'16' => 'favicon_path',
'safari-pinned-tab' => 'favicon_path',
'web-manifest' => 'favicon_path',
]);
}
}
For each template, you can declare a list of menu items via the menu() method, which will be automatically passed to the Menu component.
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\MenuManager\MenuItem;
final class MyLayout extends AppLayout
{
protected function menu(): array
{
return [
...parent::menu(),
MenuItem::make(ArticleResource::class),
];
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\MenuManager\MenuItem;
final class MyLayout extends AppLayout
{
// ...
protected function menu(): array
{
return [
...parent::menu(),
MenuItem::make(ArticleResource::class),
];
}
}
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\MenuManager\MenuItem;
final class MyLayout extends AppLayout
{
// ...
protected function menu(): array
{
return [
...parent::menu(),
MenuItem::make(ArticleResource::class),
];
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\MenuManager\MenuItem;
final class MyLayout extends AppLayout
{
// ...
protected function menu(): array
{
return [
...parent::menu(),
MenuItem::make(ArticleResource::class),
];
}
}
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\MenuManager\MenuItem;
final class MyLayout extends AppLayout
{
// ...
protected function menu(): array
{
return [
...parent::menu(),
MenuItem::make(ArticleResource::class),
];
}
}
For more detailed information, refer to the Menu section.
You can also choose not to use the menu() method and pass the list manually to the Menu component.
By default, MoonShine has a top menu component that can be used instead of Sidebar or together with it.
To replace Sidebar with TopBar, override the build() method and replace the call to the getSidebarComponent() method with getTopBarComponent().
If you want to keep both Sidebar and TopBar at the same time, be sure to maintain the order; TopBar must be first.
Moonshine supports two themes out of the box: light and dark.
By default, the theme specified in the system is used, or light if it could not be determined.
If you want the dark theme to always be enabled, override the isAlwaysDark() method and return true. The theme switcher will not be displayed.
protected function isAlwaysDark(): bool
{
return true;
}
protected function isAlwaysDark(): bool
{
return true;
}
protected function isAlwaysDark(): bool
{
return true;
}
protected function isAlwaysDark(): bool
{
return true;
}
protected function isAlwaysDark(): bool
{
return true;
}
To remove the theme switcher and leave only the light theme, override the hasThemes() method and return false.
protected function hasThemes(): bool
{
return false;
}
protected function hasThemes(): bool
{
return false;
}
protected function hasThemes(): bool
{
return false;
}
protected function hasThemes(): bool
{
return false;
}
protected function hasThemes(): bool
{
return false;
}
Each template can have its own color scheme.
The easiest way to set it is to specify a PaletteContract implementation in the $palette property:
use App\MoonShine\Palettes\CorporatePalette;
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
protected ?string $palette = CorporatePalette::class;
}
use App\MoonShine\Palettes\CorporatePalette;
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
protected ?string $palette = CorporatePalette::class;
}
use App\MoonShine\Palettes\CorporatePalette;
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
protected ?string $palette = CorporatePalette::class;
}
use App\MoonShine\Palettes\CorporatePalette;
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
protected ?string $palette = CorporatePalette::class;
}
use App\MoonShine\Palettes\CorporatePalette;
use MoonShine\Laravel\Layouts\AppLayout;
final class MyLayout extends AppLayout
{
protected ?string $palette = CorporatePalette::class;
}
For more details, see the Palettes section.
Leaving $palette as null keeps using the global config('moonshine.palette') value.
For full control you can still override the colors() method:
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
final class MyLayout extends AppLayout
{
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('oklch(65% 0.18 264)')
->secondary('oklch(70% 0.14 230)')
->bulkAssign([
'theme' => [
'body' => '0 0 0',
50 => '0.99 0 0',
100 => '0.98 0 0',
900 => '0.90 0 0',
],
])
->successBg('oklch(63.9% 0.218 142.495)')
->warningBg('oklch(80.88% 0.170358 75.3501)')
->errorBg('oklch(58.9% 0.214 26.855)')
->infoBg('oklch(60.1% 0.219 257.63)');
$colorManager
->set('body', '0.2 0.0168 274.32', dark: true)
->theme([
'body' => '1 0 0',
'stroke' => '1 0 0 / 10%',
'default' => '0.24 0.0168 274.32',
900 => '0.39 0.025 274.32',
], dark: true)
->successBg('0.639 0.218 142.495', dark: true)
->warningBg('0.898 0.177 96.726', dark: true)
->errorBg('0.589 0.214 26.855', dark: true)
->infoBg('0.601 0.219 257.63', dark: true);
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
final class MyLayout extends AppLayout
{
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('oklch(65% 0.18 264)')
->secondary('oklch(70% 0.14 230)')
->bulkAssign([
'theme' => [
'body' => '0 0 0',
50 => '0.99 0 0',
100 => '0.98 0 0',
900 => '0.90 0 0',
],
])
->successBg('oklch(63.9% 0.218 142.495)')
->warningBg('oklch(80.88% 0.170358 75.3501)')
->errorBg('oklch(58.9% 0.214 26.855)')
->infoBg('oklch(60.1% 0.219 257.63)');
$colorManager
->set('body', '0.2 0.0168 274.32', dark: true)
->theme([
'body' => '1 0 0',
'stroke' => '1 0 0 / 10%',
'default' => '0.24 0.0168 274.32',
900 => '0.39 0.025 274.32',
], dark: true)
->successBg('0.639 0.218 142.495', dark: true)
->warningBg('0.898 0.177 96.726', dark: true)
->errorBg('0.589 0.214 26.855', dark: true)
->infoBg('0.601 0.219 257.63', dark: true);
}
}
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
final class MyLayout extends AppLayout
{
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('oklch(65% 0.18 264)')
->secondary('oklch(70% 0.14 230)')
->bulkAssign([
'theme' => [
'body' => '0 0 0',
50 => '0.99 0 0',
100 => '0.98 0 0',
900 => '0.90 0 0',
],
])
->successBg('oklch(63.9% 0.218 142.495)')
->warningBg('oklch(80.88% 0.170358 75.3501)')
->errorBg('oklch(58.9% 0.214 26.855)')
->infoBg('oklch(60.1% 0.219 257.63)');
$colorManager
->set('body', '0.2 0.0168 274.32', dark: true)
->theme([
'body' => '1 0 0',
'stroke' => '1 0 0 / 10%',
'default' => '0.24 0.0168 274.32',
900 => '0.39 0.025 274.32',
], dark: true)
->successBg('0.639 0.218 142.495', dark: true)
->warningBg('0.898 0.177 96.726', dark: true)
->errorBg('0.589 0.214 26.855', dark: true)
->infoBg('0.601 0.219 257.63', dark: true);
}
}
namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
final class MyLayout extends AppLayout
{
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('oklch(65% 0.18 264)')
->secondary('oklch(70% 0.14 230)')
->bulkAssign([
'theme' => [
'body' => '0 0 0',
50 => '0.99 0 0',
100 => '0.98 0 0',
900 => '0.90 0 0',
],
])
->successBg('oklch(63.9% 0.218 142.495)')
->warningBg('oklch(80.88% 0.170358 75.3501)')
->errorBg('oklch(58.9% 0.214 26.855)')
->infoBg('oklch(60.1% 0.219 257.63)');
$colorManager
->set('body', '0.2 0.0168 274.32', dark: true)
->theme([
'body' => '1 0 0',
'stroke' => '1 0 0 / 10%',
'default' => '0.24 0.0168 274.32',
900 => '0.39 0.025 274.32',
], dark: true)
->successBg('0.639 0.218 142.495', dark: true)
->warningBg('0.898 0.177 96.726', dark: true)
->errorBg('0.589 0.214 26.855', dark: true)
->infoBg('0.601 0.219 257.63', dark: true);
}
}
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
final class MyLayout extends AppLayout
{
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('oklch(65% 0.18 264)')
->secondary('oklch(70% 0.14 230)')
->bulkAssign([
'theme' => [
'body' => '0 0 0',
50 => '0.99 0 0',
100 => '0.98 0 0',
900 => '0.90 0 0',
],
])
->successBg('oklch(63.9% 0.218 142.495)')
->warningBg('oklch(80.88% 0.170358 75.3501)')
->errorBg('oklch(58.9% 0.214 26.855)')
->infoBg('oklch(60.1% 0.219 257.63)');
$colorManager
->set('body', '0.2 0.0168 274.32', dark: true)
->theme([
'body' => '1 0 0',
'stroke' => '1 0 0 / 10%',
'default' => '0.24 0.0168 274.32',
900 => '0.39 0.025 274.32',
], dark: true)
->successBg('0.639 0.218 142.495', dark: true)
->warningBg('0.898 0.177 96.726', dark: true)
->errorBg('0.589 0.214 26.855', dark: true)
->infoBg('0.601 0.219 257.63', dark: true);
}
}
For more detailed information, refer to the Color Scheme section.
By default, in the base layout, its individual parts are located inside Fragment components. Due to this, you can update them without reloading the page using JSEvents events.
Available fragments:
sidebar-top - The top part of the side menu (logo and theme switch),
sidebar-content - Content part of the side menu,
topbar-logo - Logo in the top menu,
topbar-menu - Top menu items,
topbar-actions - Action block in the top menu,
assets - A set of styles and scripts.
An example of updating all layout fragments from a controller method:
public function saveElement(CrudRequestContract $request): JsonResponse
{
return JsonResponse::make()->events([
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),
]);
}
public function saveElement(CrudRequestContract $request): JsonResponse
{
//...
return JsonResponse::make()->events([
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),
]);
}
public function saveElement(CrudRequestContract $request): JsonResponse
{
//...
return JsonResponse::make()->events([
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),
]);
}
public function saveElement(CrudRequestContract $request): JsonResponse
{
//...
return JsonResponse::make()->events([
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),
]);
}
public function saveElement(CrudRequestContract $request): JsonResponse
{
//...
return JsonResponse::make()->events([
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),
]);
}
MoonShine allows you to create templates directly using Blade.
An example of a basic template:
<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">
<x-moonshine::layout.div class="menu-header">
<x-moonshine::layout.div class="menu-logo">
<x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-actions">
<x-moonshine::layout.theme-switcher/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu menu--vertical">
<x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/>
</x-moonshine::layout.div>
</x-moonshine::layout.sidebar>
<x-moonshine::layout.div class="layout-main">
<x-moonshine::layout.div class="layout-page">
<x-moonshine::layout.header>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
<x-moonshine::breadcrumbs :items="['#' => 'Home']"/>
<x-moonshine::layout.search placeholder="Search" />
<x-moonshine::layout.locales :locales="collect()"/>
</x-moonshine::layout.header>
<x-moonshine::layout.content>
<article class="article">
Your content
</article>
</x-moonshine::layout.content>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
</x-moonshine::layout.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>
<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">
<x-moonshine::layout.div class="menu-header">
<x-moonshine::layout.div class="menu-logo">
<x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-actions">
<x-moonshine::layout.theme-switcher/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu menu--vertical">
<x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/>
</x-moonshine::layout.div>
</x-moonshine::layout.sidebar>
<x-moonshine::layout.div class="layout-main">
<x-moonshine::layout.div class="layout-page">
<x-moonshine::layout.header>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
<x-moonshine::breadcrumbs :items="['#' => 'Home']"/>
<x-moonshine::layout.search placeholder="Search" />
<x-moonshine::layout.locales :locales="collect()"/>
</x-moonshine::layout.header>
<x-moonshine::layout.content>
<article class="article">
Your content
</article>
</x-moonshine::layout.content>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
</x-moonshine::layout.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>
<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">
<x-moonshine::layout.div class="menu-header">
<x-moonshine::layout.div class="menu-logo">
<x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-actions">
<x-moonshine::layout.theme-switcher/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu menu--vertical">
<x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/>
</x-moonshine::layout.div>
</x-moonshine::layout.sidebar>
<x-moonshine::layout.div class="layout-main">
<x-moonshine::layout.div class="layout-page">
<x-moonshine::layout.header>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
<x-moonshine::breadcrumbs :items="['#' => 'Home']"/>
<x-moonshine::layout.search placeholder="Search" />
<x-moonshine::layout.locales :locales="collect()"/>
</x-moonshine::layout.header>
<x-moonshine::layout.content>
<article class="article">
Your content
</article>
</x-moonshine::layout.content>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
</x-moonshine::layout.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>
<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">
<x-moonshine::layout.div class="menu-header">
<x-moonshine::layout.div class="menu-logo">
<x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-actions">
<x-moonshine::layout.theme-switcher/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu menu--vertical">
<x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/>
</x-moonshine::layout.div>
</x-moonshine::layout.sidebar>
<x-moonshine::layout.div class="layout-main">
<x-moonshine::layout.div class="layout-page">
<x-moonshine::layout.header>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
<x-moonshine::breadcrumbs :items="['#' => 'Home']"/>
<x-moonshine::layout.search placeholder="Search" />
<x-moonshine::layout.locales :locales="collect()"/>
</x-moonshine::layout.header>
<x-moonshine::layout.content>
<article class="article">
Your content
</article>
</x-moonshine::layout.content>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
</x-moonshine::layout.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>
<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">
<x-moonshine::layout.div class="menu-header">
<x-moonshine::layout.div class="menu-logo">
<x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-actions">
<x-moonshine::layout.theme-switcher/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu menu--vertical">
<x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/>
</x-moonshine::layout.div>
</x-moonshine::layout.sidebar>
<x-moonshine::layout.div class="layout-main">
<x-moonshine::layout.div class="layout-page">
<x-moonshine::layout.header>
<x-moonshine::layout.div class="menu-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
<x-moonshine::breadcrumbs :items="['#' => 'Home']"/>
<x-moonshine::layout.search placeholder="Search" />
<x-moonshine::layout.locales :locales="collect()"/>
</x-moonshine::layout.header>
<x-moonshine::layout.content>
<article class="article">
Your content
</article>
</x-moonshine::layout.content>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
</x-moonshine::layout.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>