10 mins read

Markdown Extension Syntax

A comprehensive guide to enhanced Markdown features in Litos theme

Markdown Extension Syntax

This guide has made slight changes based on the markdown-mdx-extended-features. Thank you to the original author Stephanie Lin for efforts.

Calloutsh2

Supported by the rehype-callouts , you can configure the plugin in plugins/index.ts.

If you change the theme configuration (default: 'vitepress'), you will also need to update the imported CSS file in src/styles/pro.css (@import 'rehype-callouts/theme/yourconfig').

<!-- Callout type names are case-insensitive: 'Note', 'NOTE', and 'note' are equivalent. -->
<!-- vitepress -->
<!-- This is a _non-collapsible_ callout -->
> [!note]
> Note content.
> [!tip]
> Tip content.
> [!important]
> Important content.
> [!warning]
> Warning content.
> [!caution]
> Caution content.
> [!caution]- This is a **collapsible** callout
> Caution content.
> [!note]+ This is a **collapsible** callout
> Note content.
NOTE

Note content.

TIP

Tip content.

IMPORTANT

Important content.

WARNING

Warning content.

CAUTION

Caution content.

This is a collapsible callout

Caution content.

This is a collapsible callout

Note content.

Supported by astro-expressive-code with @expressive-code/plugin-collapsible-sections and @expressive-code/plugin-line-numbers plugins to add styling and extra functionality for code blocks.

To customize code block themes or functionality, modify the ec.config.mjs file at the project root after reviewing the Configuring Expressive Code, such as change themes, enable word wrap, or toggle line numbers.

Here’s a quick preview of what’s possible. Check the detailed guide for more info.

Syntax highlightingh4

example.md
console.log('This code is syntax highlighted!')
ansi-example.md
ANSI colors:
- Regular: Red Green Yellow Blue Magenta Cyan
- Bold: Red Green Yellow Blue Magenta Cyan
- Dimmed: Red Green Yellow Blue Magenta Cyan
256 colors (showing colors 160-177):
160 161 162 163 164 165
166 167 168 169 170 171
172 173 174 175 176 177
Full RGB colors:
ForestGreen - RGB(34, 139, 34)
Text formatting: Bold Dimmed Italic Underline
Code editor framesh5
my-test-file.js
// Use `title="my-test-file.js"`
console.log('Title attribute example')
src/content/index.ts
// Use `// src/content/index.ts`
console.log('File name comment example')
Terminal framesh5
Terminal window
echo "This terminal frame has no title"
PowerShell terminal example
Write-Output "This one has a title!"
Marking full lines & line rangesh5
// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range "7-8"
// Line 8 - targeted by range "7-8"
Selecting line marker types (mark, ins, del)h5
line-markers.js
function demo() {
console.log('this line is marked as deleted')
// This line and the next one are marked as inserted
console.log('this is the second inserted line')
return 'this line uses the neutral default marker type'
}
Adding labels to line markersh5
labeled-line-markers.jsx
<button
role="button"
{...props}
value={value}
className={buttonClassName}
disabled={disabled}
active={active}
>
{children &&
!active &&
(typeof children === 'string' ? <span>{children}</span> : children)}
</button>
Adding long labels on their own linesh5
labeled-line-markers.jsx
<button
role="button"
{...props}
value={value}
className={buttonClassName}
disabled={disabled}
active={active}
>
{children &&
!active &&
(typeof children === 'string' ? <span>{children}</span> : children)}
</button>
Using diff-like syntaxh5
this line will be marked as inserted
this line will be marked as deleted
this is a regular line
function thisIsJavaScript() {
// This entire block gets highlighted as JavaScript,
// and we can still add diff markers to it!
console.log('Old code to be removed')
console.log('New and shiny code!')
}
Marking individual text inside linesh5
// Plaintext search strings
function demo() {
// Mark any given text inside lines
return 'Multiple matches of the given text are supported'
}
Marking individual text inside linesh5
// Regular expressions
console.log('The words yes and yep will be marked.')
Terminal window
# Regular expressions
echo "Test" > /home/test.txt
// Regular expressions
If you only want to mark certain parts matched by your regular expression, you can use capture groups.
For example, the expression `/ye(s|p)/` will match yes and yep, but only mark the character s or p:
// Regular expressions
To prevent this special treatment of capture groups, you can convert them to non-capturing groups by adding ?: after the opening parenthesis. For example:
This block uses `/ye(?:s|p)/`, which causes the full
matching words "yes" and "yep" to be marked.
// Selecting inline marker types (mark, ins, del)
function demo() {
console.log('These are inserted and deleted marker types');
// The return statement uses the default marker type
return true;
}
Configuring word wrap per blockh5
// Example with wrap
function getLongString() {
return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'
}
// Example with wrap=false
function getLongString() {
return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'
}
Configuring indentation of wrapped linesh5
// Example with preserveIndent (enabled by default)
function getLongString() {
return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'
}
// Example with preserveIndent=false
function getLongString() {
return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'
}
Collapsible sectionsh5
5 collapsed lines
// All this boilerplate setup code will be collapsed
import { someBoilerplateEngine } from '@example/some-boilerplate'
import { evenMoreBoilerplate } from '@example/even-more-boilerplate'
const engine = someBoilerplateEngine(evenMoreBoilerplate())
// This part of the code will be visible by default
engine.doSomething(1, 2, 3, calcFn)
function calcFn() {
// You can have multiple collapsed sections
3 collapsed lines
const a = 1
const b = 2
const c = a + b
// This will remain visible
console.log(`Calculation result: ${a} + ${b} = ${c}`)
return c
}
4 collapsed lines
// All this code until the end of the block will be collapsed again
engine.closeConnection()
engine.freeMemory()
engine.shutdown({ reason: 'End of example boilerplate code' })
Displaying line numbers per blockh5
// This code block will show line numbers
console.log('Greetings from line 2!')
console.log('I am on line 3')
// Line numbers are disabled for this block
console.log('Hello?')
console.log('Sorry, do you know what line I am on?')
// Changing the starting line number
console.log('Greetings from line 5!')
console.log('I am on line 6')

Use the :::image directive from remark-directive-sugar to wrap images in a container for captions, clickable links, and more. Customize via the image option in plugins/index.ts (remarkDirectiveSugar) and style under /* :::image */ in src/styles/pro.css.

:::image-figureh3

:::image-figure[caption]{<figcaption> attrs}: The square brackets define the <figcaption> text (defaults to the alt text from ![]() if omitted), while the curly braces are used for inline styles or supported attributes to the generated <figcaption> element.

![alt](image path)(<img> attrs): Standard Markdown image with optional attributes in parentheses, enabled by remark-imgattr, for customizing the generated <img> element.

:::image-figure[caption]{<figcaption> attrs}: The square brackets define the <figcaption> text (defaults to the alt text from ![]() if omitted), while the curly braces are used for inline styles or supported attributes to the generated <figcaption> element.

![alt](image path)(<img> attrs): Standard Markdown image with optional attributes in parentheses, enabled by remark-imgattr, for customizing the generated <img> element.

:::image-figure.md
:::image-figure[This Is a **Figcaption** with _`<figure>` Attrs_]{style="text-align:center;color:orange"}
![](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
:::image-figure[This is a **figcaption** with _`<img>` attrs_.]
![](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)(style: width:600px;)
:::
<!-- 💡 Use `(class:no-zoom)` to disable zoom -->
:::image-figure[This is a **figcaption** with `class:no-zoom`.]
![](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)(class:no-zoom)
:::
<!-- 💡 If no `[caption]`, use `[alt]` as figcaption. -->
:::image-figure
![If `[caption]` not set, the alt text from `![]()` will be used as the figcaption.](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
<!-- 💡 Images for light (img-light) and dark (img-dark) modes -->
<!-- ⚠️ At least one line must separate two image syntaxes (![]()), or won't work. -->
:::image-figure[This example shows different images for light (add `class:img-light`) and dark (add `class:img-dark`) modes.]
![](~/assets/images/markdown-extension-syntax/image-16-9-light.png)(class:img-light)
![](~/assets/images/markdown-extension-syntax/image-16-9-light.png)(class:img-dark)
:::
<!-- ❌ If no text is available for the figcaption, it won't work. -->
:::image-figure
![](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
This Is a Figcaption with <figure> Attrs
This is a figcaption with <img> attrs.
This is a figcaption with class:no-zoom.
If [caption] not set, the alt text from ![]() will be used as the figcaption.
If [caption] not set, the alt text from ![]() will be used as the figcaption.
This example shows different images for light (add class:img-light) and dark (add class:img-dark) modes.
WARNING

Setting an image’s width attribute directly may cause blurriness. Learn more

:::image-ah3

The custom directive wraps an image inside a link, making it clickable.

:::image-a{<a> attrs}: Define the link (href), styles, or classes in the curly braces for <a> element.

![alt](image path)(<img> attrs): Same as above.

:::image-a.md
:::image-a{href="https://github.com/Dnzzk2/Litos"}
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
:::image-a{href="https://github.com/Dnzzk2/Litos" style="display:block" .custom-class}
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)(style: margin-bottom: -1rem; transform:scaleX(1.1) scaleY(1.1);, loading: eager)
:::
::::image-a{href="https://github.com/Dnzzk2/Litos"}
:::image-figure[This example shows `:::image-a` wraps around `:::image-figure` (both are interchangeable).]
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
::::
<!-- ❌ No external links provided, it won't work.-->
:::image-a
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
OG image OG image
OG image
This example shows :::image-a wraps around :::image-figure (both are interchangeable).

:::image-figure-polaroidh3

Polaroid style images with a border and shadow.

In order to ensure the style size on the phone, I have set a minimum width of 300px, and you can modify and expand the style in src/styles/picture.css.

:::image-figure-polaroid.md
:::::image-div-polaroid
:::image-figure-polaroid[This is a **figcaption** with _`<img>` attrs_.]
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
:::::
:::::image-div-polaroid
:::image-figure-polaroid
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
markdown-extension-syntax.png
:::
:::::
:::::image-div-polaroid
:::image-figure-polaroid
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
:::::
<!-- change style -->
:::::image-div-polaroid
:::image-figure-polaroid{style="width:500px;"}
![OG image](~/assets/images/markdown-extension-syntax/markdown-extension-syntax.png)
:::
:::::

This is a figcaption with <img> attrs.

OG image
OG image

markdown-extension-syntax.png

OG image OG image

Video Embeddingh2

Use the ::video directive from remark-directive-sugar for consistent video embedding across different platforms. Customize via the video option in plugins/index.ts and style under /* ::video */ in src/styles/pro.css.

Say example.md contains:

example.md
<!-- Embed a YouTube video -->
::video-youtube{#gxBkghlglTg}
<!-- Embed a Bilibili video with a custom `title` attr -->
::video-bilibili[custom title]{id=BV1MC4y1c7Kv}
<!-- Embed a Vimeo video with class `no-scale` to disable scaling -->
::video-vimeo{id=912831806 class='no-scale'}
<!-- ::video-vimeo{id=912831806 .no-scale} -->
<!-- Embed a custom video URL (must use `id`, not `#`) -->
::video{id=https://www.youtube-nocookie.com/embed/gxBkghlglTg}

Then example.mdx renders as:

Use the :link directive from remark-directive-sugar to add links with avatars or favicons for GitHub, npm, or custom URLs. Customize via the link option in plugins/index.ts and style under /* :link */ in src/styles/pro.css.

Link to a GitHub user or organization (prepend id with @)

Example 1: :link[Dnzzk2]{#@Dnzzk2} links to the GitHub profile of the project maintainer, Dnzzk2.

Example 2: :link[Vite]{id=@vitejs} links to the GitHub profile of the Vite organization.

Example 3: :link{#@Dnzzk2 tab=repositories} links directly to the repositories tab of the GitHub user, like Dnzzk2. For GitHub users, valid tab options: 'repositories','projects', 'packages', 'stars', 'sponsoring', 'sponsors'.

Example 4: :link{#@vitejs tab=org-people} links directly to the people section of a GitHub organization, like vitejs. For GitHub organizations, valid tab options: 'org-repositories', 'org-projects', 'org-packages', 'org-sponsoring', and 'org-people'.

Link to a GitHub repository

Example 5: :link[Astro]{#withastro/astro} or :link[Astro]{id=withastro/astro} creates a link to Astro repo.

Link to an npm package

Example 6: :link{#remark-directive-sugar} links to the npm homepage of the remark-directive-sugar.

Example 7: :link{id=remark-directive-sugar tab=dependencies} links to the dependencies section of the remark-directive-sugar on npm. For npm package, valid tab options: 'readme', 'code', 'dependencies', 'dependents', and 'versions'.

Link to a custom URL (must use id, not #)

Example 8: :link{id=https://developer.mozilla.org/en-US/docs/Web/JavaScript} creates an external link to the developer.mozilla.org/en-US/docs/Web....

Example 9: :link[Google]{id=https://www.google.com/} creates an external link to the Google.

Customization

Example 10: :link[Vite]{id=@vitejs url=https://vite.dev/} creates a Vite to https://vite.dev/ instead of https://github.com/vitejs by using the url.

Example 11: :link[Vite]{id=@vitejs img=https://vitejs.dev/logo.svg} creates a Vite that displays a custom logo by using the img.

Example 12: :link{id=Dnzzk2/Litos class=github} creates a Dnzzk2/Litos with class=github (or .github) to override the default style of a GitHub repository.

Example 13: :link[Litos Themes]{id=https://github.com/Dnzzk2/Litos img=https://github.githubassets.com/assets/mona-e50f14d05e4b.png} fully customizes a link. Litos Themes

Badgesh2

Use the :badge directive from remark-directive-sugar to display small pieces of information, such as status or category.

The theme provides the following one predefined badges. You can customize them via the badge option in plugins/index.ts and style them under /* :badge */ in src/styles/pro.css.

  • badge-n: NEW

Additionally, you can direct use :badge[text]{attrs} for easy visual customization of badges. For example: :badge[ISSUE]{style="background-color: #bef264"} will display as ISSUE. If no color is specified, the default appearance will look like This.

Details Dropdownh2

:::details.md
:::details
::summary[Details Dropdown]
- List item 1
- List item 2
- List item 3
- List item 4
:::
Details Dropdown
  • List item 1
  • List item 2
  • List item 3
  • List item 4

Additionally, it also supports usage similar to the examples in remark-directive.

Thank you once again to the original author Stephanie Lin for their development, which has enabled this theme to have such excellent extension functions 💗.