Building Mr. Watson: A Botanical Restaurant Website
Mr. Watson is a vegan wood-fired restaurant in Amsterdam with a unique philosophy: "Where perspective changes perception." Named after Sherlock Holmes' legendary sidekick who always saw things from a different angle, the restaurant needed a website that captured this spirit while showcasing their botanical cuisine.
The Design System
The brand revolves around three core colors that evoke a warm, earthy, botanical feeling:
These colors work together to create a feeling of nature, warmth, and sophistication — perfect for a plant-based restaurant that takes its craft seriously.
Tech Stack
- Next.js 14 with App Router for the framework
- Tailwind CSS for styling with a custom color palette
- Framer Motion for scroll-driven animations
- Static export for fast, traditional hosting
The decision to use static export was deliberate — a restaurant website doesn't need server-side rendering for every request. Static files are faster, cheaper to host, and more reliable.
The Parallax Ribbons
The most eye-catching feature is the parallax ribbon section. Three angled ribbons scroll at different speeds as you move down the page, each displaying rotating text with the restaurant's key messages.
const { scrollYProgress } = useScroll({
target: ref,
offset: ['start end', 'end start'],
});
const x = useTransform(
scrollYProgress,
[0, 1],
direction === 'left'
? ['5%', '-20%']
: ['-20%', '5%']
);The trick is using useScroll from Framer Motion to track the element's position in the viewport, then useTransform to map that progress to horizontal movement. Each ribbon gets a different speed multiplier and direction for visual depth.
The ribbons are rotated with CSS transforms and extended beyond the viewport width to prevent any gaps showing at the edges:
style={{
transform: `rotate(${angle}deg)`,
marginLeft: '-60%',
marginRight: '-60%',
width: '220%',
}}Performance Considerations
Parallax effects can be performance killers if not done carefully. A few optimizations made a big difference:
- transform-gpu class forces GPU acceleration
- willChange: 'transform' hints to the browser about upcoming animations
- backfaceVisibility: 'hidden' prevents rendering artifacts
- Using transform instead of position properties for movement
Reservation Integration
The site integrates with Tebi, a restaurant reservation system. Rather than building a custom booking system, embedding their widget provides a battle-tested solution that the restaurant staff already knows how to manage.
Deployment
A simple deployment script builds the static export locally and rsyncs it to the server:
npm run build # Generates static files in /out rsync -avz --delete out/ server:/var/www/mrwatson/
No Node.js process needed on the server — just nginx serving static files. Fast, reliable, and easy to maintain.
Lessons Learned
- Static export is underrated: Not every site needs SSR. Static sites are fast and cheap.
- Framer Motion makes scroll animations easy: The useScroll/useTransform combo is powerful.
- Extend beyond viewport for rotated elements: Always account for the diagonal when rotating.
- GPU acceleration matters: A few CSS properties can make animations buttery smooth.
- Use existing services: Don't rebuild reservation systems from scratch.
Project Links
- Live site: mrwatson.nl
- Tech: Next.js 14, Tailwind CSS, Framer Motion
- Hosting: Static export on nginx
If you're ever in Amsterdam and want to experience plant-based cuisine done right, check out Mr. Watson. The website might be my work, but the food is all them.