DotMake Docfx-Plus
A template and a tool for enhancing DocFx.
This project includes two parts:
The
docfx-plustemplate which extends DocFx'smoderntemplate to fix many UI problems and behaviors. It looks and feels more similar to Microsoft's Learn site.The
docfx-plustool which is a wrapper arounddocfxtool, which at runtime patches the internals to fix some problems; currently mainly for advanced support of XML Comments (xmldocs)<code>blocks. This wrapper is developed because these changes cannot be applied in the template (or in a plugin as it's too late for metadata (.yml) changes).
This project was mainly done for migrating our projects' docs from SHFB (Sandcastle Help File Builder)
which is still very stable but its theme and architecture was outdated.
SHFB was used for many years mainly because of its excellent <code> block support and now we put these features into docfx.
Live Demo - API docs for our other project DotMake Command-Line.


Getting started
Install the dotnet tool from NuGet.
dotnet tool install --global docfx-plus
Prerequisites
- .NET SDK 8.0 and later. The .NET CLI (
dotnetcommand) is included with the .NET SDK.
Usage
Dotnet tool usage
Just use docfx-plus command instead of docfx command with same subcommands, arguments and options:
docfx-plus init --yes
docfx-plus --serve
docfx-plus metadata
docfx-plus build
Refer to DocFx Commandline Reference for more details.
Template usage
Edit your docfx.json and update the template property so that you are able to use the theme:
"template": [
"default",
"modern",
"docfx-plus"
]
And ensure outputFormat is not set to a value other than mref (the default value which means ManagedReference).
For example using apiPage will not make use of our theme because for that mode,
docfx internally generates the HTML, most of which is not customizable in the template.
Use these metadata settings for best results:
"metadata": [
{
"memberLayout": "separatePages",
"categoryLayout": "nested",
//"codeSourceBasePath": "../src/",
//not working for custom templates, should be mref or not set
//"outputFormat": "apiPage"
}
]
The template can also be used alone with regular docfx tool, however it's recommended to use docfx-plus tool
which already bundles the template and in addition provides important fixes for <code> blocks.
If you want to use the theme with the regular docfx tool, you can export it via:
docfx-plus template export docfx-plus
This will export the bundled docfx-plus template to _exported_templates subfolder, which then can be consumed as:
"template": [
"default",
"modern",
"_exported_templates/docfx-plus"
]
Refer to DocFx Config Reference for more details.
docfx-plus tool features
Support region includes (
<code source="..." region="...">) across more language files (e.g..vb,.js,.aspx). This will be useful especially when migrating fromSHFB.In
.vbsource files, regions defined like this can now be included:#Region "Name" #End RegionIn
.jssource files, regions defined like this can now be included://#region Name //#endregionIn xml-based source files, regions defined like this can now be included (just like
SHFB):<!-- #region Name --> <!-- #endregion -->This is in addition to
docfxdefault format:<!-- <Name> --> <!-- </Name> -->.aspx,.csproj,.slnx,.configare also added todocfxdefault xml-based formats.xml,.xaml,.html,.cshtml,.vbhtml.Support
tabSizeattribute (<code tabSize="2">) which is used when replacing tab (\t) characters to spaces in the included source file (default is4).Support
titleattribute (<code title="Custom">) which is used when showing as code tab title instead of the language.Consistent line break handling so that code is always rendered correctly in the html.
To prevent Yaml multi-line problems (
.ymlgenerated bydocfx), use<br>tags for line breaks. We can't use\nas Yaml goes crazy (converts wholeexample:block to a string with\r\n). For example if a line is empty or whitespace, in Yaml it's written without any indentation and when reading that Yaml, it splits the blocks, tries to close the previous<pre><code>and open a new one. And this caused weird rendering problems for most code.In the web page, we can simply fix
highlight.jsto support unescaped<br>tags inmain.jsconfigureHljsfunction.Convert tab (
\t) characters to spaces to always ensure correct indentation.Some source files or
<code>contents can contain tab characters which can cause inconsistent indentation.Support nested
<code>blocks for merging code even from different regions.In your XML Comments (xmldocs) you can use nested
<code>blocks:<code language="cs"> <code source="Class1.cs" region="Region1" /> <code source="Class2.cs" region="Region2" /> </code>Literal code can also be mixed inside the parent
<code>blocks.<code language="cs"> // ... Some stuff happens here ... <code source="Class1.cs" region="Region1" /> // ... Some stuff happens here ... <code source="Class2.cs" region="Region2" /> // ... Some stuff happens here ... </code>Indentation from source files and literal code will all be normalized.
If
codeSourceBasePathfrommetadataindocfx.jsonis a relative path, convert it to be relative to thedocfx.json(docfxoriginally treats it relative to the working directory, which is wrong).Also convert backslashes (
\) in code block's source attribute (<code source="...">) to forward slashes (/)
so that they are resolved correctly even when running on linux (e.g. when running in a Github Pages workflow which are usually linux based).
Since .NET 8,System.IO.Pathmethods does not do backslash mapping in Unix file paths
but we want to support existing projects which may have<code source="...">with backslashes all over the source code.NamespaceDocsupport just likeSHFB.
Namespace comments can be specified and maintained in your source code by adding an emptyNamespaceDocclass to each namespace.
XML Comments (<summary>and<remarks>tags) from these classes will be extracted and assigned to the containing namespaces in the docs.namespace DotMake.CommandLine { /// <summary> /// This is the root namespace of this library, it includes the CLI attributes and CLI parser/runner. /// <para /> /// <para>The main classes in this namespace are:</para> /// <list type="bullet"> /// <item><see cref="CliCommandAttribute"/> is the attribute which specifies a class that represents a command which is a specific action that the command line application performs.</item> /// <item><see cref="CliOptionAttribute"/> is the attribute which specifies a class property that represents an option which is a named parameter and a value for that parameter, that is used on the command line.</item> /// </list> /// </summary> [System.Runtime.CompilerServices.CompilerGenerated] internal class NamespaceDoc { } }NamespaceDocclasses will be found even if your API filter excludes them but themselves will not be included in the docs, only their XML Comments assigned to the containing namespaces will be displayed.
You can optionally make them private or internal and mark them with aCompilerGeneratedattribute.
docfx-plus template features
In namespace, class, enum or member pages use
Name Typeformat as the title e.g.CliContext Classinstead ofClass CliContext.Display
Definitionheading after the title heading.Don't break the namespace into link parts next to
Namespace:because e.g. the root namespace may not exist and cause a HTTP 404.Fix heading margins (
<h1>,<h2>...).
Display namespace members, class members and enum fields with tables with subtle borders.
Display inherited members and extension methods together with other members (in their own member type group), instead of displaying them as a huge and isolated list under the class definition section.
Display(Inherited from BaseClass)note in the description.
Sort inherited members along with other members by name.
Elegant rendering of
<code>blocks with white background and subtle borders.Group sibling
<code>blocks which are for different languages (e.g.vbfollowingcs), and display them as tabs.<code source="Class1.cs" /> <code source="Class1.vb" />If there is text between
<code>blocks or if they are for same language, they will not be grouped and will be displayed separately.Display always visible "code copy" button on the right-side of tabs.
Add
cshtml-razorgrammar forhighlight.js.
On member pages, display overloads with indentation separately from definition section and fix sub-heading levels (
<h2>,<h3>...).If no overloads, display single member.

Display values for
enumfields along with name and description and sort them by values.
Display
Obsoletebadge if class, enum or member hasObsoleteAttributeattribute and an in addition display an alertdivunder the badge ifObsoleteAttributehas a message.
Elegant rendering of TOC tree.
Highlight current tree node.
Remove root node
Namespaceswhich causes unnecessary nesting and display all namespaces as root (though fixed inv2.78.4ofdocfx).
Add icon links to top toolbar via new template property
_appIconLinksdirectly indocfx.json(no need to overridelayout/_master.tmpl):"globalMetadata": { "_appIconLinks": [ { "icon": "github", "href": "https://github.com/dotmake-build/command-line", "title": "GitHub" } ] }Fix header nav brand logo, title and buttons wrapping on phone and tablet sizes.

Fix some URL issues:
Use
./instead ofindex.htmlfor app logo by default to prevent canonical URL issues for search engines.Fix URLs starting with app relative path
~/for TOC.
This is because we can't use e.g../forhrefintoc.yml; we getCircularTocInclusionerror as it tries to load itself at./toc.yml.
This way, we can use~/.as a workaround intoc.yml(we want to use clean directory URL and avoid usingindex.html)
DocFx Tips:
Using README.md in your docs:
When you want to reference your repository's
README.mdfile, you can createindex.mdnext todocfx.jsonwith contents:[!include [getting-started](../README.md)]Note that upper case
!INCLUDEas noted in docs, may not work on some machines (probably due to turkish-I bug in docfx).
The path should be relative to the containing file.
index.mdneeds to exist anyway otherwise homepage (/) will not work so it's better to includeREADME.mdin this file if there is nothing else to put on the homepage.Another way: if you define external .md files as content in
docfx.jsonlike this:"build": { "content": [ { "files": ["README.md"], "src": "../", "dest": "docs" } ],dot-dot notation can not appear in
filesbut it can appear insrc.
srcis relative to thedocfx.jsonfolder (config root).
Thedestmeans the output subfolder (under_siteby default) for the corresponding html file, e.g.README.html.Then in
toc.ymlyou can reference the .md file:- name: Readme href: ~/../README.md - name: Prerequisites href: ~/../README.md#prerequisitesNote that the path should match
srcand notdest. Using~/in casetoc.ymlis in a subfolder and not in config root.
You can also put the # fragment but the file will not be splitted, it will just be a fragment link.
Publish to GitHub Pages:
Create a workflow file in your repository, e.g. publish-docs.yml file in .github\workflows folder with these contents:
# Your GitHub workflow file under .github/workflows/
# Trigger the action on push to main
on:
push:
branches:
- main
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
actions: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
publish-docs:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Dotnet Setup
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.x
- run: dotnet tool update -g docfx-plus
- run: docfx-plus docfx.json
# use working-directory for docfx step otherwise codeSourceBasePath is not resolved correctly
working-directory: docs
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: 'docs/_site'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Then enable GitHub Actions for your repository's Pages settings as described here:
Publishing with a custom GitHub Actions workflow
Now whenever you commit, your action will run automatically and publish your docs.
Full docfx.json sample:
Refer to docfx.json from our other project DotMake Command-Line.
Building
We provide some .cmd batch scripts in build folder for easier building:
1. Build Cli.cmd
2. Build Nuget Packages.cmd
3. Build Api Docs WebSite.cmd
Output results can be found in publish folder, for example:
DotMake.DocfxPlus.Cli-net8.0
docfx-plus.1.0.0.nupkg