summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/themes/serene/.gitignore7
-rw-r--r--src/themes/serene/CHANGELOG.md165
-rw-r--r--src/themes/serene/README.md11
-rw-r--r--src/themes/serene/USAGE.md323
-rw-r--r--src/themes/serene/config.example.toml62
-rw-r--r--src/themes/serene/highlight_themes/serene-dark.tmTheme283
-rw-r--r--src/themes/serene/highlight_themes/serene-light.tmTheme239
-rw-r--r--src/themes/serene/sass/main.scss954
-rw-r--r--src/themes/serene/screenshot.pngbin188859 -> 311370 bytes
-rw-r--r--src/themes/serene/static/icon/email.svg5
-rw-r--r--src/themes/serene/static/js/main.js14
-rw-r--r--src/themes/serene/templates/_base.html9
-rw-r--r--src/themes/serene/templates/_components/collection.html129
-rw-r--r--src/themes/serene/templates/_components/prose.html72
-rw-r--r--src/themes/serene/templates/_components/seo.html39
-rw-r--r--src/themes/serene/templates/_footer.html24
-rw-r--r--src/themes/serene/templates/feed.xml40
-rw-r--r--src/themes/serene/templates/home.html69
-rw-r--r--src/themes/serene/templates/macros/collection.html153
-rw-r--r--src/themes/serene/templates/macros/prose.html32
-rw-r--r--src/themes/serene/templates/post.html71
-rw-r--r--src/themes/serene/templates/posts.html (renamed from src/themes/serene/templates/blog.html)28
-rw-r--r--src/themes/serene/templates/prose.html38
-rw-r--r--src/themes/serene/templates/shortcodes/caution.html2
-rw-r--r--src/themes/serene/templates/shortcodes/collection.html23
-rw-r--r--src/themes/serene/templates/shortcodes/detail.html4
-rw-r--r--src/themes/serene/templates/shortcodes/figure.html8
-rw-r--r--src/themes/serene/templates/shortcodes/important.html2
-rw-r--r--src/themes/serene/templates/shortcodes/mermaid.html3
-rw-r--r--src/themes/serene/templates/shortcodes/note.html2
-rw-r--r--src/themes/serene/templates/shortcodes/quote.html10
-rw-r--r--src/themes/serene/templates/shortcodes/tip.html2
-rw-r--r--src/themes/serene/templates/shortcodes/warning.html2
-rw-r--r--src/themes/serene/templates/tags/list.html16
-rw-r--r--src/themes/serene/templates/tags/single.html20
-rw-r--r--src/themes/serene/theme.toml4
-rw-r--r--src/themes/serene/zola.toml75
-rw-r--r--src/themes/serene/zola.toml.example80
38 files changed, 1402 insertions, 1618 deletions
diff --git a/src/themes/serene/.gitignore b/src/themes/serene/.gitignore
index 87760eb..b3fa3a8 100644
--- a/src/themes/serene/.gitignore
+++ b/src/themes/serene/.gitignore
@@ -1,8 +1,9 @@
+.zed/
+.claude/
+
public/
content/
static/img/
-static/hl-light.css
-static/hl-dark.css
-config.toml
+static/assets/
.DS_Store
diff --git a/src/themes/serene/CHANGELOG.md b/src/themes/serene/CHANGELOG.md
index 872470b..77a1b42 100644
--- a/src/themes/serene/CHANGELOG.md
+++ b/src/themes/serene/CHANGELOG.md
@@ -2,6 +2,158 @@
All notable changes to this project will be documented in this file.
+## [6.0.0] - 2026-09-06
+
+- feat: migrate to zola v0.23 / Tera v2, the minimum zola version required is now `v0.23.4`, shortcodes are rewritten as Tera components
+- feat: tag links are now generated via zola's taxonomy API instead of hardcoded `/tags/` paths, so the `taxonomy_root` config option (e.g. `taxonomy_root = "blog"` for `/blog/tags/xxx` URLs) is respected
+- feat: support multiple blog-like list sections — post pages now read config from their own parent section instead of the `blog_section_path` one, and each list section can have its own feed (`generate_feeds = true` in its `_index.md`); `blog_section_path` now only designates the main section used by the home page's recent posts and the tags pages
+- refactor: `blog.html` template is renamed to `posts.html`, change `template = "blog.html"` to `template = "posts.html"` in your blog section's `_index.md`
+- feat: display options (`toc` / `code_copy` / `comment` / `math` / `mermaid` / `reaction` / `outdated_alert` / `date_format`, etc.) now follow a unified fallback chain: post front-matter → section `_index.md` → `[extra]` of `zola.toml`, the closest one wins — notably `math` / `mermaid` can now be enabled for a whole section, and site-wide defaults can be set in `zola.toml`
+- refactor: several options are renamed for clarity: `force_theme` (`false | "light" | "dark"`) → `color_scheme` (`"auto" | "light" | "dark"`), `copy` → `code_copy`, `outdate_alert*` → `outdated_alert*`, and `id` of the home section → `handle`
+- refactor: the `sections` config option is renamed to `nav`, and its `is_external` field is removed — a `path` starting with `/` is an internal link, anything else (e.g. an `https://` URL) is treated as an external link automatically; external nav links now show a `ne-resize` cursor
+- refactor: `name` / `handle` / `bio` / `avatar` / `links` of the home page are moved from the home section's `_index.md` to `[extra]` of `zola.toml`
+- refactor: the example config file is renamed from `config.example.toml` to `zola.toml.example`, following zola v0.23's new default config file name `zola.toml` (the old `config.toml` name still works)
+- refactor: the callout components (`note` / `tip` / `important` / `warning` / `caution`) are removed in favor of zola's native [GitHub alert syntax](https://github.com/orgs/community/discussions/16925) (`> [!NOTE]`), styled with icon and title by the theme; the title texts can be customized via css variables (`--callout-note-title`, etc.)
+- feat: collections are redesigned — item appearance (`layout = "card" | "row" | "tile" | "gallery"`) and arrangement (`flow = "stack" | "inline" | "grid"`, the grid adapts its column count to the available width) are now declared at the collection level, and all layouts share one unified set of item fields (`title` / `subtitle` / `content` / `icon` / `image` / `link` / `badge` / `tags` / `featured`) with graceful degradation; item `link`s are auto-detected as internal or external, and `image` supports section-colocated files; `card` items can additionally show a picture on the left via `image` (the small `icon` before the title is a separate field)
+- chore: upgrade Mermaid to 11.17.2 and KaTeX to 0.18.6, refreshing KaTeX resource integrity hashes
+
+### Migrate from zola v0.22
+
+Zola v0.23 removed shortcodes and Tera macros in favor of [Tera components](https://www.getzola.org/documentation/content/overview/#templating-your-content), and your markdown content is now itself a Tera template. Once you update zola and this theme, you need to update your content accordingly:
+
+- Shortcode calls in your markdown must be rewritten in component syntax. Arguments other than strings are wrapped in `{...}`:
+ - `{{ figure(src="...", width="600") }}` → `{{ <figure src="..." width="600" /> }}`
+ - `{% quote(cite="...") %} ... {% end %}` → `{% <quote cite="..."> %} ... {% </quote> %}`
+ - `{{ youtube(id="...", autoplay=true) }}` → `{{ <youtube id="..." autoplay={true} /> }}`
+ - `{{ collection(file="projects.toml") }}` → `{{ <collection file="projects.toml" section /> }}` (note the extra `section`)
+- The callout shortcodes are removed, rewrite them with the GitHub alert syntax (custom titles are not supported, the title is always the type name):
+
+ ```md
+ {% note(title="Note") %}
+ note text
+ {% end %}
+ ```
+
+ becomes:
+
+ ```md
+ > [!NOTE]
+ > note text
+ ```
+
+- For `figure` with a colocated image, pass the page context: `{{ <figure src="img.png" page /> }}` (or `section` instead of `page` when used in a section's `_index.md`)
+- `width` / `height` of `figure` must be strings: `width="600"`, not `width=600`
+- Literal `{{` or `{%` in your content (e.g. code blocks showing template syntax, GitHub Actions `${{ ... }}`) must be wrapped with `{% raw %}` ... `{% endraw %}`, otherwise zola will try to interpret them as Tera syntax. For files full of such content, you can instead list them in the `skip_content_templating` config option (glob patterns) to opt them out of templating entirely (components won't work in those files)
+- Component calls must be at the top level of your markdown content — nesting them inside a list item is no longer supported (the component's HTML output breaks the list's indentation rules and produces broken HTML). If you had e.g. a callout inside a list item, move it out of the list
+- The `date` filter is now backed by [jiff](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html) instead of chrono. Common `date_format` values like `"%b %-d, %Y"` still work, but chrono-specific specifiers (e.g. `%+`) are no longer supported and will fail the build — check your `date_format` options against the jiff docs
+- Syntax highlighting CSS files (`giallo-light.css` / `giallo-dark.css`) are now generated directly into the output directory instead of `static/`, so you can remove them from your `static/` folder and `.gitignore`
+- If you have custom templates that override serene's, they must be migrated to Tera v2 as well, see the [Tera migration guide](https://github.com/Keats/tera/blob/master/MIGRATION.md)
+- The `blog.html` template is renamed to `posts.html`: change `template = "blog.html"` to `template = "posts.html"` in your blog section's `_index.md`
+- Zola's default config file name is now `zola.toml` — you can rename your `config.toml` to `zola.toml` (optional, the old name still works)
+- Several options are renamed, update them in your `zola.toml` / section `_index.md` / post front-matter:
+ - `force_theme = false | "light" | "dark"` → `color_scheme = "auto" | "light" | "dark"` (`false` becomes `"auto"`)
+ - `copy` → `code_copy`
+ - `outdate_alert` / `outdate_alert_days` / `outdate_alert_text_before` / `outdate_alert_text_after` → `outdated_alert` / `outdated_alert_days` / `outdated_alert_text_before` / `outdated_alert_text_after`
+ - `id` in the home section's `_index.md` → `handle`
+ - `sections` in `zola.toml` → `nav`, and remove the `is_external` field from its entries (external links are now detected automatically from the `path`)
+ - `name` / `handle` / `bio` / `avatar` / `links` move from the home section's `_index.md` to `[extra]` of `zola.toml`
+- Collection toml files use a new schema, update them as follows:
+ - each `[[collection]]` becomes `[[item]]`, and the per-item `type` moves to a single collection-level `layout` at the top of the file: `card` → `layout = "card"`, `card_simple` → `layout = "row"` plus `flow = "stack"` (rename its `content` to `subtitle`), `entry` → `layout = "row"`, `box` → `layout = "tile"`, `art` → `layout = "gallery"`, `art_simple` → `layout = "gallery"` plus `flow = "grid"`
+ - rename item fields: `img` → `image` (`icon` keeps its name), and both `date` and `footer` → `badge` (a short mark rendered as-is: a year, a date range, a rating, a status...)
+ - remove `type = "br"` items — arrangement is now controlled by the collection-level `flow` (`"stack"` / `"inline"` / `"grid"`) option
+
+## [5.7.0] - 2026-08-09
+
+- feat: add open graph & twitter card meta tags and canonical link
+- fix: resolve colocated figure image URLs [@uchouT](https://github.com/uchouT) ([#109](https://github.com/isunjn/serene/pull/109))
+- fix: art collection content margin
+
+## [5.6.3] - 2026-02-14
+
+- fix: back link behavior on new tab
+
+## [5.6.2] - 2026-02-13
+
+- feat: add markdown support for collection's content and subtitle [@paulhdk](https://github.com/paulhdk) ([#104](https://github.com/isunjn/serene/pull/104))
+
+## [5.6.1] - 2026-02-01
+
+- fix: codeblock with filename
+
+## [5.6.0] - 2026-02-01
+
+- feat: adapt zola v0.22 new code highlighting, custom highlight themes (`serene-light`/`serene-dark`) are removed in favor of built-in themes
+- feat: add `github_alerts` color styling (zola v0.21+ feature)
+- feat: add `rel_me` option for links (useful for Mastodon verification)
+- feat: add `youtube` shortcode [@gloomydumber](https://github.com/gloomydumber) ([#92](https://github.com/isunjn/serene/pull/92))
+- fix: input element color scheme should be changed along with light/dark mode change
+
+### Migrate from zola v0.21
+
+Since this version, the minimum zola version required has changed to `v0.22.1`.
+
+Replace the old `[markdown]` section in your `config.toml`:
+
+```toml
+# Old (remove these)
+[markdown]
+highlight_code = true
+highlight_theme = "css"
+extra_syntaxes_and_themes = ["themes/serene/highlight_themes"]
+highlight_themes_css = [
+ { theme = "serene-light", filename = "hl-light.css" },
+ { theme = "serene-dark", filename = "hl-dark.css" },
+]
+```
+
+With the new format:
+
+```toml
+# New
+[markdown]
+github_alerts = true # optional, enables GitHub-style callout syntax
+
+[markdown.highlighting]
+style = "class"
+light_theme = "github-light" # or any zola built-in theme
+dark_theme = "github-dark" # or any zola built-in theme
+```
+
+You should also delete the generated `hl-light.css` and `hl-dark.css` files from your `static/` directory if you have them.
+The new generated css files for code highlighting will be `giallo-light.css` and `giallo-dark.css`. (giallo is zola's new syntax highlighter)
+
+## [5.5.0] - 2025-10-18
+
+- feat: shortcode `figure`'s `caption` can use markdown now, the `via` prop is removed, use `caption="[via](https://example.com)"` instead
+- fix: shortcode `figure` now will keep img's ratio on small screens when `width` and `height` is set
+- ui: a few tweaks
+
+## [5.4.3] - 2025-09-29
+
+- fix: correct back link button behavior when there is a hash in url
+
+## [5.4.2] - 2025-09-27
+
+- fix: feed.xml template build error when there is no post
+
+## [5.4.1] - 2025-09-21
+
+- feat: add overflow scroll indicator for table-of-contents
+
+## [5.4.0] - 2025-09-06
+
+- ui: a few tweaks
+- feat: back link button now do `history.back()` if appropriate
+- feat: add custom height and width options for `figure` shortcode [@znxftw](https://github.com/znxftw) ([#83](https://github.com/isunjn/serene/pull/83))
+
+## [5.3.1] - 2025-05-22
+
+- fix: `extra_syntaxes_and_themes` defaults to theme's, so you don't need to copy that path [@makai410](https://github.com/makai410) ([#80](https://github.com/isunjn/serene/pull/80))
+
+## [5.3.0] - 2025-05-07
+
+- ui: a few tweaks
+
## [5.2.1] - 2025-04-19
- fix: homepage avatar style
@@ -315,6 +467,19 @@ All notable changes to this project will be documented in this file.
First release 🎉
+[6.0.0]: https://github.com/isunjn/serene/compare/v5.7.0...v6.0.0
+[5.7.0]: https://github.com/isunjn/serene/compare/v5.6.3...v5.7.0
+[5.6.3]: https://github.com/isunjn/serene/compare/v5.6.2...v5.6.3
+[5.6.2]: https://github.com/isunjn/serene/compare/v5.6.1...v5.6.2
+[5.6.1]: https://github.com/isunjn/serene/compare/v5.6.0...v5.6.1
+[5.6.0]: https://github.com/isunjn/serene/compare/v5.5.0...v5.6.0
+[5.5.0]: https://github.com/isunjn/serene/compare/v5.4.3...v5.5.0
+[5.4.3]: https://github.com/isunjn/serene/compare/v5.4.2...v5.4.3
+[5.4.2]: https://github.com/isunjn/serene/compare/v5.4.1...v5.4.2
+[5.4.1]: https://github.com/isunjn/serene/compare/v5.4.0...v5.4.1
+[5.4.0]: https://github.com/isunjn/serene/compare/v5.3.1...v5.4.0
+[5.3.1]: https://github.com/isunjn/serene/compare/v5.3.0...v5.3.1
+[5.3.0]: https://github.com/isunjn/serene/compare/v5.2.1...v5.3.0
[5.2.1]: https://github.com/isunjn/serene/compare/v5.2.0...v5.2.1
[5.2.0]: https://github.com/isunjn/serene/compare/v5.1.0...v5.2.0
[5.1.0]: https://github.com/isunjn/serene/compare/v5.0.1...v5.1.0
diff --git a/src/themes/serene/README.md b/src/themes/serene/README.md
index 4fad7a7..7620101 100644
--- a/src/themes/serene/README.md
+++ b/src/themes/serene/README.md
@@ -1,10 +1,5 @@
-<p align="center">
- A minimal blog theme for <a href="https://www.getzola.org">zola</a>, well crafted
-</p>
+A minimal blog theme for [Zola](https://www.getzola.org), well crafted.
-<p align="center">
- <a href="https://serene-demo.pages.dev">Demo</a> ·
- <a href="https://github.com/isunjn/serene/blob/latest/USAGE.md">Usage</a>
-</p>
+[Demo](https://serene-demo.pages.dev) | [Usage](https://github.com/isunjn/serene/blob/latest/USAGE.md)
-<img alt="Screenshot" src="https://github.com/user-attachments/assets/fa0c9529-f83b-4e4d-aed9-5327a44bc828" />
+![screenshot](https://github.com/isunjn/serene/blob/main/screenshot.png?raw=true)
diff --git a/src/themes/serene/USAGE.md b/src/themes/serene/USAGE.md
index 1b91d88..88e73a1 100644
--- a/src/themes/serene/USAGE.md
+++ b/src/themes/serene/USAGE.md
@@ -1,5 +1,7 @@
This is the detailed guide on how to use zola-theme-serene. You should also check zola's [documentation](https://www.getzola.org/documentation/getting-started/overview/).
+Serene requires zola `0.23.4` or later.
+
## Installation
Create a zola site and add serene theme (assuming your site is called `myblog`):
@@ -11,13 +13,13 @@ git init
git submodule add -b latest https://github.com/isunjn/serene.git themes/serene
```
-Copy the content of `myblog/themes/serene/config.example.toml` to `myblog/config.toml`.
+Copy the content of `myblog/themes/serene/zola.toml.example` to `myblog/zola.toml`.
## Sections and Pages
-There is a `sections` config option in your `config.toml`, which enumerates the sections your site has. You should have at least one `blog` section.
+There is a `nav` config option in your `zola.toml`, which enumerates the navigation entries of the home page. An entry whose `path` starts with `/` links to a section of your site, any other path (e.g. an `https://` URL) is treated as an external link and opens in a new tab. You should have at least one `blog` section.
-The name and path can be changed, be noticed that if you changed the blog section path (e.g. from `/posts` to `/blog`), then you should also change `blog_section_path` option.
+The name and path can be changed, note that if you changed the blog section path (e.g. from `/posts` to `/blog`), then you should also change `blog_section_path` option.
For home page, create `myblog/content/_index.md`:
@@ -31,22 +33,10 @@ lang = 'en'
# Show footer in home page
footer = false
-# If you don't want to display id/bio/avatar, simply comment out that line
-name = "Serene Demo"
-id = "serene"
-bio = "programmer, he/him, we are young and life is fun"
-avatar = "img/avatar.webp"
-links = [
- { name = "GitHub", icon = "github", url = "https://github.com/<your-username>" },
- { name = "Twitter", icon = "twitter", url = "https://twitter.com/<your-username>" },
- { name = "Email", icon = "email", url = "mailto:<your-email-address>" },
-]
-
# Show a few recent posts in home page
recent = false
recent_max = 15
recent_more_text = "more »"
-date_format = "%b %-d, %Y"
+++
Hi, I'm ...
@@ -59,7 +49,7 @@ For blog section, create `myblog/content/posts/_index.md`:
title = "My Blog"
description = "My blog site."
sort_by = "date"
-template = "blog.html"
+template = "posts.html"
page_template = "post.html"
insert_anchor_links = "right"
generate_feeds = true
@@ -70,22 +60,14 @@ lang = "en"
title = "Posts"
subtitle = "I write about ...."
-date_format = "%b %-d, %Y"
-
categorized = false # posts can be categorized
back_to_top = true # show back-to-top button
-toc = true # show table-of-contents
-comment = false # enable comment
-copy = true # show copy button in code block
-
-outdate_alert = false
-outdate_alert_days = 12
-outdate_alert_text_before = "This article was last updated "
-outdate_alert_text_after = " days ago and may be out of date."
+++
```
-Blog section is defined by `blog.html` and `post.html`. Serene also has a special template called `prose.html`, it applies the same styles of blog post page. You can use it as a section template for a custom section page, for example if you want a separate `about` page, you can add a `{ name = "about", path = "/about", is_external = false }` to the `sections` and create a `myblog/content/about/_index.md`:
+Display options like `toc` / `code_copy` / `comment` / `date_format` have site-wide defaults in `zola.toml`, you can override them here for this section (e.g. `toc = false`), see [Front Matter](#front-matter) for details.
+
+Blog section is defined by `posts.html` and `post.html`. Serene also has a special template called `prose.html`, it applies the same styles of blog post page. You can use it as a section template for a custom section page, for example if you want a separate `about` page, you can add a `{ name = "about", path = "/about" }` to the `nav` and create a `myblog/content/about/_index.md`:
```
+++
@@ -97,25 +79,25 @@ insert_anchor_links = "none"
[extra]
lang = 'en'
-title = "Posts"
-subtitle = "I write about ...."
-
-math = false
-mermaid = false
-copy = false
-comment = false
-reaction = false
+title = "About"
+subtitle = "About this site"
+++
Hi, My name is ....
```
-The default date format is "%b %-d, %Y", e.g. "Dec 13, 2025", check [this page](https://docs.rs/chrono/0.4.40/chrono/format/strftime/index.html) if you want to customize it, for example change to "%Y-%-m-%-d", e.g. "2025-2-13".
+The default date format is "%b %-d, %Y", e.g. "Dec 13, 2025", check [this page](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html) if you want to customize it, for example change to "%Y-%-m-%-d", e.g. "2025-2-13".
+
+### Multiple list sections
+
+You can have more than one blog-like list section, e.g. a `series` section alongside `posts`. Just create `myblog/content/series/_index.md` the same way as the blog section (with `template = "posts.html"` and `page_template = "post.html"`), and add it to `nav` in `zola.toml`. Each list section has its own options in `[extra]` (`date_format`, `toc`, `categorized`, etc.), and can have its own feed by setting `generate_feeds = true` in its `_index.md` (available at e.g. `/series/feed.xml`).
+
+The `blog_section_path` option in `zola.toml` points to your *main* blog section, it is used by the recent posts list of the home page and by the tags pages (tags are site-wide: posts from all list sections that share a tag will be listed together).
-Now the myblog directory may looks like this:
+Now the myblog directory may look like this:
```
-├── config.toml
+├── zola.toml
├── content/
│ ├── posts/
│ │ └── _index.md
@@ -146,9 +128,9 @@ Create a new directory `img` under `myblog/static`, put favicon related files he
## Icon
-The default icons are placed in `myblog/themes/serene/static/icon`, the `icon` value in `links` of home section is the file name of the svg file.
+The default icons are placed in `myblog/themes/serene/static/icon`, the `icon` value in `links` of `zola.toml` is the file name of the svg file.
-To customize, find the svg file you want, modify (in case you don't kown, a svg file is just a plian text file) its width and height to `18`, and the color to `currentColor`:
+To customize, find the svg file you want, modify (in case you don't know, a svg file is just a plain text file) its width and height to `18`, and the color to `currentColor`:
`... width="18" height="18" ... fill="currentColor" ...`
@@ -158,25 +140,28 @@ The default icons mostly came from [Remix Icon](https://remixicon.com/).
## Theme
-By default there is theme toggle button to switch between light and dark mode, you can set `force_theme` in `config.toml` to force a specific mode only.
+The `color_scheme` option in `zola.toml` controls the light/dark mode behavior: `"auto"` (default) follows the visitor's system preference and shows a theme toggle button, while `"light"` / `"dark"` locks the site to a single mode and hides the button.
-## Code Highlighting
+## RSS
-Copy `myblog/themes/serene/highlight_themes` directory to `myblog/highlight_themes`.
+There are two ways to provide feeds:
-By default serene use different highlight themes for light/dark mode, configured by `highlight_theme`, `extra_syntaxes_and_themes` and `highlight_themes_css`. The default highlight theme `serene-light` `serene-dark` is a modified version of [Tomorrow](https://github.com/ChrisKempson/Tomorrow-Theme) theme.
+- **Per-section feeds** (recommended): set `generate_feeds = false` in `zola.toml`, and `generate_feeds = true` in the `_index.md` of your list sections. Each of these sections gets its own feed (e.g. `/posts/feed.xml`, `/series/feed.xml`), using the `title` and `description` of that section. The RSS button in the footer links to the feed of the section the current page belongs to.
+- **A single site-wide feed**: set `generate_feeds = true` in `zola.toml`, and `generate_feeds = false` in section `_index.md` files. The feed is located in the root directory (e.g. `/feed.xml`), contains posts from all sections, and uses the `title` and `description` of `zola.toml`. The RSS button in the footer links to it on all pages.
-If you set `highlight_theme` in `config.toml` to one of zola's [built-in highlight themes](https://www.getzola.org/documentation/getting-started/configuration/#syntax-highlighting), you will get that theme used in both light and dark mode.
+`feed_filenames` can be set to `["feed.xml"]` (serene's own atom template), or `["atom.xml"]` / `["rss.xml"]` (zola's built-in templates), corresponding to different feed standards.
-If you want a different theme, find the `.tmTheme` TextMate file of your theme, put it in `myblog/static/highlight_themes`, and then modify the `theme` value of `highlight_themes_css` to that file's name. This will generate a `hl-light.css` and a `hl-dark.css` file in `myblog/static/`, you may have to delete them first before you change the `theme` value, so zola can re-generate. You can find some TextMate themes on [this website](https://tmtheme-editor.glitch.me/).
+## Open Graph
-## RSS
+Each page has [Open Graph](https://ogp.me/) and Twitter Card meta tags (plus a canonical link), so links shared to social media and chat apps can show a rich preview card with title, description and image.
-Zola's default feed file is located in the root directory of the site, set `generate_feeds = true` in `config.toml`, `feed_filenames` can be set to `["atom.xml"]` or `["rss.xml"] ` , corresponding to two different rss file standards, you should also set `generate_feeds = false` in `myblog/content/posts/_index.md`
+Title and description come from the same sources as the page's `<title>` and meta description. The preview image is resolved as follows:
-The serene theme looks more like a personal website, the posts are in the `/posts` directory, you may want the feed file to be in the `/posts` directory instead of the root directory, this requires you to set `generate_feeds = false ` `feed_filenames = ["feed.xml"]` in `config.toml`, and set `generate_feeds = true` in `myblog/content/posts/_index.md`.
+- Post pages use `og_image` in the `[extra]` section of front matter, if set. It can be a full URL, a path in the `static` folder (starting with `/`), or the filename of a [colocated asset](https://www.getzola.org/documentation/content/overview/#asset-colocation) of the post.
+- Otherwise, the site-wide default `og_image` in the `[extra]` section of `zola.toml` is used, if set. It can be a full URL or a path in the `static` folder, e.g. `og_image = "img/og.png"`.
+- If neither is set, image related tags are omitted.
-`feed.xml` uses `title` and `description` from `myblog/content/posts/_index.md`, the other two use `config.toml`'s.
+An image around 1200x630 is recommended for the best display on most platforms.
## Analytics
@@ -186,9 +171,9 @@ To add scripts for analytics tools (such as Google Analytics, Umami, etc.), you
Copy `myblog/themes/serene/templates/_custom_css.html` to `myblog/templates/_custom_css.html`, variables in this file are used to control styles, such as the theme color `--primary-color`, modify them as you want.
-If you want to customize more, you need to copy that file under the `templates`, `static`, `sass` directory in the corresponding `themes/serene` to the same name directory of `myblog`, and modify it. Be careful not to directly modify the files under the serene directory, because these modifications may cause conflicts if the theme is updated.
+If you want to customize more, copy the file you want to change from `themes/serene`'s `templates` / `static` / `sass` directory to the same-named directory of `myblog`, and modify it there. Be careful not to directly modify the files under the serene directory, because these modifications may cause conflicts if the theme is updated.
-If you want to use a custom font, create a new `myblog/templates/_custom_font.html` and put the font link tags (for eample, from [google fonts](https://fonts.google.com/)) into it, and then modify `--main-font` or `--code-font` in `myblog/sass/templates/_custom_css.html`. For performance reason, you may want to self-host font files, but it's optional:
+If you want to use a custom font, create a new `myblog/templates/_custom_font.html` and put the font link tags (for example, from [google fonts](https://fonts.google.com/)) into it, and then modify `--main-font` or `--code-font` in `myblog/templates/_custom_css.html`. For performance reasons, you may want to self-host font files, but it's optional:
1. Open [google-webfonts-helper](https://gwfh.mranftl.com) and choose your font.
2. Modify `Customize folder prefix` of step 3 to `/font/` and then copy the css.
@@ -215,25 +200,26 @@ tags = ["one", "two", "three"]
lang = "en"
toc = true
comment = false
-copy = true
-outdate_alert = true
-outdate_alert_days = 120
+code_copy = true
+outdated_alert = true
+outdated_alert_days = 120
math = false
mermaid = false
featured = false
reaction = false
+og_image = "cover.png"
+++
new post about something...
```
-Some of these options can also be configured in `myblog/content/posts/_index.md`, as the default value for all posts.
+Display options follow a unified fallback chain: **post front-matter → the post's section `_index.md` → `[extra]` of `zola.toml`**, the closest one wins. This applies to `toc`, `code_copy`, `comment`, `math`, `mermaid`, `reaction`, `outdated_alert` and `outdated_alert_days` (`date_format` and `outdated_alert_text_before/after` follow a section → config chain). So you can set site-wide defaults in `zola.toml`, override them per section, and override again per post.
-If you set `blog_categorized = true`, posts will be sorted alphabetically by default, you can manually set the order by adding a prefix `__[0-9]{2}__` in front of the category name, for example, `categories = ["__01__CatXXX"]`
+If you set `categorized = true`, posts are grouped by category, and categories are sorted alphabetically by default, you can manually set the order by adding a prefix `__[0-9]{2}__` in front of the category name, for example, `categories = ["__01__CatXXX"]`
## Table of Contents
-Set `toc = true` to display of table-of-contents.
+Set `toc = true` to display the table-of-contents.
## Math & Chart
@@ -245,19 +231,19 @@ Set `mermaid = true` to enable chart rendering with Mermaid.
Set `featured = true` to display an asterisk(*) mark in front of the title.
-## Outdate Alert
+## Outdated Alert
-If one of your posts has strong timeliness, you can display an outdate alert after certain days.
+If one of your posts has strong timeliness, you can display an outdated alert after certain days.
-Set `outdate_alert` and `outdate_alert_days` to enable the alert.
+Set `outdated_alert` and `outdated_alert_days` to enable the alert.
-In `myblog/content/posts/_index.md`, options `outdate_alert_text_before` and `outdate_alert_text_after` are the text content of the alert.
+Options `outdated_alert_text_before` and `outdated_alert_text_after` are the text content of the alert, they can be set in `[extra]` of `zola.toml`, or per section in its `_index.md`.
## Comment
You can use [giscus](https://giscus.app) as the comment system.
-To enable it, you need to create `myblog/templates/_giscus_script.html` and put the script configured on the giscus website into it, then change the value of `data-theme` to `https://<your-domain-name>/giscus_light.css`, replace `<your-domain-name>` with you domain name, same as `base_url` in `config.toml`, if you set `force_theme` to `dark`, replace `giscus_light.css` with `giscus_dark.css`.
+To enable it, you need to create `myblog/templates/_giscus_script.html` and put the script configured on the giscus website into it, then change the value of `data-theme` to `https://<your-domain-name>/giscus_light.css`, replace `<your-domain-name>` with you domain name, same as `base_url` in `zola.toml`, if you set `color_scheme` to `"dark"`, replace `giscus_light.css` with `giscus_dark.css`.
Then set `comment = true` to enable comment.
@@ -302,7 +288,7 @@ You need to setup a backend api endpoint to enable it. Your endpoint should hand
}
```
-For conivence, you can use one template repo to to setup your own endpoint:
+For convenience, you can use one template repo to setup your own endpoint:
- [isunjn/reaction](https://github.com/isunjn/reaction): All you need is a [Cloudflare](https://cloudflare.com) account. The free tier is good enough for a low-traffic personal blog.
@@ -317,169 +303,156 @@ Giscus also support a reaction feature, but it requires visitors to log in to Gi
Zola supports some [annotations for code blocks](https://www.getzola.org/documentation/content/syntax-highlighting/#annotations).
-## Shortcodes
+## Callouts
+
+Callouts use the [GitHub alert syntax](https://github.com/orgs/community/discussions/16925), there are 5 types: `NOTE` `TIP` `IMPORTANT` `WARNING` `CAUTION`:
+
+```md
+> [!NOTE]
+> note text
+```
+
+Serene styles them with an icon and a title. The title texts default to "Note" / "Tip" / "Important" / "Warning" / "Caution", you can change them (e.g. for a non-English site) by setting css variables in your `_custom_css.html`:
-[Shortcodes](https://www.getzola.org/documentation/content/shortcodes/) are some special templates.
+```css
+--callout-note-title: "注意";
+--callout-tip-title: "提示";
+--callout-important-title: "重要";
+--callout-warning-title: "警告";
+--callout-caution-title: "当心";
+```
+
+## Components
+
+Since zola `0.23`, your markdown content is itself a [Tera](https://keats.github.io/tera/) template, and shortcodes were replaced by [Tera components](https://www.getzola.org/documentation/content/overview/#templating-your-content). Serene provides some built-in components.
+
+Note that component arguments other than strings are wrapped in `{...}`, e.g. `autoplay={true}`.
-- Use `figure` to add caption to the image:
+- Use `figure` to add caption or width/height to an image, `alt` `caption` `width` `height` are all optional (`width` and `height` take strings):
```md
- {{ figure(src="/path/to/img", alt="alt text", caption="caption text") }}
+ {{ <figure src="/path/to/img" alt="alt text" caption="caption text" width="600" height="400" /> }}
```
- Adding attribution information to an image is very common, you can directly use the `via` attribute, which will display a link named 'via' below the image:
+ If `src` is the filename of a [colocated asset](https://www.getzola.org/documentation/content/overview/#asset-colocation), pass `page` (or `section` when used in a section's `_index.md`) so the image URL can be resolved:
```md
- {{ figure(src="/path/to/img", alt="some alt text", via="https://example.com") }}
+ {{ <figure src="colocated-img.png" caption="caption text" page /> }}
```
+ The caption is parsed as markdown so you can use bold / italic / link, for example `caption="[via](https://example.com)"`
+
+ Adding height to an image is always recommended, as this can avoid page layout shift. When you use `![](https://example.com/img.png)`, browser cannot determine the image's dimensions before it loads.
+
- Use `quote` to display a special quote block, `cite` is optional:
```md
- {% quote(cite="") %}
+ {% <quote cite=""> %}
// content...
- {% end %}
+ {% </quote> %}
```
- Use `detail` to add an expandable detail block, `default_open` is optional:
```md
- {% detail(title="", default_open=false) %}
+ {% <detail title="" default_open={false}> %}
// content...
- {% end %}
- ```
-
-- As you can see in [this page](https://serene-demo.pages.dev/posts/callouts) of the demo site, callouts are special blockquote blocks, just like [github's](https://github.com/orgs/community/discussions/16925). There are currently 5 types: `note` `tip` `important` `warning` `caution`.
-
- `title` is optional:
-
- ```md
- {% note(title="Note") %}
- note text
- {% end %}
+ {% </detail> %}
```
- Use `mermaid` to add a mermaid chart:
```md
- {% mermaid() %}
+ {% <mermaid> %}
flowchart LR
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
- {% end %}
+ {% </mermaid> %}
```
-## Collection
+- Use `youtube` to embed a youtube video, `autoplay` is optional, default to `false`:
-This theme has several special shortcodes for creating a collection of items. These collections can be used to showcase various types of your list, such as projects, publications, blogroll, bookmarks, etc. Check [this page](http://serene-demo.pages.dev/collections) on demo site to see some examples.
+ ```md
+ {{ <youtube id="<youtube-video-id>" autoplay={true} /> }}
+ ```
-Currently, there are 7 types of collection item:
+Since your markdown content is now a Tera template, if you want to write literal `{{` or `{%` in your content (e.g. in a code block), wrap it with `{% raw %}` and `{% endraw %}`.
-- `card`
+Note that component calls must be at the top level of your content — don't nest them inside a list item, as the component's HTML output would break the list's indentation rules and produce broken HTML.
- ```toml
- [[collection]]
- type = "card"
- title = "Title"
- subtitle = "Subtitle" # optional
- date = "Date" # optional
- link = "https://example.com" # optional
- icon = "https://example.com/image.png" # optional
- content = "Content"
- tags = ["tag1", "tag2"] # optional
- featured = false # optional
- ```
+## Collection
-- `card_simple`
+This theme has a special component for creating a collection of items. Collections can be used to showcase various types of lists, such as projects, publications, blogroll, bookmarks, books, etc. Check [this page](https://serene-demo.pages.dev/collections) on demo site to see some examples.
- ```toml
- [[collection]]
- type = "card_simple"
- title = "Title"
- date = "Date" # optional
- link = "https://example.com" # optional
- icon = "https://example.com/image.png" # optional
- content = "Content"
- featured = false # optional
- ```
+A collection is described by a toml file. Two collection-level options decide how it looks:
-- `entry`
+- `layout`: the appearance of each item
+ - `card`: rich block item, with title / subtitle / content / tags etc.
+ - `row`: compact one-line item
+ - `tile`: small bordered block, text on the left and a 48x48 image on the right
+ - `gallery`: item with a poster image
+- `flow`: how items are arranged in the container
+ - `stack`: vertically stacked, one item per line (default for `card` and `gallery`); a stacked `row` takes the full width with its `badge` aligned to the right
+ - `inline`: items take their content width and wrap horizontally (default for `row` and `tile`)
+ - `grid`: an even grid, the number of columns adapts to the available width automatically
- ```toml
- [[collection]]
- type = "entry"
- title = "Title" # optional
- subtitle = "Subtitle" # optional
- link = "https://example.com" # optional
- icon = "https://example.com/image.png" # optional
- ```
+Not every combination makes sense: `card` doesn't work with `flow = "inline"` (it is treated as `grid`), and an unknown `layout` / `flow` value falls back to the default.
-- `box`
+All layouts share the same set of item fields, every field except `title` is optional and simply omitted from rendering when absent. Each layout renders the fields that fit its density and ignores the rest:
- ```toml
- [[collection]]
- type = "box"
- title = "Title"
- subtitle = "Subtitle" # optional
- link = "https://example.com" # optional
- img = "https://example.com/image.png" # optional
- ```
-
-- `art`
+| field | card | row | tile | gallery |
+| ---------- | ------------ | ---- | --------- | -------- |
+| `title` | ✓ | ✓ | ✓ | ✓ |
+| `subtitle` | ✓ | ✓ | ✓ | ✓ |
+| `content` | ✓ | - | - | ✓ |
+| `icon` | ✓ | ✓ | - | - |
+| `image` | ✓ (left) | - | ✓ (48x48) | ✓ (poster) |
+| `link` | title | title | whole item | title |
+| `badge` | ✓ (right) | ✓ | - | ✓ (bottom) |
+| `tags` | ✓ | - | - | - |
+| `featured` | ✓ | ✓ | - | - |
+| `rotate` | - | - | ✓ | - |
- ```toml
- [[collection]]
- type = "art"
- title = "Title"
- subtitle = "Subtitle" # optional
- link = "https://example.com" # optional
- img = "https://example.com/image.png"
- content = "Content" # optional
- footer = "Footer" # optional
- ```
-
-- `art_simple`
-
- ```toml
- [[collection]]
- type = "art_simple"
- title = "Title"
- subtitle = "Subtitle" # optional
- link = "https://example.com" # optional
- img = "https://example.com/image.png"
- ```
+```toml
+layout = "card" # "card" | "row" | "tile" | "gallery"
+# flow = "grid" # "stack" | "inline" | "grid"
+[[item]]
+title = "Title"
+subtitle = "Subtitle" # supports inline markdown
+content = "Content" # supports markdown
+icon = "https://example.com/icon.png" # a small 16x16 icon shown before the title (card and row layouts)
+image = "https://example.com/image.png" # a picture: shown on the left for card, as the 48x48 block for tile, as the poster for gallery
+link = "https://example.com" # makes the title clickable (or the whole item for tile), external links open in a new tab automatically
+# `icon` and `image` can be a full URL, a path in `static` (starting with `/`), or a file colocated with the section
+badge = "2025" # a short mark rendered as-is: a year, a date range, a rating, a status...
+tags = ["tag1", "tag2"]
+featured = true # show an asterisk mark (card and row layouts)
+rotate = true # playfully rotate the image (tile layout)
+```
-List your items in a toml file and then use a `collection` shortcode to render them.
+Note that `gallery` changes its look with the flow: `stack` puts the poster on the left with text on the right, while `grid` / `inline` puts the poster on top with text centered below.
For example, to create a "projects" section page:
1. Create `myblog/content/projects/projects.toml`:
```toml
- [[collection]]
- type = "card"
+ layout = "card"
+
+ [[item]]
title = "Tokio"
link = "https://example.com"
- content = "Tokio is an asynchronous runtime for the Rust programming language. It provides the building blocks needed for writing network applications. It gives the flexibility to target a wide range of systems, from large servers with dozens of cores to small embedded devices."
+ content = "Tokio is an asynchronous runtime for the Rust programming language."
tags = ["rust", "async", "runtime"]
- [[collection]]
- type = "card"
+ [[item]]
title = "Kubernetes"
link = "https://example.com"
- content = "Kubernetes, also known as K8s, is an open source system for managing containerized applications across multiple hosts. It provides basic mechanisms for the deployment, maintenance, and scaling of applications."
+ content = "Kubernetes, also known as K8s, is an open source system for managing containerized applications."
tags = ["k8s", "golang"]
-
- [[collection]]
- type = "card"
- title = "Next.js"
- link = "https://example.com"
- content = "Next.js is a React framework for building full-stack web applications. You use React Components to build user interfaces, and Next.js for additional features and optimizations."
- tags = ["typescript", "react", "frontend"]
-
```
2. Create `myblog/content/projects/_index.md`:
@@ -495,18 +468,20 @@ For example, to create a "projects" section page:
subtitle = "Some cool projects I made"
+++
- {{ collection(file="projects.toml") }}
+ {{ <collection file="projects.toml" section /> }}
```
-3. Add projects section in `sections` of `config.toml`
+3. Add projects section in `nav` of `zola.toml`
```toml
- sections = [
+ nav = [
# ...
- { name = "projects", path = "/projects", is_external = false },
+ { name = "projects", path = "/projects" },
]
```
+A page can have multiple collections: put several toml files in the section directory and call the component for each, with markdown headings in between to group them.
+
## Build & Deploy
Local preview:
@@ -527,7 +502,7 @@ To deploy a static site, refer to zola's [documentation about deployment](https:
Check the [CHANGELOG.md](https://github.com/isunjn/serene/blob/main/CHANGELOG.md) on github for breaking changes before you update.
-If you copied some files from `myblog/themes/serene` to `myblog/` for customization, such as `_custom_css.html` or `main.scss`, then you should record what you have modified before you update, re-copy those files and re-apply your modification after updating. The `config.toml` should be re-copied too.
+If you copied some files from `myblog/themes/serene` to `myblog/` for customization, such as `_custom_css.html` or `main.scss`, then you should record what you have modified before you update, re-copy those files and re-apply your modification after updating. The `zola.toml` should be re-copied too.
You can watch (`watch > custom > releases > apply`) this project on github to be reminded of a new release.
diff --git a/src/themes/serene/config.example.toml b/src/themes/serene/config.example.toml
deleted file mode 100644
index e35d6c4..0000000
--- a/src/themes/serene/config.example.toml
+++ /dev/null
@@ -1,62 +0,0 @@
-# serene v5.2.1
-#
-# - docs: https://github.com/isunjn/serene/blob/latest/USAGE.md
-# - check for updates: https://github.com/isunjn/serene/releases
-#
-#=========================================================================================
-
-base_url = "https://example.com"
-title = "xxxx"
-description = "xxxx xxxx xxxx"
-default_language = "en"
-theme = "serene"
-output_dir = "public"
-compile_sass = true
-minify_html = false # Keep this false, as it may cause issues with some styles
-build_search_index = false # Keep this false, search is temporarily unsupported
-generate_feeds = false # Whether to generate a feed file in root, read docs for more info about rss feed
-feed_filenames = ["feed.xml"] # "feed.xml" | "atom.xml" | "rss.xml", read docs for more info
-taxonomies = [{ name = "tags" }, { name = "categories" }]
-
-[markdown]
-highlight_code = true
-highlight_theme = "css"
-extra_syntaxes_and_themes = ["highlight_themes"]
-highlight_themes_css = [
- { theme = "serene-light", filename = "hl-light.css" },
- { theme = "serene-dark", filename = "hl-dark.css" },
-]
-render_emoji = false
-external_links_target_blank = false
-external_links_no_follow = true
-external_links_no_referrer = true
-smart_punctuation = false
-
-[slugify]
-paths = "on"
-taxonomies = "on"
-anchors = "on"
-
-#=========================================================================================
-
-[extra]
-
-sections = [
- { name = "posts", path = "/posts", is_external = false },
- # { name = "tags", path = "/tags", is_external = false },
- # { name = "github", path = "https://github.com/<your-username>", is_external = true },
-]
-blog_section_path = "/posts"
-
-back_link_text = "Back" # Text of the back button
-force_theme = false # false | "light" | "dark"
-
-footer_copyright = "© 2025 <your-name>"
-footer_credits = true # Whether to show "Built with zola and serene" in footer
-
-not_found_error_text = "404 Not Found"
-not_found_recover_text = "« back to home »"
-
-reaction = false # Whether to enable anonymous emoji reactions (Note: You need to set up a working api endpoint to enable this feature)
-reaction_align = "right" # "left" | "center" | "right"
-reaction_endpoint = "https://example.com/api/reaction"
diff --git a/src/themes/serene/highlight_themes/serene-dark.tmTheme b/src/themes/serene/highlight_themes/serene-dark.tmTheme
deleted file mode 100644
index f4fd49c..0000000
--- a/src/themes/serene/highlight_themes/serene-dark.tmTheme
+++ /dev/null
@@ -1,283 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<!-- Generated by: TmTheme-Editor -->
-<!-- ============================================ -->
-<!-- app: http://tmtheme-editor.herokuapp.com -->
-<!-- code: https://github.com/aziz/tmTheme-Editor -->
-<plist version="1.0">
-<dict>
- <key>comment</key>
- <string>http:&#x2f;&#x2f;chriskempson.com</string>
- <key>name</key>
- <string>Tomorrow Night</string>
- <key>settings</key>
- <array>
- <dict>
- <key>settings</key>
- <dict>
- <key>caret</key>
- <string>#AEAFAD</string>
- <key>foreground</key>
- <string>#C5C8C6</string>
- <key>invisibles</key>
- <string>#4B4E55</string>
- <key>lineHighlight</key>
- <string>#282A2E</string>
- <key>selection</key>
- <string>#373B41</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Comment</string>
- <key>scope</key>
- <string>comment, string.quoted.double.block.python</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#999999</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Foreground</string>
- <key>scope</key>
- <string>keyword.operator.class, constant.other, source.php.embedded.line</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#CED1CF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Variable, String Link, Regular Expression, Tag Name</string>
- <key>scope</key>
- <string>variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#A67878</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Number, Constant, Function Argument, Tag Attribute, Embedded</string>
- <key>scope</key>
- <string>constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#E08355</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Class, Support</string>
- <key>scope</key>
- <string>type,
-entity.name.class, entity.name.type.class, support.type, support.class</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#83aaa5</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String, Symbols, Inherited Class, Markup Heading</string>
- <key>scope</key>
- <string>string, constant.other.symbol, entity.other.inherited-class, markup.heading</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#85AD74</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Operator, Misc</string>
- <key>scope</key>
- <string>keyword.operator, constant.other.color</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#83aaa5</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Function, Special Method, Block Level</string>
- <key>scope</key>
- <string>entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#81A2BE</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Keyword, Storage</string>
- <key>scope</key>
- <string>keyword, storage, storage.type, entity.name.tag.css</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#B294BB</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Invalid</string>
- <key>scope</key>
- <string>invalid</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#DF5F5F</string>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#CED2CF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Separator</string>
- <key>scope</key>
- <string>meta.separator</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#82A3BF</string>
- <key>foreground</key>
- <string>#CED2CF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Deprecated</string>
- <key>scope</key>
- <string>invalid.deprecated</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#B798BF</string>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#CED2CF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff foreground</string>
- <key>scope</key>
- <string>markup.inserted.diff, markup.deleted.diff, meta.diff.header.to-file, meta.diff.header.from-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#FFFFFF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff insertion</string>
- <key>scope</key>
- <string>markup.inserted.diff, meta.diff.header.to-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#4baf5c</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff deletion</string>
- <key>scope</key>
- <string>markup.deleted.diff, meta.diff.header.from-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#D46565</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff header</string>
- <key>scope</key>
- <string>meta.diff.header.from-file, meta.diff.header.to-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#4271ae</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff range</string>
- <key>scope</key>
- <string>meta.diff.range</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string>italic</string>
- <key>foreground</key>
- <string>#3e999f</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>diff.deleted</string>
- <key>scope</key>
- <string>markup.deleted</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#F92672</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>diff.inserted</string>
- <key>scope</key>
- <string>markup.inserted</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#A6E22E</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>diff.changed</string>
- <key>scope</key>
- <string>markup.changed</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#967EFB</string>
- </dict>
- </dict>
- </array>
- <key>uuid</key>
- <string>F96223EB-1A60-4617-92F3-D24D4F13DB09</string>
- <key>colorSpaceName</key>
- <string>sRGB</string>
- <key>semanticClass</key>
- <string>theme.dark.tomorrow_night</string>
-</dict>
-</plist> \ No newline at end of file
diff --git a/src/themes/serene/highlight_themes/serene-light.tmTheme b/src/themes/serene/highlight_themes/serene-light.tmTheme
deleted file mode 100644
index c4b8d1b..0000000
--- a/src/themes/serene/highlight_themes/serene-light.tmTheme
+++ /dev/null
@@ -1,239 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<!-- Generated by: TmTheme-Editor -->
-<!-- ============================================ -->
-<!-- app: http://tmtheme-editor.herokuapp.com -->
-<!-- code: https://github.com/aziz/tmTheme-Editor -->
-<plist version="1.0">
-<dict>
- <key>comment</key>
- <string>http:&#x2f;&#x2f;chriskempson.com</string>
- <key>name</key>
- <string>Tomorrow</string>
- <key>settings</key>
- <array>
- <dict>
- <key>settings</key>
- <dict>
- <key>caret</key>
- <string>#AEAFAD</string>
- <key>foreground</key>
- <string>#4D4D4C</string>
- <key>invisibles</key>
- <string>#D1D1D1</string>
- <key>lineHighlight</key>
- <string>#EFEFEF</string>
- <key>selection</key>
- <string>#D6D6D6</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Comment</string>
- <key>scope</key>
- <string>comment, string.quoted.double.block.python</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#999999</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Foreground</string>
- <key>scope</key>
- <string>keyword.operator.class, constant.other, source.php.embedded.line</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#666969</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Variable, String Link, Regular Expression, Tag Name</string>
- <key>scope</key>
- <string>variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#A67878</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Number, Constant, Function Argument, Tag Attribute, Embedded</string>
- <key>scope</key>
- <string>constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#E08355</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Type, Class, Support</string>
- <key>scope</key>
- <string>type,
-entity.name.class, entity.name.type.class, support.type, support.class</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#568A8F</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String, Symbols, Inherited Class, Markup Heading</string>
- <key>scope</key>
- <string>string, constant.other.symbol, entity.other.inherited-class, markup.heading</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#85AD74</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Operator, Misc</string>
- <key>scope</key>
- <string>keyword.operator, constant.other.color</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#568A8F</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Function, Special Method, Block Level</string>
- <key>scope</key>
- <string>entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#4271AE</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Keyword, Storage</string>
- <key>scope</key>
- <string>keyword, storage, storage.type</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#8959A8</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Invalid</string>
- <key>scope</key>
- <string>invalid</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#DF5F5F</string>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#FFFFFF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Separator</string>
- <key>scope</key>
- <string>meta.separator</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#4271AE</string>
- <key>foreground</key>
- <string>#FFFFFF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Deprecated</string>
- <key>scope</key>
- <string>invalid.deprecated</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#8959A8</string>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#FFFFFF</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff insertion</string>
- <key>scope</key>
- <string>markup.inserted.diff, meta.diff.header.to-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#229545</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff deletion</string>
- <key>scope</key>
- <string>markup.deleted.diff, meta.diff.header.from-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#C8282966</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff header</string>
- <key>scope</key>
- <string>meta.diff.header.from-file, meta.diff.header.to-file</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#4271ae</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Diff range</string>
- <key>scope</key>
- <string>meta.diff.range</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string>italic</string>
- <key>foreground</key>
- <string>#3e999f</string>
- </dict>
- </dict>
- </array>
- <key>uuid</key>
- <string>82CCD69C-F1B1-4529-B39E-780F91F07604</string>
- <key>colorSpaceName</key>
- <string>sRGB</string>
- <key>semanticClass</key>
- <string>theme.light.tomorrow</string>
-</dict>
-</plist> \ No newline at end of file
diff --git a/src/themes/serene/sass/main.scss b/src/themes/serene/sass/main.scss
index f5628a2..79eb9ac 100644
--- a/src/themes/serene/sass/main.scss
+++ b/src/themes/serene/sass/main.scss
@@ -31,7 +31,7 @@ a:focus-visible {
::-webkit-scrollbar {
width: 8px;
- height: 8px;
+ height: 6px;
background-color: transparent;
}
::-webkit-scrollbar-track {
@@ -112,6 +112,7 @@ a:focus-visible {
img {
max-width: 100%;
+ height: auto;
display: block;
margin: 0 auto;
border-radius: var(--img-border-radius);
@@ -138,17 +139,119 @@ a:focus-visible {
p {
margin: 1em 0;
}
+
+ &[class*="markdown-alert-"] {
+ margin: 1.5em 0;
+ display: grid;
+ grid-template-columns: 18px 1fr;
+ column-gap: 0.5em;
+
+ &::before {
+ // the icon, colored via mask so it follows currentColor
+ content: "";
+ grid-row: 1;
+ grid-column: 1;
+ align-self: center;
+ width: 18px;
+ height: 18px;
+ background-color: currentColor;
+ mask-repeat: no-repeat;
+ mask-position: center;
+ mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ -webkit-mask-size: contain;
+ }
+
+ &::after {
+ // the title
+ grid-row: 1;
+ grid-column: 2;
+ align-self: center;
+ font-weight: 600;
+ }
+
+ > * {
+ grid-column: 1 / -1;
+ }
+
+ > p:first-of-type {
+ margin-top: 0.5em;
+ }
+ > p:last-of-type {
+ margin-bottom: 0;
+ }
+ }
+
+ &.markdown-alert-note {
+ color: var(--callout-note-color);
+ border-color: var(--callout-note-color);
+ &::before {
+ mask-image: url("icon/note.svg");
+ -webkit-mask-image: url("icon/note.svg");
+ }
+ &::after {
+ content: var(--callout-note-title, "Note");
+ }
+ }
+
+ &.markdown-alert-tip {
+ color: var(--callout-tip-color);
+ border-color: var(--callout-tip-color);
+ &::before {
+ mask-image: url("icon/tip.svg");
+ -webkit-mask-image: url("icon/tip.svg");
+ }
+ &::after {
+ content: var(--callout-tip-title, "Tip");
+ }
+ }
+
+ &.markdown-alert-important {
+ color: var(--callout-important-color);
+ border-color: var(--callout-important-color);
+ &::before {
+ mask-image: url("icon/important.svg");
+ -webkit-mask-image: url("icon/important.svg");
+ }
+ &::after {
+ content: var(--callout-important-title, "Important");
+ }
+ }
+
+ &.markdown-alert-warning {
+ color: var(--callout-warning-color);
+ border-color: var(--callout-warning-color);
+ &::before {
+ mask-image: url("icon/warning.svg");
+ -webkit-mask-image: url("icon/warning.svg");
+ }
+ &::after {
+ content: var(--callout-warning-title, "Warning");
+ }
+ }
+
+ &.markdown-alert-caution {
+ color: var(--callout-caution-color);
+ border-color: var(--callout-caution-color);
+ &::before {
+ mask-image: url("icon/caution.svg");
+ -webkit-mask-image: url("icon/caution.svg");
+ }
+ &::after {
+ content: var(--callout-caution-title, "Caution");
+ }
+ }
}
- ol,
- ul {
- padding-left: 1.5em;
+ ol, ul {
+ padding-left: 1.4em;
}
li {
- margin: 1em 0;
+ margin: 0.5em 0;
p {
- margin: 1em 0;
+ margin: 0.5em 0;
}
}
@@ -169,6 +272,7 @@ a:focus-visible {
border-spacing: 0;
border-collapse: collapse;
margin: 1.5em 0;
+ border: 1.5px solid var(--primary-color);
}
th,
@@ -190,7 +294,7 @@ a:focus-visible {
}
pre {
- font-size: 0.8em;
+ font-size: 0.85em;
margin: 1.25em 0;
padding: 12px 48px 12px 16px;
line-height: 1.5;
@@ -203,58 +307,14 @@ a:focus-visible {
font-family: var(--code-font);
}
- &[data-linenos] {
- padding-left: 0px;
- }
-
- table {
- width: 100%;
- margin: 0;
- border-collapse: collapse;
- border: none;
- th, td {
- line-height: 1.5;
- }
- }
-
-
- table tr td:first-of-type {
- color: var(--text-decoration-color);
- }
-
- table td {
- padding: 0;
- padding-right: 48px;
- text-align: initial;
- border: initial;
- }
-
- table td:nth-of-type(1) {
- text-align: right;
+ .giallo-ln {
+ padding-right: 12px;
user-select: none;
- padding-right: 1em;
- mark::before {
- left: -8px;
- width: calc(100% + 1em + 8px);
- }
}
- mark {
- display: block;
- color: inherit;
- background-color: transparent;
- position: relative;
- overflow: visible;
-
- &::before {
- pointer-events: none;
- content: '';
- position: absolute;
- top: 0;
- bottom: 0;
- width: calc(100% + 48px + 48px);
- background-color: var(--highlight-mark-color);
- }
+ &.z-l-code,
+ &.z-d-code {
+ background-color: var(--bg-color);
}
&.mermaid {
@@ -267,11 +327,6 @@ a:focus-visible {
}
}
- pre > code > mark::before {
- width: calc(100% + 48px + 16px);
- left: -16px;
- }
-
.codeblock {
margin: 1.5em 0;
position: relative;
@@ -280,19 +335,19 @@ a:focus-visible {
pre {
margin: 0;
}
- pre[data-name] {
+ code[data-name]::before {
+ content: attr(data-name);
+ display: block;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ padding: 12px 16px;
+ color: var(--text-pale-color);
+ width: 100%;
+ border-bottom: 1px solid var(--primary-pale-color);
+ }
+ pre:has(code[data-name]) {
padding-top: calc(36px + 1em * var(--line-height));
- &::before {
- content: attr(data-name);
- display: block;
- position: absolute;
- left: 0px;
- top: 0px;
- padding: 12px 16px;
- color: var(--text-pale-color);
- width: 100%;
- border-bottom: 1px solid var(--primary-pale-color);
- }
}
.copy {
@@ -359,69 +414,6 @@ a:focus-visible {
}
}
- .callout {
- margin: 1.5em 0;
-
- .icon {
- height: 1.75em;
- display: flex;
- align-items: center;
- }
-
- p {
- margin: 0;
- + p {
- margin: 1em 0;
- }
- }
-
- &.has-title {
- padding-left: 1em;
- .title {
- display: flex;
- align-items: center;
- gap: 0.5em;
- margin-bottom: 0.5em;
- }
- }
-
- &.no-title {
- padding-left: 0;
- border: none;
- display: flex;
- align-items: start;
- gap: 0.75em;
- .content {
- max-width: calc(100% - 30px);
- }
- }
-
- &.note {
- color: var(--callout-note-color);
- border-color: var(--callout-note-color);
- }
-
- &.tip {
- color: var(--callout-tip-color);
- border-color: var(--callout-tip-color);
- }
-
- &.important {
- color: var(--callout-important-color);
- border-color: var(--callout-important-color);
- }
-
- &.warning {
- color: var(--callout-warning-color);
- border-color: var(--callout-warning-color);
- }
-
- &.caution {
- color: var(--callout-caution-color);
- border-color: var(--callout-caution-color);
- }
- }
-
.quote {
border: none;
position: relative;
@@ -470,6 +462,26 @@ a:focus-visible {
.mermaid {
background: #fff;
}
+
+ .youtube {
+ position: relative;
+ height: 0;
+ padding-bottom: 56.25%;
+
+ iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: 0;
+ }
+ }
+
+
+ input {
+ color-scheme: light;
+ }
}
body.dark .prose {
@@ -479,6 +491,9 @@ body.dark .prose {
.mermaid {
filter: brightness(var(--dark-mode-chart-brightness));
}
+ input {
+ color-scheme: dark;
+ }
}
/* prose pages
@@ -508,365 +523,380 @@ body.prose-page {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
.prose {
- .collection-wrapper {
+ .collection {
margin: 1.5em 0;
- }
- .collection {
- border: none;
- margin: 0;
- padding: 0;
- color: var(--text-color);
- img {
- margin: 0;
+ &.flow-inline {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ }
+
+ &.flow-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
+ gap: 1.5em;
}
- a, button {
+
+ a {
-webkit-tap-highlight-color: transparent;
}
- }
- .collection.card {
- margin: 3em 0;
- position: relative;
+ .image img {
+ margin: 0;
+ }
- &.featured::before {
- content: '*';
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- transform: translateX(-200%);
- line-height: 2;
- color: var(--primary-color);
+ span.title {
+ color: var(--text-color);
}
- @media (max-width: 768px) {
- &.featured::before {
- transform: translateX(-150%);
+
+ a.title {
+ border-bottom: none;
+ > span {
+ border-bottom: 1.5px solid var(--primary-color);
}
}
- .meta {
+ .subtitle,
+ .badge {
+ font-size: 0.9em;
+ color: var(--text-pale-color);
+ }
+ }
+
+ /* card: rich block item */
+ .layout-card {
+ .item {
+ margin: 3em 0;
+ position: relative;
display: flex;
- gap: 0.5em;
- .icon-wrapper {
- height: calc(1em * var(--line-height));
- display: flex;
- align-items: center;
+ align-items: center;
+ gap: 1.5em;
+
+ > .image {
flex-shrink: 0;
+ width: 160px;
+ img {
+ width: 100%;
+ height: auto;
+ border-radius: var(--img-border-radius);
+ }
}
- .icon {
- height: 16px;
- width: 16px;
+
+ @media (max-width: 425px) {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 1em;
+
+ > .image {
+ width: 100%;
+ }
}
- .title {
+
+ .body {
+ flex: 1;
+ min-width: 0;
+ }
+
+ &.featured::before {
+ content: '*';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ transform: translateX(-200%);
+ line-height: 2;
color: var(--primary-color);
}
- a.title {
- text-decoration: none;
- border-bottom: 1.5px solid transparent;
- &:hover {
- opacity: unset;
- border-color: var(--primary-color);
+ @media (max-width: 768px) {
+ &.featured::before {
+ transform: translateX(-150%);
}
}
- .date {
- margin-left: auto;
- flex-shrink: 0;
- font-size: 0.9em;
- color: var(--text-pale-color);
- }
- }
- .subtitle {
- font-size: 0.9em;
- color: var(--text-pale-color);
- margin: 0.75em 0;
- }
+ .meta {
+ display: flex;
+ gap: 0.5em;
- .content {
- font-size: 1em;
- p {
+ .icon {
+ height: calc(1em * var(--line-height));
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ img {
+ height: 16px;
+ width: 16px;
+ }
+ }
+ .badge {
+ margin-left: auto;
+ flex-shrink: 0;
+ }
+ }
+
+ .subtitle {
+ display: block;
margin: 0.75em 0;
}
- }
- .tags {
- font-size: 0.9em;
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 0.75em 1em;
- color: var(--text-pale-color);
+ .content {
+ font-size: 1em;
+ p {
+ margin: 0.75em 0;
+ }
+ }
- div {
- span {
+ .tags {
+ font-size: 0.9em;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 0.75em 1em;
+ color: var(--text-pale-color);
+
+ .tag span {
font-size: 0.9em;
margin-right: 4px;
}
}
}
- }
- .collection.card-simple {
- margin: 0.5em 0;
- position: relative;
-
- &.featured::before {
- content: '*';
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- transform: translateX(-200%);
- line-height: 2;
- color: var(--primary-color);
+ &.flow-grid .item,
+ &.flow-inline .item {
+ margin: 0;
}
- @media (max-width: 768px) {
- &.featured::before {
- transform: translateX(-150%);
- }
+ &.flow-grid {
+ grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
+ }
- .meta {
- display: flex;
- align-items: start;
- gap: 0.5em;
+ /* row: compact one-line item */
+ .layout-row {
+ &.flow-inline {
+ gap: 0.5em 1.5em;
}
- .icon-wrapper {
- height: calc(1em * var(--line-height));
- display: flex;
- align-items: center;
- flex-shrink: 0;
- }
- .icon {
- height: 16px;
- width: 16px;
- }
- .title {
- flex-shrink: 0;
+ .item {
+ display: inline-flex;
+ width: fit-content;
max-width: 100%;
- color: var(--primary-color);
- margin-right: 0.5em;
- }
- a.title {
- text-decoration: none;
- border-bottom: 1.5px solid transparent;
- &:hover {
- opacity: unset;
- border-color: var(--primary-color);
+ gap: 0.5em;
+ padding: 4px 0;
+ color: var(--text-color);
+ position: relative;
+
+ &.featured::before {
+ content: '*';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ transform: translateX(-200%);
+ line-height: 2;
+ color: var(--primary-color);
}
- }
- .content,
- .content-narrow {
- p {
- margin: 0;
- + p {
- margin-top: 0.5em;
+ @media (max-width: 768px) {
+ &.featured::before {
+ transform: translateX(-150%);
}
}
- }
- .content-narrow {
- display: none;
- margin: 0.25em 0 2em;
- }
- @media (max-width: 425px) {
- .content {
- display: none;
- }
- .content-narrow {
- display: block;
+
+ .icon {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ height: calc(1em * var(--line-height));
+ img {
+ width: 16px;
+ height: 16px;
+ }
}
- }
- .date {
- margin-left: auto;
- flex-shrink: 0;
- font-size: 0.9em;
- line-height: calc(var(--line-height) * 1.111);
- color: var(--text-pale-color);
- }
- }
+ .main {
+ display: inline-flex;
+ gap: 0.5em;
+ min-width: 0;
+ }
- .collection.entry {
- display: inline-flex;
- width: fit-content;
- gap: 0.5em;
- padding: 4px 0;
- margin: 0.25em 1.5em 0.25em 0;
+ .main:has(+ .subtitle) {
+ margin-right: 0.5em;
+ }
+ }
- .icon-wrapper {
- flex-shrink: 0;
+ &.flow-stack .item {
display: flex;
- align-items: center;
- height: calc(1em * var(--line-height));
- }
- .icon {
- width: 16px;
- height: 16px;
- }
+ width: auto;
+ margin: 0.25em 0;
- .text div {
- display: inline;
- }
- .title:has(+.subtitle) {
- margin-right: 0.5em;
- }
- .subtitle {
- font-size: 0.9em;
- color: var(--text-pale-color);
- }
- }
- a.collection.entry {
- -webkit-tap-highlight-color: transparent;
- color: var(--text-color);
- border-bottom: 1.5px solid transparent;
- &:hover {
- border-color: var(--primary-color);
- opacity: unset;
- cursor: pointer;
+ .badge {
+ margin-left: auto;
+ padding-left: 1em;
+ flex-shrink: 0;
+ }
}
- }
- .collection.box {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- border: 1px solid var(--primary-color);
- overflow: hidden;
+ @media (max-width: 425px) {
+ .item:has(.subtitle) {
+ display: flex;
+ width: 100%;
+ flex-wrap: wrap;
+ }
- img {
- width: 48px;
- height: 48px;
- &.rotate {
- transition: transform 0.15s;
- transform: rotate(-20deg) translate(-2px, 12px);
+ .item:has(.badge) .subtitle {
+ order: 10;
+ flex-basis: 100%;
}
}
- .placehold {
- height: 48px;
+
+ }
+
+ /* tile: small bordered block, text left + image right */
+ .layout-tile {
+ &.flow-inline {
+ gap: 0.8em 0.5em;
}
- .text {
- min-height: 48px;
- line-height: 1.5;
- padding: 0 1em;
- display: flex;
- flex-direction: column;
- justify-content: center;
+ .item {
+ max-width: 100%;
+ display: inline-flex;
+ align-items: center;
+ justify-content: space-between;
+ border: 1px solid var(--primary-color);
overflow: hidden;
+ text-decoration: none;
- .title {
- font-size: 0.9em;
- color: var(--text-color);
+ img {
+ width: 48px;
+ height: 48px;
+ margin: 0;
+ &.rotate {
+ transition: transform 0.15s;
+ transform: rotate(-20deg) translate(-2px, 12px);
+ }
+ }
+ .placehold {
+ height: 48px;
}
- .subtitle {
- font-size: 0.8em;
- color: var(--text-pale-color);
+ .text {
+ min-height: 48px;
+ line-height: 1.5;
+ padding: 0 1em;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
overflow: hidden;
- text-wrap: nowrap;
- text-overflow: ellipsis;
+
+ .title {
+ font-size: 0.9em;
+ color: var(--text-color);
+ }
+
+ .subtitle {
+ font-size: 0.8em;
+ overflow: hidden;
+ text-wrap: nowrap;
+ text-overflow: ellipsis;
+ }
}
- }
- @media (hover: hover) {
- &:hover {
- opacity: unset;
- img.rotate {
- transform: rotate(0deg) translate(0px, 0px);
+ @media (hover: hover) {
+ &:hover {
+ opacity: unset;
+ img.rotate {
+ transform: rotate(0deg) translate(0px, 0px);
+ }
}
}
}
- }
- a.collection.box {
- -webkit-tap-highlight-color: transparent;
- }
- .collection-box-wrapper {
- max-width: 100%;
- width: fit-content;
- display: inline-flex;
- align-items: center;
- justify-content: space-between;
- border: 0.5px solid transparent;
- overflow: hidden;
- margin-right: 0.5em;
- margin-bottom: 0.8em;
- @media (hover: hover) {
- &:has(a):hover {
- border-color: var(--primary-color);
+
+ a.item {
+ @media (hover: hover) {
+ &:hover {
+ box-shadow: 0 0 0 1px var(--primary-color);
+ }
}
}
+
+ &.flow-stack .item {
+ display: flex;
+ width: 100%;
+ margin: 0.5em 0;
+ }
+
@media (max-width: 425px) {
- & {
+ .item {
display: flex;
width: 100%;
}
}
}
- .collection.art {
- --art-h: 160px;
- --art-w: 115px;
+ /* gallery: item with a poster image */
+ .layout-gallery {
+ --poster-h: 160px;
+ --poster-w: 115px;
- margin: 2em 0;
- display: flex;
- gap: 1.5em;
+ &.flow-grid {
+ grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
+ }
+
+ a.title {
+ width: fit-content;
+ }
- .img-wrapper {
+ .image {
flex-shrink: 0;
- width: var(--art-w);
- height: var(--art-h);
+ width: var(--poster-w);
+ height: var(--poster-h);
img {
height: 100%;
width: 100%;
object-fit: contain;
}
}
- .text {
- height: var(--art-h);
- display: flex;
- flex-direction: column;
- gap: 3px;
- }
- .title {
- color: var(--primary-color);
- }
- a.title {
- width: fit-content;
- border-color: transparent;
- &:hover {
- opacity: unset;
- border-color: var(--primary-color);
- }
- }
- .subtitle,
- .footer {
- font-size: 0.8em;
- color: var(--text-pale-color);
- }
+
.content {
font-size: 0.9em;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
+ p {
+ margin: 0;
+ }
}
- .footer {
- margin-top: auto;
+
+ .subtitle,
+ .badge {
+ font-size: 0.8em;
}
- @media (max-width: 425px) {
- & {
- --art-h: 120px;
- --art-w: 85px;
+ /* stack: poster left, text right */
+ &.flow-stack .item {
+ margin: 2em 0;
+ display: flex;
+ gap: 1.5em;
+
+ .text {
+ height: var(--poster-h);
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ }
+ .badge {
+ margin-top: auto;
+ }
+
+ @media (max-width: 425px) {
+ --poster-h: 120px;
+ --poster-w: 85px;
gap: 0.75em;
.text {
gap: 2px;
}
.subtitle,
- .footer {
+ .badge {
font-size: 0.7em;
}
.content {
@@ -876,63 +906,36 @@ body.prose-page {
}
}
- }
-
-
- .collection.art-simple {
- --art-h: 160px;
- --art-w: 115px;
-
- max-width: calc(50vw - 30px);
- margin: 1.5em 0.75em;
- display: inline-flex;
+ /* grid / inline: poster on top, text centered below */
+ &.flow-grid .item,
+ &.flow-inline .item {
+ display: flex;
flex-direction: column;
align-items: center;
gap: 1em;
- .img-wrapper {
- flex-shrink: 0;
- width: var(--art-w);
- height: var(--art-h);
- img {
- height: 100%;
- width: 100%;
- object-fit: contain;
- }
+ .title {
+ font-size: 0.9em;
}
.text {
- max-width: calc(var(--art-w) * 1.5);
+ max-width: calc(var(--poster-w) * 1.5);
display: flex;
flex-direction: column;
align-items: center;
+ text-align: center;
gap: 3px;
}
- .title {
- font-size: 0.9em;
- color: var(--primary-color);
- }
- a.title {
- width: fit-content;
- border-color: transparent;
- &:hover {
- opacity: unset;
- border-color: var(--primary-color);
- }
- }
- .subtitle {
- font-size: 0.8em;
- color: var(--text-pale-color);
- }
@media (max-width: 425px) {
- & {
- --art-h: 120px;
- --art-w: 85px;
- width: calc(50vw - 50px);
- }
+ --poster-h: 120px;
+ --poster-w: 85px;
}
-
}
+ &.flow-inline {
+ gap: 1.5em 2em;
+ align-items: start;
+ }
+ }
}
/* layout post list ( home / blog / tag )
@@ -957,10 +960,9 @@ body.prose-page {
display: flex;
justify-content: space-between;
align-items: flex-end;
- gap: 0.5em;
- padding: 4px 0px;
- margin: 6px 0;
- line-height: 1.2;
+ gap: 2em;
+ padding: 0.25em 0px;
+ /* margin: 0.25em 0; */
text-decoration: none;
color: var(--primary-color);
@@ -982,13 +984,27 @@ body.prose-page {
top: 0;
bottom: 0;
left: 0;
- transform: translateX(-200%);
- line-height: 2;
- height: 100%;
+ transform: translate(-200%, 6px);
+ height: calc(var(--line-height) * 1em);
}
@media (max-width: 768px) {
&.featured::before {
- transform: translateX(-150%);
+ transform: translate(-150%, 6px);
+ }
+ }
+
+ @media (max-width: 425px) {
+ flex-direction: column-reverse;
+ align-items: start;
+ gap: 0.25em;
+ margin: 1em 0;
+ .date {
+ margin-left: initial;
+ color: var(--text-pale-color);
+ font-size: 0.8em;
+ }
+ &.featured::before {
+ transform: translate(-150%, 115%);
}
}
}
@@ -1070,6 +1086,10 @@ body.homepage {
gap: 1em;
}
+ img + #text {
+ gap: 0.75em;
+ }
+
#id {
margin-left: 0.75em;
color: var(--primary-color);
@@ -1109,6 +1129,10 @@ body.homepage {
a {
border-bottom: 1.5px solid var(--primary-color);
line-height: 1.5;
+
+ &.external {
+ cursor: ne-resize;
+ }
}
}
@@ -1382,7 +1406,6 @@ body.post {
}
#outdate_alert {
- font-style: italic;
&.hidden {
display: none;
}
@@ -1411,11 +1434,20 @@ body.post {
min-width: 60%;
overflow-y: auto;
max-height: calc(100vh - 6em);
- scrollbar-width: none;
+ scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
}
+
+ background:
+ linear-gradient(var(--bg-color) 50%, transparent) center top,
+ linear-gradient(transparent, var(--bg-color) 50%) center bottom,
+ radial-gradient(farthest-side at 50% 0, var(--primary-decoration-color), var(--bg-color)) center top,
+ radial-gradient(farthest-side at 50% 100%, var(--primary-decoration-color), var(--bg-color)) center bottom;
+ background-repeat: no-repeat;
+ background-size: 100% 4em, 100% 4em, 100% 1.5em, 100% 1.5em;
+ background-attachment: local, local, scroll, scroll;
}
ul {
@@ -1436,27 +1468,13 @@ body.post {
padding: 0 1em;
&.h3 {
- padding-left: 2em;
- }
-
- &::before {
- display: block;
- content: "";
- width: 1.5px;
- position: absolute;
- top: 0.7em;
- bottom: 0.7em;
- left: 0em;
- background: transparent;
+ margin-left: 1.5em;
+ border-left: 1px solid var(--text-decoration-color);
}
&:hover {
color: var(--primary-color);
}
-
- &:hover::before {
- background-color: var(--primary-color);
- }
}
#back-to-top {
diff --git a/src/themes/serene/screenshot.png b/src/themes/serene/screenshot.png
index b824cde..2856540 100644
--- a/src/themes/serene/screenshot.png
+++ b/src/themes/serene/screenshot.png
Binary files differ
diff --git a/src/themes/serene/static/icon/email.svg b/src/themes/serene/static/icon/email.svg
index 0c64d95..1593667 100644
--- a/src/themes/serene/static/icon/email.svg
+++ b/src/themes/serene/static/icon/email.svg
@@ -1,4 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18">
- <path d="M3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3ZM20 7.23792L12.0718 14.338L4 7.21594V19H20V7.23792ZM4.51146 5L12.0619 11.662L19.501 5H4.51146Z" fill="currentColor">
- </path>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3ZM20 7.23792L12.0718 14.338L4 7.21594V19H20V7.23792ZM4.51146 5L12.0619 11.662L19.501 5H4.51146Z" fill="currentColor"></path></svg>
diff --git a/src/themes/serene/static/js/main.js b/src/themes/serene/static/js/main.js
index b0aee0c..df2ef33 100644
--- a/src/themes/serene/static/js/main.js
+++ b/src/themes/serene/static/js/main.js
@@ -5,7 +5,7 @@ function enableThemeToggle() {
const preferDark = window.matchMedia("(prefers-color-scheme: dark)");
function toggleTheme(theme) {
if (theme == "dark") document.body.classList.add('dark'); else document.body.classList.remove('dark');
- if (hlLink) hlLink.href = `/hl-${theme}.css`;
+ if (hlLink) hlLink.href = `/giallo-${theme}.css`;
sessionStorage.setItem("theme", theme);
toggleGiscusTheme(theme);
}
@@ -243,9 +243,21 @@ function enableReaction() {
init();
}
+function enableBackLink() {
+ const backLink = document.querySelector('#back-link');
+ if (!backLink) return;
+ backLink.addEventListener('click', (e) => {
+ if (document.referrer && location.href.startsWith(document.referrer) && !location.hash && history.length > 1) {
+ e.preventDefault();
+ history.back();
+ }
+ });
+}
+
enableThemeToggle();
enablePrerender();
enableRssMask();
+enableBackLink();
if (document.body.classList.contains('post')) {
enableOutdateAlert();
addBackToTopBtn();
diff --git a/src/themes/serene/templates/_base.html b/src/themes/serene/templates/_base.html
index 0bd7b3b..d1f2d48 100644
--- a/src/themes/serene/templates/_base.html
+++ b/src/themes/serene/templates/_base.html
@@ -1,3 +1,4 @@
+{% set color_scheme = config.extra.color_scheme if config.extra.color_scheme is defined else "auto" %}
<!DOCTYPE html>
<html lang="{% block lang %}{% endblock lang %}">
@@ -18,15 +19,15 @@
{% include "_head_extend.html" %}
</head>
-<body class="{% block page %}{% endblock page%}{% if config.extra.force_theme == "dark" %} dark{% endif %}">
- {% if not config.extra.force_theme %}
+<body class="{% block page %}{% endblock page%}{% if color_scheme == "dark" %} dark{% endif %}">
+ {% if color_scheme == "auto" %}
<script>
const theme = sessionStorage.getItem('theme');
- const match = window.matchMedia("(prefers-color-scheme: dark)").matches
+ const match = window.matchMedia("(prefers-color-scheme: dark)").matches;
if ((theme && theme == 'dark') || (!theme && match)) {
document.body.classList.add('dark');
const hl = document.querySelector('link#hl');
- if (hl) hl.href = '{{ get_url(path="hl-dark.css") }}';
+ if (hl) hl.href = '{{ get_url(path="giallo-dark.css") }}';
}
</script>
{% endif %}
diff --git a/src/themes/serene/templates/_components/collection.html b/src/themes/serene/templates/_components/collection.html
new file mode 100644
index 0000000..70a1d31
--- /dev/null
+++ b/src/themes/serene/templates/_components/collection.html
@@ -0,0 +1,129 @@
+{#
+ This component is used inside markdown content, which is rendered as a Tera
+ template first and *then* processed as markdown (zola >= 0.23). Its HTML
+ output must therefore stay markdown-safe: no blank lines in the middle of
+ an HTML block, and no lines indented by 4+ spaces.
+
+ Collection file schema:
+ layout = "card" | "row" | "tile" | "gallery" (default "card")
+ flow = "stack" | "inline" | "grid" (default: card/gallery -> "stack", row/tile -> "inline")
+ [[item]]
+ title / subtitle / content / icon / image / link / badge / tags / featured / rotate
+#}
+
+{% component collection(file: string, section) -%}
+{%- set data = load_data(path="content" ~ section.path ~ file, format="toml") -%}
+{%- set layout = data.layout if data.layout is defined else "card" -%}
+{%- if layout not in ["card", "row", "tile", "gallery"] -%}
+{%- set layout = "card" -%}
+{%- endif -%}
+{%- if data.flow is defined and data.flow in ["stack", "inline", "grid"] -%}
+{%- set flow = data.flow -%}
+{%- elif layout == "row" or layout == "tile" -%}
+{%- set flow = "inline" -%}
+{%- else -%}
+{%- set flow = "stack" -%}
+{%- endif -%}
+{%- if layout == "card" and flow == "inline" -%}
+{%- set flow = "grid" -%}
+{%- endif -%}
+<div class="collection layout-{{ layout }} flow-{{ flow }}">
+{%- for item in data.item -%}
+{%- set img_src = "" -%}
+{%- if item.image is defined -%}
+{%- if item.image is starting_with(pat="http") or item.image is starting_with(pat="/") or item.image is starting_with(pat="data:") -%}
+{%- set img_src = item.image -%}
+{%- else -%}
+{%- set img_src = get_url(path=section.colocated_path ~ item.image) -%}
+{%- endif -%}
+{%- endif -%}
+{%- set icon_src = "" -%}
+{%- if item.icon is defined -%}
+{%- if item.icon is starting_with(pat="http") or item.icon is starting_with(pat="/") or item.icon is starting_with(pat="data:") -%}
+{%- set icon_src = item.icon -%}
+{%- else -%}
+{%- set icon_src = get_url(path=section.colocated_path ~ item.icon) -%}
+{%- endif -%}
+{%- endif -%}
+{%- set external = item.link is defined and not item.link is starting_with(pat="/") -%}
+{%- if layout == "card" -%}
+<div class="item{% if item.featured %} featured{% endif %}">
+{%- if img_src -%}<div class="image"><img src="{{ img_src }}" alt="{{ item.title }}"></div>{%- endif -%}
+<div class="body">
+<div class="meta">
+{%- if icon_src -%}<span class="icon"><img class="no-lightense" src="{{ icon_src }}" alt="" width="16" height="16"></span>{%- endif -%}
+{%- if item.link -%}
+<a class="title" href="{{ item.link }}"{% if external %} target="_blank" rel="noreferrer noopener"{% endif %}><span>{{ item.title }}</span></a>
+{%- else -%}
+<span class="title">{{ item.title }}</span>
+{%- endif -%}
+{%- if item.badge -%}<span class="badge">{{ item.badge }}</span>{%- endif -%}
+</div>
+{%- if item.subtitle -%}
+<div class="subtitle">{{ item.subtitle | trim | markdown(inline=true) | trim | safe }}</div>
+{%- endif -%}
+{%- if item.content -%}
+<div class="content">
+{{ item.content | trim | markdown | trim | safe }}
+</div>
+{%- endif -%}
+{%- if item.tags -%}
+<div class="tags">
+{%- for tag in item.tags -%}
+<span class="tag"><span>#</span>{{ tag }}</span>
+{%- endfor -%}
+</div>
+{%- endif -%}
+</div>
+</div>
+{%- elif layout == "row" -%}
+<span class="item{% if item.featured %} featured{% endif %}">
+<span class="main">
+{%- if icon_src -%}<span class="icon"><img class="no-lightense" src="{{ icon_src }}" alt="" width="16" height="16"></span>{%- endif -%}
+{%- if item.link -%}
+<a class="title" href="{{ item.link }}"{% if external %} target="_blank" rel="noreferrer noopener"{% endif %}><span>{{ item.title }}</span></a>
+{%- else -%}
+<span class="title">{{ item.title }}</span>
+{%- endif -%}
+</span>
+{%- if item.subtitle -%}<span class="subtitle">{{ item.subtitle | trim | markdown(inline=true) | trim | safe }}</span>{%- endif -%}
+{%- if item.badge -%}<span class="badge">{{ item.badge }}</span>{%- endif -%}
+</span>
+{%- elif layout == "tile" -%}
+{%- if item.link -%}
+<a class="item" href="{{ item.link }}"{% if external %} target="_blank" rel="noreferrer noopener"{% endif %}>
+{%- else -%}
+<span class="item">
+{%- endif -%}
+<span class="text">
+<span class="title">{{ item.title }}</span>
+{%- if item.subtitle -%}<span class="subtitle">{{ item.subtitle | trim | markdown(inline=true) | trim | safe }}</span>{%- endif -%}
+</span>
+{%- if img_src -%}
+<img class="no-lightense{% if item.rotate %} rotate{% endif %}" src="{{ img_src }}" alt="{{ item.title }}" width="48" height="48">
+{%- else -%}
+<span class="placehold"></span>
+{%- endif -%}
+{%- if item.link -%}</a>{%- else -%}</span>{%- endif -%}
+{%- elif layout == "gallery" -%}
+<div class="item">
+{%- if img_src -%}<div class="image"><img src="{{ img_src }}" alt="{{ item.title }}"></div>{%- endif -%}
+<div class="text">
+{%- if item.link -%}
+<a class="title" href="{{ item.link }}"{% if external %} target="_blank" rel="noreferrer noopener"{% endif %}><span>{{ item.title }}</span></a>
+{%- else -%}
+<span class="title">{{ item.title }}</span>
+{%- endif -%}
+{%- if item.subtitle -%}<span class="subtitle">{{ item.subtitle | trim | markdown(inline=true) | trim | safe }}</span>{%- endif -%}
+{%- if item.content -%}
+<div class="content">
+{{ item.content | trim | markdown | trim | safe }}
+</div>
+{%- endif -%}
+{%- if item.badge -%}<span class="badge">{{ item.badge }}</span>{%- endif -%}
+</div>
+</div>
+{%- endif -%}
+{%- endfor -%}
+</div>
+{%- endcomponent collection %}
diff --git a/src/themes/serene/templates/_components/prose.html b/src/themes/serene/templates/_components/prose.html
new file mode 100644
index 0000000..c478a58
--- /dev/null
+++ b/src/themes/serene/templates/_components/prose.html
@@ -0,0 +1,72 @@
+{#
+ Components in this file are used inside markdown content, which is rendered
+ as a Tera template first and *then* processed as markdown (zola >= 0.23).
+ Their HTML output must therefore stay markdown-safe: no blank lines in the
+ middle of an HTML block, and no lines indented by 4+ spaces.
+#}
+
+{% component back_link(path: string, config) %}
+<header>
+ <nav>
+ <a id="back-link" href="{{ path }}">← {{ config.extra.back_link_text }}</a>
+ </nav>
+</header>
+{% endcomponent back_link %}
+
+
+{% component quote(cite: string = "") -%}
+{%- set icon = load_data(path="icon/quote.svg") -%}
+<blockquote class="quote">
+<div class="icon" style="display: none;">{{ icon | trim | safe }}</div>
+<div class="content">
+{{ body | markdown | trim | safe }}
+</div>
+{%- if cite -%}
+{%- set from_text = "— " ~ cite -%}
+<div class="from">
+{{ from_text | markdown | trim | safe }}
+</div>
+{%- endif -%}
+</blockquote>
+{%- endcomponent quote %}
+
+
+{% component detail(title: string = "", default_open: bool = false) -%}
+<details{% if default_open %} open{% endif %}>
+<summary><span>{{ title }}</span></summary>
+{{ body | markdown | trim | safe }}
+</details>
+{%- endcomponent detail %}
+
+
+{% component figure(src: string, alt: string = "", caption: string = "", width: string = "", height: string = "", page = {}, section = {}) -%}
+{%- if src is starting_with(pat="/") or src is starting_with(pat="http://") or src is starting_with(pat="https://") or src is starting_with(pat="//") or src is starting_with(pat="data:") -%}
+{%- set image_src = src -%}
+{%- elif page?.colocated_path -%}
+{%- set image_src = get_url(path=page.colocated_path ~ src) -%}
+{%- elif section?.colocated_path -%}
+{%- set image_src = get_url(path=section.colocated_path ~ src) -%}
+{%- else -%}
+{%- set image_src = src -%}
+{%- endif -%}
+<figure>
+<img src="{{ image_src }}"{% if alt %} alt="{{ alt }}"{% endif %}{% if height %} height="{{ height }}"{% endif %}{% if width %} width="{{ width }}"{% endif %}>
+{%- if caption -%}
+<figcaption>{{ caption | trim | markdown | trim | safe }}</figcaption>
+{%- endif -%}
+</figure>
+{%- endcomponent figure %}
+
+
+{% component mermaid() -%}
+<pre class="mermaid">
+{{ body }}
+</pre>
+{%- endcomponent mermaid %}
+
+
+{% component youtube(id: string, autoplay: bool = false) -%}
+<div class="youtube">
+<iframe src="https://www.youtube.com/embed/{{ id }}{% if autoplay %}?autoplay=1&mute=1{% endif %}" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
+</div>
+{%- endcomponent youtube %}
diff --git a/src/themes/serene/templates/_components/seo.html b/src/themes/serene/templates/_components/seo.html
new file mode 100644
index 0000000..361d7d2
--- /dev/null
+++ b/src/themes/serene/templates/_components/seo.html
@@ -0,0 +1,39 @@
+{% component seo(title, desc, config, url = "", page_type = "website", image = "", colocated_path = "", published = "", updated = "", tags = [], lang = "") %}
+{%- set og_image = "" -%}
+{%- if image -%}
+ {%- if image is starting_with(pat="http://") or image is starting_with(pat="https://") or image is starting_with(pat="//") -%}
+ {%- set og_image = image -%}
+ {%- elif image is starting_with(pat="/") -%}
+ {%- set og_image = get_url(path=image) -%}
+ {%- elif colocated_path -%}
+ {%- set og_image = get_url(path=colocated_path ~ image) -%}
+ {%- else -%}
+ {%- set og_image = get_url(path=image) -%}
+ {%- endif -%}
+{%- elif config.extra.og_image -%}
+ {%- if config.extra.og_image is starting_with(pat="http://") or config.extra.og_image is starting_with(pat="https://") or config.extra.og_image is starting_with(pat="//") -%}
+ {%- set og_image = config.extra.og_image -%}
+ {%- else -%}
+ {%- set og_image = get_url(path=config.extra.og_image) -%}
+ {%- endif -%}
+{%- endif -%}
+<meta name="description" content="{{ desc | striptags | trim }}">
+{% if url %}<link rel="canonical" href="{{ url | safe }}">{% endif %}
+<meta property="og:site_name" content="{{ config.title }}">
+<meta property="og:type" content="{{ page_type }}">
+<meta property="og:title" content="{{ title }}">
+<meta property="og:description" content="{{ desc | striptags | trim }}">
+{% if url %}<meta property="og:url" content="{{ url | safe }}">{% endif %}
+{% if lang %}<meta property="og:locale" content="{{ lang | replace(from="-", to="_") }}">{% endif %}
+{% if og_image %}<meta property="og:image" content="{{ og_image | safe }}">{% endif %}
+{% if page_type == "article" %}
+{% if published %}<meta property="article:published_time" content="{{ published }}">{% endif %}
+{% if updated %}<meta property="article:modified_time" content="{{ updated }}">{% endif %}
+{% if tags %}{% for tag in tags %}<meta property="article:tag" content="{{ tag }}">
+{% endfor %}{% endif %}
+{% endif %}
+<meta name="twitter:card" content="{% if og_image %}summary_large_image{% else %}summary{% endif %}">
+<meta name="twitter:title" content="{{ title }}">
+<meta name="twitter:description" content="{{ desc | striptags | trim }}">
+{% if og_image %}<meta name="twitter:image" content="{{ og_image | safe }}">{% endif %}
+{% endcomponent seo %}
diff --git a/src/themes/serene/templates/_footer.html b/src/themes/serene/templates/_footer.html
index e9c1dd1..78d62c8 100644
--- a/src/themes/serene/templates/_footer.html
+++ b/src/themes/serene/templates/_footer.html
@@ -1,5 +1,10 @@
-{% if config.extra.blog_section_path is defined %}
-{% set blog_section_path = config.extra.blog_section_path %}
+{% set show_rss = false %}
+{% if section is defined and section.generate_feeds %}
+ {% set rss_path = section.path ~ config.feed_filenames[0] %}
+ {% set show_rss = true %}
+{% elif config.generate_feeds %}
+ {% set rss_path = "/" ~ config.feed_filenames[0] %}
+ {% set show_rss = true %}
{% endif %}
<footer>
<div class="left">
@@ -13,18 +18,11 @@
</div>
<div class="right">
- {% if blog_section_path is defined and section.path is starting_with(blog_section_path) %}
- {% if section.generate_feeds %}
- {% set_global rss_path = blog_section_path ~ "/" ~ config.feed_filenames.0 %}
- {% elif config.generate_feeds %}
- {% set_global rss_path = "/" ~ config.feed_filenames.0 %}
- {% endif %}
- {% if section.generate_feeds or config.generate_feeds %}
+ {% if show_rss %}
<a id="rss-btn" href="{{ get_url(path=rss_path) }}">RSS</a>
{% endif %}
- {% endif %}
- {% if not config.extra.force_theme %}
+ {% if color_scheme == "auto" %}
{% set moon_icon = load_data(path="icon/moon.svg") %}
{% set sun_icon = load_data(path="icon/sun.svg") %}
<button id="theme-toggle" aria-label="theme switch">
@@ -35,8 +33,7 @@
</div>
</footer>
-{% if blog_section_path is defined and section.path is starting_with(blog_section_path) %}
-{% if section.generate_feeds or config.generate_feeds %}
+{% if show_rss %}
{% set link = get_url(path=rss_path) %}
<dialog id="rss-mask">
<div>
@@ -49,4 +46,3 @@
</div>
</dialog>
{% endif %}
-{% endif %}
diff --git a/src/themes/serene/templates/feed.xml b/src/themes/serene/templates/feed.xml
index fe1f139..00aebe5 100644
--- a/src/themes/serene/templates/feed.xml
+++ b/src/themes/serene/templates/feed.xml
@@ -1,32 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
- <title>{% if section.title %}{{ section.title }}{% else %}{{ config.title }}{% endif %}</title>
- {%- if section.description %}
+ <title>{% if section is defined and section.title %}{{ section.title }}{% else %}{{ config.title }}{% endif %}</title>
+ {%- if section is defined and section.description %}
<subtitle>{{ section.description }}</subtitle>
{%- elif config.description %}
<subtitle>{{ config.description }}</subtitle>
{%- endif %}
- <link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
- <link href="
- {%- if section -%}
- {{ section.permalink | escape_xml | safe }}
- {%- else -%}
- {{ config.base_url | escape_xml | safe }}
- {%- endif -%}
- "/>
- <updated>{{ last_updated | date(format="%+") }}</updated>
+ <link rel="self" type="application/atom+xml" href="{{ feed_url | safe }}"/>
+ <link rel="alternate" type="text/html" href="
+ {%- if section is defined -%}
+ {{ section.permalink | escape_xml | safe }}
+ {%- else -%}
+ {{ config.base_url | escape_xml | safe }}
+ {%- endif -%}
+ "/>
+ {% if last_updated is defined %}
+ <updated>{{ last_updated | date(format="%Y-%m-%dT%H:%M:%S%:z") }}</updated>
+ {% endif %}
<id>{{ feed_url | safe }}</id>
{%- for page in pages %}
<entry xml:lang="{{ page.lang }}">
<title>{{ page.title }}</title>
- <published>{{ page.date | date(format="%+") }}</published>
- <updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
- {%- if page.summary %}
- <summary>{{ page.summary }}</summary>
- {%- endif %}
- <link href="{{ page.permalink | safe }}" type="text/html"/>
+ <published>{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}</published>
+ <updated>{{ (page.updated or page.date) | date(format="%Y-%m-%dT%H:%M:%S%:z") }}</updated>
+ <link rel="alternate" type="text/html" href="{{ page.permalink | safe }}"/>
<id>{{ page.permalink | safe }}</id>
- <content type="html">{{ page.content }}</content>
+ {% if page.summary %}
+ <summary>{{ page.summary }}</summary>
+ {% endif %}
+ <content type="html" xml:base="{{ page.permalink | escape_xml | safe }}">{{ page.content }}</content>
</entry>
{%- endfor %}
-</feed> \ No newline at end of file
+</feed>
diff --git a/src/themes/serene/templates/home.html b/src/themes/serene/templates/home.html
index f899884..e074268 100644
--- a/src/themes/serene/templates/home.html
+++ b/src/themes/serene/templates/home.html
@@ -4,22 +4,28 @@
{% block lang %}{% if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ section.lang }}{% endif %}{% endblock lang %}
{% block title %}{{ config.title }}{% endblock title %}
{% block desc %}
- <meta name="description" content="{{ config.description }}">
+ {%- if section.extra.lang %}{% set og_lang = section.extra.lang %}{% else %}{% set og_lang = section.lang %}{% endif -%}
+ {{ <seo config
+ title={config.title}
+ desc={config.description}
+ url={get_url(path="/")}
+ lang={og_lang} /> }}
{% endblock desc %}
{% block head %}
-{% if config.markdown.highlight_theme == "css" %}
- {% if config.extra.force_theme == "dark" %}
- <link id="hl" rel="stylesheet" type="text/css" href="{{ get_url(path='hl-dark.css') }}" />
+{% if config.markdown.highlighting.style == "class" %}
+ {% if color_scheme == "dark" %}
+ <link id="hl" rel="stylesheet" type="text/css" href="{{ get_url(path='giallo-dark.css') }}" />
{% else %}
- <link id="hl" rel="stylesheet" type="text/css" href="{{ get_url(path='hl-light.css') }}" />
+ <link id="hl" rel="stylesheet" type="text/css" href="{{ get_url(path='giallo-light.css') }}" />
{% endif %}
{% endif %}
-{% if section.extra.math %}
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js" integrity="sha384-HORx6nWi8j5/mYA+y57/9/CZc5z8HnEw4WUZWy5yOn9ToKBv1l58vJaufFAn9Zzi" crossorigin="anonymous"></script>
+{% set enable_math = section.extra.math if section.extra.math is defined else (config.extra.math if config.extra.math is defined else false) %}
+{% if enable_math %}
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/katex.min.css" integrity="sha384-M59ezskvvpvT+a+C1x088YJ3DVmK+wZdX0UkVKalOI4Qi5Nwv0WrvpqHcfa2HQqB" crossorigin="anonymous">
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/katex.min.js" integrity="sha384-7jGyG5zFwmEamqNWdCbpsPn+GTWEis3lnV7X/jXHyhFpJG7ExABLyMapabg8F4+p" crossorigin="anonymous"></script>
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/contrib/auto-render.min.js" integrity="sha384-bjyGPfbij8/NDKJhSGZNP/khQVgtHUE5exjm4Ydllo42FwIgYsdLO2lXGmRBf5Mz" crossorigin="anonymous"></script>
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/contrib/copy-tex.min.js" integrity="sha384-Pe2JWbaShhSRT0SESJ9XLPeAsi4yrNi7r/CiH0Coq7giBjK5a6Ae7XcybE8rNlQP" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
@@ -40,36 +46,40 @@
<div id="wrapper">
<main>
<section id="info">
- {% if section.extra.avatar %}
- <img src="{{ get_url(path=section.extra.avatar) }}" alt="avatar">
+ {% if config.extra.avatar %}
+ <img src="{{ get_url(path=config.extra.avatar) }}" alt="avatar">
{% endif %}
<div id="text">
<div>
- <span id="name">{{ section.extra.name }}</span>
- {% if section.extra.id %}
- <span id="id">@{{ section.extra.id }}</span>
+ <span id="name">{{ config.extra.name }}</span>
+ {% if config.extra.handle %}
+ <span id="id">@{{ config.extra.handle }}</span>
{% endif %}
</div>
- {% if section.extra.bio %}
- <div id="bio"> {{ section.extra.bio }} </div>
+ {% if config.extra.bio %}
+ <div id="bio"> {{ config.extra.bio }} </div>
{% endif %}
</div>
</section>
<section id="links">
<div id="left">
- {% for section in config.extra.sections %}
- <a {% if section.is_external %}href="{{ section.path }}" target="_blank" rel='noreferrer noopener'{% else %}href="{{ get_url(path=section.path) }}" class="instant"{% endif %}>{{ section.name }}</a>
+ {% for item in config.extra.nav %}
+ {% if item.path is starting_with(pat="/") and item.path is not starting_with(pat="//") %}
+ <a href="{{ get_url(path=item.path) }}" class="instant">{{ item.name }}</a>
+ {% else %}
+ <a href="{{ item.path }}" class="external" target="_blank" rel='noreferrer noopener'>{{ item.name }}</a>
+ {% endif %}
{% endfor %}
</div>
<div id="right">
- {% for link in section.extra.links %}
- <a href="{{ link.url }}" aria-label="{{ link.name }}" target="_blank" rel='noreferrer noopener'>
+ {% for link in config.extra.links %}
+ <a href="{{ link.url }}" aria-label="{{ link.name }}" target="_blank" rel='noreferrer noopener{% if link.rel_me%} me{% endif %}'>
{% set icon_path = "icon/" ~ link.icon ~ ".svg" %}
{% set icon = load_data(path=icon_path) %}
{{ icon | safe }}
</a>
{% endfor %}
- {% if not section.extra.footer and not config.extra.force_theme %}
+ {% if not section.extra.footer and color_scheme == "auto" %}
{% set moon_icon = load_data(path="icon/moon.svg") %}
{% set sun_icon = load_data(path="icon/sun.svg") %}
<button id="theme-toggle" aria-label="theme switch">
@@ -83,16 +93,16 @@
{{ section.content | trim | safe }}
</section>
{% if section.extra.recent %}
- {% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %}
+ {% set blog_section_path = config.extra.blog_section_path | trim_start(pat="/") %}
{% set section_md_path = blog_section_path ~ "/_index.md" %}
{% set blog_section = get_section(path=section_md_path) %}
+ {% set date_format = section.extra.date_format if section.extra.date_format is defined else (config.extra.date_format if config.extra.date_format is defined else "%b %-d, %Y") %}
<section class="layout-list">
<div class="post-list">
- {% for post in blog_section.pages | slice(end=section.extra.recent_max) %}
- <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}">
+ {% for post in blog_section.pages[:section.extra.recent_max] %}
+ <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink }}">
<span>{{ post.title }}</span>
- <span class="line"></span>
- <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span>
+ <span class="date">{{ post.date | date(format=date_format) }}</span>
</a>
{% endfor %}
</div>
@@ -110,9 +120,10 @@
{% block script %}
<script src="{{ get_url(path='js/lightense.min.js') }}"></script>
-{% if section.extra.mermaid %}
+{% set enable_mermaid = section.extra.mermaid if section.extra.mermaid is defined else (config.extra.mermaid if config.extra.mermaid is defined else false) %}
+{% if enable_mermaid %}
<script type="module">
- import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11.17.2/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
{% endif %}
diff --git a/src/themes/serene/templates/macros/collection.html b/src/themes/serene/templates/macros/collection.html
deleted file mode 100644
index 796eae1..0000000
--- a/src/themes/serene/templates/macros/collection.html
+++ /dev/null
@@ -1,153 +0,0 @@
-{% macro card(item) %}
-<blockquote class="collection card{% if item.featured %} featured{% endif %}">
- <div class="meta">
- {% if item.icon %}
- <div class="icon-wrapper">
- <img class="icon no-lightense" src="{{ item.icon }}" alt="icon" width="16" height="16">
- </div>
- {% endif %}
- {% if item.link %}
- <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener">{{ item.title }}</a>
- {% else %}
- <div class="title">{{ item.title }}</div>
- {% endif %}
- {% if item.date %}
- <div class="date">{{ item.date }}</div>
- {% endif %}
- </div>
- {% if item.subtitle %}
- <div class="subtitle">{{ item.subtitle }}</div>
- {% endif %}
- <div class="content">{{ item.content | trim | markdown | safe }}</div>
- {% if item.tags %}
- <div class="tags">
- {% for tag in item.tags %}
- <div><span>#</span>{{ tag }}</div>
- {% endfor %}
- </div>
- {% endif %}
-</blockquote>
-{% endmacro %}
-
-
-{% macro card_simple(item) %}
-<blockquote class="collection card-simple{% if item.featured %} featured{% endif %}">
- <div class="meta">
- {% if item.icon %}
- <div class="icon-wrapper">
- <img class="icon no-lightense" src="{{ item.icon }}" alt="icon" width="16" height="16">
- </div>
- {% endif %}
- {% if item.link %}
- <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener">{{ item.title }}</a>
- {% else %}
- <div class="title">{{ item.title }}</div>
- {% endif %}
- <div class="content">{{ item.content | trim | markdown | safe }}</div>
- {% if item.date %}
- <div class="date">{{ item.date }}</div>
- {% endif %}
- </div>
- <div class="content-narrow">{{ item.content | trim | markdown | safe }}</div>
-</blockquote>
-{% endmacro %}
-
-
-{% macro entry(item) %}
-{% if item.link %}
-<a href="{{ item.link }}" target="_blank" rel="noreferrer noopener" class="collection entry">
-{% else %}
-<blockquote class="collection entry">
-{% endif %}
- {% if item.icon %}
- <div class="icon-wrapper">
- <img class="icon no-lightense" src="{{ item.icon }}" alt="icon" width="16" height="16" />
- </div>
- {% endif %}
- {% if item.title or item.subtitle %}
- <div class="text">
- {% if item.title %}
- <div class="title">{{ item.title }}</div>
- {% endif %}
- {% if item.subtitle %}
- <div class="subtitle">{{ item.subtitle }}</div>
- {% endif %}
- </div>
- {% endif %}
-{% if item.link %}
-</a>
-{% else %}
-</blockquote>
-{% endif %}
-{% endmacro %}
-
-
-{% macro box(item) %}
-<div class="collection-box-wrapper">
-{% if item.link %}
-<a href="{{ item.link }}" target="_blank" rel="noreferrer noopener" class="collection box">
-{% else %}
-<blockquote class="collection box">
-{% endif %}
- <div class="text">
- <div class="title">{{ item.title }}</div>
- {% if item.subtitle %}
- <div class="subtitle">{{ item.subtitle }}</div>
- {% endif %}
- </div>
- {% if item.img %}
- <img class="no-lightense{% if item.rotate %} rotate{% endif %}" src="{{ item.img }}" alt="{{ item.title }}" width="48" height="48" />
- {% else %}
- <div class="placehold"></div>
- {% endif %}
-{% if item.link %}
-</a>
-{% else %}
-</blockquote>
-{% endif %}
-</div>
-{% endmacro %}
-
-
-{% macro art(item) %}
-<blockquote class="collection art">
- <div class="img-wrapper">
- <img src="{{ item.img }}" alt="{{ item.title }}" height="175">
- </div>
- <div class="text">
- {% if item.link %}
- <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener" >{{ item.title }}</a>
- {% else %}
- <div class="title">{{ item.title }}</div>
- {% endif %}
- {% if item.subtitle %}
- <div class="subtitle">{{ item.subtitle }}</div>
- {% endif %}
- {% if item.content %}
- <div class="content">{{ item.content }}</div>
- {% endif %}
- {% if item.footer %}
- <div class="footer">{{ item.footer }}</div>
- {% endif %}
- </div>
-</blockquote>
-{% endmacro %}
-
-
-{% macro art_simple(item) %}
-<blockquote class="collection art-simple">
- <div class="img-wrapper">
- <img src="{{ item.img }}" alt="{{ item.title }}" height="175">
- </div>
- <div class="text">
- {% if item.link %}
- <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener" >{{ item.title }}</a>
- {% else %}
- <div class="title">{{ item.title }}</div>
- {% endif %}
- {% if item.subtitle %}
- <div class="subtitle">{{ item.subtitle }}</div>
- {% endif %}
- </div>
-</blockquote>
-{% endmacro %}
diff --git a/src/themes/serene/templates/macros/prose.html b/src/themes/serene/templates/macros/prose.html
deleted file mode 100644
index bd9a894..0000000
--- a/src/themes/serene/templates/macros/prose.html
+++ /dev/null
@@ -1,32 +0,0 @@
-{% macro back_link(path) %}
-<header>
- <nav>
- <a href="{{ path }}">← {{ config.extra.back_link_text }}</a>
- </nav>
-</header>
-{% endmacro %}
-
-
-{% macro callout(name) %}
-<blockquote class="callout {{ name }} {% if title %}has-title{% else %}no-title{% endif %}">
- {% set icon = load_data(path="icon/" ~ name ~ ".svg") %}
- {% if title %}
- <p class="title">
- <span class="icon">
- {{ icon | safe }}
- </span>
- <strong>{{ title }}</strong>
- </p>
- <div class="content">
- {{ body | markdown | safe }}
- </div>
- {% else %}
- <div class="icon">
- {{ icon | safe }}
- </div>
- <div class="content">
- {{ body | markdown | safe }}
- </div>
- {% endif %}
-</blockquote>
-{% endmacro %}
diff --git a/src/themes/serene/templates/post.html b/src/themes/serene/templates/post.html
index 24fe0cd..07f9782 100644
--- a/src/themes/serene/templates/post.html
+++ b/src/themes/serene/templates/post.html
@@ -1,15 +1,13 @@
-{% import "macros/prose.html" as macros %}
{% extends "_base.html" %}
{% block page %}post{% endblock page %}
{% block lang -%}
-{%- set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") -%}
-{%- set section_md_path = blog_section_path ~ "/_index.md" -%}
-{%- set section = get_section(path=section_md_path, metadata_only=true) -%}
+{%- set section = get_section(path=page.ancestors | last, metadata_only=true) -%}
{%- if page.extra.lang %}{{page.extra.lang}}{% elif section.extra.lang %}{{section.extra.lang}}{% else %}{{page.lang}}{% endif -%}
{%- endblock lang %}
{% block title %}{{ page.title }}{% endblock title %}
{% block desc %}
+ {%- set section = get_section(path=page.ancestors | last, metadata_only=true) -%}
{% if page.summary %}
{% set desc = page.summary %}
{% elif page.description %}
@@ -19,18 +17,30 @@
{% else %}
{% set desc = config.description %}
{% endif %}
- <meta name="description" content="{{ desc }}">
+ {%- if page.extra.lang %}{% set og_lang = page.extra.lang %}{% elif section.extra.lang %}{% set og_lang = section.extra.lang %}{% else %}{% set og_lang = page.lang %}{% endif -%}
+ {{ <seo config
+ title={page.title}
+ desc={desc}
+ url={page.permalink}
+ page_type="article"
+ image={page.extra?.og_image or ""}
+ colocated_path={page.colocated_path or ""}
+ published={page.date or ""}
+ updated={page.updated or ""}
+ tags={page.taxonomies?.tags or []}
+ lang={og_lang} /> }}
{% endblock desc %}
{% block head %}
-{% if config.markdown.highlight_theme == "css" %}
-<link id="hl" rel="stylesheet" type="text/css" href="/hl-{% if config.extra.force_theme == "dark" %}dark{% else %}light{% endif %}.css" />
+{% if config.markdown.highlighting.style == "class" %}
+<link id="hl" rel="stylesheet" type="text/css" href="/giallo-{% if color_scheme == "dark" %}dark{% else %}light{% endif %}.css" />
{% endif %}
-{% if page.extra.math %}
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js" integrity="sha384-HORx6nWi8j5/mYA+y57/9/CZc5z8HnEw4WUZWy5yOn9ToKBv1l58vJaufFAn9Zzi" crossorigin="anonymous"></script>
+{% set enable_math = page.extra.math if page.extra.math is defined else (section.extra.math if section.extra.math is defined else (config.extra.math if config.extra.math is defined else false)) %}
+{% if enable_math %}
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/katex.min.css" integrity="sha384-M59ezskvvpvT+a+C1x088YJ3DVmK+wZdX0UkVKalOI4Qi5Nwv0WrvpqHcfa2HQqB" crossorigin="anonymous">
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/katex.min.js" integrity="sha384-7jGyG5zFwmEamqNWdCbpsPn+GTWEis3lnV7X/jXHyhFpJG7ExABLyMapabg8F4+p" crossorigin="anonymous"></script>
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/contrib/auto-render.min.js" integrity="sha384-bjyGPfbij8/NDKJhSGZNP/khQVgtHUE5exjm4Ydllo42FwIgYsdLO2lXGmRBf5Mz" crossorigin="anonymous"></script>
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/contrib/copy-tex.min.js" integrity="sha384-Pe2JWbaShhSRT0SESJ9XLPeAsi4yrNi7r/CiH0Coq7giBjK5a6Ae7XcybE8rNlQP" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
@@ -51,7 +61,7 @@
<div id="wrapper">
<div id="blank"></div>
<aside>
- {% if page.extra.toc is defined %}{% set show_toc = page.extra.toc %}{% else %}{% set show_toc = section.extra.toc %}{% endif %}
+ {% set show_toc = page.extra.toc if page.extra.toc is defined else (section.extra.toc if section.extra.toc is defined else (config.extra.toc if config.extra.toc is defined else false)) %}
{% if show_toc and page.toc %}
<nav>
<ul>
@@ -80,10 +90,10 @@
{% endif %}
</aside>
<main>
- {{ macros::back_link(path = get_url(path=section.path)) }}
+ {{ <back_link path={get_url(path=section.path)} config /> }}
<div>
- {% if page.extra.copy is defined %}{% set allow_copy = page.extra.copy %}{% else %}{% set allow_copy = section.extra.copy %}{% endif %}
+ {% set allow_copy = page.extra.code_copy if page.extra.code_copy is defined else (section.extra.code_copy if section.extra.code_copy is defined else (config.extra.code_copy if config.extra.code_copy is defined else false)) %}
{% if allow_copy %}
{% set copy_icon = load_data(path="icon/copy.svg") %}
{% set check_icon = load_data(path="icon/check.svg") %}
@@ -91,31 +101,33 @@
{% endif %}
<article class="prose">
<h1>{{ page.title }}</h1>
+ {% set date_format = section.extra.date_format if section.extra.date_format is defined else (config.extra.date_format if config.extra.date_format is defined else "%b %-d, %Y") %}
<div id="post-info">
<div id="date">
- <span id="publish">{{ page.date | date(format=section.extra.date_format) }}</span>
+ <span id="publish">{{ page.date | date(format=date_format) }}</span>
{% if page.updated and page.updated != page.date -%}
- <span>Updated on <span id="updated">{{ page.updated | date(format=section.extra.date_format) }}</span></span>
+ <span>Updated on <span id="updated">{{ page.updated | date(format=date_format) }}</span></span>
{% endif -%}
</div>
{% if page.taxonomies.tags is defined %}
<div id="tags">
{% for tag in page.taxonomies.tags -%}
- {% set tag_slugify = tag | slugify -%}
- <a class="instant" href="{{ config.base_url ~ '/tags/' ~ tag_slugify | safe }}"><span>#</span>{{ tag }}</a>
+ <a class="instant" href="{{ get_taxonomy_url(kind="tags", term=tag) }}"><span>#</span>{{ tag }}</a>
{%- endfor %}
</div>
{% endif %}
</div>
- {% if page.extra.outdate_alert is defined %}{% set show_outdate_alert = page.extra.outdate_alert %}{% else %}{% set show_outdate_alert = section.extra.outdate_alert %}{% endif %}
- {% if page.extra.outdate_alert_days is defined %}{% set outdate_alert_days = page.extra.outdate_alert_days %}{% else %}{% set outdate_alert_days = section.extra.outdate_alert_days %}{% endif %}
+ {% set show_outdated_alert = page.extra.outdated_alert if page.extra.outdated_alert is defined else (section.extra.outdated_alert if section.extra.outdated_alert is defined else (config.extra.outdated_alert if config.extra.outdated_alert is defined else false)) %}
+ {% set outdated_alert_days = page.extra.outdated_alert_days if page.extra.outdated_alert_days is defined else (section.extra.outdated_alert_days if section.extra.outdated_alert_days is defined else (config.extra.outdated_alert_days if config.extra.outdated_alert_days is defined else 120)) %}
- {% if show_outdate_alert -%}
- <blockquote id="outdate_alert" class="callout caution hidden" data-days="{{ outdate_alert_days }}"
- data-alert-text-before="{{ section.extra.outdate_alert_text_before }}"
- data-alert-text-after="{{ section.extra.outdate_alert_text_after }}">
+ {% if show_outdated_alert -%}
+ {% set outdated_alert_text_before = section.extra.outdated_alert_text_before if section.extra.outdated_alert_text_before is defined else (config.extra.outdated_alert_text_before if config.extra.outdated_alert_text_before is defined else "") %}
+ {% set outdated_alert_text_after = section.extra.outdated_alert_text_after if section.extra.outdated_alert_text_after is defined else (config.extra.outdated_alert_text_after if config.extra.outdated_alert_text_after is defined else "") %}
+ <blockquote id="outdate_alert" class="markdown-alert-caution hidden" data-days="{{ outdated_alert_days }}"
+ data-alert-text-before="{{ outdated_alert_text_before }}"
+ data-alert-text-after="{{ outdated_alert_text_after }}">
<div class="content"></div>
</blockquote>
{% endif %}
@@ -123,12 +135,12 @@
{{ page.content | safe }}
</article>
- {% if page.extra.reaction is defined %}{% set show_reaction = page.extra.reaction %}{% else %}{% set show_reaction = config.extra.reaction %}{% endif %}
+ {% set show_reaction = page.extra.reaction if page.extra.reaction is defined else (section.extra.reaction if section.extra.reaction is defined else (config.extra.reaction if config.extra.reaction is defined else false)) %}
{% if show_reaction %}
<div class="reaction {{ config.extra.reaction_align }}" data-endpoint="{{ config.extra.reaction_endpoint }}"></div>
{% endif %}
- {% if page.extra.comment is defined %}{% set show_comment = page.extra.comment %}{% else %}{% set show_comment = section.extra.comment %}{% endif %}
+ {% set show_comment = page.extra.comment if page.extra.comment is defined else (section.extra.comment if section.extra.comment is defined else (config.extra.comment if config.extra.comment is defined else false)) %}
{% if show_comment %}
<div class="giscus"></div>
{% include "_giscus_script.html" %}
@@ -142,9 +154,10 @@
{% block script %}
<script src="/js/lightense.min.js"></script>
-{% if page.extra.mermaid %}
+{% set enable_mermaid = page.extra.mermaid if page.extra.mermaid is defined else (section.extra.mermaid if section.extra.mermaid is defined else (config.extra.mermaid if config.extra.mermaid is defined else false)) %}
+{% if enable_mermaid %}
<script type="module">
- import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11.17.2/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
{% endif %}
diff --git a/src/themes/serene/templates/blog.html b/src/themes/serene/templates/posts.html
index 2519d28..9b3dec1 100644
--- a/src/themes/serene/templates/blog.html
+++ b/src/themes/serene/templates/posts.html
@@ -1,4 +1,3 @@
-{% import "macros/prose.html" as macros %}
{% extends "_base.html" %}
{% block page %}blog{% endblock page%}
@@ -10,26 +9,35 @@
{% else %}
{% set desc = config.description %}
{% endif %}
- <meta name="description" content="{{ desc }}">
+ {%- if section.extra.lang %}{% set og_lang = section.extra.lang %}{% else %}{% set og_lang = section.lang %}{% endif -%}
+ {{ <seo config
+ title={section.title}
+ desc={desc}
+ url={section.permalink}
+ lang={og_lang} /> }}
{% endblock desc %}
{% block content %}
<div id="wrapper">
- {{ macros::back_link(path = get_url(path="/")) }}
+ {{ <back_link path={get_url(path="/")} config /> }}
{% include "_section_title.html" %}
<main class="layout-list">
+ {% set date_format = section.extra.date_format if section.extra.date_format is defined else (config.extra.date_format if config.extra.date_format is defined else "%b %-d, %Y") %}
{% if section.extra.categorized %}
- {% for category,posts in section.pages | sort(attribute="taxonomies.categories.0") | group_by(attribute="taxonomies.categories.0") %}
+ {% set categorized_posts = [post for post in section.pages if post.taxonomies?.categories] %}
+ {% set categories = [post.taxonomies.categories[0] for post in categorized_posts] | unique | sort %}
+ {% for category in categories %}
+ {% set posts = [post for post in categorized_posts if post.taxonomies.categories[0] == category] %}
{% set category_name = category %}
- {% if category is matching("^__[0-9]{2}__") %}
- {% set category_name = category | split(pat="") | slice(start=7) | join(sep="") %}
+ {% if category is matching(pat="^__[0-9]{2}__") %}
+ {% set category_name = category[6:] %}
{% endif %}
<h2 class="category">{{ category_name }}</h2>
<div class="post-list categorized">
{% for post in posts %}
- <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}">
+ <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink }}">
<span>{{ post.title }}</span>
- <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span>
+ <span class="date">{{ post.date | date(format=date_format) }}</span>
</a>
{% endfor %}
</div>
@@ -37,9 +45,9 @@
{% else %}
<div class="post-list">
{% for post in section.pages %}
- <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}">
+ <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink }}">
<span>{{ post.title }}</span>
- <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span>
+ <span class="date">{{ post.date | date(format=date_format) }}</span>
</a>
{% endfor %}
</div>
diff --git a/src/themes/serene/templates/prose.html b/src/themes/serene/templates/prose.html
index 661c408..114ff16 100644
--- a/src/themes/serene/templates/prose.html
+++ b/src/themes/serene/templates/prose.html
@@ -1,4 +1,3 @@
-{% import "macros/prose.html" as macros %}
{% extends "_base.html" %}
{% block page %}prose-page{% endblock page %}
@@ -12,18 +11,24 @@
{% else %}
{% set desc = config.description %}
{% endif %}
- <meta name="description" content="{{ desc }}">
+ {%- if section.extra.lang %}{% set og_lang = section.extra.lang %}{% else %}{% set og_lang = section.lang %}{% endif -%}
+ {{ <seo config
+ title={section.title}
+ desc={desc}
+ url={section.permalink}
+ lang={og_lang} /> }}
{% endblock desc %}
{% block head %}
-{% if config.markdown.highlight_theme == "css" %}
-<link id="hl" rel="stylesheet" type="text/css" href="/hl-{% if config.extra.force_theme == "dark" %}dark{% else %}light{% endif %}.css" />
+{% if config.markdown.highlighting.style == "class" %}
+<link id="hl" rel="stylesheet" type="text/css" href="/giallo-{% if color_scheme == "dark" %}dark{% else %}light{% endif %}.css" />
{% endif %}
-{% if section.extra.math %}
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js" integrity="sha384-HORx6nWi8j5/mYA+y57/9/CZc5z8HnEw4WUZWy5yOn9ToKBv1l58vJaufFAn9Zzi" crossorigin="anonymous"></script>
+{% set enable_math = section.extra.math if section.extra.math is defined else (config.extra.math if config.extra.math is defined else false) %}
+{% if enable_math %}
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/katex.min.css" integrity="sha384-M59ezskvvpvT+a+C1x088YJ3DVmK+wZdX0UkVKalOI4Qi5Nwv0WrvpqHcfa2HQqB" crossorigin="anonymous">
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/katex.min.js" integrity="sha384-7jGyG5zFwmEamqNWdCbpsPn+GTWEis3lnV7X/jXHyhFpJG7ExABLyMapabg8F4+p" crossorigin="anonymous"></script>
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/contrib/auto-render.min.js" integrity="sha384-bjyGPfbij8/NDKJhSGZNP/khQVgtHUE5exjm4Ydllo42FwIgYsdLO2lXGmRBf5Mz" crossorigin="anonymous"></script>
+<script defer src="https://cdn.jsdelivr.net/npm/katex@0.18.6/dist/contrib/copy-tex.min.js" integrity="sha384-Pe2JWbaShhSRT0SESJ9XLPeAsi4yrNi7r/CiH0Coq7giBjK5a6Ae7XcybE8rNlQP" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
@@ -43,10 +48,11 @@
{% block content %}
<div id="wrapper">
<main>
- {{ macros::back_link(path = get_url(path="/")) }}
+ {{ <back_link path={get_url(path="/")} config /> }}
{% include "_section_title.html" %}
<div>
- {% if section.extra.copy %}
+ {% set allow_copy = section.extra.code_copy if section.extra.code_copy is defined else (config.extra.code_copy if config.extra.code_copy is defined else false) %}
+ {% if allow_copy %}
{% set copy_icon = load_data(path="icon/copy.svg") %}
{% set check_icon = load_data(path="icon/check.svg") %}
<div id="copy-cfg" style="display: none;" data-copy-icon="{{ copy_icon }}" data-check-icon="{{ check_icon }}"></div>
@@ -55,12 +61,13 @@
{{ section.content | safe }}
</article>
- {% if section.extra.reaction is defined %}{% set show_reaction = section.extra.reaction %}{% else %}{% set show_reaction = config.extra.reaction %}{% endif %}
+ {% set show_reaction = section.extra.reaction if section.extra.reaction is defined else (config.extra.reaction if config.extra.reaction is defined else false) %}
{% if show_reaction %}
<div class="reaction {{ config.extra.reaction_align }}" data-endpoint="{{ config.extra.reaction_endpoint }}"></div>
{% endif %}
- {% if section.extra.comment %}
+ {% set show_comment = section.extra.comment if section.extra.comment is defined else (config.extra.comment if config.extra.comment is defined else false) %}
+ {% if show_comment %}
<div class="giscus"></div>
{% include "_giscus_script.html" %}
{% endif %}
@@ -73,9 +80,10 @@
{% block script %}
<script src="/js/lightense.min.js"></script>
-{% if section.extra.mermaid %}
+{% set enable_mermaid = section.extra.mermaid if section.extra.mermaid is defined else (config.extra.mermaid if config.extra.mermaid is defined else false) %}
+{% if enable_mermaid %}
<script type="module">
- import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11.17.2/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
{% endif %}
diff --git a/src/themes/serene/templates/shortcodes/caution.html b/src/themes/serene/templates/shortcodes/caution.html
deleted file mode 100644
index 7d1797f..0000000
--- a/src/themes/serene/templates/shortcodes/caution.html
+++ /dev/null
@@ -1,2 +0,0 @@
-{% import "macros/prose.html" as macros %}
-{{ macros::callout(name="caution") }}
diff --git a/src/themes/serene/templates/shortcodes/collection.html b/src/themes/serene/templates/shortcodes/collection.html
deleted file mode 100644
index c217a0c..0000000
--- a/src/themes/serene/templates/shortcodes/collection.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% import 'macros/collection.html' as marcos -%}
-
-{% set data = load_data(path="content" ~ section.path ~ file, format="toml") %}
-
-<section class="collection-wrapper">
-{% for item in data.collection %}
- {% if item.type == "card" %}
- {{ marcos::card(item=item) }}
- {% elif item.type == "card_simple" %}
- {{ marcos::card_simple(item=item) }}
- {% elif item.type == "entry" %}
- {{ marcos::entry(item=item) }}
- {% elif item.type == "box" %}
- {{ marcos::box(item=item) }}
- {% elif item.type == "art" %}
- {{ marcos::art(item=item) }}
- {% elif item.type == "art_simple" %}
- {{ marcos::art_simple(item=item) }}
- {% elif item.type == "br" %}
- <br />
- {% endif %}
-{% endfor %}
-</section>
diff --git a/src/themes/serene/templates/shortcodes/detail.html b/src/themes/serene/templates/shortcodes/detail.html
deleted file mode 100644
index 0d34a74..0000000
--- a/src/themes/serene/templates/shortcodes/detail.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<details {% if default_open %}open{% endif %}>
- <summary><span>{{ title }}</span></summary>
- {{ body | markdown | safe }}
-</details>
diff --git a/src/themes/serene/templates/shortcodes/figure.html b/src/themes/serene/templates/shortcodes/figure.html
deleted file mode 100644
index 8a4123f..0000000
--- a/src/themes/serene/templates/shortcodes/figure.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<figure>
- <img src="{{ src }}" {% if alt %} alt="{{ alt }}"{% endif %}>
- {% if via %}
- <figcaption><a href="{{via}}">via</a></figcaption>
- {% else %}
- <figcaption>{{ caption }}</figcaption>
- {% endif %}
-</figure> \ No newline at end of file
diff --git a/src/themes/serene/templates/shortcodes/important.html b/src/themes/serene/templates/shortcodes/important.html
deleted file mode 100644
index 451c141..0000000
--- a/src/themes/serene/templates/shortcodes/important.html
+++ /dev/null
@@ -1,2 +0,0 @@
-{% import "macros/prose.html" as macros %}
-{{ macros::callout(name="important") }}
diff --git a/src/themes/serene/templates/shortcodes/mermaid.html b/src/themes/serene/templates/shortcodes/mermaid.html
deleted file mode 100644
index 3d3725d..0000000
--- a/src/themes/serene/templates/shortcodes/mermaid.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<pre class="mermaid">
- {{ body }}
-</pre> \ No newline at end of file
diff --git a/src/themes/serene/templates/shortcodes/note.html b/src/themes/serene/templates/shortcodes/note.html
deleted file mode 100644
index b16657b..0000000
--- a/src/themes/serene/templates/shortcodes/note.html
+++ /dev/null
@@ -1,2 +0,0 @@
-{% import "macros/prose.html" as macros %}
-{{ macros::callout(name="note") }}
diff --git a/src/themes/serene/templates/shortcodes/quote.html b/src/themes/serene/templates/shortcodes/quote.html
deleted file mode 100644
index c7e3c37..0000000
--- a/src/themes/serene/templates/shortcodes/quote.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<blockquote class="quote">
- {% set icon = load_data(path="icon/quote.svg") %}
- <div class="icon" style="display: none;">{{ icon | safe }}</div>
- <div class="content">{{ body | markdown | safe }}</div>
- {% if cite %}
- <div class="from">
- {{ "— " ~ cite | markdown | safe }}
- </div>
- {% endif %}
-</blockquote> \ No newline at end of file
diff --git a/src/themes/serene/templates/shortcodes/tip.html b/src/themes/serene/templates/shortcodes/tip.html
deleted file mode 100644
index 8904437..0000000
--- a/src/themes/serene/templates/shortcodes/tip.html
+++ /dev/null
@@ -1,2 +0,0 @@
-{% import "macros/prose.html" as macros %}
-{{ macros::callout(name="tip") }}
diff --git a/src/themes/serene/templates/shortcodes/warning.html b/src/themes/serene/templates/shortcodes/warning.html
deleted file mode 100644
index 0964b15..0000000
--- a/src/themes/serene/templates/shortcodes/warning.html
+++ /dev/null
@@ -1,2 +0,0 @@
-{% import "macros/prose.html" as macros %}
-{{ macros::callout(name="warning") }}
diff --git a/src/themes/serene/templates/tags/list.html b/src/themes/serene/templates/tags/list.html
index bde1123..e2d48d7 100644
--- a/src/themes/serene/templates/tags/list.html
+++ b/src/themes/serene/templates/tags/list.html
@@ -1,26 +1,32 @@
-{% import "macros/prose.html" as macros %}
{% extends "_base.html" %}
{% block page %}tag-list{% endblock page%}
{% block lang -%}
-{% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %}
+{% set blog_section_path = config.extra.blog_section_path | trim_start(pat="/") %}
{% set section_md_path = blog_section_path ~ "/_index.md"%}
{% set section = get_section(path=section_md_path, metadata_only=true) %}
{%- if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ lang }}{% endif -%}
{%- endblock lang %}
{% block title %}Tags{% endblock title %}
{% block desc %}
- <meta name="description" content="Tags">
+ {%- set blog_section_path = config.extra.blog_section_path | trim_start(pat="/") -%}
+ {%- set section = get_section(path=blog_section_path ~ "/_index.md", metadata_only=true) -%}
+ {%- if section.extra.lang %}{% set og_lang = section.extra.lang %}{% else %}{% set og_lang = lang %}{% endif -%}
+ {{ <seo config
+ title="Tags"
+ desc="Tags"
+ url={current_url or ""}
+ lang={og_lang} /> }}
{% endblock desc %}
{% block content %}
<div id="wrapper">
- {{ macros::back_link(path = get_url(path="/")) }}
+ {{ <back_link path={get_url(path="/")} config /> }}
<main>
<h1 class="title">Tags</h1>
<div class="tags">
{% for tag in terms -%}
- <a class="instant" href="{{ tag.permalink | safe }}"># {{ tag.name | lower }}</a>
+ <a class="instant" href="{{ tag.permalink }}"># {{ tag.name | lower }}</a>
{% endfor %}
</div>
</main>
diff --git a/src/themes/serene/templates/tags/single.html b/src/themes/serene/templates/tags/single.html
index 1ce059a..ea9f360 100644
--- a/src/themes/serene/templates/tags/single.html
+++ b/src/themes/serene/templates/tags/single.html
@@ -1,30 +1,38 @@
-{% import "macros/prose.html" as macros %}
{% extends "_base.html" %}
{% block page %}tag-single{% endblock page %}
{% block lang -%}
-{% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %}
+{% set blog_section_path = config.extra.blog_section_path | trim_start(pat="/") %}
{% set section_md_path = blog_section_path ~ "/_index.md"%}
{% set section = get_section(path=section_md_path, metadata_only=true) %}
{%- if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ lang }}{% endif -%}
{%- endblock lang %}
{% block title %}{{section.title}}{% endblock title %}
{% block desc %}
- <meta name="description" content="{{ term.name }}">
+ {%- set blog_section_path = config.extra.blog_section_path | trim_start(pat="/") -%}
+ {%- set section = get_section(path=blog_section_path ~ "/_index.md", metadata_only=true) -%}
+ {%- if section.extra.lang %}{% set og_lang = section.extra.lang %}{% else %}{% set og_lang = lang %}{% endif -%}
+ {{ <seo config
+ title={section.title}
+ desc={term.name}
+ url={term.permalink}
+ lang={og_lang} /> }}
{% endblock desc %}
{% block content %}
<div id="wrapper">
- {{ macros::back_link(path = get_url(path="/tags")) }}
+ {% set tags_taxonomy = get_taxonomy(kind="tags") %}
+ {{ <back_link path={tags_taxonomy.permalink} config /> }}
<main class="layout-list">
<div class="title">
<span># {{ term.name }}</span>
</div>
<div class="post-list">
+ {% set date_format = section.extra.date_format if section.extra.date_format is defined else (config.extra.date_format if config.extra.date_format is defined else "%b %-d, %Y") %}
{% for post in term.pages %}
- <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}">
+ <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink }}">
<span>{{ post.title }}</span>
- <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span>
+ <span class="date">{{ post.date | date(format=date_format) }}</span>
</a>
{% endfor %}
</div>
diff --git a/src/themes/serene/theme.toml b/src/themes/serene/theme.toml
index 30efe2e..2d2b84a 100644
--- a/src/themes/serene/theme.toml
+++ b/src/themes/serene/theme.toml
@@ -1,8 +1,8 @@
name = "serene"
-description = "A spiffy blog theme for zola"
+description = "A minimal blog theme for zola, well crafted"
license = "MIT"
homepage = "https://github.com/isunjn/serene"
-min_version = "0.20.0"
+min_version = "0.23.4"
demo = "https://serene-demo.pages.dev"
[author]
diff --git a/src/themes/serene/zola.toml b/src/themes/serene/zola.toml
new file mode 100644
index 0000000..1c3a8e3
--- /dev/null
+++ b/src/themes/serene/zola.toml
@@ -0,0 +1,75 @@
+base_url = "https://serene-demo.pages.dev"
+title = "Serene"
+description = ""
+default_language = "en"
+# theme = "serene"
+output_dir = "public"
+compile_sass = true
+minify_html = false
+build_search_index = false
+generate_feeds = false
+feed_filenames = ["feed.xml"]
+taxonomies = [{ name = "tags" }, { name = "categories" }]
+
+[markdown]
+render_emoji = false
+external_links_target_blank = false
+external_links_no_follow = true
+external_links_no_referrer = true
+smart_punctuation = false
+github_alerts = true
+
+[markdown.highlighting]
+style = "class"
+light_theme = "github-light"
+dark_theme = "github-dark"
+
+[slugify]
+paths = "on"
+taxonomies = "on"
+anchors = "on"
+
+#=========================================================================================
+[extra]
+
+nav = [
+ { name = "posts", path = "/posts" },
+ { name = "collections", path = "/collections" },
+ { name = "about", path = "/about" },
+]
+
+name = "Serene"
+handle = "serene"
+bio = "A minimal blog theme for Zola, well crafted"
+# avatar = "img/avatar.webp"
+links = [
+ { name = "GitHub", icon = "github", url = "https://github.com/isunjn/serene" },
+ { name = "Email", icon = "email", url = "mailto:<your-email-address>" },
+]
+blog_section_path = "/posts"
+
+back_link_text = "Back"
+color_scheme = "auto" # "auto" | "light" | "dark"
+
+# og_image = "https://github.com/isunjn/serene/blob/main/screenshot.png?raw=true"
+
+footer_copyright = "© 2025 Serene Theme"
+footer_credits = true
+
+not_found_error_text = "404 Not Found"
+not_found_recover_text = "« back to home »"
+
+date_format = "%b %-d, %Y"
+toc = true
+code_copy = true
+comment = false
+math = false
+mermaid = false
+outdated_alert = false
+outdated_alert_days = 120
+outdated_alert_text_before = "This article was last updated "
+outdated_alert_text_after = " days ago and may be out of date."
+
+reaction = false
+reaction_align = "right"
+reaction_endpoint = "https://example.com/api/reaction"
diff --git a/src/themes/serene/zola.toml.example b/src/themes/serene/zola.toml.example
new file mode 100644
index 0000000..02f00fd
--- /dev/null
+++ b/src/themes/serene/zola.toml.example
@@ -0,0 +1,80 @@
+base_url = "https://example.com"
+title = "xxxx"
+description = "xxxx xxxx xxxx"
+default_language = "en"
+theme = "serene"
+output_dir = "public"
+compile_sass = true
+minify_html = false # Keep this false, as it may cause issues with some styles
+build_search_index = false # Keep this false, search is temporarily unsupported
+generate_feeds = false # Whether to generate a feed file in root, read docs for more info about rss feed
+feed_filenames = ["feed.xml"] # "feed.xml" | "atom.xml" | "rss.xml", read docs for more info
+taxonomies = [{ name = "tags" }, { name = "categories" }]
+
+[markdown]
+render_emoji = false
+external_links_target_blank = false
+external_links_no_follow = true
+external_links_no_referrer = true
+smart_punctuation = false
+github_alerts = true
+
+[markdown.highlighting]
+style = "class"
+light_theme = "github-light"
+dark_theme = "github-dark"
+
+[slugify]
+paths = "on"
+taxonomies = "on"
+anchors = "on"
+
+#=========================================================================================
+
+[extra]
+
+nav = [
+ { name = "posts", path = "/posts" },
+ # { name = "tags", path = "/tags" },
+ # { name = "github", path = "https://github.com/<your-username>" },
+]
+
+# Your info on the home page, if you don't want to display handle/bio/avatar, simply comment out that line
+name = "Jhon Wick"
+handle = "jhonwick"
+bio = "dog person, killer"
+avatar = "img/avatar.webp"
+links = [
+ { name = "GitHub", icon = "github", url = "https://github.com/<your-username>" },
+ { name = "Email", icon = "email", url = "mailto:<your-email-address>" },
+ { name = "Twitter", icon = "twitter", url = "https://twitter.com/<your-username>" },
+ { name = "Mastodon", icon = "mastodon", url = "https://mastodon.social/<your-username>", rel_me = true },
+]
+blog_section_path = "/posts" # Path of the main blog section, used by home page's recent posts and tags pages
+
+back_link_text = "Back" # Text of the back button
+color_scheme = "auto" # "auto" | "light" | "dark"
+
+# og_image = "img/og.png" # The default image for social media sharing (Open Graph), a path in static folder or a full URL
+
+footer_copyright = "© 2025 <your-name>"
+footer_credits = true # Whether to show "Built with zola and serene" in footer
+
+not_found_error_text = "404 Not Found"
+not_found_recover_text = "« back to home »"
+
+# Site-wide default values of display options, can be overridden in a section's `_index.md`, and again in a post's front-matter
+date_format = "%b %-d, %Y"
+toc = true # show table-of-contents
+code_copy = true # show copy button in code block
+comment = false # enable comment
+math = false # enable formula rendering with KaTeX
+mermaid = false # enable chart rendering with Mermaid
+outdated_alert = false # show outdate alert after certain days
+outdated_alert_days = 120
+outdated_alert_text_before = "This article was last updated "
+outdated_alert_text_after = " days ago and may be out of date."
+
+reaction = false # Whether to enable anonymous emoji reactions (Note: You need to set up a working api endpoint to enable this feature)
+reaction_align = "right" # "left" | "center" | "right"
+reaction_endpoint = "https://example.com/api/reaction"