Skip to content

JSP / Gradle setup

The JSP path is the vanilla path plus a few server conveniences:

  • paths built from the servlet context instead of hard-coded roots,
  • an asset version query string (?v=…) for cache-busting on deploy,
  • the design-system build wired into Gradle so it runs with your app build.

The design system itself is identical — same CSS, same JS.

Put this in something like includes/header.jsp. Adjust the context paths and supply an ASSET_VERSION value from your app.

includes/header.jsp
<%-- design tokens for native control chrome --%>
<style>
:root {
--select-bg: url(${pageContext.request.contextPath}/design-system/assets/section-icons/selector.svg);
--date-bg: url(${pageContext.request.contextPath}/design-system/assets/section-icons/calendar.svg);
--optional-title: 'Optional';
}
</style>
<link rel="stylesheet" href="${pageContext.request.contextPath}/app.css?v=<%=ASSET_VERSION%>" />
<%-- theme: run before paint --%>
<script>
;(function () {
const theme = localStorage.getItem('theme') || 'system'
const className =
theme === 'system'
? window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
: theme
document.documentElement.classList.add(className)
})()
</script>
<%-- third-party libraries --%>
<script src="https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/cdn.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/locale/en-US/cdn.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/animejs/dist/bundles/anime.umd.min.js" defer></script>
<%-- preline (bundled with the design system) --%>
<script
src="${pageContext.request.contextPath}/design-system/assets/preline/preline.js"
defer
></script>
<%-- runtime + components (note the ?v= cache-buster on each) --%>
<c:set var="v" value="<%=ASSET_VERSION%>" />
<script src="${pageContext.request.contextPath}/design-system/js/runtime.js?v=${v}" defer></script>
<script
src="${pageContext.request.contextPath}/design-system/js/_defaults.js?v=${v}"
defer
></script>
<script src="${pageContext.request.contextPath}/design-system/js/theme.js?v=${v}" defer></script>
<script src="${pageContext.request.contextPath}/design-system/js/format.js?v=${v}" defer></script>
<script src="${pageContext.request.contextPath}/design-system/js/shell.js?v=${v}" defer></script>
<script
src="${pageContext.request.contextPath}/design-system/js/table.js?v=${v}"
type="module"
></script>
<script
src="${pageContext.request.contextPath}/design-system/js/input.js?v=${v}"
type="module"
></script>
<%-- …load the rest of the component scripts the same way… --%>

Add the node-gradle plugin and a task that runs the design-system build through pnpm.

  1. Add the plugin:

    build.gradle
    plugins {
    id 'com.github.node-gradle.node' version '7.1.0'
    }
  2. Configure Node + pnpm (downloaded and managed by Gradle):

    build.gradle
    node {
    version = '22.12.0'
    pnpmVersion = '10.12.2'
    download = true
    }
  3. Register the UI build/watch as pnpm tasks. pnpmInstall comes from the plugin; build:ui and watch:ui are the scripts from Installation:

    build.gradle
    tasks.register('buildDs', PnpmTask) {
    dependsOn pnpmInstall
    description = 'Compile the project ui'
    args = ['build:ui']
    }
    tasks.register('watchDs', PnpmTask) {
    dependsOn pnpmInstall
    description = 'Watch the project ui'
    args = ['watch:ui']
    }

    Run the watcher during development with ./gradlew watchDs.

  4. Hook the one-shot build into your WAR/app build so the compiled CSS ships with the app, e.g.:

    build.gradle
    tasks.named('processResources') { dependsOn 'buildDs' }