Skip to content
Numan Zahid
Get in touch
All work

An animated studio portfolio that barely uses any JavaScript

Creative studio · Build from design, performance and SEO

Astro 6Svelte 5PartytownContent collectionsTypeScript
5Hydrated islands; the rest of the site is static HTML
0Analytics code on the main thread
5Image presets shared between Astro and Svelte components
Starting point
  • The design called for a continuously running reel carousel, a full-screen gallery lightbox and scroll-driven reveals -- the kind of motion that usually arrives as a heavy client bundle.
  • Case studies needed to be data, so adding one did not mean writing another page.
  • Analytics is non-negotiable for the client and is also the single biggest main-thread cost on a site like this.
What I did
  • Used islands as a budget rather than a convenience: five hydrated components across the whole site, each chosen because it genuinely needs client state.
  • Modelled projects as a typed content collection with images declared in the schema, so images are validated and optimised at build time alongside the copy.
  • Built one image preset system feeding both the Astro components and the Svelte islands, so both render the same responsive sources.
  • Moved analytics off the main thread into a web worker, with an escape hatch so the client's tag debugger still works.
Measured outcome
  • Adding a case study is a Markdown file and a folder of images; the route, the sitemap entry and the optimised sources follow.
  • The motion runs without the bundle usually attached to it, and switches itself off entirely for visitors who ask for reduced motion.
  • Every page carries canonical, Open Graph and Twitter metadata from one builder, with a generated sitemap.
Technical detail
  • The carousel pauses when the tab is hidden, when the pointer is over it, and until fonts have finished loading -- because starting a measured animation before the webfont swaps makes it jump on first paint.
  • Reduced-motion handling is in both the CSS and the JavaScript. CSS alone stops the transition but leaves the observer running, so the script checks too and skips the work.
  • Images go through the build pipeline as WebP with explicit width sets. The Svelte islands cannot call the build-time image API, so presets are resolved in Astro and passed down as pre-built source sets, which keeps one definition of each size.
  • A single frontmatter flag hides a work-in-progress case study from both the routes and the sitemap, so unfinished content stays in the repository instead of living on a branch.
  • The analytics worker is bypassed when a debug query parameter is present, because tag-assistant tools cannot see a worker and the client needed to verify their own tracking.
  • The lightbox traps focus, handles arrow and escape keys, and marks decorative clones aria-hidden with negative tabindex, so the duplicated carousel nodes are not announced twice.

Got a project?

Email me with where things are now and what you'd like to change. If I'm not the right person for it, I'll tell you, and I'll usually know someone who is.

hello@zahid.dev