Interaction
Motion and glass need a readable resting state
The hero on this portfolio pairs an illustrative code editor with a real project capture. The animation is an enhancement to a complete preview, so the content has a useful state before animation starts.
Design the complete state first
A code-to-website sequence is enjoyable when someone wants to follow it. A visitor using reduced motion or browsing without JavaScript still needs a recognisable project and a route to explore it.
The scene therefore has a fully assembled static presentation. Its controller checks the reduced-motion preference and paints the completed state. Keyboard focus inside the scene also completes the preview before project exploration.
if (motion.matches) {
furthestProgress = 1;
return paint(1);
}
scene.addEventListener('focusin', () => {
furthestProgress = 1;
paint(1);
});Let progress finish
The controller keeps the furthest progress reached. Scrolling back up does not make the assembled preview disappear again. A requestAnimationFrame queue groups scroll updates so several scroll events do not each trigger their own paint.
This is deliberately a one-time assembly rather than an endless animation loop. The project button and live-site link remain ordinary controls with a concrete destination or dialog action.
- Respect reduced motion on the first visit and when the preference changes.
- Complete the preview for keyboard exploration.
- Keep project information useful after the visual sequence has finished.
Treat refraction as decoration
The copied liquid-glass module uses an SVG displacement filter where its browser support check allows it. Safari and Firefox take the module’s frosted-blur path. The shape still reads as a glass surface without relying on refraction to communicate anything.
Each surface gets its effect when it is initialised. The displacement map is generated from the element’s size; a debounced ResizeObserver refreshes it when that size changes. Moving a panel during a scroll sequence does not require a new map.
That distinction keeps the animation work focused on visual transforms instead of rebuilding the optical effect for every position change.
const ro = new ResizeObserver(function () {
clearTimeout(timer);
timer = setTimeout(refresh, 120);
});
ro.observe(el);Verify the experience, not just the effect
The portfolio’s browser checks cover reduced-motion layouts, the hero without JavaScript, project exploration by keyboard, and the change from normal to reduced motion. Glass tests also exercise the fallback selection with simulated browser identities.
A simulated browser identity can check that the fallback branch is selected. It cannot establish how an actual Safari or Firefox renderer looks, so visual checks in those browsers remain a separate task.
- Read the complete scene with animation disabled.
- Open and close the project dialog using the keyboard.
- Check text contrast and focus visibility over the real background.