Abnormal rendering of TableOfContents list that has happened only 1 time out of 600 posts

I’m using Hugo v0.162.1.

This is an interesting one. I’m getting different outputs in development vs production for a specific feature of my site and I can’t figure out what is causing this behavior. I have ~600 blog posts using the same code which all look and work correctly, it’s only this one that’s a problem.

The development version is the one that looks correct. I know in production quotes are stripped unless it’s necessary but I enabled quotes and it made no difference, the output still looks off.

I’ve included the exact output and a screenshot. I’ll explain more after showing you these examples.

Production output (incorrect):

<div class=post-quick-jump><strong>Quick Jump:</strong><nav id=TableOfContents><ul><li><ul><li><a href=#histfile>HISTFILE</a></li><li><a href=#histsize--savehist>HISTSIZE / SAVEHIST</a></li><li><a href=#extended_history>EXTENDED_HISTORY</a></li><li><a href=#inc_append_history_time>INC_APPEND_HISTORY_TIME</a></li><li><a href=#hist_ignore_all_dups>HIST_IGNORE_ALL_DUPS</a></li><li><a href=#hist_ignore_space>HIST_IGNORE_SPACE</a></li><li><a href=#hist_reduce_blanks>HIST_REDUCE_BLANKS</a></li><li><a href=#demo-video>Demo Video</a></li></ul></li></ul></nav></div>

Development output (correct):

    <div class="post-quick-jump">
      <strong>Quick Jump:</strong>
      <nav id="TableOfContents">
  <ul>
    <li>
      <ul>
        <li><a href="#histfile">HISTFILE</a></li>
        <li><a href="#histsize--savehist">HISTSIZE / SAVEHIST</a></li>
        <li><a href="#extended_history">EXTENDED_HISTORY</a></li>
        <li><a href="#inc_append_history_time">INC_APPEND_HISTORY_TIME</a></li>
        <li><a href="#hist_ignore_all_dups">HIST_IGNORE_ALL_DUPS</a></li>
        <li><a href="#hist_ignore_space">HIST_IGNORE_SPACE</a></li>
        <li><a href="#hist_reduce_blanks">HIST_REDUCE_BLANKS</a></li>
        <li><a href="#demo-video">Demo Video</a></li>
      </ul>
    </li>
  </ul>
</nav>
    </div>

That quick jump loops over all H3 elements to create clickable links that jump to that section of the post. The Markdown looks like this for each of those headers:

### HISTFILE

There’s no trailing spaces, line breaks or special characters. I have plenty of other headers with / in it and it’s fine. I tried escaping it and even using - instead but it made no difference, it did the same thing.

Is there some type of keyword in these headers that conflicts with Hugo, but only when building a production version of your site?

The quick jump gets output with:

{{ if .Params.toc | default true }}
  {{ if ne .TableOfContents "<nav id=\"TableOfContents\"></nav>" }}
    <div class="post-quick-jump">
      <strong>Quick Jump:</strong>
      {{ .TableOfContents }}
    </div>
  {{ end }}
{{ end }}

The CSS is:

.post-quick-jump {
  font-size: 16px;
  margin-bottom: 25px;
  padding: 10px;
  background-color: #e0f5ff; }

.post-quick-jump a,
.post-quick-jump a:hover,
.post-quick-jump a:visited,
.post-quick-jump a:active,
.post-quick-jump a:focus {
  text-decoration: underline;
  color: #2774c8; }

#TableOfContents {
  display: inline;
  padding-bottom: 0;
}

#TableOfContents ul {
  display: inline;
  padding-left: 1px;
  padding-bottom: 0;
}

#TableOfContents li {
  display: inline;
  margin-right: 8px;
  list-style-type: none;
}

#TableOfContents li:not(:last-child) {
  margin-right: 7px;
  padding-right: 10px;
  border-right: 1px solid #85bdfa;
}

Not a CSS guru, so no solution here but a hint:

That does not look like a content problem

To me this looks like a CSS problem with your media queries and may width settings , overflow, flex or wrap …

you can reproduce the behavior:

  • add items to another pages Quick Jump Section until this as a single line extends some width.
  • remove as much entries of your faulty page that it fist on a line with some width it also wraps fine.

btw when resizing the browser window the content column grows but at aroudn 1200px there’s a jump to a smaller size … maybe related?

The confusing part is it wraps perfectly fine in development but fails in production with the same content / CSS.

It’s a tricky one because:

$ grep -R "^### .*" content/blog | wc -l
1337

There’s 591 published posts with 1329 H3 headers where I haven’t experienced this problem. There’s lots of posts with more than 1 line of quick jump links, here’s one with 3 lines. It renders correctly for all window sizes.

Then I published post 592 today with 8 headers and only this 1 specific post has this wrapping problem for all window sizes for a production build.

Can you clarify the reproducible steps please, I’m not able to follow them. If I make a very long header word such as “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” then it expands past the blue but the failure case here has a bunch of short words. It should have auto-wrapped no?

Here’s what grep returns for the problematic post:

content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### HISTFILE
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### HISTSIZE / SAVEHIST
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### EXTENDED_HISTORY
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### INC_APPEND_HISTORY_TIME
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### HIST_IGNORE_ALL_DUPS
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### HIST_IGNORE_SPACE
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### HIST_REDUCE_BLANKS
content/blog/2026-06-02-8-useful-ways-to-configure-your-zsh-history.md:### Demo Video

Here’s another post where it wraps fine, even when a specific quick jump item wraps across multiple lines because it had a few words near the end of its container:

content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### What Does tmux Do?
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Installation
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Sessions
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### The Command / Prefix / Leader Key
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Windows
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Panes
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Scrolling Up in the Buffer
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Copy / Paste
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Clocks and Cheat Sheets
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Detaching From and Cleaning Up Sessions
content/blog/2017-01-16-who-else-wants-to-boost-their-productivity-with-tmux.md:### Making tmux Your Own

The above renders correctly like this:

as I said, no CSS guru - just also noticed you use a lot of col- might be that some settings for the columns are wrong…

all that combination with @media, col flex … is too much for me :wink:

  • maybe it’s the initial load, that sets something, a @layer problem or :root … just buzzwording

I tested in dev console by deleting adding elements there in the html view

They don’t use @layer. Regardless: I doubt that this is a Hugo issue at all. The HTML is complicated (more than it should be, IMO: <ul><li><ul><li>...<li>...</ul></li></ul> for example looks like overkill.

Setting display: flex and flex-wrap: wrap on the inner ul seems to make the problem go away on FF. Didn’t test on other browsers.

The .TableOfContents feature is provided by Hugo, it generated all of that markup.

If I replace some of the underscores with spaces then it wraps correctly. That’s not the desired outcome of course, but there’s something fishy going on with how it’s being rendered. Keep in mind, the problem only happens when Hugo builds a production version of the site, it’s ok in development.

Do you have any thoughts or leads on why it renders 100% fine in development without this change?

maybe there is something different in the outcome of a dev build?

minifying css …

Nope. My best guess is that the CSS is different in development mode. But there’s so much going on on this page…

If you prefix each word with another word and a space, then it wraps normally. You can test this in dev tools, I didn’t push this change to the live site but I added “env” or “setopt” before each word and it immediately auto-wrapped:

Yep but that would indicate Hugo is doing something in its minification process that is altering the behavior which would be unintended.

In my OP I included the original unminified CSS related to these elements that is being used in development. Can’t really diff it vs production since production is minified.

Here is the most minimal change I came up with to make it work with no CSS adjustments:

All I did in the above was add 1 leading space before “HIST_IGNORE_ALL_DUPS” in dev tools. I’m not sure if I can push that live since leading spaces will get stripped but it at least demonstrates something weird is happening with how it’s treating many single word list items.

Too much guesswork here. If you post a link to your repo, someone might be able to help you. But with only this final page… I don’t think so.

An easy fix would be to remove display:inline from your tableOfContents, tableOfContents ul and set li { display: inline-block;} instead of inline.

I’ve heard of some software that has bugs maybe in third party components (tdewolff/minify) especially for edge cases or unexpected setups. :wink:

For me, that’s not the problematic code. The problem is in the rest (buzzwords)
also your last post points in that direction.

who could? src-> prod and src->dev 180KB unminified (complex) css …



p.s. if you want only the level three, try {{ .Fragments 3 3 false) }}or a variant which does not render nested lists

| <ul> |
|----|
| <li><a href="[#three-11](http://localhost:56612/posts/post-1/#three-11)">three 1.1</a></li> |
| <li><a href="[#three-12](http://localhost:56612/posts/post-1/#three-12)">three 1.2</a></li> |
| <li><a href="[#three-13](http://localhost:56612/posts/post-1/#three-13)">three 1.3</a></li> |
| </ul> |

The thing is that all the other headlines (or at least those I took a look at) have spaces in them. So your li elements can break there. And that’s what they do. But in the page that does not work as you want, there are but two spaces, in the second heading. And that’s where a break occurs, because it can. Every other one has _ instead of . So where, pretty please, should a browser break an element that you told it to be display: inline (namely the parent ul) if there’s no space in it?

Sorry, no Hugo issue here. Just a combination of not very good CSS with a border case.
I’ve already suggested two ways out of it.

This doesn’t fix it btw, are you suggesting this?

#TableOfContents ul {
  /*display: inline;*/
  padding-left: 1px;
  padding-bottom: 0;
}

#TableOfContents li {
  /*display: inline;*/
  display: inline-block;
  margin-right: 8px;
  list-style-type: none;
}

That yields an extra line break after the “Quick Jump” label and a trailing new line:

To be fair the suggestion doesn’t work, and this problem only happens during the black box behavior of Hugo minifying the code. It works 100% perfect in development. I believe this demonstrates Hugo is modifying the CSS output during its minification process in an unexpected way.

Perfect, you identified a possible root course. I see now two possibilities:

  • dig in yourself
  • provide a reproduceable setup for someone to track down.

I’m not a CSS parser to 100% verify but, the original CSS is:

#TableOfContents {
  display: inline;
  padding-bottom: 0;
}

#TableOfContents ul {
  display: inline;
  padding-left: 1px;
  padding-bottom: 0;
}

#TableOfContents li {
  display: inline;
  margin-right: 8px;
  list-style-type: none;
}

#TableOfContents li:not(:last-child) {
  margin-right: 7px;
  padding-right: 10px;
  border-right: 1px solid #85bdfa;
}

It gets minified by Hugo to this:

#TableOfContents,#TableOfContents ul{display:inline;padding-bottom:0}#TableOfContents ul{padding-left:1px}#TableOfContents li{display:inline;margin-right:8px;list-style-type:none}#TableOfContents li:not(:last-child){margin-right:7px;padding-right:10px;border-right:1px solid #85bdfa}

If I manually add whitespace to that output it provides:

#TableOfContents,#TableOfContents ul{
  display:inline;
  padding-bottom:0
}

#TableOfContents ul{
  padding-left:1px
}

#TableOfContents li{
  display:inline;
  margin-right:8px;
  list-style-type:none
}

#TableOfContents li:not(:last-child){
  margin-right:7px;
  padding-right:10px;
  border-right:1px solid #85bdfa
}

That output looks ok. The first 2 rules are organized differently but the rules are applied the same. I also tried this exact minified snippet in development and wasn’t able to reproduce the issue. I ensured nothing was cached and did change the background color to something else to confirm this code is live (it is).

On the bright side, I can reproduce this in development by adding --minify when running the Hugo server. Without this flag, everything works, with this flag, the layout is busted in the same way it is in production. At least this narrows the scope down to being 100% related to --minify and not other things that might be happening during a production build.

Edit:

I was able to resolve it by adding 1 new CSS rule:

#TableOfContents ul li ul li {
  display: inline-block;
}

I don’t know why this is only necessary after Hugo minifies the code, but the above fixes it for both unminified and minified outputs. At least with Firefox on the desktop and Chrome.

Fine. I told you already that it’s about spaces. Which Hugo removes from the HTML, too. So, if in your origial HTML you have
<a href='#something_somethingelse> something_somethingelse</a>, this space in the a element might be gone. We don’t know, since you don’t show us your code.

I’m not sure what you mean by that? I showed the original table of contents markup in its minified and original form in the first post. There’s no leading or trailing white space around the headers.

Keep in mind, 100% of that markup is generated by Hugo. It has a feature for rendering a table of contents based on parsing your headings.

That:

You gave us no chance to see the MD and HTML generated from it. Instead, you post a link to a rendered page and a very small part of the CSS.
Had someone had the possibility to run Hugo on your code, they might have spotted the issue a lot earlier.

You should definitely think about using more up-to-date CSS like flex and grid. Those work quite well regardless of white space in the HTML.