Zine
Devlog
About
This is a non-exhaustive, curated list of changes meant to help users quickly see what has improved since they last checked.
You can subscribe to this page via RSS.
UPGRADING SAFELY
Upgrading Zine can change how your website is rendered because of bugs or breaking changes. Reading the changelog helps you prepare, but to fully ascertain that your website looks as expected, I recommend following this procedure:
zine release -o old(with old Zine)zine release -o new(with new Zine)diff -ru new old
At this point is up to you to decide if any change in the diff is desireable or not. If you see a change that is not mentioned in the changelog, it might be the result of a bug, in which case a reproducible bug report would be appreciated.
More examples of useful diffing commands
Normal diffing is line based, but occasionally you will want to find more easily which word has changed within a line.
This command highlights individual words:
git diff --word-diff=color --no-index new old
This command highlights individual characters, useful for quickly detecting whitespace changes:
git diff --no-index --word-diff-regex=. new old
v0.13.0
2026-07-19
This release contains schema changes, make sure to run zine install-schemas after upgrading.
Breaking changes
The config file
auto_target_blanksetting has moved under the newsupermdfield, as there are now more supermd rendering settings you can tweak.Before:
.zine_version = "0.12.0", .auto_target_blank = true, // ...Now:
.zine_version = "0.12.0", .supermd = .{ .auto_target_blank = true, } // ...The fontmatter
skip_subdirsetting has been deleted (it never did anything).Starting now, SuperMD documents don’t allow heading level skips anymore. If you have a ‘#’ heading followed by ‘###’, you will now get a build error. Releated to this, SuperMD documents now must start with heading level 1 (’#’). If you were deliberately starting at ‘##’ to render
<h2>headings, the aforementionedsupermdfield in ‘zine.ziggy’ hasheadings_h2which, when set totrue, will render level 1 headings ash2and so forth. Note that sectioned content ($section) is exempt from this check for now.My recommendation for upgrading is to first understand if you were defaulting to starting each document from ‘##’. If so, then you probably want to set
headings_h2in ‘zine.ziggy’ and you will want to shave off one heading level from all your files. Here’s a one liner that can help with that (make sure to have commited all pending changes before running it!):Linux & Mac:
find . -name "*.smd" -type f -exec perl -pi -e 's/^#//' {} +Windows:
Get-ChildItem -Recurse -Filter *.smd | % { $p=$_.FullName; $c=[IO.File]::ReadAllText($p,[Text.Encoding]::UTF8); [IO.File]::WriteAllText($p,($c -replace '(?m)^#',''),[Text.Encoding]::UTF8) }After running the command, make sure to audit the resulting diff for errors.
For everybody else nothing should change, except maybe discovering that you occasionally skipped a heading level by mistake. I found a few instances of that when upgrading my own websites.
New features!
SuperMD errors have greatly improved. Techatrix started the work in #239 and coincidentally soon after I pushed the work further because I wanted the new “skipped heading level” errors to not be confusing.
In SuperHTML,
$build.developmentis true when the build is being served by the Zine live serverIn SuperHTML,
$site.index()returns the root index page (aka the homepage) of a site. It’s the same as$site.page('')but it’s clearer for a reader what your intent was.You can now specify
forbid_subsectionsin the frontmatter of aindex.smdfile. When set to true, no child page can use thefoo/index.smdnaming style as that would define a subsection. Users will be prompted to name the filefoo.smd. This setting is useful to guarantee that$page.leaves()doesn’t skip pages that were meant to be leaves.
Bugfixes
- Last release announced
$site.custombut a bug prevented scripty from being able to access that data, sorry! auto_target_blankwas a bit too eager and ended up adding the attirbute to all links when set totrue.- SuperHTML would crash when trying to put
:textinto a self-closing XML element. - The live development server would fail to serve the livereloading JS and websocket endpoints when a
url_path_prefixwas defined. - Using Zine from a
build.zigwould cause Zine to try to parse your Git tags, this was caused by a piece of build functionality that helped me remember to properly create tags and update my zon file when releasing Zine. It’s now disabled when using Zine as a dependency. - A bug prevented
[](#foo)refs to be validated, you might find new broken deep links after this update.
new-forum
2026-07-09
To give more space to requests for help, and to start disentangling Zine discussion from Discord, I created a new Discourse forum that I invite Zine users to join both to offer and receive help using Zine, and to discuss static sites in general.
v0.12.0
2026-06-29
This release contains a few new features and one big breaking change.
The Zine server now works with Firefox
I finally tracked down and solved the issue that would cause Firefox to stop loading pages from the development webserver if you “clicked around” too much. Turns out I forgot to flush, and Firefox forgot to kill half-upgraded websocket connections on page reload (firefox bug report).
You can now add directories to static assets
Tired of having to list all your fonts one by one in static_assets?
You can now put them in a fonts directory and just add that to your config file. Or you could just create assets/static and use that as a convention that is not limited to just fonts.
I’m also pretty proud of my implementation of this feature. Without some care it’s easy to make this feature computationally more hungry than it needs to be.
Also note that Zine will complain if the same file ends up being included twice (either directly or via directory inclusion). This is done to make sure you don’t end up in the situation where you think you removed a file from static assets, but in reality it’s still being installed because of a second reference.
The Zine config file now supports custom fields
The new zine.ziggy definition allows you to specify a .custom Ziggy Dictionary that you can access via $site.custom. See below in the upgrade guide section how that looks like.
Codeberg Pages
The docs now contain a page dedicated to deploying to Codeberg Pages, for those who might be interested.
Setup Zine Version 2
The GitHub / Codeberg kristoff-it/setup-zine@v1 action required you to specify which version of Zine to fetch, but the new Ziggy config file features a zine_version field that should now be considered the authoritative source of truth for which version of Zine your website requires.
Update your CI scripts to use kristoff-it/setup-zine@v2 and remove the input setting to enjoy a more seamless experience.
Note that now Zine will complain about versions not matching between the website config file and the Zine executable version.
SuperHTML Scripty changes!
Added
$page.subpagesByAuthor()and$page.subpagesByTag(), which will allow you to, well, find all subpages of a given section that have the requested author and tag, respectively. It was already possible to manually iterate subpages and filter them on this property, but the builtin functions can consult an index, offering better performance. The -byAuthorvariant of these functions relies on a Ziggy frontmatter change explained in the frontmatter upgrade section further ahead in this changelog entry.Added
$page.leaves()which returns a (flat) list of non-section pages in the subtree of the target section page. It accepts an optional argument to limit the length of the iteration. This function is useful to show the latest entries in a structured section.Removed the ability to pass arguments to
$site.pages(), which allowed you to iterate over a list of known pages. It was originally created to simplify running the same html snippet over a list of manually selected pages, but it’s fully replaceable by looping over a Ziggy array defined in the frontmatter.Old:
<ul :loop="$site.pages('foo', 'bar')"> <li :text="$loop.it.title"></li> </ul>New:
<ul :loop="$page.custom.get('menu-entries')"> <li :text="$site.page($loop.it).title"></li> </ul>You can now pass multiple arguments to
$site.page, which will become components to compose the final path used to find the target page. This means that the following invocations are equivalent:$site.page('foo/bar/baz')$site.page('foo', 'bar/baz')
This can be useful when part of your path is fixed while part is parametrized:
$site.page('speakers', $page.authors.at(0))The SuperHTML Scripty reference now uses slightly different syntax to refer to arrays. Previously
[String]was notation for an array of strings, while now it’s[]String, in order to more closely mirror the new Ziggy Schema syntax.Variadic function arguments are still specified as
[String...].
New Ziggy Upgrade Guide
Ziggy is the data serialization language used in the Zine config file (zine.ziggy) and the frontmatter in SuperMD files. Up until recently it was probably the weakest point of Zine but, after giving it some love, I’m happy to say that this is not the case anymore. Unfortunately this comes at the cost of some breaking changes, for which I do apologize. I tried to put all breaking changes that I could think of in this release, in order to leave users alone for a while once they’re done upgrading.
Some Ziggy syntax changed, but most importantly both parser and surrounding tooling has matured a lot which enables pushing past previous cul de sacs.
Better error messages
If you ever wrote a bad date, you probably saw this misleading error message:
content/index.smd:3:9:
.date = @date("1990-01-1T00:00:00"),
^
syntax error: '@'
This is now:
content/index.smd:3:9: error: unable to parse date: InvalidCharacter
| .date = .date("1990-01-1T00:00:00"),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Unfortunately Zine is not yet able to tell you that there’s a missing digit in the day section of the date, but it’s on my pile of TODOs to eventually take direct control over date parsing and provide even better diagnostics.
In-editor schema intelligence!
Looking at the usual suspects (other SSGs), I believe this is currently a unique feature of Zine. To unlock this ability:
- Your editor must be configured to launch the Ziggy LSP for
.ziggy,.ziggy-schemaand.smdfiles. In the future SuperMD will have a dedicated LSP but for now the Ziggy one will offer frontmatter support. - You must have the correct Ziggy Schema files installed in your Zine website. Those files contain the schema definition for the Zine config file (
zine.ziggy) and SuperMD frontmatter. Usezine install-schemasto have Zine automatically place up-to-date copies of those files at the right locations (alsozine initwill now generate a project with those files already in place). Note thatinstall-schemasrequires you to update yourzine.ziggyconfig file first.
It’s not pictured in the video, but zine.ziggy also has in-editor support now. The Ziggy LSP is not yet at the same level of polish of, say, the SuperHTML one, but it will be improved over time.
Upgrading your website
Ziggy Document syntax changed slightly, checkout the official docs for the full list of changes. The ones that Zine users care the most about are the following two:
@fooliterals are gone.- Structs don’t have names anymore.
- Both of the above have been functionally replaced by the newly introduced tagged unions.
- Structs now have a leading dot like in Zig, so
{ .foo = true }becomes.{ .foo = true }.
Lastly be aware that you can omit every struct field that in their Ziggy Schema definition is:
- an optional (leading
?in the type expression) - a slice (leading
[]in the type expression) - a dictionary (leading
{:}in the type expression)
Upgrading your zine.ziggy
Before:
Site {
// ...
}
After:
.zine_version = "0.12.0",
.site = .simple(.{ // <-- note the dot!
// ...
}),
The contents of .simple should remain the same as they were before.
If you have a multilingual website:
.zine_version = "0.12.0",
.site = .multilingual(.{ // <-- note the dot!
// ...
}),
In this case as well the contents of .simple should be the structurally the same but beware that nested structs (e.g. Locales), will now need a leading dot.
As a reminder, you can now also define custom properties in your config file, which become accessible via $site.custom:
.zine_version = "0.12.0",
.site = .simple(.{ // <-- note the dot!
// ...
}),
.custom = { // <-- dicts have no dot!
"my": ["custom", "values!"],
}
After fixing your zine.ziggy, it’s a good idea to run zine install-schemas.
BREAKING CHANGE
The new Zine config file schema has a new setting: auto_target_blank which defines wheder external links generated by SuperMD should have a target blank attribute by default or not.
Zine previously defaulted to adding these attributes automatically while now they are turned off by default. Define the setting to true if you want to preserve the previous behavior.
Note that you can always override this setting for each link by using explicit Scripty syntax when defining the link:
[my link]($link.url(https://example.com).new(true))
Upgrading your frontmatters
@datehas been replaced by.dateand.unix(which accepts a unix timestamp instead of a string).authorhas been replaced by.authorswhich can be omitted and accepts a list of strings- Just like for the config file, structs now need a leading dot, which will affect you if:
- did not use braceless top-level syntax for your frontmatter
- define
alternativesas each is a struct - used struct syntax inside of your
customdata dictionary
- Some previously mandatory fields have now become optional, see the frontmatter Ziggy Schema for more info (it’s also present in the SuperMD section of the Zine docs).
In short, this is what it looks like.
Before:
---
.title = "Home",
.date = @date("2018-07-06T00:00:00"),
.description = "",
.author = "Loris Cro",
.layout = "index.shtml",
.alternatives = [{
.name = "rss",
.layout = "rss.xml",
.output = "/index.xml",
}],
---
After:
---
.title = "Home",
.date = .date("2018-07-06T00:00:00"),
.layout = "index.shtml",
.alternatives = [.{ // <-- note the dot!
.name = "rss",
.layout = "rss.xml",
.output = "/index.xml",
}],
---
For the .author field my recommendation is to hardcode the author name in the template for personal websites where the author is essentially always the same person. The new .authors field accepts a list of strings for cases where your have more complex needs.
In case that you do have more complex author management needs, don’t forget that you can use entries in the .authors field not only as “directly printable” names, but also as keys into page navigation (e.g. each author has a dedicated page) or a .custom field defined somewhere, maybe in $site.custom, for example.
v0.11.3
2026-06-17
This release upgrades to Zig 0.17.0-dev all the way up from 0.15.0. I did not make a release for Zig 0.16.0 because of regressions in translate-c, which have now been fixed.
This release contains relatively few and small user-facing improvements and its main purpose is to isolate potential bugs that come from the Zig upgrade process from upcoming changes to Zine.
In the next version of Zine I will update Ziggy (the .smd frontmatter language) to a new version that changes some of the syntax, requiring you to do some work before you can use it.
Bugfixes & Breaking Changes
- Only emit
idattributes from SuperMD when it has a set value. (#198, thanks GasInfinity!). - Fixed a bug where an empty
index.smd(i.e. no content, no frontmatter) would be partially picked up by Zine, creating a buggy section, which would then crash Zine under certain circumstances. - Fixed a bug where toggling a
.draftfield fromfalsetotruewould cause the Zine dev server to crash.
New features
- In SuperMD, you can now specify audio embeds, like so:
[](<$audio.asset('music.wav')>)(#195, thanks PowerUser64!). - In SuperHTML, added
.minus()to integers (#188, thanks acc1729!). - Releases now include a FreeBSD tarball (#202, thanks dmarker!).
- Zig build system integration has been updated to Zig 0.17.0-dev (see the Zon file for more information).
v0.11.2
2025-11-21
This release fixes some bugs that were discovered since the last and should not require any change for existing websites.
The next release will merge a new version of Ziggy (the data language used in SuperMD frontmatters) that instead will require you to change your content files, so brace for upcoming impact :^) In exchange though, the Ziggy language has improved a lot and you will finally be able to get great code intelligence, which will help you get your frontmatters right more easily. More on that in the next release.
Fixes:
- Fixed rendering of links to alternatives when absolute paths are involved.
zine inittemplate files were created back before superhtml had good autoformatting support, resulting in a bunch of code moving around when you saved the file for the first time (using a new build of superhtml w/ more refined autoformatting behavior). Now the template files embedded in Zine have been formatted, giving you a better out-of-the-box experience.- Linking to the site homepage from SuperMD was impossible because of a bug and because of a missing feature. You can now use either vanilla syntax (
[home](/)) or explicit Scripty syntax ($link.site(), just added in this release). - Zig build system integration: the serve step now correctly uses a prebuilt zine if you specify the
pathoption, previously it would ignore the setting and default to building Zine from scratch always.
v0.11.1
2025-07-26
Another round of bug fixes, with this release all known bugs have been solved!
- The
releaseandservecommands dealt with some asset paths in an inconsistent manner, resulting in the situation where an asset path would work with only one of them at a time, depending on whether it was absolute or relative. Now all paths are normalized before being used. - An absolute install path given to a static asset would be interpreted as a absolute path by
release, resulting in an attempt to install assets outside of the output directory which is, generally speaking, not optimal. We now normalize paths to ensure we don’t try to override your/etc/shadow(or~/.ssh/id_rsa) by mistake :^) - Fixed some invalid memory accesses when deiniting a website build. These bugs shouldn’t have impacted the user experience and were in fact caught by CI.
v0.11.0
2025-07-24
This release brings minor backwards compatibility breakage and a few good bug fixes.
Breaking change
Zine will now refuse to output a website release to a non-empty directory. This solves the problem of users being surprised that old files are somehow still “being rendered” when they’re actually not, and this also seems a better choice than defaulting to emtpying the output dir, as that could have catastrophic consequences.
You can use --force (or -f) to ignore the presence of other files.
Related to this change --install has been renamed to --output since Zine now refuses to “install” releases.
Bugfixes
- Added missing check to
nextPage?,prevPage?,hasNext,hasPrevwhen the target page is the root index. The Zine section model defines the index page of a section to not be part of the section, consequently the root index cannot have any sibling. A proper error will be reported now. - Ziggy parsing code failed to report syntax errors in string escapes, resulting in the confusing situation where an error is being reported but the list of diagnostics is empty. This is now reported correctly.
- SuperMD would mistake
(i.e. remote url in vanilla image syntax) for a local asset. This has now been fixed.
v0.10.4
2025-07-16
This release upgrades Zine to Zig 0.15.0-dev (post Writergate) and fixes two silly bugs about build assets:
The Zine live server will now correctly serve build assets (#162, thanks llyrical-island!).
Up until now Zine failed to install all build assets when making a site release. Turns out that we were missing the required code entirely, whoops!
As an upgrade suggestion, if you have a website that uses build.zig and you don’t want to upgrade your own code to Zig 0.15.0-dev (post Writergate), then consider using in your build.zig .zine = .{ .path = null } (it’s a field of zine.Options), which will allow you to use a system-provided build of Zine.
Of course that won’t solve potential CI issues, but you will at least be able to try it out locally. If you’re in that situation, do try to upgrade your code it’s going to be worth it, especially in preparation for the upcoming async I/O redesign coming soon to Zig!
v0.10.3
2025-07-07
After hiatus of a few months, I’m resuming development of Zine. This release contains bug fixes and minor improvements.
This new Zine development season will focus on two main new big features:
- SuperHTML snippets, so the ability to create reusable components
- LSP support for Scripty
New changes:
v0.10.2
2025-04-22
- SuperHTML:
- Fixed a crash when using
$page.alternative(), in the refactoting to standalone I forgot to wire back some state management, resulting in a reliable crash when using that function. Sorry! Now it’s fixed though. - DateTime: introduced the ability to add and subtract time durations to a DateTime.
- Fixed a crash when using
- SuperMD:
- Fixed a crash when providing a URL to the root path in some directives.
- Zine Development Server:
- The hot reload script injected to your pages now has a
deferattribute which helps simplify local performance testing of your website. - Fixed websocket disconnections when the client sent long headers, particularly felt on Windows ((#139) thanks Scott!).
- The hot reload script injected to your pages now has a
- Added nix flake ((#143) thanks sreehax!)
v0.10.1
2025-04-10
SuperMD:
- Added support for
$image.size()
(specify image size manually) - Added support for fragments in Markdown vanilla link syntax. Now
[](/my/page/#my-ref)will correctly translate to$link.page('my/page').ref('my-ref')
- Added support for
SuperHTML:
- Added support for
Date.in()
(change tz of a date based on a location string) - Added support for
String.startsWith()
(#137, thanks illfygli!)
- Added support for
Bugfixes:
- Fixed one memory corruption bug that could cause Zine to crash on startup (fc0b117).
- Fixed some path manipulation inconsistencies on Windows, now CI is green also for the greatest OS ever made.
- The help menu wrongly suggested to use
--outputto specify an install path, while the correct string is--install. - Zine checks for output path collisions but a bug in how page aliases were handled in the code could cause false positives.
v0.10.0
2025-04-08
Standalone Zine is here!
It’s finally time to release the standalone version of Zine:
- it’s stupid fast
- it’s more strict than before
- it removed almost all limitations (e.g. rendering other pages via scripty)
- it might have added new bugs as many things were reimplemented, sorry!
Download a prebuilt version to try it out.
WARNING
We have updated the suggested way of deploying to GitHub Pages, and for all GitHub based deployments you will want to check out kristoff-it/setup-zine.
If you’re a new user of Zine, I highly recommend you start by running zine init in an empty folder to get a sample website bootstrapped for you by Zine.
For existing users, here’s the upgrade guide after you have downloaded Zine and put it in PATH:
Create a
zine.ziggyfile in your website’s root directory. Every option your original build.zig can be ported over exceptbuild_assets(but build.zig integration still exists if you need that), here’s an example:Site { .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 = [ // omitted for brevity ], }Run
zineto activate the development server and watch the errors that it reports. Zine has become a bit more strict when it comes to paths and it might complain about absolute paths in locations where it previously accepted them. Most errors should be straight forward to fix. The only breaking change that will silently cause issues is that previouslyaliasesandalternativesoutput paths were always relative to the website root, while now a relative path will place the alias / alternative in a subdir of the current page if the output path provided is relative.If your website is complex enough, you might want to diff the new version against the old. Assuming you have a build in
zig-out/or a similar location, you can run these commands to get a diff:$ zine release $ git diff --word-diff=color --no-index zig-out publicThe first command will output your website to
public/which then you can diff against an older build of your website. You should not expect the diff to be perfectly empty, as some minor rendering details have changed, but all the changes should be positive once you inspect them. If you see a change that seems wrong, please open an issue with a reproducer. We don’t need the full diff, just the sources to produce the new output and an indication of what is wrong (and why you think the output is wrong).
This release adds a ton of improvements to Zine, here are just a couple highlights for brevity:
- The official docs have been rewritten to reflect the changes, and most importantly I’ve finally added navigation to the Scripty references. Go take a look, they’re pretty cool in my opinion :^)
- Added support for math formulas in SuperMD via
$mathtex. Unfortunately we depend on JavaScript for runtime rendering, but eventually we will have our own parser to do everything at build time. - TreeSitter highlighting now captures more classes for tokens thanks to Willians Faria (#131)
v0.9.0
2025-03-11
NEW ZINE
At some point after the last release I started working on a standalone version of Zine that doesn’t depend on the Zig build system anymore, meaning that you will be able to download a zine executable and run zine serve without even needing to have Zig installed. I will write more on it once the work is done, but you should expect plenty of improvement with no downsides as it will still be optionally possible to integrate with the Zig build system as needed. ETA is a few weeks from now.
THIS MEANS that this is the last big release of Zine in its current form. Unless current users of Zine report regressions, I will focus entirely on the new standalone version.
Let’s start with the BREAKING CHANGES:
Zine now depends on Zig 0.14.0, which means that you will also need to change your website’s
build.zig.zon. Starting from the next version of Zine this will not be necessary anymore as you will only need azine.ziggyfile to define your website’s configuration. That said, here are the changes you will need to make to yourbuild.zig.zonfor this version of Zine after upgrading to Zig 0.14.0:- Change
namefrom a string to a Zig enum, like so:.name = .site. Note that this field is required by Zig but not used by Zine so any valid value is fine. - Run
zig buildafter fixing the name field and watch the compiler report a new error. This error will tell you that you need to add afingerprintfield. What’s important is that it will also suggest a value to give to the new field. Copy it and paste it into your Zon file (right belowversionis a good place where to put it) like so:.fingerprint = 0xAAAAAAAAAA(where0xAAAAAAAAAAis the value suggested by Zig).
- Change
$page.prevPage()and$page.nextPage()have been renamed to$page.prevPage?()and$page.nextPage?()respectively. The policy in Zine is to have all builtin functions that return anOptionalto end with a question mark.
Other new features in Zine:
Zine is now MIT licensed. It was actually intended to be that from the start but I forgot to add the file. Consider this a reverse rugpull where from proprietary we went to open source. A rug has been pushed under your feet, in a sense.
Zine can now render footnotes in SuperMD files! Note that you will need to change your SuperHTML templates to explicitly print footnotes (if present). See
$page.footnotes?()for more info. This change was contributed by Brandon in #90.Introduced opt-in automated image size attributes for local images! When enabled, this feature will make Zine add automatically
heightandwidthattributes to image assets embedded in SuperMD files. Doing so will mean that the browser will be able to know the size of all images even before downloading them, completely removing the need for reflowing the page, and overall giving the impression of a much faster page load. See this section for more info on how to enable it. This feature was contributed in #88, thank you pfg!NOTE
If you were using main branch Zine, this might be perceived as a breaking change because this feature was enabled by default when first added (but it was never part of a full release so it’s not listed under breaking changes).
Added
Array.first?()andArray.last?(), which will return the first and last item in an array, respectively, ornullif the array is empty. This can be useful to implement advanced prev-next page navigation that crosses site section boundaries, as explained here.Added in SuperMD
$link.alternative, which allows linking to an alternative version specified in the target page (by using thealternativesfrontmatter field).The Zine dev server now implements debouncing in order to avoid issuing a flurry of rebuilds when an editor splits a file save operation into multiple file system level events. The dev server has a new related CLI flag, run
zig build helpfor more info.New version of flow-syntax by neurocyte brings syntax highlighting support for:
- Gleam
- CMake (to make it easier for you to port CMake to build.zig using flow)
- Astro (not sure what that’s used for, probably nothing interesting)
Here are the bugfixes:
Git information parsing would error out when a repo only had one commit in it. This has been fixed in #85, thank you Brandon!
The Zig stdlib has a bug that causes the webserver to deadlock on Windows, Zine has a temporary workaround in it but I had left a mistake in the code that was fixed in #89, thank you again Brandon!
Added support for query parameters in URLs in Zine’s dev server (#87), thank you Techatrix!
Zine now uses a new version of Zeit by rockorarger which fixes a couple minor bugs in date time handling (#91).
Links generated for page
alternativeswould erroneously add a trailing slash, fixed in (#103) by Anthon, thanks!Fixed hot-reloading support in the Zine dev server on Windows in (#116), thanks MiXiPi!
Previously, defining a page alias that contained new directories would cause an error when outputting the website. This has been fixed in (#122), thanks deevus!
v0.8.0
2024-10-28
- Page alternatives now require you to set the
namefield. You will get a build error if the name is not defined. It would previously default to an empty string, but now it’s mandatory because of the newly added builtins. - SuperMD
- Added some error checking to SuperMD builtins that relate to links, like trying to use
$link.refin combination with$link.url. $link.alternative('rss')to link to a page alternative
- Added some error checking to SuperMD builtins that relate to links, like trying to use
- SuperHTML
- Fixed documentation for
linkRef, which wrongly reported it did not accept any argument. - Added
$page.alternative('rss')which allows you to obtain anAlternativevalue by name (instead of looping over all$page.alternatives). - Added
link()toAlternativeso you can now link to RSS feeds like so:
(the Frequently Asked Questions section was updated accordingly)<ctx alt="$page.alternative('rss')"> <a href="$ctx.alt.link()" type="$ctx.alt.type"> RSS feed </a> </ctx>
- Fixed documentation for
v0.7.2
2024-10-25
Windows should now work well. A path-related bug that caused assets to not be detected correctly has now been fixed, and a second bug that caused the webserver to fail to update when a new change happened has also been “fixed” (it’s actually an annoying networking bug unrelated to Zine, so we implemented a workaround). In any case, Windows should now be a first-class citizen for Zine so please consider rawdogging Zine on Windows instead of using Docker/WSL (also report any issue you find!).
In the previous release I added by mistake some debug logs that could not be silenced and that caused the webserver to show your the error window even when no error had happened, this is now fixed, sorry!
Creating duplicate ids inside of a content file is now properly reported as an error by Zine. Some ids would also not be correctly recorded (for the purpose of deeplinking) before, and that too has been fixed now.
New SuperHTML Scripty builtins:
$page.linkRef('foo')can be used to deep-link from SuperHTML templatessriHash()can be called on assets to generate a hash for Subresource Integrity, thanks Shardion (#73)!$build.git()can be used to access repo information when in a repository, see the SuperHTML Scripty reference for more information, thanks Sc3l3t0n (#77)!
v0.7.1
2024-10-16
When using the simplified
website()/multilingualWebsite()interface, you can now pass-Dinclude-draftsflag to enable rendering of content pages that have.draft = trueset in their frontmatter. Thank you nihklas (#70).New SuperHTML Scripty builtin functions:
$page.hasContentSection: You can now query a page to see if it contains a content section with a given id. Thanks nihklas (#76).$page.subpagesAlphabetic: lists sub pages sorted in alphabetic order. Unblocks rockorager’s recipe website. Recipe websites are good, make more of them.
New SuperMD Scripty directive:
$text, which can be used to give an id and attributes to a inline piece of text.
v0.7.0
2024-10-11
Introduced actual arrays in Zine. Previously we would immediately evaluate Scripty expressions into iterators, which made it awkward to implement bulitin functions such as
sort. Zine now has a real concept of Array with dedicated builtins, you can read more in the reference for[any]. Note that arrays don’t yet offer a sorting builtin (figuring out the interface to expose to the user has been left as work for a subsequent release), but they do support other basic stuff like slicing. I’m happy to work with users to implement functions that would unblock them, even if the design of the function will have to be changed in the future.Zine currently has a limitation that prevents it from correctly rendering (calling
content()on) pages that have been loaded through Scripty. Over time this limitation has been mitigated, but it’s not fully gone yet. Unfortunately the check that told you that this was the case was not working correctly, and has now been fixed. The most likely situation where you might encounter this problem is when trying to render the content of various pages in an RSS template. Previously it might have worked for simple cases and caused a panic in others, now you will get a consistent error message that tells you that the feature is not available yet.The webserver now properly parses percent encoding in URLs, used whenever files have funky names. Thank you GigaGrunch! (#65)
Paths with spaces were not handled correctly when tracking dependencies, causing some failures that might have looked like cache issues, now fixed thanks to brandondyck (#68)
v0.6.3
2024-09-19
v0.6.2
2024-09-17
$image.linked()is now implemented and allows you to quickly create an image that links to itself.
v0.6.1-vscode
2024-09-07
- The VSCode marketplace now has a SuperMD Extension that gives you syntax highlighting for SuperMD files (no LSP for it yet though, sorry!).
- Similarly, the VSCode SuperHTML Extension would not provide you with syntax highlighting before, but now it does.
v0.6.1
2024-09-07
- Updated
rockorager/zeit, which now includes full support for date formatting strings based on the Go magic date. Previously Zine hardcoded only a very small subset of possible formatting styles. See the reference docs forDate.formatfor more info.
v0.6.0
2024-09-06
Added
Page.parentSection()in SuperHTML.The website now has a docs section for editor support.
Follow those instructions to get syntax highlighting and LSP support in your favorite editor.
If you have trouble setting up your editor feel free to ask for help in the community.
v0.5.1
2024-09-04
Added typst syntax highlighting support.
On the topic of RSS feeds, this page now has an RSS feed that you can subscribe to. Every entry in this devlog will become an entry in the RSS feed. If you’re curious to see how this is implemented, clone this repo and see how
content/log.smdandlayouts/log.xmlwork together to make it happen (see Get Started for more info).Pagein SuperHTML gained the ability to loop over sections defined by a Page.SuperMD Directives can now store data (key-value fields) that can then be accessed programmtically by SuperHTML.
v0.5.0
2024-09-01
Added
$build.generatedwhich evaluates to the date in which the build is taking place. It doesn’t add a dependency on the current date so a page will only display a newer date if it actually gets regenerated.Useful for example to set
lastBuildDatein RSS feeds. Note that without caching the date will always be updated, potentially resulting in unwanted behavior.
2024-08-30 (later in the day)
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.7"
- Fix an off by one error that gave the wrong value to
Iterator.last
2024-08-30 (later in the day)
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.6"
- Fix a bug caused page-local assets to be installed in the wrong place
2024-08-30 (later in the day)
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.5"
- Fix off-by-one error in some iterators (eg in
$page.tags)
2024-08-30 (later in the day)
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.4"
- Introduced support for captions directly in Markdown link / image syntax. See the updated SuperMD docs for more details.
- Fixed a bug that prevented users from accessing
$ifin the intended way. - Some SuperMD Scripty reference docs were improved.
2024-08-30 (later in the day)
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.3"
Fixes a printing bug related to Int values.
2024-08-30
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.2"
Fixes a bug that prevented users from accessing $loop in the intended way.
2024-08-29 (later in the day)
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.1"
Fixes a silly but annoyng bug where SuperMD links nested inside of styiling elements would not get analyzed.
2024-08-29
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.0"
The SuperMD update!
With this release Zine is now at a point where the development experience is representative of what the final product will look like.
Error messages need to be improved and a lot of developer tooling is still missing, so take that into account, but the core authoring process is now mostly there.
Zine is also now featureful enough to begin the porting process for the official Zig website!
Note
This is going to be the last hugely breaking update to Zine in a while, so it’s a good moment to try Zine out if the fear of breaking changes has prevented you from attempting before.
Breaking changes will still keep coming, don’t worry, but for a while they will be almost exclusively minor changes that will require little work on your part.
Relatedly, I will be at SquiggleConf this October to talk about Zine and related work.
Now onto new features and breaking changes:
This website has been updated with a lot of new documentation. The docs section will now be able to guide you from getting the first few steps of creating a zine website, all the way to learning more advanced concepts.
The Scripty reference page that was very incomplete in the past has been improved heavily. Additionally, now there are two references: one for SuperMD and one for SuperHTML.
SuperMD comes with a ton of new features, too many to fully list here, but here are some highlights:
A page can now define content sections that can be rendered separately by the layout. This is huge, so check out the SuperMD Basics page for more information.
You can now give ids to headings and other elements to enable deep-linking (which will be also checked by Zine!), and you can also give classes for styling purposes.
Did you notice the “NOTE” block above? SuperMD has the concept of blocks, which make it super easy to include something like that in your content without needing to rely on inlined HTML.
So cool
These blocks can also be nested and styled!
Inlined HTML is forbidden now btw, but there’s an escape hatch for when you need to embed stuff like YouTube videos. See the SuperHTML docs page for more info.
Unfortunately we don’t have any tooling for SuperMD available just yet, but it’s high up in the list of priorities so stay tuned for updates in the near future.
Now that we have SuperMD, the file extension for content files has changed to
.smd.
If you need to update your Zine site and have a lot of markdown files, our friendly neighborhood LLM suggests to use this command to bulk rename all (run it inside your content dir):find . -type f -name "*.md" -exec bash -c 'mv "$0" "${0%.md}.smd"' {} \;The logic attributes in SuperHTML (
if,loop,var) have been renamed::if,:loopare still the same, they just gained a colon prefix:textand:htmlreplacevar. The first escapes HTML, while the second doesn’tinline-loopdoesn’t exist anymore because now we have<ctx>
Introducing
<ctx>: a special tag that allows you to create a phantom element that doesn’t render to anything. This element effectively allows you to implement whatinline-loopwas doing for you before, and more.Another cool feature of
<ctx>is that defining attributes on it makes them available as fields under$ctx. See the SuperHTML docs page for more info.
2024-08-03
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.3.0"
The Asset System update!
Zine has now an asset system with clearly defined semantics. Previously we kinda implemented informally what other static site generators offered (eg a static asset directory), while now Zine gained it’s own spin on assets
There’s a new Assets section in the docs, read it to learn how assets now work in Zine. The most notable changes are:
- Zine can now make use of artifacts generated via the Zig build system!
static_dir_pathhas becomeassets_dir_path(I also recommend renamingstatictoassets)- Page assets (eg images placed in the content directory next to the page they belong to) now have one extra rule for file placement, see the docs for more info on that (or let the error messages gently guide you)
The dev server now by default opens a
doorport to 1990 if you don’t specify-Dportoutput_prefixhas been renamed tooutput_path_prefixIn
MultilingualSite,variantshas been renamed tolocalized variantsScripty has gained a new
Assettype and handful of new builtins, including adding the ability to query for the currentlocale_codein a multilingual website ($site.localeCode())
2024-07-27
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.2.0"
The build system flexibility update (part 1)!
Upgrading from v0.1: in your
build.zigchangetry zine.addWebsitetozine.website.addWebsiteandaddMultilingualWebsitehave been renamed towebsiteandmultilingualWebsiterespectively (and now don’t return an error anymore)To align with
std.Build‘s naming convention (where ‘add’ functions create steps but don’t wire them to the default install step),addWebsiteandaddMultilingualWebsiteare now more low-level functions for defining build pipelines that allow you to customize more things:- You can have other build steps depend on the website being built
- You can specify if and how the development webserver is bound to a named step
- et cetera
To learn how to use this more fine-grained API, look at the implementation of
websiteandmultilingualWebsite.As suspected, in the last release I did not proprely wire the dependency on
zig-afl-kitas a lazy dependency and that caused build errors to some. This is now fixed in this release. Sorry!
The next item on the roadmap is to add an asset system to Zine in order to have the static content generation depend on other steps defined in your build script.
2024-07-26
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.1.3"
Scripty strings now have
eql,containsandendsWith. With a pinch of creativity you can create menus that style differently the current active page:<div class="$page.permalink().endsWith('/blog/').then('selected', '')"> Blog </div>Similarly, when printing out the list of pages in a section, you can filter-out occasional “miscellaneous” pages (as in belonging to the same section but unwanted in the current list):
<div loop="$page.subpages()"> <div if="$loop.it.permalink().contains('/blog/')"> <a href="$loop.it.permalink() var="$loop.it.title"></a> </div> </div>The SuperHTML CLI tool was updated with two new commands:
check: checks HTML and SuperHTML template files for syntax errors, useful if you don’t have / want LSP integration (see alsofmtin such case).interface: prints out the interface of a given SuperHTML template file, useful to bootstrap a new SuperHTML template that extends another.
Zine and some of its dependencies depend on kristoff-it/zig-afl-kit for fuzzing. I tried now to make it a lazy dependency so that users don’t have to download it just to use Zine (as it’s a dev-only dependency), but I haven’t yet been able to fully test if I did so correctly.
2024-07-25
zig fetch --save "git+https://github.com/kristoff-it/zine#v0.1.0"
Zine has finally reached a first tagged release!
A lot has happened in these 4 months, so read this changelog to learn how to upgrade your Zine website.
The first thing that changed is the recommended way of updating your build.zig.zon.
The second is that Zine now tracks the latest stable version of Zig, which is 0.13.0 at the moment of writing.
Now onto new features and breaking changes:
The recommended GitHub Actions Workflow files for building Zine websites on Github have changed. The new version uses the mlugg/setup-zig@v1 action that will automatically manage caching for you. It is highly recommended to update your scripts.
- While you’re at it, change
--summary allto--summary new, which will only list the pages that were rebuilt.
- While you’re at it, change
SuperHTML (Zine’s templating language) dropped Tree Sitter as its HTML parser in favor of a handcrafted implementation that more closely follows the HTML5 spec. This brings us significantly improved error messages and other advantages.
It’s highly recommended you install and configure SuperHTML as your language server for both HTML and SuperHTML Templates in order to get in-editor diagnostics and kickass autoformatting. The repo also offers a Tree Sitter grammar for SuperHTML that incudes a few visual improvements for tags and attributes that have semantic meaning.
SuperHTML also has a VSCode extension.
SuperHTML follows the HTML5 spec much more closely and, while writing an HTML parser from scratch for it, I learned that self-closing tags (tags with a final
/) are not a thing in HTML5, so now<extend>and<super>have been defined as void elements in SuperHTML and want no final slash nor closing tag.Note that SuperHTML will consider an error using self-closing tags in HTML (outside of a
<svg>scope).The correct file extension for templates is
.shtml. You must rename all your templates to the new file extension otherwise you will get an error from SuperHTML when it sees non-HTML compliant syntax, since<extend>and<super>are recognized as void elements only in SuperHTML template files.
Scripty has impoved as well: inside of nested loops, it is now possible to access outer
$loopvariables by doing$loop.up(). Each call toup()goes up one level.- This is thanks to the fact that interrupts were implemented in Scripty, opening the door to features that rely on the ability to pass from the outside values into scripty (
up()relies on that since loops are a SuperHTML concept that Scripty is completely unaware of).
- This is thanks to the fact that interrupts were implemented in Scripty, opening the door to features that rely on the ability to pass from the outside values into scripty (
For syntax highlighting, Zine uses a distribution of Tree Sitter that bundles a lot of grammars and highlighting queries from Flow Control. The dependency has now been updated to a new version that adds support for more languages.
That’s mostly it. If you encounter bugs while updating, please don’t hesitate to open a new issue on GitHub with a link to a reproduction.
2024-03-26
.url = "git+https://github.com/kristoff-it/zine#e33a1d79b09e8532db60347a7ec4bd3413888977",
.hash = "12209f9be74fcc805c0f086e4a81ccca041354448f5b3592e04b6a6d1b4a95da5a26",
- Added support for multilingual websites. See the corresponding docs page for more info. Because of this change now the
AddWebsiteOptionsstruct is slightly different, here’s how to fix it:- Take the contents of
siteand move them top level, renamebase_urltohost_url.
- Take the contents of
- Related-but-distinct from the above, you can now specify an output prefix for your static site. The feature was added primarily for i18n purposes but can also be used in simple websites to add an arbitrary prefix.
- The markdown renderer now renders tables!
- Fixed a crash in the dev server that would trigger when refreshing the page multiple times in quick succession (the crash was related to websockets). There’s still one remaining known bug related to this same problem though.
- The dev server now works on Windows (thanks Parzival-3141)
- New Scripty builtins:
- Strings
addPath()similar tosuffixbut knows when to add a/or not.fmt()replaces occurrences of{}in your strings with the provided string arguments.
- Maps, refined the
getfamily of functionsget(key, fallback)allows to get a key from a map and provide a fallback valueget!(key)errors out if the key doesn’t existget?(key)returns null if the value is missing, to be used in conjunction withifattributes.
- Strings
2024-03-21
.url = "git+https://github.com/kristoff-it/zine#ecc72eb042af07f5b4690a35a7ca1dd9c6fd5b61",
.hash = "1220610a18236cd32936502bd7e762743e89ef70408638675420e453be41f1e83de4",
- Changed datetime library to rockorager/zeit.
- If you put a
@date("...")literal in your custom fields, it will be recognized as a date by Zine. - A few improvements to bulitins:
get?(),get!()andget(): different ways of getting values out of Ziggy maps (i.e. custom fields).then()on booleans now gives you the ability to create if-then-else expressions.gton integers.lt,eq,gton dates.
2024-03-20
.url = "git+https://github.com/kristoff-it/zine#d06884ec657abe87ab4f408b5dc3f336a6dcea9b",
.hash = "1220d3bc95a5343918d69d3478f27ebb4abe14613c159737af64cd2185151efd2fa1",
- Zine now uses Ziggy as the frontmatter language! In the near future Zine will develop tooling for editing ziggy-markdown files. In the meantime consider downloading the Ziggy CLI tool for a smoother editing experience if you plan to use Ziggy directly.
- Added an initial version of sections to Zine! See the updated documentation section for more information about that. Beware that
$site.pages()was removed in favor of the new system. - Added a the ability to define
alternativesin the frontmatter of a page. Alternatives allow you to specify multiple layouts to apply to the same piece of content. Useful for generating RSS feeds. - Added syntax highlighting to layouts: now strings have a
syntaxHighlightbuiltin. - Updated Zig version because a bugfix was needed to add syntax highlighting to templates. Now Zine depends on Zig
0.12.0-dev.3381+7057bffc1and above. Make sure to update your GitHub Actions workflows accordingly.
2024-03-08
.url = "git+https://github.com/kristoff-it/zine.git#4b3efd178cb6ee9af3c864fa980ad0499823aac6",
.hash = "1220f6920dbb9540cc9013bbaa1621d62ef79aabadcbb6f7b9f45e415de815d15404",
- Added syntax highlighting support via tree-sitter. Most code snippets of this website have now gained syntax highlighting. No themes are provided for now and it’s expected that you define your own CSS from scratch. See
hightlight.cssfrom this website for an idea on how to proceed. - Updated Zig version because of a recent breaking change related to
std.http. Now Zine depends on Zig0.12.0-dev.3161+377ecc6afand above. Make sure to update your GitHub Actions workflows accordingly.
2024-02-13
- Overhauled the documentation page. Now it’s a little bit easier to get started with Zine.
- Added deployment guides: one for GitHub Pages and one for Cloudflare Pages (thanks
ninja_tron!) - Started work on the JSON replacement for better frontmatters, join the Discord server or catch me live on Twitch for related discussion & sneak peeks.
2024-02-11
.url = "git+https://github.com/kristoff-it/zine.git#beb5434a04fad660ecf8db8379532dfe5b5e13b0",
.hash = "12203c37cb5fb3931d3b7d1f1dace46cf5329ffe2fb5a8d2ac87dc78630ce7f601a7",
- Updated Zig version because of a recent breaking change related to
std.Options/root.std_options. Now Zine depends on Zig0.12.0-dev.2701+d18f52197and above. Consider using marler8997/zigup if you’re not building Zig from source. - The Scripty reference documentation was improved slightly: the reference for
Pagedisplays which fields have default values and which do not. - The dev server is now better at reporting build errors: in the event of a build error the message will be shown in the terminal, as well as being shown on the web page, and the 404 page too will connect to the hot-reloading mechanism in order to show build errors.
2024-02-09 (later in the day)
.url = "git+https://github.com/kristoff-it/zine.git#da7c32c2c253f6b0dbd392006055598feb07410b",
.hash = "1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299",
- When running the dev server (
zig build serve), Zine will now show build error messages inside of the web page itself using the hot reload mechanism. Fix the build error and the error overlay will disappear. Error messages are ugly for now (#16).
2024-02-09
.url = "git+https://github.com/kristoff-it/zine.git#527762348ef104dce601f52bca9f958a511ff11b",
.hash = "122018fb2b0ba1479ae28bacf3839d38da69044b006068fd67b1b7f4425114bec8d1",
- Zine will now stop erroring out in the presence of empty markdown files. It will instead print a warning and ignore them. Now you can
toucha bunch of files and fill them out as you make progress, without losing hot reloading in the meantime.
2024-02-08
.url = "git+https://github.com/kristoff-it/zine.git#eaa23f2d3a80868251302a1b979dbcc7e5b81d3a",
.hash = "1220230f7c6abf655ef9b1ec14161bd1c15e55afd14ceaedfe2e0e9cc2471b1dd0ca",
- Removed the
_index.mdvsindex.mdnaming convention. Now it’s alwaysindex.mdand you can use theskip_subdirsfrontmatter property to get the old behavior. See the docs for more information.