Verified n8n node that puts VIES VAT validation and EU rate lookups inside a workflow, so onboarding or invoicing can decide on a real VAT number instead of a
Zero-dependency Halloween screen-corner decorations and ambient page effects — one script tag or npm install, class-driven, seasonal by default, under 10 KB gzipped.
Key Results
Every year, sites that want a Halloween touch end up choosing between two bad options: a heavyweight jQuery-era plugin dragging in dependencies and doing DOM surgery on the page, or a developer hand-rolling one-off canvas/SVG animations that get ripped out again come November — and forgotten about, so they never come back next year either.
Neither approach fits a modern site. Marketing and content teams on WordPress or a no-build CMS need something they can drop in without a deploy pipeline. Product teams on a modern stack need something that won't touch their bundle budget or fight their build tooling. And nobody wants to remember to manually disable a seasonal decoration on November 3rd.
halloween.js is the drop-in fix: a single script tag or npm import, controlled by one CSS class, that decorates a page for Halloween and quietly turns itself off the rest of the year.
I built halloween.js as a zero-dependency vanilla JS library with a single master switch: adding class="halloween" to <body> turns the whole system on, and a MutationObserver keeps it in sync with the page automatically — no imperative init call needed for the common case. Additional classes opt into specific decorations: four static screen-corner spider webs, blinking eyes, flying witches, a dropping spider, and a rising tombstone, each with subtle/normal/party intensity presets.
The library is seasonal by default — effects only run inside a configurable ~2-week window around Halloween (18 Oct – 2 Nov), validated against the real calendar rather than naive string matching, so a site owner never has to remember to switch it off. Color inherits currentColor by default via a CSS custom property, so it matches the host page's theme without configuration on both light and dark sites. Every animation respects prefers-reduced-motion, including live changes to that preference while the page stays open.
It ships three ways from one TypeScript source: an IIFE bundle for the no-build case (WordPress, plain HTML, any CMS with a script-tag field), and ESM/CJS builds with full .d.ts types for npm/bundler consumers — all under 10 KB gzipped, zero runtime dependencies.
| Metric | Value |
|---|---|
| Bundle size | 9,102 B gzip (IIFE, styles included) |
| Runtime dependencies | 0 |
| Tests | 130, passing on Node 22 and 24 in CI |
| Public API surface | 3 exports (default, halloween, pageEffects) |
| Distribution formats | ESM, CJS, IIFE, with TypeScript declarations for each |
| Publishing | Trusted OIDC publishing to npm on tagged release |
For the technically curious, here is how the class-driven sync, season gating, and release pipeline work.
The library never requires an explicit init call for the common path. A MutationObserver watches <body>'s class attribute and re-evaluates the master switch and every effect class on every change, so toggling document.body.classList.toggle("halloween-eyes", condition) from application code — including SPA route changes — is enough on its own. An exported halloween() function exists only for forcing an immediate re-sync ahead of the next automatic check; it re-runs the same master-switch and season checks rather than bypassing them.
The default window is 18 Oct – 2 Nov, overridable via data-halloween-start / data-halloween-end attributes on <body>, or — for the no-build script tag specifically — ?s=/?e= query params on the script's own src, with attribute values taking precedence over query params, each resolved independently. Dates are validated against the real calendar rather than simple string parsing: 31-04 is rejected as invalid, while 29-02 is accepted year-round and resolves to 28 Feb in non-leap years. A malformed (but non-empty) value fails open — runs as if unrestricted — rather than silently disabling the whole library, since a config typo shouldn't be able to take out an otherwise-working page.
Ambient effects (eyes, witches, spider-drop, tombstones) stop spawning the instant prefers-reduced-motion: reduce becomes true, and any effect currently on screen is removed immediately rather than waiting for its own animation to finish — checked via a live media-query listener, not just read once on load. Static corner webs are unaffected since they were never animated. All decorative SVGs carry aria-hidden.
Source is TypeScript, built with tsup into three targets from one entry point: ESM and CJS for bundler/npm consumers (with separate .d.ts / .d.cts declaration files so both module systems get correctly typed imports), and a single self-contained IIFE — styles included, no separate CSS file — for the script-tag case. sideEffects is declared explicitly in package.json so bundlers don't tree-shake away the auto-init behavior a plain import "halloween.js" relies on.
CI runs the full check suite (typecheck, tests, build, package-export verification) on Node 22 and 24 for every push and PR. Publishing to npm is gated behind pushing a v* tag and runs as a separate workflow that first verifies the tag matches package.json's version, then publishes using npm's trusted publishing via GitHub Actions OIDC — no long-lived npm token stored in CI secrets. The interactive demo (demo/) deploys to GitHub Pages on its own workflow, served on a custom domain (halloween.js.org) via the js.org program.
AvailableNeed something similar?
I build custom solutions — from APIs to full products. Let's talk about your project.
Verified n8n node that puts VIES VAT validation and EU rate lookups inside a workflow, so onboarding or invoicing can decide on a real VAT number instead of a
Free, open-source EU VAT rates for all 27 member states + UK. Published as native packages for npm, PyPI, Packagist, Go, and RubyGems.
How to publish one dataset to npm, PyPI, Go Module, RubyGems, and Packagist automatically with GitHub Actions — architecture, versioning, and per-ecosystem
IndexNow implementation guide for Next.js: key generation, TypeScript client with retry logic, GitHub Actions workflow, and pitfalls that break submissions