How to customize and publish a docfx site

I wanted to tweak the dotnetconfig.org site in a few ways, and it turned out that many of the generated files from a docfx build are plain content files that are provided only if your project doesn’t provide them already.

Change default icons

Most docfx sites I see just have the default logo because it’s not documented (that I could easily find at least) how to change it. Turns out you just have to have your own logo.svg (and optionally favicon.ico) and include it as a resource in your docfx.json:

{
  ...
  "build": {
    "resource": [
      {
        "files": [
          "logo.svg",
          "favicon.ico"
        ]
      }
    ]
  }
}

Change default styles and scripts

Just like for for icons, you can provide a styles/main.css file that is automatically referenced. Similarly, you can provide a styles/main.js to add scripts if needed:

{
  // ...
  "build": {
    "resource": [
      {
        "files": [
          ...
          "styles/*.css",
          "styles/*.js"
        ]
      }
    ]
  }
}

Change included scripts

When I wanted to add my own files outside of the built-in extension points mentioned above, things got trickier. Exporting built-in templates and understanding how the whole template system works seemed overly complicated and overkill for the thing I needed to do. I wanted to add my extended highlightjs language (that’s what docfx uses for highlighting code snippets) to better colorize dotnetconfig, which required including the minified .js to every generated file.

In addition, the version of highlight.js included in docfx is not the latest & gratest, so I wanted to also get a newer one. Basically I need to replace this on every page:

    <script type="text/javascript" src="styles/docfx.vendor.js"></script>
    <script type="text/javascript" src="styles/docfx.js"></script>
    <script type="text/javascript" src="styles/main.js"></script>
  </body>
</html>

with

    <script type="text/javascript" src="styles/docfx.vendor.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/highlight.min.js"></script>
    <script src="https://unpkg.com/highlightjs-dotnetconfig@0.9.1/dist/dotnetconfig.min.js"></script>
    <script type="text/javascript" src="styles/docfx.js"></script>
    <script type="text/javascript" src="styles/main.js"></script>
  </body>
</html>

The docfx templating system allows applying more than one template when building the site, and every template can override what’s included in the previous one by just providing files with the same name and path. Armed with that knowledge, I set to understand how the header and footer of every page is generated by the default template, by just using the CLI:

  1. Install docfx
     > choco install docfx -y
    
  2. Export default template
     > docfx template export default
    
  3. Build a project with that exported template while tweaking things on every build
     > docfx build docfx.json -t .\_exported_templates\default --force
    
  4. I found that the changes I needed should go in template\partials\scripts.tmpl.partial

Once happy with the changes, you just create an empty folder structure matching that and place your updated file there. I placed this “template” alongside my docfx.json, which I updated to use it on build:

{
  "build": {
    ...
    "template": [
      "default",
      "./template"
    ]
  }
}

Change included stylesheets

With the learnings from above, it was trivial to find the next part of the template to modify to customize the head element, the head.tmpl.partial. I simply wanted to also reference the latest & greatest CSS from highlight.js, so I added:

  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/styles/default.min.css">

with the full file being:

{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
  <meta name="viewport" content="width=device-width">
  <meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
  <meta name="generator" content="docfx {{_docfxVersion}}">
  {{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
  <link rel="shortcut icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
  <link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/styles/default.min.css">  
  <link rel="stylesheet" href="{{_rel}}styles/docfx.css">
  <link rel="stylesheet" href="{{_rel}}styles/main.css">
  <meta property="docfx:navrel" content="{{_navRel}}">
  <meta property="docfx:tocrel" content="{{_tocRel}}">
  {{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
  {{#_enableSearch}}<meta property="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
  {{#_enableNewTab}}<meta property="docfx:newtab" content="true">{{/_enableNewTab}}
</head>

Publishing DocFx site from GitHub Actions

I decided to use the project/MSBuild-based approach to build the site, since in my case I only had a single project.

The following MSBuild properties allowed me to tweak the docfx run on build:

  <PropertyGroup Label="docfx">
    <DocfxConfigFile>../../docs/docfx.json</DocfxConfigFile>
    <MetadataOutputFolder>../..</MetadataOutputFolder>
    <PreviewOutputFolder>../../docs/_site</PreviewOutputFolder>
    <LogFile>$(MSBuildProjectExtensionsPath)/obj/docfx.log</LogFile>
    <LogLevel>Info</LogLevel>
    <BuildDocFx Condition="'$(BuildDocFx)' == ''">false</BuildDocFx>
  </PropertyGroup>

By default I’m not building docs unless BuildDocFx=true. Trying the output locally is just a matter of running dotnet build -p:BuildDocFx=true and then doing a dotnet-serve in the preview folder.

NOTE: did you know dotnet-serve supports configuration via .netconfig files?

Publishing the entire docfx site automatically on pushes to the main branch on https://github.com/dotnetconfig/dotnet-config was quite trivial using a simple GitHub Actions workflow from that point on:

name: pages
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎️
        uses: actions/checkout@v2
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.100-preview.7.20366.6
      - name: Build 🔧
        run: dotnet build -p:BuildDocFx=true
      - name: Deploy 🚀
        uses: JamesIves/github-pages-deploy-action@releases/v3
        with:
          GITHUB_TOKEN: $
          BRANCH: gh-pages
          FOLDER: _site

Note that I’m pushing to the gh-pages branch, which then is set up in the repository for GH pages, which coupled with the right CNAME drives the nice site at https://dotnetconfig.org.

Enjoy!

Tags: docs docfx

/kzu dev↻d