Tailwind CSS and Blazor
Tailwind CSS has been the talk of the web design world lately for all the good reasons. When I began work on my current redesign I just had to check it out.
If you don’t know what Tailwind CSS (Tailwind for short), they describe themselves as a utility first CSS framework. In practice Tailwind aims to let you design your project without leaving HTML and when you do need to fall back to CSS files then Tailwind offers some interesting features to help you get the most out of your designs.
This post isn’t meant to be an introduction to Tailwind however there are two features that I want to call out because I was really impressed with them. These feature are the responsive design prefix classes and the new Just in Time Compiler (JIT).
Responsive Design
Tailwind allows you to conditionally set it’s utility classes at predefined breakpoints. This means that it is really easy to be able to set a particular look depending on screen size. This is done by prefixing your class with a size prefix. Out of the box Tailwind has ‘sm’, ‘md’, ‘lg’, ‘xl’ and ‘2xl’ breakpoints but you are able to change these or add a prefix through tailwind’s config file. More information on this can be found here.
Just-in-Time (JIT)
The other feature I wanted to call out was the Just-in-Time compiler that was added in version 2.1. Tailwind’s JIT offers a huge change in flexibility as it will generate the classes you use on the fly. Prior to this Tailwind needed to have every option predefined. Now you are able to have any combinations of classes together and the JIT will generate exactly what you are after.
Tailwind CSS and Blazor
Now as I wrote in my last post I am using Blazor WebAssembly as my frontend framework and I wanted to know how to use Tailwind with my Blazor WASM project. This post will be a high level run through of the steps required to get this working. If you are after a more in depth tutorial on integrating Tailwind and Blazor, hit me up on Twitter and let me know.
Setup
As Tailwind is built with Node js we will need to setup our Blazor WebAssembly project with a basic node environment. If you are looking at Tailwind then I assume you have Node already installed but if not then you can get it from the Node js website. Just a note that if you have installed Node JS through the NVM tool (which I had initially done) this will cause issues later with Visual Studio To fix this either use a different development environment to Visual Studio (Rider or VS Code will do) or re-install Node through the link above.
Once setup, initialise the package.json through the npm init
command through your terminal of choice. This should be run at the root of you Blazor project.
Once you have completed the initial setup you need add the following list of packages to your devDependencies in your package.json file.
- autoprefixer
- cross-env
- cssnano
- postcss
- postcss-cli
- tailwindcss
We won’t go through each of these packages now but they are all used either directly in Tailwind or part of the build process.
While you are in your package.json file at the following scripts.
"scripts": {
"buildcss:dev": "cross-env TAILWIND_MODE=build npx tailwindcss -i ./Styles/app.css -o ./wwwroot/css/app.css",
"buildcss:release": "cross-env NODE_ENV=production npx tailwindcss -i ./Styles/app.css -o ./wwwroot/css/app.css"
}
These scripts should be reasonably self explanatory but one is used to build Tailwind while in development and the other is used when building a release version of your project.
you can also see here an input app.css file. This is used for any css you want to include in your build. The -o ./wwwroot/css/app.css
is the built css file and will need to be included in your index.html file. More on this in a moment.
Now create a postcss.config.js file and add the following configuration.
module.exports = ({env}) => ({
plugins: {
tailwindcss: {},
autoprefixer: {},
cssnano: env === "production" ? { preset: "default" } : false
}
})
This setups production build to be minified.
next create a tailwind.config.js file and add the following config to it.
module.exports = {
mode: 'jit',
purge: [
'../Server/Pages/_Host.cshtml',
'./**/*.razor'
],
darkMode: 'media', // or 'media or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
}
}
We won’t go through everything here but I will call out the the mode
and purge
options.
I have set the mode
option to be able to use the Just-in-Time compiler mentioned earlier in this post. This option is off by default.
the purge
option is used in production builds of Tailwind. In this list we provide files where we have used tailwind utility classes. Tailwind will then on production builds go through this list of files and anything that it can’t find being referenced will be removed. This is to minimise your css file size.
The list above is my _host.cshtml from my hosted server project and a wildcard for any razor files in my client project, you can add whatever your project requires. Take note that wildcards are allowed.
The final file we need to get tailwind setup is the app.css file mentioned earlier. Now I have put mine in a styles folder of the root of my Blazor project but this can be anywhere. Just remember to update your npm scripts to reflect the location of this file. In this file add the following.
@tailwind base;
@tailwind components;
@tailwind utilities;
The last change we need to do is reference the generated tailwind css mentioned above. In my _layout.cshtml file I have added as follows to my head file. Add this to wherever your index file is.
<head>
...
<link href="css/app.css" rel="stylesheet" />
...
</head>
with all of that you can now run either your dev or release npm scripts before you build your blazor project to get your tailwind classes.
Auto Build Tailwind on build
Now manually running NPM scripts works however it become fiddly and error prone really quickly. Wouldn’t it be better if we could get it to build as apart of the blazor build process. Well we can..
In your Blazor WebAssembly csproj file add the following.
<Target Name="CheckForNpm" BeforeTargets="NpmInstall">
<Exec Command="npm -v" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="You must install NPM to build this project" />
</Target>
<Target Name="NpmInstall" BeforeTargets="BuildCSS" Inputs="package.json" Outputs="$(NpmLastInstall)">
<Exec Command="npm install" />
<Touch Files="$(NpmLastInstall)" AlwaysCreate="true" />
</Target>
<Target Name="BuildCSS" BeforeTargets="Compile">
<Exec Command="npm run buildcss:dev" Condition="'$(Configuration)' == 'Debug'" />
<Exec Command="npm run buildcss:release" Condition="'$(Configuration)' == 'Release'" />
</Target>
I will break this down. the CheckForNPM
Target checks to make sure that npm is available in the environment that this project is being built. If it isn’t the build fails and the error message is given.
The NpmInstall
Target is then run and will make sure all npm dependencies are installed. This is really helpful for CI/CD builds of your project. This also creates a file. This is to ensure that the Target is run. I will explain more about this file shortly.
Lastly the BuildCSS
Target runs either the development or release npm scripts depending on whether we are running a Debug or Release build of our project.
Just a note that the npm install command initially failed for me when I was building this project in Visual Studio for Mac. This was because I had installed Node through the NVM tool. To work around this either install node through the official website or use another IDE like Jetbrains Rider
finally as mentioned add the following line to your csproj file to generate the NpmLastInstall
file.
<PropertyGroup>
<NpmLastInstall>node_modules/.last-install</NpmLastInstall>
</PropertyGroup>
this creates the file in the node_modules file. I put it here as it won’t get checked into my Version Control however you can put this file wherever you like.
With that now whenever you build your Blazor project it will also run a build of Tailwind to generate the latest set of css required for your site. Pretty neat hey.
That is it to get you up a running with Tailwind and Blazor. If you have any questions reach out to me on Twitter.