Skip to main content

The WireKit screens

If WireKit (pushery/wirekit) is installed, the sign-in screens render with WireKit components automatically — no configuration needed. Without it, the package serves its own dependency-free Blade views, so it works either way. Set ui.mode to blade to keep the plain views even when WireKit is present:

'ui' => [
'mode' => env('EMAIL_MAGIC_LINK_UI', 'auto'), // 'auto' | 'blade'
'vite' => ['resources/css/app.css'],
'styles' => [],
],

What the layout wires

WireKit renders with design-token CSS variables, Tailwind utility classes, and Alpine directives, so its views ship inside a layout that wires all three:

  • Design tokens — the layout injects WireKit's @wirekitStyles, which serves WireKit's own dist/wirekit.css (the --color-wk-*, --padding-wk-*, … tokens every component reads). This needs no build step and no vendor:publish; it is what keeps the screens from rendering unstyled.

  • Utility classes — the arbitrary-value utilities WireKit emits (bg-[var(--color-wk-bg-elevated)], …) are generated by your Tailwind build, so point it at WireKit's views in resources/css/app.css:

    @source '../../vendor/pushery/wirekit/resources/views/**/*.blade.php';

    The layout loads your compiled stylesheet via @vite (configurable with ui.vite, default resources/css/app.css; set it false for a non-Vite host) and/or the plain <link> URLs listed in ui.styles.

  • Behavior@livewireScripts and @wirekitScripts bring in Alpine.

Content-Security-Policy

Every tag these screens emit that a strict policy would otherwise reject carries the nonce from ui.script_nonce: the resend countdown's script tag (an external same-origin file), the layout's inline stylesheet, and the <link> and <script> WireKit itself writes. You do not have to configure that — leave script_nonce at null and the package finds your nonce on its own. See Configuration for how it looks.

One thing is still asked of your policy, and it comes from WireKit rather than from this package:

  • script-src must allow 'unsafe-eval', unless you switch WireKit's bundle. WireKit's components are driven by Alpine, and Alpine's default build compiles the expressions in x-data and @keydown with the Function constructor. On these screens that is the boxed one-time-code field: without 'unsafe-eval' its boxes stop advancing and pasting a whole code stops filling them. Nothing errors visibly — the field still accepts typing one box at a time, so the loss is easy to miss.

There are two answers:

  1. ui.mode = 'blade' needs no exception at all. The plain screens carry the same flow and use no Alpine and no Livewire.
  2. Livewire's CSP build. On these screens the Alpine that runs is Livewire's: @wirekitScripts force-injects Livewire's assets so Alpine reaches a page that renders no Livewire component, Livewire's tag carries no defer and executes first, and WireKit's own bundle registers its plugins on that Alpine and never starts one of its own. So the switch that matters is Livewire's — set csp_safe to true in config/livewire.php (Livewire 4), which serves a build that interprets expressions instead of constructing functions. Leave wirekit.scripts.bundle at its default: on these screens WireKit's csp bundle detects the Alpine already on the page, registers against it and starts nothing, so it changes the policy question not at all.

One further thing these screens need from you in a non-English application: WireKit's own screen-reader strings are translated by your application, not by this package. See Translations.

Only the look differs

The flow itself is unchanged — the same signed routes, CSRF-protected POSTs, and single-use token consumption. The bundled WireKit screens are covered by a real-browser suite that renders each one at desktop and mobile widths and asserts it is genuinely styled, not merely that the text is present.

Serving your own screens

Publish the views and edit them:

php artisan vendor:publish --tag=email-magic-link-views

They land in resources/views/vendor/email-magic-link. Published views win over both the WireKit and the plain bundled set.