FontLab theme 2026 · Integration guide

One site. Three libraries.

Keep MaterialX for your documentation. Add FontLab navigation, Basecoat and daisyUI from one shared CDN.

This is a browser asset layer, not a replacement Python theme. ProperDocs builds your Markdown; MaterialX supplies the page, navigation and search; FontLab theme 2026 connects the global menus and adds scoped components.

Download the MaterialX starterBrowse the full component catalogue →

Start a site

Install uv, download starter.zip, and extract it. The archive contains two Markdown pages, a pinned requirements file, a MaterialX configuration and a template override. From its extracted directory:

uv venv --python 3.13
uv pip install -r requirements.txt
uv run properdocs serve -f mkdocs.yml

Open the preview URL printed by ProperDocs. The sample site_url includes /docs/, so the preview may use that path. Change site_name and site_url before publishing. Then run uv run properdocs build -f mkdocs.yml --strict and publish the contents of site/.

The tested baseline is Python 3.13, ProperDocs 1.6.7, MkDocs 1.6.1 and MaterialX 10.1.8. Consumers need no npm or Tailwind build. The CDN, global menus and external fonts require internet access.

Add it to an existing MaterialX site

Keep theme.name: materialx. Merge the entries below into your existing YAML; do not create duplicate plugins, extra_css or extra_javascript keys. Keep your current navigation, plugins, fonts and template overrides.

This is the complete starter configuration. The first two CSS entries and first two JavaScript entries are the shared layer:

# this_file: examples/starter/mkdocs.yml
site_name: My FontLab documentation
site_url: https://example.org/docs/
docs_dir: docs
site_dir: site
theme:
  name: materialx
  custom_dir: overrides
  features:
    - navigation.tabs
    - search.highlight
    - content.code.copy
  palette:
    - scheme: default
      toggle:
        icon: material/brightness-7
        name: Switch to dark mode
    - scheme: slate
      toggle:
        icon: material/brightness-4
        name: Switch to light mode
plugins:
  - search
markdown_extensions:
  - attr_list
  - md_in_html
  - admonition
  - pymdownx.superfences
nav:
  - Showcase:
      - Home: index.md
  - Design:
      - Next steps: next.md
extra_css:
  - https://i.fontlab.com/fltheme26/1.0.0/components.css
  - https://i.fontlab.com/fltheme26/1.0.0/theme.css
extra_javascript:
  - https://i.fontlab.com/fltheme26/1.0.0/basecoat.js
  - https://i.fontlab.com/fltheme26/1.0.0/theme.js

Load components.css before theme.css, and basecoat.js before theme.js. Load them once per page, after broad legacy resets. Deliberate site overrides may follow them. If your toolkit already injects these assets into final HTML, check for duplicates before adding another copy.

AssetResponsibility
components.cssScoped Basecoat 1.0.2 and prefixed daisyUI 5.7.44 styles.
theme.cssShared colours, component sizing, MaterialX adjustments and responsive panel placement.
basecoat.jsBasecoat component behavior, restricted to component wrappers.
theme.jsMode synchronization, menu/search bridge, page shortcuts and component initialization.

Choose navigation and search independently

On a MaterialX documentation site, the shared layer defaults to the local drawer and local search when those elements exist. The global bar shows one loupe to the left of one hamburger. The local drawer opens on the right; the theme switcher moves to its bottom. Below the MaterialX desktop breakpoint (76.25em), the global branding scrolls away and the compact controls stay at the upper right while either control serves MaterialX.

<!-- Documentation-first site -->
<fontlab-menu mobile-menu="materialx" mobile-search="materialx"></fontlab-menu>

<!-- Webflow-first site with auxiliary MaterialX pages -->
<vexy-menu mobile-menu="global" mobile-search="global"></vexy-menu>

Both brands accept either value on either attribute. JavaScript configuration uses mobileMenu and mobileSearch; attributes take precedence. The current FontLab and Vexy main websites explicitly use global controls, including on auxiliary documentation pages.

Despite its name, mobile-search selects search at every width. Local ownership opens the native MaterialX search index in a desktop dialog or full-screen mobile panel. Global ownership uses the brand search. It does not query the local documentation index. Without a usable local target, the global control remains available.

Keep #__drawer, .md-sidebar--primary, #__search and .md-search. Enable the search plugin and publish search/search_index.json. Do not add a second hamburger, loupe or independent sticky header. Escape closes an open local panel and returns focus.

Try the native MaterialX integration fixture: change ownership, narrow the window, scroll, open the drawer and search for “specimen”.

Write components alongside Markdown

Put Basecoat and daisyUI markup inside a separate .fltheme-components container. MaterialX keeps md- class names; Basecoat uses names such as btn; every daisyUI component class gets du-. The shared reset and vendor styles stay inside the wrapper.

<div class="fltheme-components" markdown="1">

[MaterialX](next.md){ .md-button }
[Basecoat](next.md){ .btn data-variant="outline" }
[daisyUI](next.md){ .du-btn .du-btn-primary }

</div>

Enable attr_list for the brace attributes and md_in_html for Markdown inside markdown="1" containers. Keep blank lines around Markdown content. Plain HTML works without those two extensions. Use native links for navigation and buttons for actions; give interactive controls labels and unique IDs.

Standard buttons share height, padding, font size, weight and line height. Explicit small, large, icon and shape variants retain their library settings. The shared layer also aligns fields, choices, keycaps, tabs, tooltips, captions, alerts, pagination, frames and data displays. Admonitions have colored icons, headings and body text without a border or background; their body text aligns with the heading. No artificial end-of-article spacing is added.

The field guide includes rendered examples, compiled HTML and authored Markdown. The compact authoring reference covers wrapper and runtime usage. Use Basecoat 1.0 markup and daisyUI 5 markup; older Basecoat examples may use different variant classes.

The bundle includes all daisyUI component classes, but only a selected set of layout utilities. Use your site CSS for missing layouts. Chart.js and the optional Basecoat Chart module are not bundled.

Follow the site’s appearance

Components inherit the host font and active MaterialX colours. The host owns the theme picker and persistence. A wrapper may explicitly choose data-fltheme-mode="light" or data-fltheme-mode="dark". The source maps Basecoat and daisyUI tokens together; daisyUI’s border width is --du-border, while Basecoat’s --border remains a colour.

<div class="fltheme-components" data-fltheme-mode="dark">
  <button class="btn">Dark component area</button>
</div>
<script>
  // After manually inserting/restoring component containers:
  window.FLTheme?.refresh();
</script>

ProperDocs/MaterialX instant navigation is handled automatically through document$. Do not load a second Basecoat runtime. Call FLTheme.refresh() after your own DOM replacement.

Use the Marketing / writing-styleguide editorial design

The component bundle alone preserves native MaterialX typography. The optional editorial layer supplies the five branded modes used by Marketing, the writing styleguide and Ornotto. This is a larger design choice; start from a working site template so its theme picker and body classes travel with its CSS.

# Before the shared components.css and theme.css entries:
extra_css:
  - https://i.fontlab.com/fltheme26/1.0.0/editorial/marketing-vendor.css
  - https://i.fontlab.com/fltheme26/1.0.0/editorial/fontlab-theme.css
  - https://i.fontlab.com/fltheme26/1.0.0/editorial/fontlab-materialx.css
  - https://i.fontlab.com/fltheme26/1.0.0/editorial/fontlab-layout.css
# Also load the editorial theme controller:
extra_javascript:
  - https://i.fontlab.com/fltheme26/1.0.0/editorial/fontlab-theme.js

Merge these lists with the four shared assets; the fragment is not a complete configuration. Inspect the Marketing sources or Ornotto sources for the companion markup. marketing-vendor.css supports legacy unprefixed content; new components should still use the scoped bundle. The small styleguide.css variant is specific to the writing guide.

Use it in static or Webflow pages

Load the same assets in the final HTML, after existing broad styles. This example adds the FontLab global menu and footer without a MaterialX drawer:

<link rel="stylesheet" href="https://i.fontlab.com/fltheme26/1.0.0/components.css">
<link rel="stylesheet" href="https://i.fontlab.com/fltheme26/1.0.0/theme.css">
<script src="https://i.fontlab.com/fltheme26/1.0.0/basecoat.js" defer></script>
<script src="https://i.fontlab.com/fltheme26/1.0.0/theme.js" defer></script>
<script src="https://i.fontlab.com/menu/fontlab.js" defer></script>

<fontlab-menu mobile-menu="global" mobile-search="global"></fontlab-menu>
<main>
  <div class="fltheme-components"><button class="btn">Continue</button></div>
</main>
<fontlab-footer></fontlab-footer>

Plain Webflow pages retain their global controls. In fontlab-www-toolkit builds, Markdown is built first, Webflow and static pages are overlaid, then shared assets are injected into final HTML. Check public/, not only the intermediate build_docs/. Preserve cached Webflow content when rebuilding with --skip_webflow.

Navigate with the keyboard

KeyWhile reading a documentation page
Right or Alt+RightNext page in navigation.
Alt+LeftPrevious page in navigation.
LeftPrevious page in browser history.

Generated rel="next"/rel="prev" links take priority; primary navigation is the fallback. There is no wraparound at either end. Inputs, editable text, widgets, open dialogs, selected text and other modifier combinations keep their normal behavior. Repeated keydown events do not repeatedly navigate.

Standalone HTML can opt in with data-fl-page-navigation on the body and next/previous links in the head. This guide opts in; ordinary Webflow pages do not.

Check the integration

  1. Open browser Network tools. Confirm all four shared assets load once, with CSS before their overrides and Basecoat before the theme runtime.
  2. Inspect a button: its ancestor must be .fltheme-components. A daisyUI class such as btn-primary must become du-btn-primary.
  3. If Markdown attributes print literally, enable attr_list. If Markdown inside a div is literal, enable md_in_html and add markdown="1".
  4. If local menu/search is missing, preserve the native MaterialX header via {{ super() }}, enable search, and check the four required native selectors. Check your ownership attributes before changing CSS.
  5. If search opens but finds nothing, serve through HTTP, wait for its index to initialize, and check that search/search_index.json is published at the correct subpath.
  6. If an old look persists, check the final HTML after Webflow/static overlays. Remove duplicate bundles, verify the public asset against the release manifest, then reload cached assets.
  7. At a narrow width, scroll the global bar out of view. Open and close local navigation and search, check the right-hand drawer, then widen the window and confirm desktop controls and palette placement.

Local previews can retain unrelated browser-console problems from a host site. Record those separately from failures in the shared assets; do not hide them or infer success from a screenshot alone.

Build, update and publish

To consume the theme, publish the output of your normal site build. To change the shared theme itself, work in fontlab-www-docstheme:

npm ci
npm run build
npm test
npm run test:browser
npm run deploy

The build needs Node, npm, uv and Python; browser tests need Playwright Chromium (npx playwright install chromium). npm run deploy only stages and hash-verifies dist/ into the sibling img/docs/fltheme26/. Commit and push the CDN repository, wait for Pages success, purge changed URLs if needed, and verify the public files. It does not deploy consumer sites.

Edit src/ for behavior and styles, examples/ for this guide, and fixtures/materialx/ for the native integration fixture. Never edit generated CDN CSS as a source change. manifest.json contains SHA-256 hashes and sizes; 1.0.0/licenses/ retains upstream licences.

The 1.0.0/ path currently receives compatible fixes in place; it is not an immutable release snapshot. Consumers get those changes without a Markdown rebuild once caches refresh. Pin a downloaded asset set if you need an immutable deployment.

See the change history, consumer inventory and published manifest. The latest shared work covers responsive menu/search ownership, Ornotto’s branded layout, page shortcuts, component harmony, equal-sized buttons, opaque Vexy mobile search and final-paragraph spacing.

Basecoat

daisyUI

du- prefix
  • Draw
  • Space
  • Export
How do the libraries coexist?
Basecoat keeps its class names. daisyUI uses the du- prefix.