| name | write_blog_post |
| description | Specialized skill to create, structure, and format new blog posts or modify existing posts on blog.shreyaspatil.dev. |
📝 Skill: Write Blog Post
This skill guides AI agents and human authors in creating high-quality, perfectly formatted blog posts for blog.shreyaspatil.dev. It details every supported schema field, custom markdown style, layout rule, and interactive element available in this codebase, drawing directly from live blog configurations and existing posts.
1. File Placement & Naming Conventions
- Markdown Posts: All blog posts live as Markdown files (
.md or .mdx) in src/data/blog/.
- Ignored Files: Files prefixed with an underscore (e.g.,
_draft-post.md) are ignored by the loader.
- Filenames (Slugs): Name files using kebab-case matching the desired URL slug (e.g.,
a-simple-key-to-a-better-lazylist-in-jetpack-compose.md).
- Media Assets: Place post-specific content images under
src/assets/images/content/<post-slug>/ to keep assets organized. Cover images should go directly under src/assets/images/.
2. Frontmatter Schema (YAML)
Every blog post must begin with standard YAML frontmatter bounded by ---. Below is the complete template representing all supported fields as defined by the Zod schema in src/content.config.ts:
---
title: "Solving the Mystery of Recompositions in Compose's LazyList"
pubDatetime: 2026-06-28T16:25:00.000Z
modDatetime: 2026-06-29T10:00:00.000Z
description: "A compelling 1-2 sentence description for SEO and feed preview."
author: "Shreyas Patil"
tags:
- Android
- Jetpack Compose
featured: false
draft: false
coverImage: "../../assets/images/cover-my-lazy-list-post.png"
ogImage: "../../assets/images/cover-my-lazy-list-post.png"
canonicalURL: "https://yourcustomdomain.com/canonical-slug"
editPost:
disabled: false
text: "Suggest Edits"
appendFilePath: true
---
⚠️ Schema Strictness & Constraints:
- Do NOT use
subtitle: Subtitles are not supported by the Astro collection schema or the rendering layouts (PostDetails.astro).
coverImage and ogImage Paths: Must be valid relative paths from the markdown file to the asset directory (e.g., starting with ../../assets/images/).
ogImage Pixel Limits: If specified, the image must be at least 1200 × 630 pixels. If omitted, a dynamic Open Graph image is automatically generated at build time using Satori.
3. Supported Post Styles & Elements Guide
The blog features a highly customized typography system (src/styles/typography.css) and global utilities (src/styles/global.css) designed to render long-form technical content beautifully.
A. Headings
Use standard markdown headings. They are automatically styled with the premium sans-serif heading font Inter for maximum clarity and structure:
## This is an H2 (Section Title)
### This is an H3 (Subsection Title)
#### This is an H4 (Sub-subsection / Italicized Title)
B. Typography & Inline Text Elements
C. Lists
- Unordered Lists: Use
- or *. Rendered with a warm accent-colored bullet marker:
- Item A
- Item B
- Ordered Lists: Use
1., 2.. Standard warm numbering applies.
D. Table of Contents (TOC)
The blog features an automatic TOC generator using remarkToc and remarkCollapse configured at build time.
- To include a Table of Contents, simply add an
H2 heading titled precisely Table of contents or Table of Contents:
## Table of contents
- At build time, the system automatically inserts a nested list of headings found in the post and wraps it inside an interactive, collapsible
<details> accordion block!
E. Blockquotes
Standard markdown blockquotes are rendered as premium-quality quote containers with a warm vertical accent border (border-s-accent), light accent-tinted background (bg-accent/5), and elegant italicized typography:
> "A Composable function should be fast, idempotent, and free of side effects."
F. Tables
Tables are fully customized with subtle borders, background headers, and alternating hover-row highlights. Inline code inside tables will wrap appropriately:
| State Strategy | Recompositions | Memory Overhead |
| :--- | :---: | :--- |
| **Direct State** | High | Minimal |
| **Lambda-based** | Low (Optimized) | Slightly Higher |
G. Collapsible Accordions (Details Toggle)
For advanced technical notes or deep-dive details, use native HTML <details> and <summary> tags:
<details>
<summary>Click to view advanced setup details</summary>
This content stays completely collapsed and hidden until clicked by the user!
</details>
H. Native Theme-Aware Callouts
For highly prominent notices, "Pro Tips", "Notes", "Key Takeaways", or "Warnings", use the custom .post-callout HTML structure. It adapts its borders, shadows, and backgrounds perfectly for both light and dark mode:
<div class="post-callout">
<div class="emoji">💡</div>
<div class="content">
<strong>Pro Tip:</strong> Always use a unique <code>key</code> parameter for your LazyList items to prevent unnecessary layout passes. Refer to our [Compose Guide](https://blog.shreyaspatil.dev) for more details!
</div>
</div>
4. Code Blocks & Highlighting (Shiki Integration)
The blog features an advanced code highlighting system powered by Shiki (astro.config.ts), supporting theme-specific background rendering (dracula for dark mode, github-light for light mode).
A. Basic Fenced Blocks
Always annotate fenced code blocks with the exact language (e.g., kotlin, yaml, bash, html, json, javascript):
fun main() {
println("Hello World!")
}
B. Displaying Filename Labels
You can render a gorgeous, styled filename badge above your code block by adding the file="filename.ext" meta attribute next to the language name:
```kotlin file="MainActivity.kt"
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
```
This renders a neat tab showing the filename with an optional decorative green dot indicator (customizable via src/utils/transformers/fileName.js).
C. Advanced Shiki Line Notations
You can use special comment tokens inside your code block lines to apply dynamic, rich styling at build time:
- Line Additions (Diff Green): Append
// [!code ++] to the end of a line.
- Line Deletions (Diff Red): Append
// [!code --] to the end of a line.
- Line Focus/Highlight (Yellow Accent): Append
// [!code highlight] or // [!code focus] to the end of a line.
- Word Highlight: Append
// [!code word:yourTargetWord] to the end of a line.
Example Code Block with Diffs:
```kotlin file="StateComparison.kt"
fun updateState() {
state.value = "Old Value" // [!code --]
state.value = "New Optimized Value" // [!code ++]
recomposeUI() // [!code highlight]
}
```
5. Rich Tech Diagrams (Mermaid.js Flowcharts)
The blog has native, built-in support for Mermaid.js diagrams.
6. Media (Images, Videos & Animated GIFs)
A. Standard Images
Insert standard images using relative paths. They are styled automatically with the img-shimmer loading animation utility, subtle rounded corners, borders, and a light shadow:

- Lightbox Integration: The blog integrates a client-side pinch-to-zoom image lightbox. Any standard image is clickable and zoomable automatically.
B. Interactive Video Players
To embed rich high-performance app demos or screen records, use native HTML <video> elements. To avoid massive bundle sizes and stay under Cloudflare Pages' 25MB assets constraints, host larger .webm or .mp4 video files on GitHub or an external CDN and reference them:
<video controls width="100%">
<source src="https://github.com/PatilShreyas/appfunctions-notyagent-app/raw/refs/heads/main/demo.webm" type="video/webm">
Your browser does not support the video tag.
</video>
C. Animated GIFs (Automatic Video Conversion)
Because GIFs are notorious for lagging mobile browsers and inflating page size (often up to 10-20MB for a few seconds), the blog utilizes a custom remarkGifToVideo plugin.
- Place your
.gif under src/assets/images/content/.
- Reference the GIF using standard markdown image syntax inside your post:

- Add the optimized
.webm counterpart under public/videos/content/<post-slug>/ using the same image filename base.
- Run the maintainer's optimization script:
npm run optimize-gifs
- At build time, the
remarkGifToVideo plugin automatically detects the GIF, checks for the presence of the optimized WebM video on disk, and rewrites the AST node to render a responsive, loop-autoplay, hardware-accelerated, silent HTML5 <video class="gif-video"> element.
D. Responsive YouTube Embeds
To embed YouTube recordings or speaker talks, wrap an <iframe> inside a <div class="video-container"> container. This enforces a perfect 16:9 responsive aspect ratio across all devices:
<div class="video-container">
<iframe
src="https://www.youtube.com/embed/t4xYihWEcaA"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen>
</iframe>
</div>
7. Quality Verification & Formatting Workflow
Before finalizing or committing a new blog post, always run the quality verification scripts:
npm run format
npm run format:check
npm run lint
npm run build
npm run preview
(Note: During offline/sandboxed development, running npm run build may encounter a Google Fonts connection timeout during Satori OG generation. This is a sandbox network restriction and not a code issue.)