--- name: obsidian version: 1.1.0 description: Comprehensive guidelines for Obsidian.md plugin development including all 27 ESLint rules, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI/UX standards, and submission requirements. Use when working with Obsidian plugins, main.ts files, manifest.json, Plugin class, MarkdownView, TFile, vault operations, or any Obsidian API development. --- # Obsidian Plugin Development Guidelines You are assisting with Obsidian plugin development. Follow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices. ## Getting Started ### Quick Start Tool For new plugin projects, an interactive boilerplate generator is available: - **Script**: `tools/create-plugin.js` in the skill repository - **Slash command**: `/create-plugin` for guided setup - Generates minimal, best-practice boilerplate with no sample code - Detects existing projects and only adds missing files - All generated code follows these guidelines automatically ### When to Suggest the Tool Recommend the boilerplate generator when users: - Ask "how do I create a new Obsidian plugin?" - Want to start a new plugin project - Need help setting up the basic structure - Want to ensure they start with best practices ## Core Principles 1. **Memory Safety**: Prevent memory leaks through proper resource management 2. **Type Safety**: Use proper type narrowing and avoid unsafe casts 3. **API Best Practices**: Follow Obsidian's recommended patterns 4. **User Experience**: Maintain consistency in UI/UX across plugins 5. **Platform Compatibility**: Ensure cross-platform support (including iOS) 6. **Accessibility**: Make all features keyboard and screen reader accessible --- ## Quick Reference ### Top 27 Most Critical Rules **Submission & Naming:** 1. **Plugin ID: no "obsidian", can't end with "plugin"** - Validation bot enforced 2. **Plugin name: no "Obsidian", can't end with "Plugin"** - Validation bot enforced 3. **Plugin name: can't start with "Obsi" or end with "dian"** - Validation bot enforced 4. **Description: no "Obsidian", "This plugin", etc.** - Validation bot enforced 5. **Description must end with `.?!)` punctuation** - Validation bot enforced **Memory & Lifecycle:** 6. **Use `registerEvent()` for automatic cleanup** - Prevents memory leaks 7. **Don't store view references in plugin** - Causes memory leaks **Type Safety:** 8. **Use `instanceof` instead of type casting** - Type safety for TFile/TFolder **UI/UX:** 9. **Use sentence case for all UI text** - "Advanced settings" not "Advanced Settings" 10. **No "command" in command names/IDs** - Redundant 11. **No plugin ID in command IDs** - Obsidian auto-namespaces 12. **No default hotkeys** - Avoid conflicts 13. **Use `.setHeading()` for settings headings** - Not manual HTML **API Best Practices:** 14. **Use Editor API for active file edits** - Preserves cursor position 15. **Use `Vault.process()` for background file mods** - Prevents conflicts 16. **Use `normalizePath()` for user paths** - Cross-platform compatibility 17. **Use `Platform` API for OS detection** - Not navigator 18. **Use `requestUrl()` instead of `fetch()`** - Bypasses CORS restrictions 19. **No console.log in onload/onunload in production** - Pollutes console **Styling:** 20. **Use Obsidian CSS variables** - Respects user themes 21. **Scope CSS to plugin containers** - Prevents style conflicts **Accessibility (MANDATORY):** 22. **Make all interactive elements keyboard accessible** - Accessibility required 23. **Provide ARIA labels for icon buttons** - Accessibility required 24. **Define clear focus indicators** - Use `:focus-visible` **Security & Compatibility:** 25. **Don't use `innerHTML`/`outerHTML`** - Security risk (XSS) 26. **Avoid regex lookbehind** - iOS < 16.4 incompatibility **Code Quality:** 27. **Remove all sample/template code** - MyPlugin, SampleModal, etc. --- ## Detailed Guidelines For comprehensive information on specific topics, see the reference files: ### [Memory Management & Lifecycle](reference/memory-management.md) - Using `registerEvent()`, `addCommand()`, `registerDomEvent()`, `registerInterval()` - Avoiding view references in plugin - Not using plugin as component - Proper leaf cleanup ### [Type Safety](reference/type-safety.md) - Using `instanceof` instead of type casting - Avoiding `any` type - Using `const` and `let` over `var` ### [UI/UX Standards](reference/ui-ux.md) - Sentence case enforcement - Command naming conventions - Settings and configuration best practices ### [File & Vault Operations](reference/file-operations.md) - View access patterns - Editor vs Vault API - Atomic file operations - File management - Path handling ### [CSS Styling Best Practices](reference/css-styling.md) - Avoiding inline styles - Using Obsidian CSS variables - Scoping plugin styles - Theme support - Spacing and layout ### [Accessibility (A11y)](reference/accessibility.md) - Keyboard navigation (MANDATORY) - ARIA labels and roles (MANDATORY) - Tooltips and accessibility - Focus management (MANDATORY) - Focus visible styles (MANDATORY) - Screen reader support (MANDATORY) - Mobile and touch accessibility (MANDATORY) - Accessibility checklist ### [Code Quality & Best Practices](reference/code-quality.md) - Removing sample code - Security best practices - Platform compatibility - API usage best practices - Async/await patterns - DOM helpers ### [Plugin Submission Requirements](reference/submission.md) - Repository structure - Submission process - Semantic versioning - Testing checklist --- ## Essential Do's and Don'ts ### Do's ✅ **Memory & Lifecycle**: - Use `registerEvent()`, `addCommand()`, `registerDomEvent()`, `registerInterval()` - Return views/components directly (don't store unnecessarily) **Type Safety**: - Use `instanceof` for type checking (not type casting) - Use specific types or `unknown` instead of `any` - Use `const` and `let` (not `var`) **API Usage**: - Use `this.app` (not global `app`) - Use Editor API for active file edits - Use `Vault.process()` for background file modifications - Use `FileManager.processFrontMatter()` for YAML - Use `fileManager.trashFile()` for deletions - Use `normalizePath()` for user-defined paths - Use `Platform` API for OS detection - Use `AbstractInputSuggest` for autocomplete - Use direct file lookups (not vault iteration) - Use `requestUrl()` instead of `fetch()` for network requests **UI/UX**: - Use sentence case for all UI text - Use `.setHeading()` for settings headings - Use Obsidian DOM helpers (`createDiv()`, `createSpan()`, `createEl()`) - Use `window.setTimeout/setInterval` with `number` type **Styling**: - Move all styles to CSS - Use Obsidian CSS variables for all styling - Scope CSS to plugin containers - Support both light and dark themes via CSS variables - Follow Obsidian's 4px spacing grid **Accessibility (MANDATORY)**: - Make all interactive elements keyboard accessible - Provide ARIA labels for icon buttons - Define clear focus indicators using `:focus-visible` - Use `data-tooltip-position` for tooltips - Ensure minimum touch target size (44×44px) - Manage focus properly in modals - Test with keyboard navigation **Code Quality**: - Use async/await (not Promise chains) - Remove all sample/template code - Test on mobile (if not desktop-only) - Follow semantic versioning - Minimize console logging (no console.log in onload/onunload in production) ### Don'ts ❌ **Memory & Lifecycle**: - Don't store view references in plugin properties - Don't pass plugin as component to MarkdownRenderer - Don't detach leaves in `onunload()` **Type Safety**: - Don't cast to TFile/TFolder (use `instanceof`) - Don't use `any` type - Don't use `var` **API Usage**: - Don't use global `app` object - Don't use `Vault.modify()` for active file edits - Don't hardcode `.obsidian` path (use `vault.configDir`) - Don't use `navigator.platform/userAgent` (use Platform API) - Don't iterate vault when direct lookup exists - Don't use `fetch()` (use `requestUrl()` instead) **UI/UX**: - Don't use Title Case in UI (use sentence case) - Don't include "command" in command names/IDs - Don't duplicate plugin ID in command IDs - Don't set default hotkeys - Don't create manual HTML headings (use `.setHeading()`) - Don't use "General", "settings", or plugin name in settings headings **Styling**: - Don't assign styles via JavaScript - Don't hardcode colors, sizes, or spacing (use CSS variables) - Don't use broad CSS selectors (scope to plugin) - Don't manually switch themes (CSS variables adapt automatically) - Don't create `` or `