Skip to content

Commit 7d1fb24

Browse files
Replace Tailwind CSS CDN with Vite build-time compilation
Use Vite with @tailwindcss/vite to compile CSS at build time instead of loading the ~100KB Tailwind browser runtime from CDN on every page. - vite.config.js: Tailwind plugin, content-hashed CSS output + manifest - dev/vite_hook.clj: shadow-cljs hook runs `vite build` automatically - server.clj: reads Vite JSON manifest for hashed CSS filename - build-ui.sh: runs `npx vite build` before shadow-cljs release No workflow change — shadow-cljs watch/compile triggers Vite via hook.
1 parent b5768c3 commit 7d1fb24

File tree

10 files changed

+1707
-43
lines changed

10 files changed

+1707
-43
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ scripts/code-format
3030
node_modules/
3131
public/
3232

33-
# compiled frontend output
33+
# compiled frontend output (shadow-cljs)
3434
resource/public/main.js
3535
resource/public/main*.js
3636
resource/public/manifest.edn
37+
# compiled frontend output (vite)
38+
resource/public/main*.css
39+
resource/public/css-manifest.json
3740

3841
# claude files
3942
CLAUDE.md

dev/vite_hook.clj

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
(ns vite-hook
2+
"Shadow-cljs build hook: runs Vite CSS build.
3+
Dev mode: one-shot build + background watcher for live updates.
4+
Release/compile: one-shot build only."
5+
(:import [java.lang ProcessBuilder ProcessBuilder$Redirect]))
6+
7+
(defonce ^:private state (atom {}))
8+
9+
(defn- clean-old-css!
10+
"Removes previously built CSS files and manifest from resource/public/."
11+
[]
12+
(doseq [f (.listFiles (java.io.File. "resource/public"))]
13+
(let [name (.getName f)]
14+
(when (or (and (.startsWith name "main.") (.endsWith name ".css"))
15+
(= name "css-manifest.json"))
16+
(.delete f)))))
17+
18+
(defn- run-vite-build!
19+
"Runs `npx vite build` synchronously. Returns true on success.
20+
In dev mode, logs warning on failure instead of crashing the REPL."
21+
[]
22+
(clean-old-css!)
23+
(let [proc (-> (ProcessBuilder. ["npx" "vite" "build" "--mode" "development"])
24+
(.redirectError ProcessBuilder$Redirect/INHERIT)
25+
(.redirectOutput ProcessBuilder$Redirect/INHERIT)
26+
(.start))
27+
exit (.waitFor proc)]
28+
(if (zero? exit)
29+
true
30+
(do (println "[vite] CSS build failed (exit" exit ") — fix your CSS and save again")
31+
false))))
32+
33+
(defn- start-watcher!
34+
"Starts `vite build --watch` in the background for live CSS updates."
35+
[]
36+
(when-let [^Process old (:process @state)]
37+
(when (.isAlive old) (.destroyForcibly old)))
38+
(let [proc (-> (ProcessBuilder. ["npx" "vite" "build" "--watch" "--mode" "development"])
39+
(.redirectError ProcessBuilder$Redirect/INHERIT)
40+
(.redirectOutput ProcessBuilder$Redirect/INHERIT)
41+
(.start))]
42+
(swap! state assoc :process proc)
43+
(when-not (:shutdown-hook? @state)
44+
(.addShutdownHook (Runtime/getRuntime)
45+
(Thread. #(when-let [^Process p (:process @state)]
46+
(when (.isAlive p) (.destroyForcibly p)))))
47+
(swap! state assoc :shutdown-hook? true))))
48+
49+
(defn build-css
50+
"Shadow-cljs build hook. Compiles CSS via Vite at :configure stage.
51+
In dev mode, also starts a background watcher for live updates."
52+
{:shadow.build/stage :configure}
53+
[build-state & _args]
54+
(let [dev? (= :dev (:shadow.build/mode build-state))
55+
ok? (run-vite-build!)]
56+
(when (and (not ok?) (not dev?))
57+
(throw (ex-info "[vite] CSS build failed" {})))
58+
(when dev?
59+
(start-watcher!)))
60+
build-state)

0 commit comments

Comments
 (0)