laravel-frontend-presets/zurb-foundation
A Laravel frontend preset that swaps the default frontend scaffolding for Zurb Foundation. It publishes Foundation assets and matching authentication views so you can start building responsive Laravel apps with Foundation's grid and components using a simple preset command.
What it actually is: a Laravel frontend preset — a one-shot scaffolder that swaps the default Bootstrap/Vue scaffolding for Zurb Foundation 6: package.json deps, webpack.mix.js, resources/{assets,}/sass/app.scss, resources/{assets,}/js/bootstrap.js, and the Blade stubs (layouts/app.blade.php, welcome.blade.php, auth views).
What changed in v2.1.2: nothing but composer.json. The constraint was widened to allow Laravel 6.0 and up. No new stubs, no new command, no changes to the preset class. Practically this means composer require no longer fails on a 6.x/7.x app — but the php artisan preset command it hooks into was removed from the framework in 6.0, so a clean install on 6/7 is not the same as a working preset. See Gotchas.
Install (Laravel 5.5–5.8 era, which is where the command actually exists):
composer require laravel-frontend-presets/zurb-foundation --dev
php artisan preset zurb-foundation
npm install && npm run dev
Install on Laravel 6/7 (allowed since v2.1.2, but commandless): the package installs cleanly, but there is no preset artisan command to call. Either instantiate the preset class yourself from a throwaway command/tinker session, or skip the package and copy src/stubs/ into place by hand. On Laravel 9+ (Vite) treat it purely as a stub repository.
Where to look first: the preset class (src/Presets/*.php → install(), updatePackages(), updateWebpack(), updateAssets(), updateViews()) and the src/stubs/ directory. The stubs are the documentation: they tell you exactly which files will be overwritten, which is what you need to know before running it against a repo that already has views.
First use case: a Laravel 5.8 marketing/dashboard app where you want Foundation's XY grid, responsive top-bar, and a settings-driven design system instead of Bootstrap. Run the preset, npm run dev, then immediately move all design tokens into resources/sass/_settings.scss — that file is the whole point of using Foundation over hand-rolled CSS.
Commit (or stash) before running. The preset overwrites views and package.json, and it is not designed to merge with anything you've already customized.
Pattern 1 — settings-first customization. Never edit Foundation source or override compiled CSS. Everything goes through _settings.scss, imported before the framework:
// resources/sass/app.scss
@import 'settings'; // must come first
@import '~foundation-sites/scss/foundation';
@include foundation-everything($flex: true);
Enable flexbox mode ($global-flexbox: true) up front — float-grid vs flex-grid changes the markup classes (.row/.columns → .grid-x/.cell in 6.5+), and retrofitting it later means touching every Blade file.
Pattern 2 — trim the CSS with selective includes. Replace foundation-everything with only what you use; this is usually a 60–70% CSS cut:
@include foundation-global-styles;
@include foundation-xy-grid-classes;
@include foundation-typography;
@include foundation-forms;
@include foundation-button;
@include foundation-top-bar;
@include foundation-responsive-embedded;
Pattern 3 — JS init. The preset leaves you with a global init; keep it, but know the escape hatch for dynamically inserted DOM:
window.$ = window.jQuery = require('jquery');
require('what-input'); // before Foundation — drives [data-whatintent] styling
require('foundation-sites');
$(document).foundation();
// after an AJAX/Livewire/Turbo swap:
Foundation.reInit($('#my-panel')); // or Foundation.reInit('abide') for a plugin-wide reset
For Alpine/Livewire components, prefer per-instance construction over re-init: x-init="new Foundation.Reveal($el)" — it avoids the global $(document).foundation() rescanning the whole document.
Pattern 4 — Blade grid discipline. Wrap pages once and use Foundation's XY grid consistently:
<div class="grid-container">
<div class="grid-x grid-padding-x align-center">
<div class="cell small-12 medium-8 large-6">…</div>
</div>
</div>
Pattern 5 — auth scaffolding ordering. On 5.5–5.8, run php artisan make:auth before the preset if you want the preset's Foundation-styled auth views to win; running it after re-introduces Bootstrap-marked forms. Then diff the generated login/register views — the preset's stubs are the best real example of Foundation's Abide validation in Blade:
<input type="email" name="email" required data-invalid>
<p class="help-text" data-error-message>Enter a valid email.</p>
Pattern 6 — Motion UI and Reveal for interactivity. Motion UI is the reason to keep the preset's package.json block rather than rolling your own deps; wire animations through data-animate/$motion-ui settings rather than hand-written keyframes.
Pattern 7 — Mix integration. If ~foundation-sites won't resolve, point the SASS loader at node_modules instead of prefixing every import:
mix.sass('resources/sass/app.scss', 'public/css')
.webpackConfig({ resolve: { modules: ['node_modules'] } });
Pattern 8 — vendoring the stubs (the durable path on 6.0+). Copy src/stubs/ into your app once, adapt them to Vite/laravel/ui, and drop the dependency. Keeps you on modern Laravel without waiting for a preset release that will never come.
composer.json lets composer require succeed on Laravel 6/7, which reads like "now supported" — it isn't. The preset command was removed in 6.0 in favour of laravel/ui, and this package does not ship a replacement command or a service provider that registers one. Expect Command "preset" is not defined immediately after a successful install. Treat the constraint bump as installability, not compatibility.resources/assets/{sass,js}; 5.7+ use resources/sass, resources/js. Run the release tag that matches your framework, or the preset writes files into directories your Mix config doesn't read and "nothing happens."foundation-sites, jquery, what-input, motion-ui to package.json, and translate webpack.mix.js into vite.config.js. Note Vite/Dart Sass does not understand the ~ prefix: use css.preprocessorOptions.scss.includePaths: ['node_modules'] and import foundation-sites/scss/foundation bare.npm install on npm 7+ won't pull peer deps — add jquery explicitly and set window.$ = window.jQuery = require('jquery') before require('foundation-sites'), or you get Foundation is not defined / $ is not a function at runtime with a clean build.@import, darken(), and / division — noisy warnings on modern sass, and hard errors under Sass 2.0. Pin sass to a 1.x that still supports /-division or migrate the mixins you actually include..row→.grid-x and .columns→.cell. The preset's stub Blade views may use the older names depending on which foundation-sites version it pinned — check package.json against the markup before you conclude "the grid is broken."is-open, is-active, is-dropdown-submenu-parent, opens-left, show-for-sr are added by JS or composed at runtime. Safelist them (spatie/laravel-mix-purgecss safelist, or Tailwind's equivalent) or your menus silently stop opening in production only._settings.scss after foundation = zero effect. Variables must be set before @include foundation-everything. Likewise what-input before Foundation.package.json is replaced, not merged by most of these preset packages. Any custom npm deps in devDependencies get dropped. Diff package.json after running.package.json entries and re-clobber your edited views. Run once, then delete the package from require-dev.console.log(Foundation.version) — is the bundle loaded? (2) Did $(document).foundation() run after the markup existed? (3) Are the data-* attributes present (data-toggle, data-responsive-toggle, data-close)? (4) Is position: relative present on the parent of an absolutely-positioned dropdown?composer.json bump, no stub or command work since 2020. Budget an afternoon to port the stubs rather than expecting composer require to keep working across Laravel majors. The stubs are small and MIT-licensed — vendoring them into your app remains the durable move.foundation-emails + Inky) is not included. If you're building transactional mail, pair it with inky/inky-php and a separate Mix/Vite entry — it's the same design tokens with a different output pipeline.How can I help you explore Laravel packages today?