aqua/blade-emerald
aqua/blade-emerald is a Laravel package that provides reusable Emerald-styled Blade components for building clean, consistent interfaces. Install via Composer, publish assets, and compose buttons, forms, cards, and layouts with simple Blade tags.
Install
composer require aqua/blade-emerald:^1.2
v1.2.0 is a compatibility release only — it adds declared support for Laravel 10 and PHP 8.2. There are no new directives, operators, or config keys, so anything written against v1.0/v1.1 continues to work. What changes is only your floor: if you are on PHP 8.2 and Laravel 10, this is the first version you should pin to.
Laravel's package auto-discovery should register the service provider; if not, add Aqua\BladeEmerald\BladeEmeraldServiceProvider::class to config/app.php. Check vendor/aqua/blade-emerald/config/ and publish if present:
php artisan vendor:publish --provider="Aqua\BladeEmerald\BladeEmeraldServiceProvider"
Before writing code, confirm the constraints resolve on your platform (composer why aqua/blade-emerald, or read vendor/aqua/blade-emerald/composer.json for the php and illuminate/* ranges). v1.2.0 explicitly names Laravel 10 and PHP 8.2; if you are on Laravel 11/12, verify what the constraint actually allows rather than assuming the range is open.
Where to look first
README.md – confirm the exact directive/helper name and supported abbreviation syntax. The package is small (31 stars, no dependents), so verify the API against the source rather than assuming Emmet parity.src/ – the abbreviation parser is the core; read it to learn which Emmet operators are actually supported (child >, sibling +, class ., id #, multiplication *, attributes []).config/ – any defaults for wrapper tags, class prefixes, or component namespace. Nothing in v1.2.0 adds config keys, so a config you published on v1.1 is still current — diff it after upgrading anyway.First use case
Generate a Blade component whose template is scaffolded from an Emmet-style abbreviation, then use it in a view:
php artisan make:emerald-component Card "div.card>h2.title+p.body"
Rendered inside a Blade view, the component emits the nested markup and exposes a default slot so you can inject content:
<x-card>
Your body copy here
</x-card>
The value is not the one-off scaffolding — it is replacing hand-written nesting in the 20-line component templates you write every week so the HTML tree stays declarative and the class lists stay consistent.
Inline wrapping in a view. The "wrap … with markup" framing implies a directive that takes a block and wraps it with the generated structure, roughly:
@emerald('section.hero>div.container')
<h1>{{ $title }}</h1>
@endemerald
Use this for repeated structural shells (hero sections, card grids, modals) that you do not want to promote to full components.
Scaffold-then-edit workflow. Treat the command as a generator, not a runtime dependency:
.blade.php.--force-safe temp path and diff).This keeps the blast radius small if the package stops being maintained — the output is plain Blade you own.
Component-as-wrapper pattern. Generate a wrapper component once, then nest:
{{-- resources/views/components/layout/panel.blade.php --}}
<div {{ $attributes->class(['panel', 'panel--'.$variant]) }}>
{{ $slot }}
</div>
<x-layout.panel variant="error">
@emerald('ul.list>li.item') ... @endemerald
</x-layout.panel>
$attributes and $slot merge naturally because the package only emits markup; it does not fight Blade's component attribute bag.
Consistency across a design system. If the package exposes a config for class prefixes or a tag allowlist, put your design-token classes there (card, card__header) rather than spelling them inline in every abbreviation. One change in config propagates to all abbreviations — which is exactly the abstraction benefit you are paying for.
CLI batch generation. Drive it from a shell loop or a custom Artisan command for one-off migrations:
for spec in "Card:div.card>h3.card-title+p.card-body" "Badge:span.badge"; do ...
done
Useful when retrofitting a legacy view into components — you get consistent shells fast, then paste the existing content into the slots.
Testing integration. Assert on rendered output, not on the abbreviation:
$this->blade('<x-card>Hi</x-card>')
->assertSee('class="card"', false)
->assertSee('Hi');
Because the package compiles to Blade, assertSee(..., false) on the escaped HTML is the most stable contract.
Pin a CI matrix that matches the declared support. Since v1.2.0 exists specifically to declare Laravel 10 / PHP 8.2 compatibility, encode that in CI: one job on PHP 8.2 + Laravel 10 that renders a generated component. That is the cheapest guard against the next compatibility release being needed and not shipped.
composer.json ("aqua/blade-emerald": "^1.2") and smoke-test one view after every Laravel upgrade. If it breaks at compile time, you can usually replace the directive with a static partial in minutes — the generated Blade is portable.composer.json constraints changed, which can shift dependency resolution in a large app. Run composer update aqua/blade-emerald --with-dependencies --dry-run first and read the diff; then render one generated component and one inline @emerald block before merging.>, +, ^, *, #, ., [], {} are supported. Text nodes ({...}) and grouping are the ones most often missing in small implementations.storage/framework/views, not your source. Inspect the compiled file to see what the parser produced — it is the fastest debug loop. Clear storage/framework/views after upgrading, since stale compiled views from the previous version mask (or fake) a compile problem.php artisan config:cache freezes it. During development, php artisan config:clear or run without caching; a common "it works in tinker but not in the browser" symptom is a stale config cache.{!! !!} or via a directive that does not escape). Do not double-escape by wrapping the directive output in {{ }} — you will get literal <div>.{{ $attributes }}, ensure the package does not emit its own class attribute that will collide. Prefer $attributes->class([...]) in the wrapper so consumer classes merge instead of overwrite.vendor/. A single maintenance release after a long gap is not a maintenance commitment — if you need fixes upstream, fork and reference your fork via a VCS repository entry in composer.json.How can I help you explore Laravel packages today?