Naandalist Content Creation
You are helping maintain naandalist.com's content library. This is a bilingual English + Indonesian portfolio site with a content-driven architecture. Every content entry must exist in both languages with consistent structure, proper frontmatter, and voice matching the author's professional identity as a frontend developer.
When to Use This Skill
Invoke /naandalist-content whenever you need to create, edit, or understand content for this site. Use it before writing any .md files.
Core Principle: Bilingual Everything
Every content entry must have exactly two files:
- •
index.md— English version - •
index.id.md— Indonesian version
Both files live in the same directory slug. Both must have identical frontmatter structure with matching lang field.
Directory structure:
src/content/[collection]/[slug]/ ├── index.md (English, lang: "en") └── index.id.md (Indonesian, lang: "id")
Examples:
src/content/posts/git-commit-convention/ ├── index.md └── index.id.md src/content/projects/mobile-app-kbbi/ ├── index.md └── index.id.md
Content Collections
This site has 4 content collections in src/content/. Each has different required frontmatter fields.
1. Posts (Blog Articles)
Directory: src/content/posts/[slug]/
URL: /posts/[slug] (EN) or /id/posts/[slug] (ID)
Required frontmatter:
--- title: String (the post title) subtitle: String (one-liner description) description: String (longer description for SEO, 150-160 chars) date: ISO 8601 date string (e.g., "2024-02-15T10:30:00Z") draft: Boolean (true hides from production build) featured: Boolean (true pins to top of /posts listing) keywords: Array of strings (SEO keywords, 3-5 items) lang: "en" (for index.md) or "id" (for index.id.md) ---
Example frontmatter:
--- title: "Git Commit Message Convention" subtitle: "Writing clear, consistent commit messages that scale" description: "Learn how to write commit messages that improve code review and project history" date: "2024-02-15T10:30:00Z" draft: false featured: true keywords: ["git", "commit", "conventions", "best practices"] lang: "en" ---
Content: MDX markdown. The site uses @astrojs/mdx so you can use JSX components inside markdown.
2. Work (Employment History)
Directory: src/content/work/[slug]/
URL: /work page shows entries (no detail pages)
Required frontmatter:
--- company: String (company name) role: String (job title) logoUrl: String (URL to company logo, or Cloudinary CDN) dateStart: ISO 8601 date string (start date) dateEnd: ISO 8601 date string or null (end date, null for current role) featured: Boolean (true shows on homepage) lang: "en" or "id" ---
Example:
--- company: "Acme Corp" role: "Senior Frontend Developer" logoUrl: "https://example.com/logo.png" dateStart: "2022-01-15T00:00:00Z" dateEnd: "2024-06-30T00:00:00Z" featured: true lang: "en" ---
Content: Brief description of role, accomplishments, skills. Keep to 2-3 sentences.
3. Projects (Portfolio)
Directory: src/content/projects/[slug]/
URL: /projects/[slug] (EN) or /id/projects/[slug] (ID)
Required frontmatter:
--- title: String (project name) description: String (1-2 sentences describing the project) date: ISO 8601 date string draft: Boolean featured: Boolean (true pins to top of /projects) liveURL: String (URL to deployed/live project) repoURL: String (GitHub or GitLab URL) imageUrl: String (screenshot or hero image URL) techStack: Array of strings (e.g., ["React", "TypeScript", "Tailwind"]) category: String (e.g., "Web App", "Mobile App", "Library") platforms: Array of strings (e.g., ["Web", "iOS", "Android"]) price: String (free, paid, open-source, etc.) keywords: Array of strings (SEO keywords) lang: "en" or "id" ---
Example:
--- title: "KBBI Mobile App" description: "A mobile app for Indonesian dictionary lookups with offline support" date: "2023-06-20T00:00:00Z" draft: false featured: true liveURL: "https://play.google.com/store/apps/details?id=com.naandalist.kbbi" repoURL: "https://github.com/naandalist/kbbi-mobile" imageUrl: "https://res.cloudinary.com/.../kbbi-thumb.webp" techStack: ["React Native", "TypeScript", "Firebase"] category: "Mobile App" platforms: ["iOS", "Android"] price: "Free" keywords: ["kbbi", "dictionary", "indonesian", "mobile"] lang: "en" ---
Content: Longer description. How it works. Key features. What you learned.
4. NPM Packages
Directory: src/content/npmjs/[slug]/
URL: /npmjs/[slug] (EN) or /id/npmjs/[slug] (ID)
Required frontmatter:
--- title: String (package name) description: String (what it does) date: ISO 8601 date string (publication date) draft: Boolean featured: Boolean (true pins to top of /npmjs) npmURL: String (https://npmjs.com/package/...) repoURL: String (GitHub URL) version: String (e.g., "1.2.3") license: String (e.g., "MIT", "Apache-2.0") keywords: Array of strings (npm keywords) lang: "en" or "id" ---
Example:
--- title: "@naandalist/ui" description: "A collection of reusable React components with TypeScript support" date: "2023-01-10T00:00:00Z" draft: false featured: false npmURL: "https://www.npmjs.com/package/@naandalist/ui" repoURL: "https://github.com/naandalist/ui" version: "2.1.5" license: "MIT" keywords: ["react", "components", "typescript", "ui", "library"] lang: "en" ---
Content: Brief overview. Features. Installation instructions. Usage examples.
Slug Naming Convention
Format: kebab-case, descriptive, URL-friendly
Examples:
- •✓
git-commit-message-convention - •✓
mobile-app-kbbi-vi - •✓
building-performant-react-apps - •✗
git_commit_convention(use hyphens, not underscores) - •✗
GitCommitConvention(lowercase only) - •✗
git-commit-message-convention-for-clean-code(too long, >50 chars preferred)
Writing Tone & Voice
English (index.md)
Tone: Professional but conversational. Developer-to-developer. Approachable.
Author identity: Listiananda Apriliawan, a frontend developer from Indonesia. Expertise in React Native (mobile), React (web), and now Astro. Opinions backed by experience.
Style guidelines:
- •Write in first person ("I", "we") for authority and relatability
- •Keep sentences short and punchy
- •Use active voice (avoid "was", "is being")
- •Provide practical, actionable insights
- •Include code examples when relevant
- •Link to related content or external resources
- •Be opinionated but respectful of alternatives
- •Avoid jargon; explain technical terms
Post structure:
- •Hook: Why this topic matters
- •Context: What problem does this solve?
- •Deep dive: Explanation with examples
- •Practical application: How to use this
- •Conclusion: Takeaway, next steps
Indonesian (index.id.md)
Language: Correct, professional Bahasa Indonesia. Not machine-translated from English.
Tone: Match English version's conversational professionalism.
Translation approach:
- •Translate meaning, not word-for-word
- •Keep technical terms in English (e.g., "React", "TypeScript") where appropriate
- •Adapt examples to Indonesian context where it makes sense
- •Preserve the author's voice
Style guidelines:
- •Use formal "Anda" (you) or casual "kamu" depending on context (usually formal for professional content)
- •Keep sentences short
- •Use standard Indonesian spelling and grammar
- •Provide context for Indonesian readers if culturally relevant
Frontmatter Field Details
Shared Fields
draft: Boolean
- •
true— Exclude from production builds. Useful for work-in-progress content. - •
false— Include in production builds (default).
featured: Boolean
- •
true— Pin to top of listing pages (/posts, /projects, /npmjs). Home page shows featured items. - •
false— Normal listing order (default).
lang: String
- •MUST be
"en"forindex.mdfiles - •MUST be
"id"forindex.id.mdfiles - •Never mixed or inconsistent
date: String
- •ISO 8601 format:
"YYYY-MM-DDTHH:MM:SSZ" - •Example:
"2024-02-15T09:30:00Z" - •Use
Zfor UTC timezone - •Even if you don't know exact time, use
T00:00:00Z
keywords: Array
- •3-5 relevant keywords for SEO
- •Lowercase, no duplicates
- •Example:
["react", "performance", "optimization"]
File Creation Workflow
Step 1: Create the slug directory
mkdir -p src/content/posts/my-new-post
Step 2: Create both language files
touch src/content/posts/my-new-post/index.md touch src/content/posts/my-new-post/index.id.md
Step 3: Write frontmatter (identical structure in both files, except lang field)
index.md:
--- title: "My Post Title" subtitle: "Short description" description: "SEO description" date: "2024-02-15T00:00:00Z" draft: false featured: false keywords: ["keyword1", "keyword2"] lang: "en" --- # English content starts here...
index.id.md:
--- title: "Judul Post Saya" subtitle: "Deskripsi singkat" description: "Deskripsi untuk SEO" date: "2024-02-15T00:00:00Z" draft: false featured: false keywords: ["keyword1", "keyword2"] lang: "id" --- # Konten bahasa Indonesia dimulai di sini...
Step 4: Write content in markdown (MDX supported)
Use standard markdown + JSX components. The site supports all MDX features.
Step 5: Test locally
bun run dev
Visit http://localhost:4321 (EN) and http://localhost:4321/id (ID) to verify.
Step 6: Build and verify
bun run build bun run verify:routes
Date Handling
ISO 8601 format required: YYYY-MM-DDTHH:MM:SSZ
How to get ISO dates:
new Date("2024-02-15").toISOString()
// Output: "2024-02-15T00:00:00.000Z"
// Remove milliseconds: "2024-02-15T00:00:00Z"
Dates in posts display as: "Feb 15, 2024" (formatted by FormattedDate.astro)
Anti-Patterns (NEVER Do These)
- •Create only one language file: Always create both
index.mdANDindex.id.md - •Use different frontmatter structure in EN vs ID: Same fields, only
langdiffers - •Use incorrect date format: Always use
YYYY-MM-DDTHH:MM:SSZ - •Leave frontmatter fields blank: Fill all required fields
- •Use camelCase or snake_case slugs: Only kebab-case
- •Translate English title literally into Indonesian: Adapt the title to read naturally in Indonesian
- •Use
draft: truefor finished content: Use only for WIP - •Forget the
langfield: ALWAYS include it, matching the file language - •Copy-paste English content for Indonesian: Write proper Indonesian, even if shorter/different
- •Use relative imports in markdown: Link using site URLs (
/posts/slug,/id/posts/slug)
Verification Checklist
Before committing content:
- •✓ Both
index.mdandindex.id.mdfiles exist in the slug directory - •✓ Frontmatter is valid YAML with all required fields
- •✓
lang: "en"in index.md,lang: "id"in index.id.md - •✓
datefield is ISO 8601 format with Z timezone - •✓ No typos in field names (common mistake:
authorsvsauthor) - •✓
keywordsis an array, not a string - •✓ English content is natural and conversational
- •✓ Indonesian content is properly written (not machine-translated)
- •✓ Slug is kebab-case
- •✓ No broken links in content
- •✓ Images/media are optimized webp if possible
- •✓ Run
bun run devand verify both EN and ID versions render - •✓ Run
bun run build && bun run verify:routes— no errors
Key Files to Reference
- •
src/content/config.ts— Collection schemas and validation (read if unsure about field types) - •
src/content/posts/git-commit-message-convention/— Example post entry (read for structure) - •
src/lib/utils.ts—formatDate()function (how dates are displayed) - •
src/i18n/utils.ts—useTranslations()helper (how content is translated)
When in doubt, look at existing entries. Consistency is key.
Example: Creating a New Blog Post
Command:
mkdir -p src/content/posts/my-new-article touch src/content/posts/my-new-article/index.md touch src/content/posts/my-new-article/index.id.md
index.md:
--- title: "Building Accessible React Components" subtitle: "A practical guide to WCAG compliance" description: "Learn how to build accessible React components that work for everyone" date: "2024-02-15T10:00:00Z" draft: false featured: false keywords: ["react", "accessibility", "wcag", "a11y"] lang: "en" --- # Building Accessible React Components Accessibility isn't a feature—it's a responsibility. In this post, I'll share practical techniques for building React components that work for everyone. ## Why Accessibility Matters ...content continues...
index.id.md:
--- title: "Membangun Komponen React yang Aksesibel" subtitle: "Panduan praktis kepatuhan WCAG" description: "Pelajari cara membangun komponen React yang aksesibel untuk semua orang" date: "2024-02-15T10:00:00Z" draft: false featured: false keywords: ["react", "accessibility", "wcag", "a11y"] lang: "id" --- # Membangun Komponen React yang Aksesibel Aksesibilitas bukan sekadar fitur—ini adalah tanggung jawab. Dalam artikel ini, saya akan berbagi teknik praktis untuk membangun komponen React yang bekerja untuk semua orang. ## Mengapa Aksesibilitas Penting ...konten lanjutan...
Then:
bun run dev # Test locally bun run build # Build for production
Done! The content will appear on /posts/building-accessible-react-components (EN) and /id/posts/building-accessible-react-components (ID).