fix(v2): fix contentTitle issues when markdown h1 title contains code blocks (#4882)

* attempt to fix contentTitle issues when markdown h1 title contains inline code blocks

* mention hide_title frontmatter only prevents frontmatter.title from being added in the dom (not a markdown # title in content)

* alwayss insert MainHeading under the div.markdown container for consistency

* ensure MainHeading has no useless id

* revert https://github.com/facebook/docusaurus/pull/4859 as it's now useless: docMeta.title contains the text/frontmatter title in priority over the contentTitle

* fix docs test after revert

* improve markdownParser and fix tests

* fix docs tests

* markdownParser: restore option to remove contentTitle (mostly for blog plugin)

* use removeContentTitle for blog
This commit is contained in:
Sébastien Lorber 2021-06-03 17:45:19 +02:00 committed by GitHub
parent 85e87b560e
commit 57806798c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 246 additions and 178 deletions

View file

@ -31,9 +31,8 @@ module.exports = async function docusaurusMdxLoader(fileString) {
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);
// By default, will remove the markdown title from the content
const {content} = parseMarkdownContentTitle(contentWithTitle, {
keepContentTitle: reqOptions.keepContentTitle,
const {content, contentTitle} = parseMarkdownContentTitle(contentWithTitle, {
removeContentTitle: reqOptions.removeContentTitle,
});
const hasFrontMatter = Object.keys(frontMatter).length > 0;
@ -69,7 +68,11 @@ module.exports = async function docusaurusMdxLoader(fileString) {
return callback(err);
}
let exportStr = `export const frontMatter = ${stringifyObject(frontMatter)};`;
let exportStr = ``;
exportStr += `\nexport const frontMatter = ${stringifyObject(frontMatter)};`;
exportStr += `\nexport const contentTitle = ${stringifyObject(
contentTitle,
)};`;
// Read metadata for this MDX and export it.
if (options.metadataPath && typeof options.metadataPath === 'function') {

View file

@ -146,7 +146,7 @@ export async function generateBlogPosts(
content,
contentTitle,
excerpt,
} = await parseMarkdownFile(source);
} = await parseMarkdownFile(source, {removeContentTitle: true});
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
const aliasedSource = aliasedSitePath(source, siteDir);

View file

@ -459,6 +459,9 @@ export default function pluginContentBlog(
`${docuHash(aliasedPath)}.json`,
);
},
// For blog posts a title in markdown is always removed
// Blog posts title are rendered separately
removeContentTitle: true,
},
},
{

View file

@ -168,7 +168,7 @@ Object {
\\"unversionedId\\": \\"foo/bar\\",
\\"id\\": \\"foo/bar\\",
\\"isDocsHomePage\\": false,
\\"title\\": \\"Remarkable\\",
\\"title\\": \\"Bar\\",
\\"description\\": \\"This is custom description\\",
\\"source\\": \\"@site/docs/foo/bar.md\\",
\\"sourceDirName\\": \\"foo\\",
@ -190,7 +190,7 @@ Object {
\\"unversionedId\\": \\"foo/baz\\",
\\"id\\": \\"foo/baz\\",
\\"isDocsHomePage\\": false,
\\"title\\": \\"Baz markdown title\\",
\\"title\\": \\"baz\\",
\\"description\\": \\"Images\\",
\\"source\\": \\"@site/docs/foo/baz.md\\",
\\"sourceDirName\\": \\"foo\\",
@ -418,12 +418,12 @@ Object {
\\"items\\": [
{
\\"type\\": \\"link\\",
\\"label\\": \\"Remarkable\\",
\\"label\\": \\"Bar\\",
\\"href\\": \\"/docs/foo/bar\\"
},
{
\\"type\\": \\"link\\",
\\"label\\": \\"Baz markdown title\\",
\\"label\\": \\"baz\\",
\\"href\\": \\"/docs/foo/bazSlug.html\\"
}
]

View file

@ -180,7 +180,7 @@ describe('simple site', () => {
isDocsHomePage: false,
permalink: '/docs/foo/bar',
slug: '/foo/bar',
title: 'Remarkable',
title: 'Bar',
description: 'This is custom description',
frontMatter: {
description: 'This is custom description',
@ -254,7 +254,7 @@ describe('simple site', () => {
isDocsHomePage: true,
permalink: '/docs/',
slug: '/',
title: 'Remarkable',
title: 'Bar',
description: 'This is custom description',
frontMatter: {
description: 'This is custom description',
@ -286,7 +286,7 @@ describe('simple site', () => {
isDocsHomePage: false,
permalink: '/docs/foo/bazSlug.html',
slug: '/foo/bazSlug.html',
title: 'Baz markdown title',
title: 'baz',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs/foo/baz.md',
description: 'Images',
@ -345,7 +345,7 @@ describe('simple site', () => {
isDocsHomePage: false,
permalink: '/docs/foo/bazSlug.html',
slug: '/foo/bazSlug.html',
title: 'Baz markdown title',
title: 'baz',
editUrl: hardcodedEditUrl,
description: 'Images',
frontMatter: {

View file

@ -375,7 +375,7 @@ describe('simple website', () => {
'foo',
'bar.md',
),
title: 'Remarkable',
title: 'Bar',
description: 'This is custom description',
frontMatter: {
description: 'This is custom description',

View file

@ -202,11 +202,9 @@ export function processDocMetadata({
numberPrefixParser: options.numberPrefixParser,
});
// TODO expose both headingTitle+metaTitle to theme?
// Different fallbacks order on purpose!
// See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
const headingTitle: string = contentTitle ?? frontMatter.title ?? baseID;
// const metaTitle: string = frontMatter.title ?? contentTitle ?? baseID;
// Note: the title is used by default for page title, sidebar label, pagination buttons...
// frontMatter.title should be used in priority over contentTitle (because it can contain markdown/JSX syntax)
const title: string = frontMatter.title ?? contentTitle ?? baseID;
const description: string = frontMatter.description ?? excerpt ?? '';
@ -245,7 +243,7 @@ export function processDocMetadata({
unversionedId,
id,
isDocsHomePage,
title: headingTitle,
title,
description,
source: aliasedSitePath(filePath, siteDir),
sourceDirName,

View file

@ -199,11 +199,7 @@ export default function pluginContentDocs(
nextId,
} = sidebarsUtils.getDocNavigation(doc.id);
const toDocNavLink = (navDocId: string): DocNavLink => ({
// Use frontMatter.title in priority over a potential # title found in markdown
// See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
title:
docsBaseById[navDocId].frontMatter.title ||
docsBaseById[navDocId].title,
title: docsBaseById[navDocId].title,
permalink: docsBaseById[navDocId].permalink,
});
return {

View file

@ -82,6 +82,7 @@ declare module '@theme/DocItem' {
readonly frontMatter: FrontMatter;
readonly metadata: Metadata;
readonly toc: readonly TOCItem[];
readonly contentTitle: string | undefined;
(): JSX.Element;
};
};

View file

@ -222,7 +222,6 @@ export default function pluginContentPages(
rehypePlugins,
beforeDefaultRehypePlugins,
beforeDefaultRemarkPlugins,
keepContentTitle: true,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
// Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX.

View file

@ -47,8 +47,8 @@ function BlogPostItem(props: Props): JSX.Element {
truncated,
isBlogPostPage = false,
} = props;
const {date, formattedDate, permalink, tags, readingTime} = metadata;
const {author, title, image, keywords} = frontMatter;
const {date, formattedDate, permalink, tags, readingTime, title} = metadata;
const {author, image, keywords} = frontMatter;
const authorURL = frontMatter.author_url || frontMatter.authorURL;
const authorTitle = frontMatter.author_title || frontMatter.authorTitle;

View file

@ -13,6 +13,7 @@ import LastUpdated from '@theme/LastUpdated';
import type {Props} from '@theme/DocItem';
import TOC from '@theme/TOC';
import EditThisPage from '@theme/EditThisPage';
import {MainHeading} from '@theme/Heading';
import clsx from 'clsx';
import styles from './styles.module.css';
@ -49,13 +50,15 @@ function DocItem(props: Props): JSX.Element {
// See https://github.com/facebook/docusaurus/issues/3362
const showVersionBadge = versions.length > 1;
// For meta title, using frontMatter.title in priority over a potential # title found in markdown
// See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
const metaTitle = frontMatter.title || title;
// We only add a title if:
// - user asks to hide it with frontmatter
// - the markdown content does not already contain a top-level h1 heading
const shouldAddTitle =
!hideTitle && typeof DocContent.contentTitle === 'undefined';
return (
<>
<Seo {...{title: metaTitle, description, keywords, image}} />
<Seo {...{title, description, keywords, image}} />
<div className="row">
<div
@ -72,12 +75,13 @@ function DocItem(props: Props): JSX.Element {
</span>
</div>
)}
{!hideTitle && (
<header>
<h1 className={styles.docTitle}>{title}</h1>
</header>
)}
<div className="markdown">
{/*
Title can be declared inside md content or declared through frontmatter and added manually
To make both cases consistent, the added title is added under the same div.markdown block
See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
*/}
{shouldAddTitle && <MainHeading>{title}</MainHeading>}
<DocContent />
</div>
</article>

View file

@ -5,11 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
.docTitle {
font-size: 3rem;
margin-bottom: calc(var(--ifm-leading-desktop) * var(--ifm-leading));
}
.docItemContainer {
margin: 0 auto;
padding: 0 0.5rem;

View file

@ -16,7 +16,24 @@ import {useThemeConfig} from '@docusaurus/theme-common';
import './styles.css';
import styles from './styles.module.css';
const Heading = (Tag: HeadingType): ((props: Props) => JSX.Element) =>
type HeadingComponent = (props: Props) => JSX.Element;
export const MainHeading: HeadingComponent = function MainHeading({...props}) {
return (
<header>
<h1
{...props}
id={undefined} // h1 headings do not need an id because they don't appear in the TOC
className={styles.h1Heading}>
{props.children}
</h1>
</header>
);
};
const createAnchorHeading = (
Tag: HeadingType,
): ((props: Props) => JSX.Element) =>
function TargetComponent({id, ...props}) {
const {
navbar: {hideOnScroll},
@ -51,4 +68,8 @@ const Heading = (Tag: HeadingType): ((props: Props) => JSX.Element) =>
);
};
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) => {
return headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
};
export default Heading;

View file

@ -8,3 +8,8 @@
.enhancedAnchor {
top: calc(var(--ifm-navbar-height) * -1 - 0.5rem);
}
.h1Heading {
font-size: 3rem;
margin-bottom: calc(var(--ifm-leading-desktop) * var(--ifm-leading));
}

View file

@ -114,6 +114,7 @@ declare module '@theme/Heading' {
const Heading: (Tag: HeadingType) => (props: Props) => JSX.Element;
export default Heading;
export const MainHeading: (props: Props) => JSX.Element;
}
declare module '@theme/hooks/useAnnouncementBar' {

View file

@ -141,11 +141,71 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: markdown,
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 title at the top and remove it', () => {
const markdown = dedent`
# Markdown Title
Lorem Ipsum
`;
expect(
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
).toEqual({
content: 'Lorem Ipsum',
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 title at the top and unwrap inline code block', () => {
const markdown = dedent`
# \`Markdown Title\`
Lorem Ipsum
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: markdown,
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 title and trim content', () => {
const markdown = `
# Markdown Title
Lorem Ipsum
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: markdown.trim(),
contentTitle: 'Markdown Title',
});
});
test('Should parse not parse markdown h1 title and trim content', () => {
const markdown = `
Lorem Ipsum
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: markdown.trim(),
contentTitle: undefined,
});
});
test('Should parse markdown h1 title with fixed anchor-id syntax', () => {
const markdown = dedent`
@ -155,7 +215,7 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: 'Lorem Ipsum',
content: markdown,
contentTitle: 'Markdown Title',
});
});
@ -169,7 +229,7 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: 'Lorem Ipsum',
content: markdown,
contentTitle: 'Markdown Title',
});
});
@ -185,12 +245,7 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: dedent`
## Heading 2
Lorem Ipsum
`,
content: markdown,
contentTitle: 'Markdown Title',
});
});
@ -206,12 +261,7 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: dedent`
# Markdown Title 2
Lorem Ipsum
`,
content: markdown,
contentTitle: 'Markdown Title',
});
});
@ -227,15 +277,7 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: dedent`
Lorem Ipsum
# Markdown Title 2
Lorem Ipsum
`,
content: markdown,
contentTitle: undefined,
});
});
@ -250,6 +292,23 @@ describe('parseMarkdownContentTitle', () => {
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: markdown,
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 alternate title and remove it', () => {
const markdown = dedent`
Markdown Title
================
Lorem Ipsum
`;
expect(
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
).toEqual({
content: 'Lorem Ipsum',
contentTitle: 'Markdown Title',
});
@ -271,17 +330,7 @@ describe('parseMarkdownContentTitle', () => {
// remove the useless line breaks? Does not matter too much
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: dedent`
import Component1 from '@site/src/components/Component1';
import Component2 from '@site/src/components/Component2'
import Component3 from '@site/src/components/Component3'
import './styles.css';
Lorem Ipsum
`,
content: markdown,
contentTitle: 'Markdown Title',
});
});
@ -306,10 +355,16 @@ import "module-name"
# Markdown Title
Lorem Ipsum
`;
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: `
content: markdown.trim(),
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 title placed after various import declarations and remove it', () => {
const markdown = `
import DefaultComponent from '@site/src/components/Component1';
import DefaultComponent2 from '../relative/path/Component2';
import * as EntireComponent from './relative/path/Component3';
@ -325,16 +380,22 @@ import './styles.css';
import _ from 'underscore';
import "module-name"
# Markdown Title
Lorem Ipsum
`.trim(),
`;
expect(
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
).toEqual({
content: markdown.trim().replace('# Markdown Title', ''),
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 alternate title placed after import declarations', () => {
const markdown = dedent`
import Component from '@site/src/components/Component';
import Component from '@site/src/components/Component'
import './styles.css';
@ -346,41 +407,40 @@ Lorem Ipsum
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: dedent`
import Component from '@site/src/components/Component';
import Component from '@site/src/components/Component'
import './styles.css';
Lorem Ipsum
`,
content: markdown,
contentTitle: 'Markdown Title',
});
});
test('Should parse title-only', () => {
const markdown = '# Document With Only A Title ';
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: '',
contentTitle: 'Document With Only A Title',
});
});
test('Should parse markdown h1 title at the top but keep it in content', () => {
test('Should parse markdown h1 alternate title placed after import declarations and remove it', () => {
const markdown = dedent`
# Markdown Title
import Component from '@site/src/components/Component';
import Component from '@site/src/components/Component'
import './styles.css';
Markdown Title
==============
Lorem Ipsum
`;
expect(
parseMarkdownContentTitle(markdown, {keepContentTitle: true}),
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
).toEqual({
content: markdown.trim(),
content: markdown.replace('Markdown Title\n==============\n\n', ''),
contentTitle: 'Markdown Title',
});
});
test('Should parse title-only', () => {
const markdown = '# Document With Only A Title';
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: markdown,
contentTitle: 'Document With Only A Title',
});
});
test('Should not parse markdown h1 title in the middle of a doc', () => {
const markdown = dedent`
@ -439,7 +499,13 @@ Lorem Ipsum
`;
expect(parseMarkdownContentTitle(markdown)).toEqual({
content: dedent`
content: markdown,
contentTitle: 'Markdown Title',
});
});
test('Should parse markdown h1 title placed after multiple import declarations and remove it', () => {
const markdown = dedent`
import Component1 from '@site/src/components/Component1';
import Component2 from '@site/src/components/Component2';
import Component3 from '@site/src/components/Component3';
@ -456,11 +522,16 @@ Lorem Ipsum
import Component14 from '@site/src/components/Component14';
import Component15 from '@site/src/components/Component15';
# Markdown Title
Lorem Ipsum
`,
`;
expect(
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
).toEqual({
content: markdown.replace('# Markdown Title', ''),
contentTitle: 'Markdown Title',
});
});
@ -497,7 +568,9 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "Some text",
"content": "# Markdown Title
Some text",
"contentTitle": "Markdown Title",
"excerpt": "Some text",
"frontMatter": Object {},
@ -518,7 +591,9 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "Some text",
"content": "# Markdown Title
Some text",
"contentTitle": "Markdown Title",
"excerpt": "Some text",
"frontMatter": Object {
@ -542,36 +617,11 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "Some text",
"contentTitle": "Markdown Title alternate",
"excerpt": "Some text",
"frontMatter": Object {
"title": "Frontmatter title",
},
}
`);
});
test('should not warn for duplicate title if keepContentTitle=true', () => {
expect(
parseMarkdownString(
dedent`
---
title: Frontmatter title
---
# Markdown Title
Some text
`,
{keepContentTitle: true},
),
).toMatchInlineSnapshot(`
Object {
"content": "# Markdown Title
"content": "Markdown Title alternate
================
Some text",
"contentTitle": "Markdown Title",
"contentTitle": "Markdown Title alternate",
"excerpt": "Some text",
"frontMatter": Object {
"title": "Frontmatter title",
@ -605,24 +655,6 @@ describe('parseMarkdownString', () => {
`);
});
test('should parse markdown title and keep it in content', () => {
expect(
parseMarkdownString(
dedent`
# Markdown Title
`,
{keepContentTitle: true},
),
).toMatchInlineSnapshot(`
Object {
"content": "# Markdown Title",
"contentTitle": "Markdown Title",
"excerpt": undefined,
"frontMatter": Object {},
}
`);
});
test('should delete only first heading', () => {
expect(
parseMarkdownString(dedent`
@ -636,7 +668,9 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "test test test # test bar
"content": "# Markdown Title
test test test # test bar
# Markdown Title 2
@ -692,7 +726,7 @@ describe('parseMarkdownString', () => {
test('should parse title only', () => {
expect(parseMarkdownString('# test')).toMatchInlineSnapshot(`
Object {
"content": "",
"content": "# test",
"contentTitle": "test",
"excerpt": undefined,
"frontMatter": Object {},
@ -708,7 +742,8 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "",
"content": "test
===",
"contentTitle": "test",
"excerpt": undefined,
"frontMatter": Object {},
@ -726,7 +761,7 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "",
"content": "# test",
"contentTitle": "test",
"excerpt": undefined,
"frontMatter": Object {
@ -766,7 +801,9 @@ describe('parseMarkdownString', () => {
`),
).toMatchInlineSnapshot(`
Object {
"content": "test test test test test test
"content": "# test
test test test test test test
test test test # test bar
# test2
### test

View file

@ -80,11 +80,21 @@ export function parseFrontMatter(
};
}
// Try to convert markdown heading as text
// Does not need to be perfect, it is only used as a fallback when frontMatter.title is not provided
// For now, we just unwrap possible inline code blocks (# `config.js`)
function toTextContentTitle(contentTitle: string): string {
if (contentTitle.startsWith('`') && contentTitle.endsWith('`')) {
return contentTitle.substring(1, contentTitle.length - 1);
}
return contentTitle;
}
export function parseMarkdownContentTitle(
contentUntrimmed: string,
options?: {keepContentTitle?: boolean},
options?: {removeContentTitle?: boolean},
): {content: string; contentTitle: string | undefined} {
const keepContentTitleOption = options?.keepContentTitle ?? false;
const removeContentTitleOption = options?.removeContentTitle ?? false;
const content = contentUntrimmed.trim();
@ -108,16 +118,15 @@ export function parseMarkdownContentTitle(
if (!pattern || !title) {
return {content, contentTitle: undefined};
} else {
const newContent = removeContentTitleOption
? content.replace(pattern, '')
: content;
return {
content: newContent.trim(),
contentTitle: toTextContentTitle(title.trim()).trim(),
};
}
const newContent = keepContentTitleOption
? content
: content.replace(pattern, '');
return {
content: newContent.trim(),
contentTitle: title.trim(),
};
}
type ParsedMarkdown = {
@ -129,22 +138,16 @@ type ParsedMarkdown = {
export function parseMarkdownString(
markdownFileContent: string,
options?: {
keepContentTitle?: boolean;
},
options?: {removeContentTitle?: boolean},
): ParsedMarkdown {
try {
const keepContentTitle = options?.keepContentTitle ?? false;
const {frontMatter, content: contentWithoutFrontMatter} = parseFrontMatter(
markdownFileContent,
);
const {content, contentTitle} = parseMarkdownContentTitle(
contentWithoutFrontMatter,
{
keepContentTitle,
},
options,
);
const excerpt = createExcerpt(content);
@ -166,10 +169,11 @@ This can happen if you use special characters like : in frontmatter values (try
export async function parseMarkdownFile(
source: string,
options?: {removeContentTitle?: boolean},
): Promise<ParsedMarkdown> {
const markdownString = await fs.readFile(source, 'utf-8');
try {
return parseMarkdownString(markdownString);
return parseMarkdownString(markdownString, options);
} catch (e) {
throw new Error(
`Error while parsing markdown file ${source}

View file

@ -1,10 +1,11 @@
---
id: docusaurus.config.js
title: docusaurus.config.js
description: API reference for Docusaurus configuration file.
slug: /docusaurus.config.js
---
# `docusaurus.config.js`
## Overview {#overview}
`docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site.

View file

@ -197,7 +197,7 @@ Markdown documents can use the following markdown frontmatter metadata fields, e
- `id`: A unique document id. If this field is not present, the document's `id` will default to its file name (without the extension)
- `title`: The title of your document. If this field is not present, the document's `title` will default to its `id`
- `hide_title`: Whether to hide the title at the top of the doc. By default, it is `false`
- `hide_title`: Whether to hide the title at the top of the doc. It only hides a title declared through the frontmatter, and have no effect on a title at the top of your Markdown document. By default, it is `false`
- `hide_table_of_contents`: Whether to hide the table of contents to the right. By default it is `false`
- `sidebar_label`: The text shown in the document sidebar and in the next/previous button for this document. If this field is not present, the document's `sidebar_label` will default to its `title`
- `sidebar_position`: Permits to control the position of a doc inside the generated sidebar slice, when using `autogenerated` sidebar items. Can be Int or Float.