Skip to content
Completed2026//Case study

Hafiz HQ

The portfolio you are reading now — a three-surface Next.js site (Work, Services, Info) with a live particle substrate, role-filtered project grid, and MDX case studies.

  • Next.js
  • Tailwind CSS v4
  • MDX
  • TypeScript
  • Motion

Hafiz HQ homepage hero over the interactive particle substrate

Problem

A portfolio has one job: get a visitor from "who is this?" to "I want to talk to them" without making them work for it. The usual failure is a single endless scroll that mixes three unrelated errands — someone hiring a QA engineer, someone hiring a data analyst, and a small business owner pricing a website all land on the same wall of text and have to dig.

The second problem is narrower and more personal: my work splits across two identities — QA/data and web development — and a portfolio that flattens both into "full stack" says nothing.

Approach

Three surfaces, not one page. Work is the projects grid, Services is the paid-work funnel with pricing, and Info is the résumé surface — about, education, skills, experience, contact. The navbar is a three-tab pill, so the visitor picks their errand in one click instead of scrolling to find it.

Work page — role filter over the project grid

The dual identity is handled by the grid itself rather than by a persona quiz: a three-way filter (all / web dev / data & qa) with a sliding pill indicator that turns coral for web-dev and violet for data & QA — the same two accents used site-wide, so the color is doing the labeling. The filter reads ?role= from the URL, so a link can drop someone straight into the relevant half of my work.

Architecture

  • Next.js 16 App Router — Server Components by default. Client components exist only where they must: the tab bar (pathname), the grid filter (state), the typed hero, the particle canvas, and the magnetic buttons.
  • Tailwind CSS v4 with OKLCH tokens — a light-lavender base (--color-background: oklch(0.92 0.042 292)) with coral and violet brand accents plus mint/amber status colors. Every token is defined once in app/globals.css; nothing hardcodes a hex.
  • MDX content layer — each case study is one file in content/projects/. Frontmatter drives the card, the body drives the page, and mdx-components.tsx maps every element to design tokens so prose matches the site without a typography plugin.
  • Particle substrate — a canvas of ~85 drifting nodes that link when within 150px and repel from the cursor, reading its color straight from --color-violet so the effect can never drift from the palette.

Implementation

The Info surface is where the résumé actually lives, assembled from independent section components rather than one page file — about, education, skills, experience, résumé, contact.

About and education sections on the Info surface

Skills are the part most portfolios get wrong by listing every technology at equal weight. Here each skill carries an honest proficiency level — proficient, familiar, or learning — grouped into QA, Data, Web, and Docs with a counted filter row, so the claim is legible instead of inflated.

Skills matrix with proficiency levels and category filters

Experience is a timeline with expandable entries, framed as a trajectory rather than a job list.

Career timeline from IT support to QA engineer

Contact closes the page with the things a recruiter checks before writing: local time, availability, response window, and every channel in one row — no form to fill in.

Résumé card and contact channels

Challenges

The lavender base fought the accents. Coral at oklch(0.55 0.19 35) looked right and measured 4.31:1 against the background and 4.22:1 against its own tint — both under WCAG AA. Darkening it to 0.50 fixed both without changing the character of the color, and the reasoning stays as a comment next to the token so nobody "brightens it back up" later.

The particle canvas had the opposite problem: readable motion that doesn't compete with the text. It sits behind everything at low alpha, the link lines fade with distance, and the whole loop is skipped when prefers-reduced-motion: reduce is set — decoration that is allowed to disappear entirely.

Outcome

A portfolio with three clear entry points, a project grid that can be linked to by role, and case studies that ship by dropping an MDX file into a folder. Verified with Vitest unit tests and Playwright browser tests, deployed on Vercel.

Lessons Learned

Splitting one scroll into three surfaces was the change that mattered — far more than any animation. Each visitor type now has a page that is entirely theirs, and the site can grow in one direction without crowding the others.

The design-system discipline paid off in an unglamorous way: because every color is a token and every contrast decision is written down next to it, the accessibility fixes were three-line edits rather than a redesign.