TLDR; This post will show how to create a custom Tailwind CSS build in a Gradle based Java project. This post is an update to the post made in 2021, as the steps outlined in that one are completely unneeded.
As of late December 2021 Tailwind features a CLI install, which is a node.js distribution packaged together with the tailwind code. All you need to do is to install that binary and no need to fiddle with any node or npm setup.
However that standalone Tailwind CLI build is platform specific and needs to
be treated like that. As I develop under osx, but the application Docker
image is built and run under Linux I need to take care of those two
platforms. In order to do that I added code like this to my build.gradle
file.
project.ext.tailwind = [
version: '3.3.2',
]
if (OperatingSystem.current().isMacOsX()) {
project.ext.tailwind.binary = 'tailwindcss-macos-arm64'
project.ext.tailwind.checksum = '58afb95d8d887d2c2bedb1a66880e98e'
}
if (OperatingSystem.current().isLinux()) {
project.ext.tailwind.binary = 'tailwindcss-linux-x64'
project.ext.tailwind.checksum = 'bedccc4c35b489909878a715020ab6a2'
}
project.ext.tailwind.exec = "${project.ext.tailwind.binary}-${project.ext.tailwind.version}"
project.ext.tailwind.path = ".tailwindcss-binaries/${project.ext.tailwind.exec}"
The idea is to ensure downloading the correct build per platform as well as checking SHA sums, as you’re downloading arbitrary content from the internet.
The next step is define a task to download the CLI distribution
plugins {
id "de.undercouch.download" version "5.4.0"
}
task tailwindCliDownload(type: Download) {
src "https://github.com/tailwindlabs/tailwindcss/releases/download/v${project.ext.tailwind.version}/${project.ext.tailwind.binary}"
dest project.ext.tailwind.path
overwrite false
}
The above Gradle task requires the fantastic Gradle download task plugin which only downloads the plugin if it does not exist yet locally. Next up is the requirement to verify the SHA sum (that task is from the same download plugin):
task tailwindCliVerify(type: Verify, dependsOn: tailwindCliDownload) {
src new File(project.ext.tailwind.path)
algorithm 'MD5'
checksum project.ext.tailwind.checksum
}
The last setup step is to ensure that the downloaded file is executable:
task tailwindCliExecutable(dependsOn: tailwindCliVerify) {
def perms = [
java.nio.file.attribute.PosixFilePermission.OWNER_READ,
java.nio.file.attribute.PosixFilePermission.OWNER_WRITE,
java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE
] as java.util.Set
if (java.nio.file.Files.exists(java.nio.file.Paths.get(project.ext.tailwind.path))) {
java.nio.file.Files.setPosixFilePermissions(java.nio.file.Paths.get(project.ext.tailwind.path), perms)
}
}
This task makes the binary executable. Having finished the setup, you can
now set up a tailwind.config.js
to make sure to create a small custom
build:
module.exports = {
content: [ './src/main/jte/**/*.{jte,html}' ],
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
]
}
The ‘only’ missing piece is building the CSS when needed, as well as having a watch task running. Let’s add tasks for this as well:
task watchCss(type: Exec, dependsOn: tailwindCliExecutable) {
commandLine project.ext.tailwind.path, '--watch=always', '-i', 'src/main/resources/css/build.css', '-o', 'src/main/resources/css/workshop.css'
}
task buildCss(type: Exec, dependsOn: tailwindCliExecutable) {
commandLine project.ext.tailwind.path, '-i', 'src/main/resources/css/build.css', '-o', 'src/main/resources/css/workshop.css'
}
assemble.dependsOn(buildCss)
You probably will have a different strategy than building your CSS file in
the src/main/resources
by adding a proper source set, instead of putting
workshop.css
file into the .gitignore
file in order to not push it. I
kept this for simplicity in my sample project.
The only obstacle I had was adding --watch=always
to the watchCss
task,
as without the always
the tailwind binary returns immediately instead of
keeping running. I found this not in the docs, but in this GitHub
issue.
Finally, you may want to add the --minify
argument to your build in order
to produce minified CSS.
That’s it for today. Happy tailwinding!