muscodes
← Blog
3 min read

This site morphs. Here is how.

Why my portfolio reshapes itself into a terminal, an app and a dashboard, and builds visitors' small tool ideas overnight. One content model, four renderers, and a strict set of constraints.

#portfolio#nextjs#design

A portfolio that says "I can build anything" is asking you to take its word for it. I did not want to ask. So this site does the thing instead of claiming it: it reshapes itself into the mediums I actually work in, and it builds small tools that visitors ask for, overnight.

There are four Forms. Editorial is the calm default you are reading now. Terminal turns the site into a real shell session you can type into. App becomes a native-feeling mobile app with a bottom tab bar. Dashboard becomes a data product with live counts and a sparkline. The switch is a control I call the Dial, up in the header. Try it, or press Shift+F.

One content model, four renderers#

The important part is what is not duplicated. There is exactly one copy of the content. A Form is a renderer over that content, never a fork of it. Every page is an ordered list of typed sections, and each Form is a single component that decides how to draw each section type.

export type Section =
  | { type: "hero"; data: HeroData }
  | { type: "projectList"; title: string; projects: ProjectCardData[] }
  | { type: "machineTeaser"; shippedCount: number }
  // ...one entry per section type

// Each Form is just: given the sections, draw them my way.
export function TerminalForm({ sections }: FormPageProps) {
  return sections.map((s) => <TerminalSection key={s.type} section={s} />);
}

Adding a project never touches a renderer. Adding a Form never touches content. That single rule is the whole architecture, and TypeScript enforces it.

The Morph#

Switching Forms is the one place I spent any boldness. Shared elements, like the hero name and the counters, carry a stable id across every Form, so when you switch they travel to their new position and size instead of blinking out. It is a choreographed transition, not a crossfade. On a low-end phone, or when you have asked your OS for reduced motion, it collapses to a plain 150ms fade. Motion is a feature, not a tax.

The Machine#

The other half of the site is a standing offer: describe a small tool you wish existed, and overnight it gets built and lives here permanently, at its own link. Each tool is one self-contained HTML file, served inside a locked-down iframe that cannot touch the network, storage, or the page around it.1 A human ships every one of them, which is the real moderation layer.

The constraints did the design work#

The rules I set were more useful than any moodboard: 60fps on a mid-range Android, every service on a genuinely free tier, no cookie banners, and no fake numbers anywhere. The Dashboard's metrics are all real counts of things that actually exist. There are no skill bars, because "React 90%" is a number you invented and every reader knows it.

The result is a site that argues for itself by working. That felt more honest than a list of adjectives.

Footnotes#

  1. The frame uses sandbox="allow-scripts" with no same-origin access, plus a strict Content-Security-Policy on the serving route. There is a self-test artifact that tries to escape and reports that every attempt fails.