Moving a React design kit to Svelte 5 is the easy part. Proving the result is faithful is the work.
Stageshow is Benji Taylor's kit for making faux product UI that reads as a real screenshot: browser chrome, a cursor that types under a script, 69 worked scenes. It shipped as React. This is a port to Svelte 5, and the interesting question was never "does it compile" — it was "is it the same thing".
The obvious one is that it does not work. The subtle one is that it works and looks almost right: a 3px gap where there should be 4, a cursor that lands a character early, a card that pops instead of flying. Almost right is worse, because it looks finished.
So the port needed a test that could tell "almost right" from "right", on all 69 scenes, without a human staring at two screenshots for an afternoon.
The first version of the test captured each scene's final frame and diffed it against the original. It produced failures that were not real.
Animated scenes do not settle all at once. A scene can finish its last beat and still be mid-fade, mid-spring, mid-scroll. Diffing the last frame compares a settled original against a port that is one frame behind, and the score collapses for a reason that has nothing to do with the port.
Three scenes failed this way and then passed untouched: a cursor raycast, a dynamic island, and a slide transition. That is the point where you stop trusting the test.
Score at a named, settled mark in the scene's own script. Never at the last frame.
Every scene now declares a mark where it is visually at rest — after the transition finishes, before the next one starts. The gate reads that mark for both sides. The table of marks is in the repository, one row per scene.
A comparison is meaningless if the same scene renders differently on two loads. The kit
drives its animations from a script, so the port exposes a mode where CSS transitions are
off and a virtual 60fps clock drives every wait, tween and keystroke. Advancing the clock
to frame n must produce identical DOM, every time.
That harness ran 1,501 frames across the scenes and reported no mismatch. Only then was a screen comparison worth anything.
| measure | result |
|---|---|
| scene slugs | 69, every one ported |
| stylesheets | 42, carried over unchanged |
| brand marks | 44 SVGs |
| comparisons | 71, all matched at their settled mark |
| determinism | 1,501 frames, no mismatch |
| runtime | no React, react-dom or lucide |
Seventy-one comparisons for 69 scenes, because two of them are compared twice: once at the mark, once as a still.
Keeping the stylesheets unchanged was a deliberate constraint. If the CSS is the same, any visual difference is the port's fault, which makes the comparison mean something. It also means the design language stays Benji's rather than becoming a looser interpretation of it.
The code follows the same rule: same prop names, same defaults, CSS custom properties for the tokens. React's node props became Svelte snippets, and React's event props became callback props, because those are the language's native equivalents rather than a translation layer.
A scene can match its reference and still be a bad demo. The comparison says the port is faithful; it says nothing about whether the original was worth making. That judgment is still a person looking at the thing and deciding.
It also cannot tell you the port is usable. Matching pixels is not the same as a component that behaves well in someone else's app, with someone else's state and their own content inside it. That takes people using it, which is the next stage and the reason the package is public.