Zine
Site Structure
Main configuration file
A Zine website is defined by a zine.ziggy config file.
Here’s what the config of zine-ssg.io looks like:
zine.ziggy
.zine_version = "0.13.0",
.site = .simple(.{
.title = "Zine - Static Site Generator",
.host_url = "https://zine-ssg.io",
.content_dir_path = "content",
.layouts_dir_path = "layouts",
.assets_dir_path = "assets",
.static_assets = [
"CNAME",
"fonts",
],
}),
Once you put a zine.ziggy file in a directory, you’re able to run zine to start the dev server and begin to edit your website.
Expand to see its Ziggy Schema
$ = Config
/// This is the schema of a Zine website config file, usually named 'zine.ziggy'.
struct Config {
/// A semver string representing the version of Zine that your website
/// requires. Used by GitHub / Forgejo actions to select automatically
/// the right version of Zine to fetch, see the 'Deploying' section of
/// https://zine-ssg.io/docs/.
zine_version: bytes,
/// Should external links generated by SuperMD automatically add
/// `target="_blank"`? Defaults to `false`.
///
/// Note: you can override this setting on each link by using explicit
/// syntax (e.g. `[my link]($link.url(https://example.com).new(true))`)
auto_target_blank: ?bool,
/// Your website definition
site: Site,
/// User-defined properties that you can reference from SuperHTML via
/// '$site.custom'.
custom: {:}any,
}
union Site {
simple: Simple,
multilingual: Multilingual,
struct Simple {
title: bytes,
/// URL where the website will be hosted.
/// It must not contain a subpath, see `url_path_prefix`.
host_url: bytes,
/// Set this value if your website is hosted under a subpath of `host_url`.
///
/// `host_url` and `url_prefix_path` are split to allow the development
/// server to generate correct relative paths when serving the website
/// locally.
url_path_prefix: ?bytes,
/// Directory containing your SuperHTML layouts.
layouts_dir_path: bytes,
/// Directory containing your SuperMD content.
content_dir_path: bytes,
/// Directory containing your site-wide assets.
assets_dir_path: bytes,
/// Subpaths in `assets_dir_path` that will be installed unconditionally.
/// All other assets will be installed only if referenced by a content file
/// or a layout by calling `$site.asset('foo').link()`.
///
/// Examples of incorrect usage of this field:
/// - site-wide CSS files (should be `link`ed by templates)
/// - RSS feeds (should be generated by defining `alternative` pages)
///
/// Examples of correct usage of this field:
/// - `favicon.ico` and other similar assets auto-discovered by browsers
/// - Font files only referenced inside of CSS files.
static_assets: []bytes,
/// When enabled, Zine will automatically add 'width' and 'height'
/// attributes to <img> elements for local assets.
/// Be aware that setting 'width' and 'heigth' of an image will in some
/// circumstances cause browsers to distort images.
///
/// This problem can be solved by adding the following CSS code to your
/// site:
///
/// img { height: auto; }
///
image_size_attributes: ?bool,
}
struct Multilingual {
/// URL where the website will be hosted.
/// It must not contain a subpath, see `Locale.output_prefix_override`.
host_url: bytes,
/// Directory that contains mappings from placeholders to translations,
/// expressed as Ziggy files.
///
/// Each Ziggy file must be named after the locale it's meant to offer
/// translations for.
i18n_dir_path: bytes,
/// Directory containing your SuperHTML layouts.
layouts_dir_path: bytes,
/// Directory containing your site-wide assets.
assets_dir_path: bytes,
/// Location where site- and build- assets will be installed. By default
/// assets will be installed directly in the output location.
///
/// In mulitilingual websites Zine will create a single copy of
/// site assets which will then be installed at this location. It
/// will be your duty to then copy this directory elsewhere if
/// needed in your deployment setup (e.g. when deploying different
/// localized variants to different hosts).
///
/// NOTE: *page* assets will still be installed next to their
/// relative page.
assets_prefix_path: ?bytes,
/// Subpaths in `assets_dir_path` that will be installed unconditionally.
/// All other assets will be installed only if referenced by a content file
/// or a layout by using `$site.asset('foo').link()`.
///
/// Examples of incorrect usage of this field:
/// - site-wide CSS files (should be `link`ed by templates)
/// - RSS feeds (should be generated by defining `alternative` pages)
///
/// Examples of correct usage of this field:
/// - `favicon.ico` and other similar assets auto-discovered by browsers
/// - Font files only referenced inside of CSS files.
static_assets: []bytes,
/// A list of locales of this website.
///
/// For each entry the following values must be unique:
/// - `code`
/// - `output_prefix_override` (if not null) + `host_url_override`
locales: []Locale,
/// When enabled, Zine will automatically add 'width' and 'height'
/// attributes to <img> elements for local assets.
/// Be aware that setting 'width' and 'heigth' of an image will in some
/// circumstances cause browsers to distort images.
///
/// This problem can be solved by adding the following CSS code to your
/// site:
///
/// img { height: auto; }
///
image_size_attributes: ?bool,
struct Locale {
/// A language-NATION code, e.g. 'en-US', used to identify each
/// individual localized variant of the website.
///
/// Must be unique.
code: bytes,
/// A name that identifies this locale, e.g. 'English'
name: bytes,
/// Content dir for this locale,
content_dir_path: bytes,
/// Site title for this locale.
site_title: bytes,
/// Set to a non-null value when deploying this locale from a dedicated
/// host (e.g. 'https://us.site.com', 'http://de.site.com').
///
/// It must not contain a path other than '/'.
host_url_override: ?bytes,
/// | output_ | host_ | resulting | resulting |
/// | prefix_ | url_ | url | path |
/// | override | override | prefix | prefix |
/// | -------- | ------------- | ---------------- | --------------- |
/// | null | null | site.com/en-US/ | zig-out/en-US/ |
/// | null | "us.site.com" | us.site.com/ | zig-out/en-US/ |
/// | "foo" | null | site.com/foo/ | zig-out/foo/ |
/// | "foo" | "us.site.com" | us.site.com/foo/ | zig-out/foo/ |
/// | "" | null | site.com/ | zig-out/ |
///
/// The last case is how you create a default locale.
output_prefix_override: ?bytes,
}
}
}
File formats used by Zine
Zine uses SuperMD for content and SuperHTML for defining layouts.
- File extensions:
- SuperMD:
.smd - SuperHTML:
.shtml
- SuperMD:
Each file format has a dedicated page in the docs section.
The Content Directory
The content directory contains your SuperMD files and their structure will be reflected verbatim in the final site.
content/index.smd
is the main index page of your website- (i.e.
https://site.com/).
- (i.e.
content/about.smd
will generate/about/index.html- (i.e.
https://site.com/about/)
- (i.e.
content/foo/index.smd
will generate/foo/index.html- (i.e.
https://site.com/foo/)
- (i.e.
content/foo/bar.smd
will generate/foo/bar/index.html- (i.e.
https://site.com/foo/bar/)
- (i.e.
Note that SSGs rely heavily on the implication that webservers will automatically serve index.html when a directory (usually indicated by a final / in the URL) is requested.
Site Sections
Consider the following content structure:
content
├── about
│ └── contacts.smd
├── about.smd
├── blog
│ ├── a.smd
│ ├── b.smd
│ └── index.smd
└── index.smd
In Zine every index.smd file denotes a section.
The main use for sections is to group pages together for the purpose of defining content lists, like listing all posts in a blog section, for example.
In this example there are 2 sections:
The first one is the “root” section, defined by content/index.smd, containing:
about/contacts.smdabout.smdblog/index.smd
The second section is the “blog” section, defined by content/blog/index.smd, containing:
blog/a.smdblog/b.smd
Note how an index.smd file defines a section but as a page it is not included in the list of pages that belong to that same section.
Lastly, note how pages in a section don’t all share the same basepath. In the absence of a nested index.smd, all pages are considered siblings regardless of how deeply they are nested inside of different directories.
Frontmatter
Each SuperMD file must start with a Ziggy frontmatter delimited by --- (three dashes in a row).
Ziggy is a data serialization language that extends JSON with new useful constructs. You can learn more on the official Ziggy website: https://ziggy-lang.io.
index.smd
---
.title = "Homepage",
.date = .date("2020-07-06T00:00:00"),
.layout = "home.shtml",
---
Your **SuperMD** content goes here.
Some fields, like title, date, and layout are mandatory, while others have default values. You can find a complete list of frontmatter fields in the SuperMD documentation page.
A very important required field is layout. This field must point at the layout that you want to use to style your content. In Zine this field must always be explicitly filled out and there is no implicit naming convention (like in Hugo, for example).
The Layouts Directory
Layouts are used to style your content files. You can learn more about layouts in the SuperHTML Basics section.
The Assets Directory
This directory collects various assets (images, css files, etc) necessary to build the website. Note that unlike other mainstream static site generators, Zine doesn’t have a “static” asset folder as all assets are managed explicitly through Zine’s Asset System.
First Steps
The quickest way to start a Zine site is to run zine init, which will generate a sample site for you.
In this section we will instead go through the motions of bootstrapping a Zine website entirely from scratch.
First of all you need to create a zine.ziggy file and place it in an empty directory. The zine.ziggy file must be structured like the one at the top of this page.
Once that’s done, let’s create a homepage and a relative template.
shell
$ touch content/index.smd
$ touch layouts/home.shtml
content/index.smd
---
.title = "Home",
.date = .date("2020-07-06T00:00:00"),
.layout = "home.shtml",
---
Hello World!
layouts/home.shtml
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title :text="$site.title"></title>
</head>
<body>
<h1 :text="$page.title"></h1>
<div :html="$page.content()"></div>
</body>
</html>
public/index.shtml (output)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample Site</title>
</head>
<body>
<h1>Home</h1>
<div><p>Hello World!</p></div>
</body>
</html>
In this example we just saw:
index.smddefines that its layout ishome.shtmlhome.shtmldefines some HTML and uses special attributes to pull in the contents ofindex.smd- the final output is going to be the homepage of our website
If you start the dev server now by running zine, you should see the output page at http://localhost:1990/.
Adding more pages
Let’s imagine now that we want to add a blog section to our website with a first post in it.
shell
$ mkdir content/blog
$ touch content/blog/first-post.smd
$ touch layouts/post.shtml
content/blog/first-post.smd
---
.title = "First Post!",
.date = @date("2020-07-06T00:00:00"),
.layout = "post.shtml",
.authors = ["Loris Cro"],
---
This is my first post!
layouts/post.shtml
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title :text="$site.title"></title>
</head>
<body>
<h1>Blog</h1>
<h2 :text="$page.title"></h2>
<h3 :if="$page.authors.first?()">
by <span :text="$if"></span>
</h3>
<h4>
Posted on:
<span
:text="$page.date.format('January 02, 2006')"
></span>
</h4>
<div :html="$page.content()"></div>
</body>
</html>
At this point you should be able to navigate to http://localhost:1990/blog/first-post/ and see the result.
Note how we needed a new layout (post.shtml) since the blog post is not going to have the same structure as the homepage.
While the structure is indeed different, both post.shtml and home.shtml share a lot of common boilerplate that can be collected into a single template by leveraging SuperHTML template extension features.
You can learn more about extending templates in the docs section dedicated to SuperHTML.
Multilingual sites
In this page we showed you the structure of a simple Zine website, but it’s also possible to use Zine to generate multilingual websites.
You can learn more about that in the Multilingual (i18n) section.