my diamond-shape logo; quite minimalisitic. Liam Collod

Site Developer Documentation

The developer documentation for building this html site.

This is a static website, meaning all its content is generated before being published online and every page correspond to a single html file.

The published website is just HTML and CSS, that are generated using a custom workflow based on Python, rst and Jinja.

Basics

The philoshophy for this website is that the source file structure represents the published site structure. It tries to be as explicit as possible.

So let's say your website is published at https://mywebsite.com, then https://mywebsite.com/blog/index.html implies there must be a /blog/index.rst file. Same for https://mywebsite.com/images/art.jpg which implies images/art.jpg must exist in the source file structure.

It is possible to exclude some file in the source hierarchy to be collected. This is achieved using the .siteignore mechanism.

Another feature which affect the file structure is shelves. A shelf will allow to group and browse multiple pages.

.siteignore files

A .siteignore is a file that indicate which path must not be collected to build the site. It can be fond at root but also in any directory.

It's file format is as follow:

  • each line defines a "path expression" which resolves to multiple paths to ignore.

  • a line can be empty

  • path expression follow the python glob syntax

  • path expression MUST be relative to the .siteignore file directory

  • path expression may resolve to non-existing paths

.siteignore files are cumulative, this means that their paths are made absolute then grouped together and its this list which is used to ignore paths.

Example:

.siteignore
   **/.*.html
   *.txt
index.html
somestuff.txt
blog/
   .siteignore
      **/*.cpp
   index.html
   .template.html
   snippet.cpp
   resource.txt

In the above we have an expression at root that will ignore all html files that starts with a dot, the ** is a glob pattern which express recursion, meaning that blog/.template.html will be ignored. We will also ignore somestuff.txt but NOT resource.txt.

We then ignore blog/snippet.cpp.

shelf feature

TBD

build process

This is how the source file structure is parsed the site final file structure:

  • collect all file paths in the source directory and ignore some paths using the .siteignore files.

  • read and convert rst file as pages

  • collect shelves

  • render pages with their template and write to disk

  • write shelves pages to disk

  • copy static resources

See lxmsite._build for the code implementation.

Creating a page

All pages MUST have an .rst file, even if it just have a title. You are then free to define its content using the standard rst syntax or to manually create the html with a template.

writing rst content

See https://docutils.sourceforge.io/docs/user/rst/quickref.html.

page metadata

This are the fields that are understood as page metadata:

name

description

authors

Comma separated list of person who authored the page. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name

tags

List of tags matching the page topics

language

Language of the page. As standardized by https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang and https://www.w3.org/International/articles/language-tags/

title

Additional override if the rst file title is not desired. See https://ogp.me/#metadata

type

Caracterize the kind of content of the page. As standardized by https://ogp.me/#types

image

Relative file path to the image to use as cover for the page. See https://ogp.me/#metadata

image-alt

Alt text to describe the content of the image field.

description

Short, human-readable summary of the page content. See https://ogp.me/#optional

date-created

Date at which the page was created. Format is YYYY-MM-DDThh:mm. See https://en.wikipedia.org/wiki/ISO_8601

date-modified

Date at which the page was last modified. Format is YYYY-MM-DDThh:mm. See https://en.wikipedia.org/wiki/ISO_8601

template

Relative file path to the html template to use for rendering the page.

stylesheets

Comma separated list of stylesheet path relative to the page. Prefix with a + to inherit the parent stylesheets.

status

either published (no effect) or unlisted (will be excluded from being listed in its parent shelf)

Some extra fields may be used depending on the context:

blog context:

name

description

category

(optional) which type of content is the page

cover

(optional) path to an image to display on top of the blog post.

cover-alt

(optional) the alt text for the cover image.

resources context:

name

description

category

(optional) which type of content is the page

A field is specified under the page title as :field-name: value. Example:

my page
=======

:description: this is quite a long summary that would be
   cool to wrap on 2 lines.

See lxmsite._page for the code implementation.

rst directives

In extent to the builtin rst directives ( https://docutils.sourceforge.io/docs/ref/rst/directives.html ), we provides additional directives, or edit the existing ones.

Here is a quick directive's glossary as reminder:

.. directivename:: argument1 argument2
    :option1:
    :option2:

    content

code, code-block

You can embed code snippets with the code and code-block directives. They use pygments to provide syntax highlighting.

Example:

.. code:: languageName
    :option1: optionValue

    your code
    in multiple lines

admonitions

Admonitions are builtin to rst and there is no changes to them.

admonition, attention, caution, danger, error, hint, important, note, tip, warning

If you want to render a specific admonition type with a custom title you can use the generic .. admonition:: and add the class option with the type. Example:

.. admonition:: πŸ• About pizza
    :class: warning

    Pineapple do belongs on them.

Will render:

highlight

It is however possible to have an admonition without a title using the custom directive .. highlight:::

.. highlight::
    :class: tip

    Look ma', no hands !

Will render:

url-preview

This is a customd directive which allow you to share links as "static embeds", meaning they have the box with rich content that is prettier than just a link, but you actually have to write all the rich content yourself instead of having fetch using javascript.

It required one mandatory argument which is the url to "prettify".

The directive have 4 options:

  • title: title to use for the preview

  • image: url to an image file (relative or absolute).

  • svg: relative url to a local svg file (relative to the page directory).

  • color: the css color of the svg.

  • svg-size: 1 or 2 number indicating the size of the svg. ex: '64' will set the svg to 64x64 px

The content of the directive will be used as description.

Example:

.. url-preview:: https://liamcollod.xyz
    :title: Website - Liam Collod
    :image: ../.static/images/cover-social.jpg

    Check my website & blog. VFX, imaging and software development.

Website - Liam Collod

Check my website & blog. VFX, imaging and software development.

image-grid

When needing to display a lot of image in a non-sequential layout (so as a grid), you can use the .. image-grid:: directive.

It accept no argument, neither options and all works based on its content.

Each line of the content is treated as an image. You group images into one row by separating them by a blank line. The line must start by the image uri, relative to the page its in and is optionally followed by the image caption.

It is possible the image caption span multiple line; in that case the following lines must start with a 2+ spaced indent.

Example:

.. image-grid::

    path/to/image1.jpg
    path/to/image2.jpg

    path/to/image3.jpg some caption that will be displayed under
    path/to/image4.jpg the caption can span
        multiple lines if it's too long.
    path/to/image5.jpg

.meta.json files

We see previously that each rst page can define some metadata at its top. However specifying everytime some of those fields is a repetitive task. To adress this issue you can use meta files.

Meta files are json files whose content specify default metadata value to use for all files that are next or children in the hierarchy of the meta file. The meta file hierachy is recursively merged so the meta file "closest" to your page will get priority.

Example:

.meta.json
index.rst
blog/
    .meta.json
    index.rst
    post1.rst
    post2.rst

In the above example .meta.json at root will affect index.rst but also all files in the blog/ directory. However the content of blog/.meta.json will take priority over the root one.

Meta file use standard JSON syntax, where a non-nested dict is expected. Each root key defines the name of the metadata to set, which is the same as you would use in the rst page. The value can either be a string or list of string.

List of strings are handled differently but allow merging, this mean that the child meta file will extend() the parent meta file list if it exists. When resolved in the rst file, lists are converted back to string by joining its items with a ,.

It's also totally possibel that for the same metadata key, switch between a list type or a str type. A str type will override any list value defined before, and a list value when the previous value was a string, will cast the previous value to a list automatically.

The code logic can be found in lmxsite._browse.

Writing page html templates

All html templates are processed with Jinja. Refers to their documentation for how to write Jinja templates.

In addition to the standard Jinja syntax, the following objects are available (some explained in details after):

filters:

  • slugify: make the string url-compatible

  • mksiteabs: Convert the given site-relative url to absolute.

  • mksiterel: make an internal link relative to the site root

  • mkpagerel: make an internal link relative to the current page

  • prettylink: remove the ".html" or "index.html" of internal links

variables:

  • Page: the page instance being rendered.

  • Config: the global site config used.

  • Context: additional variables specific to this build.

  • Shelf: optional parent shelf the page belongs to (can be None).

  • ShelfLibrary: collection of all shelves the site has.

  • include_script_output: function to include the output of a python script.

script system

The jinja syntax is not enough and you wish some part of the template was procedurally generated ? You can use the script include system to run an arbitrary python script that generates html (or actually anything).

To create a script, create a standard python file next to the template (can actually be stored anywhere but you need to specify its path relative to the template it is used in). Inside, you only need to declare one mandatory function:

def generate(template_renderer: lxmsite.TemplateRenderer) -> str:
    # your implementation here

The function when executed will return the text that need to be included in the template. The only argument template_renderer is a copy of the instance that is responsible of rendering the template that the script was called from. It allows in theory to recursively render another jinja template from the script or use its attributes for whatever you might need.

To use a script inside a template you use the include_script_output variable that is actually a function to call with the script path (relative to the template):

<div>
    {{ include_script_output("script_name.py") }}
</div>

cross-referencing

How to link to other html pages or static content ?

First, reminder that all relative urls are relative to the page they are on. This mean that if you want to link to a resources based on its site root location, like .static/icon/icon.svg you will need to make it relative to the page instead. This is easily done using the custom jinja filter mkpagerel.

Example:

<img src="{{ ".static/icons/icon.svg"|mkpagerel }}">

If you need the opposite you can also use mksiterel to make an page-relative url; relative to the site root instead.

And if you ever need an absolute url you can use mksiteabs that will prepend the site url but only on publish.

Then when linking pages or content, you must link a file, never a directory. While once published work/myproject/ might resolve fine by the server, locally it will not and you will need to link work/myproject/index.html instead. However just because this make links uglier you can use prettylink that will shorten the links on publish; best of both worlds !

Rss feeds

When creating a shelf, an rss feed will automatically be generated from that shelf as long as a template is specified in the site-config.

The template is a regular jinja2 file that have access to the same filters as the page templates, but different variables which are:

  • URL_PATH: the url path of the feed file; relative to the site root

  • Config: the global site config used.

  • Shelf: teh shelf object to generated the feed from

The generated feed can be accessed at {shelf url}/{shelf name}.rss.xml.