jalendport / spark-craft
Craft CMS starter project
Package info
github.com/jalendport/spark-craft
Language:Vue
Type:project
pkg:composer/jalendport/spark-craft
Fund package maintenance!
Requires
- craftcms/ckeditor: ^5.6.0
- craftcms/cms: ^5.10.0
- nystudio107/craft-emptycoalesce: ^5.0.0
- nystudio107/craft-typogrify: ^5.0.0
- nystudio107/craft-vite: ^5.0.0
- vaersaagod/seomate: ^3.3.0
- verbb/expanded-singles: ^3.0.0
- vlucas/phpdotenv: ^5.6.0
- yiisoft/yii2-redis: ^2.0.0
Requires (Dev)
- craftcms/generator: ^2.2.0
- phpstan/phpstan: ^2.2.0
- yiisoft/yii2-shell: ^2.0.0
This package is auto-updated.
Last update: 2026-07-17 02:28:02 UTC
README
An opinionated Craft CMS 5 starter project by Jalen Davenport. It ships a Dockerized local stack, a modern Vite frontend, and a Twig/Vue template skeleton so a new site starts from a working, conventional baseline instead of a blank slate.
Stack at a glance
- Craft CMS 5.10 on PHP 8.4
- Vite 8 with Vue 3.5 and Tailwind CSS 4 (CSS-first config)
- Docker for the full local stack: MySQL, nginx, Node/Vite, PHP-FPM, a queue worker, Redis, and Mailpit
- Plugins: CKEditor, SEOMate, Expanded Singles, Empty Coalesce, Typogrify
Requirements
- Docker Desktop (or a compatible engine) for the local stack
- Node 22+ and npm, only for host-side tooling such as Prettier and the SVG optimizer — the Vite dev server itself runs inside the
nodecontainer
Getting started
Create a project from the boilerplate:
composer create-project jalendport/spark-craft <dir>
While spark-craft is in beta there's no stable release for Composer's default stability to match, so pass the stability flag: composer create-project jalendport/spark-craft <dir> --stability=beta.
Composer's post-create-project-cmd then runs automatically and:
- Renames
.env.example.devto.env. - Generates the app keys (
craft setup/keys), which populatesCRAFT_APP_IDandCRAFT_SECURITY_KEYin.env. - Swaps in the real
composer.json(see below) and dumps an optimized autoloader.
The dual composer.json mechanism
The repo tracks two files: composer.json and composer.json.project. The first is what composer create-project reads to build your project — it carries the post-create-project-cmd script and project metadata. composer.json.project is the leaner manifest your project should actually live with (it drops the create-time scaffolding and adds the craft-update / phpstan scripts). During post-create, the original composer.json is deleted and composer.json.project is renamed into its place. After creating a project you'll only have a single composer.json; the .project variant only matters inside this boilerplate repo.
Once created, bring the stack up (see below), then install Craft and enable the bundled plugins:
docker compose exec php ./craft install docker compose exec php sh -c 'for plugin in vite seomate typogrify empty-coalesce ckeditor expanded-singles; do ./craft plugin/install $plugin; done'
(You can also install from the browser at http://localhost:8000/admin, but the plugins still need plugin/install — the composer packages alone don't enable them.)
Docker workflow
docker-compose.yml defines the local services:
| Service | Image / build | Host port |
|---|---|---|
mysql |
jalendport/spark-mysql |
3307 → 3306 |
nginx |
jalendport/spark-nginx |
8000 → 80 (the site) |
node |
local build (Vite) | 8080 (dev server) |
php |
local build (PHP-FPM) | internal only |
queue |
local build | internal only (queue worker) |
redis |
redis:7-alpine |
internal only |
mailpit |
axllent/mailpit |
8025 (web UI, overridable) |
Start everything:
docker compose up
The site is served at http://localhost:8000, the Vite dev server at http://localhost:8080, and Mailpit's inbox UI at http://localhost:8025 (set MAILPIT_UI_PORT in .env when another project already claims 8025).
On first boot the node container runs npm install for you. Keep it that way: Vite's bundler ships platform-native binaries, so a node_modules/ installed from the macOS host won't run inside the Linux container. If you ever install on the host (e.g. for editor tooling), delete node_modules/ and restart the node container to get a container-native install back.
Run Craft console commands inside the php container:
docker compose exec php ./craft <command>
Scaffold Craft classes with the generator:
docker compose exec php ./craft make <type> --with-docblocks
Frontend
The frontend source lives in src/, and Vite is configured in config/frontend/vite.config.js. There are two entry points, src/js/main.js and src/css/main.css, injected into Twig via the craft-vite plugin (craft.vite.script(...) / craft.vite.asset(...)). Built assets land in web/dist/.
npm scripts:
npm run dev— start the Vite dev server (normally run for you by thenodecontainer)npm run build— production buildnpm run preview— preview a production buildnpm run svg— optimize icons inweb/assets/icons/{16,20,24}with SVGO
Tailwind 4
Design tokens live in src/css/theme.css using Tailwind 4's CSS-first @theme block — there is no tailwind.config.js. Notable conventions:
- Semantic brand tokens.
--color-brand-accent,--color-brand-heading,--color-brand-body, and theiron-*counterparts are placeholders meant to be swapped per project. Every colored background gets anon-*pair so contrast decisions live in the theme, not scattered across templates. - 1px spacing grid.
--spacingis set to1px, so utilities map directly to pixels (p-20is 20px,text-16is 16px). - Fluid values via
tailwind-clamp, clamped between thexsand2xlbreakpoints (e.g.clamp-[text,34,48]).
Vue
The Vue app auto-registers components from src/js/vue/components/ using import.meta.glob:
*.vuefiles are registered eagerly.*.lazy.vuefiles are registered as async components. An optional*.loading.vueand*.error.vuecompanion (matched by name) supply the loading and error states.
Because the glob is evaluated at startup, a Vite plugin restarts the dev server when a component file is added or removed.
The kit ships a set of form components (FormInput, FormSelect, FormCheckbox, FormRadio, FormFileUpload, etc.) and a SuperForm wrapper built on vee-validate and yup, plus composables in src/js/vue/composables/ and framework-agnostic helpers in src/js/utils/.
Icons and fonts
- Icons are optimized SVGs in
web/assets/icons/{16,20,24}(organized by pixel size) and rendered inline via Craft'ssvg()function — for example the_components/button.twigleftIcon/rightIconparams. - Fonts are self-hosted: drop
woff2files intoweb/assets/fonts/, uncomment the@font-faceblocks insrc/css/components/typography.css, and point--font-sans/--font-displayat the family intheme.css.
Templates
Templates live in src/templates/ — the templates path is moved there by the CRAFT_TEMPLATES_PATH constant defined in config/craft/bootstrap.php.
_router.twigis the dispatch convention: point a section's template at it and itincludes_entries/<section>/<type>, falling back to_entries/<section>, so each entry type gets its own template without per-section routing boilerplate._layouts/holds base layouts; templatesextend_layouts/base._components/are self-contained, parameterized includes rendered with theinclude … with { … } onlystyle so nothing leaks in from the parent scope._partials/is reserved for Craft element partials._includes/holds shared chrome such as the header, footer, and favicon._errors/holds error templates;errorTemplatePrefix(set inconfig/craft/general.php) points Craft's error pages at that folder.
Config
Craft config lives in config/craft/:
general.php,app.php,vite.php,seomate.php, plus a sharedbootstrap.phpthat defines the base/templates/config paths and loads the environment.- Redis is wired as the cache backend and Mailpit as the dev mailer in
app.php, both driven by env vars.
Environment files come as a per-environment trio: .env.example.dev, .env.example.staging, and .env.example.production. Post-create copies the dev variant to .env; copy the staging/production variants onto their respective servers.
Quality
- PHPStan —
composer phpstan, configured inphpstan.neonat level 5 and scoped tosrc/modules. - Prettier with
prettier-plugin-tailwindcssfor class sorting (config in.prettierrc). .editorconfig— tabs, 4-wide (2-space for YAML and JSON).
Deployment
Two GitHub Actions workflows, built on the reusable composite actions in jalendport/spark-github-actions, handle deploys:
.github/workflows/build-and-deploy--staging.yml— deploysdevelop..github/workflows/build-and-deploy--production.yml— deploysmaster.
Each builds (composer install, npm install, npm run build), rsyncs to the server, and triggers a Laravel Forge deployment. Set these repository secrets, per environment (prefix with STAGING_ and PRODUCTION_):
SSH_HOSTSSH_USERSSH_KEYSSH_PATHFORGE_DEPLOY_TRIGGER_URL
Note: the composite actions currently pin
php-8.3andnode-22variants, while the runtime images are on PHP 8.4.
Releases
The project follows git-flow: work merges into develop, releases are cut on release/* branches and merged to master. Changes are recorded in CHANGELOG.md following Common Changelog. Pushing a tag triggers .github/workflows/release.yml, which creates the GitHub release (tags containing alpha or beta are marked pre-releases).