feat: add CSS live reload for dev setup
Two-part solution for automatic CSS updates during development: 1. `bb watch-theme` — polls src/ui/*.css and tokens.edn every 500ms, rebuilds dist/theme.css and copies to dev targets on change. 2. `dev/css-live-reload.js` — browser-side script that polls /theme.css and hot-swaps the stylesheet without a full page reload (no FOUC). The watcher runs automatically in the hiccup tmux pane when using `bb dev-all`. It can also be run standalone with `bb watch-theme`. The live-reload script is included in all three dev targets (hiccup, replicant, squint) and copied by `bb build-theme`.
This commit is contained in:
42
dev/css-live-reload.js
Normal file
42
dev/css-live-reload.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// CSS Live Reload — polls /theme.css and hot-swaps the stylesheet on change.
|
||||
// No full page reload needed. Included automatically in dev pages.
|
||||
(function() {
|
||||
'use strict';
|
||||
var POLL_MS = 500;
|
||||
var link = document.querySelector('link[rel="stylesheet"][href*="theme"]');
|
||||
if (!link) return;
|
||||
|
||||
var prevHash = 0;
|
||||
|
||||
function djb2(str) {
|
||||
var h = 5381;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
h = ((h << 5) + h + str.charCodeAt(i)) | 0;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
function check() {
|
||||
fetch('/theme.css', { cache: 'no-store' })
|
||||
.then(function(r) { return r.text(); })
|
||||
.then(function(css) {
|
||||
var h = djb2(css);
|
||||
if (prevHash && h !== prevHash) {
|
||||
// Clone link and swap — avoids FOUC
|
||||
var next = link.cloneNode();
|
||||
next.href = '/theme.css?t=' + Date.now();
|
||||
link.parentNode.insertBefore(next, link.nextSibling);
|
||||
next.onload = function() {
|
||||
link.parentNode.removeChild(link);
|
||||
link = next;
|
||||
};
|
||||
console.log('[css-live-reload] theme.css updated');
|
||||
}
|
||||
prevHash = h;
|
||||
})
|
||||
.catch(function() { /* ignore network errors */ });
|
||||
}
|
||||
|
||||
check();
|
||||
setInterval(check, POLL_MS);
|
||||
})();
|
||||
@@ -503,6 +503,7 @@
|
||||
[:script (h/raw theme-persistence-script)]]
|
||||
[:body
|
||||
[:script {:src "/theme-adapter.js" :defer true}]
|
||||
[:script {:src "/css-live-reload.js" :defer true}]
|
||||
(sidebar/sidebar-layout {}
|
||||
(app-sidebar active-page port)
|
||||
(sidebar/sidebar-overlay {})
|
||||
@@ -534,6 +535,11 @@
|
||||
:headers {"Content-Type" "application/javascript"}
|
||||
:body (slurp "dev/theme-adapter.js")}
|
||||
|
||||
(= path "/css-live-reload.js")
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/javascript"}
|
||||
:body (slurp "dev/css-live-reload.js")}
|
||||
|
||||
(resolve-page path)
|
||||
{:status 200
|
||||
:headers {"Content-Type" "text/html; charset=utf-8"}
|
||||
|
||||
@@ -47,5 +47,6 @@
|
||||
<div id="app"></div>
|
||||
<script src="/js/main.js"></script>
|
||||
<script src="/theme-adapter.js" defer></script>
|
||||
<script src="/css-live-reload.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -47,5 +47,6 @@
|
||||
<div id="app"></div>
|
||||
<script type="module" src=".compiled/dev/squint.mjs"></script>
|
||||
<script src="/theme-adapter.js" defer></script>
|
||||
<script src="/css-live-reload.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user