twig/cssinliner-extra
Twig extension adding the inline_css filter to inline CSS styles into HTML documents. Useful for rendering emails and templates with CSS applied directly to elements, improving compatibility with clients that strip or ignore external styles.
Install the Package
composer require twig/cssinliner-extra
For Laravel with TwigBridge:
composer require twig/laravel
Register the Extension
In config/view.php (Laravel) or your Twig environment setup:
Twig\Extension\CssInlinerExtraExtension::class,
First Use Case
Inline CSS for a static HTML template (e.g., resources/views/email/welcome.twig):
{{ content|inline_css }}
Or for a Blade template (via custom directive or Twig bridge):
@twig({{ $html }}, inline_css)
Verify Output
Check the rendered HTML for inlined styles (no external <link> or <style> tags).
Static HTML Optimization Inline CSS for emails, marketing pages, or static site exports:
{# resources/views/email/newsletter.twig #}
{{ email_content|inline_css }}
Dynamic Content with Caution Use for user-generated HTML (e.g., CMS content) but sanitize input first:
{{ cms_content|inline_css(exclude=['.dynamic-class']) }}
Conditional Inlining Skip inlining for non-critical pages (e.g., admin dashboards):
{% if app.environment == 'production' %}
{{ page_html|inline_css }}
{% else %}
{{ page_html }}
{% endif %}
Integration with Asset Pipelines Combine with Laravel Mix/Vite for hybrid workflows:
// mix.js
mix.postProcess('public/css', (cssFiles) => {
return Promise.all(cssFiles.map(file => {
return inlineCss(file.content); // Custom wrapper for twig/cssinliner-extra
}));
});
Email Optimization Workflow
resources/views/emails/welcome.twig).{{ email_body|inline_css }}
Marketing Page Optimization
// routes/web.php
Route::get('/marketing', function () {
return view('marketing.landing', [
'content' => file_get_contents('marketing-content.html'),
]);
});
{{ content|inline_css }}
Headless CMS Export
// console/commands/BuildStaticSite.php
public function handle() {
$html = $this->twig->render('templates/page.twig');
$inlined = $this->twig->getFilter('inline_css')->filter($html);
file_put_contents('dist/page.html', $inlined);
}
Laravel Blade Compatibility
Create a custom Blade directive for inline_css:
// app/Providers/BladeServiceProvider.php
Blade::directive('inlineCss', function ($expression) {
return "<?php echo (new \\Twig\\Extension\\CssInlinerExtraExtension())->getInlineCssFilter()->filter($expression); ?>";
});
Usage:
@inlineCss($html)
Caching Inlined Output Cache results for static content (e.g., emails):
// app/Services/EmailRenderer.php
public function render($template, $data) {
$cacheKey = md5($template . serialize($data));
return Cache::remember($cacheKey, now()->addHours(1), function () use ($template, $data) {
return $this->twig->render($template, $data);
})->pipe(function ($html) {
return $this->twig->getFilter('inline_css')->filter($html);
});
}
Excluding Specific Elements Skip inlining for dynamic or third-party content:
{{ content|inline_css(exclude=['.ad-banner', 'iframe']) }}
Testing Inlined CSS Use PHPUnit to assert inlined styles:
public function testCssInlining() {
$html = '<div style="color:red">Hello</div>';
$inlined = $this->twig->getFilter('inline_css')->filter($html);
$this->assertStringNotContainsString('<style', $inlined);
$this->assertStringContainsString('color:red', $inlined);
}
DOM Extension Requirement
FatalErrorException: Class 'DOMDocument' not found.dom extension in php.ini:
extension=dom
Malformed HTML
Twig\Error\RuntimeError: Failed to parse HTML.league/html-to-markup or DOMDocument::loadHTML():
$dom = new DOMDocument();
@$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$html = $dom->saveHTML();
CSS Parsing Limits
Allowed memory exhausted for large CSS files (>100KB).Blade-Twig Interop Issues
Twig\Error\SyntaxError when using in Blade.Dynamic Class Names
{{ content|inline_css(exclude=['.[^a-z-]']) }}
Media Query Inlining
@media) are not inlined by default.grunt-css-inliner for advanced cases.Email Client Quirks
Inspect Inlined Output Use Twig’s debug mode to see raw inlined HTML:
{% set debugHtml = content|inline_css %}
{{ dump(debugHtml) }}
Log Parsing Errors Catch exceptions and log them:
try {
$inlined = $this->twig->getFilter('inline_css')->filter($html);
} catch (\Exception $e) {
\Log::error('CSS Inlining Failed: ' . $e->getMessage());
$inlined = $html; // Fallback
}
Validate HTML Structure
Use tidy to check HTML validity:
tidy -e input.html
Twig Environment Setup Ensure the extension is registered after the Twig environment is built:
$twig = new \Twig\Environment($loader);
$twig->addExtension(new \Twig\Extension\CssInlinerExtraExtension());
Laravel Caching Clear Twig cache after installing the package:
php artisan view:clear
Custom Filter Options
Extend the filter for additional options (e.g., preserveMediaQueries):
// app/Extensions/CustomCssInlinerExtension.php
class CustomCssInlinerExtension extends \Twig\Extension\AbstractExtension {
public function getFilters() {
return [
new \Twig\TwigFilter('custom_inline_css', [$this, 'inlineCss']),
];
}
public function inlineCss($html, array $options = []) {
$inliner = new \Symfony\Component\CssInliner\CssInliner();
return $inliner->inline($html, $options);
}
}
$css = $this->extractCss($html);
How can I help you explore Laravel packages today?