# SymfonyCasts TailwindBundle The SymfonyCasts TailwindBundle integrates Tailwind CSS directly into Symfony applications using the AssetMapper component, with no Node.js required. It automates downloading the standalone Tailwind CSS binary for the current platform, compiles CSS input files into built output files, and transparently swaps the compiled CSS into the asset pipeline when assets are served or compiled. It supports both Tailwind CSS v3 (with `tailwind.config.js` and PostCSS) and Tailwind CSS v4 (with the `@import "tailwindcss"` directive), and covers development (with file watching), production (with minification), and deployment workflows. The bundle's core components are `TailwindBuilder` (orchestrates the build process), `TailwindBinary` (manages binary download and process creation), `TailwindCssAssetCompiler` (hooks into Symfony's AssetMapper pipeline to replace input CSS with compiled output), and two console commands (`tailwind:build` and `tailwind:init`). Configuration is handled through the `symfonycasts_tailwind` extension key, exposing options for input CSS paths, binary version, platform overrides, PostCSS config, and strict mode. --- ## Installation Install the bundle via Composer and initialize Tailwind for your project. ```bash composer require symfonycasts/tailwind-bundle php bin/console tailwind:init ``` --- ## `tailwind:init` — Initialize Tailwind CSS Runs the Tailwind `init` command to generate `tailwind.config.js` (v3 only), then prepends the required Tailwind directives to the configured input CSS file. For Tailwind v4, skips config file creation and writes `@import "tailwindcss"` instead of the three `@tailwind` directives. ```bash # Initialize Tailwind CSS (auto-detects v3 or v4 based on binary_version config) php bin/console tailwind:init # --- Generated tailwind.config.js (v3 only) --- # /** @type {import('tailwindcss').Config} */ # module.exports = { # content: [ # "./assets/**/*.js", # "./templates/**/*.html.twig", # ], # theme: { extend: {} }, # plugins: [], # } # --- Directives prepended to assets/styles/app.css (v3) --- # @tailwind base; # @tailwind components; # @tailwind utilities; # --- Directives prepended to assets/styles/app.css (v4) --- # @import "tailwindcss"; ``` --- ## `tailwind:build` — Build Tailwind CSS Compiles the configured input CSS file(s) using the Tailwind binary, writing output to `var/tailwind/.built.css`. Supports one-shot builds, file watching, polling mode (v3 only), minification, and a specific input file override when multiple input CSS files are configured. ```bash # One-shot build (development) php bin/console tailwind:build # Watch for changes and rebuild automatically (keep running in background) php bin/console tailwind:build --watch # Watch with polling (useful for Docker on Windows, v3 only) php bin/console tailwind:build --watch --poll # Production build: minify output php bin/console tailwind:build --minify # Build a specific input file (when multiple input_css files are configured) php bin/console tailwind:build assets/styles/other.css # Use a custom PostCSS config (v3 only) php bin/console tailwind:build --postcss=postcss.config.js # Verbose output showing the exact binary command executed php bin/console tailwind:build -v # Deployment workflow: minify then compile the asset map php bin/console tailwind:build --minify php bin/console asset-map:compile ``` --- ## Bundle Configuration (`symfonycasts_tailwind`) All bundle options are set under the `symfonycasts_tailwind` key in `config/packages/symfonycasts_tailwind.yaml`. Dump the full reference with `php bin/console config:dump symfonycasts_tailwind`. ```yaml # config/packages/symfonycasts_tailwind.yaml symfonycasts_tailwind: # Single input CSS file (default: assets/styles/app.css) input_css: 'assets/styles/app.css' # Multiple input CSS files input_css: - 'assets/styles/app.css' - 'assets/styles/admin.css' # Path to tailwind.config.js (v3 only; ignored in v4) config_file: 'tailwind.config.js' # Pin a specific Tailwind CLI version (recommended) binary_version: 'v3.4.17' # or 'v4.0.7' for v4 # Use an npm-installed binary instead of auto-downloading binary: 'node_modules/.bin/tailwindcss' # Override platform detection (auto | linux-arm64 | linux-arm64-musl | # linux-x64 | linux-x64-musl | macos-arm64 | macos-x64 | windows-x64) binary_platform: 'linux-x64' # PostCSS config file (v3 only) postcss_config_file: 'postcss.config.js' # strict_mode: throw if built CSS is missing (default: true except in 'test' env) strict_mode: true ``` --- ## `TailwindBuilder` — Programmatic Build API `TailwindBuilder` is the central service (`tailwind.builder`) that manages building Tailwind CSS. It validates input paths, constructs the binary process, and exposes the built output content for the asset pipeline. ```php use Symfonycasts\TailwindBundle\TailwindBuilder; use Symfony\Component\Console\Style\SymfonyStyle; // Construct directly (normally injected by the container) $builder = new TailwindBuilder( projectRootDir: '/var/www/myapp', inputPaths: ['/var/www/myapp/assets/styles/app.css'], tailwindVarDir: '/var/www/myapp/var/tailwind', binaryPath: null, // null = auto-download binaryVersion: 'v3.4.17', configPath: 'tailwind.config.js', postCssConfigPath: null, binaryPlatform: 'auto', ); // Attach Symfony console output for user-facing messages $builder->setOutput($io); // $io is a SymfonyStyle instance // Run a one-shot build $process = $builder->runBuild( watch: false, poll: false, minify: false, inputFile: null, // null = use the first configured input file postCssConfigFile: null, ); $process->wait(function ($type, $buffer) { echo $buffer; }); if (!$process->isSuccessful()) { throw new \RuntimeException('Tailwind build failed'); } // Run a minified build for a specific input file $process = $builder->runBuild( watch: false, poll: false, minify: true, inputFile: 'assets/styles/admin.css', ); $process->wait(); // Start a watch process (non-blocking) $process = $builder->runBuild(watch: true, poll: false, minify: false); // $process runs until terminated // Get the path of the compiled output file $outputPath = $builder->getInternalOutputCssPath('/var/www/myapp/assets/styles/app.css'); // => '/var/www/myapp/var/tailwind/app.built.css' // Read the compiled CSS content directly $css = $builder->getOutputCssContent('/var/www/myapp/assets/styles/app.css'); // => "/* Tailwind base, components, utilities + custom styles */" // Get all configured input CSS paths $paths = $builder->getInputCssPaths(); // => ['/var/www/myapp/assets/styles/app.css'] // Get the configured Tailwind config file path $configPath = $builder->getConfigFilePath(); // => 'tailwind.config.js' // Run tailwind init (creates tailwind.config.js) $process = $builder->runInit(); $process->wait(); ``` --- ## `TailwindBinary` — Binary Management `TailwindBinary` handles downloading the correct Tailwind standalone binary for the current OS and CPU architecture, caching it under `var/tailwind//`, making it executable, and wrapping it in a Symfony `Process`. ```php use Symfonycasts\TailwindBundle\TailwindBinary; use Symfony\Component\HttpClient\MockHttpClient; // Auto-download mode: binary stored in var/tailwind/v3.4.17/tailwindcss-linux-x64 $binary = new TailwindBinary( binaryDownloadDir: '/var/www/myapp/var/tailwind', cwd: '/var/www/myapp', binaryPath: null, // null = auto-download binaryVersion: 'v3.4.17', ); // Create a Symfony Process with arbitrary CLI arguments $process = $binary->createProcess(['-i', 'assets/styles/app.css', '-o', 'var/tailwind/app.built.css']); $process->run(); // Use a custom (npm-installed) binary path $binary = new TailwindBinary( binaryDownloadDir: '', cwd: '/var/www/myapp', binaryPath: 'node_modules/.bin/tailwindcss', binaryVersion: null, ); // Inspect the detected version echo $binary->getVersion(); // => 'v3.4.17' echo $binary->getRawVersion(); // => '3.4.17' echo $binary->isV4() ? 'v4' : 'v3'; // => 'v3' // Get the binary filename for a given version and platform (static, internal helper) $name = TailwindBinary::getBinaryName('3.4.17', 'linux-x64'); // => 'tailwindcss-linux-x64' $name = TailwindBinary::getBinaryName('4.0.0', 'linux-x64-musl'); // => 'tailwindcss-linux-x64-musl' $name = TailwindBinary::getBinaryName('3.4.17', 'windows-x64'); // => 'tailwindcss-windows-x64.exe' // Override platform to force a specific download (e.g., cross-compile for CI) $binary = new TailwindBinary( binaryDownloadDir: '/var/www/myapp/var/tailwind', cwd: '/var/www/myapp', binaryPath: null, binaryVersion: 'v4.0.7', binaryPlatform: 'linux-x64', ); ``` --- ## `TailwindCssAssetCompiler` — AssetMapper Integration `TailwindCssAssetCompiler` implements Symfony's `AssetCompilerInterface` and is tagged as an `asset_mapper.compiler` with priority 10 (runs before the core CSS URL compiler). It intercepts requests for the configured input CSS file(s) and transparently replaces their content with the pre-built Tailwind output from `var/tailwind/.built.css`. ```php use Symfonycasts\TailwindBundle\AssetMapper\TailwindCssAssetCompiler; use Symfony\Component\AssetMapper\MappedAsset; // Normally registered automatically by the bundle's service container. // Manual instantiation for testing or custom wiring: $compiler = new TailwindCssAssetCompiler( tailwindBuilder: $builder, // TailwindBuilder instance strictMode: true, // throw RuntimeException if built file is missing ); // The AssetMapper calls supports() before compile(): // Returns true only for assets whose realpath matches a configured input CSS path, // and (when strictMode=false) only if the built file already exists. $asset = new MappedAsset('styles/app.css', '/var/www/myapp/assets/styles/app.css'); if ($compiler->supports($asset)) { $compiled = $compiler->compile($asset->content, $asset, $assetMapper); // $compiled now contains the full Tailwind-processed CSS } // --- Functional test demonstrating the full pipeline --- // 1. Pre-populate the built file to simulate a prior tailwind:build run file_put_contents('/var/www/myapp/var/tailwind/app.built.css', 'body { padding: 17px; }'); // 2. AssetMapper automatically swaps in the compiled content $asset = $assetMapper->getAsset('styles/app.css'); assert(str_contains($asset->content, 'padding: 17px')); // The core CssAssetUrlCompiler also runs, rewriting url() references with hashed filenames ``` --- ## Symfony CLI Worker Configuration Use the Symfony CLI's worker feature to automatically start `tailwind:build --watch` alongside the dev server. ```yaml # .symfony.local.yaml workers: tailwind: cmd: ['symfony', 'console', 'tailwind:build', '--watch'] ``` ```bash # Start the dev server with the Tailwind watcher running in parallel symfony server:start # Tail worker output when running as a daemon symfony server:log ``` --- ## Tailwind Plugins Configuration ```yaml # config/packages/symfonycasts_tailwind.yaml (v3) # The downloaded standalone binary already bundles all official plugins. # Third-party plugins still require npm. ``` ```javascript // tailwind.config.js — official plugin (bundled, no npm needed) module.exports = { content: [ "./assets/**/*.js", "./templates/**/*.html.twig", // Include vendor paths if using Symfony Twig form themes "./vendor/symfony/twig-bridge/Resources/views/Form/*.html.twig", ], plugins: [ require('@tailwindcss/typography'), // bundled in standalone binary require('flowbite/plugin'), // requires: npm install flowbite ], }; ``` ```css /* assets/styles/app.css — v4 plugin syntax */ @import "tailwindcss"; @plugin "@tailwindcss/typography"; ``` --- ## Summary The primary use cases for this bundle are: integrating Tailwind CSS into Symfony AssetMapper-based projects without requiring a Node.js toolchain; running a continuous watch process during development so CSS changes are reflected immediately on page refresh; and producing minified, production-ready CSS during deployment by running `tailwind:build --minify` before `asset-map:compile`. The bundle supports both the Tailwind v3 (config-file and PostCSS) and v4 (import-based) workflows, with automatic binary detection and download per platform and version. Integration follows standard Symfony bundle conventions: install via Composer, register automatically through Symfony Flex or `bundles.php`, configure under `symfonycasts_tailwind` in `config/packages/`, and interact through the `tailwind:init` and `tailwind:build` console commands. For advanced use cases — such as custom binary paths, multiple input CSS files, platform overrides for cross-platform CI, or programmatic builds — all functionality is accessible through the `tailwind.builder` service (`TailwindBuilder`) and `TailwindBinary` classes, which can be injected or instantiated directly.