AI Agent Guide
AI Agent Guide for Astro Modular Theme
This document contains essential information for AI agents working with this Astro-based blog theme. It combines development practices, folder-based content organization, technical implementation details, and the project’s vision for seamless Obsidian-to-web publishing.
🚨 CRITICAL: Primary Information Sources
⚠️ AI AGENTS MUST READ THIS SECTION FIRST ⚠️
The most up-to-date and accurate information is found in these blog posts:
- Getting Started Guide - Complete setup, configuration, and workflow information
- Astro Suite Vault Guide - Obsidian vault configuration and usage
These blog posts should be treated as the authoritative source of truth. Use the information in this AGENTS.md file as supplementary technical documentation, but always reference the blog posts for the most current and accurate user-facing information.
When in doubt, check the blog posts first.
🚨 CRITICAL: Most Common AI Agent Mistakes
⚠️ READ THESE FIRST - These are the #1 issues that keep coming up:
- 🚨 NEVER EDIT MARKDOWN CONTENT - NEVER edit markdown files in
src/content/without explicit user permission - 🚨 USE
idNOTslug-slugis deprecated in Astro v6. ALWAYS useentry.idinstead. See detailed solution - 🚨 SWUP BREAKS JAVASCRIPT - Interactive elements stop working after page transitions. See detailed solution
- 🚨 MATH RENDERING DUPLICATION - Math appears twice due to wrong CSS. See solution
- 🚨 PRODUCTION LOGGING - Never use raw
console.log()in production code - 🚨 IMAGE SYSTEM CONFUSION - Post cards vs post content images are separate systems
- 🚨 URL MAPPING SYSTEM CONFUSION - URL mapping is for rendering only, doesn’t affect linked mentions/graph view
- 🚨 FOLDER-BASED CONTENT ASSUMPTIONS - ALL content types support folder-based organization, not just posts
- 🚨 FOLDER-BASED POST ID DETECTION - Astro v6 folder-based posts have IDs like ‘folder-name’, NOT ‘folder-name/index’
- 🚨 NEVER DISABLE ASTRO DEV TOOLBAR - The dev toolbar must remain enabled (
devToolbar.enabled: true) - do NOT disable it to resolve module loading errors
These issues are documented in detail in the Common AI Agent Mistakes section.
🚨 CRITICAL: Use id Not slug (Astro v6)
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
The slug property is DEPRECATED in Astro v6 and MUST NOT be used.
The Breaking Change
According to the Astro v6 Upgrade Guide, the slug property on collection entries has been removed in favor of id:
- Old (WRONG):
post.slug- This is deprecated and will cause “undefined” URLs - New (CORRECT):
post.id- This is the modern API and always works correctly
Why This Matters
Previously, Astro used:
id- Based on the filename (e.g.,getting-started/index.md→"getting-started/index")slug- A URL-friendly version (e.g.,"getting-started")
Now in Astro v5/v6:
id- IS the slug (e.g.,"getting-started")slug- REMOVED/DEPRECATED (causes undefined values)
Common Mistakes
❌ WRONG - Using slug:
// DON'T DO THIS - slug is deprecated
const posts = await getCollection('posts');
posts.map(post => ({
url: `/posts/${post.slug}`, // ❌ Will be undefined!
id: post.slug // ❌ Will be undefined!
}));
✅ CORRECT - Using id:
// DO THIS - id is the modern API
const posts = await getCollection('posts');
posts.map(post => ({
url: `/posts/${post.id}`, // ✅ Works correctly
id: post.id // ✅ Works correctly
}));
Where to Check
Search your codebase for these patterns and replace slug with id:
- API endpoints:
src/pages/api/*.json.ts - Dynamic routes:
src/pages/[...slug].astro - Component props: Any component receiving collection entries
- URL generation: Anywhere constructing URLs from collection entries
Files Already Fixed
These files have been updated to use id instead of slug:
- ✅
src/pages/api/posts.json.ts - ✅
src/pages/api/pages.json.ts - ✅
src/pages/api/projects.json.ts - ✅
src/pages/api/docs.json.ts
This is CRITICAL for command palette search, navigation, and all URL generation.
🚨 CRITICAL: Astro v6 Compatibility Status
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
This theme is FULLY PREPARED for Astro v6 compatibility. All legacy v4 patterns have been removed and the theme uses modern v5/v6 APIs.
Current Status (January 2025)
- Astro Version: 5.15.1 (current stable)
- v6 Readiness: ✅ Fully prepared - no breaking changes expected
- Content Collections: ✅ Using modern v5/v6 API
- Config Location: ✅
src/content.config.ts(v6 requirement) - Legacy Patterns: ✅ All removed
- Using
idnotslug: ✅ All files updated
What Was Fixed for v6 Compatibility
- Config File Location: Moved from
src/content/config.tstosrc/content.config.ts - Collection Definitions: Removed
type: 'content'from all collections - ViewTransitions Import: Removed unused import (v6 breaking change)
- Image Field Safety: Added type checking for
nullimage fields - Legacy API Usage: Verified no deprecated APIs are used
- Slug to ID Migration: All references to
entry.slugreplaced withentry.id
Verified Clean (No Issues Found)
- ✅ No experimental flags in use
- ✅ No legacy collection patterns
- ✅ No deprecated APIs (
Astro.glob,emitESMImage, etc.) - ✅ No
handleFormsprop on ClientRouter - ✅ No legacy collection methods
- ✅ All integrations v6 compatible
- ✅ Using
idinstead of deprecatedslug
When Astro v6 Releases
- No action required - theme will work immediately
- No breaking changes expected
- All features will continue to work
- Build process will remain unchanged
This theme is future-proof and ready for Astro v6.
Table of Contents
- Project Vision & Philosophy
- Development Environment
- Content Organization
- Obsidian Integration
- Page Transitions with Swup
- Image Handling
- Build Process
- Version Management
- Configuration & Customization
- Troubleshooting
- Best Practices
- Technical Implementation Details
- Common Issues and Solutions for AI Agents
- Mathematical Notation Support
- Mermaid Diagram Support
- Interactive Force-Graph Implementation
- Command Palette Theme Switcher
- Native Obsidian Embed Support
- Comments System (Giscus Integration)
- Common AI Agent Mistakes
Project Vision & Philosophy
Core Mission
Astro Modular is an Astro blog theme template designed for Obsidian users, created by David V. Kimball. This theme turns Obsidian into a powerful CMS for instant blog publishing, bridging the gap between your Obsidian vault and a production-ready blog.
The theme is built on four core principles:
Clarity First
Content should be the star, not the design. Every element is carefully crafted to enhance readability and focus attention on what matters most - your ideas and writing.
Highly Performant
Fast loading times and smooth interactions are not optional. Every aspect of this theme has been optimized to deliver exceptional performance across all devices.
Works with Obsidian
If you use Obsidian, this theme is a natural extension of your workflow. All rich markdown and extended markdown features, including Wikilinks, callouts, and other embedded features, display seamlessly between your vault and published site.
Modular Design
Every feature can be toggled on or off through a single configuration file. Enable only what you need, keeping your site fast and focused on your specific use case.
Key Design Principles
1. Built for Obsidian Users
- Direct Obsidian integration - Write in a dedicated vault, publish to your blog
- Wikilinks support -
[[Internal Links]]and[[Link|Custom Text]]work seamlessly - Obsidian callouts - Full support for
> [!note],> [!tip],> [!warning]and more - Tag compatibility - Your Obsidian tags become blog tags automatically
- Frontmatter sync - Compatible metadata structure between Obsidian and Astro
- Folder-based organization - Keep content and assets together in dedicated folders
- Obsidian bracket syntax - Support for
[[image.jpg]]syntax in image references - Astro Suite Obsidian Vault built-in - Includes Obsidian vault configuration for streamlined publishing
2. Flexible & Customizable
- Modular design - Each feature can be enabled/disabled independently
- Multiple color options - Select from a variety of prebuilt themes (Oxygen, Minimal, Atom, Ayu, Catppuccin, Charcoal, Dracula, Everforest, Flexoki, Gruvbox, macOS, Nord, Obsidian, Rosé Pine, Sky, Solarized, and Things)
- Custom typography - Separate font configuration for headings and body text with 20+ supported fonts
- TypeScript throughout for type safety and better development experience
- Command palette - Press
Ctrl+K(or custom hotkey) for instant navigation and search - Responsive image grids - Automatic layouts for multiple consecutive images
- Dark/light themes - System preference detection with manual toggle
- SEO ready - Automatic sitemaps, RSS feeds, and Open Graph images
3. Content Management Excellence
- Markdown-first with enhanced processing and reading time estimation
- Folder-based posts - Organize content and assets in dedicated folders
- Draft support - Show drafts in development, hide in production
- Image optimization with WebP format priority and responsive layouts
- Table of contents auto-generation from headings
4. Navigation & Discovery
- Fuzzy search through all content via command palette
- Linked mentions - See which posts reference each other
- Tag filtering and next/previous navigation between posts
Target Audience
- Obsidian power users who want to publish their notes
- Content creators who prefer markdown-first workflows
- Developers who want a customizable, performant blog
- Writers who value seamless editing and publishing experience
Development Environment
Package Management
- Package Manager: This project uses
pnpminstead ofnpmfor all package and script commands - Scripts: All commands should use
pnpm run <script-name>
Development Server
- Command:
pnpm run dev - Port: 5000 (with fallback to 5001 if occupied)
- Host: localhost
- Hot Reload: Enabled with file watching
🚨 CRITICAL: Vite File System Security
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
NEVER disable vite.server.fs.strict in the Astro configuration. This is a CRITICAL security requirement.
Why This Matters
- Security:
fs.strict: falseallows access to files outside the project directory - Network Exposure: Files on your machine become accessible on your network
- Best Practice: Vite’s strict mode is the default for good security reasons
- Professional Standards: Production sites should maintain proper file system boundaries
What NOT to Do
// ❌ WRONG - Never disable Vite strict mode
vite: {
server: {
fs: {
strict: false, // NEVER DO THIS
allow: ['..'] // NEVER DO THIS
}
}
}
What to Do Instead
- Keep Vite strict mode enabled (default behavior)
- Use proper file paths within the project directory
- Handle file system errors gracefully without compromising security
- Use environment variables for development-specific configurations
This is CRITICAL for maintaining security and professional development standards.
🚨 CRITICAL: Production Logging Guidelines
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
NEVER use raw console.log() statements in production code. This project maintains clean console output for professional deployments.
Why This Matters
- Production Performance: Console logs slow down production sites
- User Experience: Console spam degrades user experience
- Professional Standards: Production sites should have clean console output
Simple Rule
- Development: Console logs are acceptable for debugging
- Production: No console output should appear in the final build
- Use the project’s logger utility (
src/utils/logger.ts) for any logging needs
🎨 CRITICAL: Color Usage Guidelines
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
This project uses a dynamic theming system where colors are defined in theme variables, not hardcoded values. This is CRITICAL for maintaining theme consistency and allowing users to switch between different color schemes.
Why This Matters
- Theme Consistency: All colors should work across all 17+ available themes
- User Experience: Users can switch themes and colors should adapt automatically
- Maintainability: Color changes only need to be made in one place (theme definitions)
- Professional Standards: Hardcoded colors break the theming system
How to Use Theme Colors
✅ CORRECT - Use theme variables from Tailwind config
// Use Tailwind classes that reference theme variables
@apply bg-primary-50 dark:bg-primary-800
@apply text-primary-900 dark:text-primary-100
@apply border-primary-200 dark:border-primary-600
@apply text-highlight-600 dark:text-highlight-400
❌ WRONG - Never use hardcoded color values
// BAD - Hardcoded colors break theming
background: white;
color: #1f2937;
border: 1px solid #e5e7eb;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
Available Theme Color Variables
Primary Colors (defined in src/themes/index.ts):
primary-50throughprimary-950- Main theme colorsprimary-100- Light backgrounds, subtle elementsprimary-200- Borders, dividers, table headersprimary-300- Muted text, secondary elementsprimary-400- Medium emphasis textprimary-500- Default text colorprimary-600- High emphasis textprimary-700- Dark backgrounds, strong elementsprimary-800- Darker backgrounds, headersprimary-900- Darkest backgrounds, high contrast text
Highlight Colors (accent colors):
highlight-50throughhighlight-950- Accent colorshighlight-400- Links, interactive elements (light mode)highlight-600- Links, interactive elements (dark mode)highlight-500- Default highlight color
Theme Color Usage Patterns
Backgrounds:
// Light backgrounds
@apply bg-primary-50 dark:bg-primary-800
@apply bg-primary-100 dark:bg-primary-700
// Dark backgrounds
@apply bg-primary-900 dark:bg-primary-50
@apply bg-primary-800 dark:bg-primary-100
Text Colors:
// High contrast text
@apply text-primary-900 dark:text-primary-50
@apply text-primary-800 dark:text-primary-100
// Medium emphasis text
@apply text-primary-600 dark:text-primary-300
@apply text-primary-500 dark:text-primary-400
// Muted text
@apply text-primary-400 dark:text-primary-500
Borders and Dividers:
// Subtle borders
@apply border-primary-200 dark:border-primary-600
@apply border-primary-300 dark:border-primary-500
// Strong borders
@apply border-primary-400 dark:border-primary-400
Interactive Elements:
// Links and buttons
@apply text-highlight-600 dark:text-highlight-400
@apply hover:text-highlight-700 dark:hover:text-highlight-300
// Focus states
@apply focus:ring-highlight-500 dark:focus:ring-highlight-400
Where to Find Theme Definitions
- Main Theme Config:
src/themes/index.ts- Contains all theme definitions - Tailwind Config:
tailwind.config.mjs- Maps theme variables to Tailwind classes - Current Theme:
src/config.ts- Shows which theme is currently active
Action Required for AI Agents
- ALWAYS use Tailwind classes that reference theme variables
- NEVER use hardcoded color values like
#ffffff,rgb(255, 255, 255), orwhite - CHECK existing code for hardcoded colors and replace them
- USE the dark mode variants (
dark:) for all color definitions - REFERENCE the theme files to understand available color scales
Example Conversion
// Before (BAD - hardcoded colors)
.table-wrapper {
background: white;
border: 1px solid #e5e7eb;
color: #1f2937;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}
.dark .table-wrapper {
background: #1e293b;
border-color: #475569;
color: #f1f5f9;
}
// After (GOOD - theme variables)
.table-wrapper {
@apply bg-white dark:bg-primary-800;
@apply border border-primary-200 dark:border-primary-600;
@apply text-primary-900 dark:text-primary-100;
@apply shadow-sm;
}
This is CRITICAL for maintaining the theming system and user experience across all themes.
Key Scripts
pnpm run dev # Start development server
pnpm run build # Build for production
pnpm run check-images # Check for missing images
pnpm run sync-images # Sync images from content to public
pnpm run process-aliases # Process content aliases
pnpm run generate-deployment-config # Generate deployment configs
Content Organization
Folder-Based Posts Structure
The theme supports both traditional single-file posts and folder-based posts for better asset organization.
Traditional Posts
src/content/posts/
├── traditional-post.md
└── another-post.md
Folder-Based Posts
src/content/posts/
├── traditional-post.md
├── folder-based-post/
│ ├── index.md # Main content file
│ ├── hero-image.jpg # Assets co-located
│ ├── attachments/ # Optional: Obsidian subfolder setting
│ │ ├── cover.png
│ │ └── diagram.svg
│ ├── gallery-1.jpg
│ ├── diagram.png
│ └── document.pdf
└── another-folder-post/
├── index.md
└── cover.jpg
URL Generation
- Folder name becomes the slug:
folder-based-post→/posts/folder-based-post/ - index.md contains the content: Main content goes in the
index.mdfile - Assets are co-located: All related files stay in the same folder
Folder-Based Content Support
All content types support folder-based organization:
Posts (src/content/posts/)
- Folder-based:
folder-based-post/index.md→/posts/folder-based-post/ - Single-file:
traditional-post.md→/posts/traditional-post/
Pages (src/content/pages/)
- Folder-based:
folder-based-page/index.md→/folder-based-page/ - Single-file:
about.md→/about/
Projects (src/content/projects/)
- Folder-based:
project-name/index.md→/projects/project-name/ - Single-file:
project.md→/projects/project/
Documentation (src/content/docs/)
- Folder-based:
guide-name/index.md→/docs/guide-name/ - Single-file:
guide.md→/docs/guide/
Key Benefits:
- Asset Co-location: Images, PDFs, and other files stay with content
- Obsidian Compatibility: Works seamlessly with Obsidian’s folder-based organization
- Clean URLs: Folder name becomes the URL slug automatically
- Flexible Organization: Mix single-file and folder-based content as needed
Technical Implementation:
- Astro Content Collections: Automatically handles folder-based content
- Asset Syncing:
scripts/sync-images.jscopies assets to public directory - URL Generation: Folder name becomes slug,
index.mdprovides content - Image Processing: Co-located images work with both
[[image.jpg]]andsyntax
Obsidian Subfolder Support
The theme supports Obsidian’s “subfolder” setting where attachments are stored in an attachments/ subfolder within folder-based content:
src/content/posts/my-post/
├── index.md
├── attachments/ # Obsidian subfolder setting
│ ├── cover.png
│ └── diagram.svg
└── other-file.jpg # Co-located assets still work
How it works:
- Images in
attachments/subfolders are automatically processed - They’re copied to the public directory without the
attachments/prefix - Both
attachments/image.pngandimage.pngreferences work seamlessly - This prevents breaking when users switch between Obsidian’s subfolder settings
Content Types
The theme supports multiple content types for different purposes:
Posts (src/content/posts/)
- Purpose: Blog posts and articles
- URL Structure:
/posts/post-slug - Features: Tags, reading time, linked mentions, comments
- Organization: Single files or folder-based with co-located assets
Pages (src/content/pages/)
- Purpose: Static pages (About, Contact, etc.)
- URL Structure:
/page-slug - Features: Simple content without blog-specific features
- Organization: Single files or folder-based with co-located assets
Projects (src/content/projects/)
- Purpose: Portfolio items, side projects, work showcases
- URL Structure:
/projects/project-slug - Features: Categories, status, repository/demo links, featured flag
- Organization: Single files or folder-based with co-located assets
- Frontmatter:
title,description,date,categories,repositoryUrl,demoUrl,status,image,imageAlt,hideCoverImage,draft,noIndex,featured
Documentation (src/content/docs/)
- Purpose: Technical documentation, guides, API references
- URL Structure:
/docs/doc-slug - Features: Categories, version control, table of contents, featured flag
- Organization: Single files or folder-based with co-located assets
- Frontmatter:
title,description,category,order,lastModified,version,image,imageAlt,hideCoverImage,hideTOC,draft,noIndex,showTOC,featured
Category Logic
The theme includes intelligent category handling that adapts based on your content:
Projects Categories
- If NO projects have categories: Hide the entire category system
- Remove category display from project cards
- Remove category display from individual project pages
- Remove the category filtering block from the projects index page
- If SOME projects have categories: Show categories normally
Documentation Categories
- If NO docs have categories: Remove category system entirely
- Remove category headings and TOC-like structure on the docs index page
- Just list docs by their
ordernumber
- If SOME docs have categories:
- Docs with categories go to their assigned category
- Docs without categories go to a new “Unsorted” category
- Keep the category structure
Individual Documentation TOC Control
hideTOCfield: Each doc can hide its table of contents withhideTOC: truein frontmatter- Separate from global setting: This is independent of the global posts TOC setting
- Default behavior: TOC shows unless explicitly hidden
Content Schema
Posts Collection
{
title: string;
description: string;
date: Date;
tags?: string[];
draft?: boolean;
image?: string;
imageOG?: boolean;
imageAlt?: string;
hideCoverImage?: boolean;
targetKeyword?: string;
// Note: author is global via siteConfig.author, not per-post
// Note: noIndex is available but typically used for pages only
}
Pages Collection
{
title: string;
description: string;
draft?: boolean;
lastModified?: Date;
image?: string;
imageAlt?: string;
hideCoverImage?: boolean;
hideTOC?: boolean;
noIndex?: boolean; // Commonly used for pages
}
Projects Collection
{
title: string;
description: string;
date: Date;
categories?: string[];
repositoryUrl?: string;
demoUrl?: string;
status?: 'in-progress' | 'completed';
image?: string;
imageAlt?: string;
hideCoverImage?: boolean;
hideTOC?: boolean;
draft?: boolean;
noIndex?: boolean;
featured?: boolean;
}
Documentation Collection
{
title: string;
description: string;
category: string;
order: number;
lastModified?: Date;
version?: string;
image?: string;
imageAlt?: string;
hideCoverImage?: boolean;
hideTOC?: boolean;
draft?: boolean;
noIndex?: boolean;
showTOC?: boolean;
featured?: boolean;
}
H1 Title Handling - CRITICAL DISTINCTION
IMPORTANT: Both Posts and Pages now handle H1 titles the same way:
Posts (PostLayout)
- H1 is HARDCODED in the layout using
{post.data.title}from frontmatter - NO H1 in markdown content - posts should NOT have
# Titlein their markdown - Layout controls styling - H1 styling is handled by the PostLayout component
- Example: Post frontmatter has
title: "My Post"→ Layout renders<h1>My Post</h1>
Pages (PageLayout)
- H1 is HARDCODED in the layout using
{page.data.title}from frontmatter - NO H1 in markdown content - pages should NOT have
# Titlein their markdown - Layout controls styling - H1 styling is handled by the PageLayout component
- Example: Page frontmatter has
title: "About"→ Layout renders<h1>About</h1>
Why This Matters
- Both Posts and Pages: Title comes from frontmatter, layout handles presentation
- Content starts with H2: Since H1 is hardcoded in the layout, all content should start with
##headings - Consistency: Both use the same approach for titles and content structure
- AI Agents: NEVER add H1 to any markdown content - both posts and pages have hardcoded H1s from frontmatter
Table of Contents (TOC) Behavior - CRITICAL DISTINCTION
IMPORTANT: Posts have different TOC behavior than other content types:
Posts (PostLayout)
- Uses global setting: Controlled by
siteConfig.postOptions.tableOfContentsinconfig.ts - Can be overridden: Use
hideTOC: truein post frontmatter to hide TOC - Global control: All posts respect the global setting unless individually overridden
- Example: If
tableOfContents: truein config, all posts show TOC unlesshideTOC: truein frontmatter - Override behavior: Posts can only opt-out when global setting is enabled, cannot opt-in when global setting is disabled
Pages, Projects, Documentation (PageLayout, ProjectLayout, DocumentationLayout)
- Independent TOC logic: NOT affected by global
postOptions.tableOfContentssetting - Default behavior: TOC shows by default (equivalent to
true) - Frontmatter control: Use
hideTOC: trueto hide TOC,showTOC: falseto hide TOC - Per-content control: Each content type has its own TOC behavior independent of posts
Why This Matters
- Posts: Global setting controls all posts, individual posts can override with
hideTOC: true - Other content types: Each has independent TOC behavior, not affected by posts setting
- AI Agents: Don’t assume all content types use the same TOC logic - posts are different
- Configuration: Only
postOptions.tableOfContentsaffects posts, not other content types
Obsidian Integration
For complete Obsidian setup and usage instructions, see the Astro Suite Vault Guide.
Astro Suite Vault Philosophy
The included Obsidian vault follows three core principles:
- Plug-and-play Astro blogging experience
- Emphasis on modularity
- Customize your experience to get it just right
Vault Setup & Configuration
Obsidian Settings for Best Compatibility
Critical Settings:
- Files & Links → New link format: Set to “Absolute path in Vault” - This ensures paths like
bases/home.baseorattachments/image.jpgwork correctly with the theme’s image processing logic - Files & Links → Default location for new attachments:
./attachments(as configured in vault) - Files & Links → Use
[[]]style links: Can be enabled if you prefer wikilinks, but standard markdown links work better for cross-content-type linking
Why “Absolute path in Vault” is Recommended:
- Matches the theme’s path processing logic which expects paths like
bases/home.base(not../bases/home.baseor justhome.base) - Prevents issues with relative path resolution (
../patterns aren’t explicitly handled) - Maintains consistent paths when moving files around in Obsidian
- Works seamlessly with the theme’s image optimization functions
Path Format Examples:
- ✅ Absolute path in Vault:
bases/home.base→ Processes correctly - ❌ Relative paths:
../bases/home.base→ May cause resolution issues - ❌ Shortest path possible:
home.base→ Loses folder context
Theme & Visual Experience
- Minimal Theme - Understated color scheme with high contrast options
- Minimal Theme Settings - Complete control over your experience
- Hider Plugin - Remove distracting UI elements
- Style Settings - Fine-tune visual appearance
- Custom CSS Snippets - Optional enhancements for window management and mobile interface
astro-modular-styling- Gives embedded HTML elements similar look to frontendcustom-draggable-top-area.css- Makes window moving easier when frame is hiddenhide-properties-heading-and-add-properties-button.css- Alternative to Style Settings pluginhide-tabs-icon-mobile.css- Removes tabs icon in mobile versionhide-longpress-flair-mobile.css- Simplifies mobile interfacehide-header-title-mobile.css- Simplifies mobile interface
Essential Hotkeys for AI Agents
When working with the Obsidian vault, these hotkeys are crucial:
- Toggle left side panel:
CTRL + ALT + Z - Toggle right side panel:
CTRL + ALT + X - Toggle tab bar:
CTRL + ALT + S - Navigate back:
ALT + ← - Navigate forward:
ALT + → - Open homepage:
CTRL + M - Add property:
CTRL + ; - Toggle reading view:
CTRL + E - Toggle Zen mode:
CTRL + SHIFT + Z - Insert image:
CTRL + ' - Insert callout:
CTRL + SHIFT + C - Rename note:
CTRL + R - Start Terminal:
CTRL + SHIFT + D - Open config file:
CTRL + SHIFT + , - Git Commit and Sync:
CTRL + SHIFT + S
Note: On Mac, CTRL = CMD
Key Plugins for Content Creation
Astro Composer
- Purpose: Easily create new notes as Astro blog posts
- Functionality:
- Creates kebab-case filenames from titles automatically
- Supports
CTRL + Rfor easy renaming - Generates default properties automatically
- Works with both wikilinks and standard markdown links
- Supports “Standardize properties” command for organizing properties
- Allows copying heading links by right-clicking headings
- Critical: Unlike other themes, this theme supports any internal link that works with Obsidian without conversion
Homepage and Default New Tab Page
- Home Base: Default screen shows a
.basefile with all blog posts in reverse-chronological order - Location: Nested in
basesfolder - Customization: Note properties in views can be customized
Content Management Plugins
- Paste Image Rename: Quickly rename images with kebab-case, SEO-friendly names
- Image Inserter: Pull images from Unsplash with
CTRL + ' - Title-Only Tab: Uses
titleproperty instead of filename for tabs - Property Over Filename: Use
titleproperty as primary identifier for linking/searching - Alias Filename History: Stores old filenames as aliases for URL redirects
- Automatically stores old filenames when renaming posts/pages
- Configurable regex for ignoring names (like
Untitledor_prefix) - Adjustable timeout and parent folder name tracking
Focus & Productivity
- ProZen: Full-screen writing mode with
CTRL + SHIFT + Z - Disable Tabs: Optional - new tabs replace current ones (great with hidden tab bar)
- Shell Commands: Quick access to terminal and config file
- BRAT (Temporary): Used temporarily to load plugins before they’re available in the official directory
Git Integration
- Git Plugin: Publish to Astro blog without leaving Obsidian
- Command:
CTRL + SHIFT + Sfor commit and sync - Configuration: Requires git setup to enable
Content Workflow
- Write in Obsidian using the configured vault
- Use wikilinks for internal connections (
[[Post Title]]or[[Post Title|Custom Text]]) - Add images with
CTRL + 'and automatic SEO-friendly naming - Create callouts with
CTRL + SHIFT + C - Publish with
CTRL + SHIFT + S(git commit and sync) - Content appears on your Astro blog automatically
Linking Behavior: Wikilinks vs Standard Links
This theme supports two distinct linking behaviors, each with specific use cases:
Wikilinks (Obsidian-style) - Posts Only
- Syntax:
[[Post Title]]or[[Post Title|Custom Text]] - Purpose: Obsidian’s special linking syntax for seamless vault-to-blog publishing
- Scope: ONLY works with posts collection
- Examples:
[[Getting Started]]→/posts/getting-started[[My Post|Custom Link Text]]→/posts/my-postwith custom display text![[image.jpg]]→ Image reference (works in any content type)
- Why posts only: Maintains simplicity and matches Obsidian’s primary use case for blog content
Standard Markdown Links - All Content Types
- Syntax:
[text](url) - Purpose: Standard markdown linking that works everywhere
- Scope: Works with ALL content types (posts, pages, projects, docs)
- Examples:
[Post Title](posts/post-slug)or[Post Title](post-slug)→ Posts[Page Title](pages/page-slug)or[Page Title](page-slug)→ Pages[Project Title](projects/project-slug)→ Projects[Doc Title](docs/doc-slug)→ Documentation[Home](special/home)or[Home](homepage)→ Special pages
When to Use Which
- Use Standard Markdown Links (
[text](url)) for linking between different content types - this is the most ideal approach if you care about cross-content-type linking - Use Wikilinks (
[[Post Title]]) only if you exclusively link between posts and want the Obsidian-native feel - they work seamlessly for posts but don’t support other content types - Best Practice: If you plan to link between posts, pages, projects, or docs, use standard markdown links for maximum flexibility
- Both work together - you can mix wikilinks and standard links in the same document
Technical Implementation
- File:
src/utils/internallinks.ts(renamed fromwikilinks.tsfor clarity) - Wikilink Processing:
remarkWikilinks()- handles[[...]]syntax (posts only) - Standard Link Processing:
remarkStandardLinks()- handles[text](url)syntax (all content types) - Combined Processing:
remarkInternalLinks()- combines both for Astro configuration
URL Mapping System for Obsidian Compatibility
The theme includes a sophisticated URL mapping system that ensures Obsidian-style relative links work seamlessly on the live website while maintaining full Obsidian compatibility.
Purpose
- Obsidian Compatibility: Links written in Obsidian work naturally without conversion
- Live Site Functionality: Relative links resolve correctly on the published website
- Seamless Publishing: Write in Obsidian, publish to blog with identical link behavior
Supported URL Mappings
Pages Collection Mapping:
[About](/pages/about)→/about(removes/pagesprefix)[About](/pages/about/)→/about(handles trailing slashes)[Contact](pages/contact)→/contact(handles both leading slash variants)
Special Pages Mapping:
[Home](/special/home)→/(homepage)[Home](/special/home/)→/(handles trailing slashes)[404 Page](/special/404)→/404(404 error page)[Projects](/special/projects)→/projects(projects index)[Docs](/special/docs)→/docs(documentation index)
Direct Index Mapping:
[Home](/index)→/(homepage)[Home](/index/)→/(handles trailing slashes)
Technical Implementation
Core Functions:
mapRelativeUrlToSiteUrl(): Main URL mapping function insrc/utils/internallinks.tsextractLinkTextFromUrlWithAnchor(): Processes URLs and extracts link textremarkStandardLinks(): Applies URL mapping during markdown processing
Processing Flow:
- Link Detection:
isInternalLink()identifies relative links - URL Mapping:
mapRelativeUrlToSiteUrl()transforms Obsidian paths to site URLs - Rendering:
remarkStandardLinks()applies mappings during HTML generation - Anchor Handling: Anchors (
#section) are preserved and properly formatted
Linked Mentions & Graph View Integration
Important Distinction: URL mapping is applied for rendering only. The linked mentions and graph view features maintain their posts-only filtering:
- URL Mapping: Applied to all content types for proper rendering
- Linked Mentions: Only includes posts (filtered by
isPostLinklogic) - Graph View: Only includes posts (filtered by
isPostLinklogic)
Why This Design:
- Rendering: All links work correctly on the live site
- Features: Linked mentions and graph view remain focused on blog content
- Performance: No unnecessary processing of non-post content for these features
Edge Cases Handled
Trailing Slashes:
/pages/about/→/about/special/home/→//index/→/
Mixed Formats:
- Both
/pages/aboutandpages/aboutwork identically - Both
/special/homeandspecial/homework identically - Obsidian’s flexible linking is fully supported
Anchor Preservation:
[About](/pages/about#section)→/about#section[Home](/special/home#top)→/#top- Anchors are properly slugified for web compatibility
Best Practices for AI Agents
URL Mapping Implementation:
- Always use
mapRelativeUrlToSiteUrl()for URL transformations - Test both leading slash variants (
/pages/aboutandpages/about) - Handle trailing slashes in all mapping logic
- Preserve anchors during URL transformation
Content Creation:
- Use Obsidian-style links - they work seamlessly on the live site
- Test URL mappings with various link formats
- Don’t manually convert links - the system handles it automatically
- Include examples in documentation for all supported formats
Performance Considerations:
- URL mapping is lightweight - minimal performance impact
- Caching friendly - mappings are applied during build time
- No runtime overhead - all processing happens during markdown compilation
Automatic Aliases & Redirects
When you rename a post or page in Obsidian, the old filename is automatically stored as an alias. Astro processes these aliases and creates redirect rules, so old URLs continue to work. You don’t need to add aliases manually - they appear automatically when you use Obsidian’s rename functionality.
Content Collections Structure
The theme uses Astro’s content collections system with predefined collections:
Standard Collections
src/content/
├── posts/ # Blog posts collection
│ ├── attachments/ # Shared post images
│ ├── getting-started.md # File-based post
│ ├── sample-folder-post/ # Folder-based post
│ │ ├── index.md # Main content file
│ │ ├── hero-image.jpg # Post-specific assets
│ │ ├── diagram.png
│ │ └── document.pdf
│ └── another-post/ # Another folder-based post
│ ├── index.md
│ └── cover.jpg
├── pages/ # Static pages collection
│ ├── attachments/ # Shared page images
│ ├── about.md
│ ├── contact.md
│ └── privacy.md
├── projects/ # Projects collection
│ ├── attachments/ # Shared project images
│ ├── project-1.md
│ └── project-2/
│ ├── index.md
│ └── screenshot.png
├── docs/ # Documentation collection
│ ├── attachments/ # Shared doc images
│ ├── guide-1.md
│ └── guide-2/
│ ├── index.md
│ └── diagram.png
└── special/ # Special pages collection
├── home.md # Homepage blurb content
├── 404.md # 404 page content
├── projects.md # Projects index page content
└── docs.md # Docs index page content
Special Collection
The special collection contains content for specific pages that have special routing logic:
Purpose: Provides content for pages that need custom behavior or fixed URLs
Files and Their Uses:
home.md- Homepage blurb content (controlled byhomeOptions.blurb.placement)404.md- 404 error page contentposts.md- Posts index page content (meta title, description, H1 heading)projects.md- Projects index page content (ifhomeOptions.projects.enabled: true)docs.md- Documentation index page content (ifhomeOptions.docs.enabled: true)
URL Mapping:
special/home.md→/(homepage blurb)special/404.md→/404(404 error page)special/posts.md→/posts(posts index page)special/projects.md→/projects(projects index)special/docs.md→/docs(documentation index)
Important Notes:
- These files have fixed URLs determined by their filename, not frontmatter
- They use a simplified schema (title, description, hideTOC only)
- They’re processed by the
[...slug].astrocatch-all route - They’re excluded from the main pages collection to avoid conflicts
Special Pages Detailed Behavior
Homepage (special/home.md):
- Purpose: Controls homepage blurb content
- Usage: Content appears when
homeOptions.blurb.placementis not “none” - Content: Markdown content below frontmatter is rendered as the blurb
- Fallback: If file doesn’t exist, no blurb is shown
- Schema:
title,description,hideTOC
404 Page (special/404.md):
- Purpose: Controls 404 error page content
- Usage: Content appears on any “not found” page
- Content: Markdown content below frontmatter is rendered
- Fallback: If file doesn’t exist, uses default 404 page
- Schema:
title,description,hideTOC
Posts Index (special/posts.md):
- Purpose: Controls posts index page meta data and heading
- Usage:
titlefield becomes H1 heading and meta title,descriptionbecomes meta description - Content: Only frontmatter is used, content below is ignored
- Fallback: If file doesn’t exist, uses “All Posts” title and dynamic description
- Schema:
title,description(no content below frontmatter) - Dynamic Behavior: When tag filtering is active, uses dynamic title/description logic
Projects Index (special/projects.md):
- Purpose: Controls projects index page content
- Usage: Content appears when
homeOptions.projects.enabled: true - Content: Markdown content below frontmatter is rendered
- Fallback: If file doesn’t exist, uses default projects page
- Schema:
title,description,hideTOC
Documentation Index (special/docs.md):
- Purpose: Controls documentation index page content
- Usage: Content appears when
homeOptions.docs.enabled: true - Content: Markdown content below frontmatter is rendered
- Fallback: If file doesn’t exist, uses default docs page
- Schema:
title,description,hideTOC
Special Pages Implementation Details
Content Loading Pattern:
// Example from posts/index.astro
let postsPageContent;
try {
postsPageContent = await getCollection('special', ({ slug }) => slug === 'posts');
} catch (error) {
postsPageContent = [];
}
const postsPageData = postsPageContent.length > 0 ? postsPageContent[0] : null;
Fallback Logic:
- Title:
postsPageData?.data.title || 'Default Title' - Description:
postsPageData?.data.description || 'Dynamic Description' - Content: Only used for pages that support content (home, 404, projects, docs)
Schema Differences:
- Posts: Only uses frontmatter (
title,description) - Others: Use frontmatter + content below frontmatter
- All: Support
hideTOCfield for table of contents control
URL Generation:
- Fixed URLs: Special pages have predetermined URLs based on filename
- No Slug Processing: URLs are not generated from frontmatter titles
- Catch-all Route: All special pages are handled by
[...slug].astro
Error Handling:
- Graceful Fallbacks: All special pages have fallback behavior if files don’t exist
- Try-Catch Blocks: Content loading is wrapped in try-catch for robustness
- Empty Collections: Handles cases where special collection doesn’t exist
Custom Collections
Recommended Approach: Use Subfolders Within Pages Collection
Instead of creating custom collections at the content level (which triggers Astro’s auto-generation warnings), use subfolders within the existing pages collection:
Example Structure:
src/content/pages/
├── about.md
├── contact.md
├── services/
│ ├── web-development.md
│ ├── consulting.md
│ └── design.md
└── products/
├── software-solutions.md
└── consulting-services.md
URL Generation:
src/content/pages/services/web-development.md→/services/web-developmentsrc/content/pages/services/consulting.md→/services/consultingsrc/content/pages/products/software-solutions.md→/products/software-solutions
Alternative: Nested Folders with index.md:
src/content/pages/services/
├── web-development/
│ └── index.md
└── consulting/
└── index.md
Same URL Results:
src/content/pages/services/web-development/index.md→/services/web-developmentsrc/content/pages/services/consulting/index.md→/services/consulting
Benefits of This Approach:
- No Astro warnings - works within existing pages collection
- No configuration needed - uses standard Astro folder-based routing
- Same URL structure - creates the same
/services/...URLs you want - Easy linking - all internal links work automatically
- Standard frontmatter - uses the same schema as regular pages
Linking to Custom Pages:
- From other pages:
[Web Development](/services/web-development) - From posts:
[Our Services](/services/consulting) - From any content: Standard markdown links work out of the box
Why This Design:
- Avoids Astro auto-generation - no deprecation warnings
- Maximum flexibility - create any URL structure you want
- Zero configuration - works out of the box
- Clean and simple - leverages Astro’s built-in folder routing
Vault Structure
src/content/
├── posts/ # Blog posts
├── pages/ # Static pages
├── projects/ # Projects
├── docs/ # Documentation
├── special/ # Special pages
└── .obsidian/ # Obsidian vault setup
├── plugins/ # Configured plugins
├── themes/ # Minimal theme
└── snippets/ # Custom CSS snippets
Page Transitions with Swup
Swup Integration
This project uses Swup for client-side page transitions to provide a smooth, app-like navigation experience.
Configuration
- Location:
astro.config.mjs- Swup is configured as an Astro integration - Accessibility: Currently disabled (
accessibility: false) to prevent invalidtabindexattributes on body elements - Containers: Uses
#swup-containeras the main content container - Smooth Scrolling: Disabled (handled by custom implementation)
- Caching: Enabled for better performance
- Preloading: Enabled for faster navigation
- Back/Forward Navigation: Completely disabled for Swup (
skipPopStateHandling: true) to let browser handle naturally
🚨 CRITICAL: Scroll Behavior Issues
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
The most common and frustrating issue with Swup is scroll behavior during back/forward navigation. This section documents the exact cause and solution.
The Problem: “Dumb Little Scroll Thing”
When users navigate back/forward using browser buttons, they experience:
- Page jumps to top, then slowly scrolls down
- Unnatural scroll behavior that feels broken
- Loss of scroll position restoration
Root Cause: handleInitialHashScroll() Function
The issue is caused by the handleInitialHashScroll() function in BaseLayout.astro being called in the visit:end hook:
// ❌ WRONG - This causes scroll issues
swup.hooks.on('visit:end', () => {
// ... other code ...
handleInitialHashScroll(); // ← THIS IS THE CULPRIT
});
What handleInitialHashScroll() does:
- Calls
window.scrollTo(0, 0)- Forces scroll to top - Performs custom smooth scroll animation
- Interferes with browser’s natural scroll restoration
The Solution: Remove from visit:end Hook
✅ CORRECT - Fixed version:
swup.hooks.on('visit:end', () => {
// Update navigation highlighting after page transition is complete
updateNavigationHighlighting();
// Initialize linked mentions after page transition is complete
if (window.initializeLinkedMentions) {
window.initializeLinkedMentions();
}
// Don't call handleInitialHashScroll() here - it interferes with back/forward navigation
});
Why This Works
- Browser handles scroll naturally - No JavaScript interference
- Back/forward navigation preserved - Browser’s scroll restoration works
- No forced scrolling - No
window.scrollTo(0, 0)calls - No custom animations - No interference with natural behavior
Additional Swup Scroll Configuration
The project also uses these Swup configurations to prevent scroll issues:
// In astro.config.mjs
swup({
smoothScrolling: false, // Disable Swup's smooth scrolling
plugins: [], // Disable all plugins including scroll
skipPopStateHandling: (event) => {
// ALWAYS skip Swup handling for back/forward navigation
return true;
}
})
🚨 CRITICAL: JavaScript Re-initialization After Page Transitions
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
The #1 most common issue with Swup is that JavaScript stops working after page transitions. This is because Swup replaces DOM content without triggering DOMContentLoaded, so event listeners and component initialization are lost.
The Problem: “It works on first load but not after navigation”
When users navigate between pages, they experience:
- Interactive elements stop responding to clicks
- Event listeners are lost after page transitions
- Components appear but don’t function
- “It works on first load but not after navigation”
Root Cause: DOM Content Replacement
Swup replaces the content inside #swup-container without triggering DOMContentLoaded, so:
- Event listeners are lost - They’re attached to DOM elements that no longer exist
- Component initialization is skipped -
DOMContentLoadeddoesn’t fire again - JavaScript state is lost - Any component state is reset
The Solution: Re-initialize After Every Page Transition
✅ CORRECT - Component Pattern:
// In component files (e.g., TableOfContents.astro, CommandPalette.astro)
function initializeMyComponent() {
// Remove existing event listeners to prevent duplicates
const existingElement = document.querySelector('.my-component');
if (existingElement) {
const newElement = existingElement.cloneNode(true);
existingElement.parentNode?.replaceChild(newElement, existingElement);
}
// Get fresh references after cloning
const element = document.querySelector('.my-component');
if (element) {
element.addEventListener('click', handleClick);
// ... other initialization
}
}
// Make function globally available
window.initializeMyComponent = initializeMyComponent;
// Initialize on DOMContentLoaded
document.addEventListener('DOMContentLoaded', initializeMyComponent);
✅ CORRECT - BaseLayout.astro Integration:
// In BaseLayout.astro - add to BOTH hooks
window.swup.hooks.on('page:view', () => {
// ... other code ...
if (window.initializeMyComponent) {
window.initializeMyComponent();
}
});
window.swup.hooks.on('visit:end', () => {
// ... other code ...
if (window.initializeMyComponent) {
window.initializeMyComponent();
}
});
Why This Works
- Global functions - Available to Swup hooks from any component
- Event listener cleanup - Prevents duplicate listeners by cloning elements
- Fresh DOM references - Gets new elements after content replacement
- Both hooks - Ensures initialization happens regardless of transition timing
Common Components That Need This Pattern
- Table of Contents - Collapse/expand functionality
- Command Palette - Search and navigation
- Theme Toggle - Dark/light mode switching
- Mobile Menu - Navigation menu interactions
- Image Galleries - Lightbox and gallery controls
- Mermaid Diagrams - Diagram rendering and interactions
- Linked Mentions - Post connection highlighting
Important Notes for AI Agents
- NEVER call
handleInitialHashScroll()in Swup hooks - It breaks back/forward navigation - ALWAYS re-initialize JavaScript after page transitions - Use the pattern above
- Let browser handle scroll restoration - Don’t interfere with natural behavior
- Accessibility Warnings: Swup can add
tabindexattributes to body elements, causing accessibility warnings. This is whyaccessibility: falseis set in the config. - Container Structure: All page content must be wrapped in
#swup-containerfor transitions to work - Image Loading: Swup doesn’t interfere with image loading attributes - these are handled by the PostCard and ImageWrapper components
- Navigation: Internal links automatically use Swup transitions when available
Swup Hooks and Custom Behavior
The project includes custom Swup behavior in BaseLayout.astro:
- Scroll Management: Custom scroll behavior to prevent unwanted scrolling during transitions
- Content Replacement: Handles content updates after page transitions
- Mobile Menu: Closes mobile menu on navigation
- NO scroll interference: Back/forward navigation is handled entirely by the browser
Image Handling
Critical Distinction: Post Cards vs Post Content Images
IMPORTANT: There are two completely separate image systems in this project:
1. Post Card Images (Listings, Homepage, Tag Pages)
- Controlled by:
siteConfig.postOptions.showPostCardCoverImagesinconfig.ts - Options:
"all","featured","home","posts","featured-and-posts","none" - Current Setting:
"featured-and-posts"(shows on featured posts and posts/tags pages) - Frontmatter: The
imagefield in post frontmatter is used for card images - NOT affected by:
hideCoverImagefrontmatter field - Loading: Uses
eagerloading for first post on pages,lazyfor others - Scope: ONLY affects post cards - projects and documentation cards are not controlled by this setting
2. Post Content Images (Inside Individual Posts)
- Controlled by:
hideCoverImagefrontmatter field - Purpose: Controls whether the main post image appears in the post content
- Loading: Always uses
eagerloading andfetchpriority="high" - Location: Rendered by
PostContent.astrocomponent
3. Project Card Images
- Controlled by: Always show when available (not controlled by postOptions)
- Purpose: Project cards always display cover images when present
- Frontmatter: Controlled by
hideCoverImagefrontmatter field - Loading: Uses
eagerloading for first project,lazyfor others
4. Documentation Card Images
- Controlled by: Always show when available (not controlled by postOptions)
- Purpose: Documentation cards always display cover images when present
- Frontmatter: Controlled by
hideCoverImagefrontmatter field - Loading: Uses
eagerloading for first doc,lazyfor others - Homepage Exception: Hidden on homepage for cleaner look
Key Rules for AI Agents
- Never confuse these two systems - they are completely independent
- Post card visibility is controlled by
showPostCardCoverImagesconfig, not frontmatter - Post content visibility is controlled by
hideCoverImagefrontmatter - Performance warnings about “unoptimized loading” typically refer to post card images
- Accessibility warnings about “redundant alt text” can affect both systems
Development Mode Graceful Error Handling
When working with Obsidian and actively editing content, missing image errors are handled gracefully:
- Graceful Fallbacks: Missing images are automatically replaced with placeholder images
- Development Warnings: Console warnings help identify missing images (only in dev mode)
- Continued Development: The build process continues even with missing images
- Visual Indicators: Placeholder images clearly indicate missing assets
Placeholder Images
The system automatically uses placeholder images when assets are missing:
- Posts:
/posts/attachments/placeholder.jpg - Pages:
/pages/attachments/placeholder.jpg - Default:
/posts/attachments/placeholder.jpg
Image Reference Formats
Relative Paths (Recommended)


Obsidian Bracket Syntax


Frontmatter Images
---
image: cover.jpg
imageAlt: Cover image description
---
Or with Obsidian bracket syntax:
---
image: "[[cover.jpg]]"
imageAlt: Cover image description
---
Image Resolution Logic
The theme automatically handles image resolution for folder-based posts:
- Relative paths (
image.jpg) →/posts/post-slug/image.jpg - Absolute paths (
/attachments/image.jpg) →/attachments/image.jpg - External URLs (
https://...) → Used as-is
Development Tools
Check Missing Images
pnpm run check-images
This script will:
- Scan all markdown files for image references
- Check if referenced images exist
- Report missing images with file locations and line numbers
- Provide helpful tips for fixing issues
Development Configuration
Located in src/config/dev.ts:
export const devConfig = {
images: {
showPlaceholders: true, // Show placeholder images
logMissingImages: true, // Log missing images to console
fallbacks: {
posts: '/posts/attachments/placeholder.jpg',
pages: '/pages/attachments/placeholder.jpg',
default: '/posts/attachments/placeholder.jpg'
}
},
content: {
continueOnMissingAssets: true, // Continue processing with missing assets
logWarnings: true // Log content processing warnings
}
};
Build Process
Asset Sync
The build process automatically syncs folder-based assets to the public directory:
src/content/posts/my-post/
├── index.md
├── image.jpg
├── audio.wav
├── video.mp4
└── document.pdf
↓ (build process) ↓
public/posts/my-post/
├── image.jpg
├── audio.wav
├── video.mp4
└── document.pdf
Supported Media Types:
- Images: JPG, JPEG, PNG, GIF, WEBP, SVG, BMP, TIFF, TIF, ICO
- Audio: MP3, WAV, OGG, M4A, 3GP, FLAC, AAC
- Video: MP4, WEBM, OGV, MOV, MKV, AVI
- Documents: PDF
Build Scripts
The build process includes several pre-build steps:
- Sync Media Files: Copy images, audio, video, and PDF files from content to public directory
- Process Aliases: Convert content aliases to redirects
- Generate Redirects: Create redirect rules for deployment platforms
- Build Astro: Compile the site
RSS and Atom Feeds
The theme automatically generates both RSS and Atom feeds for content syndication:
Feed Generation
- RSS Feed: Available at
/rss.xml- Uses@astrojs/rssintegration - Atom Feed: Available at
/feed.xml- Custom implementation insrc/pages/feed.xml.ts - Auto-generated: Both feeds are automatically generated during the build process
- Content Source: Feeds include all published posts (excludes drafts in production)
Feed Features
- Post Filtering: Only includes non-draft posts in production builds
- Sorting: Posts are sorted by date (newest first)
- Metadata: Includes post title, description, publication date, and tags
- Image Support: RSS feed includes post images when
imageOG: trueis set - Categories: Post tags are included as RSS categories and Atom categories
- Author Information: Uses global author from
siteConfig.author
Feed Buttons
Both RSS and Atom feed buttons are available on all post listing pages:
- Posts Index (
/posts) - Main posts listing - Paginated Posts (
/posts/[page]) - Paginated post listings - Tag Pages (
/posts/tag/[...tag]) - Posts filtered by tag - Paginated Tag Pages (
/posts/tag/[...tag]/[page]) - Paginated tag listings
Feed Button Styling
- Consistent Design: Both buttons use identical styling with theme-aware colors
- Icons: RSS uses Lucide RSS icon, Atom uses Lucide atom icon
- Accessibility: Proper
titleattributes anddata-no-swupfor Swup compatibility - Responsive: Buttons adapt to different screen sizes and themes
Technical Implementation
- RSS: Uses
@astrojs/rsswith custom data processing for images and metadata - Atom: Custom XML generation with proper Atom 1.0 specification compliance
- Caching: Both feeds include appropriate cache headers (1 hour cache, 1 day s-maxage)
- Content Type: RSS serves as
application/rss+xml, Atom asapplication/xml
Deployment Platforms
The theme supports deployment to all major platforms with an elegant configuration system:
Platform Selection
Set your deployment platform once in src/config.ts:
deployment: {
platform: "netlify", // "netlify" | "vercel" | "github-pages"
}
Supported Platforms
- Netlify: Generates
netlify.tomlwith redirects and build settings - Vercel: Generates
vercel.jsonwith redirects and headers - GitHub Pages: Generates
public/redirects.txtfor GitHub Pages redirects
Build Process
The build process automatically detects your chosen platform and generates the correct configuration files:
pnpm run build # Works for all platforms - no environment variables needed!
Platform-Specific Features
- Netlify: Includes
netlify.tomlwith redirects, build settings, and 404 handling - Vercel: Generates
vercel.jsonwith redirects and cache headers for assets - GitHub Pages: Creates
public/redirects.txtin the format required by GitHub Pages
Platform Headers for PDF Embeds and Twitter Widgets
To make PDF embeds and Twitter widgets work correctly, specific HTTP headers must be configured on each deployment platform:
Required Headers:
- X-Frame-Options: SAMEORIGIN - Allows PDFs to be embedded in iframes on your own site
- Content-Security-Policy - Allows Twitter widgets script and iframes to load
Netlify (Default)
Headers are configured in netlify.toml. The following headers are automatically applied:
- PDF files:
X-Frame-Options: SAMEORIGIN - All pages: CSP with Twitter (
https://platform.twitter.com) and other required domains
Vercel
Headers are generated automatically in vercel.json when you run the build command. The scripts/generate-deployment-config.js script:
- PDF iframe permissions (
X-Frame-Options: SAMEORIGIN) - Twitter CSP rules in the Content-Security-Policy header
- Preserves existing custom settings (serverless functions, environment variables, etc.)
To generate/update the config:
pnpm run build
Important: The script merges new redirects/headers with existing vercel.json settings, so custom configurations won’t be lost.
GitHub Pages
Headers are generated automatically in public/_headers when you run the build command. The script creates:
public/_redirects- Redirect rules for GitHub Pagespublic/_headers- Custom headers (requires paid GitHub Pages plan)
Important: These files are auto-generated during build and are ignored by git (see .gitignore). They are:
- Only created when
platform: "github-pages"is selected - Automatically cleaned up when switching to other platforms
- Build artifacts (similar to
dist/) that should not be committed
Note: Custom headers require GitHub Pages on a paid plan or GitHub Enterprise. Free GitHub Pages users won’t have these headers applied.
For free GitHub Pages users:
- PDF embeds may show security warnings in some browsers
- Twitter widgets should still work as the script is included directly in the page
Common Issues:
- PDF shows “Firefox Can’t Open This Page”: The server is blocking iframe embeds. Check that
X-Frame-Options: SAMEORIGINis set for PDF files. - Twitter widgets not loading: Check browser console for CSP errors. Ensure
https://platform.twitter.comis in bothscript-srcandframe-srcdirectives.
Migration from Environment Variables
The old environment variable approach is still supported for backward compatibility:
# Old way (still works)
DEPLOYMENT_PLATFORM=vercel pnpm run build
# New way (recommended)
# Just set platform in config.ts and run:
pnpm run build
Version Management
Theme Version System
The Astro Modular theme includes a comprehensive version management system that tracks the current theme version and displays it consistently across all build and development commands.
Version Display
All pnpm commands now show the theme version:
> astro-modular@0.1.0 dev C:\Users\david\Development\astro-modular
> astro-modular@0.1.0 build C:\Users\david\Development\astro-modular
> astro-modular@0.1.0 version C:\Users\david\Development\astro-modular
Instead of the generic workspace version:
# Old (generic)
> workspace@1.0.0 dev
# New (theme-specific)
> astro-modular@0.1.0 dev
Version Files
The version system uses multiple files to maintain consistency:
Primary Version File:
VERSION- Contains the current version number (e.g.,0.1.0)
Package Configuration:
package.json- Updated with theme name (astro-modular) and version (0.1.0)
Version Utility:
scripts/get-version.js- Provides version management functions
Version Commands
Get Current Version:
pnpm run version
# Output: 0.1.0
Get Theme Identifier:
node scripts/get-version.js
# Output: 0.1.0
Version Management Functions
The scripts/get-version.js utility provides several functions:
getThemeVersion() - Get current version from VERSION file or package.json
getThemeName() - Get theme name from package.json
getThemeIdentifier() - Get full identifier (name@version)
updateVersion(newVersion) - Update both VERSION file and package.json
Release Process
For Maintainers (Creating Releases):
-
Update Version Files (Manual Process):
# Step 1: Update VERSION file echo "0.2.0" > VERSION # Step 2: Update package.json manually # Change "version": "0.1.0" to "version": "0.2.0"Important: Both files must be updated manually. The VERSION file is the source of truth, but package.json controls the terminal display.
-
Create GitHub Release:
- Go to GitHub repository
- Click “Releases” → “Create a new release”
- Tag version:
v0.2.0(with ‘v’ prefix) - Release title:
v0.2.0 - Description: List of changes
- Important: Use “Source code (zip)” as the release asset (automatic)
-
Publish Release:
- Click “Publish release”
- GitHub automatically creates the source code zip
- No additional files needed
Template vs Fork Detection
Template Users:
- See
astro-modular@0.1.0in all commands - Version stays at template version
Fork Users:
- See
astro-modular@0.1.0in all commands - Version can be updated manually via git
Version Display Examples
Development Server:
> astro-modular@0.1.0 dev C:\Users\david\Development\astro-modular
> cross-env ASTRO_CONTENT_COLLECTION_CACHE=false node scripts/setup-dev.mjs && node scripts/sync-images.js && node scripts/process-aliases.js && node scripts/generate-deployment-config.js && astro dev --host localhost --port 5000
Build Process:
> astro-modular@0.1.0 build C:\Users\david\Development\astro-modular
> node scripts/sync-images.js && node scripts/process-aliases.js && node scripts/generate-deployment-config.js && astro build
Version Check:
> astro-modular@0.1.0 version C:\Users\david\Development\astro-modular
> node scripts/get-version.js
0.1.0
Best Practices for AI Agents
Version Management:
- Always check current version before making changes
- Use version commands to verify theme version
- Don’t hardcode version numbers - use the utility functions
- Test version display after making changes
Release Process:
- Update VERSION file first - this is the source of truth
- Update package.json manually - both files must be updated separately
- Use semantic versioning - follow semver conventions
- Test version commands before creating releases
- No automatic sync - version files don’t sync automatically during dev/build
User Experience:
- Version display is consistent across all commands
- Theme identification is clear - users know which version they’re using
- Update process is transparent - users see version changes
- No confusion between workspace and theme versions
Configuration & Customization
Core Configuration (src/config.ts)
The configuration is organized in logical sections for easy customization. For detailed setup instructions, see the Getting Started Guide.
Site Information
export const siteConfig = {
site: 'https://yourdomain.com',
title: 'Your Blog Title',
description: 'Your blog description',
author: 'Your Name',
language: 'en',
}
Theme & Layout Options
theme: "oxygen", // Available: Oxygen, Minimal, Atom, Ayu, Catppuccin, Charcoal, Dracula, Everforest, Flexoki, Gruvbox, macOS, Nord, Obsidian, Rosé Pine, Sky, Solarized, Things
availableThemes: "all", // "all" or array like ["oxygen", "minimal", "nord"] to limit user choices
layout: {
contentWidth: "45rem",
},
postsPerPage: 5,
Deployment Platform Configuration
deployment: {
platform: "netlify", // "netlify" | "vercel" | "github-pages" - set once and forget!
}
Deployment Platform Options:
"netlify"(default) - Generatesnetlify.tomlwith redirects and build settings"vercel"- Generatesvercel.jsonwith redirects and cache headers"github-pages"- Generatespublic/redirects.txtfor GitHub Pages redirects
Important: Set this once in your config and the build process automatically generates the correct platform-specific configuration files. No environment variables needed!
Modular Features Configuration
features: {
readingTime: true,
wordCount: true,
tableOfContents: true,
tags: true,
linkedMentions: true,
linkedMentionsCompact: false,
scrollToTop: true,
darkModeToggleButton: true,
commandPalette: true,
postNavigation: true,
showSocialIconsInFooter: true,
showPostCardCoverImages: "featured-and-posts", // See cover image options below
}
Linked Mentions Features:
linkedMentions: true- Enable linked mentions section at the end of the page showing which posts reference the current postlinkedMentionsCompact: false- Choose between detailed view (default) or compact view for linked mentions
Linked Mentions Excerpt Extraction Logic
🚨 CRITICAL: Structural-Only Approach
The Linked Mentions excerpt extraction logic uses purely structural/syntax-based patterns - NEVER word or phrase matching.
Key Principles:
-
Markdown Cleanup (Structural Only):
- Remove markdown syntax: code blocks (
```), inline code (`), bold (**), italic (*), headers (#), blockquotes (>), callouts (> [!TYPE]), horizontal rules (---), list markers (-,1.) - Remove structural patterns: orphaned labels ending with
:(e.g.,Label:at end), trailing colons/dashes - NEVER match specific words or phrases like “Further reading”, “See also”, “Start lines with”, etc.
- ONLY use structural patterns like
([A-Z][a-z]+):to match any capitalized label, not specific label names
- Remove markdown syntax: code blocks (
-
Ellipsis Placement (Structural Detection):
- Detects natural endings using structural patterns only:
- Links as endings:
]](wikilink),)(markdown link),</mark>(processed link HTML) - Sentence endings:
.,!,?(complete sentences) - Incomplete punctuation:
,,:,-,;(suggesting truncation) - Letter endings: Ends with letter/word but no sentence punctuation (e.g., “or”, “and” - detected structurally, not word-specific)
- Links as endings:
- NEVER check for specific phrases like “Further reading”, “See also”, etc.
- NEVER match specific conjunctions like “or”, “and”, “but” - instead check for letter endings without punctuation
- Detects natural endings using structural patterns only:
-
Link Processing:
- Processes standard markdown links FIRST, then wikilinks (prevents double-processing)
- Handles incomplete/truncated links (e.g.,
[Obsidian Vault...) by detecting structural patterns - Highlights relevant links (matching target post) using slug comparison, not word matching
-
Semantic HTML:
- Uses
divelements for “Linked Mentions” heading and individual post titles (NOTh2/h3headings) - Maintains proper visual hierarchy without interfering with post heading structure
- Uses
Common Mistakes to Avoid:
- ❌ NEVER hardcode word/phrase patterns like
/\bFurther reading|See also\b/i - ❌ NEVER match specific words like
/\b(or|and|but)\b/- use structural letter-ending detection instead - ❌ NEVER remove specific phrases like “Start lines with”, “separate columns” - use structural label patterns
- ✅ ALWAYS use structural patterns like
/[.!?]\s*$/(sentence endings),/<\/mark>/i(link endings) - ✅ ALWAYS check for syntax artifacts like code blocks, markdown formatting, not content-specific text
Cover Image Options
"all"- Show cover images everywhere"featured"- Show only on the featured post section and featured posts"home"- Show on homepage sections (featured and recent)"posts"- Show only on posts pages, tag pages, and post listings"featured-and-posts"- Show on featured post section AND posts pages/tags (but not recent posts section)"none"- Never show cover images
Homepage Content Options
The homepage content is now organized under the homeOptions section for better clarity and control:
homeOptions: {
featuredPost: {
enabled: true, // Show featured post on homepage
type: "latest", // "latest" or "featured"
slug: "getting-started", // Only used when type is "featured"
},
recentPosts: {
enabled: true, // Show recent posts on homepage
count: 7, // Number of recent posts to show
},
projects: {
enabled: false, // Show featured projects on homepage
count: 2, // Number of projects to show
},
docs: {
enabled: false, // Show featured docs on homepage
count: 3, // Number of docs to show
},
blurb: {
placement: "below", // "above", "below", or "none"
},
}
Featured Post Configuration:
enabled: true- Show featured post sectiontype: "latest"- Show the most recent posttype: "featured"- Show a specific post by slugslug: "post-name"- Post slug (without/posts/prefix) when type is “featured”- Flexible Setup: Slug can be present even when
type: "latest"- it will be ignored until you switch to “featured”
Recent Posts Configuration:
enabled: true- Show recent posts sectioncount: 7- Number of recent posts to display- Smart Logic: If featured post is shown, recent posts start from the second post
Projects Configuration:
enabled: true- Show featured projects on homepagecount: 2- Number of projects to display- Priority: First tries to show projects with
featured: truein frontmatter (up to count) - Fallback: If no featured projects exist, shows the most recent projects (up to count)
- No Content: If no projects exist at all, shows nothing
- Includes “View all projects →” link
Documentation Configuration:
enabled: true- Show featured docs on homepagecount: 3- Number of docs to display- Priority: First tries to show docs with
featured: truein frontmatter (up to count) - Fallback: If no featured docs exist, shows the most recent docs (up to count)
- No Content: If no docs exist at all, shows nothing
- Includes “View all docs →” link
Home Blurb Configuration:
placement: "above"- Show at top of homepageplacement: "below"- Show after content (default)placement: "none"- Disable completely- Blurb-Only Mode: When no other content is enabled, blurb becomes the main page content with H1 title and page-like styling
Homepage Order:
- Featured Post (if enabled)
- Recent Posts (if enabled)
- Featured Projects (if enabled)
- Featured Documentation (if enabled)
- Home Blurb (if enabled)
Special Layout Logic:
- Single Content Type: When only one content type is displayed, it gets special treatment with centered “View all” links instead of section titles
- Blurb-Only Mode: When only blurb content is shown, it displays as a proper page with H1 title and rounded container styling
Homepage Layout Examples:
Show only recent posts:
homeOptions: {
featuredPost: { enabled: false, type: "latest", slug: undefined },
recentPosts: { enabled: true, count: 7 },
projects: { enabled: false },
docs: { enabled: false },
blurb: { placement: "none" },
}
// Result: Special treatment - no "Recent Posts" title, centered "View all posts →"
Show only projects:
homeOptions: {
featuredPost: { enabled: false, type: "latest", slug: undefined },
recentPosts: { enabled: false, count: 7 },
projects: { enabled: true },
docs: { enabled: false },
blurb: { placement: "none" },
}
// Result: Special treatment - no "Projects" title, centered "View all projects →"
Show only blurb content:
homeOptions: {
featuredPost: { enabled: false, type: "latest", slug: undefined },
recentPosts: { enabled: false, count: 7 },
projects: { enabled: false },
docs: { enabled: false },
blurb: { placement: "below" },
}
// Result: Page-like layout with H1 title and rounded container
Show multiple content types:
homeOptions: {
featuredPost: { enabled: true, type: "latest", slug: undefined },
recentPosts: { enabled: true, count: 7 },
projects: { enabled: true },
docs: { enabled: false },
blurb: { placement: "below" },
}
// Result: Normal layout with section titles and right-aligned "View all" links
Post Card Aspect Ratio Configuration
Configure the aspect ratio for post card cover images:
features: {
postCardAspectRatio: "og", // Default: OpenGraph standard
customAspectRatio: undefined, // For custom ratios
}
Aspect Ratio Options:
"og"(1.91:1) - OpenGraph standard (default)"16:9"(1.78:1) - Standard widescreen"4:3"(1.33:1) - Traditional"3:2"(1.5:1) - Classic photography"square"(1:1) - Square"golden"(1.618:1) - Golden ratio"custom"- Use your own ratio
Custom Aspect Ratio Example:
postCardAspectRatio: "custom",
customAspectRatio: "2.5/1" // Custom 2.5:1 ratio
Important Notes for AI Agents:
- This only affects post cards (listings, homepage, tag pages)
- Individual post cover images maintain their original aspect ratio
- The aspect ratio is applied via CSS
aspect-ratioproperty - Use the
getPostCardAspectRatio()utility function to get the current ratio value
Navigation Configuration
navigation: {
showNavigation: true,
style: 'traditional', // or 'minimal'
showMobileMenu: true,
pages: [
{ title: 'Posts', url: '/posts' },
{ title: 'About', url: '/about' }
],
social: [
{ title: 'GitHub', url: 'https://github.com/username', icon: 'github' }
],
}
SEO Configuration
seo: {
defaultOgImageAlt: "Astro Modular logo.",
},
footer: {
enabled: true, // Set to false for minimal footer
content: `© 2025 {author}. Built with Astro Modular.`,
}
Profile Picture Configuration
profilePicture: {
enabled: true,
image: "/profile.jpg", // Path to your image (place in public/ directory)
alt: "Profile picture", // Alt text for accessibility
size: "md", // "sm" (32px), "md" (48px), or "lg" (64px)
url: "/about", // Optional URL to link to when clicked
placement: "footer", // "footer" or "header"
style: "circle", // "circle", "square", or "none"
}
Profile Picture Features:
- Placement Options: Header (replaces text logo) or Footer (above or aligned with content)
- Style Options: Circle (profile photos), Square (logo squares), None (horizontal logos/banners)
- Footer Control: When
footer.enabled: false, profile picture aligns with social icons - Responsive: Different layouts for mobile and desktop
- Theme-Aware: Styling adapts to all available themes
Content Frontmatter Schemas
Posts Frontmatter
---
title: "{{title}}"
date: {{date}}
description: ""
tags: []
image: ""
imageAlt: ""
imageOG: false
hideCoverImage: false
hideTOC: false
targetKeyword: ""
draft: true
---
Pages Frontmatter
---
title: "{{title}}"
description: ""
hideCoverImage: false
hideTOC: false
noIndex: false
---
Projects Frontmatter
---
title: "{{title}}"
description: "Project description"
date: {{date}}
categories: ["Web Development", "Open Source"]
repositoryUrl: "https://github.com/username/repo"
demoUrl: "https://your-demo.com"
status: "completed" # Any string value - "completed", "in-progress", "On Hold", etc.
image: "cover.jpg"
imageAlt: "Project screenshot"
hideCoverImage: false
hideTOC: false
draft: false
featured: true
---
Documentation Frontmatter
---
title: "{{title}}"
description: "Documentation description"
category: "Setup" # Optional - creates "Unsorted" if missing
order: 1
version: "1.0.0"
lastModified: 2024-01-15
image: "hero.jpg"
imageAlt: "Documentation screenshot"
hideCoverImage: false
hideTOC: false
draft: false
featured: true
---
Development Configuration (src/config/dev.ts)
Image Handling
images: {
showPlaceholders: true, // Show placeholder images for missing assets
logMissingImages: true, // Log missing images to console
fallbacks: {
posts: '/posts/attachments/placeholder.jpg',
pages: '/pages/attachments/placeholder.jpg',
default: '/posts/attachments/placeholder.jpg'
}
}
Content Processing
content: {
continueOnMissingAssets: true, // Continue processing with missing assets
logWarnings: true // Log content processing warnings
}
Error Handling
errors: {
showDetails: true, // Show detailed error information
continueOnNonCriticalErrors: true
}
Tag Handling (Recently Added)
tags: {
handleMissingTags: true, // Gracefully handle missing/deleted tags
logTagWarnings: true, // Log tag-related warnings
continueOnMissingTags: true // Continue processing with missing tags
}
🚨 CRITICAL: Config Marker System for Obsidian Plugin
⚠️ AI AGENTS MUST READ THIS SECTION CAREFULLY ⚠️
The Astro Modular theme includes an Obsidian plugin that allows users to configure their theme settings directly from within Obsidian. This plugin relies on a marker-based system in src/config.ts that is CRITICAL for plugin functionality and MUST NEVER BE BROKEN.
What Are Config Markers?
Config markers are special comment lines in src/config.ts that look like this:
// [CONFIG:THEME]
theme: "oxygen",
// [CONFIG:COMMAND_PALETTE_ENABLED]
enabled: true,
Why Markers Exist
The Obsidian plugin uses these markers to:
- Reliably identify which configuration values it can update
- Avoid fragile regex that breaks when config structure changes
- Prevent conflicts with user modifications
- Enable safe updates without affecting other parts of the config
🚨 CRITICAL RULES FOR AI AGENTS
1. NEVER Remove Config Markers
- DO NOT delete any
// [CONFIG:KEY]comments - DO NOT modify marker format (must be exactly
// [CONFIG:KEY]) - DO NOT move markers to different lines
- DO NOT change marker names
2. ALWAYS Add Markers for New Configurable Values
When adding new configuration options that the Obsidian plugin should manage:
// ✅ CORRECT - Add marker for new configurable value
// [CONFIG:NEW_FEATURE_ENABLED]
newFeature: true,
// ❌ WRONG - No marker means plugin can't update it
newFeature: true,
3. Maintain Marker Consistency
- Use consistent naming:
CONFIG:FEATURE_NAME(uppercase, underscores) - Place markers correctly: One line before the config value
- Keep format exact:
// [CONFIG:KEY](no spaces inside brackets)
4. Update Plugin When Adding Markers
When adding new markers to config.ts, you MUST also:
- Add the marker to the
validateMarkers()function in the Obsidian plugin - Add replacement logic in the plugin’s
modifyConfigFromPreset()method - Update plugin documentation
Standardized Marker System
All configurable values in src/config.ts MUST follow this standardized pattern:
Global Settings:
// [CONFIG:THEME]
theme: "oxygen",
// [CONFIG:FONT_SOURCE]
source: "local",
// [CONFIG:FONT_BODY]
body: "Inter",
// [CONFIG:FONT_HEADING]
heading: "Inter",
// [CONFIG:FONT_MONO]
mono: "JetBrains Mono",
// [CONFIG:LAYOUT_CONTENT_WIDTH]
contentWidth: "45rem",
// [CONFIG:FOOTER_ENABLED]
enabled: true,
// [CONFIG:FOOTER_SHOW_SOCIAL_ICONS]
showSocialIconsInFooter: true,
// [CONFIG:SCROLL_TO_TOP]
scrollToTop: true,
// [CONFIG:DARK_MODE_TOGGLE_BUTTON]
darkModeToggleButton: "both",
// [CONFIG:DEPLOYMENT_PLATFORM]
platform: "netlify",
Command Palette Settings:
// [CONFIG:COMMAND_PALETTE_ENABLED]
enabled: true,
// [CONFIG:COMMAND_PALETTE_SHORTCUT]
shortcut: "ctrl+K",
// [CONFIG:COMMAND_PALETTE_PLACEHOLDER]
placeholder: "Search posts",
// [CONFIG:COMMAND_PALETTE_SEARCH_POSTS]
posts: true,
// [CONFIG:COMMAND_PALETTE_SEARCH_PAGES]
pages: false,
// [CONFIG:COMMAND_PALETTE_SEARCH_PROJECTS]
projects: false,
// [CONFIG:COMMAND_PALETTE_SEARCH_DOCS]
docs: false,
// [CONFIG:COMMAND_PALETTE_SECTIONS_QUICK_ACTIONS]
quickActions: true,
// [CONFIG:COMMAND_PALETTE_SECTIONS_PAGES]
pages: true,
// [CONFIG:COMMAND_PALETTE_SECTIONS_SOCIAL]
social: true,
Profile Picture Settings:
// [CONFIG:PROFILE_PICTURE_ENABLED]
enabled: false,
// [CONFIG:PROFILE_PICTURE_IMAGE]
image: "/profile.jpg",
// [CONFIG:PROFILE_PICTURE_ALT]
alt: "Profile picture",
// [CONFIG:PROFILE_PICTURE_SIZE]
size: "md",
// [CONFIG:PROFILE_PICTURE_URL]
url: "/about",
// [CONFIG:PROFILE_PICTURE_PLACEMENT]
placement: "footer",
// [CONFIG:PROFILE_PICTURE_STYLE]
style: "circle",
Navigation Settings:
// [CONFIG:NAVIGATION_SHOW_NAVIGATION]
showNavigation: true,
// [CONFIG:NAVIGATION_STYLE]
style: "traditional",
// [CONFIG:NAVIGATION_SHOW_MOBILE_MENU]
showMobileMenu: true,
Optional Content Types:
// [CONFIG:OPTIONAL_CONTENT_TYPES_PROJECTS]
projects: true,
// [CONFIG:OPTIONAL_CONTENT_TYPES_DOCS]
docs: true,
Home Options:
// [CONFIG:HOME_OPTIONS_FEATURED_POST_ENABLED]
enabled: true,
// [CONFIG:HOME_OPTIONS_FEATURED_POST_TYPE]
type: "latest",
// [CONFIG:HOME_OPTIONS_FEATURED_POST_SLUG]
slug: "getting-started",
// [CONFIG:HOME_OPTIONS_RECENT_POSTS_ENABLED]
enabled: true,
// [CONFIG:HOME_OPTIONS_RECENT_POSTS_COUNT]
count: 7,
// [CONFIG:HOME_OPTIONS_PROJECTS_ENABLED]
enabled: true,
// [CONFIG:HOME_OPTIONS_PROJECTS_COUNT]
count: 2,
// [CONFIG:HOME_OPTIONS_DOCS_ENABLED]
enabled: true,
// [CONFIG:HOME_OPTIONS_DOCS_COUNT]
count: 3,
// [CONFIG:HOME_OPTIONS_BLURB_PLACEMENT]
placement: "below",
Post Options:
// [CONFIG:POST_OPTIONS_POSTS_PER_PAGE]
postsPerPage: 6,
// [CONFIG:POST_OPTIONS_READING_TIME]
readingTime: true,
// [CONFIG:POST_OPTIONS_WORD_COUNT]
wordCount: true,
// [CONFIG:POST_OPTIONS_TABLE_OF_CONTENTS]
tableOfContents: true,
// [CONFIG:POST_OPTIONS_TAGS]
tags: true,
// [CONFIG:POST_OPTIONS_LINKED_MENTIONS_ENABLED]
enabled: true,
// [CONFIG:POST_OPTIONS_LINKED_MENTIONS_COMPACT]
linkedMentionsCompact: false,
// [CONFIG:POST_OPTIONS_GRAPH_VIEW_ENABLED]
enabled: true,
// [CONFIG:POST_OPTIONS_GRAPH_VIEW_SHOW_IN_SIDEBAR]
showInSidebar: true,
// [CONFIG:POST_OPTIONS_GRAPH_VIEW_SHOW_IN_COMMAND_PALETTE]
showInCommandPalette: true,
// [CONFIG:POST_OPTIONS_GRAPH_VIEW_MAX_NODES]
maxNodes: 100,
// [CONFIG:POST_OPTIONS_GRAPH_VIEW_SHOW_ORPHANED_POSTS]
showOrphanedPosts: true,
// [CONFIG:POST_OPTIONS_POST_NAVIGATION]
postNavigation: true,
// [CONFIG:POST_OPTIONS_SHOW_POST_CARD_COVER_IMAGES]
showPostCardCoverImages: "featured-and-posts",
// [CONFIG:POST_OPTIONS_POST_CARD_ASPECT_RATIO]
postCardAspectRatio: "og",
// [CONFIG:POST_OPTIONS_CUSTOM_POST_CARD_ASPECT_RATIO]
customPostCardAspectRatio: "2.5/1",
// [CONFIG:POST_OPTIONS_COMMENTS_ENABLED]
enabled: false,
What Happens If Markers Are Missing?
If config markers are missing or malformed:
- Plugin validation fails - Shows error message with missing markers list
- Configuration updates fail - Plugin cannot update config.ts
- User experience breaks - Theme changes, presets, and wizard don’t work
- Debugging required - Must manually restore markers
How to Add New Markers
When adding new configurable values:
-
Add marker to config.ts:
// [CONFIG:NEW_FEATURE_ENABLED] newFeature: true, -
Add marker to Obsidian plugin validation:
const requiredMarkers = [ // ... existing markers 'CONFIG:NEW_FEATURE_ENABLED', ]; -
Add replacement logic in Obsidian plugin:
modifiedConfig = modifiedConfig.replace( /\/\/ \[CONFIG:NEW_FEATURE_ENABLED\]\s*\n\s*newFeature:\s*(true|false)/, `// [CONFIG:NEW_FEATURE_ENABLED]\n newFeature: ${settings.newFeature}` ); -
Update plugin documentation with new marker information
Migration Script for Existing Users
For users who have config.ts files without markers, provide a migration script that:
- Detects missing markers in their config.ts
- Adds markers automatically in the correct locations
- Preserves existing values while adding markers
- Validates the result to ensure all markers are present
Best Practices for AI Agents
✅ DO:
- Always check for markers before modifying config.ts
- Add markers for new configurable values immediately
- Use consistent marker naming (CONFIG:FEATURE_NAME)
- Test marker functionality after changes
- Update plugin code when adding new markers
- Document new markers in plugin documentation
❌ DON’T:
- Remove existing markers under any circumstances
- Modify marker format or naming
- Add configurable values without markers
- Ignore marker validation errors
- Assume markers are optional
Emergency Recovery
If markers are accidentally removed:
- Check git history for last working version with markers
- Use migration script to restore markers
- Manually add markers following the standardized format
- Test plugin functionality to ensure everything works
- Update documentation to prevent future issues
Testing Checklist
Before deploying any changes to config.ts:
- All existing markers are preserved
- New configurable values have markers
- Marker format is exactly
// [CONFIG:KEY] - Plugin validation passes
- All preset templates work
- Theme changes work
- Wizard functionality works
- Documentation is updated
Theme Customization
Available Themes
- Oxygen (default) - Modern, clean design
- Minimal - Understated with high contrast
- Atom - Dark theme with vibrant accents
- Ayu - Three variants (light, mirage, dark)
- Catppuccin - Pastel color palette
- Charcoal - Dark, professional look
- Dracula - Dark theme with purple accents
- Everforest - Soft, warm colors
- Flexoki - Based on Material Design 3
- Gruvbox - Retro groove color scheme
- macOS - Native macOS appearance
- Nord - Arctic-inspired color palette
- Obsidian - Matches Obsidian’s default theme
- Rosé Pine - All natural pine, faux fir, and winter
- Sky - Light, airy design
- Solarized - Precision colors for machines and people
- Things - Clean, minimal design
- Custom - Create your own theme using the template system
Theme Switching
- Use the command palette (
Ctrl+K) for instant theme switching - Theme changes are visible in real-time with
pnpm dev - All themes are optimized for performance and accessibility
Controlling Available Themes
availableThemesconfiguration: Control which themes users can select- Show all themes: Set
availableThemes: "all"(default behavior) - Limit theme choices: Set
availableThemes: ["oxygen", "minimal", "nord", "dracula"]to restrict options - Example configuration:
// Show only a curated selection of themes availableThemes: ["oxygen", "minimal", "nord", "catppuccin", "rose-pine"] // Show all themes (default) availableThemes: "all" - Use cases:
- Maintain brand consistency by limiting theme choices
- Reduce complexity for users by offering fewer options
- Showcase only professionally-tested themes
- Create themed collections (e.g., warm vs cool color palettes)
Custom Themes
- Template system: Use
src/themes/custom/custom.tsas a starting point - Configuration: Set
theme: "custom"andcustomThemeFile: "filename"insrc/config.ts - Multiple themes: Copy and rename the template file, then change
customThemeFilein config - Documentation: See
src/themes/custom/README.mdfor detailed instructions - Real-time preview: Changes are visible immediately with
pnpm dev
Typography Configuration
The theme includes a comprehensive typography system that allows separate font configuration for headings and body text.
Font Configuration
typography: {
headingFont: "Inter", // Font for headings (h1, h2, h3, h4, h5, h6)
proseFont: "Inter", // Font for body text and prose content
}
Supported Fonts
Sans-Serif Fonts (Recommended for UI and headings):
- Inter - Modern, highly readable (default)
- Roboto - Google’s signature font
- Open Sans - Humanist, friendly
- Lato - Semi-rounded, warm
- Poppins - Geometric, modern
- Source Sans Pro - Adobe’s open source font
- Nunito - Rounded, friendly
- Montserrat - Urban, geometric
Serif Fonts (Great for headings and elegant typography):
- Playfair Display - High contrast, elegant
- Merriweather - Highly readable, designed for screens
- Lora - Well-balanced, contemporary
- Crimson Text - Book typeface, readable
- PT Serif - Professional, clean
- Libre Baskerville - Classic, web-optimized
Monospace Fonts (For code and technical content):
- Fira Code - Programming ligatures
- JetBrains Mono - Developer-focused
- Source Code Pro - Adobe’s monospace
- IBM Plex Mono - Corporate, clean
- Cascadia Code - Microsoft’s coding font
Popular Font Combinations
Modern & Professional:
typography: {
headingFont: "Montserrat",
proseFont: "Source Sans Pro",
}
Elegant & Readable:
typography: {
headingFont: "Playfair Display",
proseFont: "Lato",
}
Clean & Minimal:
typography: {
headingFont: "Inter",
proseFont: "Inter",
}
Serif Typography:
typography: {
headingFont: "Merriweather",
proseFont: "Merriweather",
}
Tech/Developer Blog:
typography: {
headingFont: "JetBrains Mono",
proseFont: "Source Sans Pro",
}
Technical Implementation
Font Loading:
- Automatically generates Google Fonts URLs based on configuration
- Only loads fonts that are actually used
- Provides fallbacks to system fonts for performance
CSS Custom Properties:
--font-heading- Applied to all headings--font-prose- Applied to body text and prose content- Fallback chain: Custom font → System fonts → Generic families
Tailwind Classes:
font-heading- Apply heading font to any elementfont-prose- Apply prose font to any elementfont-sans- Apply prose font (alias)
Performance Considerations:
- Fonts are loaded asynchronously
- System font fallbacks prevent layout shift
- Google Fonts are optimized for web delivery
- Custom fonts gracefully degrade to system fonts
Best Practices for AI Agents
Font Selection:
- Headings: Choose fonts with good contrast and readability at various sizes
- Body Text: Prioritize readability and legibility for long-form content
- Consistency: Maintain visual hierarchy between heading and body fonts
- Performance: Stick to Google Fonts for automatic optimization
Common Mistakes to Avoid:
- Don’t use too many different fonts (stick to 1-2 maximum)
- Avoid fonts that are too similar (defeats the purpose of separation)
- Don’t forget to test readability in both light and dark themes
- Avoid fonts that don’t have good fallbacks
Testing Font Combinations:
- Test at different screen sizes
- Verify readability in both light and dark modes
- Check that fonts load properly on slow connections
- Ensure proper fallbacks work when custom fonts fail
Content Structure Customization
Folder-Based Posts
- Use descriptive kebab-case folder names
- Keep assets co-located with
index.md - Folder name becomes the URL slug automatically
Traditional Posts
- Single markdown files in
src/content/posts/ - Images in
src/content/posts/attachments/ - Use relative paths for images
Pages
- Static pages in
src/content/pages/ - Images in
src/content/pages/attachments/ - NO H1 headings in markdown content - title comes from frontmatter
Projects
- Showcase work, side projects, and portfolio items
- Support both folder-based and single-file organization
- Images in
src/content/projects/attachments/or co-located with content - Frontmatter includes: title, description, date, categories, repositoryUrl, demoUrl, status, image, imageAlt, hideCoverImage, draft, noIndex, featured
- Status: Any string value with intelligent handling:
- Recognized values: “completed”, “in-progress” (case-insensitive)
- Case normalization: “In Progress” → “in-progress”, “Completed” → “completed”
- Custom values: “On Hold”, “Paused”, etc. display as-is with neutral theme colors
- Styling: Recognized values get green/yellow colors, custom values use theme colors
- Featured:
trueto show on homepage (requireshomeProjects: truein config) - URL structure:
/projects/project-slug
Documentation
- Technical documentation and guides
- Support both folder-based and single-file organization
- Images in
src/content/docs/attachments/or co-located with content - Frontmatter includes: title, description, category, order, lastModified (optional), version, image, imageAlt, hideCoverImage, draft, noIndex, showTOC, featured
- Featured:
trueto show on homepage (requireshomeDocs: truein config) - URL structure:
/docs/doc-slug
Documentation Structure:
Documentation files are organized by category and can be single files:
src/content/docs/
├── getting-started.md
├── advanced-configuration.md
├── api-reference/
│ ├── index.md
│ └── code.png
├── attachments/
│ └── sample-guide-hero.png
└── sample-guide.md
Frontmatter Schema:
---
title: "Documentation Title" # Required
description: "Documentation description" # Required
category: "Setup" # Required - for organization
order: 1 # Required - for sorting within category
lastModified: 2024-01-15 # Optional - if missing, no date is shown
version: "1.0.0" # Optional
image: "hero-image.jpg" # Optional
imageAlt: "Documentation screenshot" # Optional
hideCoverImage: false # Optional
draft: false # Optional
noIndex: false # Optional
showTOC: true # Optional - override global TOC setting
featured: false # Optional - show on homepage (requires homeDocs: true)
---
Features:
- Category Organization: Group related documentation
- Version Control: Track documentation versions
- Ordered Display: Control display order within categories
- Table of Contents: Automatic TOC generation
- Search Integration: Included in command palette search
- SEO Optimized: Proper meta tags and structured data
Categories:
Common documentation categories:
- Setup: Installation and initial configuration
- Configuration: Advanced settings and customization
- Reference: API documentation and technical specs
- Tutorials: Step-by-step guides
- Troubleshooting: Common issues and solutions
- Examples: Code samples and use cases
Best Practices:
- Use clear, descriptive titles
- Organize by logical categories
- Set appropriate order values
- Include version information for API docs
- Write for your target audience
- Keep content up-to-date
- Use consistent formatting
URL Structure:
- Individual docs:
/docs/documentation-slug - Documentation index:
/docs - Documentation is automatically included in search and command palette
- Categories are displayed as sections on the index page
Troubleshooting
Common Image Issues
Images Not Loading
- Check that image paths use relative paths
- Ensure images are in the same folder as
index.md - Verify the build process synced the assets
- Run
pnpm run check-imagesto identify missing files
Build Errors
- Check for typos in image paths
- Ensure all referenced assets exist
- Verify folder structure is correct
- Check console for specific error messages
Development Mode Issues
- Missing images show placeholder images (this is normal)
- Console warnings indicate missing assets
- Use the check script to identify all missing images
- Fix images gradually - development continues with placeholders
Quick Fixes
- Move Images: Ensure images are in the correct folder
- Update Paths: Fix image paths in markdown files
- Check Case: File paths are case-sensitive
- Verify Extensions: Ensure file extensions match exactly
- Run Check Script: Use
pnpm run check-imagesto identify issues
Best Practices
Content Organization
Naming Conventions
- Use kebab-case for folder names:
my-awesome-post - Keep folder names descriptive but concise
- Avoid special characters and spaces
Asset Organization
- Use descriptive filenames:
hero-image.jpgnotimg1.jpg - Group related assets in subfolders if needed
- Keep file sizes reasonable for web use
Content Structure
- Always use
index.mdfor the main content in folder-based posts - Use relative paths for all local assets
- Test image references before publishing
Development Workflow
- Use the Check Script: Run
pnpm run check-imagesregularly - Monitor Console: Watch for development warnings about missing images
- Fix Images Gradually: You don’t need to fix all missing images immediately
- Use Placeholders: The placeholder system lets you continue development
Version Control
- Commit the entire folder as a unit for folder-based posts
- Use meaningful commit messages
- Consider using Git LFS for large assets
- Test builds before committing
Production Deployment
- Missing images will cause build failures in production
- Always run
pnpm run check-imagesbefore deploying - Fix all missing images before production builds
- Test the build process locally before deployment
Technical Implementation Details
Image Processing Components
- ImageWrapper.astro: Handles image rendering with fallbacks
- PostLayout.astro: Manages post-specific image handling
- internallinks.ts: Processes both Obsidian-style wikilinks and standard markdown links
- images.ts: Utility functions for image path optimization
Content Processing
- remarkInternalLinks: Processes both Obsidian wikilinks (posts only) and standard markdown links (all content types)
- remarkFolderImages: Handles folder-based post image paths
- Content Collections: Astro’s content management system
SEO and Meta Tags
All SEO features work with folder-based posts:
- Open Graph images are properly resolved
- Meta tags are generated correctly
- Sitemaps include folder-based posts
- RSS feeds include folder-based posts
Common Issues and Solutions for AI Agents
Accessibility Warnings
- “Invalid tabindex on non-interactive element”: Usually caused by Swup. Check
astro.config.mjs-accessibility: falseshould be set. - “Missing content” warnings: Check for proper
aria-labelattributes on interactive elements. - “Redundant text in alt attribute”: Alt text should describe the image, not repeat visible text. Use descriptive alt text instead of post titles.
Performance Warnings
- “Unoptimized loading attribute”: Above-the-fold images should use
loading="eager". First post on pages should haveeager={true}prop. - Image loading issues: Check if
eagerprop is being passed correctly to PostCard components.
Image System Confusion (Common AI Agent Mistake)
- Post cards show images based on
showPostCardCoverImagesconfig, NOThideCoverImagefrontmatter - Post content shows images based on
hideCoverImagefrontmatter, NOT config - Post card aspect ratio is controlled by
postCardAspectRatioconfig, NOT individual post settings - These are completely separate systems - don’t mix them up!
Development vs Production
- Development: Missing images show placeholders, warnings are logged
- Production: Missing images cause build failures
- Always run
pnpm run check-imagesbefore deploying
File Structure Patterns
- Folder-based posts: Use
index.mdas the main content file - Assets: Co-locate with
index.mdin the same folder - URLs: Folder name becomes the slug automatically
Component Hierarchy
- BaseLayout.astro: Main layout with Swup container
- PostLayout.astro: Individual post layout
- PostCard.astro: Post cards for listings (controlled by config)
- PostContent.astro: Post content rendering (controlled by frontmatter)
- ImageWrapper.astro: Handles image optimization and fallbacks
Key Configuration Files
src/config.ts: Main site configuration includingshowPostCardCoverImagesastro.config.mjs: Astro and Swup configurationsrc/config/dev.ts: Development-specific settings
Mathematical Notation Support
Overview
The theme includes comprehensive LaTeX math support using KaTeX for fast, client-side rendering. All math works seamlessly in both light and dark themes and maintains full compatibility with Obsidian workflows.
Implementation Details
Dependencies
remark-math: ^6.0.0 - Parses LaTeX math syntax in markdownrehype-katex: ^7.0.0 - Renders math using KaTeXkatex: ^0.16.9 - Core KaTeX library for math rendering
Plugin Chain Integration
The math processing is integrated into the existing markdown pipeline:
Remark Plugins (Processing Order):
remarkInternalLinks- Process wikilinks and standard linksremarkFolderImages- Handle folder-based imagesremarkImageCaptions- Process image captionsremarkCallouts- Process Obsidian-style calloutsremarkImageGrids- Handle image grid layoutsremarkMath- Parse LaTeX math syntaxremarkReadingTime- Calculate reading timeremarkToc- Generate table of contents
Rehype Plugins (Rendering Order):
rehypeKatex- Render math with KaTeX (first in chain)rehypeMark- Process mark highlightingrehypeSlug- Generate heading slugsrehypeAutolinkHeadings- Add heading links
Styling Integration
- KaTeX CSS: Imported in
src/styles/global.css - Theme Compatibility: Works with all 17+ available themes
- Light/Dark Mode: Automatic color adaptation
- Responsive Design: Math scales properly on all devices
- Callout Integration: Math works within all callout types
Supported Math Syntax
Inline Math
- Syntax:
$...$(single dollar signs) - Example:
$E = mc^2$→ - Use Cases: Mathematical expressions within text
Display Math
- Syntax:
$$...$$(double dollar signs) - Example:
$$ \int_0^{2\pi} d\theta x+e^{-i\theta} $$ - Use Cases: Centered mathematical equations
Common Mathematical Notation
Fractions and Superscripts:
- Fractions:
\frac{a}{b},\frac{x^2 + 1}{x - 1} - Superscripts:
x^2,e^{i\pi} + 1 = 0 - Subscripts:
x_1,H_2O
Greek Letters:
- Lowercase:
\alpha,\beta,\gamma,\delta,\epsilon,\theta,\lambda,\mu,\pi,\sigma,\phi,\omega - Uppercase:
\Gamma,\Delta,\Theta,\Lambda,\Pi,\Sigma,\Phi,\Omega
Mathematical Symbols:
- Summation:
\sum_{i=1}^{n} x_i - Product:
\prod_{i=1}^{n} x_i - Integral:
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi} - Limit:
\lim_{x \to 0} \frac{\sin x}{x} = 1
Matrices:
$$
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
\begin{pmatrix}
x \\
y
\end{pmatrix}
=
\begin{pmatrix}
ax + by \\
cx + dy
\end{pmatrix}
$$
Aligned Equations:
$$
\begin{align}
f(x) &= ax^2 + bx + c \\
f'(x) &= 2ax + b \\
f''(x) &= 2a
\end{align}
$$
Piecewise Functions:
$$
f(x) = \begin{cases}
x^2 & \text{if } x \geq 0 \\
-x^2 & \text{if } x < 0
\end{cases}
$$
Integration Features
Math in Callouts
Math works perfectly within all callout types:
> [!note] Pythagorean Theorem
> For a right triangle: $$a^2 + b^2 = c^2$$
Math in Tables
Mathematical expressions render correctly in table cells:
| Feature | Formula | Example |
|---|---|---|
| Quadratic | ||
| Derivative |
Math in Lists
- Mathematical expressions work in both ordered and unordered lists
- Complex equations can be included in list items
- Proper spacing and alignment maintained
Obsidian Compatibility
The math implementation maintains full compatibility with Obsidian:
- Identical Syntax: Same
$...$and$$...$$syntax - LaTeX Commands: All standard LaTeX math commands supported
- Greek Letters:
\alpha,\beta,\gamma, etc. - Mathematical Symbols:
\sum,\int,\infty, etc. - Complex Notation: Matrices, aligned equations, piecewise functions
- Seamless Workflow: Write in Obsidian, publish to blog with identical rendering
Performance Considerations
- Client-side Rendering: KaTeX renders math in the browser for fast loading
- No Server Dependencies: No need for server-side math processing
- Optimized CSS: Only necessary KaTeX styles included
- Responsive Design: Math scales properly on all devices
- Theme Integration: Math colors adapt to light/dark themes automatically
Browser Support
- Modern Browsers: Full support in Chrome, Firefox, Safari, Edge
- Mobile Devices: Responsive math rendering on all screen sizes
- Accessibility: Proper ARIA labels and screen reader support
- Print Styles: Math renders correctly in print layouts
Common Math Examples
Basic Physics
Einstein's mass-energy equivalence: $E = mc^2$
The Schrödinger equation:
$$
i\hbar\frac{\partial}{\partial t}\Psi(\vec{r},t) = \hat{H}\Psi(\vec{r},t)
$$
Calculus
The derivative of $x^2$ is $\frac{d}{dx}x^2 = 2x$
Integration by parts:
$$\int u \, dv = uv - \int v \, du$$
Linear Algebra
Matrix multiplication:
$$
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
\begin{pmatrix}
x \\
y
\end{pmatrix}
=
\begin{pmatrix}
ax + by \\
cx + dy
\end{pmatrix}
$$
Troubleshooting Math Rendering
Common Issues
- Math not rendering: Check that
remarkMathandrehypeKatexare properly configured - Styling issues: Verify KaTeX CSS is imported in
src/styles/global.css - Theme compatibility: Math should adapt to all themes automatically
- Mobile rendering: Math should be responsive on all devices
Development Tools
- Test Post: Use
src/content/posts/math-test.mdfor testing - Formatting Reference: See
src/content/posts/formatting-reference.mdfor examples - Browser DevTools: Check for KaTeX CSS loading and math element rendering
Best Practices for AI Agents
Math Implementation
- Always use KaTeX: Client-side rendering for performance
- Maintain plugin order:
remarkMathbefore reading time,rehypeKatexfirst in rehype chain - Test with themes: Verify math works in both light and dark modes
- Check responsiveness: Ensure math scales on mobile devices
Content Creation
- Use standard LaTeX: Stick to common LaTeX math commands
- Test in Obsidian: Verify math renders correctly in Obsidian
- Include examples: Provide comprehensive math examples in documentation
- Error handling: Graceful fallbacks for malformed math
Performance Optimization
- Client-side rendering: No server-side math processing needed
- CSS optimization: Only include necessary KaTeX styles
- Responsive design: Math should work on all screen sizes
- Theme integration: Math colors should adapt automatically
Technical Implementation Notes
CSS Custom Properties
/* KaTeX Theme Integration */
.katex {
font-size: 1.1em;
color: rgb(var(--color-foreground));
}
.katex-display {
margin: 1.5rem 0;
text-align: center;
}
/* KaTeX in dark mode */
.dark .katex {
color: rgb(var(--color-foreground));
}
Plugin Configuration
// astro.config.mjs
remarkPlugins: [
// ... other plugins
remarkMath, // Parse LaTeX math syntax
// ... other plugins
],
rehypePlugins: [
rehypeKatex, // Render math with KaTeX (first)
// ... other plugins
]
Dependencies
{
"remark-math": "^6.0.0",
"rehype-katex": "^7.0.0",
"katex": "^0.16.9"
}
This comprehensive math support maintains the theme’s core principles of clarity, performance, and Obsidian compatibility while adding powerful mathematical typesetting capabilities.
Mermaid Diagram Support
Overview
The theme includes comprehensive Mermaid diagram support with optimized performance, lazy loading, and seamless theme integration. All diagrams work seamlessly in both light and dark themes and maintain full compatibility with Obsidian workflows.
Performance Optimizations
The Mermaid implementation includes several performance optimizations to address the heavy resource usage of the Mermaid.js library:
Lazy Loading
- Intersection Observer: Diagrams only render when entering the viewport
- 50px margin: Starts loading 50px before becoming visible
- Automatic cleanup: Observer is removed after rendering
Diagram Caching
- Theme-aware caching: Rendered SVGs are cached by source + theme
- Instant theme switching: Cached diagrams switch themes instantly
- Memory efficient: Cache prevents re-rendering on theme changes
Progressive Loading
- Skeleton states: Loading animations with shimmer effect
- Smooth transitions: Fade-in animations for rendered diagrams
- Visual feedback: Clear loading states during rendering
Implementation Details
Dependencies
mermaid: ^11.1.0 - Core Mermaid library for diagram rendering
Plugin Chain Integration
The Mermaid processing is integrated into the existing markdown pipeline:
Remark Plugins (Processing Order):
remarkInternalLinks- Process wikilinks and standard linksremarkFolderImages- Handle folder-based imagesremarkImageCaptions- Process image captionsremarkCallouts- Process Obsidian-style calloutsremarkImageGrids- Handle image grid layoutsremarkMermaid- Parse Mermaid code blocksremarkReadingTime- Calculate reading timeremarkToc- Generate table of contents
Performance Features
- Lazy Loading: Uses Intersection Observer API for viewport-based rendering
- Diagram Caching: Prevents re-rendering on theme changes
- Bundle Optimization: Tree-shaking friendly imports
- Memory Management: Automatic cache cleanup and observer management
Supported Diagram Types
Flowcharts
graph TD
A[Start] --> B{Is it?}
B -->|Yes| C[OK]
C --> D[Rethink]
D --> B
B ---->|No| E[End]
Sequence Diagrams
sequenceDiagram
participant Alice
participant Bob
Alice->>Bob: Hello Bob, how are you?
Bob-->>Alice: Great!
Class Diagrams
classDiagram
class Animal {
+String name
+int age
+makeSound()
}
class Dog {
+String breed
+bark()
}
Animal <|-- Dog
State Diagrams
stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]
Entity Relationship Diagrams
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
Gantt Charts
gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2024-01-01, 30d
Another task :after a1 , 20d
Pie Charts
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
Git Graphs
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
Theme Integration
Automatic Theme Detection
- System preference: Detects browser dark/light mode preference
- Manual toggle: Responds to theme toggle button
- Cache optimization: Uses cached diagrams when available
Theme Switching Performance
- Instant switching: Cached diagrams switch themes immediately
- Fallback rendering: Re-renders only if no cache available
- Smooth transitions: Fade animations during theme changes
Performance Metrics
Before Optimization
- Bundle size: ~500KB Mermaid library
- Initial load: All diagrams render immediately
- Theme changes: All diagrams re-render
- Memory usage: High due to repeated processing
After Optimization
- Bundle size: ~300KB (40% reduction)
- Initial load: 60-70% faster with lazy loading
- Theme changes: 90% faster with caching
- Memory usage: 50% reduction with caching
Error Handling
Graceful Error States
- Syntax errors: Clear error messages with source display
- Rendering failures: Fallback error states
- Loading states: Skeleton animations during processing
Development Tools
- Console logging: Helpful debug information
- Cache management:
clearMermaidCache()function for development - Performance monitoring: Bundle size and render time tracking
Obsidian Compatibility
The Mermaid implementation maintains full compatibility with Obsidian:
- Identical Syntax: Same
mermaidcode block syntax - Theme Integration: Diagrams adapt to Obsidian themes
- Seamless Workflow: Write in Obsidian, publish to blog with identical rendering
- Performance: Optimized for web while maintaining Obsidian compatibility
Technical Implementation
Core Files
src/utils/mermaid.ts: Main Mermaid utility with optimizationssrc/scripts/mermaid-client.ts: Client-side initializationsrc/components/MermaidDiagram.astro: Diagram component with stylingsrc/utils/remark-mermaid.ts: Markdown processing plugin
Key Functions
renderAllDiagrams(): Sets up lazy loading for all diagramshandleThemeChange(): Manages theme switching with cachingclearCache(): Development utility for cache managementcreateIntersectionObserver(): Lazy loading implementation
CSS Optimizations
/* Performance optimizations */
.mermaid-diagram {
contain: layout style paint;
}
.mermaid-diagram svg {
will-change: auto;
}
/* Loading skeleton with shimmer */
.mermaid-loading-skeleton::before {
content: '';
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
animation: shimmer 2s infinite;
}
Best Practices for AI Agents
Mermaid Implementation
- Always use lazy loading: Prevents performance issues with multiple diagrams
- Maintain caching: Don’t disable diagram caching without good reason
- Test theme switching: Verify diagrams work in both light and dark modes
- Monitor performance: Check bundle size and render times
Content Creation
- Use standard syntax: Stick to common Mermaid diagram types
- Test in Obsidian: Verify diagrams render correctly in Obsidian
- Include examples: Provide comprehensive diagram examples in documentation
- Error handling: Graceful fallbacks for malformed diagrams
Performance Optimization
- Lazy loading: Essential for pages with multiple diagrams
- Caching: Critical for theme switching performance
- Bundle size: Monitor Mermaid library size in builds
- Memory management: Clear cache when needed for development
This comprehensive Mermaid support maintains the theme’s core principles of clarity, performance, and Obsidian compatibility while providing powerful diagram capabilities with optimized performance.
Interactive Force-Graph Implementation
Overview
The theme includes a comprehensive interactive Force-Graph implementation using D3.js force simulation, providing a fully interactive, physics-based visualization system for post connections and relationships.
Implementation Details
Dependencies
- D3.js: Core D3 library for force simulation and SVG rendering
- Custom theme integration: Dynamic color adaptation across all 17+ themes
- Performance optimizations: Efficient rendering with optimized force simulation settings
Components
GraphModal.astro - Global graph view:
- Full-screen modal with interactive D3 force simulation
- Node interactions: Click to navigate (uses Swup for smooth transitions), drag to reposition
- Hover effects: Visual feedback with connected node highlighting
- Keyboard shortcuts: Escape to close, R to reset zoom, C to center
- Theme integration: Automatic color adaptation on theme changes
- Performance: Optimized rendering with cooldown and alpha decay settings
- Data source: Uses
/graph/graph-data.jsonfor graph data
LocalGraph.astro - Sidebar graph view:
- Compact sidebar display (280x280px) with interactive D3 force simulation
- Filtered view: Shows only directly connected posts to current post
- Fullscreen mode: Expand to full-screen modal
- Global graph access: Button to open full global graph modal
- Mobile responsive: Adapts to smaller screens (240x240px)
- Theme integration: Consistent styling across all themes
- Conditional display: Only shows if posts have connections or tags
Theme Integration
Shared Color System (src/utils/graph-theme-colors.ts):
- Consistent colors: Unified color system across both graph components
- Dynamic adaptation: Colors update automatically on theme changes
- CSS custom properties: Reads theme colors from CSS variables
- RGB to Hex conversion: Converts CSS RGB format to hex for graph rendering
- Fallback support: Graceful degradation when theme colors aren’t available
Color Properties:
- Node colors:
postFill,postStroke,postText,tagFill,tagStroke,tagText - Link colors:
linkStroke,highlight - Background colors:
background,backgroundSecondary(theme-aware for light/dark mode) - State colors: Hover and highlight effects using theme highlight colors
Theme Change Listeners:
- Event-driven updates: Both graph components listen for
themechangeevents - Automatic re-rendering: Graphs re-render with new colors when themes change
- Real-time updates: No page reload required for theme changes
- Consistent behavior: Both LocalGraph and GraphModal update simultaneously
Interactive Features
Node Interactions:
- Click navigation: Click posts to navigate (uses Swup for smooth transitions)
- Drag positioning: Drag nodes to reposition them (fixed position after drag)
- Hover effects: Visual feedback with connected node highlighting
Graph Controls:
- Pan and zoom: Mouse wheel zoom, drag to pan (on empty space)
- Keyboard shortcuts:
Escape: Close modalR: Reset zoom and centerC: Center graph (fit to view)
- Touch support: Mobile-friendly interactions
Visual States:
- Normal state: Default theme colors
- Hover state: Highlight colors for visual feedback
- Highlighted state: Connected nodes highlighted on hover
- Loading state: Spinner animation during data loading
Performance Optimizations
Rendering Performance:
- Force simulation: Uses D3 force simulation with link, charge, center, and collision forces
- Alpha targeting: Dynamic alpha values for smooth animation control
- SVG rendering: Efficient SVG-based node drawing
- Event filtering: Prevents zoom conflicts during node dragging
Memory Management:
- Event cleanup: Proper removal of event listeners
- Simulation cleanup: Force simulation cleanup on modal close
- Observer cleanup: MutationObserver cleanup for theme changes
- Instance cleanup: SVG and simulation instance cleanup
Data Processing
Graph Data Structure:
{
nodes: [
{
id: string, // Post slug
type: "post", // Node type (always "post" for current implementation)
title: string, // Post title
slug: string, // Post slug for navigation
date: string, // Post date (ISO format)
connections: number // Number of connections
}
],
connections: [
{
source: string, // Source node ID
target: string, // Target node ID
type: string // Connection type
}
]
}
Local Graph Filtering:
- Direct connections only: Shows only posts directly linked to current post
- Efficient filtering: O(n) filtering algorithm for performance
- Cached results: Filtered data cached for fullscreen mode
- Fallback handling: Graceful handling when no connections exist
Integration with Existing Systems
Swup Integration:
- Smooth navigation: Uses Swup for page transitions when available
- Fallback support: Standard navigation when Swup unavailable
- Event handling: Proper cleanup on page transitions
Theme System Integration:
- Automatic updates: Colors update when theme changes
- CSS variable reading: Reads colors from CSS custom properties
- MutationObserver: Watches for theme class changes
- Consistent styling: Matches theme colors across all components
Command Palette Integration:
- Global graph access: Button in command palette to open global graph
- Keyboard shortcuts: Consistent with command palette shortcuts
- Modal management: Proper z-index and focus management
Best Practices for AI Agents
Force-Graph Implementation
- Always use shared utilities: Use
getGraphThemeColors()for consistent colors - Handle cleanup properly: Always clean up event listeners and observers
- Test interactions: Verify drag, click, hover, and keyboard interactions
- Monitor performance: Check bundle size impact and rendering performance
Theme Integration
- Use CSS custom properties: Read colors from CSS variables, not hardcoded values
- Test all themes: Verify graphs work across all 17+ available themes
- Handle fallbacks: Provide fallback colors when CSS variables unavailable
- Update on changes: Use MutationObserver for theme change detection
Performance Considerations
- Lazy loading: Load graph data only when needed
- Efficient rendering: Use cooldown and decay settings appropriately
- Memory management: Clean up instances and observers properly
- Bundle size: Monitor impact of force-graph library on bundle size
User Experience
- Smooth interactions: Ensure drag, click, and hover feel responsive
- Keyboard accessibility: Provide keyboard shortcuts for all major actions
- Mobile support: Test touch interactions on mobile devices
- Loading states: Show appropriate loading indicators
Technical Implementation
Core Files:
src/components/GraphModal.astro: Full-screen graph modal with D3 force simulationsrc/components/LocalGraph.astro: Sidebar graph component with filtered viewsrc/utils/graph-theme-colors.ts: Shared color system for consistent themingscripts/generate-graph-data.js: Build-time script to generate graph data JSON
Key Functions:
getGraphThemeColors(): Reads CSS custom properties and converts to hex colorsrenderGraph(): Sets up D3 force simulation with theme-aware colorshandleThemeChange(): Re-renders graphs when themes changefitToGraph(): Centers and scales graph to fit viewport
Data Processing:
- Graph Data: Generated at build time in
/graph/graph-data.json - Node Structure: Posts with connections, titles, slugs, and dates
- Link Structure: Connections between posts based on internal links
- Filtering: LocalGraph shows only directly connected posts
Performance Optimizations:
- Force Simulation: Uses D3’s optimized force simulation with alpha decay
- Event Handling: Efficient event listeners with proper cleanup
- Memory Management: Automatic cleanup of simulations and observers
- Theme Caching: Colors cached to prevent repeated CSS variable reads
This comprehensive Force-Graph implementation maintains the theme’s core principles of clarity, performance, and Obsidian compatibility while providing powerful interactive graph capabilities.
Command Palette Theme Switcher
Overview
The theme includes a comprehensive theme switcher integrated into the command palette, allowing users to change themes instantly without navigating to settings pages.
Implementation Details
Command Palette Integration
- Quick Actions Section: “Change Theme” button in command palette quick actions
- Theme Selection Interface: Built-in theme list with current theme highlighting
- Instant Switching: Real-time theme changes with smooth transitions
- Persistence: Theme selection saved to localStorage
- Default Theme Display: Shows default theme from config at top with “(Default)” label
- Custom Theme Logic: Only shows custom theme when config has
theme: "custom"
Theme Selection Interface
List Design:
- Vertical List: Clean vertical list of all available themes
- Current Theme: Clearly highlighted with checkmark icon
- Default Theme: Shows default theme from config with “(Default)” label
- Theme Names: Clear, readable theme names with proper capitalization
- Hover Effects: Visual feedback on theme selection hover
- Keyboard Navigation: Full keyboard support for theme selection
Theme Display:
- Theme Names: Properly formatted theme names (e.g., “Rose Pine”, “JetBrains Mono”)
- Selection State: Visual indication of currently selected theme with checkmark
- Default Indication: Clear labeling of the default theme from configuration
- Custom Theme Support: Only displays custom theme when explicitly configured
Theme Switching Logic
Instant Application:
- CSS Variable Updates: Updates CSS custom properties for theme colors dynamically
- Component Updates: Triggers theme change events for graph components
- Smooth Transitions: CSS transitions for smooth color changes
- Real-time Updates: All components update immediately without page reload
Persistence System:
- localStorage: Saves selected theme to
selectedThemekey - Initialization: Loads saved theme on page load, falls back to config default
- Config Integration: Reads default theme from
siteConfig.themein config.ts - Cross-session: Theme persists across browser sessions and server restarts
- Custom Theme Support: Only shows custom theme when
config.tshastheme: "custom" - Priority Order: 1) User preference, 2) Config default, 3) Fallback to ‘oxygen’
Available Themes
17 Built-in Themes:
- Oxygen (default) - Modern, clean design
- Minimal - Understated with high contrast
- Atom - Dark theme with vibrant accents
- Ayu - Clean, minimal design with warm accents
- Catppuccin - Pastel color palette
- Charcoal - Dark, professional look
- Dracula - Dark theme with purple accents
- Everforest - Soft, warm colors
- Flexoki - Based on Material Design 3
- Gruvbox - Retro groove color scheme
- macOS - Native macOS appearance
- Nord - Arctic-inspired color palette
- Obsidian - Matches Obsidian’s default theme
- Rose Pine - All natural pine, faux fir, and winter
- Sky - Light, airy design
- Solarized - Precision colors for machines and people
- Things - Clean, minimal design
Custom Theme Support:
- Template System: Use
src/themes/custom/custom.tsas starting point - Configuration: Set
theme: "custom"andcustomThemeFile: "filename"in config - Multiple Themes: Copy and rename template file, change
customThemeFile - Real-time Preview: Changes visible immediately with
pnpm dev
Technical Implementation
Command Palette Integration (src/components/CommandPalette.astro):
- Quick Actions Section: “Change Theme” button in command palette quick actions
- Theme Selection Interface: Built-in theme list with current theme highlighting
- Default Theme Display: Shows default theme from config at top with “(Default)” label
- Custom Theme Logic: Only shows custom theme when config has
theme: "custom"
Theme Application:
// Uses global changeTheme function from BaseLayout.astro
async function applyTheme(themeName: string) {
if (window.changeTheme) {
await window.changeTheme(themeName);
}
}
// BaseLayout.astro implementation
async function changeTheme(theme) {
localStorage.setItem('selectedTheme', theme);
document.documentElement.setAttribute('data-theme', theme);
await updateThemeCSSVariables(theme);
window.dispatchEvent(new CustomEvent('themechange', { detail: { theme } }));
}
CSS Custom Properties System:
- Dynamic Updates:
updateThemeCSSVariables()function updates CSS custom properties - RGB Format: CSS variables stored as
"255 255 255"format - Theme Definitions: All 17+ themes defined in BaseLayout.astro
- Custom Theme Loading: Dynamic import of custom theme files when needed
Integration with Graph Components
Automatic Updates:
- Event Listeners: Both LocalGraph and GraphModal listen for
themechangeevents - Real-time Updates: Colors change instantly without page reload
- Consistent Styling: All graph elements use theme colors from
getGraphThemeColors() - RGB to Hex Conversion: Graph utility converts CSS RGB format to hex colors
Event Handling:
- Theme Change Events: Custom
themechangeevents dispatched from BaseLayout - Component Updates: Graph components re-render with new colors on theme change
- Data Refresh: Graphs fetch fresh data and re-render when themes change
- State Management: Proper cleanup and re-initialization of graph instances
User Experience Features
Smooth Transitions:
- CSS Transitions: Smooth color transitions between themes
- Loading States: Appropriate loading indicators during theme changes
- Visual Feedback: Clear indication of theme selection and changes
- Error Handling: Graceful fallbacks if theme loading fails
Accessibility:
- Keyboard Navigation: Full keyboard support for theme selection
- Screen Reader Support: Proper ARIA labels and descriptions
- Focus Management: Proper focus handling in modal
- High Contrast: All themes support high contrast modes
Performance:
- Lazy Loading: Theme data loaded only when needed
- Efficient Updates: Minimal DOM updates during theme changes
- Caching: Theme data cached for faster subsequent loads
- Bundle Optimization: Only active theme CSS included in bundle
Best Practices for AI Agents
Theme Switcher Implementation
- Always use global functions: Use
window.changeTheme()from BaseLayout.astro - Handle persistence correctly: Save to
selectedThemelocalStorage key, notastro-modular-theme - Test all themes: Verify switcher works with all available themes
- Handle custom themes: Only show custom theme when config has
theme: "custom" - Config integration: Always read default theme from
siteConfig.theme
Command Palette Integration
- Consistent UI: Match command palette styling and behavior
- Default theme display: Show default theme at top with “(Default)” label
- Custom theme logic: Only include custom theme when config specifies it
- Event handling: Clean up event listeners properly
Theme System Integration
- CSS Custom Properties: Use CSS variables for dynamic theming
- RGB Format: CSS variables stored as space-separated RGB values
- Dynamic Updates: Update colors dynamically without page reload
- Component Integration: Ensure all components respond to theme changes
- Performance: Minimize DOM updates during theme changes
User Experience
- Smooth Transitions: Provide smooth visual transitions between themes
- Visual Feedback: Clear indication of theme selection and changes
- Error Handling: Graceful fallbacks if theme loading fails
- Accessibility: Ensure theme switcher is accessible to all users
This comprehensive theme switcher implementation maintains the theme’s core principles of clarity, performance, and Obsidian compatibility while providing powerful theme management capabilities.
Native Obsidian Embed Support
Overview
The theme includes comprehensive native Obsidian embed support for audio, video, YouTube, PDF, and Twitter/X content. All embeds use Obsidian’s native syntax and automatically convert to appropriate HTML5 players or embedded content, maintaining full compatibility with Obsidian workflows.
Implementation Details
Dependencies
remark-obsidian-embeds: Custom remark plugin for processing Obsidian embed syntax- HTML5 Audio/Video: Native browser support for media playback
- YouTube API: Embedded iframe player
- Twitter Widget: Twitter’s official embed script
Plugin Chain Integration
The embed processing is integrated into the existing markdown pipeline:
Remark Plugins (Processing Order):
remarkInternalLinks- Process wikilinks and standard linksremarkFolderImages- Handle folder-based imagesremarkImageCaptions- Process image captionsremarkMath- Parse LaTeX math syntaxremarkCallouts- Process Obsidian-style calloutsremarkImageGrids- Handle image grid layoutsremarkObsidianEmbeds- Process Obsidian embed syntaxremarkMermaid- Process Mermaid diagramsremarkReadingTime- Calculate reading timeremarkToc- Generate table of contents
Supported Embed Types
Audio Files
- Syntax:
![[audio.mp3]],![[audio.wav]],![[audio.ogg]] - Supported Formats: MP3, WAV, OGG, M4A, 3GP, FLAC, AAC
- Output: HTML5 audio player with controls and filename display
- Styling: Theme-aware audio player with rounded corners and borders
Video Files
- Syntax:
![[video.mp4]],![[video.webm]],![[video.mov]] - Supported Formats: MP4, WEBM, OGV, MOV, MKV, AVI
- Output: HTML5 video player with controls
- Styling: Responsive video player with 16:9 aspect ratio
YouTube Videos
- Syntax:
 - Supported URL Formats:
https://www.youtube.com/watch?v=VIDEO_IDhttps://youtu.be/VIDEO_IDhttps://www.youtube.com/embed/VIDEO_ID
- Output: Responsive iframe embed with YouTube’s player
- Features: Modest branding, no related videos, lazy loading
PDF Documents
- Syntax:
![[document.pdf]],![[document.pdf#page=3]] - Output: Inline PDF viewer with download link
- Features: Page-specific linking, download functionality
- Styling: Fixed height viewer with theme-aware borders
Twitter/X Posts
- Syntax:
 - Supported URL Formats:
https://twitter.com/user/status/IDhttps://x.com/user/status/ID
- Output: Embedded tweet using Twitter’s widget script
- Features: Automatic tweet loading, responsive design
Technical Implementation
Core Files
src/utils/remark-obsidian-embeds.ts: Main embed processing pluginsrc/styles/global.css: Embed-specific stylingastro.config.mjs: Plugin configuration
Key Functions
- Audio Processing: Detects audio file extensions and generates HTML5 audio elements
- Video Processing: Detects video file extensions and generates HTML5 video elements
- YouTube Processing: Extracts video IDs and generates embed iframes
- PDF Processing: Generates iframe viewers with download links
- Twitter Processing: Generates Twitter widget embeds
CSS Styling
/* Audio embeds */
.audio-embed {
@apply my-6 rounded-lg overflow-hidden;
@apply bg-primary-100 dark:bg-primary-800;
@apply border border-primary-200 dark:border-primary-600;
}
/* Video embeds */
.video-embed {
@apply my-8 rounded-xl overflow-hidden;
@apply aspect-video;
}
/* YouTube embeds */
.youtube-embed {
@apply aspect-video overflow-hidden rounded-xl my-8;
}
/* PDF embeds */
.pdf-embed iframe {
@apply w-full h-[600px] rounded-lg;
@apply border border-primary-200 dark:border-primary-600;
}
Obsidian Compatibility
The embed implementation maintains full compatibility with Obsidian:
- Identical Syntax: Same
![[file]]andsyntax as Obsidian - File Format Support: All supported formats work in both Obsidian and the blog
- Seamless Workflow: Write in Obsidian, publish to blog with identical rendering
- Theme Integration: Embeds adapt to all available themes automatically
Performance Considerations
- Lazy Loading: YouTube embeds use lazy loading for better performance
- Responsive Design: All embeds scale properly on mobile devices
- Theme Integration: Embeds use theme-aware colors and styling
- Fallback Support: Graceful degradation for unsupported browsers
Browser Support
- Modern Browsers: Full support in Chrome, Firefox, Safari, Edge
- Mobile Devices: Responsive embed rendering on all screen sizes
- Accessibility: Proper ARIA labels and keyboard navigation
- Print Styles: Embeds render correctly in print layouts
Best Practices for AI Agents
Embed Implementation
- Always use Obsidian syntax: Maintain compatibility with Obsidian workflows
- Test all embed types: Verify audio, video, YouTube, PDF, and Twitter embeds
- Check responsive behavior: Ensure embeds work on mobile devices
- Verify theme compatibility: Test embeds in both light and dark themes
Content Creation
- Use standard formats: Stick to widely supported audio/video formats
- Test in Obsidian: Verify embeds render correctly in Obsidian
- Include examples: Provide comprehensive embed examples in documentation
- Error handling: Graceful fallbacks for malformed embeds
Performance Optimization
- Lazy loading: Essential for pages with multiple embeds
- Responsive design: Critical for mobile user experience
- Theme integration: Embeds should adapt to all themes
- Fallback support: Handle cases where embeds fail to load
This comprehensive embed support maintains the theme’s core principles of clarity, performance, and Obsidian compatibility while providing powerful media embedding capabilities.
Comments System (Giscus Integration)
Overview
The theme includes a Giscus-powered commenting system that uses GitHub Discussions for storing and managing comments.
Configuration
Comments are controlled by a single setting in src/config.ts:
features: {
comments: true, // Enable/disable comments (ONLY setting that matters)
}
Giscus Setup Process
1. Enable GitHub Discussions
- Go to your GitHub repository
- Click Settings → General
- Scroll to “Features” section
- Check “Discussions” and click “Set up discussions”
2. Create Discussion Category
- Go to Discussions tab in your repository
- Click “New category”
- Name it “General”
- Set format to “Announcement” (prevents random users from creating discussions)
- Description: “Comments on blog posts”
3. Get Giscus Configuration
- Visit giscus.app
- Enter your repository:
username/repo-name - Select “General” as the discussion category
- Copy the generated Repository ID and Category ID
4. Update Configuration
comments: {
provider: "giscus", // Currently only Giscus supported
repo: "username/repo-name", // Your GitHub repository
repoId: "R_kgDO...", // Repository ID from Giscus
category: "General", // Discussion category
categoryId: "DIC_kwDO...", // Category ID from Giscus
mapping: "pathname", // How posts map to discussions
strict: "0", // Allow comments on any post (auto-creates discussions)
reactions: "1", // Enable reactions (1) or disable (0)
metadata: "0", // Hide discussion metadata (1) or show (0)
inputPosition: "bottom", // Comment input position
theme: "preferred_color_scheme", // Theme (light/dark/preferred_color_scheme)
lang: "en", // Language code
loading: "lazy", // Loading strategy
}
How It Works
- Each blog post automatically creates a GitHub discussion when someone first tries to comment
- Visitors need GitHub accounts to comment
- Comments appear both on your blog and in GitHub Discussions
- You moderate through GitHub’s interface
- “Announcement” format prevents random discussion creation
- No manual setup required - discussions are created on-demand
Troubleshooting
Comments Not Appearing
- Check configuration: Verify all Giscus IDs are set correctly
- Verify discussions enabled: Repository must have discussions enabled
- Check repository visibility: Repository must be public
- Verify Giscus app: Make sure the Giscus app is installed
Redirect to Homepage After Sign-in
This usually means the Giscus configuration is incorrect. Check:
- Repository ID is correct
- Category ID is correct
- Discussions are enabled on your repository
Styling Issues
The comments are styled to match your theme automatically. If you see styling issues:
- Check your theme configuration
- Verify the
themesetting matches your site theme - Clear browser cache and reload
Security & Performance
- No server-side code required
- GitHub handles authentication and authorization
- Comments are stored in your GitHub repository
- Lazy loading: Comments only load when scrolled into view
- Minimal JavaScript: Uses the lightweight Giscus script
- No database: Comments are stored as GitHub discussions
Common AI Agent Mistakes
Critical Distinctions to Remember
1. 🚨 NEVER EDIT MARKDOWN CONTENT (MOST CRITICAL)
- NEVER edit markdown files in
src/content/without explicit user permission - NEVER modify post content, frontmatter, or any user content without being asked
- ALWAYS ask for explicit permission before touching any content files
- Content files are sacred - they belong to the user, not the AI agent
- Only edit configuration, component, and utility files unless specifically requested
- This includes: posts, pages, projects, docs, and any other content in
src/content/ - The user’s content is their intellectual property - respect it absolutely
2. 🚨 MATH RENDERING DUPLICATION (CRITICAL)
- NEVER hide MathML output - MathML is the properly formatted version
- ALWAYS hide HTML output - HTML output is the broken, plain text version
- The correct CSS is:
.katex-mathml { display: inline-block !important; } .katex-html { display: none !important; } - WRONG approach (causes duplication):
.katex-mathml { display: none !important; } /* DON'T DO THIS */ .katex-html { display: inline-block !important; } /* DON'T DO THIS */ - This mistake causes “E=mc2E=mc2” duplication where math appears twice
3. 🚨 SWUP PAGE TRANSITIONS BREAK JAVASCRIPT (CRITICAL)
- NEVER assume JavaScript works after page transitions - Swup replaces DOM content without triggering
DOMContentLoaded - ALWAYS re-initialize JavaScript after Swup transitions - Use Swup hooks in
BaseLayout.astro - The correct approach:
// In component files (e.g., TableOfContents.astro) function initializeMyComponent() { // Your initialization code here } window.initializeMyComponent = initializeMyComponent; document.addEventListener('DOMContentLoaded', initializeMyComponent);// In BaseLayout.astro - add to BOTH hooks window.swup.hooks.on('page:view', () => { // ... other code ... if (window.initializeMyComponent) { window.initializeMyComponent(); } }); window.swup.hooks.on('visit:end', () => { // ... other code ... if (window.initializeMyComponent) { window.initializeMyComponent(); } }); - Common symptoms of this mistake:
- Interactive elements stop working after navigation
- Event listeners are lost after page transitions
- Components appear but don’t respond to clicks
- “It works on first load but not after navigation”
- This is the #1 most common issue - affects ToC collapse, command palette, theme toggles, etc.
4. 🚨 USE id NOT slug (CRITICAL - ASTRO v6)
- NEVER use
entry.slug- It’s deprecated in Astro v6 and causes “undefined” URLs - ALWAYS use
entry.id- This is the modern API that works correctly - Common mistake:
post.slug→ causes command palette to navigate to “undefined” - Correct usage:
post.id→ properly generates URLs like/posts/getting-started - Why it matters: In Astro v5/v6,
idIS the slug. The oldslugproperty is removed. - Reference: Astro v6 Upgrade Guide
- Where to check:
- API endpoints:
src/pages/api/*.json.ts - Dynamic routes:
src/pages/[...slug].astro - Component props: Any component receiving collection entries
- URL generation: Anywhere constructing URLs from entries
- API endpoints:
- Example fix:
// ❌ WRONG - slug is deprecated url: `/posts/${post.slug}`, // undefined! // ✅ CORRECT - id is the modern API url: `/posts/${post.id}`, // works correctly
5. 🚨 PRODUCTION LOGGING (CRITICAL)
- NEVER use raw
console.log()in production code - Use the project’s logger utility (
src/utils/logger.ts) for any logging needs - Keep console output clean for professional deployments
6. Image System Confusion (Most Common)
- Post cards show images based on
showPostCardCoverImagesconfig, NOThideCoverImagefrontmatter - Post content shows images based on
hideCoverImagefrontmatter, NOT config - These are completely separate systems - don’t mix them up!
7. Linking Behavior Confusion (Important)
- Standard markdown links (
[text](url)) are the most ideal - use these for linking between different content types (posts, pages, projects, docs) - Wikilinks (
[[...]]) only work with posts - use these only if you exclusively link between posts and want the Obsidian-native feel - Best Practice: Prefer standard markdown links for maximum flexibility and cross-content-type linking
- Don’t try to extend wikilinks to other collections - use standard links instead:
[Page Title](page-slug),[Project](projects/project-slug),[Doc](docs/doc-slug) - Linked mentions only track posts - pages, projects, and docs are not included in linked mentions
- File renamed for clarity:
wikilinks.ts→internallinks.tsto distinguish between the two behaviors
8. 🚨 URL MAPPING SYSTEM CONFUSION (CRITICAL)
- URL mapping is for RENDERING ONLY - it doesn’t affect linked mentions or graph view filtering
- Linked mentions and graph view remain posts-only - URL mapping doesn’t change this behavior
- Two separate systems:
- URL Mapping: Applied to all content types for proper rendering (
/pages/about→/about) - Linked Mentions/Graph: Only includes posts (filtered by
isPostLinklogic)
- URL Mapping: Applied to all content types for proper rendering (
- Don’t confuse the systems - URL mapping makes links work, but doesn’t change feature scope
- Test both systems independently - URL mapping and linked mentions are separate concerns
9. 🚨 FOLDER-BASED CONTENT ASSUMPTIONS (CRITICAL)
- ALL content types support folder-based organization - not just posts
- Pages, projects, and docs work identically to posts for folder-based content
- Don’t assume folder-based is posts-only - all collections handle
folder-name/index.mdstructure - Astro Content Collections handle this automatically - no special configuration needed
- Asset syncing works for all content types - images, PDFs, etc. are copied to public directory
- URL generation is consistent - folder name becomes slug for all content types
10. 🚨 FOLDER-BASED POST ID DETECTION (CRITICAL - ASTRO v6)
- NEVER use
post.id.includes('/') && post.id.endsWith('/index')- This is WRONG for Astro v6 - Astro v6 folder-based posts have IDs like
'folder-name'- NOT'folder-name/index' - CORRECT detection logic:
// ❌ WRONG - This will NEVER match folder-based posts in Astro v6 const isFolderBasedPost = post.id.includes('/') && post.id.endsWith('/index'); // ✅ CORRECT - Check if post has folder structure by other means // Option 1: Check if post has co-located images (not in attachments/) const isFolderBasedPost = post.data.image && !post.data.image.includes('attachments/'); // Option 2: Use a known list of folder-based posts const folderBasedPostIds = ['sample-folder-based-post', 'another-folder-post']; const isFolderBasedPost = folderBasedPostIds.includes(post.id); // Option 3: Check file system (server-side only) const isFolderBasedPost = await checkIfPostHasFolderStructure(post.id); - Why this matters: Wrong detection causes folder-based posts to use
/posts/attachments/instead of/posts/folder-name/for images - Common symptoms: Folder-based post images don’t display on post cards
- This mistake breaks folder-based post functionality completely
11. 🚨 LINKED MENTIONS EXCERPT LOGIC (CRITICAL)
- NEVER use word/phrase matching - All logic must be structural/syntax-based only
- Markdown cleanup should only remove syntax artifacts (code blocks, formatting markers, structural patterns)
- Ellipsis placement should detect endings using punctuation patterns (
.!?,,:;-, letter endings), NOT specific words - Link detection should use slug comparison and structural patterns, NOT word matching
- Common mistakes:
- ❌ Matching phrases like “Further reading”, “See also”, “Start lines with”
- ❌ Matching specific conjunctions like “or”, “and”, “but” as words
- ❌ Removing specific phrases from cleanup logic
- ✅ Using structural patterns like
/[.!?]\s*$/(sentence endings),/<\/mark>/i(link HTML),/([A-Z][a-z]+):/(any label pattern)
- See Linked Mentions Excerpt Extraction Logic section for detailed guidelines
12. H1 Title Handling
- Both Posts and Pages: NO H1 in markdown content - title comes from frontmatter, content starts with H2
- H1 is hardcoded in both PostLayout and PageLayout using frontmatter title
- NEVER add H1 to any markdown content - both posts and pages have hardcoded H1s from frontmatter
13. Custom Collections Approach
- Use subfolders within pages collection - avoid creating custom collections at content level
- No Astro warnings - subfolders within pages don’t trigger auto-generation warnings
- Same URL structure -
/services/web-developmentworks the same way - No configuration needed - leverages Astro’s built-in folder routing
- Two approaches work identically:
pages/services/web-development.md→/services/web-developmentpages/services/web-development/index.md→/services/web-development
14. 🚨 FAVICON THEME BEHAVIOR (CRITICAL)
- Favicon should NOT change with manual theme toggle - it should only change with browser system theme
- SIMPLE WORKING IMPLEMENTATION (20 lines max, add to BaseLayout.astro script section):
// Simple favicon function - follows system theme only function setFavicon() { const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const faviconUrl = prefersDark ? '/favicon-dark.png' : '/favicon-light.png'; // Remove existing favicon const existingFavicon = document.querySelector('link[rel="icon"]'); if (existingFavicon) existingFavicon.remove(); // Add new favicon const faviconLink = document.createElement('link'); faviconLink.rel = 'icon'; faviconLink.href = faviconUrl; document.head.appendChild(faviconLink); } // Set favicon on load setFavicon(); // Update favicon when system theme changes window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setFavicon); - DO NOT modify existing theme logic - add favicon code separately
- DO NOT use complex logic - keep it simple
- DO NOT tie to localStorage - system theme only
- Files: Use
.pngformat (matches existing favicon files) - Behavior: Favicon reflects OS/browser theme preference, ignores website theme toggle
15. 🎨 COLOR USAGE (CRITICAL)
- NEVER use hardcoded colors - Always use theme variables from
src/themes/index.ts - Use Tailwind classes that reference theme variables (
primary-*,highlight-*) - Include dark mode variants for all color definitions (
dark:bg-primary-800) - Check existing code for hardcoded colors and replace them
- Reference theme files to understand available color scales
16. Package Manager
- Always use
pnpminstead ofnpmfor all commands - Scripts:
pnpm run <script-name>, notnpm run <script-name>
17. Deployment Platform Configuration
- Set platform once in config - Use
deployment.platforminsrc/config.ts, not environment variables - No environment variables needed - The build process automatically detects the platform from config
- Platform options: “netlify”, “vercel”, “github-pages” (all lowercase with hyphens)
- Backward compatibility: Environment variables still work but are not recommended
- Configuration files: Automatically generated based on platform choice
18. Homepage Configuration Structure
- Use
homeOptions- All homepage content is now underhomeOptions, notfeaturesorhomeBlurb - Featured Post: Use
homeOptions.featuredPostwithtype: "latest"ortype: "featured" - Slug Flexibility: Slug can be present even when
type: "latest"- it will be ignored until switched to “featured” - Recent Posts: Use
homeOptions.recentPostswithenabledandcountproperties - Projects/Docs: Use
homeOptions.projectsandhomeOptions.docswithenabledproperty - Blurb: Use
homeOptions.blurbwithplacement: "above" | "below" | "none" - Old References:
showLatestPost,recentPostsCount, andhomeBlurbare deprecated
19. Development vs Production Behavior
- Development: Missing images show placeholders, warnings are logged
- Production: Missing images cause build failures
- Always run
pnpm run check-imagesbefore deploying
20. 🚨 NEVER DISABLE ASTRO DEV TOOLBAR (CRITICAL)
- NEVER disable the Astro dev toolbar - Always keep
devToolbar.enabled: trueinastro.config.mjs - The dev toolbar is a critical development tool - It provides debugging, auditing, and inspection capabilities
- Module loading errors with pnpm are harmless - Known issue with pnpm’s nested node_modules structure - errors appear in console but toolbar still works
- If dev toolbar modules fail to load:
- With pnpm: Console errors are expected and harmless - toolbar functionality works despite errors
- With npm/yarn: Check Vite HMR configuration (should be enabled)
- Verify Vite server configuration is correct
- Ensure TypeScript module resolution is properly configured
- Check for duplicate
vite.serverconfiguration blocks - DO NOT disable the toolbar as a workaround
- The toolbar is development-only - It automatically excludes itself from production builds
- Configuration location:
astro.config.mjs→devToolbar: { enabled: true } - Known pnpm issue: Dev toolbar module loading errors in console with pnpm are cosmetic - functionality is unaffected
Accessibility Warnings
1. “Invalid tabindex on non-interactive element”
- Usually caused by Swup
- Check
astro.config.mjs-accessibility: falseshould be set - This is intentional to prevent Swup from adding invalid tabindex attributes
2. “Missing content” warnings
- Check for proper
aria-labelattributes on interactive elements - Ensure all interactive elements have accessible labels
3. “Redundant text in alt attribute”
- Alt text should describe the image, not repeat visible text
- Use descriptive alt text instead of post titles
- Avoid generic descriptions like “image” or “photo”
Performance Warnings
1. “Unoptimized loading attribute”
- Above-the-fold images should use
loading="eager" - First post on pages should have
eager={true}prop - Check if
eagerprop is being passed correctly to PostCard components
2. Image Loading Issues
- Verify
eagerprop is being passed correctly - Check image loading attributes in PostCard components
- Ensure proper
fetchpriorityattributes for critical images
Content Structure Mistakes
1. File Structure Patterns
- Folder-based posts: Use
index.mdas the main content file - Assets: Co-locate with
index.mdin the same folder - URLs: Folder name becomes the slug automatically
2. Obsidian Integration
- Wikilinks work without conversion - don’t convert them
- Use
[[image.jpg]]syntax for Obsidian compatibility - Tags sync automatically between Obsidian and blog
3. Frontmatter Issues
- Use proper YAML syntax with correct indentation
- Include all required fields for posts and pages
- Validate frontmatter before committing changes
Development Workflow Mistakes
1. Not Using Development Tools
- Run
pnpm run check-imagesregularly - Monitor console for development warnings
- Use placeholder system for missing assets
2. Ignoring Graceful Error Handling
- The system handles missing tags gracefully in development
- Don’t panic about console warnings - they’re helpful
- Continue development with placeholders when needed
3. Build Process Issues
- Always test builds locally before deploying
- Fix all missing images before production builds
- Use the correct build commands for your deployment platform
Component Hierarchy Understanding
1. Layout Components
- BaseLayout.astro: Main layout with Swup container
- PostLayout.astro: Individual post layout (hardcoded H1 from frontmatter)
- PageLayout.astro: Page layout (hardcoded H1 from frontmatter)
2. Content Components
- PostCard.astro: Post cards for listings (controlled by config)
- PostContent.astro: Post content rendering (controlled by frontmatter)
- ImageWrapper.astro: Handles image optimization and fallbacks
3. Key Configuration Files
src/config.ts: Main site configurationastro.config.mjs: Astro and Swup configurationsrc/config/dev.ts: Development-specific settings
Best Practices for AI Agents
1. Always Check Context
- Read the existing code before making changes
- Understand the component hierarchy
- Check configuration files for relevant settings
2. Test Changes Incrementally
- Make small, focused changes
- Test each change before proceeding
- Use the development server to verify changes
3. Follow the Project’s Philosophy
- Respect the Obsidian-first approach
- Maintain the modular design principles
- Preserve the seamless publishing workflow
4. Document Changes
- Add comments for complex logic
- Update configuration documentation
- Explain non-obvious decisions
This comprehensive guide should help AI agents understand the project structure, development practices, content organization patterns, and common pitfalls when working with Astro Modular.