0.5.0: on npm, 36% faster list creation, accessibility at 10/10
Voodoo.js is on npm.
npm install voodoojs<script src="https://cdn.jsdelivr.net/npm/voodoojs@0.5/dist/voodoo.min.js" defer></script>Performance
Creating a 1,000-row keyed list is 36.2% faster; clearing one is 44.6% faster.
| before | after | ||
|---|---|---|---|
| create 1k | 129.98 ms | 82.98 ms | -36.2%, won 45 of 45 rounds |
| clear 1k | 28.14 ms | 15.59 ms | -44.6%, won 38 of 45 |
| update | 2.60 ms | 2.71 ms | no measurable change |
Measured with a paired harness that loads both builds in one process and interleaves their samples, alternating order each round, so the machine's 20-40% drift between runs cancels out. A null A/B of identical sources put the noise floor at ±0.5 ms.
Against other frameworks, Voodoo moved from sixth of seven to third on create, and from last to fourth on clear.
| create 1k | update every 10th | clear 1k | |
|---|---|---|---|
| vanilla JS | 48.74 | 7.52 | 21.06 |
| Preact 10.29.8 | 91.62 | 2.59 | 29.48 |
| Voodoo.js | 97.70 | 5.42 | 30.44 |
| React 19.2.8 | 100.23 | 4.63 | 33.59 |
| Vue 3.5.42 | 110.56 | 19.21 | 31.65 |
| Solid 1.9.15 | 111.63 | 0.91 | 19.99 |
| Alpine 3.17.1 | 179.47 | 104.51 | 31.39 |
Four changes account for nearly all of it: reading getAttributeNames() instead of indexing the live attributes collection, v-for stripping the key attribute off its row template, reactive class fields declared rather than emitted as Object.defineProperty calls under useDefineForClassFields, and building a directive's EffectScope only on demand.
Six further optimisations were measured, found to sit inside the noise floor, and reverted. They are written down next to the ones that worked, because a list of what did not help is worth as much as the list of what did.
Accessibility: 5.3 → 10/10
Drawer naming and reduced motion, dropdown Enter and Space, tooltips reachable by keyboard and touch rather than hover alone, tablist orientation driving its arrow keys, accordion panels as labelled regions, a command palette that traps focus against script and pointer escapes rather than only Tab, and aria-atomic on toasts.
44 browser tests, real Chromium
Playwright, covering what jsdom structurally cannot: clicks hit-tested against real layout, v-for node identity proven with expandos stamped from outside the framework, the modal focus trap (jsdom reports every element as zero-sized, so the trap could never be exercised there), and a CSP suite that loads the framework under script-src 'self' with no unsafe-eval and asserts zero policy violations.
It found a real defect, kept at full strength as a failing test rather than skipped: v-modal-content stays visible after the modal closes, because cleanAttributes strips the very attribute the re-hide condition tests for.
Also
walker.tsheld a raw NUL byte in theparseAttributecache key, which made git treat a TypeScript file as binary and undiffable.scripts/quality/browser.mjsasserted before the boot loop mounts. No runner had ever been installed, so that check had never once executed.- The
voodoo.core.min.jsgzip budget moves 46 → 47 KB, with the reason recorded insize.mjs: a zero-headroom budget was about to block a one-line accessibility fix over ten gzipped bytes. scripts/chart-comparison.mjsdraws the comparison chart from the benchmark JSON, so the picture can no longer drift from the table beside it.
Full changelog: CHANGELOG.md