Case study · 4 min read
Tobi Owoade Portfolio
A designer's portfolio built to keep his Figma work sharp on the web.

- My role
- Built the site from Tobi's Figma designs, including the case-study pages and the AI assistant.
- Timeline
- Sep 2024 - Oct 2026
- Stack
- Next.js 14, React, TypeScript, Tailwind CSS, Framer Motion, Groq, react-markdown, sharp
- Credit
- Designed by Tobi Owoade
A portfolio site for UX designer Tobi Owoade: a page per design case study, a looping playlist, and an Ask AI assistant that answers questions about his work. The main engineering problem was making screenshots from Figma look as crisp in the browser as they do in the design file.
- 4full design case-study pages
- 3xlossless image exports to keep detail sharp
- 1Ask AI assistant, with web lookup for his public profile
The problem
A designer's portfolio is judged by how it looks. Dropping a Figma frame onto a web page usually makes it softer: the browser rescales it, the image optimizer recompresses it, and small interface text turns to mush.
The brief was to match Tobi's Figma pixel for pixel, keep the work sharp, and give visitors a quicker way to learn about him than reading every page.
Constraints
- Match the design. Layout, spacing, type and motion had to follow the Figma file.
- Heavy, detailed images of phone interfaces where small text must stay readable.
- No CMS. Tobi wanted a fast static-feeling site, so content lives in code.
- A low-cost AI feature with predictable behaviour on a free-tier model.
Approach
A Next.js App Router site with a page for each case study, built from section components (hero, personas, flows) and shared pieces such as a project card, a zoomable image, a Figma frame and project navigation. Scroll reveals use Framer Motion with an intersection observer, and the playlist marquee is a CSS keyframe animation.
Content lives in a data file and constants. An Ask AI page sends questions to a Groq-backed API route that answers from that content.
The fix was upstream of the code: change what gets exported.
In the app
Key decisions
01Fight blur at the source
- Context
- The soft look came from two places: Figma's SVG exports (which embed a bitmap) and the optimizer recompressing them.
- What I chose
- Images are exported from Figma at 3x as lossless files, shown with the optimizer bypassed where it would recompress, and a click-to-zoom viewer lets people inspect detail. Commit history shows this as "lossless Figma images", "3x mockups" and "sharper thumbnails".
- What I passed on
- Trust the default image pipeline and a single 1x export.
- Trade-off
- Heavier pages: the public folder is about 58 MB, and the repository is around 85 MB because exports were redone several times.
02Match Figma, not the framework's defaults
- Context
- A designer will notice a few pixels of drift in a way most clients will not.
- What I chose
- Each case study is a page of small section components that follow the Figma frames, and a series of "match Figma" passes cleaned up spacing and type after review.
- What I passed on
- One generic case-study template for every project.
- Trade-off
- More components and more upkeep, because every case study is bespoke.
03Content in code, no CMS
- Context
- A portfolio changes a few times a year, not a few times a day.
- What I chose
- Projects, experience and skills live in a data file and constants, which keeps the site fast and the build simple.
- What I passed on
- A headless CMS so Tobi can edit without a developer.
- Trade-off
- Content changes need a developer. A CMS is the next step if that becomes a burden.
04An Ask AI assistant with a budget
- Context
- Visitors have questions a project page does not answer, and a free-tier model has limits.
- What I chose
- A Groq route (a gpt-oss model by default, overridable from the environment) answers from the site content, with an adaptive token budget (from about 260 up to 1,400 depending on how detailed the question is), a web lookup for his public profile, and a simple rate limiter. The system prompt asks for Markdown and forbids HTML line breaks.
- What I passed on
- No assistant, or a fixed FAQ.
- Trade-off
- The in-memory rate limiter resets whenever a serverless instance restarts, so it is a guard rail rather than a guarantee.
The token budget grows only when the question asks for detail.
Challenges
- Weight versus sharpness. Lossless 3x images are exactly what makes the site crisp and heavy. Re-exporting them several times also bloated the repository history.
- Pixel-matching the design across breakpoints took repeated passes.
- Sharing a lesson. My own portfolio showed this project with a blurry thumbnail. I re-captured it at 2x and added an automated test that fails when an image is rendered at less than 1.5x its displayed size.
Results
The site is live with four full case-study pages and an Ask AI assistant, and its thumbnails and mockups stay readable at full size. There are no traffic numbers to report.
The takeaway I use everywhere now: sharpness is decided when an image is exported, not when it is displayed.
What I would do next
- Convert the heavy exports to AVIF or WebP at 3x and compare them to the originals visually.
- Move the rate limiter to a shared store so it survives restarts.
- Trim the repository history of old image blobs.
- Add a CMS if Tobi wants to publish new case studies himself.