Theming
MonkeysLegion’s UI stack is driven by MLView and a tiny compile-time pipeline:
Loader → Parser → Compiler → Renderer → ✨ HTML
^ ^
| |
└── ComponentNode / SlotNode ──┘
You edit plain .ml.php files under resources/views/.
Loader finds them, Parser builds an AST (using ComponentNode & SlotNode), Compiler turns that AST into cached PHP, and Renderer includes the cache with your data array. All this is wrapped in the MLView façade—so day-to-day you call just $view->render().
1 · Base directory layout
app/
├─ Controller/
├─ Entity/
├─ Repository/
│
resources/
└─ views/
├─ layouts/
│ └─ app.ml.php
├─ components/
│ └─ Card.php (class)
└─ home.ml.php
Hot reload: The dev-server watches resources/views/** and rebuilds caches in <50 ms.
2 · Creating your base layout
resources/views/layouts/app.ml.php
<!doctype html>
<html lang="en" class="h-full bg-gray-50">
<head>
<meta charset="utf-8"/>
<title>{{ $title ?? 'MonkeysLegion' }}</title>
@vite('resources/css/app.css')
</head>
<body class="min-h-full">
<header class="bg-primary text-white p-4">
<h1 class="text-2xl font-bold">{{ $header ?? 'My App' }}</h1>
</header>
<main class="container mx-auto py-8">
{{ $slot('default') }}
</main>
<footer class="py-4 text-center text-xs text-gray-500">
© <?= date('Y') ?> Example Inc
</footer>
</body>
</html>
{{ … }} is HTML-escaped; {!! … !!} prints raw.
3 · Re-usable components
app/View/Components/Card.php
namespace App\View\Components;
use MonkeysLegion\Template\Component;
final class Card extends Component
{
public function __construct(
public bool $bordered = false,
public bool $shadow = true,
){}
public function render(): string
{
$classes = "bg-white rounded-xl p-6";
$classes .= $this->bordered ? " border border-gray-200" : "";
$classes .= $this->shadow ? " shadow-md" : "";
return <<<ML
<div class="$classes">
<h3 class="text-lg font-semibold mb-2">{{ \$this->slot('title') }}</h3>
<div>{{ \$this->slot('default') }}</div>
</div>
ML;
}
}
Use it in any view:
<x-card bordered>
<x-slot:title>Welcome!</x-slot:title>
Thanks for trying MonkeysLegion.
</x-card>
ComponentNode & SlotNode in the parser AST guarantee slots are wired correctly.
4 · Live example (home.ml.php)
resources/views/home.ml.php
<x-layout>
<x-slot:title>Hello ✨</x-slot:title>
<x-card>
<x-slot:title>Quick start</x-slot:title>
Edit <code>resources/views/home.ml.php</code> and watch me hot-reload!
</x-card>
</x-layout>
The first request:
Loader grabs the template.
Parser detects <x-layout> → ComponentNode, two SlotNodes.
Compiler emits PHP to .ml.cache.php under storage/cache/views/.
Renderer includes that cache with your data.
Subsequent requests skip 1-3 until the source file’s mtime changes.
5 · Styling with Tailwind (default)
tailwind.config.js
module.exports = {
content: ["../resources/views/**/*.ml.php"],
theme: {
extend: {
colors: { primary: "#4F46E5" }
}
},
darkMode: 'class',
}
Run Vite in watch mode:
npm run dev
Tailwind purges unused classes on npm run build.
6 · Dark mode toggle
In layouts/app.ml.php:
<html class="{{ $prefersDark ? 'dark' : '' }}">
Persist preference in a cookie or localStorage and flip the class from JS.
7 · Config-driven theming
config/theme.mlc
colors.primary = "#FF5733"
fonts.body = "Inter, sans-serif"
Retrieve in the layout:
$theme = $config->get('colors');
<header style="background: <?= $theme['primary'] ?>">
Override per-environment with config/env/prod.mlc (e.g. white-label branding).
8 · Emails
MLView works for transactional emails too:
resources/views/emails/welcome.ml.php
<x-email::layout>
<x-slot:heading>Welcome, {{ $user->name }}!</x-slot:heading>
We’re thrilled you joined.
</x-email::layout>
Email\Layout component outputs inlined CSS.
9 · Testing your theme
Tool | What to test |
---|---|
Playwright | Visual screenshots across break-points |
Lighthouse CI | CLS, LCP, bundle size budgets |
axe-core | WCAG 2.1 accessibility violations |
Integrate these in GitHub Actions for every PR.
Recap
Loader → Parser → Compiler → Renderer pipeline keeps render time fast.
The AST uses ComponentNode and SlotNode to wire slots safely.
Tailwind + Vite give instant hot-refresh and production minification.
Config tokens (theme.mlc) let you re-brand without touching templates.
Happy theming! 🎨🦄