diff --git a/site/content/docs/01-component-format.md b/site/content/docs/01-component-format.md
index 7286205bdf..7c5f423ca8 100644
--- a/site/content/docs/01-component-format.md
+++ b/site/content/docs/01-component-format.md
@@ -24,7 +24,7 @@ All three sections — script, styles and markup — are optional.
A `
```
-#### 2. Assignments are 'reactive'
+#### 2. Assignments are 'reactive' { data-toc-ignore }
---
@@ -107,7 +107,7 @@ Because Svelte's reactivity is based on assignments, using array methods like `.
```
-#### 3. `$:` marks a statement as reactive
+#### 3. `$:` marks a statement as reactive { data-toc-ignore }
---
@@ -143,7 +143,7 @@ If a statement consists entirely of an assignment to an undeclared variable, Sve
```
-#### 4. Prefix stores with `$` to access their values
+#### 4. Prefix stores with `$` to access their values { data-toc-ignore }
---
@@ -172,7 +172,7 @@ Local variables (that do not represent store values) must *not* have a `$` prefi
```
-#### Store contract
+#### Store contract { data-toc-ignore }
```js
store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }
diff --git a/site/src/routes/docs/_sections.js b/site/src/routes/docs/_sections.js
index 3657ba85ac..b730abbe2f 100644
--- a/site/src/routes/docs/_sections.js
+++ b/site/src/routes/docs/_sections.js
@@ -3,6 +3,7 @@ import path from 'path';
import { SLUG_PRESERVE_UNICODE, SLUG_SEPARATOR } from '../../../config';
import { extract_frontmatter, extract_metadata, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
import { make_session_slug_processor } from '@sveltejs/site-kit/utils/slug';
+import { extract_attributes } from '../../utils/attributes';
import { highlight } from '../../utils/highlight';
import marked from 'marked';
@@ -85,6 +86,10 @@ export default function() {
renderer.heading = (text, level, rawtext) => {
let slug;
+ let extracted = extract_attributes(text, rawtext);
+ text = extracted.text;
+ rawtext = extracted.raw;
+
const match = /]*>(.+)<\/a>/.exec(text);
if (match) {
slug = match[1];
@@ -93,7 +98,8 @@ export default function() {
slug = make_slug(rawtext);
}
- if (level === 3 || level === 4) {
+ const tocIgnore = 'data-toc-ignore' in extracted.attrs;
+ if ((level === 3 || level === 4) && !tocIgnore) {
const title = text
.replace(/<\/?code>/g, '')
.replace(/\.(\w+)(\((.+)?\))?/, (m, $1, $2, $3) => {
@@ -106,8 +112,8 @@ export default function() {
}
return `
-
- 4 ? 'data-scrollignore' : ''}>
+
+ 4 || tocIgnore ? 'data-scrollignore' : ''}>
${text}
`;
diff --git a/site/src/utils/attributes.js b/site/src/utils/attributes.js
new file mode 100644
index 0000000000..1a823cab11
--- /dev/null
+++ b/site/src/utils/attributes.js
@@ -0,0 +1,61 @@
+const ATTRS_REGEX = /^(.+)\s+\{(.+)\}$/;
+/**
+ * Extracts attributes from markdown text to be applied to the resulting HTML.
+ * @example
+ * // returns {
+ * // text: 'Heading',
+ * // raw: 'Heading
',
+ * // attrs: { id: 'important', class: 'red', test: 'true' },
+ * // attrstring: 'id="important" class="red" test="true"'
+ * // }
+ * extract_attributes(
+ * 'Heading { #important .red test=true }',
+ * 'Heading
{ #important .red test=true }'
+ * );
+ * ```
+ * @param {string} text
+ * @param {string} raw
+ */
+export function extract_attributes(text, raw) {
+ try {
+ const textMatch = text.match(ATTRS_REGEX);
+ const rawMatch = raw && raw.match(ATTRS_REGEX);
+ const attrs = textMatch ? parse_attributes(textMatch[2]) : {};
+
+ return {
+ text: textMatch ? textMatch[1] : text,
+ raw: rawMatch ? rawMatch[1] : raw,
+ attrs,
+ attrstring: Object.keys(attrs).map(key => `${key}="${attrs[key].trim()}"`).join(' ')
+ }
+ } catch (err) {
+ console.log(err);
+ return {
+ text,
+ raw,
+ attrs: {},
+ attrstring: ''
+ };
+ }
+}
+
+function parse_attributes(raw_attributes) {
+ const attributes = raw_attributes.split(' ');
+ const result = { };
+ attributes.forEach(attr => {
+ if (!attr) return;
+ if (attr.startsWith('#')) {
+ result.id = attr.substring(1);
+ } else if (attr.startsWith('.')) {
+ if (!result.class) {
+ result.class = '';
+ }
+ result.class += attr.substring(1) + ' ';
+ } else {
+ let [key, value = ''] = attr.split('=');
+ result[key] = value;
+ }
+ })
+
+ return result;
+}
\ No newline at end of file