← All posts
4 min readUpdated May 30, 2026

Every component this blog supports - a single post that exercises the whole MDX toolkit, from callouts and code to terminals, tabs, and tables.

This post is the living style guide for the blog. Every block, primitive, and pattern lives here so the page becomes a single, scrollable proof that the system actually works.

Prose primitives

Paragraphs read the way you'd expect - generous leading, balanced measure, honey-yellow accents reserved for the moments that earn them. Bold weight is restrained to real emphasis. Inline code looks like const x = 42, links like the Zod docs carry the accent. Use highlight sparingly when an idea genuinely needs to be picked out of the line. Keyboard shortcuts render via ⌘K.

Strikethrough comes from GFM: old idea → new idea. Footnotes work too1 - the back-link returns the reader to where they left off.

Lists

Bulleted lists are clean:

  • Type-safety from the build, not the runtime
  • Performance that maps to perception
  • Accessibility designed in, not retro-fitted

Ordered lists track the steps:

  1. Read the patterns and the anti-patterns
  2. Compare against current best practice
  3. Place the piece that fits - nothing more

Task lists work too (GFM):

  • Schema-validated frontmatter
  • Shiki syntax highlighting
  • Cross-post canonical handling
  • Multi-author support (not planned)
  • Comments (deliberately skipped)

Callouts

Six tones, picked to match how the writing actually feels.

Asides and quotes

The two editorial blocks have distinct jobs. <Aside> is the author's margin note. <Quote> is somebody else speaking.

We can build big things out of simple parts. Or we can build small things out of complicated parts. The first approach scales.

-Rich Hickey· Clojure creator

Code blocks

The fenced block is the workhorse. Shiki renders every fence at build time in the brand theme, with copy-to-clipboard built in. No language is wrong - unknown ones fall back to TypeScript.

ts
import { z } from "zod";

export const blogPostFrontmatter = z.object({
  title: z.string().min(3).max(120),
  slug: z.string().regex(/^[a-z0-9-]+$/, "kebab-case"),
  publishedAt: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  tags: z.array(z.string()).min(1).max(6),
  externalCanonical: z.string().url().optional(),
});

export type BlogPostFrontmatter = z.infer<typeof blogPostFrontmatter>;
tsx
export function Hello({ name }: { name: string }) {
  return <p className="text-accent">Hello, {name}</p>;
}
bash
bun add shiki remark-gfm
bun run build
sql
SELECT slug, title, published_at
  FROM posts
 WHERE published_at <= CURRENT_DATE
   AND draft = false
 ORDER BY published_at DESC
 LIMIT 10;

Statistics

Three quiet hairline-divided numbers carry weight when surrounded by space.

1.2s

LCP, p75

Real-user, last 30d

42ms

INP, p75

Interaction-to-next-paint

0.02

CLS, p75

Cumulative layout shift

Terminal sessions

Compose with <Line> children - prompt="$" marks a command, dim mutes echoed output.

bash · zain-portfolio-website
$bun add shiki remark-gfmSaved lockfileinstalled shiki@4.1.0installed remark-gfm@4.0.1$bun run buildNext.js 16.2.6 (Turbopack)Compiled successfully in 3.8s27 routes prerendered.

File trees

Nest <Folder> and <File> to any depth.

tree

Tabs

Pipe-delimited labels match panel children in order.

bash
bun add zod
bash
pnpm add zod
bash
npm install zod

Comparison blocks

Side-by-side before/after - naive vs optimised, untyped vs typed. The "after" column gets a subtle accent so the win is visually obvious.

Before - drift waiting

After - schema is the contract

Spec sheet

A labeled key-value list, composed of <Row> children.

Build & deploy

Framework
Next.js 16.2 + React 19.2
Runtime
Node 20 (Vercel)
Package mgr
Bun 1.3
Styling
Tailwind v4 (CSS-first config)
Highlighter
Shiki - build-time, zero runtime JS
Validation
Zod 4 (frontmatter + runtime)

Tables

Standard GFM markdown tables get the brand styling for free:

BlockPurposeStatus
<Lead>Editorial stand-first✓
<Callout>6 tones: note / tip / info / warn / etc.✓
<Aside>Author margin note✓
<Quote>Attributed pull-quote✓
<Steps> <Step>Numbered procedural sequence✓
<Terminal> <Line>CLI session✓
<FileTree> <Folder> <File>Directory tree✓
<Tabs>Compare-in-place tabbed content✓
<Spec> <Row>Key-value labeled list✓
<Compare>Before/after two-column✓
<Stat>Big number + label✓
<BlogImage>Captioned next/image wrapper✓
<Video>YouTube / Loom / Vimeo / file✓
<Bookmark>External link preview card✓
<GitHubRepo>Static GitHub repo card✓
<Mark> <Kbd>Inline highlight + keyboard shortcut✓
Tables / footnotes / task listsStandard GFM✓

Steps

For procedural content where order is the structure. Numerals auto-increment.

  1. Install the package

    Run bun add shiki once. The lockfile pins the version so every build gets the same highlighter.

  2. Create a highlighter singleton

    Wrap createHighlighter in React.cache() so the same instance is reused across server renders.

  3. Render via MDX pre override

    The MDX components map gets a custom pre that pipes fenced code through the highlighter and into your themed <CodeBlock> shell.

Two flavours, depending on what the reader needs to know about the link.

zod.dev

Zod - the docs

The reference for the schema-as-validator-as-type pattern.

zainmustafaizain-portfolio-website

The site you are reading right now - Next.js 16, Bun, Tailwind v4, GSAP, MDX. Designed and engineered by hand.

TypeScript

Media

Images come with captions and reserved aspect for zero CLS.

Zain Ul Abidin's workspace
A square photograph rendered through the brand chrome.

Video embeds normalise provider URLs and lazy-load the iframe.

A placeholder - replace with a real demo video

Blockquotes

Standard markdown blockquotes still work - used for short, in-line passages where <Quote> would be too heavy:

A schema is the contract, not a comment. Write the contract once; let the compiler enforce it everywhere.

Multiple paragraphs in a blockquote stay grouped:

First, build the abstraction.

Then, see if it earns its keep against three concrete callsites. If it doesn't, delete it.

Horizontal rule

Three dashes become a hairline divider:


Footnotes

Drop a [^id] next to the claim that needs a citation. The numbered footer section is auto-built by remark-gfm2.

Closing

Every block above is a real component, not a mockup. If a future post needs something this list doesn't cover, the answer is to add one more entry here

  • and the new block joins the system for good.

Footnotes

  1. This is the first footnote - a simple paragraph reference. ↩

  2. Footnotes can include formatting, inline code, and even links - for instance the Zod docs. Multi-paragraph footnotes also work, though they're rarely worth the indentation. ↩