License Key Required
No license key entered.
Enter or update your key with php artisan health:license <key>,
or purchase one at scriptgain.com.
Login
auth.login
Compiled PHP
What Blade turns your template into. This is what the syntax check parses.
Loading…
Available Variables And Components
A guide to what this template can use, derived from the shipped source. Click any item to insert it at your cursor. It is a helpful reference, not a runtime guarantee.
Always Available
Shared with the admin panel by the staff layout.- Validation error bag. Always present. Pairs with @error('field').
- The admin sidebar navigation structure.
- Sub-items of the currently open nav group.
- Breadcrumb trail for the current page.
- Signed-in staffer's initials, for the avatar.
- Signed-in staffer's name.
- Signed-in staffer's email address.
- The staffer's role, e.g. "Administrator".
- True when the signed-in staffer is an administrator.
- Number of open service requests, for the badge.
- Number of unread form submissions, for the badge.
- The configured admin-shell width class.
- URL of the uploaded site logo, or null.
- URL of the uploaded favicon, or null.
In This Template
- Demo Personas.
Drop any of these in. Click to insert the opening tag at your cursor.
General
Admin Panel
Layouts
Records
Site (Public)
Common Blade
- Print a value, safely escaped. The default choice.
- Print raw HTML. Only for content you trust.
- Show something conditionally.
- Loop over a collection.
- Loop, with a fallback when the collection is empty.
- Show a validation message for a field.
- A small block of PHP. Never on line 1. Keep logic out of templates.
- A comment that never reaches the browser.
Civic Helpers
- Build a URL to a named route, e.g. a department page.
- Link to a single news post.
- Turn a stored upload path into a public URL.
- A cache-busted URL for a bundled asset.
- Read a config value, e.g. the site width class.
- Build an absolute URL to a path.
No History Yet
Every save, revert, and reset of this template is recorded here, with a one-click way back to any of them.
The Template That Shipped With The Product
Read only. This is what Reset To Default puts back.
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sign In – {{ config('brand.name') }}</title>
<x-tailwind-cdn />
<x-accent-style />
</head>
<body class="h-full bg-slate-50">
<div class="min-h-full flex flex-col lg:flex-row">
{{-- Brand panel --}}
<div class="lg:w-1/2 bg-chrome text-white px-8 py-12 lg:p-16 flex flex-col justify-between">
<x-brand class="text-white" />
<div class="hidden lg:block max-w-md">
<h2 class="text-3xl font-semibold tracking-tight">One place to run your school.</h2>
<p class="mt-4 text-slate-300 leading-relaxed">
Students, faculty, courses, grades, and attendance — with a portal
for teachers and one for students and families, all in a single
self-hosted platform.
</p>
<ul class="mt-8 space-y-3 text-sm text-slate-300">
<li class="flex items-center gap-2"><x-icon name="check-circle" class="w-5 h-5 text-brand-400" /> Courses, sections, and enrollment</li>
<li class="flex items-center gap-2"><x-icon name="check-circle" class="w-5 h-5 text-brand-400" /> Gradebook and daily attendance</li>
<li class="flex items-center gap-2"><x-icon name="check-circle" class="w-5 h-5 text-brand-400" /> Teacher, student & family portals</li>
</ul>
</div>
<p class="text-xs text-slate-400">{{ config('brand.name') }} · {{ config('brand.tagline') }}</p>
</div>
{{-- Form panel --}}
<div class="lg:w-1/2 flex items-center justify-center px-6 py-12">
<div class="w-full max-w-sm">
<h1 class="text-2xl font-semibold tracking-tight text-slate-900">Sign In</h1>
<p class="mt-1 text-sm text-slate-500">Welcome back. Enter your credentials to continue.</p>
@if ($errors->any())
<div class="mt-6">
<x-alert type="danger">{{ $errors->first() }}</x-alert>
</div>
@endif
<form method="POST" action="{{ route('login') }}" class="mt-6 space-y-5">
@csrf
<x-field label="Email" for="email" required>
<x-input id="email" name="email" type="email" :value="old('email')" required autofocus autocomplete="username" placeholder="you@example.com" />
</x-field>
<x-field label="Password" for="password" required>
<x-input id="password" name="password" type="password" required autocomplete="current-password" placeholder="••••••••" />
</x-field>
<x-toggle name="remember" label="Remember Me" />
<x-captcha context="login" />
<x-button type="submit" class="w-full">Sign In</x-button>
</form>
@if (\App\Services\Auth\OAuthSettings::anyEnabled())
<div class="mt-6 flex items-center gap-3">
<span class="h-px flex-1 bg-slate-200"></span>
<span class="text-xs uppercase tracking-wide text-slate-400">Or</span>
<span class="h-px flex-1 bg-slate-200"></span>
</div>
<div class="mt-4 space-y-2">
@foreach (\App\Services\Auth\OAuthSettings::enabledProviders() as $provider)
<a href="{{ route('auth.redirect', $provider) }}" class="flex w-full items-center justify-center gap-2 rounded-lg border border-slate-300 bg-white px-4 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-50 transition">
<x-icon name="key" class="w-4 h-4 text-slate-400" /> Sign in with {{ \App\Services\Auth\OAuthSettings::label($provider) }}
</a>
@endforeach
</div>
@endif
@if (! empty($demoPersonas))
{{-- Only rendered when the request IP matches the dev_login_ip
setting. The endpoint enforces the same check, so these
buttons are a convenience, not the security boundary. Each
signs straight in as a staff persona, bypassing the login
captcha as the trusted shortcut it is. --}}
<div class="mt-6 section-divider pt-6">
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-slate-400">Demo Logins</p>
<p class="mt-1 text-xs text-slate-500">One click to preview each staff experience. Visible only from your allowlisted IP address.</p>
<div class="mt-3 space-y-2">
@foreach ($demoPersonas as $persona)
<form method="POST" action="{{ route('dev-login', $persona['key']) }}">
@csrf
<button type="submit"
class="w-full flex items-start gap-3 rounded-lg bg-white px-3 py-2.5 text-left ring-1 ring-inset ring-slate-300 transition hover:bg-slate-50 hover:ring-brand-300">
<span class="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-brand-50 text-brand-700 ring-1 ring-inset ring-brand-200">
<x-icon :name="$persona['icon']" class="h-4 w-4" />
</span>
<span class="min-w-0">
<span class="block text-sm font-semibold text-slate-800">{{ $persona['label'] }}</span>
<span class="block text-xs text-slate-500">{{ $persona['description'] }}</span>
<span class="mt-0.5 block text-xs text-slate-400">As {{ $persona['name'] }}</span>
</span>
</button>
</form>
@endforeach
</div>
</div>
@endif
</div>
</div>
</div>
</body>
</html>
Blade In One Screen
The directives you will use most.
- Print a value, escaped. Safe by default.
- Print raw HTML. Only for content you trust.
- Show something conditionally.
- Loop over a collection.
- Use a shared component. Every component in Shared Components is available.
- A comment that never reaches the browser.
{{ $variable }}
{!! $html !!}
@if / @else / @endif
@foreach / @endforeach
<x-card>
{{-- comment --}}
House Rules
Follow these and your edits will survive updates.
-
Never start line 1 with
@php. Blade mis-parses it and the page fails. The editor refuses to save it. - Keep logic out of templates. If you need a calculation, it belongs in a composer or component class, not here.
- The shipped file is never modified. Reset To Default always gets you back.
- Every save is versioned. You can revert to any earlier version in one click.
- Press Tab inside the editor to indent rather than jump to the next field.