diff --git a/docs/docs/template.md b/docs/docs/template.md
index 6ae7d12ceab..e8d274921c8 100644
--- a/docs/docs/template.md
+++ b/docs/docs/template.md
@@ -1,35 +1,94 @@
# Template
-Template defines the appearance of the website.
+Template defines the appearance of the website. Docfx ships a default modern website template with the same look and feel as this site. Additional templates are available at the [Template Gallery](../extensions/templates.yml).
-Docfx ships a default website template with the same look and feel as this site. Additional templates are available at the [Template Gallery](../extensions/templates.yml).
+## Custom Template
-## Create a Custom Template
+Customization options varies depending on the chosen template. We recommend using the modern template which supports dark mode, more features, rich customization options and is being actively maintained. The rest of this article assumes you are using the modern template. To use the modern template, set `build.templates` property to `["default", "modern"]` in `docfx.json`:
-To build your own template, create a new folder and add it to `templates` config in `docfx.json`:
+```json
+{
+ "build": {
+ "templates": [
+ "default",
+ "modern"
+ ]
+ }
+}
+```
+
+To customize the template, create a new folder next to `docfx.json` and add it to `templates` config in `docfx.json`:
```json
{
"build": {
"templates": [
"default",
+ "modern",
"my-template" // <-- Path to custom template
]
}
}
```
-Add your custom CSS file to `styles/main.css` and JavaScript file to `styles/main.js`. Docfx loads these 2 files and use them to style the website.
+Docfx loads files for the `default` and `modern` entries from the `templates` directory shipped with the `docfx` executable. On windows, this is typically `C:\Users\{name}\.nuget\packages\docfx\{version}\tools\{tfm}\any\templates` if install docfx using `dotnet tool install -g docfx`.
-This is an example stylesheet that adjust the font size of article headers:
+Docfx loads files for the `my-template` entries from the `my-templates` directory next to your `docfx.json` project. The `my-template/public` directory contains files that will be published to the website.
+
+Add your custom CSS file to `my-template/public/main.css` to customize colors, show and hide elements, etc. This is an example stylesheet that adjust the font size of article headers.
```css
-/* file: styles/main.css */
+/* file: my-template/public/main.css */
article h1 {
font-size: 40px;
}
```
+You can also use [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) to adjust the templates. There are many predefined CSS variables in [Bootstrap](https://getbootstrap.com/docs/5.3/customize/color/#colors) that can be used to customize the site:
+
+```css
+/* file: my-template/public/main.css */
+body {
+ --bs-link-color-rgb: 66, 184, 131 !important;
+ --bs-link-hover-color-rgb: 64, 180, 128 !important;
+}
+```
+
+The `my-template/public/main.js` file is the entry JavaScript file to customize docfx site behaviors. This is a basic setup that changes the default color mode to dark and adds some icons in the header:
+
+```js
+/* file: my-template/public/main.js */
+export default {
+ defaultTheme: 'dark',
+ icons?: [
+ {
+ icon: 'github',
+ href: 'https://github.com/dotnet/docfx'
+ title: 'GitHub'
+ },
+ {
+ icon: 'twitter',
+ href: 'https://twitter.com'
+ title: 'Twitter'
+ }
+ ]
+}
+```
+
+You can also configure syntax highlighting options using the `configureHljs` option:
+
+```js
+export default {
+ configureHljs: (hljs) => {
+ // Customize hightlight.js here
+ },
+}
+```
+
+See [this example](https://github.com/dotnet/docfx/blob/main/samples/seed/template/public/main.js) on how to enable `bicep` syntax highlighting.
+
+More customization options are available in the [docfx options object](https://github.com/dotnet/docfx/blob/main/templates/modern/src/options.d.ts).
+
## Custom HTML Templates
In addition to CSS and JavaScript, you can customize how docfx generates HTML using [Mustache Templates](https://mustache.github.io/).
diff --git a/docs/template/styles/main.css b/docs/template/public/main.css
similarity index 100%
rename from docs/template/styles/main.css
rename to docs/template/public/main.css
diff --git a/docs/template/public/main.js b/docs/template/public/main.js
new file mode 100644
index 00000000000..75034d0e24e
--- /dev/null
+++ b/docs/template/public/main.js
@@ -0,0 +1,11 @@
+/* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. */
+
+export default {
+ icons: [
+ {
+ icon: 'github',
+ href: 'https://github.com/dotnet/docfx',
+ title: 'GitHub'
+ }
+ ]
+}
diff --git a/samples/seed/articles/markdown.md b/samples/seed/articles/markdown.md
index 990f69ed15a..02ce1f0ef37 100644
--- a/samples/seed/articles/markdown.md
+++ b/samples/seed/articles/markdown.md
@@ -68,6 +68,15 @@ This expression uses `\$` to display a dollar sign: $\sqrt{\$4}$
To split $100 in half, we calculate $100/2$
+## Custom Syntax Highlighting
+
+```bicep
+resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
+ name: 'hello'
+ // (...)
+}
+```
+
## Tabs
# [Linux](#tab/linux)
diff --git a/samples/seed/docfx.json b/samples/seed/docfx.json
index 67258dec80c..35acf37ca74 100644
--- a/samples/seed/docfx.json
+++ b/samples/seed/docfx.json
@@ -44,7 +44,7 @@
},
"dest": "_site",
"exportViewModel": true,
- "template": ["default", "modern"]
+ "template": ["default", "modern", "template"]
},
"pdf": {
"content": [
diff --git a/samples/seed/template/public/bicep.js b/samples/seed/template/public/bicep.js
new file mode 100644
index 00000000000..d2bfe5c9173
--- /dev/null
+++ b/samples/seed/template/public/bicep.js
@@ -0,0 +1,156 @@
+/**
+ Origin: https://github.com/Duncanma/highlight.js/blob/stable/src/languages/bicep.js
+ */
+
+export function bicep(hljs) {
+ var bounded = function (text) { return "\\b" + text + "\\b"; };
+ var after = function (regex) { return "(?<=" + regex + ")"; };
+ var notAfter = function (regex) { return "(?partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
{{#_description}}{{/_description}}
-
-
+
+
{{#_noindex}}{{/_noindex}}
{{#_enableSearch}}{{/_enableSearch}}
{{#docurl}}{{/docurl}}
-
-
-
-
+
{{/redirect_url}}
diff --git a/templates/modern/public/main.css b/templates/modern/public/main.css
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/templates/modern/public/main.js b/templates/modern/public/main.js
new file mode 100644
index 00000000000..56004c9f9e0
--- /dev/null
+++ b/templates/modern/public/main.js
@@ -0,0 +1 @@
+export default {}
\ No newline at end of file
diff --git a/templates/modern/src/docfx.ts b/templates/modern/src/docfx.ts
index 48770cbc150..129b38d9aed 100644
--- a/templates/modern/src/docfx.ts
+++ b/templates/modern/src/docfx.ts
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
import 'bootstrap'
+import { DocfxOptions } from './options'
import { highlight } from './highlight'
import { renderMarkdown } from './markdown'
import { enableSearch } from './search'
@@ -15,7 +16,7 @@ import 'mathjax/es5/tex-svg-full.js'
declare global {
interface Window {
- docfx: {
+ docfx: DocfxOptions & {
ready?: boolean,
searchReady?: boolean,
searchResultReady?: boolean,
@@ -23,7 +24,7 @@ declare global {
}
}
-window.docfx = {}
+window.docfx = window.docfx || {}
initTheme()
diff --git a/templates/modern/src/highlight.ts b/templates/modern/src/highlight.ts
index cd3808dd7ac..581e1b21c6a 100644
--- a/templates/modern/src/highlight.ts
+++ b/templates/modern/src/highlight.ts
@@ -4,6 +4,9 @@
import hljs from 'highlight.js'
export function highlight() {
+
+ window.docfx.configureHljs?.(hljs)
+
document.querySelectorAll('pre code').forEach(block => {
hljs.highlightElement(block as HTMLElement)
})
diff --git a/templates/modern/src/markdown.ts b/templates/modern/src/markdown.ts
index 945537f34fc..31c26e2ff11 100644
--- a/templates/modern/src/markdown.ts
+++ b/templates/modern/src/markdown.ts
@@ -34,7 +34,7 @@ function renderMermaid() {
})
const theme = getTheme() === 'dark' ? 'dark' : 'default'
- mermaid.initialize({ startOnLoad: true, deterministicIds: true, theme })
+ mermaid.initialize(Object.assign({ startOnLoad: true, deterministicIds: true, theme }, window.docfx.mermaid))
}
/**
@@ -132,10 +132,11 @@ function renderLinks() {
*/
function renderAnchor() {
const anchors = new AnchorJs()
- anchors.options = {
+ anchors.options = Object.assign({
visible: 'hover',
icon: '#'
- }
+ }, window.docfx.anchors)
+
anchors.add('article h2:not(.no-anchor), article h3:not(.no-anchor), article h4:not(.no-anchor)')
}
diff --git a/templates/modern/src/nav.ts b/templates/modern/src/nav.ts
index 519c9221852..84a7f12be59 100644
--- a/templates/modern/src/nav.ts
+++ b/templates/modern/src/nav.ts
@@ -5,6 +5,7 @@ import { render, html, TemplateResult } from 'lit-html'
import { breakWordLit, meta } from './helper'
import { themePicker } from './theme'
import { TocNode } from './toc'
+import { IconLink } from './options'
export type NavItem = {
name: string
@@ -33,7 +34,11 @@ export async function renderNavbar(): Promise {
})
}`
- const icons = html``
+ const icons = html`
+ `
function renderCore() {
render(html`${menu} ${icons}`, navbar)
@@ -53,21 +58,6 @@ export async function renderNavbar(): Promise {
const { items } = await fetch(navUrl).then(res => res.json())
return items.map(a => ({ name: a.name, href: new URL(a.href, navUrl) }))
}
-
- function githubLink() {
- const docurl = meta('docfx:docurl')
- const github = parseGitHubRepo(docurl)
- if (github) {
- return html``
- }
- }
-
- function parseGitHubRepo(url: string): string {
- const match = /^https:\/\/github.com\/([^/]+\/[^/]+)/gi.exec(url)
- if (match) {
- return match[0]
- }
- }
}
export function renderBreadcrumb(breadcrumb: (NavItem | TocNode)[]) {
diff --git a/templates/modern/src/options.d.ts b/templates/modern/src/options.d.ts
new file mode 100644
index 00000000000..1ff1bc156ff
--- /dev/null
+++ b/templates/modern/src/options.d.ts
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+import BootstrapIcons from 'bootstrap-icons/font/bootstrap-icons.json'
+import { HLJSApi } from "highlight.js"
+import { AnchorJSOptions } from 'anchor-js'
+import { MermaidConfig } from "mermaid"
+
+export type Theme = 'light' | 'dark' | 'auto'
+
+export type IconLink = {
+ /** A [bootstrap-icons](https://icons.getbootstrap.com/) name */
+ icon: keyof typeof BootstrapIcons,
+
+ /** The URL of this icon link */
+ href: string,
+
+ /** The title of this icon link shown on mouse hover */
+ title?: string
+}
+
+/**
+ * Enables customization of the website through the global `window.docfx` object.
+ */
+export type DocfxOptions = {
+ /** Configures the default theme */
+ defaultTheme?: Theme,
+
+ /** Whether to show the theme picker */
+ hideThemePicker?: boolean,
+
+ /** A list of icons to show in the header next to the theme picker */
+ icons?: IconLink[],
+
+ /** Configures [anchor-js](https://www.bryanbraun.com/anchorjs#options) options*/
+ anchors?: AnchorJSOptions,
+
+ /** Configures mermaid diagram options */
+ mermaid?: MermaidConfig,
+
+ /** Configures [hightlight.js](https://highlightjs.org/) */
+ configureHljs?: (hljs: HLJSApi) => void,
+}
diff --git a/templates/modern/src/search.ts b/templates/modern/src/search.ts
index bbba9adabca..8c41b99de70 100644
--- a/templates/modern/src/search.ts
+++ b/templates/modern/src/search.ts
@@ -23,7 +23,7 @@ export function enableSearch() {
}
const relHref = meta('docfx:rel') || ''
- const worker = new Worker(relHref + 'styles/search-worker.min.js')
+ const worker = new Worker(relHref + 'public/search-worker.min.js')
worker.onmessage = function(oEvent) {
switch (oEvent.data.e) {
case 'index-ready':
diff --git a/templates/modern/src/theme.ts b/templates/modern/src/theme.ts
index 7f1bbe9676c..770a38a38b4 100644
--- a/templates/modern/src/theme.ts
+++ b/templates/modern/src/theme.ts
@@ -2,8 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
import { html } from 'lit-html'
-
-type Theme = 'light' | 'dark' | 'auto'
+import { Theme } from './options'
function setTheme(theme: Theme) {
localStorage.setItem('theme', theme)
@@ -14,8 +13,12 @@ function setTheme(theme: Theme) {
}
}
+function getDefaultTheme() {
+ return localStorage.getItem('theme') as Theme || window.docfx.defaultTheme || 'auto'
+}
+
export function initTheme() {
- setTheme(localStorage.getItem('theme') as Theme || 'auto')
+ setTheme(getDefaultTheme())
}
export function getTheme(): 'light' | 'dark' {
@@ -23,7 +26,11 @@ export function getTheme(): 'light' | 'dark' {
}
export function themePicker(refresh: () => void) {
- const theme = localStorage.getItem('theme') as Theme || 'auto'
+ if (window.docfx.hideThemePicker) {
+ return html``
+ }
+
+ const theme = getDefaultTheme()
const icon = theme === 'light' ? 'sun' : theme === 'dark' ? 'moon' : 'circle-half'
return html`
diff --git a/templates/tsconfig.json b/templates/tsconfig.json
index 5895bf74c51..a5f8309041c 100644
--- a/templates/tsconfig.json
+++ b/templates/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"removeComments": true,
+ "resolveJsonModule": true,
"esModuleInterop": true
}
}