Skip to content

Going further

This page collects advanced FrontMark operations: compiled Svelte components, optimized assets, cache policy, imports, upgrades, translations, protected content, Sveltia collections and deployment pipelines.

Markdown can mount Svelte components with ::: svelte. The source files live in svelte-components/src/** and are compiled to public/svelte/ before Astro builds the site.

make svelte

During component development, run the watcher beside make dev:

make svelte_watch

make build, make preview and both Docker image builds compile the Svelte bundle before Astro. See Svelte Components for directive syntax and component configuration examples.

FrontMark optimizes local PNG, JPEG and WebP images at build time with Sharp. Originals stay in place, generated variants go to public/_optimized/, and Markdown keeps referencing canonical public paths.

make optimize

make build, make preview and Docker builds run optimization before Astro. Existing variants are reused when the source image and image configuration have not changed.

site.config.yaml
images:
  optimize: true
  mode: build
  output:
    directory: /_optimized
    keep_original_fallback: true
    content_hash: true
  formats:
    - avif
    - webp
    - original
  quality:
    jpeg: 82
    webp: 82
    avif: 68
    png_lossless: true
  folders:
    /images:
      profile: documentation
    /uploads:
      profile: balanced
    /brand:
      profile: lossless
    /icons:
      profile: lossless
    /media:
      profile: balanced
  skip:
    max_width_below: 300
    size_below_kb: 15
    extensions:
      - svg
      - gif

Available profiles are balanced, documentation, screenshots, photos, lossless, brand, logos, icons and bypass. Use screenshots for captures containing text, photos for stronger compression, and lossless or bypass for brand-sensitive assets.

Static Docker images generate nginx.conf from site.config.yaml.

site.config.yaml
runtime:
  nginx:
    cache:
      validation:
        etag: true
        if_modified_since: exact
      html: public, no-cache
      stable_assets: public, no-cache
      optimized_assets: public, max-age=31536000, immutable
      optimized_manifest: public, no-cache
      astro_assets: public, max-age=31536000, immutable
      admin_config: public, no-cache
      cms_bundle: public, max-age=31536000, immutable
      stable_asset_paths:
        - /brand/
        - /documents/
        - /icons/
        - /images/
        - /media/
        - /scripts/
        - /styles/
        - /uploads/
        - /videos/

Use immutable caching only for URLs that change when content changes: /_astro/, optimized image variants and /admin/sveltia-cms.js?v=<content-hash>. Keep HTML, manifests, admin config and stable asset paths in no-cache; Nginx sends ETag/Last-Modified validators so browsers revalidate changed files cleanly.

Use the importer when an existing MkDocs Material project must become a FrontMark/Starlight site:

make import /path/to/mkdocs-site
make import MKDOCS=/path/to/mkdocs-site
make install import /path/to/mkdocs-site

The make install import /path/to/mkdocs-site form bootstraps dependencies first, then runs the same import. The importer reads mkdocs.yml or mkdocs.yaml, then copies the MkDocs docs_dir into FrontMark-friendly locations.

Source itemImported behavior
index.mdImported as the documentation entry page, with generated frontmatter when needed.
Markdown subfoldersImported recursively, with slugified file and folder names.
documents/Copied to public/documents/.
images/Copied to public/images/.
videos/Copied to public/videos/.
tags.mdIgnored, because FrontMark builds its own tag index.
overrides/, js/, css/Ignored, because FrontMark owns rendering, scripts and styles.

The importer updates site.config.yaml with portable MkDocs values only.

Config itemImported behavior
site_name, site_url, site_description, site_author, copyrightMapped to FrontMark site, SEO and brand metadata.
theme.logo, theme.favicon, theme.language or theme.localeMapped to FrontMark brand and language settings.
navConverted to menus.sidebar.
Existing astro.menu.linksPreserved during import.
Previous-menu comments from make erase-allRemoved when site.config.yaml is rewritten.
Analytics IDs found in ga.jsMapped to analytics.ids.
extra.consentNormalized to FrontMark consent keys.
Public encryptcontent UI labelsNormalized to security.encryptcontent.

Run make erase-all before importing when you want a clean navigation state. It resets astro.menu.links and menus.sidebar to empty lists and comments their previous values temporarily; make import then fills menus.sidebar from the MkDocs nav and removes those comments.

Directory entries such as Raquettes a neige: raquettes are expanded from the matching folder. If include_dir_to_nav is configured, reverse_sort_file and reverse_sort_directory are respected so numbered files and folders keep the same ordering as MkDocs.

MkDocs Material features, palette, markdown_extensions, arbitrary extra data, plugin internals, passwords, theme internals, extra_css and extra_javascript are not migrated.

Every imported Markdown page receives at least frontmatter title; description is filled from site_description when the page does not define one. Relative links to copied assets are rewritten to public paths such as /images/photo.jpg, /documents/manual.pdf and /videos/demo.mp4.

Run the importer test after changing import behavior:

make import_test

Use make upgrade to refresh the FrontMark engine of an existing site without replacing local content or central configuration.

make upgrade

By default, the upgrade clones https://gitea.newkube.ia86.cc/sigMAX/Image_frontmark, copies the core implementation over the current project, refreshes the Makefile so the upgrade target itself can evolve, then runs npm install.

Local IMAGE_NAME values in the Makefile and Dockerfile ARG IMAGE_NAME=... lines are preserved during the refresh, along with Dockerfile ARG VERSION_MAJOR=... values.

Protected paths are skipped:

Protected pathReason
site.config.yamlCentral site configuration stays local; bundled theme entries under theme.availableStyles are synchronized from the reference.
src/content/Markdown pages and collections stay local.
src/imported/Imported MkDocs sidebar and redirects stay local.
public/brand/, public/styles/overrides.css, public/styles/astro-overrides.cssBranding assets, generated favicon and both local CSS overrides stay local.
public/images/, public/videos/, public/media/, public/documents/, public/icons/, public/uploads/Site assets stay local.
public/scripts/mkdocs-import-config.js, public/search/, public/admin/, public/svelte/, dist/, .astro/, node_modules/Generated artifacts stay local or are rebuilt.

Useful variants:

make upgrade UPGRADE_DRY_RUN=1
make upgrade UPGRADE_REF=main
make upgrade UPGRADE_INSTALL=0
make upgrade UPGRADE_REPO=https://example.test/FrontMark.git
make upgrade UPGRADE_PROTECTED_PATHS="site.config.yaml public/brand public/styles/overrides.css public/styles/astro-overrides.css"

Bundled themes under public/styles/themes/ are part of FrontMark core and are refreshed by make upgrade. The matching entries in theme.availableStyles are replaced from the reference so their metadata stays identical, and missing bundled themes are appended. Missing site.config.yaml options are added from the reference, while existing local values such as the active theme.style are kept. Local custom theme entries that do not exist in the reference are kept too. Put documentation-wide CSS changes in public/styles/overrides.css and top-menu Markdown page changes in public/styles/astro-overrides.css; both files are protected.

The left documentation menu is controlled by menus.sidebar.

site.config.yaml
menus:
  sidebar:
    - label: FrontMark
      items:
        - label: Overview
          link: /docs/
          icon: material-home-outline
        - label: Getting started
          link: /docs/getting-started/
          icon: material-playlist-check
        - label: Going further
          link: /docs/going-further/
          icon: material-rocket-launch-outline

Rules:

  • link values are written without the public prefix.
  • site.base is added at build time.
  • External entries use href and external: true; they are rendered with target="_blank" and do not receive site.base.
  • icon values use the same icon shortcode style as the imported MkDocs Material content.

Add footer.categories to define a structured multi-column footer. Each category and link is read from site.config.yaml; internal links receive site.base, while repository: true resolves to the configured Git repository. The structured layout is enabled per theme with categoriesEnabled: true.

site.config.yaml
footer:
  showVersion: true
  brand:
    title: FrontMark
    subtitle: Enriched Markdown
    description: Write once. Publish clearly.
    logo: /brand/icon.png
    logoWidth: 45px
    logoAlt: FrontMark
    mode: both # text, logo, or both
  categories:
    - label: Domains
      links:
        - label: Enriched documentation
          href: /docs/
    - label: Contact
      links:
        - label: Contact us
          href: /contact/
        - label: Repository
          repository: true

footer.showVersion: true displays the same build version badge as the header. The value comes from PUBLIC_SITE_VERSION, or from APP_VERSION combined with VERSION_MAJOR; no badge is rendered when none of these variables supplies a version. PUBLIC_SHOW_FOOTER_VERSION=false can disable it at build time.

footer.brand.mode accepts text, logo, or both. In logo mode the configured image replaces the title and subtitle, while the description and social links remain available. footer.brand.logoWidth controls the displayed width and accepts either a number in pixels or a CSS length such as 45px or 3rem; the image height is calculated automatically from its original ratio.

Enable the categorized layout only on the themes that need it:

site.config.yaml
theme:
  availableStyles:
    - name: underground
      footer:
        enabled: true
        fullWidthInDoc: true
        alwaysOnTop: false
        categoriesEnabled: true

Themes without categoriesEnabled: true keep the original compact footer, even when category data is configured globally. Astro pages already render the footer as a full-width child of the page; fullWidthInDoc: true gives documentation pages the same full-bleed behavior outside the Starlight content column.

The top-level astro and documentation blocks provide defaults for every theme. Add the matching block to a style when that rendering mode needs different behavior; the style values override the global values and omitted values keep inheriting them.

site.config.yaml
astro:
  menuAlign: right
  showQrCode: true
  showBackToTop: false

documentation:
  search: true
  menu: frontmark
  menuAlign: left
  chapters: desktop-and-tablet
  showQrCode: true
  hideInactiveMenusOnScroll: true
  bodyLineHeight: 1.75

theme:
  availableStyles:
    - name: underground
      astro:
        menuAlign: extreme-right
        showQrCode: false
        showBackToTop: true
      documentation:
        chapters: false
        menuAlign: extreme-right
        showQrCode: false

astro.showQrCode and documentation.showQrCode are independent. Setting one to false removes the complete page-tools block in that mode, including the source and edit actions; no empty placeholder is kept. The other supported documentation overrides are search, menu, chapters, hideInactiveMenusOnScroll, and bodyLineHeight. Setting chapters: false hides the Previous/Next chapter links and keeps every documentation category visible instead of applying the scroll-based chapter filter. Theme changes made with the development style selector update these display settings immediately.

astro.showBackToTop controls the translated return-to-top button on Astro pages. It is hidden by default and appears after the visitor has scrolled down. Set it globally under astro, then override it for one style under theme.availableStyles[].astro. The button reuses the documentation up-arrow icon and smooth-scroll behavior, and updates immediately in the development style selector.

astro.menuAlign and documentation.menuAlign independently position the desktop navigation menu. The global defaults are right for Astro and left for documentation; the underground theme overrides both alignments with extreme-right. The accepted values are:

  • extreme-left: before the logo;
  • left: immediately after the logo;
  • center: centered in the header;
  • right: on the right, before the search field;
  • extreme-right: after the search field but before the action icons;
  • max-right: completely right, after the action icons.

max-left is accepted as a compatibility alias for max-right. Tablet and mobile layouts continue to use the responsive sandwich menu.

The active style is selected in site.config.yaml. A style is one folder containing its CSS and optional font files under public/styles/themes/<name>/.

site.config.yaml
theme:
  defaultMode: auto
  showThemeSwitcher: ondev
  scrollProgress:
    enabled: true
    height: 4px
  cssOverrides:
    - /styles/overrides.css
  style: sigmax
  availableStyles:
    - name: sigmax
      label: sigMAX
      colorMode: dark
      stylesheet: /styles/themes/sigmax/theme.css
      footer:
        enabled: true
        fullWidthInDoc: true
        alwaysOnTop: true
      i18n:
        visibility: footer
      header:
        minimizeOnScroll: true
        minimizeScrollY: 56
    - name: underground
      label: underground
      colorMode: light
      stylesheet: /styles/themes/underground/theme.css
      header:
        minimizeOnScroll: false
        alwaysOnTop: transparent
        transparentScrollY: 100
        fullWidthInAstro: true
        brandMode: logo
        brandPosition: left
        indexMenu: sandwich
      astro:
        fullWidthBody: true
        showBackButton: false
        showBackToTop: true

Available bundled styles include sigmax, health, mountains, caving, underground, software, blog, old and wired. Use theme.defaultMode: auto to follow the active style colorMode, or set dark or light to force the site theme. The top light/dark selector is only usable when the active style declares colorMode: switch. theme.scrollProgress controls the top scroll indicator; its gradient is provided by the active theme CSS.

For Astro pages, header.fullWidthInAstro: true expands the navigation bar inside responsive page gutters, while astro.fullWidthBody: true does the same for the page body below it. Header branding is also selected per style: header.brandMode accepts both, logo, or text, and header.brandPosition accepts left or right. header.indexMenu changes navigation only on the index page: use normal for desktop links, sandwich for the same compact menu used on tablets, or hidden for no navigation menu. Other pages retain their normal responsive menu. header.alwaysOnTop: true keeps the navigation fixed with its nominal surface, false lets it leave the viewport with the page, and transparent keeps it fixed but transparent until header.transparentScrollY (100 pixels by default), then restores the active style colors. The transparent_white_in_mini and transparent_black_in_mini variants retain that behavior and the existing responsive icon, while forcing a white/black or black/white button palette on tablet and mobile layouts. When the optional brand.logo2 is configured, it replaces brand.logo only during that transparent state. astro.showBackButton: false hides the small return-to-home icon for the selected style; page frontmatter can still set back: false to disable it on an individual page regardless of style. astro.showBackToTop: true independently enables the translated button that appears after scrolling and returns to the top of the current Astro page.

Astro and documentation pages can override the active theme for one page. The nested header.indexMenu form is preferred; a top-level indexMenu value is also accepted as a short alias.

See Page shell, header and first-screen scrolling for the complete mode table, configuration precedence, responsive behavior and troubleshooting.

Page frontmatter
---
scrollonpage: true
header:
  indexMenu: sandwich # normal, sandwich, or hidden
  alwaysOnTop: transparent # false, true, transparent, or a transparent_*_in_mini mode
  transparentScrollY: 100
footer:
  alwaysOnTop: false
---

scrollonpage: true animates a downward wheel, touch, or keyboard gesture to the end of the first root=top section whenever the current position is inside it. The current viewport height is used as a fallback, so the destination adapts to the screen size. Scrolling is normal from the section boundary onward; returning inside the first section enables the animated jump again. Leave it unset or use scrollonpage: false to disable the behavior. The camel-case alias scrollOnPage is also accepted.

header.indexMenu: hidden removes both the desktop links and the sandwich button, while normal restores the regular responsive navigation. header.alwaysOnTop: false makes the complete documentation frame scroll: the header, left navigation, and right table of contents all leave the viewport with the page. It also keeps every left-navigation category visible, disables the scroll-based current-category filter, and forces the footer into the normal document flow even if footer.alwaysOnTop is true. With a fixed header, footer.alwaysOnTop independently chooses between the normal document flow and a footer fixed to the bottom of the viewport. A categorized footer always remains in the document flow because its multi-column height is not suitable for a fixed overlay. The transparent header mode is also treated as fixed; on a top root container, combine it with root-header=light or dark to keep the navigation legible over the hero before the nominal theme colors return.

CSS override files are loaded after the active bundled theme. The default public/styles/overrides.css and public/styles/astro-overrides.css files are protected from make upgrade, which makes them the safest places for project-specific CSS.

The language selector is generated from i18n.languages. The number of available translation buttons is derived from that list, or limited explicitly with languageCount. Toolbar placement is configured per style with theme.availableStyles[].i18n.visibility.

site.config.yaml
i18n:
  enabled: true
  provider: google
  defaultLanguage: en
  defaultTextScale: xlarge
  languageCount: 3
  languages:
    - code: en
      label: English
      nativeLabel: English
      flag: GB
    - code: fr
      label: French
      nativeLabel: Francais
      flag: FR

The language and text-size toolbar is marked as notranslate, so Google Translate does not rewrite the controls themselves.

The cookie consent banner is generated from the central YAML configuration.

site.config.yaml
consent:
  enabled: true
  storageKey: frontmark-cookie-consent
  title: Cookie preferences
  description: FrontMark can store local preferences such as language and text size.
  acceptText: Accept
  rejectText: Reject
  policyLink: /docs/going-further/#cookie-banner
  policyText: Learn more

FrontMark supports the common mkdocs-encryptcontent-plugin frontmatter pattern. Use password for a page-specific password, or level for a named password stored centrally.

site.config.yaml
security:
  encryptcontent:
    enabled: true
    rememberPassword: true
    storage: session
    levels:
      demo: frontmark
src/content/docs/encryptcontent.md
---
title: Protected page
level: demo
---

The example page is available at Encrypt Content. Its demo password is frontmark.

Collections live under admin.collections.

site.config.yaml
admin:
  mediaFolder: public/uploads
  publicFolder: /uploads
  collections:
    - name: docs
      label: FrontMark Documentation
      folder: src/content/docs
      create: true
      extension: md
      format: frontmatter
      slug: "{{slug}}"
      previewPath: /docs/{{slug}}/
      fields:
        - label: Title
          name: title
          widget: string
        - label: Content
          name: body
          widget: markdown

Important details:

  • folder points to the repository folder edited by Sveltia.
  • previewPath is written without /frontmark; the active site.base is added automatically.
  • publicFolder is also written without /frontmark; the generated Sveltia config adds the base.
  • mediaFolder is the repository path where uploaded files are committed.
  • fields are passed to Sveltia as-is, so nested fields, selects, booleans and lists are defined here.

The broader Sveltia backend setup is documented in Configure Sveltia.

To publish the site at the domain root with no /frontmark prefix anywhere, change only site.base:

site.config.yaml
site:
  base: ""

The build, tag routes, runtime config, Sveltia api_root, Sveltia base_url, public_folder, logo paths and every preview_path are generated from the same configuration. No manual edit is needed in generated runtime files.

After changing the base, rebuild:

make rebuild

Then check the site at /, the documentation at /docs/, the admin at /admin/, and static assets such as /brand/favicon.ico.

After editing site.config.yaml, run:

make rebuild

Then check the generated Sveltia endpoints:

URL with /frontmarkExpected result
/frontmark/admin/config.jsonJSON config consumed by the CMS.
/frontmark/admin/config.ymlYAML view useful for debugging.
/frontmark/admin/sveltia-cms.jsSveltia CMS JavaScript bundle.

For a domain-root deployment, use /admin/config.json, /admin/config.yml and /admin/sveltia-cms.js.

Use Make commands in CI:

.gitea/workflows/build.yml
steps:
  - name: Install dependencies
    run: make install

  - name: Build site
    run: make build

For Docker deployments:

make image IMAGE=registry.example.com/frontmark:latest
docker push registry.example.com/frontmark:latest

The default Dockerfile builds the fully static variant: Svelte is compiled, tags are generated, images are optimized, Astro writes static files, the CMS config is generated for Forgejo OAuth through the dedicated proxy, and Nginx serves the prepared static root. Cache headers are generated from runtime.nginx.cache.

Behind Traefik, keep site.base equal to the public prefix, for example /frontmark. The static Nginx runtime accepts both upstream shapes: Traefik can preserve the PathPrefix("/frontmark") path, or an existing StripPrefix("/frontmark") middleware can strip it before the request reaches the container.

For a static deployment with OAuth sign-in, build the static image and deploy the dedicated OAuth/API proxy container beside it:

make image IMAGE=registry.example.com/frontmark:latest
make image_oauth OAUTH_IMAGE=registry.example.com/frontmark-oauth:latest
docker push registry.example.com/frontmark:latest
docker push registry.example.com/frontmark-oauth:latest

Route these Traefik prefixes to the OAuth proxy service:

Public pathService
/frontmark/oauth/forgejo/*OAuth/API proxy container.
/frontmark/api/forgejo/v1/*OAuth/API proxy container.
/frontmark/*Static FrontMark container.

The proxy container accepts both Traefik modes: preserving /frontmark or using StripPrefix("/frontmark"). Configure PUBLIC_GIT_ORIGIN when the Gitea/Forgejo origin should come from the environment instead of site.config.yaml.