Structuring Tailwind CSS with Spring Boot, gradle, and (optionally) thymeleaf templates
See the simple hosted example here
or
Structured as a flat multi-module gradle project. Java is the spring boot application, web builds tailwind with gulp and copies the thymeleaf templates (for live-reloading during development)
root
- /docker
- build.gradle
- /java
- build.gradle
- /web
- build.gradle
- settings.gradle
In addition to the template generated from https://start.spring.io/,
our java build.gradle
module should have a dependency on the web
module so a ./gradlew bootRun
will completely build and start our
application without running any additional commands.
We accomplish this by first creating a tailwind
configuration
configurations {
tailwind
bootJars
}
then declare a dependency on the web module
// there's a hack here that i'll explain later so we don't have to
// declare the configuration
dependencies {
tailwind project(':web')
}
finally create a task to copy the css files from the web configuration to our spring boot resource output directory
task copyCss(type: Copy) {
from configurations.tailwind
into "$sourceSets.main.output.resourcesDir/static"
}
// depend on assemble
// https://docs.gradle.org/current/userguide/img/javaPluginTasks.png
assemble.dependsOn copyCss
We use the node gradle plugin but it's not strictly necessary if you want to exec commands directly. Most of the code here is related to the gulp watch task and passing paths around. If you want you can just hardcode all the paths without too much concern (not publishing). The only magic we add is to declare our compiled tailwind styles as an artifact so we can depend on it in our java project.
// declare the default configuration
configurations {
create 'default'
}
// depend on assemble so styles are compiled when we do a `build`
task assemble {
dependsOn 'build'
}
artifacts {
// use 'default' configuration so we don't have to specify in :java
'default' file("$buildDir/styles")
}
The only other logic is in the gulpfile which is fairly boilerplate besides passing some env variables from the gradle build to control the paths. We also set
spring:
thymeleaf:
cache: false
in the spring application.yaml
so that spring picks up any changed
thymeleaf templates instead of caching them in memory.
For the dev workflow we open one terminal to run ./gradlew bootRun
then in another we run our gulp watch task (directly in the web dir
or through gradle) with ./gradlew :web:watch