Everyone

Creating a Minimal Template

Step-by-step guide to building a custom Opterius Mail theme from scratch, starting with just a layout file.

Last updated 2026-04-12

Creating a Minimal Template

Thanks to the template fallback system, you do not need to recreate every view to build a custom theme. You only need to create the views you actually want to change. The minimum viable template is a single layouts/app.blade.php file — everything else falls back to the default template automatically.

This guide walks through creating a working theme from scratch.

Step 1 — Create the Template Directory

cd /opt/opterius-mail
mkdir -p resources/views/templates/mytheme/layouts

Replace mytheme with your chosen template name. Use lowercase letters, numbers, and hyphens only — no spaces.

Step 2 — Create the Layout File

Create resources/views/templates/mytheme/layouts/app.blade.php. This is the only required file. Here is a complete minimal example with Tailwind CSS loaded from CDN:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>@yield('title') — {{ config('app.name') }}</title>

    {{-- Tailwind CSS via CDN (suitable for development; use compiled CSS in production) --}}
    <script src="https://cdn.tailwindcss.com"></script>

    {{-- Alpine.js --}}
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body class="bg-gray-50 text-gray-900 min-h-screen flex">

    {{-- Sidebar --}}
    <aside class="w-56 bg-white border-r border-gray-200 flex flex-col p-4 shrink-0">
        <div class="font-bold text-lg mb-6">Opterius Mail</div>

        <nav class="space-y-1">
            @foreach ($folders as $folder)
                <a href="{{ route('inbox.index', ['folder' => $folder['name']]) }}"
                   class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm
                          {{ $currentFolder === $folder['name'] ? 'bg-blue-50 text-blue-700 font-medium' : 'text-gray-700 hover:bg-gray-100' }}">
                    <span>{{ $folder['name'] }}</span>
                    @if ($folder['unseen'] > 0)
                        <span class="ml-auto text-xs bg-blue-500 text-white rounded-full px-2 py-0.5">
                            {{ $folder['unseen'] }}
                        </span>
                    @endif
                </a>
            @endforeach
        </nav>

        <div class="mt-auto pt-4 border-t border-gray-200">
            <form method="POST" action="{{ route('logout') }}">
                @csrf
                <button class="text-sm text-gray-500 hover:text-gray-900">Sign out</button>
            </form>
        </div>
    </aside>

    {{-- Main content area --}}
    <main class="flex-1 flex flex-col min-h-screen">

        {{-- Top bar --}}
        <header class="bg-white border-b border-gray-200 px-6 py-3 flex items-center gap-4">
            <form action="{{ route('search.index') }}" method="GET" class="flex-1 max-w-xl">
                <input type="text" name="q" placeholder="Search mail…"
                       class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
            </form>
            <a href="{{ route('compose.index') }}"
               class="bg-blue-600 text-white text-sm px-4 py-2 rounded-lg hover:bg-blue-700">
                Compose
            </a>
        </header>

        {{-- Page content --}}
        <div class="flex-1 p-6">
            @yield('content')
        </div>

    </main>

    {{-- Page-specific scripts --}}
    @stack('scripts')

</body>
</html>

Step 3 — Activate the Template

Add to /opt/opterius-mail/.env:

MAIL_UI_TEMPLATE=mytheme

Then clear the view cache:

php artisan view:clear
php artisan config:clear

Visit your webmail URL. You should see your new layout with the default template's content views inside it (inbox, compose, etc. all render from the default template via fallback).

Step 4 — Verify the Fallback Is Working

Log in to webmail. You should see:

  • Your custom sidebar and header from mytheme/layouts/app.blade.php.
  • The inbox message list from default/inbox/index.blade.php (rendered inside your @yield('content') area).

If you see a blank page or errors, check storage/logs/laravel.log for details.

Step 5 — Customising Individual Views

To override a specific view, copy it from the default template and edit your copy:

# Override the inbox list
cp resources/views/templates/default/inbox/index.blade.php \
   resources/views/templates/mytheme/inbox/index.blade.php

# Override the login page
mkdir -p resources/views/templates/mytheme/auth
cp resources/views/templates/default/auth/login.blade.php \
   resources/views/templates/mytheme/auth/login.blade.php

Edit the copies as needed. You only need to copy the views you want to change — all others continue using the default.

Step 6 — Compile CSS for Production

The Tailwind CDN approach used in the example above is fine for development and low-traffic deployments. For production, compile Tailwind properly to remove unused utility classes and improve performance:

# On your development machine (NOT on the live server)
cd /opt/opterius-mail
npm install
npx tailwindcss -i resources/css/app.css -o public/css/mytheme.css --minify

Then replace the CDN <script> tag in your layout with a compiled stylesheet:

<link rel="stylesheet" href="{{ asset('css/mytheme.css') }}">

Copy the compiled file to the server and include it in your repository. Never run npm build on the live server.

Next Steps

  • Read Template Variables Reference to understand what data is available in each view.
  • Read Layout Contract for the complete list of requirements for layouts/app.blade.php.
  • Share your template with the community by publishing it as opterius-mail-template-{name} on GitHub.