Back to blog

Aug 2026 · 9 min read

The Building Blocks of This Site

In August 2026 I rebuilt my portfolio from scratch. This catalogue shows every UI element as a living example — and explains why it looks the way it does.

In August 2026 I rebuilt this portfolio from the ground up. Before that it had two sources for the same colours, a light mode that plainly did not work on the landing page, and 654 lines of canvas code for a background. The rebuild pulled all of it down to one set of tokens and a handful of building blocks.

This post is the catalogue. Each layer shows the real element on the left — not a picture of it, but the same component the rest of the site uses. Switch the theme in the header and the examples switch with it. On the right is why the element is built that way.

  • --bg
  • --surface
  • --elevated
  • --border
  • --text
  • --muted
  • --accent

src/app/globals.css

01

Colour tokens instead of colour values

Colours live in exactly one place: as CSS variables on the root selector, overridden once more for the dark theme. Tailwind reads them through rgb(var(--surface) / <alpha-value>), so opacity modifiers like bg-surface/80 keep working.

The payoff shows up in component code, which reads bg-surface text-textnot a single dark:. There used to be 54 of those prefixes, and every one of them was a place where an element could be correct in only one theme.

The values are not picked by feel. The worst contrast for body text sitting over the coloured fields in the background is 6.7:1 in dark and 7.5:1 in light — measured at the brightest point of each field, not at its average.

Eyebrow

Überschrift

Fließtext in 16 px, so gesetzt, dass eine Zeile bequem gelesen wird.

Nebentext für Datum, Ort und Quellen.

src/design-system/index.ts

02

A type scale that grows with the viewport

The old version had enlarged the Tailwind scale globally: text-base was 19px instead of 16px. On a desktop you barely noticed; on a phone every page felt inflated and headings broke in awkward places.

The standard scale is back, and only the three display sizes are fluid: clamp(2.25rem, 6vw, 4rem) for a page heading. Between a phone and a large monitor it scales continuously, with no rule per breakpoint.

The monospace face carries dates, categories and small labels. It marks what is technical and keeps it visually separate from running text.

Projekte ansehenKontakt aufnehmenBlogmin-height 44 px

src/design-system/index.ts

03

Three buttons, one focus style

A portfolio needs no more than three button variants: one for the primary action, one for anything of equal weight, one for navigation in the header.

All three carry min-height: 44px. That is the size a fingertip reliably hits — the usual cause of mis-taps on mobile is targets that are too small, not text that is too small.

A single rule defines the focus ring for everything interactive, fed from --ring. Anyone navigating by keyboard sees the same ring everywhere, and no new component can forget it.

Redis mit Hochverfügbarkeit

Automatisiertes Deployment auf Kubernetes, Failover über Sentinel.

  • Redis
  • K8s
  • Helm
GitHub

src/components/sections/projects.tsx

04

Project card

A card carries a title, two or three sentences of outcome, the stack as chips, and the links. Nothing more — someone scanning projects reads the title and the stack; the rest is supporting detail.

At first the leading tile was twice the size of the others. It looked more dynamic, but it asserted a ranking that does not exist: every project here is equally real. Now all tiles are the same size, the grid is calm, and order alone sets the emphasis.

On hover only the border colour changes. Cards that lift or grow make the text jump, and with six tiles side by side that turns restless fast.

DevOps

  • Linux
  • Docker
  • K8s
  • Ansible

src/components/sections/skills.tsx

05

Technologies grouped by job

The technologies first sat next to the projects in the same row. That read as if the two belonged together, when they answer a different question: not what did I build, but what do I work with.

Now they have a section of their own, one card per category: DevOps, databases, backend, frontend, tooling. Grouping says more than an alphabetical list, because it makes the centre of gravity visible.

Deliberately without stars or percentage bars. Ratings like that claim a precision nobody can verify; the context comes from the experience section and the projects.

  1. Juli 2025 — heute

    DevOps Engineer

  2. Sept. 2022 — Juli 2025

    Ausbildung Systemintegration

src/components/sections/experience-timeline.tsx

06

Experience as a timeline

Experience is the only place where order genuinely carries meaning, so it gets the only line on the page.

Date ranges sit in monospace above each role. They are short enough to fit on one line at any width, and the fixed character width keeps them aligned down the column.

The dots sit on the line and are hidden from screen readers: they carry no information the text does not already state.

JH
  • Start
  • Projekte
  • Kontakt

src/components/header.tsx

07

Three ways through one page

Because everything lives on one page, there are three ways in. The header leads to sections and to the CV. Below 768px the links move into the menu; the brand, theme, language and the CV button stay visible.

On the right, a column of dots marks the current section. An IntersectionObserver watches a narrow band around the middle of the viewport — without that band the marker jitters between long sections. It appears from 1024px up; below that it would only be in the way.

Every anchor target has scroll-margin-top. Without it the sticky header parks itself exactly over the heading you just jumped to.

Raster + Farbflächen

src/components/backgrounds/dot-grid.tsx

08

A background that stays out of the way

Its predecessor was a starfield: 654 lines of canvas, a draw loop on every page, noticeable on battery. A radial-gradient pattern replaced it as a dot grid — plain CSS, no draw loop.

Above it, three colour fields drift slowly across. Only transform is animated, so the compositor does the work and layout and paint stay untouched. In the dark theme they use deep rather than light hues: they tint the ground without lifting it, and the text over them stays readable.

The grid shifts slightly as you scroll, and a soft glow follows the pointer — the latter only under pointer: fine; on touch devices the listener is never registered. Under prefers-reduced-motion: reduce everything holds still.

What I took away from it

The largest effect came not from a new colour but from defining the colours once. After that every further decision was cheap: a second theme costs a few lines rather than a pass over every component.

The second largest effect was deletion. An unused 600KB package, a second hero, an entire analytics stack — 40% less code than before, and the site does more than it did.

The code is open: github.com/JahongirHabibov