@dloizides/event-landing-kit
v1.0.0
Published
Framework-free toolkit for single-page event landing sites (dance socials, congresses): a configurable PWA service worker, live registration stats from a public ledger.json, a registration-deadline countdown with door-fallback swap, and a WhatsApp deep-li
Maintainers
Readme
@dloizides/event-landing-kit
Framework-free toolkit for single-page event landing sites (dance socials, congresses, one-off parties). Extracted from the hand-written Kizomba Union CY and United by Salsa sites, which duplicated the same four pieces of logic with a copy-paste bug between them.
It ships browser IIFE bundles meant to be vendored into static sites that
have no build step (<script src> + importScripts), plus CJS/ESM for anyone who
does build. Zero runtime dependencies.
What's in it
| Piece | API | Replaces |
|---|---|---|
| Service-worker core | self.EventSW.register({...}) | the per-site sw.js (network-first HTML, SWR assets, gated live paths) |
| Live registration stats | EventLandingKit.initLedgerStats({...}) | the ledger.json → counts widget |
| Deadline countdown | EventLandingKit.initCountdown({...}) | the register ↔ "pay at the door" swap + countdown |
| WhatsApp deep link | EventLandingKit.buildWhatsAppUrl(phone, lines) | the wa.me URL builder in the register form |
The registration form itself stays per-site (each event has its own passes / add-ons / pricing); only the WhatsApp-link builder is shared.
Vendoring into a build-step-less static site
Copy the two IIFE files from dist/ into the site (e.g. assets/js/):
dist/event-landing-kit.global.js → assets/js/event-landing-kit.js
dist/event-sw-core.global.js → assets/js/event-sw-core.jsPage kit
<script src="/assets/js/event-landing-kit.js"></script>
<script>
EventLandingKit.initLedgerStats(); // defaults: /ledger/ledger.json, threshold 100
EventLandingKit.initCountdown({
deadlineMs: new Date('2026-05-16T13:00:00+03:00').getTime(),
strings: {
bookSubOpen: 'Reserve your pass, then pay via Revolut.',
bookSubClosed: 'Online payments closed — pay cash at the door.',
},
});
// In the register form's submit handler:
const url = EventLandingKit.buildWhatsAppUrl('35796650527', lines);
</script>Both initLedgerStats and initCountdown default to the canonical element IDs
(reg-stats, reg-stat-total, register-countdown, cd-days, book-sub, …)
and no-op safely when an element is missing — so partial pages just work.
Service worker (sw.js)
importScripts('/assets/js/event-sw-core.js');
self.EventSW.register({
cacheName: 'kucy-v185', // bump on each asset change
cachePrefix: 'kucy-', // cleanup deletes ONLY this site's caches
precache: ['/', '/index.html', '/manifest.webmanifest', '/assets/logo/icon-192.png'],
gatedPaths: ['/ledger/', '/door/', '/ticket/', '/media/', '/mediaTicket/', '/dj/'],
// fallbackDocument defaults to '/index.html'
});cachePrefix fixes the original fork bug: each site must clean up only its own
caches. (The pre-extraction UBS worker still purged kucy- caches — a leftover from
being copied off the KUCY worker.)
Behaviour notes
- Strategy is identical to the workers it replaces: HTML/navigation → network-first (cache fallback → fallback document); other same-origin GETs → stale-while-revalidate; cross-origin + gated live paths → pass-through.
- The install step per-item-catches precache failures so one renamed/missing
asset doesn't fail the whole install. (The extracted core does this silently —
the original logged a
console.warn.) initLedgerStatsexposes only aggregate counts (total / male / female / other / not-specified, paid vs expected) and reveals the card only past the threshold. It never throws — on any error the stats just stay hidden.initCountdownis pure client-side: every visit re-checks the clock, so no redeploy is needed at the cutover. It returns a controller withstop().
Development
npm install
npm run lint && npm run typecheck && npm test # 100% coverage gate
npm run build # dist/ (cjs + esm + iife + d.ts)MIT © dloizides
