Coding • ☕ 5 minutes de lecture

Intégrer facilement TailwindCSS à un projet Sapper

Voyons ensemble comment intégrer efficacement Tailwind au sein d’un projet Sapper utilisant rollup pour le bundling.

Quand je travaille sur mes projets personnels, j’aime bien en profiter pour tester de nouvelles choses afin de mesurer la maturité de certaines technos ou frameworks.

En ce moment, je me penche principalement sur sapper et tailwindcss.

Qui, que, quoi ?

Sapper

Sapper s’incrit dans le mouvement des micro frameworks front comme Next.js, Nuxt.js et autres en se basant pour sa part sur svelte.

Je ne vais pas vous le cacher, svelte est mon petit coup de cœur du moment quand il s’agit de faire des applis réactives en Javascript. Il ne nécessite pas de runtime, pas de DOM virtuel, est relativement simple, le bundle final est hyper léger, bref, ça me plaît !

Sapper est quant à lui relativement jeune, mais déjà très prometteur.

Tailwind

À la base, je ne suis pas fan du côté utility-first où chaque classe CSS ne modifie qu’une propriété à la fois (dans 90% des cas) et où il suffit de composer ces classes dans le HTML pour produire le résultat voulu.

Il faut cependant avouer qu’après avoir visionné les vidéos officielles, j’avais sacrément envie de tester 😁

C’est justement en voulant intégrer Tailwind au sein d’un projet Sapper que j’ai rencontré quelques soucis, des articles souvent obsolètes ou trop complexes et que du coup je vous propose mon approche actuelle.

Création du projet sapper

On commence par créer un projet sapper en suivant le guide officiel :

$ npx degit "sveltejs/sapper-template#rollup" my-sapper-app
$ cd my-sapper-app
$ npm i
$ npm run dev

Vous devriez voir apparaître le template sapper de base qui utilise rollup pour bundler le tout 👍

Installation des dépendances

Tailwind étant utilisé comme un plugin postcss, il va nous falloir ajouter la librairie tailwindcss et svelte-preprocess (dont on reparlera plus bas).

J’en profite aussi pour ajouter autoprefixer et générer le fichier de config standard de Tailwind :

$ npm i -D tailwindcss autoprefixer@9.8.6 svelte-preprocess
$ npx tailwindcss init

Configuration de rollup

Désormais, il nous faut configurer le pipeline rollup de manière à sortir un bundle qui traite les styles définis dans les fichiers *.svelte.

Le but est ici d’extraire les styles définis dans les balises <style> de chaque fichier *.svelte et d’appliquer postcss dessus pour les transformer.

Pour se faire, nous utilisons svelte-preprocess en précisant l’étape postcss avec les plugins qui nous intéressent.

Je vous met ici le diff entre le rollup de base issu du template sapper avec celui modifié qui inclut ce qu’il faut pour traiter nos fichiers CSS avec postcss. Vous remarquerez qu’il y’a très peu de changements et que le preprocess se met à la fois côté serveur et côté client.

@@ -3,6 +3,7 @@ import replace from "@rollup/plugin-replace";
 import commonjs from "@rollup/plugin-commonjs";
 import svelte from "rollup-plugin-svelte";
 import babel from "@rollup/plugin-babel";
+import sveltePreprocess from "svelte-preprocess";
 import { terser } from "rollup-plugin-terser";
 import config from "sapper/config/rollup.js";
 import pkg from "./package.json";
@@ -17,6 +18,12 @@ const onwarn = (warning, onwarn) =>
     /[/\\]@sapper[/\\]/.test(warning.message)) ||
   onwarn(warning);

+const preprocess = sveltePreprocess({
+  postcss: {
+    plugins: [require("tailwindcss"), require("autoprefixer")],
+  },
+});
+
 export default {
   client: {
     input: config.client.input(),
@@ -30,6 +37,7 @@ export default {
         dev,
         hydratable: true,
         emitCss: true,
+        preprocess,
       }),
       resolve({
         browser: true,
@@ -82,6 +90,7 @@ export default {
         generate: "ssr",
         hydratable: true,
         dev,
+        preprocess,
       }),
       resolve({
         dedupe: ["svelte"],

Un npm run dev devrait fonctionner sans soucis mais rien de changé pour le moment, il nous reste à importer Tailwind et utiliser ses directives dans un composant afin que postcss puisse générer le fichier CSS qui va bien.

Création d’un composant pour importer Tailwind

Même si on pourrait imaginer importer Tailwind un peu partout, le but est de le faire dans un seul composant, présent sur toutes les pages avec un style global. on crée donc le fichier src/components/Tailwind.svelte qui contient :

<style global>
  @tailwind base;
  @tailwind components;
  @tailwind utilities;
</style>

Et on l’inclut ensuite dans le fichier src/routes/_layout.svelte qui se trouve être présent pour toutes les pages de l’application :

<script>
  // ...
  import Tailwind from "../components/Tailwind.svelte";
  // ...
</script>

<Tailwind /> <!-- On l'utilise ici -->

<!-- Le reste est inchangé... -->

Si vous lancez désormais npm run dev, toutes les classes issues de Tailwind devraient être générées ! Hourra ! 🎉 Alors oui, le point négatif c’est que le premier lancement est un peu long mais c’est le prix à payer.

Vous êtes aussi libre d’utiliser la directive @apply dans vos différents composants sans que cela ne pose le moindre soucis.

En route pour la production !

Dernière étape, il va nous falloir purger les classes CSS issues de tailwind non utilisées afin d’avoir un fichier CSS plus light (2mo, c’est un poil too much non ?) lors du déploiement en production.

Heureusement, pour nous, c’est désormais intégré dans Tailwind et il nous suffit juste de modifier le fichier tailwind.config.js généré plus tôt :

module.exports = {
  future: {
    removeDeprecatedGapUtilities: true,
    purgeLayersByDefault: true,
  },
  purge: ["src/**/*.svelte", "src/**/*.html"],
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
};

Avec la variable d’environnement NODE_ENV à production et un npm run build, vous devriez avoir un bundle avec uniquement les classes CSS que vous utilisez ! Elle est pas belle la vie ?