LinkedIn (opens in new tab)
All case studies

Global Navigation System

Redesigned Vimeo's global navigation as a controlled A/B test. Collaborated cross-functionally to resolve a mobile conversion regression, delivering a net lift to the primary conversion funnel.

Company
Vimeo, 2025/2026
Role
Software Engineer III
Team
2 eng · 1 designer
Timeline
4 months
Focus
UI, UX, Navigation
Redesigned Vimeo navigation with the Features menu open, showing products grouped into columns such as Hosting and player, Creation and editing, and Platform

Problem

Vimeo needed a redesigned global navigation as part of a broader UX overhaul. Navigation sits at the top of every marketing page and has a direct relationship with conversion, so the stakes for getting it right were high, both visually and technically.

Old Vimeo navigation with the Product menu open: a plain list of links with small icons, grouped under headings like Hosting and Player, Creation and Editing, and Platform
Before
New Vimeo navigation with the Features menu open: the same groups, now with an icon tile and a one-line description for each item, plus a See all features link
After

Approach

I built the navigation as one of the first major components within Vimeo’s new marketing design system, working closely with the UX designer throughout. One intentional decision that shaped the mobile implementation: rather than placing the navigation toggle at the top of the screen, the designer anchored it at the bottom. This was a deliberate ergonomic choice that makes one-handed use on a real device significantly more comfortable.

New Vimeo mobile site with a floating bar at the bottom of the screen holding Menu, Upload, Staff Picks, Watch and a highlighted Join free button
New Vimeo mobile menu opened from the bottom bar: a dark panel listing Features, Use cases, Enterprise, Learn, Pricing and What's new, each with an icon
New Vimeo mobile navigation

A navigation that morphs

On desktop, the navigation doesn’t open and close separate dropdowns. As you move between top-level links, a single panel morphs from one submenu to the next, sliding and resizing to fit each one while the new content quickly fades in.

The trick was separating the panel’s background from its content. The background is its own absolutely positioned box, so it can be animated independently while the submenu content fades in on top of it. My first approach animated it with scale, which runs on the GPU, but scaling distorted the panel’s rounded corners. Instead, I moved the box with translate and animated its width and height directly. To keep that cheap, I promoted the background to its own compositing layer with will-change and translateZ(0). Because it’s absolutely positioned on its own layer, resizing it never pushes other content around or makes the rest of the page re-lay itself out.

To know where the background should go and how large it should be for each submenu, I rendered a copy of every submenu off screen, fully laid out, and measured them on load and again on resize. Measuring the live menu during the animation would have caused visible stutter. Below a certain breakpoint, the submenus simply span the full width, so only height needs to change. When a menu opens, its stored position and dimensions are passed to the background through CSS custom properties, and CSS transitions handle the morph. The off-screen copies are hidden from screen readers with aria-hidden and can’t receive focus, so they never get in the way of assistive technology.

Accessibility as a requirement, not a pass at the end

Navigation is how every visitor finds their way around the site, so it had to work for everyone, including people who don’t use a mouse or can’t see the screen. Accessibility was a priority from the start. I wrote interaction tests for keyboard navigation directly in the component’s Storybook stories, covering Tab and arrow-key movement through the menus, Escape to close them, and where focus lands when a menu opens and closes. For screen reader users, I used aria-expanded on the menu triggers so each menu’s open or closed state is announced, and aria-hidden on decorative elements so they don’t clutter what a screen reader reads out. The morphing animation respects prefers-reduced-motion, toning down motion for people who’ve asked their device to minimize it. I also added automated accessibility checks with axe-core in Storybook, so common issues would surface during development instead of after launch.

The bottom-anchored mobile menu needed its own care. While the panel is open, focus stays inside it, so keyboard and assistive-technology users can’t accidentally tab into the page behind it. It also closes when you tap outside it, which is what people expect from a panel like this. Because all of these tests ran alongside Chroma’s other component tests, the nav’s accessibility was protected against regressions as the design system grew.

Launching safely

We launched as a controlled A/B test, starting with roughly 10% of traffic to limit risk. A stakeholder monitoring the results flagged a significant drop in mobile traffic to Vimeo’s join page. I was brought into the cross-functional response, organizing a brainstorm sync with the UX designer, UI designer, and project manager. Rather than debating a single fix, we rapidly generated three distinct design directions to narrow faster. I implemented the winning approach, making the join button significantly more prominent on mobile, which not only recovered the lost traffic but produced a net lift to traffic on the primary conversion page.

Tracking down a performance issue

After launch, I noticed a performance issue: hovering over top-level navigation items caused a noticeable delay before dropdown menus appeared. Nobody had reported it yet, but on a component this visible, I didn’t want to wait until someone did. I used Chrome’s performance monitor to record and trace the interaction down the component tree. The culprit was a useWindowResize hook relying on a resize event handler, with many instances mounted simultaneously throughout the navigation. I replaced it with a new hook built on the browser’s native matchMedia API, hoisted the listener to a single point higher in the tree, and passed the result down via React Context. This eliminated the delay entirely.

Did you know?

You can export the Chrome Performance profile and hand it to your AI tool of choice (mine’s Claude). It’s surprisingly good at narrowing down what’s actually causing a slowdown like this.

Outcome

The navigation now sits at the top of every page on Vimeo’s marketing site, one of its highest-traffic surfaces. Starting the A/B test at roughly 10% of traffic kept the blast radius small when the mobile regression surfaced, and the fix shipped within two days. It didn’t just recover the lost mobile traffic to the join page. It finished above the original baseline, turning the regression into a measurable gain on the site’s primary conversion page. Keyboard, screen reader, and automated accessibility checks were built in from the start and covered by tests that run with the rest of the design system. Separately, the performance fix I identified and shipped replaced many per-instance resize listeners with a single shared one, removing a visible hover delay across every dropdown in the navigation.

Blog Migration
Next case study