gremo/zurb-ink-bundle
GremoZurbInkBundle integrates the ZURB Ink responsive email framework into Symfony, giving developers bundle configuration and helpers to build mobile-friendly HTML email templates for transactional messages within their applications.
Read this first: it's a Symfony bundle, not a Laravel package. There is no service provider, no config/zurb-ink.php to publish, and no facade. composer require gremo/zurb-ink-bundle in a Laravel app will pull in Symfony-era dependencies (the last tagged release is from August 2018) and give you nothing wired up. The realistic way a Laravel developer "uses" this package is as a reference implementation: it shows how to take an Ink-flavoured Twig/Blade template and turn it into email-safe HTML by running Ink's component parsing plus CSS inlining as a render step.
If you are actually on Symfony, the flow is: register the bundle in AppKernel/bundles.php, let it wire its Twig extension, then apply the provided filter to a template rendered from Ink markup — the bundle's job is essentially "render → parse Ink components → inline the stylesheet → hand back a string you can put in a Swift_Message/Email."
For a Laravel app, the minimal equivalent path is:
<link>/<script> pair) — you want ink.css plus the Ink components in your email layout.resources/views/emails/layouts/ink.blade.php with the standard Ink boilerplate (<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ...>, the <style> block with ink.css inlined via @include or a raw file read, and the ink.min.js script tag guarded by the Outlook conditional comment).tijsverkoyen/css-to-inline-styles, and you can call CssToInlineStyles directly for plain Blade mailables. That is the same conceptual slot the bundle fills.Mailable + Mail::to(...)->send(...), not through the bundle's message helpers.First use case: a welcome email with a two-column Ink grid and a button. Render it, inline the CSS, open the raw HTML in a browser, then run it through Litmus or Email on Acid before shipping. The bundle's value to you is the inlining step; the Ink framework itself is just HTML/CSS you vendor in.
Render-then-inline, not inline-in-template. Keep the styling in a <style> block in the layout and treat inlining as a post-processing pass. The bundle's Twig extension is built around this, and it's the only pattern that survives contact with Gmail (which strips <style> in some contexts but honours inlined attributes) alongside Outlook (which needs the conditional-comment fallbacks Ink emits).
Wrap the inliner in a small service or trait. In Laravel terms:
class InkedMailable extends Mailable
{
public function build()
{
$html = view('emails.welcome', $this->viewData)->render();
$html = (new CssToInlineStyles())->convert($html, $this->inkCss());
return $this->html($html)->subject('Welcome');
}
}
Cache $this->inkCss() (the contents of ink.css) — reading and re-parsing it on every send is wasted work in a queue worker that fires thousands of emails.
Mirror the bundle's extension point rather than its code. The bundle exposes its inliner as a Twig filter; the Laravel equivalent is a Blade directive or a View::composer that post-processes rendered email views. Pick one and standardise on it, because double-inlining (once in a composer, once in the Mailable) produces bloated, hard-to-debug output.
Keep Ink's JS out of the send path. ink.min.js is a preview-time / client-side polyfill for Outlook's ghost-table behaviour. You do not run it server-side. Ship the <script> tag in the layout wrapped in <!--[if mso]> ... <![endif]-->-adjacent conditionals and let the mail client deal with it. Any Node/PhantomJS step to "run Ink server-side" is a maintenance trap.
Preview route in local/staging. Add a Route::get('/emails/{view}') behind app()->isLocal() that renders the Mailable and dumps $mailable->render() straight to the browser. This is the single highest-value piece of tooling; the bundle gives you nothing here, and you'll iterate on templates far more than on send logic.
Test with Mail::fake() and assert on rendered HTML. Mail::assertSent(WelcomeMail::class, fn ($mail) => str_contains($mail->render(), 'background-color')) catches the classic "CSS didn't inline" regression, which is invisible in a normal feature test.
CssToInlineStyles (already a transitive dependency of laravel/framework) covers your need — it usually does.@media (max-width: 600px) blocks must stay in the <style> head. If your pipeline strips or fully inlines the stylesheet, mobile layouts silently break. The bundle's inliner, like every other, will drop or mangle these unless you keep the original <style> block in the output.<table> wrappers get no styles. If you write Ink components by hand (no JS expansion step), this is fine; if you rely on the JS to expand them, you have an ordering bug the bundle also has.mso- properties. Nothing in this bundle or in CSS inlining fixes Outlook's 120 DPI scaling or its lack of max-width. Budget for testing in actual Outlook, not just the HTML preview.!important stripping. Several inliners (including older CssToInlineStyles versions) handle !important inconsistently; Ink's own CSS leans on specificity, so validate the output rather than assuming it round-trips.Storage::put('debug/'.$mailable.'.html', $html)) and diff it against the pre-inline render. Nine out of ten "email looks broken" bugs are either a missing stylesheet include or a double-inline pass.EmailRenderer), inject it into mailables, and you can swap the inliner implementation later — or drop it entirely if you move to Markdown mailables — without touching a single template.How can I help you explore Laravel packages today?