---
title: "Utiliser Tailwind CSS 2, PurgeCSS avec Symfony et Webpack Encore"
excerpt: "Vous avez envie de découvrir Tailwind CSS 2.0, mais vous ne savez pas comment l'installer dans un projet Symfony ? Je vous propose de découvrir comment mettre en place Tailwind CSS 2.0 et PurgeCSS dans un projet Symfony avec Webpack Encore."
publishDate: 2020-11-25T00:00:00.000Z
tags: ["css", "encore", "symfony", "tailwind", "webpack"]
canonical: "https://yoandev.co/utiliser-tailwind-css-2-purgecss-avec-symfony-et-webpack-encore"
---

<YouTube id=" HNV1_BG0w-o" />

### Introduction

J'ai suivi une formation Backend, et le Front n'est pas du tout ma spécialité, mais au quotidien, j'utilise généralement [Bootstrap](https://getbootstrap.com/), et je dois dire que jusqu'à présent ce Framework me suffit largement. Ceci dit, de plus en plus de monde disent tout le bien qu'ils pensent de [Tailwind CSS](https://tailwindcss.com/), et j'avais envie de tester par moi-même !

Histoire de faire les choses dans les règles de l'art, je vous propose d'utiliser **Tailwind** dans un projet **Symfony,** en utilisant **Webpack Encore** et **PurgeCSS** (qui est utilisé par Tailwind pour réduire la taille du fichier CSS, nous le verrons plus tard).

### Mise en place d'un projet Symfony

Première étape, mettre en place un nouveau projet Symfony que l'on va appeler "tailwind". Pour cela on utilise la commande classique de la [CLI Symfony](https://symfony.com/download).

```shell
symfony new tailwind --full
```

On lance le serveur de Symfony pour vérifier que tout est OK.

```shell
symfony server:start -d
```

Et on vérifie dans un navigateur internet que tout est OK. Dans mon cas à l'adresse [http://127.0.0.1:8000](http://127.0.0.1:8000).

![](./images/image-19-1024x520.png)

### Installation de Webpack Encore

Deuxième étape de notre découverte, l'installation de [Webpack Encore](https://symfony.com/doc/current/frontend.html).

```shell
composer require symfony/webpack-encore-bundle
```

Une fois que Webpack Encore est en place, demandons à **npm** de nous télécharger les dépendances de base de Webpack Encore. (Nous aurions pu également utiliser **yarn**.)

```shell
npm install
```

Notre projet Symfony est désormais capable d'utiliser Webpack pour compiler du JS, et dans le cas qui nous concerne du CSS.

### Installation de Tailwind CSS et PurgeCSS

Il est temps d'installer Tailwind et PurgeCSS. Comme nous l'utilisons dans un contexte "Webpack", nous ajoutons et modifions un peu les commandes suggérées [dans la doc](https://tailwindcss.com/docs/installation) de Tailwind.

```shell
npm install -D tailwindcss postcss-loader purgecss-webpack-plugin glob-all path autoprefixer
```

Qu'avons-nous installé avec cette commande ?

- tailwindcss : Installation de [Tailwind](https://tailwindcss.com/docs/installation)
- postcss-loader: Installation de PostCSS conformément à la [documentation Symfony](https://symfony.com/doc/current/frontend/encore/postcss.html)
- purgecss-webpack: Cela permet d'utiliser [PurgeCSS avec Webpack](https://purgecss.com/plugins/webpack.html#installation)
- glob-all: [https://www.npmjs.com/package/glob-all](https://www.npmjs.com/package/glob-all)
- autoprefixer: Installation conformément à la doc de [Tailwind](https://tailwindcss.com/docs/installation)

### Fichier de configurations

Pour que notre mise en place de Tailwind fonctionne, nous devons modifier quelques fichiers.

Commençons par mettre en place un fichier **postcss.config.js** (à la racine de notre projet).

```js
module.exports = {
    plugins: [
        require('tailwindcss'),
    ],
};
```

Modifiions ensuite notre fichier **webpack.config.js**, en y ajoutant en début de fichier les références aux librairies que nous allons utiliser.

```js
var Encore = require('@symfony/webpack-encore');
const PurgeCssPlugin = require('purgecss-webpack-plugin');
const glob = require('glob-all');
const path = require('path')
```

Toujours dans le fichier **webpack.config.js**, activons l'utilisation du **loader PostCSS** tel que décrit dans la documentation de [Webpack Encore](https://symfony.com/doc/current/frontend/encore/postcss.html).

```js
    .enablePostCssLoader()
```

Il nous faut également **importer les feuilles de style dans notre fichier CSS**, c'est quand même l'objectif lorsque l'on utilise un framework CSS ;-). Pour cela ajoutons ces quelques lignes à notre fichier **assets/styles/app.css**.

```js
@import "tailwindcss/base";

@import "tailwindcss/components";

@import "tailwindcss/utilities";
```

### Notre premier build

À cette étape nous devrions être en mesure de lancer un premier build pour vérifier que notre configuration fonctionne.

```shell
npm run build
```

_Suivant les cas, vous pouvez obtenir le message "**Error: PostCSS plugin tailwindcss requires PostCSS 8."**. Pour corriger le problème, Tailwind vous propose la solution dans la documentation officielle : [https://tailwindcss.com/docs/installation#post-css-7-compatibility-build](https://tailwindcss.com/docs/installation#post-css-7-compatibility-build)_

### Une page de test

Pour valider le bon fonctionnement de notre installation, nous allons mettre en place une page de test qui utilisera les possibilités du Tailwind CSS.

```shell
symfony console make:controller Demo
```

Modifions le fichier "**templates/base.html.twig**" pour utiliser nos fichiers compilés par Webpack Encore.

```html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        {% block stylesheets %}{{ encore_entry_link_tags('app') }}{% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
        {% block javascripts %}{{ encore_entry_script_tags('app') }}{% endblock %}
    </body>
</html>
```

Récupérons un exemple de code utilisant Tailwind sur [tailwindui.com](https://tailwindui.com/components/marketing/sections/heroes) et ajoutons-le dans notre fichier "**templates/demo/index.html.twig**".

```html
{% extends 'base.html.twig' %}

{% block title %}Hello DemoController!{% endblock %}

{% block body %}
<!-- This example requires Tailwind CSS v2.0+ -->
<div class="relative bg-white overflow-hidden">
  <div class="max-w-7xl mx-auto">
    <div class="relative z-10 pb-8 bg-white sm:pb-16 md:pb-20 lg:max-w-2xl lg:w-full lg:pb-28 xl:pb-32">
      <svg class="hidden lg:block absolute right-0 inset-y-0 h-full w-48 text-white transform translate-x-1/2" fill="currentColor" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
        <polygon points="50,0 100,0 50,100 0,100" />
      </svg>

      <div class="relative pt-6 px-4 sm:px-6 lg:px-8">
        <nav class="relative flex items-center justify-between sm:h-10 lg:justify-start" aria-label="Global">
          <div class="flex items-center flex-grow flex-shrink-0 lg:flex-grow-0">
            <div class="flex items-center justify-between w-full md:w-auto">
              <a href="#">
                <span class="sr-only">Workflow</span>
                <img class="h-8 w-auto sm:h-10" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg">
              </a>
              <div class="-mr-2 flex items-center md:hidden">
                <button type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500" id="main-menu" aria-haspopup="true">
                  <span class="sr-only">Open main menu</span>
                  <!-- Heroicon name: menu -->
                  <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
                  </svg>
                </button>
              </div>
            </div>
          </div>
          <div class="hidden md:block md:ml-10 md:pr-4 md:space-x-8">
            <a href="#" class="font-medium text-gray-500 hover:text-gray-900">Product</a>

            <a href="#" class="font-medium text-gray-500 hover:text-gray-900">Features</a>

            <a href="#" class="font-medium text-gray-500 hover:text-gray-900">Marketplace</a>

            <a href="#" class="font-medium text-gray-500 hover:text-gray-900">Company</a>

            <a href="#" class="font-medium text-indigo-600 hover:text-indigo-500">Log in</a>
          </div>
        </nav>
      </div>

      <!--
        Mobile menu, show/hide based on menu open state.

        Entering: "duration-150 ease-out"
          From: "opacity-0 scale-95"
          To: "opacity-100 scale-100"
        Leaving: "duration-100 ease-in"
          From: "opacity-100 scale-100"
          To: "opacity-0 scale-95"
      -->
      <div class="absolute top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden">
        <div class="rounded-lg shadow-md bg-white ring-1 ring-black ring-opacity-5 overflow-hidden">
          <div class="px-5 pt-4 flex items-center justify-between">
            <div>
              <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" alt="">
            </div>
            <div class="-mr-2">
              <button type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
                <span class="sr-only">Close main menu</span>
                <!-- Heroicon name: x -->
                <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
                </svg>
              </button>
            </div>
          </div>
          <div role="menu" aria-orientation="vertical" aria-labelledby="main-menu">
            <div class="px-2 pt-2 pb-3 space-y-1" role="none">
              <a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Product</a>

              <a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Features</a>

              <a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Marketplace</a>

              <a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem">Company</a>
            </div>
            <div role="none">
              <a href="#" class="block w-full px-5 py-3 text-center font-medium text-indigo-600 bg-gray-50 hover:bg-gray-100" role="menuitem">
                Log in
              </a>
            </div>
          </div>
        </div>
      </div>

      <main class="mt-10 mx-auto max-w-7xl px-4 sm:mt-12 sm:px-6 md:mt-16 lg:mt-20 lg:px-8 xl:mt-28">
        <div class="sm:text-center lg:text-left">
          <h1 class="text-4xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl">
            <span class="block xl:inline">Data to enrich your</span>
            <span class="block text-indigo-600 xl:inline">online business</span>
          </h1>
          <p class="mt-3 text-base text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
            Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat aliqua.
          </p>
          <div class="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start">
            <div class="rounded-md shadow">
              <a href="#" class="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 md:py-4 md:text-lg md:px-10">
                Get started
              </a>
            </div>
            <div class="mt-3 sm:mt-0 sm:ml-3">
              <a href="#" class="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 md:py-4 md:text-lg md:px-10">
                Live demo
              </a>
            </div>
          </div>
        </div>
      </main>
    </div>
  </div>
  <div class="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
    <img class="h-56 w-full object-cover sm:h-72 md:h-96 lg:w-full lg:h-full" src="https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80" alt="">
  </div>
</div>

{% endblock %}
```

Ouvrons dans un navigateur internet, la route de notre page de démo (chez moi [https://127.0.0.1:8000/demo](https://127.0.0.1:8000/demo)) et constatons le bon fonctionnement. Cool non ?!

![](./images/image-20-1024x519.png)

### Optimisations du fichier CSS avec PurgeCSS

Si vous êtes curieux, vous avez peut-être remarqué que le f**ichier CSS compilé par Webpack Encore pèse lourd**, très lourd même ! Sur mon poste, l**e fichier pèse 2.8 Mo** !

![](./images/image-21.png)

Pas d'inquiétudes ! À ce stade c'est tout à fait logique, puisque nous importons l'ensemble de Tailwind CSS, sans le nettoyer de tout ce que nous n'utilisons pas.

C'est le moment de faire entrer **PurgeCSS**, dont la mission va consister à **supprimer tout le CSS inutile**, c’est-à-dire non-utiliser dans nos fichiers twig. On retrouve pas mal de documentation sur la mise en place de PurgeCSS avec [Webpack Encore](https://phproberto.com/en/41-integrating-purgecss-with-symfony-encore), mais le meilleur [bout de code](https://markrailton.com/blog/using-tailwind-css-and-purgecss-with-symfony-encore) que j'ai trouvé permet en plus de ne réaliser la purge du CSS que lors d'un build de prod, génial non !?

Ajoutons donc, en fin du fichier **webpack.config.js** ces quelques lignes.

```js
if (Encore.isProduction()) {
    Encore.addPlugin(new PurgeCssPlugin({
          paths: glob.sync([
              path.join(__dirname, 'templates/**/*.html.twig')
          ]),
          defaultExtractor: (content) => {
              return content.match(/[\w-/:]+(?<!:)/g) || [];
          }
      }));
  }
;
```

Relançons un build (de prod).

```shell
npm run build
```

Et nous pouvons constater que la taille du fichier CSS à considérablement été réduite. **Elle est passée de 2.8 Mo à 9.3 Ko** !

![](./images/image-22.png)

### Conclusions et dépôt GitLab

Nous venons de voir à quel point il est simple de mettre en place Tailwind CSS 2.0 et PurgeCSS avec Symfony et Webpack Encore ! Il ne reste finalement plus qu'à découvrir le fonctionnement de Tailwind pour construire de belles interfaces front !

Les sources du projet sont disponibles dans ce [dépôt GitLab](https://gitlab.com/yoandev.co/utiliser-tailwind-css-2-purgecss-avec-symfony-et-webpack-encore).
