🎨 Moving from vanillaCSS to TailwindCSS in an Express app using Pug templates 🎨

Thomas Dargon
12 min readJul 14, 2021



If you are only just starting with CSS and are tempted to take a shortcut with CSS frameworks, my personal opinion is: take your time. Styling is a BIG part of web app development and taking the time to learn and experiment with vanilla CSS will not only make you a more confident web developer but also save time in the future. It can seem overwhelming at first due to all the “rules” you’ve got to learn and you will have to do a lot of reading but it will payoff 💯 %.

I think it is easier / more enjoyable to start playing with a CSS framework like Tailwind once you have a good level of understanding and confidence with CSS.

As always one of the best place to learn web development technologies is:🔥🦊 https://developer.mozilla.org/en-US/docs/Learn/CSS .


I recently had an impulse through work to start working with a CSS framework: TailwindCSS.
I have been writing vanilla CSS for a while and to be honest I quite like it. It is a very pleasant activity which in my opinion uses both the programming and graphic design areas of one’s brain. So I would often take a few days to fully focus on the styling of an app, this would give me the time to dig into the implementation details of a design, experiment with fonts / colors / responsive layouts. Sometimes getting stuck and spending 3 hours or more just switching between different colors or padding values. (We have all been there..)

This is fine if you like it and for small scale projects where one or maybe two devs are responsible for the whole project 👍. Now, in a bigger company setup where you typically have a separate design department the implementation work won’t be as straightforward. 📋

Especially when you consider an app which will be developed and maintained by different people through many years / version implementations — using custom CSS utility classes written and implemented by different devs at different time will create lots of frictions.

If on top of that you now consider a company which builds not only one app but a bunch of apps then it becomes even harder to follow and maintain. 🤯

You have two options: build a set of company-wide utility classes (building your own CSS framework) 🚀 or use a publicly available CSS framework ❤️.

Using a publicly available CSS framework will save some time but it would be great if that CSS framework was fully customisable so you could add your own color palettes, custom fonts and implements all those lovely designs from your Design department ? 👨‍🎨

Tailwind offers just that: 🖼 https://tailwindcss.com

“Because Tailwind is a framework for building bespoke user interfaces, it has been designed from the ground up with customization in mind.”

“ Extend it, tweak it, change it.”

“Tailwind includes an expertly crafted set of defaults out-of-the-box, but literally everything can be customized — from the color palette to the spacing scale to the box shadows to the mouse cursor. “

That sounds awesome ! I was quite convinced till my eyes fell upon that scary statement: “Rapidly build modern websites without ever leaving your HTML.” What ? No more CSS files… How come ? I love CSS files and I love writing them, they allow you to separate the style from the substance of you app, I don’t want to have cluttered HTML files… Right ?

But maybe I am being a bit change adverse and stuck in my ways, I have been working with vanilla CSS for a while, it is time to try something new!

Let’s dive in!

I gave myself the following exercise:

Converting an existing App built with vanilla CSS (and custom CSS utility classes) to only using Tailwind for styling.

The starting point for the project can be found here: https://github.com/argonathmos/joyeux

“Joyeux”: A simple Express app using Pug templates. 🦦

👾 https://expressjs.com/

🐶 https://pugjs.org/api/getting-started.html

Part 1 — Install TailwindCSS

Our goal for part one is to install Tailwind in our app and go through the steps to make it usable directly in our Pug templates.

Simply clone the git repo and follow the steps detailed below to get started.

1.a — Install Tailwind via npm (without PostCSS and autoprefixer):

npm install -D tailwindcss@latest

Output :

1.b — Manually build your CSS file:

npx tailwindcss-cli@latest build -o public/styles/tailwind.css

(This will create the file tailwind.css — you are free to name it however you want).

Output :

1.c — Replace stylish.css by tailwind.css in the index.pug file:

link(rel=”stylesheet” type=”text/css” href=”styles/stylish.css”)

Now becomes:

link(rel=”stylesheet” type=”text/css” href=”styles/tailwind.css”)

(This is the stylesheet that the app will use)

1.d — Run the app locally

npm run dev

It should now look pretty basic as only the default tailwind styles (preflight) are applied — https://tailwindcss.com/docs/preflight

The stylish.css file is not loaded so the CSS classes in the pug file are not applied anymore (we can delete them from the index.pug )

👉 Don’t delete the stylish.css file yet, we will keep going back to our previously defined styles in order to translate them to Tailwind’s classes.

👉 If you want to start from step 1.d, use the code from this starting point: https://github.com/argonathmos/joyeux/commit/76d965972ab9fc972b8fade3eaf4d1d81b1cef51

It includes the tailwind install, the generated tailwind.css file and index.pug cleaned up.

Part 2 — Adding styles to HTML attributes via Classes

Our goal is to match our previous 🍦 vanillaCSS styling using Tailwind’s default utility classes only.

At this step we will “merge” stylish.css into index.pug , going through each properties in the CSS file and adding them as classes onto our html element in the pug file.

But how do I know the class names Tailwind uses ? There is so many ? You are right there is no point learning them. The official website is very well made and make it simple to find classnames by searching for a regular CSS properties. You will find what you are looking for plus a short and insightful description.

So we will simply search for classes matching our CSS properties in stylish.css and apply them to our HMTL elements in index.pug in order to style the app.

Tailwind’s splendid website: https://tailwindcss.com

Properties applied to the body are now becoming classes onto the body element.

/* stylish.css */body {  font-family: monospace;  font-size: 15px;  color: rgb(205, 217, 229);  background-color: rgb(34, 39, 46);  width: 75%;  max-width: 640px;  margin: 10px auto;  display: flex;  flex-direction: column;  justify-content: center;}

Our previous styles applied via CSS properties inside a CSS file become a class attribute value on and HTML element in our Pug template.

//- index.pugbody(class=”font-mono text-base text-gray-50 bg-gray-800 w-3/4 max-w-screen-sm my-10 mx-auto flex flex-col justify-center”)

text-gray-50 is not exactly matching {color:rgb(205, 217; 229);} but is close enough for now.

max-w-screen-smmatches exactly our css property{max-width: 640px;}

Search results for max-width property — https://tailwindcss.com/docs/max-width

Refresh the browser, the app should look like this:

UI after styling the body element only

We are getting close to our our original vanillaCSS file 💪, let’s keep adding Tailwind utility classes to our HTML elements to get as close as possible to

You can stop at any point, if you have gained enough understanding of the process or can take a few shortcuts by either:

Part 3 (Optional) — Customising Tailwind:

Now we have moved from styles implemented through vanilla CSS (using our own utility classes) to styles implemented using Tailwind’s default utility classes.

The eagle eyed 🦅 reader will have noticed that some of the default styles provided by Tailwind don’t exactly match our previous styles from stylish.css .

We will now have a look at how to tweak Tailwind’s config files to implement our own custom styles.

3.a — Add webfonts

At the moment we are using Tailwind’s class font-mono instead of our CSS property: body{font-family:monospace} . I personally can’t tell the difference between 🧐 the two but let’s pretend we want to use another font: Space Mono by colophon-foundry.org ✒️👌.

i — The first step is to load the web font into the head section of the pug template:

link(href=”https://fonts.gstatic.com" rel=”preconnect”)link(href=”https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel=”stylesheet”)

ii — Then create a tailwind.config.js file and replace the default font utility classes with our own:

module.exports = {  theme: {    fontFamily: {      thatCoolColophonFont: [‘“Space Mono”’, “monospace”],    },  },};=> ❤️ Ensure fonts with spaces are wrapped by   .

iii — Rebuild our CSS file to overwrite tailwind default fontFamily utility classes:

npx tailwindcss-cli@latest build -o public/styles/tailwind.css

It will generate the following lines in our tailwind.css file:

.font-thatCoolColophonFont {  font-family: “Space Mono”, monospace;}
New utility class generated in our CSS file from tailwind.config.js

iv — Update index.pug to use our new utility class: font-thatCoolColophonFont

v — Restart the server npm run dev and reload the page:

Taadaa 🐇 🎩 !! The UI is now using “Space Mono” which will default to monospace if the browser can’t load it.

3.b — Add custom colors

We had 6 different colors in our stylish.css file which we moved away from to use exclusively colors from Tailwind’s default color palette. The default palette is very rich, great for rapid prototyping but what if you would like to use a custome color palette / scheme you took ages to come up with ? 🤔

By default, Tailwind color classes point to a default color palette:

gray-50 points to #F9FAFB

gray-800 points to #1F2937

pink-600 points to #DB2777 etc…

Full list here: https://tailwindcss.com/docs/customizing-colors

To change this and use our own colors, we need to add to tailwind.config.js file again to override the default color palette.

Our stylish.css file, we have used a mix of rgb() functional notation, css named color notation and RGB hex notation-> 🤫 … let’s clean up and convert them all to RGB Hex notation. 🧹

i — Add `colors`field to tailwind.config.js to override the default color utility classes with our own color palette.


module.exports = { theme: {  fontFamily:{…},  colors: {    gray: {      50: “#cdd9e5”,      600: “#495057”,      800: “#22272e”,    },    blue: {      400: “#539bf5”,    },    pink: {      600: “#DB277”,    },    transparent: “transparent”,    white: “#fff”,  },},};

=> ❤️ We still have to define white and transparent if we want to use them as the config file will override all the tailwind predefined colors.

ii — Rebuild or css style to override Tailwind default colors with our own colors:

npx tailwindcss-cli@latest build -o public/styles/tailwind.css

iii — Restart the server and reload the page:

Our own colors are now being applied, without needing to update our classes in index.pug as the color classes now points to our own values. 👏👏

blue-400 now points to our own custom color #539bf5 -> rgb(83, 155, 245) 💙🫐

3.c — Add custom utility class (optional)

If you followed carefully 🦉 you might have noticed that we had the property {height: 40vh;} for our form element defined in stylish.css but I have used h-96 Tailwind’s class on that element as Tailwind doesn’t offer the equivalent in its pre-defined utility classes.

At the time of writing Tailwind only offer h-screen class which will make the height of the element: 100vh.

<div class=”h-screen”></div>

⬇️ ⬇️ ⬇️

div {  height: 100vh;}


There might be a good reason behind it: https://stackoverflow.com/questions/21624014/css-are-view-height-vh-and-view-width-vw-units-widely-supported. 🤷‍♂️

But let’s say I really want to add a height of 40 viewport-height to my form element, what are my options 🤓 ?

I could simply add a style attribute to my form element in the pug template (CSS inline styles) which will take precedence and override the h-96 class but that would mean mixing inline styling with Tailwind classes… I would like to stick to one approach.

Let’s create our own utility class: https://tailwindcss.com/docs/adding-new-utilities#using-css 🪄

i — Create a custom.css file in the folder of your choice (mine is /build at the root of the project)

// custom.css@tailwind base;@tailwind components;@tailwind utilities;@layer utilities { .h-screen-40 {   height: 40vh; }}

ii — Manually build our tailwind.css file taking into account our custom.css file:

npx tailwindcss-cli@latest build build/custom.css -o public/styles/tailwind.css

The output of the command is

iii — Add our custom `h-screen-40` class to our form element in the pug template.

Our custom utility class `h-screen-40` is applied

Part 4 (recommended) — Purge the generated CSS file

Removing unused CSS styles from our tailwind.css file — https://tailwindcss.com/docs/optimizing-for-production#basic-usage

By default Tailwind will generate a file containing all the CSS code their default utility classes will point to, but in a single project we use only a fraction those styles made available to us. Tailwind offers a way to remove this unnecessary styling properties from the generated style files.

4.a — Add the purge key to our tailwind.config.js file, and make sure to target all the files in which we are using tailwind classes (here only index.pug):

// tailwind.config.jsmodule.exports = {  purge: [    ‘./views/*.pug’,  ],  theme: { … },}

4.b Manually build the tailwind.css file by running:

NODE_ENV=production npx tailwindcss-cli@latest build -o public/styles/tailwind.css

By setting NODE_ENV=production, Tailwind will automatically “purge” the generated CSS.

The generated CSS file contains only the necessary styles and is way smaller.

If you followed the step 3.c, you will have to run:

NODE_ENV=production npx tailwindcss-cli@latest build build/custom.css -o public/styles/tailwind.css

To include our custom utility class.

Generating the small CSS file containing our utility classes defined in build/custom.css


We moved from vanilla CSS with pre-defiend custom CSS utility classes such as (.btn .form-control-field .form-control-label) in our stylish.css filed to using Tailwind’s utility classes directly in our Pug template, we didn’t need to write any CSS code in order to style our app (apart from adding custom configurations), though knowing CSS makes the process way easier as we can browse Tailwind’s website through CSS properties.

At this stage, I have no opinion on which approach is better, using Tailwind in a project could implement a sort of de facto standard for styling. So a big plus ✌️ if you work within a team and will help with maintenance in the future (the class names will stay the same and mean the same to everyone who knows Tailwind / can search the docs), plus you don’t have to write CSS files (we ended up adding a few classes to your HTML elements instead).

On the other hand, if you are working on a smaller project or if you want to create your own standard (and you like vanilla CSS and want to stick with it) by all means go for it 🏎 🏁 and create your own utility classes.

Regarding this particular exercise facts are: I got rid of my stylish.css file (replaced by the automatically generated tailwind.css file), HTML elements and their associated styles are all in one place: index.pug.

We also ended up with a “bloated” index.pug file with lots of CSS classes duplication: the classes in <p> elements, <input> elements and <label> elements needs to be written on each element. (This could be avoided by creating “Pug component” or “template partial” files and referencing them from the index.pug file https://pugjs.org/language/inheritance.html . But this will create extra work especially for a small project like this one).

I believe Tailwind is a great tool that offers lots of power and flexibility if you decide to implement it to force consistency between your projects.

In my opinion, using Tailwind would be even more interesting when building SPAs with components based frameworks such as Vue or React as you won’t have to worry about duplicating classes to style element as your App’s UI would likely be split in components already. Bonus: you won’t have to worry about styles scope anymore as using tailwind classes from within your component makes the styles de facto scoped to that component ! 👑 👑

Will try this in a future article: update a ⚛️ React App where style is implemented via “CSS modules” to use TailwindCSS.

✌️ Stay Awesome 😘