Skip to content
Stephan.

03Personal

Stephan.in

Personal Portfolio & Design System

This site. Next.js 16 and Tailwind v4, built on a design system where off-system styling is a build-visible failure rather than a slow drift.

Company
Personal Project
Role
Design and engineering
Category
Web Development
012400×1000

/images/work/stephan-portfolio/cover.webp

Full-bleed project cover. Wide editorial crop of the interface or its subject matter.

01Impact

By the numbers.

37

Static routes

5

Colours in the system

2

Typefaces

0

Dark-mode variants

02Overview

The brief.

Rebuilt from the ground up in three phases: strip the previous interface while preserving every piece of content, encode the new visual language as an enforceable design system, then compose the pages from it. Swiss editorial typography, a single accent colour, and hairline rules — the restraint is the design.

Problem

Most student portfolios look like student portfolios: many colours, many gradients, decoration standing in for hierarchy. And a design system written only as documentation drifts the moment someone adds a shadow "just for this card".

Solution

The design tokens wipe Tailwind’s defaults entirely — `--color-*: initial` and `--text-*: initial` — so `bg-red-500` and `text-4xl` do not exist. An off-system value produces no styling at all, which makes drift immediately visible instead of gradual. The rules live in a skill that loads automatically whenever the interface is edited.

03Features

What it does.

01

Hard-locked design tokens

Tailwind’s default palette and type scale are wiped, so only system values resolve. Off-system styling fails visibly.

  • Tailwind CSS v4
  • @theme
02

Design system as a loadable skill

Rules, token tables, and component contracts encoded so they enter context automatically on any interface work.

  • Markdown
  • Progressive disclosure
03

Media manifest

Every image slot is declared data. A build-time filesystem check renders a self-documenting placeholder until the real file exists, then swaps automatically.

  • Node fs
  • next/image
04

Motion primitives

Reveal, stagger, and word-by-word heading animation, each returning visible content under reduced-motion rather than a stalled hidden state.

  • Motion
  • IntersectionObserver
05

Smooth scroll

Lenis for eased scrolling, unmounted entirely under reduced-motion so native behaviour is restored.

  • Lenis
  • React
06

Server-first rendering

Client boundaries kept at the leaves — only interactive primitives ship JavaScript; pages stay server components.

  • React 19
  • Next.js App Router

04Architecture

How it's built.

framework

  • Next.js 16
  • React 19
  • App Router
  • Turbopack

styling

  • Tailwind CSS v4
  • CSS-first @theme
  • Custom utilities

motion

  • Motion
  • Lenis
  • Reduced-motion fallbacks

typography

  • Geist
  • Geist Mono
  • Fluid clamp() scale

05Challenges

What was hard.

01

Design systems drift the moment they are only documentation

Wiped Tailwind’s defaults in the theme layer so off-system values resolve to nothing — a mistake you can see rather than one that accumulates.

02

Scroll animation that hides content when animation is disabled

Every motion primitive returns the visible state under reduced-motion, never a hidden initial state that then fails to animate in.

03

Filesystem checks are server-only, but filtering needs client state

Split the media component into a presentational frame and a server wrapper, with the server page resolving asset availability and passing it down.

06Gallery

A closer look.

021600×1200

/images/work/stephan-portfolio/01.webp

Interface detail. Crop tight on one screen or one interaction — not a full-page screenshot shrunk down.

031600×1200

/images/work/stephan-portfolio/02.webp

Interface detail. Crop tight on one screen or one interaction — not a full-page screenshot shrunk down.

041600×1200

/images/work/stephan-portfolio/03.webp

Interface detail. Crop tight on one screen or one interaction — not a full-page screenshot shrunk down.

051600×1200

/images/work/stephan-portfolio/04.webp

Interface detail. Crop tight on one screen or one interaction — not a full-page screenshot shrunk down.

07Stack

Built with.

  • Next.js 16
  • React 19
  • Tailwind CSS v4
  • Motion
  • Lenis
  • Geist
  • Vercel

Outcomes

  • 37 statically generated routes
  • Five colours and two typefaces across the entire site
  • Off-system styling made a build-visible failure
  • Reduced-motion honoured by every animation