diff --git a/tests/Feature/ErrorPagesTest.php b/tests/Feature/ErrorPagesTest.php index 151f76ec..88dce129 100644 --- a/tests/Feature/ErrorPagesTest.php +++ b/tests/Feature/ErrorPagesTest.php @@ -97,6 +97,7 @@ it('lets the application\'s own error view win', function () { it('loads the application\'s Vite entries', function () { File::ensureDirectoryExists($this->temporary.'/build'); File::put($this->temporary.'/build/manifest.json', json_encode([ + 'workbench/resources/css/package.css' => ['file' => 'assets/package-probe.css', 'src' => 'workbench/resources/css/package.css', 'isEntry' => true], 'workbench/resources/css/app.css' => ['file' => 'assets/app-probe.css', 'src' => 'workbench/resources/css/app.css', 'isEntry' => true], 'workbench/resources/js/app.js' => ['file' => 'assets/app-probe.js', 'src' => 'workbench/resources/js/app.js', 'isEntry' => true], ])); @@ -106,6 +107,7 @@ it('loads the application\'s Vite entries', function () { $this->get('/abort/404') ->assertNotFound() + ->assertSee('build/assets/package-probe.css', false) ->assertSee('build/assets/app-probe.css', false) ->assertSee('build/assets/app-probe.js', false) ->assertDontSee('data-md-error-fallback', false); diff --git a/tests/Feature/StylesheetsTest.php b/tests/Feature/StylesheetsTest.php index 26366516..f4c21d0b 100644 --- a/tests/Feature/StylesheetsTest.php +++ b/tests/Feature/StylesheetsTest.php @@ -630,23 +630,35 @@ it('draws the interaction classes as the utilities the Tailwind components still ->and(stylesheetBlock($interaction, '.md-touch-target'))->toContain('min-width: var(--md-sys-measurement-space600);'); }); -it('declares the material layers in the Workbench between Tailwind\'s preflight and its utilities', function () { +it('builds the package in a Workbench entry of its own, with the material layers between Tailwind\'s preflight and its utilities', function () { + $package = stylesheetWithoutComments(File::get(__DIR__.'/../../workbench/resources/css/package.css')); $app = stylesheetWithoutComments(File::get(__DIR__.'/../../workbench/resources/css/app.css')); - // The first statement orders the layers: above preflight, whose `* { padding: 0 }` would - // otherwise beat a rewritten component's padding, and below the utilities. - expect(trim($app))->toStartWith('@layer theme, base, material, components, utilities;'); + // Both entries open by ordering the layers, so the order holds whichever the page links first: + // above preflight, whose `* { padding: 0 }` would otherwise beat a component's padding, and + // below the utilities. + foreach (['package.css' => $package, 'app.css' => $app] as $name => $css) { + expect(trim($css))->toStartWith('@layer properties, theme, base, material, components, utilities;', $name); + } - $all = strpos($app, "@import '../../../resources/css/all.css';"); - $tailwind = strpos($app, "@import 'tailwindcss'"); - $components = strpos($app, "@import '../../../resources/css/tailwind.css';"); - - expect($all)->toBeInt() - ->and($tailwind)->toBeGreaterThan($all) - ->and($components)->toBeGreaterThan($tailwind) - // material.css declared the tokens a second time; components.css and layout.css are the - // interim lists all.css replaced. - ->and($app)->not->toContain('material.css') - ->not->toContain("resources/css/components.css'") - ->not->toContain("resources/css/layout.css'"); + // The package's CSS never shares an entry with Tailwind: `@tailwindcss/vite` inlines the + // imports of an entry that uses Tailwind without deduplicating them, so every shared component + // stylesheet would repeat later in the cascade instead of landing once, as it does for an + // application (StylesheetsBundleTest.php pins that against a real Vite build). + expect($package)->toContain("@import '../../../resources/css/all.css';") + ->toContain("@import './material-scheme.css';") + ->not->toContain('tailwind') + ->not->toMatch('/@(?:source|theme|utility|variant|custom-variant|apply)\b/') + ->and($app)->toContain("@import 'tailwindcss'") + ->toContain("@import '../../../resources/css/tailwind.css';") + ->not->toContain('all.css') + ->not->toContain('foundation.css') + ->not->toContain('/components/') + ->not->toContain('material-scheme.css') + ->and(File::get(__DIR__.'/../../vite.config.js'))->toContain("'workbench/resources/css/package.css', 'workbench/resources/css/app.css'") + ->and(config('livewire-material.showcase.vite'))->toBe([ + 'workbench/resources/css/package.css', + 'workbench/resources/css/app.css', + 'workbench/resources/js/app.js', + ]); }); diff --git a/vite.config.js b/vite.config.js index 10751ea6..41cc97e1 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,7 +7,7 @@ import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ laravel({ - input: ['workbench/resources/css/app.css', 'workbench/resources/js/app.js'], + input: ['workbench/resources/css/package.css', 'workbench/resources/css/app.css', 'workbench/resources/js/app.js'], publicDirectory: 'workbench/public', refresh: ['resources/views/**', 'workbench/resources/views/**'], }), diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php index d10c6bc3..9fa1cfd7 100644 --- a/workbench/app/Providers/WorkbenchServiceProvider.php +++ b/workbench/app/Providers/WorkbenchServiceProvider.php @@ -19,6 +19,8 @@ class WorkbenchServiceProvider extends ServiceProvider public function boot(): void { config(['livewire-material.showcase.vite' => [ + // The package's CSS apart from Tailwind's, so Vite deduplicates its imports (package.css). + 'workbench/resources/css/package.css', 'workbench/resources/css/app.css', 'workbench/resources/js/app.js', ]]); diff --git a/workbench/resources/css/app.css b/workbench/resources/css/app.css index 74b26ce2..16fe7f68 100644 --- a/workbench/resources/css/app.css +++ b/workbench/resources/css/app.css @@ -1,16 +1,11 @@ -/* The Workbench builds the package as an application would, with explicit sources so the plans - in docs/ are not scanned for class names. The layer statement puts the `material` layers above - Tailwind's preflight (`base`) and below its `components` and `utilities`: a rewritten component's - padding, margin and border would otherwise lose to preflight's `* { padding: 0 }`, which the - material reset already carries, while every utility still outranks the package. all.css brings - the foundation, every layout stylesheet and every component stylesheet in one import; tailwind.css - brings the theme and token utilities the showcase still writes as classes, without declaring the - tokens a second time. */ -@layer theme, base, material, components, utilities; -@import '../../../resources/css/all.css'; +/* The Workbench's Tailwind entry, for the classes the showcase still writes, with explicit sources + so the plans in docs/ are not scanned for class names. tailwind.css brings the theme and token + utilities without declaring the tokens a second time. The package's own CSS is package.css, an + entry of its own (its header says why); the layer statement is the same in both, so the + `material` layers sit between Tailwind's preflight and its utilities whichever loads first. */ +@layer properties, theme, base, material, components, utilities; @import 'tailwindcss' source(none); @import '../../../resources/css/tailwind.css'; -@import './material-scheme.css'; @source '../../../resources/views'; @source '../../../src'; diff --git a/workbench/resources/css/package.css b/workbench/resources/css/package.css new file mode 100644 index 00000000..debfc3e1 --- /dev/null +++ b/workbench/resources/css/package.css @@ -0,0 +1,16 @@ +/* The package's CSS for the Workbench, built the way an application without Tailwind builds it: + all.css (the foundation, every layout and every component stylesheet) and the Workbench's + scheme, in an entry Tailwind never touches. Vite's own CSS pipeline inlines the imports here and + keeps a stylesheet several components import once, at its first position — the order an + application gets. `@tailwindcss/vite` inlines the imports of an entry that uses Tailwind without + that deduplication, so all.css imported beside `tailwindcss` would repeat every shared rule later + in the cascade; app.css keeps Tailwind, for the showcase's classes, apart. + + Both entries open with the same layer statement, so the order holds whichever the page links + first: the `material` layers above Tailwind's preflight (`base`), whose `* { padding: 0 }` would + otherwise beat a component's padding, and below its `components` and `utilities`. `properties` + is the layer Tailwind hoists to the top of its output (the `--tw-*` fallbacks for a browser + without `@property`); named first, it stays lowest, as it was when both shared one entry. */ +@layer properties, theme, base, material, components, utilities; +@import '../../../resources/css/all.css'; +@import './material-scheme.css';