Sujin Kim.React 19 portfolio website for a graphic designer
Figma to React build · 2025
Sujin is a UI/UX, branding and graphic designer, so the brief was less “show my work” than “the site is a piece of work”. She designed it as a gallery: three rooms rather than pages, and a career you walk through rather than read. I built it.
That framing decides the engineering. A gallery has no address bar and no scrollbar, so the site has neither: one URL, a state machine, and scroll treated as a step rather than a distance. The work was making an interface that behaves like a place while staying something its owner can still add a year to.


Outcomes
From Figma to build
What arrived was unusually complete: a designer's file, drawn to the pixel, with the type and spacing already decided. What it could not describe is the part that matters most here, because a static frame has no way to say how long a transition takes or what a scroll means. The archive was handed over as one continuous column. The build had to turn it into somewhere you move through.




- The archive: one continuous column twelve full-screen chapters, generated from a data file, with a year rail to navigate them
- Finding work: a search field and five category filters the filters alone, since six projects across five categories is a list to browse rather than a corpus to query
- The category image: a static logotype two halves that slide apart on hover to expose a dictionary entry underneath
Building it in React 19
None of this photographs. A screenshot of the hero is a word, and a screenshot of the archive is a page: the whole argument of the site is what happens between two of them. So the clip below is the honest version of the build.





The hero is not text
The word is painted to a canvas once, read back with getImageData, and every pixel above alpha 50 becomes a particle with its own size and its own sensitivity to the cursor. Inside a 100px radius they are pushed away along the vector from the mouse, so the word does not shift, it shreds. Two layers crossfade, which keeps the typography sharp at rest and only pays for particles while someone is touching it.
Scroll read as intent, not distance
The page never scrolls: it is overflow hidden throughout, with a hidden element giving Lenis something to measure and each chapter moved on a transform. Four input sources feed one index, behind a 1000ms lockout and a velocity floor, so one flick of a trackpad advances exactly one year rather than three.
An archive the client can extend
The twelve chapters are not twelve components. They are derived at module scope from one keyed data file, which splits composite keys so a single year can hold several pages and branches when several things happened at once. The file documents the contract in its own comment, so adding a year is duplicating an object.
Also in the build: a nav that scrambles its letters through five randomised phases before resolving, a warm glow that falls from the active year onto the rail below it, seven photographs of the client's dog wired up as cursors, and an explicit filename to import map so the data files can stay plain strings while Vite still gets imports it can fingerprint.
Results
- The metaphor survived the build: Entrance, Exhibition and Invitation are the actual architecture, not section headings, and the copy stays in the same voice all the way down to the archive calling itself a chronicle.
- The hero is hers, not an effect: the particles are sampled from the letterforms of her chosen display face, so what scatters is the typography rather than a generic field laid over it.
- She can extend it without me: a new year in the archive is a duplicated object in one data file, which is why the twelve chapters are generated rather than drawn.
“Victor is absolutely amazing developer to work with. He is very responsible professional, and his kind advice will make your project even better. Attentive, caring, professional person to work with. Will definitely work with him again.”
Sujin Kim, via Upwork
Reflection
What worked
- Two layers instead of one: keeping real text at rest and only crossfading to particles on contact meant the hero could be both crisp typography and a physical effect, without paying for 90,000 particles while nobody is there.
- Deriving the archive rather than drawing it: twelve chapters from one keyed file was more work up front than twelve components, and it is the only reason the site is still hers to edit.
What I'd improve
- No reduced-motion path: on a site this motion-dependent that is the first thing I would fix. The particles, the chapter transitions and the entrance animations all run regardless of what the visitor has asked their system for.
- Nothing is linkable: no router means no URL for a project or a year, so a visitor cannot send anyone a specific piece of work. It is a defensible trade for one continuous canvas, but it is a real cost and I would want it priced openly next time.
More work
A SaaS marketing site that got its identity back
A B2B site that had drifted into looking like everyone else, rebuilt as a 39-token design system in hand-written code inside HubSpot.
L.M. Douglas · Vue 3A two-book site grown into an author platform
An interactive realm map, a status-tagged catalogue and a free-novella funnel, built in Vue 3 for a fantasy saga.
The Orchard Bar · React + TypeScriptA dated WordPress site rebuilt in React
Online booking, self-updating seasonal menus and a design that matches the venue, for a client retained since 2020.
Need something similar?
If you have a design that only makes sense in motion, the interesting work is deciding what the transitions mean. Happy to talk it through.
← Back to all work