pentiminax/ux-driver
A lightweight Laravel package that brings a driver-based approach to UX features, letting you define and swap user-experience behaviors through clean, configurable drivers. Integrates with Blade and Laravel's service container to keep interface logic organized, reusable, and easy to test.
This is a Symfony UX bundle (pentiminax/ux-driver), not a Laravel package — there is no service provider, config file, or Blade integration. Day-to-day it means: Composer install, bundle registration in config/bundles.php, and Stimulus controllers wired through StimulusBundle (importmap or Webpack Encore). If you are coming from Laravel, budget time for the Symfony/AssetMapper side before you get a single tour on screen; the bundle itself is thin.
Minimal path:
composer require pentiminax/ux-driver (PHP 8.2+, Symfony 7/8, StimulusBundle 2/3, TwigComponent 2/3, Twig 3.8+). Nothing here supports Symfony 6 or PHP 8.1.assets/controllers.json / controllers directory.<twig:Driver:Tour> wrapping a <twig:Driver:Step>, and confirm the payload reaches the controller.Where to look first, in this order:
StimulusContract — the single PHP↔JS contract (identifiers, values, targets, actions, data-*). Read this before anything else; it tells you exactly what the browser side expects.Model\Tour::DEFAULTS — now the one place defaults like showProgress live.Model\Options plus Model\Step / Model\Hint option tables — the canonical list of supported driver.js option names.Html\DriverOptions — the single list of options written with innerHTML, and the one place they're escaped. This is your trust boundary.First real use case: a welcome tour driven from a controller (PHP builder) rather than a template, because that's where options(array) and the shared overlay setters pay off fastest.
Pick one authoring mode per feature and stick to it. There are three (Twig Components, Twig builders, PHP builders) and they now all funnel through the same Model. Mixing them in one tour is where drift used to happen — and the refactors in 0.2.0 only reduced that risk inside the bundle, not in your code.
Bulk options instead of setter chains. Model\Tour::options(array) and Model\Hints::options(array) are the new happy path. Good pattern: keep tour config in YAML/DB/a PHP config array, map it straight into options(), and let Model\Options do validation/normalization. This is also what makes upgrading driver.js options cheap later.
Escaping belongs to the Model, not your templates. When a step title or description needs markup, pass it through and let Model\Step::titleHtml() / descriptionHtml() (and the Hint equivalents) do the work. If a custom template previously called the Twig component's titleHtml(), call the model instead.
Lean on Model\OverlayOptions for popover/overlay work. Tour and Hints share those setters now, so a helper that configures overlay behavior can target both without duplicating branches.
Treat a beacon hint's element as optional. Model\Hint::$element is nullable and getElement(): ?string. In declarative mode the element is the DOM node, so your PHP-side consumers must null-guard. Hints::addHint() still requires an element, so builder-mode code is unaffected — only read paths need the guard.
Adding an option is now a one-liner, in one of four places. New driver.js option → one entry in the relevant Model\Options table. Reaches innerHTML → one entry in Html\DriverOptions. New Stimulus value/target/action/data-* → StimulusContract, which StimulusContractTest cross-checks against assets/src/. New shared overlay setter → Model\OverlayOptions. Follow that map and you stay inside the design; deviate and you're back to four call sites.
Use the shorter payload as a testing win. Tours no longer emit animate, smoothScroll, allowClose unless you set them, so rendered JSON is smaller and more diffable. Assertions on the serialized payload are now a cheap regression net for tour shape.
Twig\Components\Step::titleHtml(), descriptionHtml(), sideValue() and alignValue() are gone. Shipped templates never used them; a custom step template that did will fatal. Fix: call the Model\Step / Model\Hint methods directly.animate, smoothScroll and allowClose are no longer pinned on <twig:Driver:Tour> — the values it used to send were the driver.js defaults, so behavior is identical but the serialized payload differs. Any snapshot test or JS assertion on raw options will go red for a non-bug. Update the fixtures, don't "fix" the code.showProgress is still pinned to true, now via Model\Tour::DEFAULTS. If you want it off, override it explicitly; don't assume the stripping applies to it too.Model\Hint is now nullable on $element. Property-level nullability plus a ?string return will trip static analysis (PHPStan/Psalm) and any code doing strlen($hint->getElement()). Also expect null to be a legitimate runtime value, not an error.Html\DriverOptions is now the single escaping pass for innerHTML options. Escaping in Twig with |e before handing content to a step will render entities. Conversely, if you bypass the Model and inject a raw options array, you get no escaping — the centralization is a guarantee only along the supported path.Model\Tour::DEFAULTS before hunting a bug in the component.Unknown beacon option "…". Serializable driver.js beacon options are: …, matching step and hint. Any test regexing on the message needs a touch-up.StimulusContractTest parses assets/src/ and compares against StimulusContract. Renaming an attribute on one side alone turns the suite red — that's the feature. Run the full suite, not just PHP unit tests, after touching controllers.How can I help you explore Laravel packages today?