orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1// Two pages animate words in and out through the enter / enter-active /
2// enter-done / exit / exit-active class sequence, and their CSS is written
3// against exactly those state names.
4//
5// The forced reflow matters. Adding the "from" class and the "active" class in
6// the same frame means the browser only ever computes the end state, and no
7// transition runs at all.
8
9const nextFrame = (fn) => requestAnimationFrame(() => requestAnimationFrame(fn));
10
11/** Play the enter sequence on an element. Classes: {from, active, done}. */
12export const enter = (el, classes, duration) => {
13 el.classList.add(classes.from);
14 void el.offsetWidth;
15
16 nextFrame(() => {
17 el.classList.add(classes.active);
18 window.setTimeout(() => {
19 el.classList.remove(classes.from, classes.active);
20 if (classes.done) el.classList.add(classes.done);
21 }, duration);
22 });
23};
24
25/** Play the exit sequence, then run onDone (usually removing the element). */
26export const exit = (el, classes, duration, onDone) => {
27 if (classes.clear) el.classList.remove(...classes.clear);
28 el.classList.add(classes.from);
29 void el.offsetWidth;
30
31 nextFrame(() => {
32 el.classList.add(classes.active);
33 window.setTimeout(() => {
34 el.classList.remove(classes.from, classes.active);
35 if (onDone) onDone(el);
36 }, duration);
37 });
38};