Skip to content

Markdown/MDX Extended Features

· 10 min

This post introduces some handy tricks for using special syntax in the Astro AntfuStyle Theme to enhance your Markdown/Mdx content. These shortcuts let the theme’s built-in integrations or plugins handle the heavy lifting, automatically converting everything into HTML --- no need to write complex code yourself! 🎨

Callouts (Alerts / Admonitions)#

Supported by rehype-callouts, you can modify the plugin’s configuration 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/markdown.css (@import 'rehype-callouts/theme/yourconfig').

<!-- Callout type names are case-insensitive: 'Note', 'NOTE', and 'note' are equivalent. -->
<!-- With 'vitepress' theme, you can use the following callout types: -->
> [!note]
> Highlights information that users should take into account, even when skimming.
> [!TIP] You can customize the `title`
> Optional information to help a user be more successful.
> [!Important]- This is a **collapsible** callout, **initially close**.
> Crucial information necessary for users to succeed.
> [!Warning]+ This is a _collapsible and nested_ callout, _initially open_.
>
> Critical content demanding immediate user attention due to potential risks.
>
> > [!caution]- This is a **collapsible** callout, too
> > Negative potential consequences of an action.
NOTE

Highlights information that users should take into account, even when skimming.

You can customize the title

Optional information to help a user be more successful.

This is a collapsible callout, initially close.

Crucial information necessary for users to succeed.

This is a collapsible and nested callout, initially open.

Critical content demanding immediate user attention due to potential risks.

This is also a collapsible callout.

Negative potential consequences of an action.

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 Highlighting#
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 frames#
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 frames#
Terminal window
echo "This terminal frame has no title"
PowerShell terminal example
Write-Output "This one has a title!"
Marking full lines & line ranges#
// 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)#
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 markers#
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 lines#
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 syntax#
this line will be marked as inserted
this line will be marked as deleted
this is a regular line
this line will be marked as inserted
this line will be marked as deleted
this is a regular line
Marking individual text inside lines (Plaintext search strings)#
function demo() {
// Mark any given text inside lines
return 'Multiple matches of the given text are supported'
}
Marking individual text inside lines (Regular expressions)#
console.log('The words yes and yep will be marked.')
Terminal window
echo "Test" > /home/test.txt
Marking individual text inside lines (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 block#
// 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 lines#
// 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 sections#
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
return a + b
}
Displaying line numbers per block#
// 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')

Build on remark-directive with a custom remark-image-container plugin (located in plugins/remark-image-container.ts) to quickly add image captions,links and more.

TIP

You can refer to the Remark Directive Syntax for a quick overview of its basic rules --- it’s easy to understand and remember!

:::image-figure#

The custom directive creates a block with an image, figcaption, and optional styling, much like a figure in academic papers.

:::image-figure[caption]{<figcaption> attrs}: The square brackets hold the figcaption (if not set, the alt text from ![]() will be used as the default), and curly braces are for inline styles or supported attributes.

![alt](image path)(<img> attrs): Standard Markdown image syntax with optional attributes inside parentheses (powered by remark-imgattr, allowing you to adjust the size of individual images).

:::image-figure.md
:::image-figure[This Is a **Figcaption** with _`<figure>` Attrs_]{style="text-align:center;color:orange"}
![](../../assets/markdown-mdx-extended-featurs/og-image.png)
:::
:::image-figure[This is a **figcaption** with _`<img>` attrs_.]
![](~/assets/markdown-mdx-extended-featurs/og-image.png)(width:600)
:::
<!-- 💡 Use `(class:no-zoom)` to disable zoom -->
:::image-figure[This is a **figcaption** with `class:no-zoom`.]
![](~/assets/markdown-mdx-extended-featurs/og-image.png)(class:no-zoom)
:::
<!-- 💡 If no `[caption]`, use alt text as figcaption. -->
:::image-figure
![If [caption] not set, the alt text from ![]() will be used as the figcaption.](~/assets/markdown-mdx-extended-featurs/og-image.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/markdown-mdx-extended-featurs/rose-dark.png)(class:img-light)
![](~/assets/markdown-mdx-extended-featurs/plum-light.png)(class:img-dark)
:::
<!-- ❌ If no text is available for the figcaption, it won't work. -->
:::image-figure
![](~/assets/markdown-mdx-extended-featurs/og-image.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.

:::image-a#

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/lin-stephanie/astro-antfustyle-theme"}
![OG image](~/assets/markdown-mdx-extended-featurs/og-image.png)
:::
:::image-a{href="https://github.com/lin-stephanie/astro-antfustyle-theme" style="display:block" .custom-class}
![OG image](~/assets/markdown-mdx-extended-featurs/og-image.png)(style: margin-bottom: -1rem; transform:scaleX(1.1) scaleY(1.1);, loading: eager)
:::
::::image-a{href="https://github.com/lin-stephanie/astro-antfustyle-theme"}
:::image-figure[This example shows `:::image-a` wraps around `:::image-figure` (both are interchangeable).]
![OG image](~/assets/markdown-mdx-extended-featurs/og-image.png)
:::
::::
<!-- ❌ No external links provided, it won't work.-->
:::image-a
![OG image](~/assets/markdown-mdx-extended-featurs/og-image.png)
:::

OG image

OG image

OG image

This example shows :::image-a wraps around :::image-figure (both are interchangeable).

Video Embedding(::video#

Similarly, this theme includes predefined directives through a custom remark-sugar-directive plugin (located in plugins/remark-sugar-directive.ts), enabling quick implementation of features like video embedding, styled GitHub links, badge-like markers and details dropdown as described below.

The custom ::video directive allows for consistent video embedding across different platforms, with flexible options for configuration.

::video{<youtubeId|bilibiliId|vimeoId> <noScale> <iframeSrc>}: Curly braces contain the platform-specific video ID, along with optional parameters.

::video.md
::video{youtubeId=gxBkghlglTg}
::video{bilibiliId=BV1MC4y1c7Kv}
<!-- Setting `noScale=true` disables video scaling -->
::video{vimeoId=912831806 noScale=true}
<!-- You can directly provide the iframe URL for embedding a video via `iframeSrc` -->
::video{iframeSrc='https://www.youtube-nocookie.com/embed/gxBkghlglTg'}

The custom :link directive simplifies the process of creating styled links to GitHub repositories, users/organizations, or any external URLs with streamlined syntax.

Basic Syntax

Use :link with display text in square brackets and id parameter (or shorthand with a # prefix) in curly braces.

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

Example 2: :link[Stephanie Lin]{#@lin-stephanie} links to the GitHub profile of the project maintainer, Stephanie Lin.

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

IMPORTANT

When linking to a GitHub user or organization, you must prepend the id with @.

If the [] part is omitted, the link will display the text of the id parameter by default.

Specific Subsections

The :link directive also supports linking to specific sections within a GitHub user or organization using the tab parameter.

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

Example 5: :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'.

External URLs

When you want to link to something other than a GitHub resource, use the link parameter to customize external links. This parameter is required if no id is specified.

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

Icon and Style Customization

You can customize icons with the imageUrl parameter or adjust button styles using the styleparameter (optional values: 'square', 'rounded', 'github').

Example 7: :link[Astro]{#withastro/astro style=rounded} creates a rounded button like Astro.

Example 8: :link[Vite]{id=@vitejs style=square} creates a square button like Vite.

Example 9: :link{#lin-stephanie/astro-antfustyle-theme style=github} creates a GitHub-styled link like:

lin-stephanie/astro-antfustyle-theme

Example 10: :link[send a little encouragement my way ❤️]{link=https://github.com/sponsors/lin-stephanie imageUrl=https://github.githubassets.com/assets/mona-e50f14d05e4b.png} fully customizes a link.

Thanks for making it this far! Writing is no easy task --- maybe you’d like to send a little encouragement my way ❤️.

Badge-Like Markers(:badge#

The :badge directive allows you to add customizable markers to improve document visuals.

There are built-in markers that automatically apply predefined labels and colors based on the configuration:

You can find and redefine these configurations in plugins/remark-sugar-directive.ts.

Additionally, you can use :badge[Text]{<color>} for easy visual customization of badges. For example: :badge[ISSUE]{color=#aaf233} will display as ISSUE. If no color is specified, the default appearance will look like This.

Details Dropdown#

:::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.

Wrapping Up#

With the features above, the theme streamlines your Markdown/MDX content creation without requiring you to dive into complex HTML or CSS. Just focus on your ideas and let the theme handle the rest!

If you’re feeling adventurous, consider defining your own custom “directive syntax sugar” to tailor the experience even further! Contributions are always welcome --- feel free to join the discussion or submit an issue or pull request.

Thanks for checking out the theme. Have fun and enjoy creating! ⚡️

> share on twitter / mastodon
> cd ..