Inline the error layout's own rules beside the app's build

Plan step 46, from SealShare's breakpoint walk: an application imports
the stylesheets its own views render, and none renders the error layout,
so under a real build its 403/404/500 pages drew unstyled. With a build
the page now keeps the app's Vite tags (foundation, tokens, scheme, font)
and inlines Stylesheets::bundle() of components/error-page.css and its
imports beside them (ErrorPage::layoutStyles(), cached). Without a build
the fallback is unchanged. An application imports nothing for its error
pages; one that imports all.css gets the same layered rules twice, which
is harmless.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-09-15 16:10:01 +02:00
co-authored by Claude Opus 5
parent 05f115ab36
commit 70c983b7bb
8 changed files with 194 additions and 29 deletions
+55
View File
@@ -3,6 +3,9 @@
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Vite;
use Illuminate\Support\Str;
use NoNameWeb\LivewireMaterial\Support\Stylesheets;
use function Orchestra\Testbench\workbench_path;
/**
* The 404 page, reached the way a visitor reaches it: an address nothing answers.
@@ -95,3 +98,55 @@ it('draws the button and the shape from the inlined fallback stylesheet, and rep
File::deleteDirectory($public);
}
});
/**
* Plan step 46: an application's own build, which imports the stylesheets its views render and so
* never `error-page.css` — SealShare's didn't, and its error pages drew a shape filling the window
* with the headline unstyled in a corner. The probe build here is the foundation and the Workbench's
* scheme and nothing else, so everything the layout draws must come from the rules the page inlines
* beside the application's `<link>`.
*/
it('draws the layout from its own inlined rules under a build that never imports them', function () {
$public = sys_get_temp_dir().'/livewire-material-build-'.Str::random(8);
File::ensureDirectoryExists($public.'/build/assets');
// The foundation and the scheme, as an application's bundle would carry them; without the font
// face, whose relative url would 404 from here and is no part of what this test proves.
$css = Stylesheets::bundle([
realpath(__DIR__.'/../../resources/css/foundation.css'),
workbench_path('resources/css/material-scheme.css'),
]);
File::put($public.'/build/assets/app-probe.css', (string) preg_replace('/@font-face\s*\{[^}]*\}/', '', $css));
File::put($public.'/build/manifest.json', json_encode([
'resources/css/app.css' => ['file' => 'assets/app-probe.css', 'src' => 'resources/css/app.css', 'isEntry' => true],
]));
$originalPublicPath = app()->publicPath();
$originalVite = config('livewire-material.showcase.vite');
app()->usePublicPath($public);
Vite::useHotFile($public.'/hot');
config(['livewire-material.showcase.vite' => ['resources/css/app.css']]);
try {
missingPage('light')
->resize(1280, 800)
->assertScript("document.querySelector('link[href*=\"/build/assets/app-probe.css\"]') !== null")
->assertScript("document.querySelector('style[data-md-error-styles]') !== null")
->assertScript("document.querySelector('style[data-md-error-fallback]') === null")
// The layout: a centred column, the art at its 240px from medium, not a shape filling the window.
->assertScript("getComputedStyle(document.querySelector('[data-md-error-page]')).display === 'flex'")
->assertScript("Math.round(document.querySelector('[data-md-error-art]').getBoundingClientRect().width) === 240")
->assertScript("document.querySelector('[data-md-error-shape] svg').getBoundingClientRect().width <= 240")
->assertScript("(() => { const r = document.querySelector('[data-md-error-headline]').getBoundingClientRect(); return Math.abs((r.left + r.right) / 2 - window.innerWidth / 2) <= 2; })()")
// The button and shape rules the layout imports, and the scheme from the application's build.
->assertScript("getComputedStyle(document.querySelector('[data-md-button]')).borderRadius !== '0px'")
->assertScript(paintedIn('[data-md-button][data-md-variant=filled]', 'primary'))
->assertScript(paintedIn('body', 'surface'))
->assertNoJavaScriptErrors();
} finally {
config(['livewire-material.showcase.vite' => $originalVite]);
app()->usePublicPath($originalPublicPath);
Vite::useHotFile($originalPublicPath.'/hot');
File::deleteDirectory($public);
}
});