remark-shift-headings
v0.1.1
Published
Remark plugin to shift heading levels based on rendering context
Readme
remark-shift-headings
Remark plugin to shift heading levels based on rendering context.
Why?
When building content sites with frameworks like Astro, you often have different heading requirements for different contexts:
- Content collections (articles, blog posts): The page title is typically an
<h1>in the layout, so your markdown content should start at<h2> - Standalone pages: No layout title, so markdown content can start at
<h1>
This plugin automatically adjusts heading levels based on context, with support for:
- Auto-detection of content collections via file path
- Runtime context setting (for Container API usage)
- Frontmatter overrides for per-page control
Installation
pnpm add remark-shift-headingsUsage
Basic Usage (Astro)
// astro.config.mjs
import { defineConfig } from 'astro/config';
import { remarkShiftHeadings } from 'remark-shift-headings';
export default defineConfig({
markdown: {
remarkPlugins: [remarkShiftHeadings],
},
});With Custom Options
remarkPlugins: [
[remarkShiftHeadings, {
defaultCollectionLevel: 2, // Start at h2 for collections (default: 2)
defaultPageLevel: 1, // Start at h1 for pages (default: 1)
maxLevel: 6, // Maximum heading level (default: 6)
}]
],Per-Page Override
Use frontmatter to override the heading start level for specific pages:
---
headingStartLevel: 3
---
# This becomes h3
## This becomes h4Runtime Context (Container API)
When using Astro's Container API or similar, you can set the context level at runtime:
const result = await remark()
.use(remarkShiftHeadings)
.process({
value: markdown,
data: {
headingStartLevel: 2, // Force h2 start level
},
});How It Works
The plugin determines the target heading level using a 3-tier strategy:
- Frontmatter override (
headingStartLevelin metadata) - Runtime context (set via
file.data.headingStartLevel) - Auto-detect (checks file path for
/src/content/(articles|projects)/)
Once the target level is determined:
- Finds the minimum heading level in your content (e.g.,
# H1= level 1) - Calculates the shift needed (e.g., target h2 - current h1 = shift by 1)
- Applies the shift to all headings, clamping to valid range (1-6)
Examples
Content Collection (Auto-detected)
Input (/src/content/articles/my-post.md):
# Introduction
## Details
### More InfoOutput (starts at h2):
## Introduction
### Details
#### More InfoStandalone Page
Input (/src/pages/about.md):
## About Us
### Our TeamOutput (starts at h1, no change since min level is already h2):
## About Us
### Our TeamWith Max Level Clamping
Input with maxLevel: 4:
# H1
##### H5
###### H6Output (h2 start, but h5/h6 clamped to h4):
## H1
#### H5
#### H6Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| defaultCollectionLevel | number | 2 | Starting heading level for content collections |
| defaultPageLevel | number | 1 | Starting heading level for standalone pages |
| maxLevel | number | 6 | Maximum heading level (clamps higher levels) |
License
MIT © Tyler Butler
