Coricamu

Coricamu allows you to generate a static site using the Nix package manager.

A static site is one which does not run any code on the server side. It can still use JavaScript on the client side to provide some level of interactivity.

The following documentation assumes that you have some prior experience with the Nix language and website development.

Usage and features

Coricamu uses the module system for website configuration. This is the same way that NixOS machines are configured, but with different options to choose from. You can use imports, define your own options, and use the mkIf, mkMerge and mkForce functions, just as you would in NixOS.

Creating a flake

Coricamu expects you to use Flakes for dependency management.

Before writing a module, you need to create flake.nix as follows. Alternatively, this can be combined with an existing flake.nix as part of a larger project.

{
  inputs.coricamu.url = "github:danth/coricamu";

  outputs = { coricamu, ... }:
    coricamu.lib.generateFlakeOutputs {
      outputName = "my-website";
      modules = [ ./website.nix ];
    };
}

Basic information

Within the main module, you must define the following options:

baseUrl
The root URL where your site will be served.
siteTitle
A human-readable title for the site. If it’s not given, this will use your domain name.
language
Representation of the spoken language used on your website, for example EN-US for English, or DE for German.

Here’s an example:

{
  baseUrl = "https://coricamu.example.com/";
  siteTitle = "Coricamu Example Site";
  language = "en-gb";
}

Files

You can add any file directly to your site by putting it in the files attribute set:

{
  files."favicon.ico" = ./favicon.ico;
}

This is versatile, but does not have any smart features. For many file types you should use a more specific option as described below.

Images

The images option will automatically convert many bitmap file types to the modern webp format. This is a recommended action on PageSpeed Insights.

svg images don’t need this conversion: they should be added directly to files.

{
  images = [
    {
      path = "clouds.webp";
      file = ./clouds.png;
    }
  ];
}

path and file can look totally different: there is no need to lay out your source files in the same way that they will appear in the finished website.

Specifying both a path and a file in this way is a common pattern throughout Coricamu.

Pages

Rather than writing out entire documents by hand and adding them to files, Coricamu can generate a lot of the boilerplate for you.

The pages option is what you should use for most pages, unless you have a finished HTML file already.

{
  pages = [
    {
      path = "index.html";
      title = "Home";

      # Currently supports HTML, Markdown or DocBook input
      body.markdown = ''
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
        eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
        minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
        ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
        voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
        sint occaecat cupidatat non proident, sunt in culpa qui officia
        deserunt mollit anim id est laborum.
      '';
    }

    {
      path = "about.html";
      title = "About Us";
      # File only needs to contain the insides of <body>, not an entire page
      body.htmlFile = ./about.html;
    }
  ];
}

Coricamu supports three formats for the body:

All of which can either be given as a string within your Nix file, or a path to a separate file.

You can also give images and files within an individual page. The path of each image or file is still relative to the root of the website - so a path of clouds.png will appear at https://example.com/clouds.png, even if the page is in a subdirectory at https://example.com/subdirectory/page.html.

Header / Footer

You can define a header and footer which will be repeated on every page of your site.

{
  header.html = ''
    <h1>My Website</h1>
  '';

  footer.markdown = ''
    Content on this site is available under the *Lorem Ipsum License* unless
    otherwise stated.
  '';
}

The supported content types for the header and footer are the same as those for the body of each page.

Posts

If you are building a blog, consider using posts instead of pages. You can of course choose to build your own blog by using the pages option instead.

The posts option requires some extra information about each post, in return for which you get an automatically generated index page and RSS feed, which are linked at the bottom of each post. The extra information is also embedded into the page, using Microdata, so that search engines can understand it.

{
  posts = [
    {
      title = "Lorem Ipsum";
      datetime = "2022-01-31 20:10:05Z";
      authors = [ "John Doe" "Jane Doe" ];
      body.markdownFile = ./lorem_ipsum.md;
    }
    {
      title = "Ut Enim Ad Minim";
      datetime = "2022-01-31 20:10:05Z";
      edited = "2022-03-10 07:55:00Z";
      authors = [ "Jane Doe" ];
      body.htmlFile = ./ut_enim_ad_minim.html;
    }
  ];
}

Posts can be categorised by adding one or more sections:

{
  title = "Lorem Ipsum Dolor";
  datetime = "2022-02-26 11:29:26Z";
  authors = [ "John Doe" ];
  sections = [ "lorem" "ipsum" "dolor" "sit amet" ];
  body.markdownFile = ./lorem_ipsum_dolor.md;
}

Generated pages

The generated index page is at posts/index.html, and the RSS feed is at posts/rss/index.xml.

If you have more than one author, or have used sections, posts/pills.html will also be generated. This allows visitors to filter posts by author or section.

Coricamu asks search engines not to index posts/index.html, posts/pills.html or any other lists. This allows them to spend more time indexing your actual content instead. Search engines don’t need to read these pages to discover your posts because sitemap.xml is provided.

Templates related to posts

Coricamu includes two templates related to posts:

all-posts
A chronological list of all posts, as found on posts/index.html.
recent-posts
A chronological list of the newest count posts.

You will learn more about how to use templates later in this document.

Styles

Coricamu comes with a basic style sheet which is imported by default. This makes some of the generated elements look better, without introducing any outstanding design.

If you define any style sheets of your own…

{
  styles = [{
    path = "style.css";
    cssFile = ./style.css;
  }];
}

…then the default one will be removed. If you would like to build on top of Coricamu’s style sheet rather than replacing it, you can insert the default manually like this:

{ options, ... }:

{
  styles = options.styles.default ++ [{
    custom = {
      path = "style.css";
      cssFile = ./style.css;
    };
  }];
}

Sass / SCSS style sheets are also supported:

{
  styles = [{
    # This is the path of the output file, so it is still .css
    path = "style.css";

    # This is the input file
    scssFile = ./style.scss;
  }];
}

Styles can be added within an individual page too. If you use the same path for style sheets on multiple pages, it will cause an error unless those style sheets are exactly the same.

Templates

Templates can be used to avoid HTML boilerplate even more, or to standardise the presentation of a particular design element.

Defining templates

A template is just a Nix function which takes an arbitrary set of parameters (in this case title and contents), and returns some HTML. Templates can rely on other templates; they can even call themselves, if you are careful to avoid infinite recursion.

Custom templates are added to the templates attribute set:

{
  templates.info.function =
    { title, contents }: ''
      <div class="info">
        <h2>${title}</h2>
        <p>${contents}</p>
      </div>
    '';
}

Most page settings can be specified within templates too. Those settings will be added to any page where that template is used. This can be used to install extra files to make the template work, for example a style sheet:

{
  templates.info = {
    function =
      { title, contents }: ''
        <div class="info">
          <h2>${title}</h2>
          <p>${contents}</p>
        </div>
      '';

    styles = [{
      path = "info.css";
      css = ''
        .info {
          border: 3px solid black;
          padding: 5px;
        }
      '';
    }];
  };
}

Template tags

Templates are inserted using “template tag” syntax. This looks similar to a HTML tag, but its name must match up with one of the templates you have defined for your website.

<p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
  tempor incididunt ut labore et dolore magna aliqua.
</p>
<templates-note title="An important note">
  Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
  ut aliquip ex ea commodo consequat.
</templates-note>
<p>
  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
  dolore eu fugiat nulla pariatur.
</p>

Coricamu will parse the tag and call the corresponding template function.

This is all handled within Nix.

Note that template tags also work in Markdown, but not DocBook.

Icons

Icons from Font Awesome 6 can be inserted using the built-in font-awesome template:

<templates-font-awesome icon="book" />

For logos, you need to add style="brands":

<templates-font-awesome style="brands" icon="github" />

The style attribute can also used to switch between regular and solid for the non-branded icons.

You can add content to the template to have it written alongside the icon:

<templates-font-awesome icon="book">
  <p>These are some notes next to a book icon.</p>
</templates-font-awesome>

Icons and styles from Font Awesome Pro are not yet supported.

Diagrams

Mermaid diagrams can be inserted using the built-in mermaid template.

<templates-mermaid>
flowchart TD
insert template ---> get diagram
</templates-mermaid>

Using a mermaid code block in Markdown may have same visual output, however this could break in future updates. You should prefer using the template.

Compilation

There are two commands which will be most important to you during development:

nix build .#my-website
This will compile your website and place its files into result. These can be inspected, or deployed to a web server by hand.
nix run .#my-website-preview
This will compile the website as above, but launch a local web server so that you can test the site in a browser. Further instructions on how to do this will be printed after running the command.

Deployment

The commands above are useful while writing a website, but you will want to use a more automated setup when you publish it. Coricamu works with multiple web servers and hosts, some of which are documented below.

GitHub Pages

There is a reusable GitHub Actions workflow for deploying to GitHub Pages. To use it, copy the following text to .github/workflows/docs.yml in your repository:

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  pages:
    name: Pages
    uses: danth/coricamu/.github/workflows/pages.yml@cd253a6940853ffc3da7c14c9311940f1d70e222
    with:
      output_name: my-website

The text after output_name must correspond to the outputName you provided to coricamu.lib.generateFlakeOutputs in flake.nix. In the example earler on this page, we used my-website.

Credits

Coricamu was heavily inspired by Styx. Many thanks to the authors of that project!