If a document with headings containing special characters is printed to HTML, generated ToC links point to slugified ids, while <h1> , <h2>, etc. tags are given a non-slugified id, breaking the link.
What's the expected result
header tags in the generated HTML are given slugified ids.
How to reproduce
Take the following sample document:
# BROKEN TOC DEMO
test1, comma
lorem ipsum
test2 — em dash
dolor sit
2. Generate a ToC with `Levels`=`2..6` and `Slugify Mode`=`github` (although the issue should be reproducible with any other `Levels` and `Slugify Mode` setting too). The following should be obtained:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BROKEN TOC DEMO</title>
<style>
/* From extension vscode.github */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.vscode-dark img[src$=\#gh-light-mode-only],
.vscode-light img[src$=\#gh-dark-mode-only],
.vscode-high-contrast:not(.vscode-high-contrast-light) img[src$=\#gh-light-mode-only],
.vscode-high-contrast-light img[src$=\#gh-dark-mode-only] {
display: none;
}
/* From extension searKing.preview-vscode */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.preview-vscode-error {
color: var(--vscode-editorError-foreground);
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/markdown.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/highlight.css">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif;
font-size: 14px;
line-height: 1.6;
}
</style>
<style>
.task-list-item {
list-style-type: none;
}
.task-list-item-checkbox {
margin-left: -20px;
vertical-align: middle;
pointer-events: none;
}
</style>
<style>
:root {
--color-note: #0969da;
--color-tip: #1a7f37;
--color-warning: #9a6700;
--color-severe: #bc4c00;
--color-caution: #d1242f;
--color-important: #8250df;
}
</style>
<style>
@media (prefers-color-scheme: dark) {
:root {
--color-note: #2f81f7;
--color-tip: #3fb950;
--color-warning: #d29922;
--color-severe: #db6d28;
--color-caution: #f85149;
--color-important: #a371f7;
}
}
</style>
<style>
.markdown-alert {
padding: 0.5rem 1rem;
margin-bottom: 16px;
color: inherit;
border-left: .25em solid #888;
}
.markdown-alert>:first-child {
margin-top: 0
}
.markdown-alert>:last-child {
margin-bottom: 0
}
.markdown-alert .markdown-alert-title {
display: flex;
font-weight: 500;
align-items: center;
line-height: 1
}
.markdown-alert .markdown-alert-title .octicon {
margin-right: 0.5rem;
display: inline-block;
overflow: visible !important;
vertical-align: text-bottom;
fill: currentColor;
}
.markdown-alert.markdown-alert-note {
border-left-color: var(--color-note);
}
.markdown-alert.markdown-alert-note .markdown-alert-title {
color: var(--color-note);
}
.markdown-alert.markdown-alert-important {
border-left-color: var(--color-important);
}
.markdown-alert.markdown-alert-important .markdown-alert-title {
color: var(--color-important);
}
.markdown-alert.markdown-alert-warning {
border-left-color: var(--color-warning);
}
.markdown-alert.markdown-alert-warning .markdown-alert-title {
color: var(--color-warning);
}
.markdown-alert.markdown-alert-tip {
border-left-color: var(--color-tip);
}
.markdown-alert.markdown-alert-tip .markdown-alert-title {
color: var(--color-tip);
}
.markdown-alert.markdown-alert-caution {
border-left-color: var(--color-caution);
}
.markdown-alert.markdown-alert-caution .markdown-alert-title {
color: var(--color-caution);
}
</style>
</head>
<body class="vscode-body vscode-light">
<h1 id="broken-toc-demo" tabindex="-1" id="broken-toc-demo">BROKEN TOC DEMO</h1>
<ul>
<li><a href="#test1-comma">test1, comma</a></li>
<li><a href="#test2--em-dash">test2 — em dash</a></li>
</ul>
<h2 id="test1%2C-comma" tabindex="-1" id="test1-comma">test1, comma</h2>
<p>lorem ipsum</p>
<h2 id="test2-%E2%80%94-em-dash" tabindex="-1" id="test2--em-dash">test2 — em dash</h2>
<p>dolor sit</p>
</body>
</html>
Try following a ToC link.
Other information
I also noticed that the generated HTML header tags always have two ids.
If the markdown header didn't include characters removable by slugify, both the generated ids in the HTML header tags are equal, so the first one gets kept, and the link is not broken
If the markdown header did include special characters, in the generated HTML, the first id to appear in HTML header tags is the "malformed", non-slugified one, and since it appears first, that is what gets read by the browser, and the correct one is ignored. The HTML spec does specify that only one ID is allowed per DOM element.
What's the problem
If a document with headings containing special characters is printed to HTML, generated ToC links point to slugified ids, while
<h1>
,<h2>
, etc. tags are given a non-slugified id, breaking the link.What's the expected result
header tags in the generated HTML are given slugified ids.
How to reproduce
test1, comma
lorem ipsum
test2 — em dash
dolor sit
Other information
I also noticed that the generated HTML header tags always have two ids.