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 , you can modify rehype-calloutsthe 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.
Highlights information that users should take into account, even when skimming.
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.
Fully-featured Code Blocks#
Supported by with astro-expressive-code@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 , such as Configuring Expressive Codechange 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#
console.log('This code is syntax highlighted!')
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 165166 167 168 169 170 171172 173 174 175 176 177
Full RGB colors:ForestGreen - RGB(34, 139, 34)
Text formatting: Bold Dimmed Italic Underline
Code editor frames#
// Use `title="my-test-file.js"`console.log('Title attribute example')
// Use `// src/content/index.ts`console.log('File name comment example')
Terminal frames#
echo "This terminal frame has no title"
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)#
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#
<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#
<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 insertedthis line will be marked as deletedthis 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 lines#
// Plaintext search stringsfunction demo() { // Mark any given text inside lines return 'Multiple matches of the given text are supported'}
Marking individual text inside lines#
// Regular expressionsconsole.log('The words yes and yep will be marked.')
# Regular expressionsecho "Test" > /home/test.txt
// Regular expressionsIf 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 expressionsTo 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 fullmatching 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 block#
// Example with wrapfunction 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=falsefunction 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=falsefunction 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 collapsedimport { 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 defaultengine.doSomething(1, 2, 3, calcFn)
function calcFn() { // You can have multiple collapsed sections3 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 againengine.closeConnection()engine.freeMemory()engine.shutdown({ reason: 'End of example boilerplate code' })
Displaying line numbers per block#
// This code block will show line numbersconsole.log('Greetings from line 2!')console.log('I am on line 3')
// Line numbers are disabled for this blockconsole.log('Hello?')console.log('Sorry, do you know what line I am on?')
// Changing the starting line numberconsole.log('Greetings from line 5!')console.log('I am on line 6')
Image Caption & Link (:::image
)#
Use the :::image
directive from to wrap images in a container for captions, clickable links, and more. Customize via the remark-directive-sugarimage
option in plugins/index.ts
(remarkDirectiveSugar
) and style under /* :::image */
in src/styles/markdown.css
.
:::image-figure
#
:::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.
(<img> attrs)
: Standard Markdown image with optional attributes in parentheses, enabled by , for customizing the generated remark-imgattr<img>
element.
:::image-figure[This Is a **Figcaption** with _`<figure>` Attrs_]{style="text-align:center;color:orange"}:::
:::image-figure[This is a **figcaption** with _`<img>` attrs_.](style: width:600px;):::
<!-- 💡 Use `(class:no-zoom)` to disable zoom -->:::image-figure[This is a **figcaption** with `class:no-zoom`.](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/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.](class:img-light)
(class:img-dark):::
<!-- ❌ If no text is available for the figcaption, it won't work. -->:::image-figure:::
<figure>
Attrs<img>
attrs.class:no-zoom
.class:img-light
) and dark (add class:img-dark
) modes.Setting an image’s width
attribute directly may cause blurriness. Learn more
:::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.
(<img> attrs)
: Same as above.
:::image-a{href="https://github.com/lin-stephanie/astro-antfustyle-theme"}:::
:::image-a{href="https://github.com/lin-stephanie/astro-antfustyle-theme" style="display:block" .custom-class}(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).]:::::::
<!-- ❌ No external links provided, it won't work.-->:::image-a:::
:::image-a
wraps around :::image-figure
(both are interchangeable).Video Embedding(::video
)#
Use the ::video
directive from for consistent video embedding across different platforms. Customize via the remark-directive-sugarvideo
option in plugins/index.ts
and style under /* ::video */
in src/styles/markdown.css
.
Say example.md
contains:
<!-- 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:
Styled Link(:link
)#
Use the :link
directive from to add links with avatars or favicons for GitHub, npm, or custom URLs. Customize via the remark-directive-sugarlink
option in plugins/index.ts
and style under /* :link */
in src/styles/markdown.css
.
Link to a GitHub user or organization (prepend id
with @
)
Example 1: :link[Stephanie Lin]{#@lin-stephanie}
links to the GitHub profile of the project maintainer, . Stephanie Lin
Example 2: :link[Vite]{id=@vitejs}
links to the GitHub profile of the organization. Vite
Example 3: :link{#@lin-stephanie tab=repositories}
links directly to the repositories tab of the GitHub user, like . For GitHub users, valid lin-stephanietab
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 . For GitHub organizations, valid vitejstab
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 repo. Astro
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 on npm. For npm package, valid remark-directive-sugartab
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 to Vitehttps://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 that displays a custom logo by using the Viteimg
.
Example 12: :link{id=lin-stephanie/astro-antfustyle-theme class=github}
creates a with lin-stephanie/astro-antfustyle-themeclass=github
(or .github
) to override the default style of a GitHub repository.
Example 13: :link[Google]{id=https://www.google.com/ class=rounded}
creates a with Googleclass=rounded
(or .rounded
) to override the default style of a custom URL.
Example 14: :link[Google]{id=https://www.google.com/ class=square}
creates a with Googleclass=square
(or .square
) to override the default style of a custom URL.
Example 15: :link[send a little encouragement my way]{id=https://github.com/sponsors/lin-stephanie img=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
Badges(:badge
)#
Use the :badge
directive from to display small pieces of information, such as status or category. remark-directive-sugar
The theme provides the following three predefined badges. You can customize them via the badge
option in plugins/index.ts
and style them under /* :badge */
in src/styles/markdown.css
.
badge-n
: NEWbadge-a
: ARTICLEbadge-v
: VIDEO
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 Dropdown#
:::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! ⚡️
Changelog
2025-03-31
- Update: Image Caption & Link, Video Embedding, Styled Link and Badges