Interaction

Motion and glass need a readable resting state

Sahil Reghate ·

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.

JavaScript excerpt from dist/hero-motion.js
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.

JavaScript excerpt from dist/liquid-glass.js
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.