Negative Heading
approvedby Ashan Devine
Render Discord-style "-#" lines as compact headings in reading view and the editor.
Negative Heading Plugin
An Obsidian plugin that renders Discord-style -# Heading lines as compact, muted headings in all view modes (Reading View, Live Preview, and Source Mode). The rendered block keeps normal Markdown content (bold, italics, links) while the -# marker is dimmed, creating lightweight subheadings perfect for organizing content without the visual weight of traditional headings.
Features
- All View Modes: Works seamlessly in Reading View, Live Preview, and Source Mode
- Semantic Rendering: Reading View converts
-# Headinginto<div role="heading" aria-level="7">for proper accessibility - CodeMirror Integration: Source mode and Live Preview use decorations for real-time syntax highlighting
- Smart Toggle Command: Intelligently add or remove negative heading tokens based on majority detection
- List Item Support: Works inside list items (
- -# List heading) - Escape Character Support: Use
\-#to prevent transformation (renders as literal-#text) - Context-Aware: Skips fenced code blocks, math blocks, and inline code for proper Markdown rendering
- Theme Integration: Uses
var(--text-muted)/var(--text-faint)with intelligent fallbacks to theme comment color or neutral gray
Some theme examples:
Usage
Manual Entry
- Type
-#at the start of a line, follow with text. - Switch to Reading View (or Live Preview) to see a compact heading that respects theme typography.
- In Source mode, the text is tinted to match muted text/comment colors so it remains identifiable.
Smart Toggle Command
The plugin provides a Smart toggle negative heading command that intelligently adds or removes the -# token based on context:
- Single line: Place cursor on any line and run the command to toggle the token on/off.
- Multiple lines: Select multiple lines and run the command. It will analyze the selection:
- If majority are regular text → adds
-#to all lines (SET operation) - If majority are already negative headings → removes
-#from all lines (UNSET operation) - On a 50/50 tie → defaults to SET (add tokens)
- If majority are regular text → adds
- List items: Preserves list markers (
-,1., etc.) and inserts/removes the token after the marker. - Cursor preservation: Your cursor/selection position adjusts automatically after the transformation.
To use: Open the command palette (Ctrl/Cmd + P) and search for "Smart Toggle Negative Heading", or assign a hotkey in Settings → Hotkeys.
Examples:
- Single line:
Text here→-# Text here(and vice versa) - In lists:
- Item text→- -# Item text(and vice versa) - Multiple lines: If you select 3 regular lines and 1 negative heading, all 4 become negative headings (majority rule)
Notes:
- Only a single leading
-#token is supported per block. - Syntax inside code fences and math blocks is ignored on purpose.
- The smart toggle command skips empty lines and whitespace-only lines in multi-line selections.
Installation
From Obsidian Community Plugins (Recommended)
Note: Pending approval to the community plugins repository.
Once approved, you'll be able to install directly from Obsidian:
- Open Obsidian Settings
- Navigate to Community plugins and disable Safe Mode
- Click Browse and search for "Negative Heading"
- Click Install, then Enable
From source
npm install
npm run build
Copy the generated main.js, along with manifest.json and styles.css, into <vault>/.obsidian/plugins/negative-heading-plugin/.
Development
npm run dev- watch mode via esbuild.npm run build- type-check plus production bundle.
Reload Obsidian after each build, or use the Reload app without saving hotkey in the developer tools.
Limitations
- Only matches lines that begin with
-#(at line start); indented lines are treated as plain text. - The plugin targets Obsidian v1.6+ where Live Preview and the current CodeMirror 6 API are available.
- Single
-#token per line (repeated markers are treated as plain text). - Nested Lists in Reader Mode: There is a known issue with rendering negative headings inside nested list items in Reader Mode. The styling may not apply correctly in deeply nested list structures. This is being investigated for a future update.
Testing
The plugin includes comprehensive automated testing:
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:visual # Visual regression tests
npm run test:edge # Edge case tests
Test Coverage:
- 43 tests across 22 test files
- 200+ edge case scenarios
- Visual regression testing
- Mode parity verification (Reading/Live Preview/Source)
- Toggle command functionality
- Escape character handling
- List item behavior
Troubleshooting
Plugin doesn't appear in Reading View
- Ensure you're using
-#at the start of a line (not indented) - Check that the line isn't inside a code block or math block
- Try reloading Obsidian
Styles look wrong
- The plugin uses theme variables for colors
- Check your theme supports
--text-mutedand--text-faintvariables - Plugin provides fallbacks if variables aren't available
Toggle command isn't working
- Ensure you have text selected or cursor on a line
- Empty lines are skipped in multi-line selections
- Check the command palette for "Smart toggle negative heading"
Escape characters not working
- Use single backslash:
\-# Text(not\\-#) - Verify you're using the correct syntax (backslash before the dash)
Architecture
This plugin uses a dual-pipeline architecture to support all Obsidian view modes:
- Reader Mode Pipeline: DOM post-processing via
registerMarkdownPostProcessor() - Edit Mode Pipeline: CodeMirror 6 decorations via
registerEditorExtension()
For detailed architecture documentation, see ARCHITECTURE.md.
Contributing
Contributions are welcome! Please see AGENTS.md for development guidelines and project conventions.
Development Setup:
npm install
npm run dev # Watch mode
npm test # Run tests
Before submitting:
- Ensure all tests pass (
npm test) - Follow the coding conventions in AGENTS.md
- Test in all three view modes (Reading View, Live Preview, Source Mode)
- Test edge cases (lists, code blocks, escape characters)
License
This project is licensed under the GPL License. See LICENSE for details.
Changelog
1.0.0 (Initial Release)
- Discord-style
-# Headingsyntax support - Smart Toggle Command with majority detection
- Escape character support (
\-#) - List item support
- Works in all view modes
- Comprehensive test suite
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.