With Jetpack Compose for Web, you have the option of defining your own styles using the provided Kotlin DSL, but I wanted to use Tailwind CSS for my project so this post outlines how did the setup required.
Background
The Kotlin IR compiler takes Kotlin code as input and outputs Javascript code. For this Javascript code to be usable on the browser, the Kotlin gradle plugin uses Webpack. Webpack bundles the Javascript code into a single one. It can also bundle CSS files that are imported in Javascript files.
To get this set up to work, I needed to customise the webpack configuration to include the required tools for Tailwind CSS.
NPM dependencies
Following the official Tailwind CSS guide, we should add the following npm dependencies
implementation(npm("tailwindcss", "3.0.23")) // Official tailwindcss package
implementation(npm("postcss", "8.4.8")) // CSS processor called postcss
implementation(npm("autoprefixer", "10.4.2")) //parse CSS and add browser specific prefixes to CSS rules
And since KotlinJS projects use Webpack by default, we will need to add the following webpack loaders to handle CSS files and integrate with postcss
implementation(npm("style-loader", "2.0.0"))
implementation(npm("css-loader", "5.2.7")) // required to handle @import/url() rules in CSS files
implementation(npm("postcss-loader", "4.3.0")) // required to invoke postcss during bundling
Configuring Tailwind CSS
Per the official documentation we need to have a postcss.config.js
and a tailwind.config.js
. I’ve created them and put
them in the root directory of my project.
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
// tailwind.config.js
module.exports = {
content: ["**/*.{html,js}"], // This is important
theme: {
extend: {},
},
plugins: [],
}
Customising Webpack
By default, the Kotlin gradle plugin will generate its own webpack config file but we can customise it
by adding .js
file into webpack.config.d
directory. This directory should be in the root of the project.
In order to add support for handling css files using style-loader
, css-loader
and postcss-loader
, I’ve created postcss.js
inside my webpack.config.d
config.module.rules.push({
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
});
Updating the build process
By default, webpack is invoked after the Kotlin code has been compiled. This means that the working directory for webpack is somewhere inside the build/
directory
and for our css configuration to work, we need to move postcss.config.js
and tailwind.config.js
to the same directory before webpack is invoked.
So First, let’s create a gradle task to copy the files
tasks.register<Copy>("copyTailwindConfig") {
dependsOn("kotlinNpmInstall") // ensures that all required dependencies are installed
val jsFolder = project
.buildDir
.resolve("js")
.resolve("packages")
.resolve(rootProject.name) // This is where the JS is and where webpack works
delete(jsFolder.resolve("tailwind.config.js")) // deleting any existing tailwind.config.js file
// Copying tailwind.config.js from root directory to js folder
from(projectDir.resolve("tailwind.config.js"))
into(jsFolder)
delete(jsFolder.resolve("postcss.config.js"))
// Copying tailwind.config.js from root directory to js folder
from(projectDir.resolve("postcss.config.js"))
into(jsFolder)
}
Then we can hook into the build process by making jsBrowserDevelopmentRun
& jsBrowserProductionRun
depend on our task
tasks.getByName("jsBrowserDevelopmentRun").dependsOn("copyTailwindConfig")
tasks.getByName("jsBrowserProductionRun").dependsOn("copyTailwindConfig")
Importing Tailwind CSS
After setting up the build system to handle CSS files, we can include our default CSS file.
This file is copied from Tailwind’s official guide and stored in src/jsMain/resources/
directory.
/* index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
If you compile and check where this CSS file located, you will notice it’s on the same directory level as the JS bundle.
This is important as we need to reference that CSS file in our Javascript bundle, via Kotlin, for Webpack to know about it.
In Javascript, we usually can do import "./index.css"
and the equivalent in Kotlin is using @JsModule
annotation.
@JsModule("./input.css")
external val cssFile: dynamic
Wrapping up
With the above configuration, we made sure that first we moved Tailwind CSS and PostCSS configuration files to the correct location before webpack starts using them. We also customised the webpack configuration to support bundling CSS files and process them with PostCSS.
PostCSS will invoke Tailwind to insect the generated JS bundle for usages for Tailwind CSS related CSS classes and include the required styles in our bundle.
Finally, we are ready to use Tailwind’s classes in our composables
fun main() {
renderComposable(rootElementId = "root") {
Div({ classes("container", "mx-auto", "bg-cyan-700") }) {
// Content
}
}
}