Skip to content

feat(blog): add MDX-powered post system with content collections#2

Open
edwintantawi wants to merge 22 commits into
mainfrom
chore/markdown
Open

feat(blog): add MDX-powered post system with content collections#2
edwintantawi wants to merge 22 commits into
mainfrom
chore/markdown

Conversation

@edwintantawi

@edwintantawi edwintantawi commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a content-collections + MDX pipeline for blog posts, streamed via RSC so compiled MDX never ships to the client bundle
  • New /posts and /posts/$slug routes backed by server functions in modules/post
  • Generalize i18n pathname/prerender helpers to accept a dynamic route list, so /posts prerenders per locale alongside /
  • Preload Geist fonts from a dedicated stylesheet to avoid render-blocking loads
  • Enable CSRF protection for server function requests

Test plan

  • vp check (format, lint, type check)
  • vp test
  • vp dev — verify /, /posts, and /posts/markdown-style-guide render correctly in both locales
  • Confirm compiled MDX is not present in the client bundle output

Split font imports out of app.css into their own stylesheet and preload
them from the root route, avoiding render-blocking font loads.
Add createCsrfMiddleware to the start instance so server function
requests are validated, independent of any specific route.
Introduce a content-collections pipeline that parses MDX posts under
src/content/posts, compiles them via @mdx-js/rollup, and streams the
rendered output through React Server Components so the compiled MDX
never ships to the client bundle.

- content-collections.ts defines the `posts` collection, validated
  against post.schema.ts and enriched with git-derived last-modified
  timestamps (modules/markdown/utils.ts).
- modules/post exposes server functions to list and load posts, used
  by the new /posts and /posts/$slug routes.
- vite.config.ts wires up the MDX plugin, content-collections vite
  plugin, and RSC support (needed to stream MDX without eval on
  Workers), and enables typography via @tailwindcss/typography.
- Generalize the i18n pathname/prerender helpers
  (lib/i18n/utils.ts) to accept an arbitrary route list instead of a
  fixed config object, so the new /posts routes can be prerendered
  per locale alongside /.
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🚀 Preview Deployment (2)

Your changes have been deployed to a preview environment:

Name Preview URL Commit Updated (UTC)
devsantara-website-preview-2 Visit Preview 8919b2a Mon, 13 Jul 2026 04:47:17 GMT

🏗️ This comment updates automatically with each push.

…osts

Introduce a series content type on top of the existing MDX pipeline:

- Add `series` and `seriesPost` content collections with frontmatter
  validation, numeric order prefixes, and integrity checks that reject
  duplicate orders and posts orphaned by a missing `_index.mdx`.
- Add the series module (server functions, schema, types) exposing list,
  detail, and post loaders backed by RSC-streamed MDX.
- Add /series, /series/$slug, and /series/$slug/$postSlug routes plus a
  home-page nav link and the /series prerender path.
- Move markdown-style-guide.mdx into a markdown series and add sample
  frontend-development series content.
Pulls in the packages behind the new MDX pipeline: shiki and
rehype-pretty-code with @shikijs/transformers for compile-time syntax
highlighting, remark-directive with mdast-util-directive/mdx-jsx for the
:::tabs syntax, plus unist-util-visit, vfile and @types/mdast for the
custom remark plugin.
Replaces the empty component map with app-aware overrides handed to the
MDX runtime: Anchor routes internal links through TanStack Router and
opens external ones in an isolated tab; CodeBlock wraps fenced code with
a copy button that reads the block's rendered end state; and Tabs renders
the group emitted by the :::tabs directive.
Extends the Vite MDX plugin with rehype-pretty-code (dual GitHub themes
emitted as CSS variables, toggled by .dark) and the Shiki notation
transformers for diff/focus/error/warning lines and indent guides. Adds a
remark-tabs plugin that rewrites :::tabs / ::tab[Label] container
directives into <Tabs> elements, and styles all of it in a new
markdown.css imported from app.css.

Renames plugin.ts to vite-plugin.ts to sit alongside the new plugins/
directory of remark transforms.
Grows the reference post into a full tour: paragraphs and line breaks,
link variants, footnotes, and horizontal rules, plus a large code section
covering language tags, titles, line/word highlights, line numbers,
diffs, focus, errors/warnings, indent guides, and inline highlighting —
alongside :::tabs and escape examples.
Support the build-time image pipeline and thumbnail captions: image-size
for intrinsic dimensions, rehype-mdx-import-media to fingerprint local
srcs, and hast/mdast utilities to parse inline-Markdown captions. Pin
@types/hast to 3.0.5 in the workspace catalog to avoid a duplicate-copy
type mismatch between mdast-util-to-hast and hast-util-raw.
Add a thumbnail module (schema, server-side caption parser, and figure
component) and wire it into posts and series. Frontmatter gains an
optional thumbnail whose document-relative src resolves to a
fingerprinted asset at build time (resolveAsset) and whose inline-Markdown
caption is rendered to HTML on the server, so no Markdown parser reaches
the client. Series posts inherit the series thumbnail when they don't set
their own, and detail pages render it above the header.
…tions

Add a rehype-image build pass that stamps intrinsic width/height on
colocated images to eliminate layout shift, and promotes a standalone
titled image into a figure with a Markdown-rendered caption. Swap the MDX
img tag for an app-aware Image component with lazy loading, run
rehype-mdx-import-media (after rehype-image) to fingerprint local srcs,
and add the matching prose image/figure styles. Also moves the
font-ligature reset from code spans to the global root.
Rework the style guide into a complete reference covering frontmatter,
GFM prose, code tooling, tabs, and the sharp edges around each, with
example fences shown beside their rendered output. Add a series cover
thumbnail with an image-credit caption and the supporting assets.
Drop the placeholder demo content now that the Markdown series is the
canonical formatting reference for the site.
Rename utils.ts to markdown.utils.ts and vite-plugin.ts to
markdown.vite.ts to match the module-prefixed filename convention, and
update the imports in content-collections.ts and vite.config.ts.
Add a JSDoc block to MarkdownRender explaining that it renders compiled
MDXContent with the app's mdxComponents overrides applied.
The title header was a flex container, where text-overflow has no effect.
Switch it to a block box (line-height centers the single line) so a long
filename ellipsis-truncates on narrow screens instead of overflowing.
Add the @shikijs/colorized-brackets transformer to the rehype-pretty-code
pipeline so nested brackets render in level-cycling colors. Pinned to
4.3.1 to match the existing shiki/@shikijs/transformers version.

Works with the dual-theme setup without CSS changes: under defaultColor:
false the transformer overrides each bracket span's --shiki-light/
--shiki-dark vars, which markdown.css already reads.
Stamp a stable id on every heading (rehype-slug) and append a
hover-revealed "#" permalink (rehype-autolink-headings) for deep linking,
with scroll-margin-top so fragment jumps clear the top edge. Route hash
links through a plain same-page anchor in the Anchor override so
permalinks scroll in place instead of opening a new tab.

Wrap GFM tables in a horizontal scroll container floored by a min-width,
so wide tables scroll instead of crushing columns on narrow viewports,
reusing the code block's thin-scrollbar styling.
Give footnote definitions and back-reference targets the same
scroll-margin-top as headings, so following a footnote reference or its
back-arrow lands with a gap above rather than flush against the top edge.
Rewrite `> [!NOTE]`-style blockquotes into styled Callout components via a
new remarkAlert plugin. Supports the five GitHub alert types (NOTE, TIP,
IMPORTANT, WARNING, CAUTION), each with an accent left border and icon,
and documents them in the Markdown style guide.
Add extractTableOfContents, which reuses the render pipeline's rehype-slug
to build {depth, title, id} entries from a document's Markdown headings, so
#id links land on the same slugs the page renders. Wire the resulting toc
through the post, series, and series-post collections and expose it on their
document types and server functions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant