02 / Full-Stack E-Commerce · 2024–2025

A punk shop.
A design system.
One non-negotiable aesthetic.

FieberPrints sells DIY punk shirts and linocut prints out of Berlin. The brief was to build something that felt as uncompromising as the product — dark backgrounds, zero border-radius, a magenta accent used as punctuation, not wallpaper. Full e-commerce: browsing, auth, cart, checkout, order history, admin dashboard. The visual identity and the engineering are the same project.

  • Next.js 15
  • JavaScript
  • Supabase
  • Modular CSS
  • Stripe
View live project
Fig. 01 — FieberPrints homepage · hero section with glitch typography

When the Identity Is the Brief

The hardest constraint wasn't technical. It was keeping every decision honest to the aesthetic.

FieberPrints has a specific customer: someone who buys handmade punk shirts, not someone browsing a polished lifestyle brand. Every UX decision was made with that person in mind. The dark background isn't atmosphere — it's the correct environment for photography of black and grey screen-printed garments. The zero border-radius isn't retro styling — it's a visual language that reads as hand-cut, immediate, no-nonsense.

The magenta (#fd00d4) appears on one thing: the primary call to action. Add to cart. Checkout. Confirm. It's used so sparingly that when it appears, it's impossible to miss. That's a deliberate information hierarchy decision — on a dark site with muted typography, the one thing that's always pink is the thing you're supposed to do next.

Order status badges follow the same logic: pending is orange-red (unresolved, needs attention), completed is green (done, calm), cancelled is desaturated red (final, distinct from pending). A customer reading their order history navigates by color before they read a word. That's the goal.

Product images had inconsistent backgrounds — real shirts shot against white, yellow, blue. Rather than hiding this, I locked every image to aspect-ratio: 3/4with object-fit: cover and added a 1px inset box-shadow against the dark site background. The inconsistency reads as intentional rawness rather than a production oversight.

Technical implementation follows ↓

Technical Stack

Every layer of the stack was chosen to serve the product, not the résumé.

Framework
Next.js 15
App Router for server components on gallery and product pages — only the cart, auth flows, and interactive overlays run client-side. Per-page CSS Modules with a shared globals layer keeps the bundle clean and the dark aesthetic enforced at the root.
Backend
Supabase
Authentication (email/password + session persistence), artworks table (product data, image URLs, sizes, pricing), and orders table. Row-level security ensures users can only access their own order history — no custom middleware needed.
Design System
Modular CSS + CSS Variables
Strict token architecture: --color-background, --color-surface, --color-surface-alt for three depth levels; --color-secondary reserved for primary CTAs only; --font-display (Bebas Neue) and --font-body (Space Grotesk) enforced globally. Every component imports its own .module.css. No Tailwind, no CSS-in-JS — the constraint was the point.
State
React Context + useState
Cart state lives in a context provider at layout level — nav badge, cart drawer, and checkout page share one source of truth. Auth state derives from Supabase's onAuthStateChange listener. No external store needed at this scope.
Payments
Stripe
Stripe Checkout for payment flow. Server-side session creation keeps secret keys off the client. Order confirmation writes back to Supabase via a webhook, so order history is always consistent with payment state.

Keeping the Aesthetic Honest at Scale

CSS defaults fight dark, zero-radius design. Every component needed explicit intent.

Browser defaults constantly push back against a dark, borderless aesthetic — button styles, input focus rings, link colors, background fills. The token system was the answer: surface colors and text colors are explicitly separated so no component can accidentally use a text token as a background. If something renders wrong, the token name tells you exactly why.

The ticker animation required a specific architectural fix. A single strip withtranslateX(-50%) produces a gap mid-loop whenever content width doesn't divide evenly into the viewport. Two identical strips side-by-side, each animating at translateX(-100%), solves it completely — the second strip is always immediately adjacent to the first, so no gap is geometrically possible regardless of content length.

/* Two strips. Each moves its own full width left. No gap. */
.ticker {
  display: flex;         /* strips sit side by side */
  overflow: hidden;
  width: 100%;
}

.tickerInner {
  min-width: 100%;       /* always fills viewport */
  animation: tickerScroll 22s linear infinite;
}

@keyframes tickerScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}
20+ components · full auth + cart + checkout flow · zero border-radius · single-token design system

Continue