Statac

Shape the site

Assets and static files

Files that aren’t pages go in assets/, for templates to link to, or in static/, to be published exactly as they are.

Which folder?

assets/ static/
What goes in it Files templates link to: stylesheets, Sass, scripts, fonts. Files that must keep their name and address, such as .well-known/security.txt or a worker’s script.
What Statac does Builds Sass into CSS, and names each file by what it holds. Nothing: each file is published as it is.
Where it is published At the top of the site, with a mark in its name: assets/site.css at /site.1a2b3c4d.css. At the same path from the top of the site: static/.well-known/security.txt at /.well-known/security.txt.
How a page links to it With css(), js() or asset() in a template. By its address. In a template, url() keeps it inside the site.

The mark in an asset’s name changes whenever a byte of the file does, so a browser can keep the file for as long as the host lets it.

A new site’s static/favicon.ico is published at /favicon.ico, where browsers look for the icon they show in a site’s tab.

Link a file from a template

{{ css("site.css") }}
{{ js("app.js") }}
<link rel="preload" href="{{ asset('fonts/font-1.woff2') }}" as="font" crossorigin>
Function Writes
css("site.css") The stylesheet’s <link>.
js("app.js") The script’s <script>: see scripts.
asset("fonts/font-1.woff2") The file’s address, for a tag you write yourself.

Each takes the file’s path inside assets/.

Stylesheets

In assets/site.css:

@font-face {
  font-family: "Font 1";
  src: url("fonts/font-1.woff2") format("woff2");
}

An address in a stylesheet that names another file in assets/, in url(...) or @import, is written as that file’s published name.

In assets/ Statac
site.scss or site.sass Builds it into site.css, linked as css("site.css").
A Sass file whose name starts with _, such as _parts.scss Publishes it only as part of the stylesheets that use it.

For another CSS tool, make it a command of your own that writes its stylesheet into assets/ before each build.

The code stylesheet

{{ css("code.css") }}

Each build publishes code.css, a stylesheet Statac writes with the colours for code: code blocks, files shown as code, and the highlight filter. Link it in the layout’s <head>.

code_style in statac.yaml chooses the colours:

code_style The colours
ocean Base16 Ocean: muted blues, greens and reds. The default.
gruvbox Gruvbox: warm and retro.
solarized Solarized.
catppuccin Catppuccin: Latte in the light, Mocha in the dark.
one-half One Half.

Each style is a light look and its dark twin, and the reader’s own setting chooses between them. Where an element around the code carries data-theme="light" or data-theme="dark", as a site’s own switch might set on <html>, that look is kept whatever the reader’s setting.

A colour too faint to read on its background is written a little darker, or lighter in the dark look, to give a contrast of 4.5 to 1. The classes on the code follow code_style, so a stylesheet written for one style doesn’t fit another.

Each style brings a background for the code. The stylesheet statac new writes puts the code on the page’s own surface instead:

pre.code,
figure.code {
  background: var(--surface);
}

On that surface two styles fall a little under 4.5 to 1 in the light look: solarized at 4.31 to 1 and one-half at 4.16 to 1. Remove the three lines and each style has its own background back.

An assets/code.css of your own takes the place of Statac’s. To start from Statac’s, copy the one in the built site, named with its mark, such as _site/code.7d70e732.css, into assets/ as code.css.

A stylesheet for maths

In assets/maths.css, beside a copy of mathmlfixes.css:

@import "mathmlfixes.css";

@font-face {
  font-family: "Libertinus Math";
  src: url("fonts/LibertinusMath-Regular.woff2") format("woff2");
  font-display: swap;
}

math {
  font-family: "Libertinus Math", math;
  /* The patched font's raised primes. */
  font-feature-settings: "ss09";
}

Statac builds maths as MathML and adds nothing else for it. Without a maths font of the site’s own, a browser uses the system’s, which differs from one computer to the next. Two files put that right:

  • A maths font in assets/, such as Libertinus Math from the patched math-core-fonts, made for MathML in browsers.
  • mathmlfixes.css, published with math-core, the library Statac builds maths with. It evens out how browsers space a matrix’s cells and draw \cancel, \boxed and accents.

A template loads the stylesheet only on pages whose writing holds maths:

{% if page.maths %}{{ css("maths.css") }}{% endif %}

Scripts

{{ js("app.js") }}

A script in assets/ is published as written, under its marked name. js() reads it to tell which kind it is:

The script js() writes
A module: it has an import or export statement, or import.meta <script type="module" src="/app.01c5c780.js"></script>
Any other script <script src="/plain.95a7d502.js" defer></script>

Modules and the import map

In assets/app.js:

import { one } from "./lib.js";

Modules import each other by their own names. On a page where js() places a module, or a script that loads one with import(...), Statac writes an import map before it, sending the name of every script in assets/ to its published one:

<script type="importmap">
{
  "imports": {
    "/app.js": "/app.01c5c780.js",
    "/lib.js": "/lib.574cb057.js"
  }
}
</script>

Warning:

A browser reads the import map on the page, never inside a worker. Keep a worker, and the files it imports, in static/.

What JavaScript does the site carry?

JavaScript the site carries:
  `/app.01c5c780.js` from `assets/app.js`, on 1 page

Statac never puts JavaScript on a page unasked: the one <script> a build adds is the import map, which holds no code. Serve adds a script to each page it sends, to reload it, and never writes it into a file.

statac check lists the JavaScript the site carries after the problems it shows.

On a page statac check lists it
A script from assets/, placed with js() By its address and file, as above.
JavaScript written in a template, in a page’s own HTML, or in a piece of the writing under snippets/markdown/ By its file: Written in `templates/base.html`, on 12 pages.
A script loaded from another site, or anything embedded that may run a script of its own By its address.
What a snippet called by name holds, written out whole in its file Not at all: calling a snippet is choosing what its file holds.
JavaScript a snippet is handed, or an address it fills in as the page is built, such as <script src="{{ address }}"> By the file that wrote it.

The starter’s two snippets for videos kept on other sites each hold a script, so a site that calls them is told nothing of it.

Where next?

Something unclear or wrong? Open an issue on GitHub.