JustHold MDX Content (CMS)
Reuse this skill
Copy justhold-mdx-content into your project's .agents/skills/justhold-mdx-content/ (or your Cursor skills folder). The JustHold app source stays private — these playbooks are published so you can reuse our engineering conventions.
When to Use
- Editing the admin contents UI or post actions
- Changing MDX component registry or article rendering
- Updating R2 post keys, locales, or
lib/postsmetadata loading
Instructions
- Posts are authored in
/admin/contents(Monaco + live preview). The index is a CMS workspace with Published | Drafts | Pinned tabs (?tab=published|drafts|pinned; default published). Published/Drafts keep searchable lists (category / locale-completeness via URL params; EN / 中文 chips open or create a locale). The Pinned tab manages homepage slots 1–3 (published posts only). There is no file explorer — list + editor only. Metadata lives in Supabase (posts+post_locales); MDX bodies live in Cloudflare R2. - R2 object layout (see
lib/r2.ts):posts/<slug>/<locale>/content.mdxwherelocaleiszh-hantoren. - Shared fields on
posts: slug, category, author, status, pin_order (1–3 or null for blog hero), cover_key (R2), published_at. - Locale-specific fields on
post_locales: title, excerpt, reading_time. Body is only in R2. - Cover image R2 key:
posts/<slug>/cover.png(seepostCoverKey/publicObjectUrlinlib/r2.ts). - Blog listing:
getPinnedPosts(hero slots) +getPosts(date-ordered, excludes pins). - Public pages load published posts via
lib/posts.ts+lib/mdx/render(same component registry as the admin preview). - Do not hardcode title/date/category in the MDX body — the public page renders
<ArticleHeader post={...} />from Supabase metadata. - All markdown elements are styled via
lib/mdx/components.tsx— don't add custom className overrides per-post. - Blog TOC is auto-built from
##/###viaextractToc(lib/docs/toc.ts). Headingids come fromHeadingWithIdin the shared MDX registry — keep section titles unique so slugs stay stable. - Use
<Sidenote>text</Sidenote>for tangential commentary/citations (Tufte-style margin note on wide screens, inline toggle on mobile). Do not replace sidenotes with endnotes. - Prefer English (
en) as the default content locale (matches site/and admin); addzh-hantwhen a translation is ready.