# Option to shift headings?

**URL:** <https://discourse.gohugo.io/t/option-to-shift-headings/6136>\
**Category:** support\
**Created:** [April 12, 2017, 3:31pm UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136 "2017-04-12T15:31:28Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![patdavid](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/patdavid/32/9203_2.png) [@patdavid](https://discourse.gohugo.io/u/patdavid)\
**Post date:** [April 12, 2017, 3:31pm UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/1 "2017-04-12T15:31:28Z")

</div>

I’ve had a look around and haven’t had any luck finding this out.

Markdown articles will sometimes contain `<h1>` elements that may clash with the page being rendered (where an `<h1>` for the article section would be used for the post title, for instance).

Is there an option or setting to force all headings in a post to start at a certain level (that is, _all_ headings in a post now begin at `<h2>`, even if they were created as `<h1> / #` elements)?

---

<div class="post-metadata">

**Author:** ![leejoramo](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/leejoramo/32/2871_2.png) [@leejoramo](https://discourse.gohugo.io/u/leejoramo)\
**Post date:** [April 12, 2017, 4:31pm UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/2 "2017-04-12T16:31:06Z")

</div>

I described doing exactly this in another Hugo Forum Thread: [How I Extended Markdown for Hugo](https://discuss.gohugo.io/t/how-i-extended-markdown-for-hugo/5983)

I do a number of other changes to Markdown rendering in that post, so let me break out just the part you need.

We will create a partial template called `contentShiftHeadings.html` with the following code:

```
{{/* get the Markdown without front matter */}} 
{{ $s := .RawContent }}

{{/* the regex of start of line `^` doesn't work. (GoLang has a subset of regex)
     Need to specify newlines via `\n` 
     Need to ensure newlines at start and end of content */}} 
{{ $s := delimit (slice "\n" $s "\n") "" }}

{{/* search for any Markdown headings `#` at start of line  
      and add another `#` to start of line. NOTE: we could increment by more `#` here. */}} 
{{ $s := $s | replaceRE "\n(#+) " "\n#$1 " }}

{{/* render the Markdown to HTML
     declare it to be safe HTML and return */}} 
{{ $s := $s | markdownify }}
{{ $s := $s | safeHTML }}
{{ $s }}

```

In all templates replace `{{ .Content }}` with `{{ partial "contentShiftHeadings.html" . }}` You may want to refer to the [documentation about Partial Templates](https://gohugo.io/templates/partials/)

This works very well for me.

But there are potential problems:

1. No _shortcodes_ in the content. `RawContent` bypasses rendering shortcodes and I didn’t find a way to tigger shortcode rendering later. This doesn’t affect me, but I would prefer not to take away features.

2. I force safeHTML. I want to sanitize the HTML first, but there is no function for this. There is an issue filed: [https://github.com/spf13/hugo/issues/1457](https://github.com/spf13/hugo/issues/1457)

3. I am not sure how much of an impact this has on Hugo’s site building speed, or if I need to configure caching partial templates.

Hope this helps

---

<div class="post-metadata">

**Author:** ![rdwatters](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/rdwatters/32/2848_2.png) [@rdwatters](https://discourse.gohugo.io/u/rdwatters)\
**Post date:** [April 12, 2017, 6:12pm UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/3 "2017-04-12T18:12:53Z")

</div>

@patdavid Sounds like @leejoramo has put some thought into his solution.

I don’t know your full use case, but have you considered scripting this against the content files themselves rather than relying on Hugo to do it every single time you build your site? Is there a reason you _need_ `#` in your content when likely `##` is more semantic?

---

<div class="post-metadata">

**Author:** ![leejoramo](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/leejoramo/32/2871_2.png) [@leejoramo](https://discourse.gohugo.io/u/leejoramo)\
**Post date:** [April 12, 2017, 7:13pm UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/4 "2017-04-12T19:13:51Z")

</div>

I can’t speak for @patdavid but I have a several reasons for shifting headings.

1. I feel the default should be that individual Markdown pages should be able to stand alone and content rooted at `#`

2. It can depend on context. Are you viewing at a single page or something composed of multiple pages: Sections, Chapter, or Book or a home page that lists the entire content of the last 10 postings.

3. A number of Markdown editor tools treat heading structures as Tables of Content for navigation or provide outliner functionality. It is better to work with pages rooted at `#`

4. Allow writers to not worry about these technical details which can be automated.

5. I write almost every thing in Markdown. It might end out posted to a blog, exported to MS Word or PDF, copied and pasted into an email, made into a Presentation. Consistent use of starting with `#` makes it easier to build workflows to wherever the content gets sent.

In the past I have used Pelican with this python markdown add-on: [https://github.com/cprieto/mdx\_downheader](https://github.com/cprieto/mdx_downheader)

---

<div class="post-metadata">

**Author:** ![adiabatic](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/adiabatic/32/2913_2.png) [@adiabatic](https://discourse.gohugo.io/u/adiabatic)\
**Post date:** [April 13, 2017, 3:28am UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/5 "2017-04-13T03:28:32Z")

</div>

I use the [HTML5 outline algorithm](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_HTML_sections_and_outlines) for this.

Given a document like

```
---
title: bald eagle
---

# Actually not bald

…

# Cooler than turkeys

…

```

I’d have it generate a page that uses [sectioning elements](https://www.w3.org/TR/html5/dom.html#sectioning-content-0) like `article` and `section` like so:

```
<!DOCTYPE html>
<html lang="en-US">
<head>
    <meta charset="UTF-8" />
    <title>allbirds.local</title>
</head>
<body>
    <h1>allbirds.local — your local guide for all birds!</h1>
    <article>
        <h1>bald eagle</h1>
        <section>
            <h1>Actually not bald</h1>
            …
        
            <h1>Cooler than turkeys</h1>
            …
        </section>
    </article>
</body>
</html>

```

And [conformance checkers](https://gsnedders.html5.org/outliner/) (but not, alas, anything designed for blind people — nobody’s put a fire under the people who make VoiceOver and JAWS yet) would realize the outline looks like this:

```
- allbirds.local — your local guide for all birds!
    - bald eagle
        - Actually not bald
        - Cooler than turkeys
```

---

<div class="post-metadata">

**Author:** ![patdavid](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/patdavid/32/9203_2.png) [@patdavid](https://discourse.gohugo.io/u/patdavid)\
**Post date:** [April 24, 2017, 2:10pm UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/6 "2017-04-24T14:10:36Z")

</div>

> [@leejoramo](#):
>
> 1. I feel the default should be that individual Markdown pages should be able to stand alone and content rooted at #
> 2. It can depend on context. Are you viewing at a single page or something composed of multiple pages: Sections, Chapter, or Book or a home page that lists the entire content of the last 10 postings.
> 3. A number of Markdown editor tools treat heading structures as Tables of Content for navigation or provide outliner functionality. It is better to work with pages rooted at #
> 4. Allow writers to not worry about these technical details which can be automated.
> 5. I write almost every thing in Markdown. It might end out posted to a blog, exported to MS Word or PDF, copied and pasted into an email, made into a Presentation. Consistent use of starting with # makes it easier to build workflows to wherever the content gets sent.

Sorry for my late response, was busy working on other things for the site.

Yes, all of the reasons as enumerated by @leejoramo are reasons I also asked my question in the first place.

In particular, authors shouldn’t have to worry about this. Just let them write valid, stand-alone markdown posts. In this case it’s perfectly natural for them to start headings at # in their doc. Or, they are cognizant that the title may be the `<h1>` and as such start all of their markdown headings at ##.

I happened to use Pelican for building [www.gimp.org](http://www.gimp.org), and it actually has [an option to set a base header level for rendered documents](https://pythonhosted.org/Markdown/extensions/header_id.html#usage). This is pretty handy for the reasons listed above.

I suppose this could be done with simple regex - where/how would I inject this into my site build when parsing .md?

---

<div class="post-metadata">

**Author:** ![jbenet](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jbenet/32/9653_2.png) [@jbenet](https://discourse.gohugo.io/u/jbenet)\
**Post date:** [September 28, 2019, 7:52am UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/7 "2019-09-28T07:52:48Z")

</div>

I wrote up [https://github.com/gohugoio/hugo/issues/6373](https://github.com/gohugoio/hugo/issues/6373) to try to do this in hugo itself. It should be possible, as [blackfriday supports it](https://github.com/russross/blackfriday/blob/v2.0.1/html.go#L91-L94)

---

<div class="post-metadata">

**Author:** ![loikein](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/loikein/32/19230_2.png) [@loikein](https://discourse.gohugo.io/u/loikein)\
**Post date:** [June 14, 2024, 12:30am UTC](https://discourse.gohugo.io/t/option-to-shift-headings/6136/8 "2024-06-14T00:30:04Z")

</div>

Hi, I know this is an ancient topic, but since [issue 6373](https://github.com/gohugoio/hugo/issues/6373) went nowhere, this is the next best thing I could found.

Thank you so much for this extremely insightful code. I have made a couple of changes to get it work with shortcodes and (layout-specific) render hooks.

```go-html-template
{{/* get the Markdown without front matter */}} 
{{- $s := .content -}}

{{- /* the regex of start of line `^` doesn't work. (GoLang has a subset of regex)
     Need to specify newlines via `\n` 
     Need to ensure newlines at start and end of content */ -}}
{{- $s = delimit (slice "\n" $s "\n") "" -}}

{{- /* search for any Markdown headings `#` at start of line
      and add another `#` to start of line. NOTE: we could increment by more `#` here. */ -}}
{{- $s = $s | replaceRE `\n(#+) ` `#$1 ` -}}

{{- /* render the Markdown to HTML
     declare it to be safe HTML and return */ -}}
{{- $s = $s | .page.RenderString -}}
{{ $s }}

```

And in place of `{{ .Content }}`, call the partial with `{{ partial "contentShiftHeadings.html" (dict "page" . "content" .RawContent) }}`
