Best practices for Markdown authoring in VSC
This page helps you create a consistent, parallel, readable browsing experience. You achieve this by doing the small things consistently right, from the ground up. (If you’re looking for editorial standards and style guidelines, see Editorial guidelines.)
- Guide titles and descriptions on ExL landing pages
- Tables of contents
- Placement of common elements on pages
Tips and rules to prevent common errors common-errors
File and folder names
- Markdown filenames must be lowercase with hyphens. No capital letters, underscores, periods, or spaces. However, although not recommended, assets such as image files can include capital letters and underscores.
- Avoid filenames that can conflict with JavaScript/CSS conventions such as
metadata.md
andsearch.md
.
Articles
- Discrete components such as headings, fenced code blocks, and bullet/number lists should be surrounded by blank lines.
- Only a single h1 heading (
#
) is allowed in the document, and all later headings are h2 (##
) or lower level headings. First line in the file below the metadata should be h1 (top level heading, or title). - No duplicate heading IDs (anchor IDs) are allowed in a document.
- Heading IDs should not include periods. Bad example:
{#version-2.3}
Good example:{#version-2-3}
- No space between the markers of the text or surrounding the links e.g.
abcd
or[ abcd ]
TOC
- Every section heading in the TOC must have a valid section ID. Bad example:
+ Processing rules
Good example:+ Processing rules {#processing-rules}
- A section heading (parent) in the TOC cannot be a link.
- You must use proper syntax for TOC section headings (e.g.,
+ Processing rules {#processing-rules}
) and TOC article links (e.g.,+ [Article name](article.md)
). - Avoid adding the same file multiple times to a TOC (or to multiple TOCs). Doing so causes odd behavior.
Headings
Headings can be used to jump to a specific section of an article. You have the option of adding a specific heading anchor. (For editorial guidance, see Headings.)
Heading without anchor: ## Creating widgets
Heading with custom anchor: ## Creating widgets {#widgets}
In the first example, an anchor is generated automatically with the heading text hyphenated and made lowercase (creating-widgets
).
- search
- results
- facets
- pagination
- sorting
- query
- searchbox
- content
- header
- footer
- main
- navigation
- sidebar
- page
- container
- wrapper
For editorial guidance, see Headings.
Visual Studio Code tips vsc-tips
If you’re using Visual Studio Code (VSC) for editing, here are some tips and techniques that we’ve found useful.
Adobe Markdown Authoring extension for VSC adobe-extension
The Adobe Markdown Authoring extension is available for download in the VSC Extensions Marketplace.
-
Do the following:
- Uninstall any previous version of Adobe Markdown Authoring.
- Disable any custom preview extension you may have installed.
- Disable Markdown Shortcuts by mdickin if previously installed to avoid duplicated shortcuts.
-
In the left nav, click the Extensions icon.
-
Search for adobe markdown authoring.
-
Click Adobe Markdown Authoring, and then click Install.
This extension pack includes the following add-ons:
- Preview. Click the Preview icon
- Shortcuts. Right-click in the document area to view available keyboard shortcuts. You can edit these shortcuts or create your own, as described later.
- Validation. Most of the Adobe syntax rules are included in this add-on. If you add unsupported syntax, you’ll see squiggly underlines below the potentially offending text. Hover over the text to view the flagged problem.
Known bugs and limitations
- There isn’t a perfect match between the plugin’s syntax rules and the Jenkins validation rules. We try to keep the extension up to date, but there will rarely be a 100% match between Jenkins validation and what gets flagged in VS Code.
- The preview is not a 100% representation of what the rendered page will look like. Preview is an imperfect tool.
To view or report issues, see Issues.
Visual Studio Code add-ons we like vsc-addons
/
instead of \
, which is necessary for root links.Feel free to add any extension to this list that you find useful.
Use Quick Open
Command+P is your friend. If you need to open a file, press Command+P
and paste or start typing the filename, or open one of your recently edited files. Press Command+Shift+P
to open the Command palette.
Linking tips linking-tips
See the Linking tips article.
Global Find/Replace global-find-replace
Visual Studio Code lets you find/change text either within a document or across all the files you have open.
- Make sure that your cloned repo is in sync with the main branch on the server.
- Choose File > Open, and open the folder containing the repo, user guide, or section that you want to search.
- Choose Edit > Replace in Files. Specify the text you want to search for and what you want to replace it with.
-
Do any of the following:
- Click the Replace All icon next to the Replace box to replace all occurrences across all files.
- Click the Replace All icon next to the filename to replace all occurrences in that file.
- Click the Replace icon next to the occurrence to replace only that instance.
- Click the
x
icon next to any occurrance to remove it. This will exclude it from any Replace All action.
When you use the Replace in Files feature, replacing items automatically saves the affected files.
Wildcard find/replace wildcards
If you do a lot of finding and changing, or if you’re making global changes for a migration or cutover, using wildcards can save time. But this section isn’t for everyone.
Common expressions
- Use
(.+?)
for wildcard search text. - Use
$1
for replacing with the same wildcard content.$1
for first wildcard,$2
for second, and so on. - Use
^
for from start of line and$
for to end of line. - Use
[^n]
for not this character. - Use
\
before a character to escape a character that will otherwise be used as an expression.
-
Choose File > Open, and open the folder containing the repo, user guide, or section that you want to search.
-
Choose Edit > Replace or Replace in Files.
-
(Important) Click the Use Regular Expression icon to the right of the search box.
-
Specify the search string using
(.+?)
for wildcard.The parentheses indicate a group. The dot
.
indicates a wildcard. The plus sign+
indicates multiple characters. The question mark?
tells it not to be greedy so that the string ends with the first instance of whatever comes after the group. -
Specify the replace string using
$1
where you want the wildcard text to appear.The
$1
returns wildcard text was stored in the first group.
Wildcard search examples
Here are some useful strings for converting the loc tags in HTML table content.
search/replace docs.adobe.com links
https://docs.adobe.com/content/help/en/(.+?)$
https://experienceleague.adobe.com/docs/$1
search/replace links
<a href="(.+?)"(.+?)> (.+?)</a>
[$3]($1)
In the above example, we use three wildcard groups. The first group captures the link path. The second group captures HTML properties that we don’t need. The third group captures the link text. We then put the link text in brackets [$3]
and the link path in parentheses ($1)
.
search replace image
<img (.+?)src="assets/(.+?)"(.+?)>

You might need to tweak the above example by adding or removing spaces, depending on how the table was migrated.
search replace code
<span class="codeph">(.+?)</span>
`$1` (Markdown) or <code>$1</code> (HTML)
You might need to add a space before or after the wildcard group, depending on how the table was migrated.
search replace varname
<span class="varname">(.+?)</span>
*$1* (Markdown) or <i>$1</i>
search for a character that’s not a specific character
<a [^h]
In the above example, we want to find HTML links that start with something other than a href
.
\{[^#](.+?)\}
In the above example, we want to find anchor IDs that are missing the #
character.
&search replace [ ]
in headings
We’ll use [!DNL]
as an example below.
\# (.+?) \[!DNL(.+?)]
# $1$2
Above, we use \
to escape characters such as #
and [
used in expressions.
In the below example, we’ll look for anchors in a TOC that are missing the #
character, such as {admin}
instead of {#admin}
:
\{[^#]
Copy empty alt text with image name
For example, you want 
to become 
. Here’s how:
!\[\]\(assets/(.+?)\.(.+?)\)

Find spaces or tabs at end of a line
[\s]+$
Find step numbers that start with 2-9
instead of 1.
Best practice is to start all numbered list items with 1
so that steps are treated as a list. (If your numbering extends beyond 9, you’ll want to do a manual search for those numbers before you run this find/change. There’s probably a smarter way to do this…)
^[2-9]\.
Find headings with missing anchor hashes
We require {#anchor}
not {anchor}
.
\#(.+?)\{[^#](.+?)\}
Find markdown components with extra space
In Experience League, we don’t allow a space between >
and [!
in extension syntax. In other words, we accept >[!NOTE]
but not > [!NOTE]
. Use this search to find these unwanted spaces, and remove them.
^>\s+\[!
Post-migration cleanup searches post-migration-cleanup
You can use these Visual Studio Code search techniques to find common issues that appeared when the files were migrated. When it’s difficult to find errors in Visual Studio Code searches, you can use search patterns in Google to locate problems.
Visual Studio Code Searches
Here’s a list of searches in Visual Studio Code that help identify common formatting problems. Feel free to add to this list.
Basic (non-expression) searches
Use these basic searches in Visual Studio Code to find common formatting errors.
Find “the the”
- Syntax: “
the the
” (with “Match Case” selected and surrounding spaces) - Problem: “Time zone of the the data workbench server”
- Should be: “Time zone of the data workbench server”
Find any bad bolds before [!DNL]
or [!UICONTROL]
- Syntax:
** [!
- Problem:
** Visitor Page Views**
- Should be:
**Visitor Page Views**
Find failed bold in definition lists
- Syntax: "
* **
" (add space after**
) - Problem:
* ** Longitude column:** This column...
- Should be:
* **Longitude column:** This column...
Expression-based searches
For these search expressions, make sure that you turn on the Use Regular Expressions option.
Find any **
preceded by a space at the end of a line
-
Syntax:
code language-none ^(.+?) \*\*$
-
Problem: **Period-Based Unique Visitors Reports **
-
Should be: Period-Based Unique Visitors Reports
Find any **
followed by a space at the beginning of a line
-
Syntax:
code language-none ^\*\* (.+?)
-
Problem: ** Next steps:** We will begin to migrate users
-
Should be: Next steps: We will begin to migrate users
Find [!DNL]
or [!UICONTROL]
in HTML tables
These errors are usually the result of a global Find/Change that failed to account for HTML. Remember, Markdown syntax isn’t supported in HTML.
-
Syntax:
<p(.+?)\[!
-
Problem:
code language-none <td colname="col2"> <p>Replaces the <span class="uicontrol"> Show Metrics</span> popup in Reports & Analytics. </p>
-
Should be:
code language-none <td colname="col2"> <p>Replaces the <span class="uicontrol"> Show Metrics</span> popup in <span class="uicontrol"> Reports & Analytics</span>. </p>
Find Markdown links in HTML tables
Here’s another problem caused by an overly greedy Find/Change.
-
Syntax:
<p(.+?)\]\(
-
Problem:
code language-none <td colname="col2"> <p>If your site contains links to files with any of the listed extensions, the URLs of these links will appear in reporting. See [Configuration Variables](js-implementation /c-variables/configuration-variables.md). </p> </td>
-
Should be:
code language-none <td colname="col2"> <p>If your site contains links to files with any of the listed extensions, the URLs of these links will appear in reporting. See <a href="/docs/authoring-guide/implement/js-implementation/c-variables /configuration-variables.md"> Configuration Variables</a>. </p> </td>
Google Searches
If you do a search in Visual Studio Code for "**
" or “>
”, the improper uses get lost among all the proper uses. With a Google search, it’s much easier to search for formatting that failed to render properly.
In these examples, using the site:experienceleague.adobe.com
parameter narrows the search to our help content. You can further narrow the search by adding your specific help domain, such as site:experienceleague.adobe.com/content/help/en/target
.
Find the failed instances of bold **
syntax
** ** site:experienceleague.adobe.com/content/help/en/
<myrepo>
Find unformatted [!UICONTROL]
/[!DNL]
tags
UICONTROL site:experienceleague.adobe.com/content/help/en/
<myrepo>
DNL site:experienceleague.adobe.com/content/help/en/
<myrepo>
Find unescaped escape characters
> site:experienceleague.adobe.com/content/help/en/
<myrepo>
< site:experienceleague.adobe.com/content/help/en/
<myrepo>
site:experienceleague.adobe.com/content/help/en/
<myrepo>
& site:experienceleague.adobe.com/content/help/en/
<myrepo>
Keyboard shortcuts for Visual Studio Code shortcuts
You can use existing VS Code keyboard shortcuts, create your own custom shortcuts, and use shortcuts from an excellent add-on.
Editing keyboard shortcuts edit-keyboard-shortcuts
You can edit existing keyboard shortcuts. This is especially useful if you’ve added the Adobe Markdown Authoring extension and want to use different keyboard shortcuts.
- Choose Code > Preferences > Keyboard Shortcuts.
- Locate the shortcut you want to edit. Click the pencil icon, and enter the new shortcut.
In this example, I replaced the default ‘Toggle Tip’ shortcut (which is available because of the Adobe Markdown Authoring extension) with Control+Shift+Option+T. After I did that, the new shortcut appeared in the context menu.
See Key Bindings for Visual Studio Code (Microsoft documentation) for details.
Creating custom keyboard shortcuts for markdown extensions shortcuts-extensions
Consider adding your own keyboard shortcuts for common tasks. Here’s how.
-
Choose Code > Preferences > Keyboard Shortcuts.
-
Click the icon for editing shortcuts.
-
Copy and paste the shortcuts below, and save the keybindings.json file.
-
Test the keyboard shortcuts in a different file.
You can switch between different windows to test the keystrokes and make/save edits. The shortcuts work in runtime.
note note NOTE In some cases, you might already have a function assigned to one or more of these keystrokes. You’ll need to find a different keyboard shortcut to get around the conflict. keybindings.json file
code language-none [ { "key": "ctrl+shift+u", "command": "type", "args": { "text": "" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+d", "command": "type", "args": { "text": "" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+n", "command": "type", "args": { "text": ">[!NOTE]\n>\n>" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+i", "command": "type", "args": { "text": ">[!IMPORTANT]\n>\n>" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+c", "command": "type", "args": { "text": ">[!CAUTION]\n>\n>" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+w", "command": "type", "args": { "text": ">[!WARNING]\n>\n>" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+t", "command": "type", "args": { "text": ">[!TIP]\n>\n>" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+v", "command": "type", "args": { "text": "<div class="extension video"><iframe allowfullscreen embedded-video src="url" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"><source src="url" type="" /><p>Your browser does not support the iframe element.</p></iframe></div>" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+m", "command": "type", "args": { "text": ">[!MORELIKETHIS]\n>\n>* [article](url)\n>* [article](url)" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+a", "command": "type", "args": { "text": "" }, "when": "editorTextFocus" },{ "key": "ctrl+shift+l", "command": "type", "args": { "text": "<ul><li>Item1</li><li>Item2</li></ul>" }, "when": "editorTextFocus" } ]
Advanced version that applies UICONTROL and DNL to selection
Most writers would rather apply UICONTROL and DNL tags to a selection of text instead of inserting the tags at the cursor. To get the keystroke to work with a selection, you need to use a combination of snippets and shortcuts. It’s a little more advanced, but not that difficult. Here’s how to set that up.
If you create a snippet for UICONTROL and DNL, we recommend that you use different keystrokes (such as Ctrl+u and Ctrl+d) for selected text, as instructed. Then you can use one keystroke for applying to a selection and another keystroke (ctrl+shift+u and ctrl+shift+d) to insert the syntax at the cursor. Additionally, you can create a snippet for when you want to also apply bold formatting to your UICONTROL selection.
-
Create a snippet for UICONTROL with bold. This shortcut wraps the selection in UICONTROL and applies bold formatting to the selection.
-
Choose Code > Preferences > User Snippets > New Global Snippets file.
-
Specify a name such as
uicontrol with bold
and press Enter. -
Paste the following code into the snippet:
code language-none { "uicontrol-with-bold": { "scope": "markdown", "prefix": "uicontrol with bold", "body": [ "**$TM_SELECTED_TEXT**" ], "description": "Wrap selection with uicontrol and apply bold formatting to selection"} }
-
Save the snippet file.
-
-
Create a similar snippet for UICONTROL without bold.
code language-none { "uicontrol-without-bold": { "scope": "markdown", "prefix": "uicontrol without bold", "body": [ "$TM_SELECTED_TEXT" ], "description": "Wrap selection with uicontrol"} }
-
Create a similar snippet for DNL.
code language-none { "dnl": { "scope": "markdown", "prefix": "dnl", "body": [ "$TM_SELECTED_TEXT" ], "description": "Wrap selection with dnl"} }
-
In the keybindings.json file (see above), add the shortcuts for UICONTROL with bold, UICONTROL without bold, and DNL.
code language-none { "key": "ctrl+u", "command": "editor.action.insertSnippet", "args": { "name": "uicontrol-with-bold" }, "when": "editorHasSelection" }, { "key": "ctrl+n", "command": "editor.action.insertSnippet", "args": { "name": "uicontrol-without-bold" }, "when": "editorHasSelection" }, { "key": "ctrl+d", "command": "editor.action.insertSnippet", "args": { "name": "dnl" }, "when": "editorHasSelection" }
-
Test the keyboard shortcuts in a different file with selected text. Again, replace any keyboard shortcuts that are already taken on your system.
Custom keyboard shortcut quick reference
The following table shows the keyboard shortcuts that we used in the above examples. Make the appropriate adjustments if you edited your shortcuts to avoid system conflicts.
!NOTE
!IMPORTANT
!CAUTION
!WARNING
!TIP
!VIDEO
!MORELIKETHIS
!UICONTROL
- Insert at cursor!UICONTROL
- Wrap around selected text and apply bold formatting!UICONTROL
- Wrap around selected text, no bold!DNL
- Insert at cursor!DNL
- Wrap around selected textAdditional keystrokes to consider creating
Comments
If you want to wrap selected text in comment syntax (<!-- -->
), here’s the snippet syntax:
{
"comment": {
"scope": "markdown",
"prefix": "comment",
"body": [
"<!-- $TM_SELECTED_TEXT -->"
],
"description": "Wrap selection with comment tags"}
}
And here’s the section you could add to the keybindings.json file:
{
"key": "ctrl+shift+h",
"command": "editor.action.insertSnippet",
"args": { "name": "comment" },
"when": "editorHasSelection"
}
Shortcuts for commands such as slugify or lowercase
Visual Studio Code has many editing actions you can take advantage of. For example, if you want to create anchor IDs from heading text, you can run the “Slugify selection” command to convert headings (such as Creating visual elements) into anchor format (such as creating-visual-elements). Or, you can change the case of selected text.
Here’s how to assign keyboard shortcuts to VSC commands.
-
Identify the command you want to use.
In VSC, press Ctrl+Shift+P and begin typing the command you want to use. You can include commands such as “Toggle Note” that are used in the Adobe Markdown Authoring plug-in.
-
Choose Code > Preferences > Keyboard Shortcuts.
-
In the search box, type the name of the action, such as “Slugify selection” or “Transform to Lowercase.”
-
Click the plus sign next to the action and specify your keystroke.
Using the AdobeDocs Chrome Extension
Sean’s team created a wonderful Chrome extension (thanks David and Daniel!) that lets you do the following:
- Open the source file of the existing page in the browser or in Visual Studio Code.
- View basic doc details.
- View all page metadata.
- View page analytics.
- Interact with video content (for tutorial authors)
Installing the extension
-
Open a Chromium-based browser such as Chrome, Vivaldi, or Edge.
-
Go to this link directly in the Chrome Web Store:
https://chrome.google.com/webstore/detail/adobedocs-metadata/likkkalbnnlnmneamhdhofdglodgmgjc
This extension is not public on the Chrome Web Store, so you must use the direct link to access it. A search of the store will not find it.
-
Click the Add to Chrome button.
Using the extension
- Navigate to an Adobe Docs page such as https://experienceleague.adobe.com/docs/target-learn/tutorials/administration/set-up-account-preferences.html.
- Click the AdobeDocs extension icon
Specifing advanced options
Specifying advanced options lets you open files in Visual Studio Code from the browser and view analytics.
-
Open extension settings.
-
Specify the following information:
- File system path of clone Git repos. Example:
/users/<username>/documents/github
- Analytics API key:
everyoneYdv22Cxkw4jgfWfd6th
- Analytics duration (such as 30 days)
- File system path of clone Git repos. Example:
Renaming, Moving, and Deleting Files
See Moving, renaming, and restructuring content
Working with Tables tables
See Tables
Adding redirects
See Redirects
Experimenting with test content
Feel free to use the sandbox.en
repo to experiment with the Git workflow or test out Markdown syntax. The sandbox repo should be clean and small. Feel free to edit or remove any content.
If you value any sandbox content, save a backup. We want to keep this repo clean, so remove content whenever we need to.