Customization and Styling
When using fsdocs, there are six levels of extra content development and styling.
-
Don't do any styling or documentation customization and simply write content. This is by far the simplest option to maintain.
-
Add content such as an
docs/index.mdto customize the front-page content for your generated docs. You can also add content such asdocs/reference/fslib.mdto give a bespoke landing page for one of your namespaces, e.g. here assumed to benamespace FsLib. This will override any generated content. - Customize via Styling Parameters
- Customize via CSS
- Customize via a new template
- Customize by generating your own site using your own code
By default fsdocs does no styling customization and uses the following defaults. These are the settings used to build
this site.
-
Uses the default template in docs/_template.html
-
Uses the default styles in docs/content/fsdocs-default.css.
-
Uses no custom styles in docs/content/fsdocs-custom.css.
- Uses no styling parameters except those extracted from the project files.
For your project, you don't need any of these files. However, you can add them if you wish, though if you adjust them there is no guarantee that your template will continue to work with future versions of F# Formatting.
Customizing via Styling Parameters
The following content parameters are particularly related to visual styling:
Substitution name |
Value (if not overriden by --parameters) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
These basic entry-level styling parameters can be set in the project file or Directory.Build.props.
For example:
<PropertyGroup>
<!-- Example ultra-simple styling and generation settings for FsDocs default template-->
<PackageLicenseUrl>https://github.com/foo/bar/blob/master/License.txt</PackageLicenseUrl>
<PackageProjectUrl>https://foo.github.io/bar/</PackageProjectUrl>
<RepositoryUrl>https://github.com/foo/bar/</RepositoryUrl>
<FsDocsLogoLink>https://fsharp.org</FsDocsLogoLink>
<FsDocsLogoSource>img/logo.png</FsDocsLogoSource>
<FsDocsFaviconSource>img/favicon.ico</FsDocsFaviconSource>
<FsDocsLicenseLink>https://github.com/foo/bar/blob/master/License.txt</FsDocsLicenseLink>
<FsDocsReleaseNotesLink>https://github.com/foo/bar/blob/master/release-notes.md</FsDocsReleaseNotesLink>
<FsDocsWarnOnMissingDocs>true</FsDocsWarnOnMissingDocs>
<FsDocsTheme>default</FsDocsTheme>
</PropertyGroup>
LLM-Friendly Output
By default, fsdocs build generates llms.txt and llms-full.txt in the output root,
following the llmstxt.org convention. These files provide a structured
index (and full content) of all documentation pages and API reference entries, making it easy
to add documentation context to LLMs and AI coding assistants.
When this feature is enabled, markdown (.md) files are automatically generated alongside HTML
for all documentation pages — even if you have not added a _template.md file. The llms.txt
links point to these markdown files, which are more suitable for LLM consumption.
To opt out, set the following property in your project file or Directory.Build.props:
<PropertyGroup>
<FsDocsGenerateLlmsTxt>false</FsDocsGenerateLlmsTxt>
</PropertyGroup>
As an example, here is a page with alternative styling.
Customizing via CSS
You can start styling by creating a file docs/content/fsdocs-theme.css and adding entries to it.
It is loaded by the standard template.
CSS variables
The default template is heavily based
on CSS variables. These can easily be
override to customize the look and feel of the default theme.
A full list of the overrideable variables can be
found here.
:root {
--text-color: light-dark(red, darkred);
}
The default theme uses the CSS light-dark()
function so that each variable carries both its light and dark values.
The active value is resolved automatically based on the color-scheme property.
When you override a variable, use light-dark(light-value, dark-value) to supply both variants in a single declaration.
CSS classes
The API documentation uses a set of fixed CSS classes:
CSS class |
Corresponding Content |
|---|---|
|
generated tooltips |
|
generated xmldoc sections |
|
generated member lists (tables) |
|
usage in generated member lists |
|
tooltips in generated member lists |
|
documentation in generated member lists |
|
generated entity lists |
|
generated entity lists |
|
documentation in generated entity lists |
|
generated exception lists |
|
the 'summary' section of an XML doc |
|
the 'remarks' section of an XML doc |
|
the 'parameters' section of an XML doc |
|
a 'parameter' section of an XML doc |
|
a 'parameter' name of an XML doc |
|
the 'returns' section of an XML doc |
|
the 'example' section of an XML doc |
|
the 'notes' section of an XML doc |
|
a paragraph of an XML doc |
Some generated elements are given specific HTML ids:
HTML element selector |
Content |
|---|---|
|
The navigation-bar |
|
The main menu on the left side |
|
The generated content |
|
The sub menu on the right side |
|
The search dialog |
|
The search box |
|
The logo |
If you write a new theme by CSS styling please contribute it back to FSharp.Formatting.
Customizing via a new template
You can do advanced styling by creating a new template. Add a file docs/_template.html, likely starting
with the existing default template.
NOTE: To enable hot reload during development with
fsdocs watchin a custom_template.htmlfile, make sure to add the single line{{fsdocs-watch-script}}to your<head>tag. NOTE: There is no guarantee that your template will continue to work with future versions of F# Formatting. If you do develop a good template please consider contributing it back to F# Formatting.
Customizing menu items by template
You can add advanced styling to the sidebar generated menu items by creating a new template for it.
fsdoc will look for menu templates in the --input folder, which defaults to the docs folder.
To customize the generated menu-item headers, use file _menu_template.html with starting template:
<li class="nav-header">
{{fsdocs-menu-header-content}}
</li>
{{fsdocs-menu-items}}
Similarly, to customize the individual menu item list, use file _menu-item_template.html with the starting template:
<li class="nav-item"><a href="{{fsdocs-menu-item-link}}" class="nav-link">{{fsdocs-menu-item-content}}</a></li>
Do note that files must be added before running, or won't be generated.
In case you want to get a unique identifier for a header or menu item, you can use {{fsdocs-menu-header-id}}
and {{fsdocs-menu-item-id}}, respectively.
Both menu templates also get the substitutions of the page they are rendered into, {{root}} among them, so a
menu can link anywhere on the site and not only where fsdocs hands it a link. {{root}} is relative to the page
being rendered, so {{root}}reference/index.html resolves from every depth:
<li class="nav-header">{{fsdocs-menu-header-content}}</li>
<li class="nav-item"><a href="./reference/index.html">All Namespaces</a></li>
{{fsdocs-menu-items}}
A key that a menu template defines for itself always wins over a site-wide one of the same name.
One template renders every section of the menu: the documents, each of their categories, and the namespaces of the
API reference. The header tells them apart, through {{fsdocs-menu-header-content}} and its {{fsdocs-menu-header-id}},
but the substitutions have no conditionals, so anything else a template adds appears in every section.
That matters in one place. The built-in menu makes the "API Reference" header itself the link to the index of all namespaces:
<li class="nav-header"><a href="../reference/index.html">API Reference</a></li>
A templated header is a label, and a template cannot single out one section, so putting that link back is a job for a few lines of script. Give the header its id and its root, both of which the template already has:
<li class="nav-header" id="{{fsdocs-menu-header-id}}" data-root="./">{{fsdocs-menu-header-content}}</li>
and turn the text of that one header into a link, the way the built-in menu does:
const header = document.getElementById("api_reference");
if (header) {
const link = document.createElement("a");
link.href = `${header.dataset.root}reference/index.html`;
link.textContent = header.textContent.trim();
header.replaceChildren(link);
}
The id comes from the header text, so "API Reference" gives api_reference and "Getting started" gives
getting_started.
The API reference menu lists every namespace already, so what the index adds over it is the description of each one.
{{fsdocs-menu-item-title}} is the hover text of an item, meant for the title attribute of the link. The API
reference uses it for the full name of a namespace, where the menu shortened the entry to what tells it apart from
its neighbours. It is empty for an item whose text already says where it leads.
Injecting additional html into the default template
Occasionally, you may find the need to make small customizations to the default template, such as adding a Google
Analytics snippet or including additional style or script tags. To address this scenario, you can create two
files: _head.html and/or _body.html.
The content within these files will serve as replacements for the {{fsdocs-head-extra}} and {{fsdocs-body-extra}}
placeholders, which are utilized in the default template.
Customizing by generating your own site using your own code
The FSharp.Formatting.ApiDocs namespace includes a GenerateModel that captures
the results of documentation preparation in ApiDocsModel and allows you to
generate your own site using your own code.
NOTE: The ApiDocsModel API is undergoing change and improvement, and there is no guarantee that your bespoke site generation will continue to work with future versions of F# Formatting. NOTE: The
ApiDocsModelcurrently includes some generated HTML with some specific style tags. In the long term these may be removed from the design of that component.
FSharp.Formatting