For this year’s refresh, I really wanted to tackle an idea I’ve been trying to do for years. In my 2018 and 2019 case studies, I talked about an experience that feels like you’re traveling through physical space. I imagined a mix between walking forward in a first person video game and the layered, parallax effect Disney innovated (as seen in the intro of Beauty and Beast). And as with years past, I wanted you to experience this by resizing the browser window.
Spoiler: I finally made it work. Here’s a screen recording from Eric Van Holtz that shows the final resizing experience (plus a few extra interactions):

“Wonderful Weirdos of the Web” are welcomed to resize their browsers in another epic @lynnandtonic portfolio redesign.
How it works
This effect is ultimately created by “stacking” a bunch of SVGs on top of each other and then scaling and showing/hiding them in sequence as you resize the browser.
I always hoped I could achieve this experience with CSS alone, but every time I attempted it I ran into big performance issues. To make the artwork scale inversely to the browser width, I could set something like: height: calc(1200px - 100vw)
.
This works mostly fine on its own, but as the artwork became more layered and complicated, the more the effect broke down. As you resized, the browser would stop rendering the changes. Then once you stopped resizing, it would “catch up” and render the final state. Because the experience relies on things changing as smoothly and as real-time as possible, this just wasn’t going to work.
This was even before I started experimenting with blur effects. To make the effect feel more real, I wanted objects to blur and focus as they moved closer/farther away. I could do this with something like filter: blur(1vw)
but this made browsers actually crash once I started resizing. Whoopsies.
I tried a few other things like using clamp
(which worked ok for scaling but didn’t provide the level of control I needed) and setting tons of media queries in sequence (which has worked in the past for me, but would become very unwieldy with this one).
So I was about ready to throw in the towel until Scott Kellum tweeted at me and asked if something like Typetura could help. And it was like the clouds parting and I could finally feel the warm sun on my weary face. 🙏
Typetura uses a bit of JavaScript and makes flexible, responsive web typography a breeze (and as it turns out, zooming animations too). It was an especially nice solution here because I was still able to do all of my work in the CSS.
It allows you to set a max-width
plus CSS animation keyframes
and will interpolate between them as you resize the browser. So for the scaling of a layer of artwork, I could do something like this:
svg {
--tt-max: 3000;
--tt-key: scene0;
--tt-ease: cubic-bezier(.17,.67,.24,.97);
height: 300vh;
}
@keyframes scene0 {
0% {
height: 2400vh;
}
43%, 100% {
height: 5vh;
}
}
I’m setting three Typetura custom properties for the svg
:
--tt-max
sets amax-width
of 3000px here (which serves as the endpoint for the animation)--tt-key
associates the selector with the keyframe animation--tt-ease
sets theanimation-timing-function
The animation for scene0
sets two height values at different keyframes for Typetura to animate between. At 0% (0px wide), the artwork will be 2400vh tall and as you scale the browser window wider, the artwork will shrink to 5vh tall around 1290px browser width (43% of the 3000px max-width).
You might be wondering why I didn’t set --tt-max
to 1290px then? I started things that way (with each scene having its own max-width), but soon found it difficult to quickly grok when things were happening. 50% meant something different for each scene. It was much easier to wrap my head around the entire sequence as one long timeline where any keyframe could be compared to another.
So I set the --tt-max
just once (I dropped it under the body class .home
in case I wanted to change it for different pages).
.home {
--tt-max: 3000;
}
This allowed me to easily “place” and stagger transitions along the main timeline spread across different elements. Here’s a simplified example of the door in the desert scene and the timing for when the scene appears and when the window pane becomes opaque (two separate layers):
@keyframes window {
0%, 37.5% {
opacity: 0;
}
39.5% {
opacity: 1;
}
}
@keyframes scene2 {
0%, 37% {
opacity: 0;
}
37.6% {
opacity: 1;
}
}
And here’s a visualization of what the staggered animations look like along the global timeline:
So from here, any animatable CSS property became fair game, so I was able to use opacity
to fade things, transform
to move things (like opening and closing the elevator doors), and filter
to create blur effects like I mentioned earlier:
@keyframes scene1 {
20% {
filter: blur(0);
}
25% {
filter: blur(22em);
}
28% {
filter: blur(0);
}
}
Or especially fun was one transition that animates between color and grayscale:
@keyframes scene3 {
45.6% {
filter: grayscale(0);
}
50%, 100% {
filter: grayscale(100%);
}
}
Here’s what that looks like in action:
Typetura made the calculations and animations so easy to manage that I was able to spend most of my time illustrating and working out the small details with each scene transition. (Also big hat tip to Lea Verou for her cubic bezier tool which I used a bunch.)
Creating the artwork sequence
So I’ve jumped ahead a wee bit. Once I figured out I could do this, I actually spent a lot of time figuring out what this even was. I had about 2500px of browser width to account for and I wasn’t sure how many different scenes and transitions I would need.
My initial thought was to have three scenes starting with a super wide view of an outdoor landscape zooming into a building far off in the distance and then into a computer screen inside that room. But with some initial tests, zooming a “far distance” felt like it took forever and not enough was changing to make it interesting.
So I shifted to one-point perspective “rooms” you could move through quickly. The living room zooming out through a window was the first transition I figured out. You should have seen the cheering I did once it worked.
And from there I figured out the next scene and then the next and the next. I probably should have made a storyboard but I didn’t!
I did make a list of ideas and inspiration which included surrealism, Centaurworld, Wes Anderson films, the Marvel series Loki and WandaVision, and The Scary Door (the Futurama parody of The Twilight Zone).

It was fun coming up with the different rooms to visit and various “windows” you would travel through (like a television screen or elevator). A few ideas and transitions that didn’t make it in:
- room of clouds
- TRON-like virtual room
- room where everything is upside down
- snowglobe transition
- Stargate or Dr. Strange portal transition
- “through the looking glass” transition
Maybe for another time. 😉
Challenges
As easy as Typetura made this process, I still ran into some weird behavior and gotchas.
Scenes needed to be top level, inline SVG.
If the SVGs were <img>
s, some browers would rasterize them when scaling. This helps a ton with performance, but was exactly the opposite of what I wanted to happen! So inline they went.
I also found if I wrapped the SVG with a containing <div>
(for namespacing purposes) and applied transforms to the <div>
instead, the scaling and transitions started to choke again. So that was out. I guess I could have still applied the transforms to the SVG and left the <div>
there solely for the organizational benefit but that felt weird!
If anyone was digging into my CSS for this (which you can do on GitHub), this is why I’m using svg:nth-of-type(5)
instead of class names for many of the scenes. This is not great but it allowed me to import the SVG into my index.pug
file like this:
main
include ../_assets/images/scene0-stars.svg
include ../_assets/images/scene0.svg
.airplanes
include ../_assets/images/scene0-airplane.svg
include ../_assets/images/scene0-airplane2.svg
.reflection
include ../_assets/images/scene1.svg
.window
include ../_assets/images/scene2.svg
...
This is definitely a me problem. I wanted to use the SVG directly exported from Illustrator without needing to move or edit any code or having to reorganize my Illustrator layers (yikes 😱). This way I could make changes, export, and preview really quickly which I needed to do many, many times.
Balancing visual interest and file size.
I wanted the illustrations to feel fun, whimsical, and detailed while maintaining a reasonable file size. To minimize points and reduce complexity, I tried to stick with rectangles, ellipses, efficient lines, basic strokes, and solid blocks of color everywhere possible. Brushes and textures were off limits. A good, but challenging constraint.

So many screen sizes.
When you actively encourage people to resize your website you better prepare for the weird dimensions! Throughout this process, I had multiple windows open on my laptop and monitor, making sure each scene and transition made sense for narrow, wide, short, and tall windows. The one place where things really started to break is really wide + super short: a very special aspect ratio where you can start to see the edges of the artwork. I did a wee bit of extending widths for some scenes, but for the most part let it ride.

Mobile. Confusion.
As with past years, you need a browser that can resize to experience the fun. So the site on a phone isn’t broken, but it can be confusing if you got there by word of mouth. (I am exploring a way for you to watch the sequence without resizing your browser, stay tuned.)
This version of the site also has a really high chance you land on a weird inbetween state. My friend Kate arrived at the site to see a Pop-Tart against a black background. She suggested I add a little note to encourage folks to resize their browser, which does help. But! I also don’t mind a little bit of initial confusion in this case.

A few other things I considered:
- With Typetura, I needed to be ok with the experience requiring JavaScript. I love a CSS-only effect, but this one benefited so much from the JS assist that it felt worth it. Also, if interpolated values made it into the CSS spec that would be extremely cool.
- Screenreaders felt like a bit of an unknown for me on this one. I ended up including a visually-hidden step-by-step description of the sequence.
- I also thought a lot about motion. Is this a scenario that needs a motion warning? Or should I be using a
prefers-reduced-motion
query? The animations are all triggered by resizing the browser where you control the speed and duration. I wasn’t sure and I’m still not?
Anything else?
The monitor scene at the largest width has a few fun Easter eggs. These were a last minute addition and I’m glad they made it in! It helped me learn a bit more about fine tuning exports from Illustrator and using JavaScript inside SVG.

It was fun dusting off my timeline animation skills I haven’t used much since the ol’ Flash days. I still don’t think I totally understand bezier curves, but I got much better at predicting how they would behave.
With the end of year deadline looming, the rest of the site got a new coat of paint but not much else changed structurally. I did get to try out variable fonts and do some small optimizations where I could, so that feels like a win too.
At the end of the day I’m still tickled I was able to achieve the landing page I’ve been thinking about for years. I’m again reminded of how the web is such a cool medium and I just love building for it.
As always, previous versions of the site are still viewable in the archive.
Until next year’s refresh. 👋 Thanks for reading!