I built The Beuter Design as a portfolio replica of the BEUTER® webstore — a fashion brand whose site I've always liked for how restrained and editorial it feels. The goal wasn't to clone pixels; it was to use a real brand system as a forcing function to ship a full ecommerce frontend with the current generation of Next.js tooling.

The stack
- Next.js 16 (App Router, Turbopack) + React 19
- TypeScript
- Tailwind CSS v4 — using
@theme inlinefor brand tokens - Zustand for client state (cart, wishlist) with localStorage persistence
- Framer Motion for transitions
- next-intl for translation-driven copy
- Storybook for component docs
- Playwright for end-to-end tests
- GitHub Actions for CI (lint · typecheck · build · Storybook · Playwright)
What I built
The store has the surfaces you'd expect from a real DTC brand:
- A fixed left-sidebar nav with a full-bleed campaign hero and reveal-on-scroll editorial sections.
- A server-rendered shop at
/shopwith a category sidebar (Collections / Tops / Bottoms / Accessories) and URL-driven filters. - SSG product detail pages with an image gallery, size picker, and add-to-cart.
- A persistent cart drawer and wishlist powered by Zustand, with localStorage persistence and an SSR-safe storage shim so hydration doesn't blow up.
- A full checkout flow with shipping form, country/state/city/postal fields, payment-method selection (online / COD / bank transfer), order notes, and a live order summary with discount-code input.




Things I actually learned
A few notes I want to remember.
Tailwind v4 + brand tokens is genuinely nice
@theme inline lets you define brand tokens in CSS and have them flow into every utility class. I stopped fighting the "where do design tokens live" question and just let the CSS layer own them. Components stayed clean — no token-prop-drilling, no theme provider boilerplate.
Zustand + SSR is a small puzzle
Persisting a Zustand store to localStorage in a Next.js app means you have to be careful: server render has no window, so the persisted state can't materialize until the client takes over. The fix was a small SSR-safe storage shim that returns no-ops on the server and reads from localStorage on the client. Once that's in place, the dev experience is great.
Checkout is where the abstractions get tested
The checkout page is the first place where the design system, the cart store, the currency formatter, the routing, and the form patterns all have to agree at once. I'd built most of those pieces in isolation, and the checkout was where the seams showed — places where the cart drawer summary and the checkout summary diverged, where currency formatting was reinvented per surface. Worth doing the checkout early, even on a portfolio piece, just to surface those decisions.
A real quality pipeline pays off on a solo project too
Storybook for components, Playwright e2e covering the critical journeys (home, shop, cart, checkout), CI running lint / typecheck / build / Storybook / Playwright on every push. Overkill for a portfolio piece? Maybe — but it forced me to keep the codebase honest, and the e2e tests have already caught two regressions I would've missed.
What I'd do differently
- Start with the design tokens, not the components. I built a handful of components first and then retro-fitted the token layer. Doing it the other way would've saved a refactor.
- Lean harder on RSCs. I defaulted to client components in a few places where I didn't need to. Worth being more deliberate about the boundary.
- Treat checkout as a first-class surface, not a final one. Building it last meant I had to retro-fit a few primitives (form inputs, summary rows) that should've been there from day one.
Try it
Code: github.com/nathannewyen/the-beuter-design-ecommerce-platform
If you spot something I could do better, I'd love to hear it.