From 6acf4ab874c58ee14f35da671029e56972745ce6 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Fri, 25 Apr 2025 12:09:21 +0200 Subject: feat(treewide): Migrate to zola --- src/themes/serene/.gitignore | 8 + src/themes/serene/CHANGELOG.md | 347 ++++ src/themes/serene/LICENSE | 21 + src/themes/serene/README.md | 10 + src/themes/serene/USAGE.md | 536 ++++++ src/themes/serene/config.example.toml | 62 + .../serene/highlight_themes/serene-dark.tmTheme | 283 +++ .../serene/highlight_themes/serene-light.tmTheme | 239 +++ src/themes/serene/sass/giscus_dark.scss | 100 ++ src/themes/serene/sass/giscus_light.scss | 104 ++ src/themes/serene/sass/main.scss | 1839 ++++++++++++++++++++ src/themes/serene/screenshot.png | Bin 0 -> 188859 bytes src/themes/serene/static/icon/arrow-up.svg | 1 + src/themes/serene/static/icon/bluesky.svg | 1 + src/themes/serene/static/icon/caution.svg | 1 + src/themes/serene/static/icon/check.svg | 1 + src/themes/serene/static/icon/copy.svg | 1 + src/themes/serene/static/icon/email.svg | 1 + src/themes/serene/static/icon/facebook.svg | 1 + src/themes/serene/static/icon/fingerprint.svg | 1 + src/themes/serene/static/icon/github.svg | 1 + src/themes/serene/static/icon/important.svg | 1 + src/themes/serene/static/icon/instagram.svg | 1 + src/themes/serene/static/icon/linkedin.svg | 1 + src/themes/serene/static/icon/mastodon.svg | 1 + src/themes/serene/static/icon/moon.svg | 1 + src/themes/serene/static/icon/note.svg | 1 + src/themes/serene/static/icon/question.svg | 1 + src/themes/serene/static/icon/quote.svg | 8 + src/themes/serene/static/icon/sun.svg | 1 + src/themes/serene/static/icon/threads.svg | 1 + src/themes/serene/static/icon/tip.svg | 1 + src/themes/serene/static/icon/twitter.svg | 1 + src/themes/serene/static/icon/warning.svg | 1 + src/themes/serene/static/js/lightense.min.js | 2 + src/themes/serene/static/js/main.js | 259 +++ src/themes/serene/templates/404.html | 14 + src/themes/serene/templates/_base.html | 38 + src/themes/serene/templates/_custom_css.html | 59 + src/themes/serene/templates/_custom_font.html | 0 src/themes/serene/templates/_footer.html | 52 + src/themes/serene/templates/_giscus_script.html | 0 src/themes/serene/templates/_head_extend.html | 0 src/themes/serene/templates/_section_title.html | 10 + src/themes/serene/templates/anchor-link.html | 1 + src/themes/serene/templates/blog.html | 50 + src/themes/serene/templates/categories/list.html | 3 + src/themes/serene/templates/categories/single.html | 3 + src/themes/serene/templates/feed.xml | 32 + src/themes/serene/templates/home.html | 119 ++ src/themes/serene/templates/macros/collection.html | 153 ++ src/themes/serene/templates/macros/prose.html | 32 + src/themes/serene/templates/post.html | 151 ++ src/themes/serene/templates/prose.html | 82 + .../serene/templates/shortcodes/caution.html | 2 + .../serene/templates/shortcodes/collection.html | 23 + src/themes/serene/templates/shortcodes/detail.html | 4 + src/themes/serene/templates/shortcodes/figure.html | 8 + .../serene/templates/shortcodes/important.html | 2 + .../serene/templates/shortcodes/mermaid.html | 3 + src/themes/serene/templates/shortcodes/note.html | 2 + src/themes/serene/templates/shortcodes/quote.html | 10 + src/themes/serene/templates/shortcodes/tip.html | 2 + .../serene/templates/shortcodes/warning.html | 2 + src/themes/serene/templates/tags/list.html | 29 + src/themes/serene/templates/tags/single.html | 34 + src/themes/serene/theme.toml | 12 + 67 files changed, 4771 insertions(+) create mode 100644 src/themes/serene/.gitignore create mode 100644 src/themes/serene/CHANGELOG.md create mode 100644 src/themes/serene/LICENSE create mode 100644 src/themes/serene/README.md create mode 100644 src/themes/serene/USAGE.md create mode 100644 src/themes/serene/config.example.toml create mode 100644 src/themes/serene/highlight_themes/serene-dark.tmTheme create mode 100644 src/themes/serene/highlight_themes/serene-light.tmTheme create mode 100644 src/themes/serene/sass/giscus_dark.scss create mode 100644 src/themes/serene/sass/giscus_light.scss create mode 100644 src/themes/serene/sass/main.scss create mode 100644 src/themes/serene/screenshot.png create mode 100644 src/themes/serene/static/icon/arrow-up.svg create mode 100644 src/themes/serene/static/icon/bluesky.svg create mode 100644 src/themes/serene/static/icon/caution.svg create mode 100644 src/themes/serene/static/icon/check.svg create mode 100644 src/themes/serene/static/icon/copy.svg create mode 100644 src/themes/serene/static/icon/email.svg create mode 100644 src/themes/serene/static/icon/facebook.svg create mode 100644 src/themes/serene/static/icon/fingerprint.svg create mode 100644 src/themes/serene/static/icon/github.svg create mode 100644 src/themes/serene/static/icon/important.svg create mode 100644 src/themes/serene/static/icon/instagram.svg create mode 100644 src/themes/serene/static/icon/linkedin.svg create mode 100644 src/themes/serene/static/icon/mastodon.svg create mode 100644 src/themes/serene/static/icon/moon.svg create mode 100644 src/themes/serene/static/icon/note.svg create mode 100644 src/themes/serene/static/icon/question.svg create mode 100644 src/themes/serene/static/icon/quote.svg create mode 100644 src/themes/serene/static/icon/sun.svg create mode 100644 src/themes/serene/static/icon/threads.svg create mode 100644 src/themes/serene/static/icon/tip.svg create mode 100644 src/themes/serene/static/icon/twitter.svg create mode 100644 src/themes/serene/static/icon/warning.svg create mode 100644 src/themes/serene/static/js/lightense.min.js create mode 100644 src/themes/serene/static/js/main.js create mode 100644 src/themes/serene/templates/404.html create mode 100644 src/themes/serene/templates/_base.html create mode 100644 src/themes/serene/templates/_custom_css.html create mode 100644 src/themes/serene/templates/_custom_font.html create mode 100644 src/themes/serene/templates/_footer.html create mode 100644 src/themes/serene/templates/_giscus_script.html create mode 100644 src/themes/serene/templates/_head_extend.html create mode 100644 src/themes/serene/templates/_section_title.html create mode 100644 src/themes/serene/templates/anchor-link.html create mode 100644 src/themes/serene/templates/blog.html create mode 100644 src/themes/serene/templates/categories/list.html create mode 100644 src/themes/serene/templates/categories/single.html create mode 100644 src/themes/serene/templates/feed.xml create mode 100644 src/themes/serene/templates/home.html create mode 100644 src/themes/serene/templates/macros/collection.html create mode 100644 src/themes/serene/templates/macros/prose.html create mode 100644 src/themes/serene/templates/post.html create mode 100644 src/themes/serene/templates/prose.html create mode 100644 src/themes/serene/templates/shortcodes/caution.html create mode 100644 src/themes/serene/templates/shortcodes/collection.html create mode 100644 src/themes/serene/templates/shortcodes/detail.html create mode 100644 src/themes/serene/templates/shortcodes/figure.html create mode 100644 src/themes/serene/templates/shortcodes/important.html create mode 100644 src/themes/serene/templates/shortcodes/mermaid.html create mode 100644 src/themes/serene/templates/shortcodes/note.html create mode 100644 src/themes/serene/templates/shortcodes/quote.html create mode 100644 src/themes/serene/templates/shortcodes/tip.html create mode 100644 src/themes/serene/templates/shortcodes/warning.html create mode 100644 src/themes/serene/templates/tags/list.html create mode 100644 src/themes/serene/templates/tags/single.html create mode 100644 src/themes/serene/theme.toml (limited to 'src/themes') diff --git a/src/themes/serene/.gitignore b/src/themes/serene/.gitignore new file mode 100644 index 0000000..87760eb --- /dev/null +++ b/src/themes/serene/.gitignore @@ -0,0 +1,8 @@ +public/ +content/ +static/img/ +static/hl-light.css +static/hl-dark.css +config.toml + +.DS_Store diff --git a/src/themes/serene/CHANGELOG.md b/src/themes/serene/CHANGELOG.md new file mode 100644 index 0000000..872470b --- /dev/null +++ b/src/themes/serene/CHANGELOG.md @@ -0,0 +1,347 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [5.2.1] - 2025-04-19 + +- fix: homepage avatar style + +## [5.2.0] - 2025-04-10 + +- ui: a few tweaks + +## [5.1.0] - 2025-04-08 + +- feat: add support for zola v0.20.0 codeblock [name annotation](https://www.getzola.org/documentation/content/syntax-highlighting/#annotations), previous `codeblock` shortcode is deprecated +- refactor: remove `static/` prefix from icon paths, eliminates the need to copy icon files [@koyokr](https://github.com/koyokr) ([#76](https://github.com/isunjn/serene/pull/76)) + +## [5.0.1] - 2025-03-31 + +- fix: external links & recent posts on the homepage [@Hiramiya](https://github.com/Hiramiya) ([#74](https://github.com/isunjn/serene/pull/74)) + +## [5.0.0] - 2025-03-20 + +> **Warning** +> +> This version is a big redesign and contains lots of breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +- New style: headerless, section title and subtitle, improved typography... +- Collections: special blocks for showcasing your list (a more general form of previous 'projects' page) +- Default icon size changed from 20 to 18 +- Some config options are moved from `config.toml` to specific `_index.md` +- Added options: `date_format` `back_link_text`, (section) `title` `subtitle`, (homepage) `footer` +- Removed options: `display_*` `nav_*` `blur_effect` `display_tags` `truncate_summary` `not_found_title` +- Callouts: `question` removed, `alert` renamed to `caution`, attribution `header` renamed to `title` +- Typst math rendering removed +- Added CSS variables: `--primary-decoration-color` `--text-decoration-color` `--highlight-mark-color` `--font-size` `--line-height` +- Removed CSS variables: `--homepage-font-size` `--homepage-line-height` `--paragraph-font-size` `--paragraph-line-height` `--aside-font-size` +- Lots of UI tweaks +- feat: Support subpath `base_url` [@b-d-e](https://github.com/b-d-e) ([#68](https://github.com/isunjn/serene/pull/68)) +- fix: `force_theme` option [@teh-banana](https://github.com/teh-banana) ([#71](https://github.com/isunjn/serene/pull/71)) +- fix: Add content-type header to reaction fetch [@sorokya](https://github.com/sorokya) ([#72](https://github.com/isunjn/serene/pull/72)) + +## [4.5.0] - 2024-11-03 + +### UI: + +- A few tweaks + +## [4.4.0] - 2024-11-02 + +### Add: + +- New feature: Anonymous emoji reactions +- New options: `display_bio` `display_avatar` `recent` + +### Remove: + +- Removed options: `homepage_layout` (use `recent` instead), `recent_more` +- Removed css variable: `--icon-size` + +### UI: + +- A few tweaks + +## [4.3.0] - 2024-10-13 + +### Add: + +- Add katex [copy-tex](https://github.com/KaTeX/KaTeX/tree/main/contrib/copy-tex) extension & bump katex version to 0.16.11 + +## [4.2.0] - 2024-10-04 + +### Fix: + +- Fix anchor link style issue, now `#` should no be present in the RSS file + +## [4.1.0] - 2024-09-16 + +### Add: + +- `force_theme` option to only use light or dark theme [@bruceoberg](https://github.com/bruceoberg) ([#62](https://github.com/isunjn/serene/issues/62)) +- A few more icons [@bruceoberg](https://github.com/bruceoberg) ([#63](https://github.com/isunjn/serene/issues/63)) + + +## [4.0.0] - 2024-08-11 + +- Deal with breaking changes of zola 19 config options: +> - Changed config options named `generate_feed` to `generate_feeds` (both in config.toml and in section front-matter) +> - Changed config option `feed_filename: String` to `feed_filenames: Vec` + +## [3.4.0] - 2024-04-25 + +### Add: + +- Math rending with [Typst](https://typst.app) [@Lambdaris](https://github.com/Lambdaris) ([#57](https://github.com/isunjn/serene/pull/57)) + +## [3.3.1] - 2024-03-10 + +### Fix: + +- Callout content overflow issue + +### UI: + +- Change highlight color of `diff` syntax +- A few tweaks + +## [3.3.0] - 2024-03-01 + +### Add: + +- New css variables: `--callout-border-radius` `--detail-border-radius` + +### Fix: + +- Overflow issue on mobile screens + +### UI: + +- Update quote icon +- A few tweaks + +## [3.2.0] - 2024-01-26 + +### Add: + +- Dark mode img/chart brightness option + +## [3.1.0] - 2024-01-20 + +### Add: + +- New shortcode: `quote` and `detail` + +### Fix: + +- Add `word-wrap: break-word` to inline code + + +## [3.0.0] - 2024-01-14 + +> **Warning** +> +> This version contains several breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +### Add: + +- `recent` homepage layout +- `featured` mark +- Add title to ToC when it's too long +- A way to sort categories +- Project item image +- prerender/prefetch when hover, using `speculationrules` or `prefetch` +- RSS mask +- A few more css variables + +### Fix: + +- Theme init logic +- Mobile sidebar ui + +### UI: + +- A few tweaks +- Default icon size set to 20 (You should re-copy the `static/icon` folder) + + +## [2.3.0] - 2024-01-09 + +### Fix: + +- `z-index` of mobile sidebar + +### UI: + +- Color change and some small tweaks + +### Remove: + +- Default custom font removed + + +## [2.2.1] - 2024-01-02 + +### Fix: + +- Use `sessionStorage` for theme init in `_base.html` + +## [2.2.0] - 2023-12-29 + +### Fix: + +- Use `sessionStorage` for theme restore +- Fix an issue when initializing giscus theme +- Hide `#` anchor link in feed file + +## [2.1.2] - 2023-09-19 + +### Fix: + +- Outdate alert not 'hidden' ([#49](https://github.com/isunjn/serene/issues/49)) + +## [2.1.1] - 2023-09-16 + +### Add: + +- Custom 404 page + +## [2.0.1] - 2023-09-13 + +### Fix: + +- Min height of prose page & post page + +## [2.0.0] - 2023-09-01 + +> **Warning** +> +> This version contains several breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +### UI: + +- Text selction now is styled +- Other minor tweaks +- Change defalut bg color of codeblock to transparent + +### Add: + +- Option `dispaly_tags` and `truncate_summary` [@woojiq](https://github.com/woojiq) ([#40](https://github.com/isunjn/serene/issues/40)) +- Support for footnote and backlink +- Active TOC indicator +- Generay `prose` section/page +- Config option `sections`, now you can rename `blog` to somthing else, e.g. `posts` +- Support for header nav fold/unfold +- Option for homepage layout, can be `about` or `list` +- A separate `_custom_css.html` for css customization + +### Fix: + +- Codeblock distance calculation +- Codeblock highlight style +- Add description tag only when it's available +- Post 3 column layout issue +- Inline code style in list item +- Link text-decoration style on mobile + + +## [1.2.0] - 2023-08-19 + +### UI: + +- Use noborder theme of giscus by default +- Post list item and callout styles changed +- Code block styles improved +- Default colors changed + +### Add: + +- Outline styles [@mrtnvgr](https://github.com/mrtnvgr) ([#26](https://github.com/isunjn/serene/pull/26)) +- Support self-host font ([#29](https://github.com/isunjn/serene/pull/29)) +- Copy button for code blocks ([#30](https://github.com/isunjn/serene/pull/30)) +- Support light/dark switch for code blocks ([#33](https://github.com/isunjn/serene/pull/33)) +- Support tags for project page +- Back-to-top button +- A shortcode for code block with file name: `codeblock` ([#39](https://github.com/isunjn/serene/pull/39)) + +### Fix: + +- Update theme toggle icon on page load [@mrtnvgr](https://github.com/mrtnvgr) ([#25](https://github.com/isunjn/serene/pull/25)) +- Layout shift problem on post page ([#27](https://github.com/isunjn/serene/pull/27)) + +## [1.1.1] - 2023-08-09 + +- Allow no tags in front matter [@mrtnvgr](https://github.com/mrtnvgr) +- Fix figcaption width issue + +## [1.1.0] - 2023-05-27 + +- Fix theme auto-toggle logic +- A few ui tweaks + +## [1.0.0] - 2023-05-24 + +> **Warning** +> The 1.0.0 version contains many breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +### Breaking + +- `config.toml` restructured, config items are renamed +- All analytics configs removed, use `_head_extend.html` instead +- All comment-support configs removed, replace with [giscus](https://giscus.app) +- Icons now using svg files +- Callout renamed: `info -> note`, `caution -> warning`, `warning -> alert` +- Callout removed: `good`, `bad`, `happy`, `unhappy`, `check`, `wrong`, `flag`, `star` +- `cc_license` removed +- Reading-progress-bar removed +- Back-to-top button removed +- Many other tweaks + +## [0.2.0] - 2022-02-16 + +### Add: +- KaTeX support +- Mermaid support + +### Fix: +- Style issue of table-of-contents +- A few non-critical bugs + +## [0.1.0] - 2022-01-14 + +First release ๐ŸŽ‰ + +[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 +[5.0.1]: https://github.com/isunjn/serene/compare/v5.0.0...v5.0.1 +[5.0.0]: https://github.com/isunjn/serene/compare/v4.5.0...v5.0.0 +[4.5.0]: https://github.com/isunjn/serene/compare/v4.4.0...v4.5.0 +[4.4.0]: https://github.com/isunjn/serene/compare/v4.3.0...v4.4.0 +[4.3.0]: https://github.com/isunjn/serene/compare/v4.2.0...v4.3.0 +[4.2.0]: https://github.com/isunjn/serene/compare/v4.1.0...v4.2.0 +[4.1.0]: https://github.com/isunjn/serene/compare/v4.0.0...v4.1.0 +[4.0.0]: https://github.com/isunjn/serene/compare/v3.4.0...v4.0.0 +[3.4.0]: https://github.com/isunjn/serene/compare/v3.3.1...v3.4.0 +[3.3.1]: https://github.com/isunjn/serene/compare/v3.3.0...v3.3.1 +[3.3.0]: https://github.com/isunjn/serene/compare/v3.2.0...v3.3.0 +[3.2.0]: https://github.com/isunjn/serene/compare/v3.1.0...v3.2.0 +[3.1.0]: https://github.com/isunjn/serene/compare/v3.0.0...v3.1.0 +[3.0.0]: https://github.com/isunjn/serene/compare/v2.3.0...v3.0.0 +[2.3.0]: https://github.com/isunjn/serene/compare/v2.2.1...v2.3.0 +[2.2.1]: https://github.com/isunjn/serene/compare/v2.2.0...v2.2.1 +[2.2.0]: https://github.com/isunjn/serene/compare/v2.1.2...v2.2.0 +[2.1.2]: https://github.com/isunjn/serene/compare/v2.1.1...v2.1.2 +[2.1.1]: https://github.com/isunjn/serene/compare/v2.0.1...v2.1.1 +[2.0.1]: https://github.com/isunjn/serene/compare/v2.0.0...v2.0.1 +[2.0.0]: https://github.com/isunjn/serene/compare/v1.2.0...v2.0.0 +[1.2.0]: https://github.com/isunjn/serene/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/isunjn/serene/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/isunjn/serene/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/isunjn/serene/compare/v0.2.0...v1.0.0 +[0.2.0]: https://github.com/isunjn/serene/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/isunjn/serene/releases/tag/v0.1.0 diff --git a/src/themes/serene/LICENSE b/src/themes/serene/LICENSE new file mode 100644 index 0000000..23f481c --- /dev/null +++ b/src/themes/serene/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present isunjn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/themes/serene/README.md b/src/themes/serene/README.md new file mode 100644 index 0000000..4fad7a7 --- /dev/null +++ b/src/themes/serene/README.md @@ -0,0 +1,10 @@ +

+ A minimal blog theme for zola, well crafted +

+ +

+ Demo ยท + Usage +

+ +Screenshot diff --git a/src/themes/serene/USAGE.md b/src/themes/serene/USAGE.md new file mode 100644 index 0000000..1b91d88 --- /dev/null +++ b/src/themes/serene/USAGE.md @@ -0,0 +1,536 @@ +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/). + +## Installation + +Create a zola site and add serene theme (assuming your site is called `myblog`): + +```sh +zola init myblog +cd myblog +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`. + +## 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. + +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. + +For home page, create `myblog/content/_index.md`: + +``` ++++ +template = 'home.html' + +[extra] +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/" }, + { name = "Twitter", icon = "twitter", url = "https://twitter.com/" }, + { name = "Email", icon = "email", url = "mailto:" }, +] + +# 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 ... +``` + +For blog section, create `myblog/content/posts/_index.md`: + +``` ++++ +title = "My Blog" +description = "My blog site." +sort_by = "date" +template = "blog.html" +page_template = "post.html" +insert_anchor_links = "right" +generate_feeds = true + +[extra] +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`: + +``` ++++ +title = "About me" +description = "About page of ..." +template = "prose.html" +insert_anchor_links = "none" + +[extra] +lang = 'en' + +title = "Posts" +subtitle = "I write about ...." + +math = false +mermaid = false +copy = false +comment = false +reaction = false ++++ + +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". + +Now the myblog directory may looks like this: + +``` +โ”œโ”€โ”€ config.toml +โ”œโ”€โ”€ content/ +โ”‚ โ”œโ”€โ”€ posts/ +โ”‚ โ”‚ โ””โ”€โ”€ _index.md +โ”‚ โ”œโ”€โ”€ about/ +โ”‚ โ”‚ โ””โ”€โ”€ _index.md +โ”‚ โ””โ”€โ”€ _index.md +โ”œโ”€โ”€ sass/ +โ”œโ”€โ”€ static/ +โ”œโ”€โ”€ templates/ +โ””โ”€โ”€ themes/ + โ””โ”€โ”€ serene/ +``` + +## Favicon + +Create a new directory `img` under `myblog/static`, put favicon related files here, you can use tools like [favicon.io](https://favicon.io/favicon-converter/) to generate those files. If you want to display avatar in home page, also put your avatar picture file `avatar.webp` here, webp format is recommended. + +``` +... +โ”œโ”€โ”€ static/ +โ”‚ โ””โ”€โ”€ img/ +โ”‚ โ”œโ”€โ”€ favicon-16x16.png +โ”‚ โ”œโ”€โ”€ favicon-32x32.png +โ”‚ โ”œโ”€โ”€ apple-touch-icon.png +โ”‚ โ””โ”€โ”€ avatar.webp +... +``` + +## 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. + +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`: + +`... width="18" height="18" ... fill="currentColor" ...` + +and then put it in `myblog/static/icon`, file in this folder with the same name will override the default one. + +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. + +## Code Highlighting + +Copy `myblog/themes/serene/highlight_themes` directory to `myblog/highlight_themes`. + +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. + +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. + +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/). + +## RSS + +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` + +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`. + +`feed.xml` uses `title` and `description` from `myblog/content/posts/_index.md`, the other two use `config.toml`'s. + +## Analytics + +To add scripts for analytics tools (such as Google Analytics, Umami, etc.), you can create `myblog/templates/_head_extend.html`. The content of this file will be added to the html head of each page. + +## Custom CSS + +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 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: + +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. +3. Replace the content of `myblog/templates/_custom_font.html` with a `` tag, with the css you just copied. +4. Download step 4 font files and put them in `myblog/static/font/` folder. + +## Front Matter + +The content inside the two `+++` at the top of the post markdown file is called front matter, used to provide metadata and config options of that post. Supported options: + +```md ++++ +title = "" +description = "" +date = 2022-01-01 +updated = 2025-01-01 +draft = true + +[taxonomies] +categories = ["one"] +tags = ["one", "two", "three"] + +[extra] +lang = "en" +toc = true +comment = false +copy = true +outdate_alert = true +outdate_alert_days = 120 +math = false +mermaid = false +featured = false +reaction = false ++++ + +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. + +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"]` + +## Table of Contents + +Set `toc = true` to display of table-of-contents. + +## Math & Chart + +Set `math = true` to enable formula rendering with KaTeX. + +Set `mermaid = true` to enable chart rendering with Mermaid. + +## Featured Mark + +Set `featured = true` to display an asterisk(*) mark in front of the title. + +## Outdate Alert + +If one of your posts has strong timeliness, you can display an outdate alert after certain days. + +Set `outdate_alert` and `outdate_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. + +## 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:///giscus_light.css`, replace `` 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`. + +Then set `comment = true` to enable comment. + +## Reaction + +This theme supports a feature called anonymous emoji reaction, visitors of you site can react to your post with emojis, without the need to log in or register. + +You need to setup a backend api endpoint to enable it. Your endpoint should handle both `GET` and `POST` request: + +- `GET` + + Request query: + + `slug`: the slug of the post + + Response: + + ```jsonc + { + "๐Ÿ‘": [123, true], // emoji: [count, reacted] + "๐Ÿ‘€": [456, false] + } + ``` + +- `POST` + + Request body: + + ```json + { + "slug": "post-slug", + "target": "๐Ÿ‘", + "reacted": true + } + ``` + + Response: + + ```json + { + "success": true + } + ``` + +For conivence, you can use one template repo to 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. + +- [mildronize/reaction](https://github.com/mildronize/reaction): Specific to [Azure](https://azure.microsoft.com/) platform. +- [sorokya/reaction](https://github.com/sorokya/reaction): A self-contained one, you can run it with docker. + +After you setup your endpoint, set `reaction_endpoint = ""` and `reaction = true` to enable it. + +Giscus also support a reaction feature, but it requires visitors to log in to GitHub, you can disable it in giscus's settings. + +## Codeblock + +Zola supports some [annotations for code blocks](https://www.getzola.org/documentation/content/syntax-highlighting/#annotations). + +## Shortcodes + +[Shortcodes](https://www.getzola.org/documentation/content/shortcodes/) are some special templates. + +- Use `figure` to add caption to the image: + + ```md + {{ figure(src="/path/to/img", alt="alt text", caption="caption text") }} + ``` + + 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: + + ```md + {{ figure(src="/path/to/img", alt="some alt text", via="https://example.com") }} + ``` + +- Use `quote` to display a special quote block, `cite` is optional: + + ```md + {% quote(cite="") %} + // content... + {% end %} + ``` + +- Use `detail` to add an expandable detail block, `default_open` is optional: + + ```md + {% 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 %} + ``` + +- Use `mermaid` to add a mermaid chart: + + ```md + {% mermaid() %} + flowchart LR + A[Hard] -->|Text| B(Round) + B --> C{Decision} + C -->|One| D[Result 1] + C -->|Two| E[Result 2] + {% end %} + ``` + +## Collection + +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. + +Currently, there are 7 types of collection item: + +- `card` + + ```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 + ``` + +- `card_simple` + + ```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 + ``` + +- `entry` + + ```toml + [[collection]] + type = "entry" + title = "Title" # optional + subtitle = "Subtitle" # optional + link = "https://example.com" # optional + icon = "https://example.com/image.png" # optional + ``` + +- `box` + + ```toml + [[collection]] + type = "box" + title = "Title" + subtitle = "Subtitle" # optional + link = "https://example.com" # optional + img = "https://example.com/image.png" # optional + ``` + +- `art` + + ```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" + ``` + + +List your items in a toml file and then use a `collection` shortcode to render them. + +For example, to create a "projects" section page: + +1. Create `myblog/content/projects/projects.toml`: + + ```toml + [[collection]] + type = "card" + 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." + tags = ["rust", "async", "runtime"] + + [[collection]] + type = "card" + 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." + 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`: + + ``` + +++ + title = "My projects" + description = "Projects page of ..." + template = "prose.html" + + [extra] + title = "Projects" + subtitle = "Some cool projects I made" + +++ + + {{ collection(file="projects.toml") }} + ``` + +3. Add projects section in `sections` of `config.toml` + + ```toml + sections = [ + # ... + { name = "projects", path = "/projects", is_external = false }, + ] + ``` + +## Build & Deploy + +Local preview: + +```sh +zola serve +``` + +Build the site: + +```sh +zola build +``` + +To deploy a static site, refer to zola's [documentation about deployment](https://www.getzola.org/documentation/deployment/overview/). + +## Update + +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. + +You can watch (`watch > custom > releases > apply`) this project on github to be reminded of a new release. + +```sh +git submodule update --remote themes/serene +``` diff --git a/src/themes/serene/config.example.toml b/src/themes/serene/config.example.toml new file mode 100644 index 0000000..e35d6c4 --- /dev/null +++ b/src/themes/serene/config.example.toml @@ -0,0 +1,62 @@ +# 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/", 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 " +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 new file mode 100644 index 0000000..f4fd49c --- /dev/null +++ b/src/themes/serene/highlight_themes/serene-dark.tmTheme @@ -0,0 +1,283 @@ + + + + + + + + + comment + http://chriskempson.com + name + Tomorrow Night + settings + + + settings + + caret + #AEAFAD + foreground + #C5C8C6 + invisibles + #4B4E55 + lineHighlight + #282A2E + selection + #373B41 + + + + name + Comment + scope + comment, string.quoted.double.block.python + settings + + foreground + #999999 + + + + name + Foreground + scope + keyword.operator.class, constant.other, source.php.embedded.line + settings + + fontStyle + + foreground + #CED1CF + + + + name + Variable, String Link, Regular Expression, Tag Name + scope + variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag + settings + + foreground + #A67878 + + + + name + Number, Constant, Function Argument, Tag Attribute, Embedded + scope + constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit + settings + + fontStyle + + foreground + #E08355 + + + + name + Class, Support + scope + type, +entity.name.class, entity.name.type.class, support.type, support.class + settings + + fontStyle + + foreground + #83aaa5 + + + + name + String, Symbols, Inherited Class, Markup Heading + scope + string, constant.other.symbol, entity.other.inherited-class, markup.heading + settings + + fontStyle + + foreground + #85AD74 + + + + name + Operator, Misc + scope + keyword.operator, constant.other.color + settings + + foreground + #83aaa5 + + + + name + Function, Special Method, Block Level + scope + entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level + settings + + fontStyle + + foreground + #81A2BE + + + + name + Keyword, Storage + scope + keyword, storage, storage.type, entity.name.tag.css + settings + + fontStyle + + foreground + #B294BB + + + + name + Invalid + scope + invalid + settings + + background + #DF5F5F + fontStyle + + foreground + #CED2CF + + + + name + Separator + scope + meta.separator + settings + + background + #82A3BF + foreground + #CED2CF + + + + name + Deprecated + scope + invalid.deprecated + settings + + background + #B798BF + fontStyle + + foreground + #CED2CF + + + + name + Diff foreground + scope + markup.inserted.diff, markup.deleted.diff, meta.diff.header.to-file, meta.diff.header.from-file + settings + + foreground + #FFFFFF + + + + name + Diff insertion + scope + markup.inserted.diff, meta.diff.header.to-file + settings + + foreground + #4baf5c + + + + name + Diff deletion + scope + markup.deleted.diff, meta.diff.header.from-file + settings + + foreground + #D46565 + + + + name + Diff header + scope + meta.diff.header.from-file, meta.diff.header.to-file + settings + + foreground + #4271ae + + + + name + Diff range + scope + meta.diff.range + settings + + fontStyle + italic + foreground + #3e999f + + + + name + diff.deleted + scope + markup.deleted + settings + + foreground + #F92672 + + + + name + diff.inserted + scope + markup.inserted + settings + + foreground + #A6E22E + + + + name + diff.changed + scope + markup.changed + settings + + foreground + #967EFB + + + + uuid + F96223EB-1A60-4617-92F3-D24D4F13DB09 + colorSpaceName + sRGB + semanticClass + theme.dark.tomorrow_night + + \ 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 new file mode 100644 index 0000000..c4b8d1b --- /dev/null +++ b/src/themes/serene/highlight_themes/serene-light.tmTheme @@ -0,0 +1,239 @@ + + + + + + + + + comment + http://chriskempson.com + name + Tomorrow + settings + + + settings + + caret + #AEAFAD + foreground + #4D4D4C + invisibles + #D1D1D1 + lineHighlight + #EFEFEF + selection + #D6D6D6 + + + + name + Comment + scope + comment, string.quoted.double.block.python + settings + + foreground + #999999 + + + + name + Foreground + scope + keyword.operator.class, constant.other, source.php.embedded.line + settings + + fontStyle + + foreground + #666969 + + + + name + Variable, String Link, Regular Expression, Tag Name + scope + variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag + settings + + foreground + #A67878 + + + + name + Number, Constant, Function Argument, Tag Attribute, Embedded + scope + constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit + settings + + fontStyle + + foreground + #E08355 + + + + name + Type, Class, Support + scope + type, +entity.name.class, entity.name.type.class, support.type, support.class + settings + + fontStyle + + foreground + #568A8F + + + + name + String, Symbols, Inherited Class, Markup Heading + scope + string, constant.other.symbol, entity.other.inherited-class, markup.heading + settings + + fontStyle + + foreground + #85AD74 + + + + name + Operator, Misc + scope + keyword.operator, constant.other.color + settings + + foreground + #568A8F + + + + name + Function, Special Method, Block Level + scope + entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level + settings + + fontStyle + + foreground + #4271AE + + + + name + Keyword, Storage + scope + keyword, storage, storage.type + settings + + fontStyle + + foreground + #8959A8 + + + + name + Invalid + scope + invalid + settings + + background + #DF5F5F + fontStyle + + foreground + #FFFFFF + + + + name + Separator + scope + meta.separator + settings + + background + #4271AE + foreground + #FFFFFF + + + + name + Deprecated + scope + invalid.deprecated + settings + + background + #8959A8 + fontStyle + + foreground + #FFFFFF + + + + name + Diff insertion + scope + markup.inserted.diff, meta.diff.header.to-file + settings + + foreground + #229545 + + + + name + Diff deletion + scope + markup.deleted.diff, meta.diff.header.from-file + settings + + foreground + #C8282966 + + + + name + Diff header + scope + meta.diff.header.from-file, meta.diff.header.to-file + settings + + foreground + #4271ae + + + + name + Diff range + scope + meta.diff.range + settings + + fontStyle + italic + foreground + #3e999f + + + + uuid + 82CCD69C-F1B1-4529-B39E-780F91F07604 + colorSpaceName + sRGB + semanticClass + theme.light.tomorrow + + \ No newline at end of file diff --git a/src/themes/serene/sass/giscus_dark.scss b/src/themes/serene/sass/giscus_dark.scss new file mode 100644 index 0000000..ea0572f --- /dev/null +++ b/src/themes/serene/sass/giscus_dark.scss @@ -0,0 +1,100 @@ +main { + --primary-default: 88, 113, 162; + --bg-default: 22, 22, 24; + --color-prettylights-syntax-comment: #8b949e; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #c9d1d9; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #c9d1d9; + --color-prettylights-syntax-markup-bold: #c9d1d9; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #c9d1d9; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-brackethighlighter-angle: #8b949e; + --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-btn-text: rgb(235 235 245 / 86%); + --color-btn-bg: rgba(var(--bg-default), 1); + --color-btn-border: rgba(var(--bg-default), 1); + --color-btn-shadow: 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-inset-shadow: inset 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-hover-bg: rgba(var(--bg-default), 0.5); + --color-btn-hover-border: rgba(var(--bg-default), 0.5); + --color-btn-active-bg: rgba(var(--primary-default), 0.2); + --color-btn-active-border: rgba(var(--primary-default), 1); + --color-btn-selected-bg: rgba(var(--primary-default), 0.15); + --color-btn-primary-text: rgb(255 255 255 / 100%); + --color-btn-primary-bg: rgba(var(--primary-default), 0.45); + --color-btn-primary-border: rgba(var(--primary-default), 0.5); + --color-btn-primary-shadow: 0 1px 0 rgb(27 31 36 / 10%); + --color-btn-primary-inset-shadow: inset 0 1px 0 hsl(0deg 0% 100% / 3%); + --color-btn-primary-hover-bg: rgba(var(--primary-default), 0.53); + --color-btn-primary-hover-border: rgba(var(--primary-default), 0.75); + --color-btn-primary-selected-bg: rgba(var(--primary-default), 0.45); + --color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%); + --color-btn-primary-disabled-text: rgb(255 255 255 / 80%); + --color-btn-primary-disabled-bg: rgba(var(--primary-default), 0.5); + --color-btn-primary-disabled-border: rgba(var(--primary-default), 0.5); + --color-action-list-item-default-hover-bg: rgb(177 186 196 / 12%); + --color-segmented-control-bg: rgb(110 118 129 / 10%); + --color-segmented-control-button-bg: #0d1117; + --color-segmented-control-button-selected-border: rgba(var(--bg-default), 0.85); + --color-fg-default: rgb(235 235 245 / 86%); + --color-fg-muted: rgb(235 235 245 / 60%); + --color-fg-subtle: rgb(235 235 245 / 50%); + --color-canvas-default: rgb(30 30 32 / 100%); + --color-canvas-overlay: rgb(30 30 32 / 100%); + --color-canvas-inset: rgba(var(--bg-default), 0.85); + --color-canvas-subtle: rgba(var(--bg-default), 1); + --color-border-default: rgba(var(--bg-default), 0.85); + --color-border-muted: rgb(175 184 193 / 20%); + --color-neutral-muted: rgb(175 184 193 / 20%); + --color-accent-fg: rgba(var(--primary-default), 0.85); + --color-accent-emphasis: rgba(var(--primary-default), 0.95); + --color-accent-muted: rgba(var(--primary-default), 0.4); + --color-accent-subtle: rgba(var(--primary-default), 0.1); + --color-success-fg: #3fb950; + --color-attention-fg: #d29922; + --color-attention-muted: rgb(187 128 9 / 40%); + --color-attention-subtle: rgb(187 128 9 / 15%); + --color-danger-fg: #f85149; + --color-danger-muted: rgb(248 81 73 / 40%); + --color-danger-subtle: rgb(248 81 73 / 10%); + --color-primer-shadow-inset: 0 1px 0 rgba(var(--bg-default), 1), inset 0 1px 0 rgba(var(--bg-default), 1); + --color-scale-gray-7: rgb(22 22 24 / 100%); + --color-scale-blue-8: rgb(16 185 129 / 15%); + + /*! Extensions from @primer/css/alerts/flash.scss */ + --color-social-reaction-bg-hover: var(--color-scale-gray-7); + --color-social-reaction-bg-reacted-hover: var(--color-scale-blue-8); +} + +main .pagination-loader-container { + background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line-dark.svg"); +} + +main .gsc-loading-image { + background-image: url("https://github.githubassets.com/images/mona-loading-dark.gif"); +} + +.gsc-comment-box-buttons a { + border-radius: 0.25rem !important; +} diff --git a/src/themes/serene/sass/giscus_light.scss b/src/themes/serene/sass/giscus_light.scss new file mode 100644 index 0000000..9433d60 --- /dev/null +++ b/src/themes/serene/sass/giscus_light.scss @@ -0,0 +1,104 @@ +main { + --primary-default: 88, 113, 162; + --bg-default: 246, 246, 247; + --color-prettylights-syntax-comment: #6e7781; + --color-prettylights-syntax-constant: #0550ae; + --color-prettylights-syntax-entity: #8250df; + --color-prettylights-syntax-storage-modifier-import: #24292f; + --color-prettylights-syntax-entity-tag: #116329; + --color-prettylights-syntax-keyword: #cf222e; + --color-prettylights-syntax-string: #0a3069; + --color-prettylights-syntax-variable: #953800; + --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; + --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; + --color-prettylights-syntax-invalid-illegal-bg: #82071e; + --color-prettylights-syntax-carriage-return-text: #f6f8fa; + --color-prettylights-syntax-carriage-return-bg: #cf222e; + --color-prettylights-syntax-string-regexp: #116329; + --color-prettylights-syntax-markup-list: #3b2300; + --color-prettylights-syntax-markup-heading: #0550ae; + --color-prettylights-syntax-markup-italic: #24292f; + --color-prettylights-syntax-markup-bold: #24292f; + --color-prettylights-syntax-markup-deleted-text: #82071e; + --color-prettylights-syntax-markup-deleted-bg: #ffebe9; + --color-prettylights-syntax-markup-inserted-text: #116329; + --color-prettylights-syntax-markup-inserted-bg: #dafbe1; + --color-prettylights-syntax-markup-changed-text: #953800; + --color-prettylights-syntax-markup-changed-bg: #ffd8b5; + --color-prettylights-syntax-markup-ignored-text: #eaeef2; + --color-prettylights-syntax-markup-ignored-bg: #0550ae; + --color-prettylights-syntax-meta-diff-range: #8250df; + --color-prettylights-syntax-brackethighlighter-angle: #57606a; + --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; + --color-prettylights-syntax-constant-other-reference-link: #0a3069; + --color-btn-text: #24292f; + --color-btn-bg: rgba(var(--bg-default), 1); + --color-btn-border: rgba(var(--bg-default), 1); + --color-btn-shadow: 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-inset-shadow: inset 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-hover-bg: rgba(var(--bg-default), 0.5); + --color-btn-hover-border: rgba(var(--bg-default), 0.5); + --color-btn-active-bg: rgba(var(--primary-default), 0.2); + --color-btn-active-border: rgba(var(--primary-default), 1); + --color-btn-selected-bg: rgba(var(--primary-default), 0.15); + --color-btn-primary-text: rgb(255 255 255 / 100%); + --color-btn-primary-bg: rgba(var(--primary-default), 1); + --color-btn-primary-border: rgba(var(--primary-default), 1); + --color-btn-primary-shadow: 0 1px 0 rgb(31 35 40 / 10%); + --color-btn-primary-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 3%); + --color-btn-primary-hover-bg: rgba(var(--primary-default), 0.9); + --color-btn-primary-hover-border: rgba(var(--primary-default), 0.75); + --color-btn-primary-selected-bg: rgba(var(--primary-default), 1); + --color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%); + --color-btn-primary-disabled-text: rgb(255 255 255 / 80%); + --color-btn-primary-disabled-bg: rgba(var(--primary-default), 0.5); + --color-btn-primary-disabled-border: rgba(var(--primary-default), 0.5); + --color-action-list-item-default-hover-bg: rgb(208 215 222 / 32%); + --color-segmented-control-bg: #eaeef2; + --color-segmented-control-button-bg: #fff; + --color-segmented-control-button-selected-border: rgba(var(--bg-default), 0.85); + --color-fg-default: rgb(60 60 67); + --color-fg-muted: rgb(60 60 67 / 75%); + --color-fg-subtle: rgb(60 60 67 / 33%); + --color-canvas-default: rgb(255 255 255); + --color-canvas-overlay: rgb(255 255 255); + --color-canvas-inset: rgba(var(--bg-default), 0.85); + --color-canvas-subtle: rgba(var(--bg-default), 1); + --color-border-default: rgba(var(--bg-default), 0.85); + --color-border-muted: rgb(175 184 193 / 20%); + --color-neutral-muted: rgb(175 184 193 / 20%); + --color-accent-fg: rgba(var(--primary-default), 0.85); + --color-accent-emphasis: rgba(var(--primary-default), 0.95); + --color-accent-muted: rgba(var(--primary-default), 0.4); + --color-accent-subtle: rgba(var(--primary-default), 0.1); + --color-success-fg: #1a7f37; + --color-attention-fg: #9a6700; + --color-attention-muted: rgb(212 167 44 / 40%); + --color-attention-subtle: #fff8c5; + --color-danger-fg: #d1242f; + --color-danger-muted: rgb(255 129 130 / 40%); + --color-danger-subtle: #ffebe9; + --color-primer-shadow-inset: 0 1px 0 rgba(var(--bg-default), 1), inset 0 1px 0 rgba(var(--bg-default), 1); + --color-scale-gray-1: rgb(234 238 242 / 100%); + --color-scale-blue-1: rgb(16 185 129 / 15%); + + /*! Extensions from @primer/css/alerts/flash.scss */ + --color-social-reaction-bg-hover: var(--color-scale-gray-1); + --color-social-reaction-bg-reacted-hover: var(--color-scale-blue-1); +} + +main .pagination-loader-container { + background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line.svg"); +} + +main .gsc-loading-image { + background-image: url("https://github.githubassets.com/images/mona-loading-default.gif"); +} + +.gsc-comment:not(.gsc-reply-box) .gsc-replies { + border-radius: unset; +} + +.gsc-comment-box-buttons a { + border-radius: 0.25rem !important; +} diff --git a/src/themes/serene/sass/main.scss b/src/themes/serene/sass/main.scss new file mode 100644 index 0000000..f5628a2 --- /dev/null +++ b/src/themes/serene/sass/main.scss @@ -0,0 +1,1839 @@ +/* base + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +*, +*::before, +*::after { + box-sizing: border-box; +} + +body { + font-family: var(--main-font); + background-color: var(--bg-color); + color: var(--text-color); + font-size: var(--font-size); + line-height: var(--line-height); + overflow-y: scroll; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +button:focus-visible, +a:focus-visible { + outline: var(--primary-color) solid 3px; +} + +::selection { + background-color: var(--primary-color); + color: var(--bg-color); +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; + background-color: transparent; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: var(--text-decoration-color); + background-clip: padding-box; + border-radius: 4px; + border: 2px solid transparent; +} +@supports not selector(::-webkit-scrollbar) { + * { + scrollbar-width: thin; + scrollbar-color: var(--text-decoration-color) transparent; + } + body { + overflow-y: auto; + } +} + +/* prose + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.prose { + h1 { + font-size: 1.15em; + font-weight: bolder; + margin: 1.5em 0 0.75em; + } + + h2 { + font-size: 1.1em; + margin: 3em 0 1.5em; + } + + h3, h4, h5, h6 { + font-size: 1em; + margin: 2.5em 0 1.5em; + } + + .zola-anchor { + visibility: hidden; + margin-left: 0.75em; + border: none; + + &::after { + content: '#'; + } + } + + h1, h2, h3, h4, h5, h6 { + width: fit-content; + &:hover a.zola-anchor { + visibility: visible !important; + } + } + + p { + font-size: 1em; + line-height: inherit; + word-wrap: break-word; + margin: 1.5em 0; + } + + a { + border-bottom: 1.5px solid var(--primary-color); + color: var(--primary-color); + text-decoration: none; + overflow-wrap: anywhere; + + @media (hover: hover) { + &:hover { + opacity: 0.9; + } + } + } + + img { + max-width: 100%; + display: block; + margin: 0 auto; + border-radius: var(--img-border-radius); + } + + figure { + margin: 0 auto; + } + + figcaption { + width: 100%; + text-align: center; + margin: 1em auto 2em; + color: var(--text-pale-color); + font-size: 0.9em; + } + + blockquote { + border-left: 1.5px var(--text-decoration-color) solid; + padding-left: 16px; + margin: 1em 0; + color: var(--text-pale-color); + + p { + margin: 1em 0; + } + } + + ol, + ul { + padding-left: 1.5em; + } + + li { + margin: 1em 0; + p { + margin: 1em 0; + } + } + + li::marker { + color: var(--primary-color); + } + + hr { + border: none; + background-color: var(--text-decoration-color); + opacity: 0.25; + height: 2px; + margin: 3em 0; + } + + table { + width: 100%; + border-spacing: 0; + border-collapse: collapse; + margin: 1.5em 0; + } + + th, + td { + line-height: 2; + text-align: center; + border: 1px solid var(--primary-color); + padding: 1px 10px; + } + + :not(pre) > code { + font-family: var(--code-font); + font-size: 0.85em; + padding: 1px 6px; + color: var(--primary-color); + background-color: var(--inline-code-bg-color); + border-radius: var(--inline-code-border-radius); + word-wrap: break-word; + } + + pre { + font-size: 0.8em; + margin: 1.25em 0; + padding: 12px 48px 12px 16px; + line-height: 1.5; + border: 1.5px solid var(--primary-color); + border-color: var(--block-code-border-color); + border-radius: var(--block-code-border-radius); + overflow: auto; + + code { + 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; + 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); + } + } + + &.mermaid { + padding-right: 16px; + border: none; + svg { + display: block; + margin: 0 auto; + } + } + } + + pre > code > mark::before { + width: calc(100% + 48px + 16px); + left: -16px; + } + + .codeblock { + margin: 1.5em 0; + position: relative; + overflow: auto; + + pre { + margin: 0; + } + pre[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 { + display: none; + z-index: 9; + position: absolute; + right: 0.6em; + top: 0.75em; + width: 24px; + height: 24px; + padding: 2px; + cursor: pointer; + background: transparent; + border: none; + color: var(--text-pale-color); + + &.copied, &:hover { + display: block; + color: var(--primary-color); + } + } + + @media (hover: hover) { + &:hover .copy { + display: block; + } + } + } + + .footnote-definition { + position: relative; + padding-left: 1.5em; + font-size: 0.9em; + margin: 3em 0 1em; + + .footnote-definition { + margin: 1em 0; + } + + .footnote-definition-label { + position: absolute; + top: 0; + left: 0; + font-size: 1em; + line-height: inherit; + vertical-align: auto; + &::after { + content: "."; + } + } + + p { + margin: 1em 0; + } + + button.backlink { + border: none; + background: none; + color: var(--primary-color); + line-height: inherit; + &:hover { + cursor: pointer; + text-decoration: underline; + } + } + } + + .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; + margin: 2em 0; + padding: 1.5em 0 0 1.25em; + color: var(--text-pale-color); + + .icon { + color: var(--text-decoration-color); + display: block !important; + position: absolute; + left: 0; + top: 0; + } + + .content > p:first-of-type { + margin-top: 0; + } + .content > p:last-of-type { + margin-bottom: 0; + } + + .from { + display: flex; + justify-content: end; + align-items: center; + gap: 0.5em; + p { + margin: 0; + } + } + } + + details { + border: 1.5px solid var(--detail-border-color); + border-radius: var(--detail-border-radius); + margin: 1em 0; + padding: 0.5em 1em; + -webkit-tap-highlight-color: transparent; + + summary span { + margin-left: 0.25em; + } + } + + .mermaid { + background: #fff; + } +} + +body.dark .prose { + img { + filter: brightness(var(--dark-mode-img-brightness)); + } + .mermaid { + filter: brightness(var(--dark-mode-chart-brightness)); + } +} + +/* prose pages + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.prose-page { + main { + min-height: 100vh; + min-height: 100dvh; + margin: 0 auto; + max-width: var(--main-max-width); + display: flex; + flex-direction: column; + justify-content: space-between; + } + + article { + padding: 0 15px; + } + + .giscus { + padding: 0 15px; + } +} + +/* collections + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.prose { + .collection-wrapper { + margin: 1.5em 0; + } + + .collection { + border: none; + margin: 0; + padding: 0; + color: var(--text-color); + img { + margin: 0; + } + a, button { + -webkit-tap-highlight-color: transparent; + } + } + + .collection.card { + margin: 3em 0; + position: relative; + + &.featured::before { + content: '*'; + position: absolute; + top: 0; + bottom: 0; + left: 0; + transform: translateX(-200%); + line-height: 2; + color: var(--primary-color); + } + @media (max-width: 768px) { + &.featured::before { + transform: translateX(-150%); + } + } + + .meta { + display: flex; + gap: 0.5em; + .icon-wrapper { + height: calc(1em * var(--line-height)); + display: flex; + align-items: center; + flex-shrink: 0; + } + .icon { + height: 16px; + width: 16px; + } + .title { + color: var(--primary-color); + } + a.title { + text-decoration: none; + border-bottom: 1.5px solid transparent; + &:hover { + opacity: unset; + border-color: var(--primary-color); + } + } + .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; + } + + .content { + font-size: 1em; + p { + 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); + + div { + 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); + } + @media (max-width: 768px) { + &.featured::before { + transform: translateX(-150%); + } + } + + .meta { + display: flex; + align-items: start; + gap: 0.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; + 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); + } + } + .content, + .content-narrow { + p { + margin: 0; + + p { + margin-top: 0.5em; + } + } + } + .content-narrow { + display: none; + margin: 0.25em 0 2em; + } + @media (max-width: 425px) { + .content { + display: none; + } + .content-narrow { + display: block; + } + } + .date { + margin-left: auto; + flex-shrink: 0; + font-size: 0.9em; + line-height: calc(var(--line-height) * 1.111); + color: var(--text-pale-color); + } + + } + + .collection.entry { + display: inline-flex; + width: fit-content; + gap: 0.5em; + padding: 4px 0; + margin: 0.25em 1.5em 0.25em 0; + + .icon-wrapper { + flex-shrink: 0; + display: flex; + align-items: center; + height: calc(1em * var(--line-height)); + } + .icon { + width: 16px; + height: 16px; + } + + .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; + } + } + + .collection.box { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid var(--primary-color); + overflow: hidden; + + img { + width: 48px; + height: 48px; + &.rotate { + transition: transform 0.15s; + transform: rotate(-20deg) translate(-2px, 12px); + } + } + .placehold { + height: 48px; + } + + .text { + min-height: 48px; + line-height: 1.5; + padding: 0 1em; + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + + .title { + font-size: 0.9em; + color: var(--text-color); + } + + .subtitle { + font-size: 0.8em; + color: var(--text-pale-color); + overflow: hidden; + text-wrap: nowrap; + text-overflow: ellipsis; + } + } + + @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); + } + } + @media (max-width: 425px) { + & { + display: flex; + width: 100%; + } + } + } + + .collection.art { + --art-h: 160px; + --art-w: 115px; + + margin: 2em 0; + display: flex; + gap: 1.5em; + + .img-wrapper { + flex-shrink: 0; + width: var(--art-w); + height: var(--art-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; + } + .footer { + margin-top: auto; + } + + @media (max-width: 425px) { + & { + --art-h: 120px; + --art-w: 85px; + gap: 0.75em; + .text { + gap: 2px; + } + .subtitle, + .footer { + font-size: 0.7em; + } + .content { + font-size: 0.8em; + -webkit-line-clamp: 2; + } + } + } + + } + + + .collection.art-simple { + --art-h: 160px; + --art-w: 115px; + + max-width: calc(50vw - 30px); + margin: 1.5em 0.75em; + display: inline-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; + } + } + .text { + max-width: calc(var(--art-w) * 1.5); + display: flex; + flex-direction: column; + align-items: 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); + } + } + + } +} + +/* layout post list ( home / blog / tag ) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.layout-list { + .category { + font-size: 0.9em; + margin: 1em 15px; + font-weight: 400; + } + + .post-list { + padding: 0 15px; + + &.categorized { + margin: 1em 0 3em 0; + } + } + + .post { + display: flex; + justify-content: space-between; + align-items: flex-end; + gap: 0.5em; + padding: 4px 0px; + margin: 6px 0; + line-height: 1.2; + text-decoration: none; + color: var(--primary-color); + + border-bottom: 1.5px solid transparent; + @media (hover: hover) { + &:hover { + border-bottom-color: var(--primary-color); + } + } + + .date { + white-space: nowrap; + } + + position: relative; + &.featured::before { + content: '*'; + position: absolute; + top: 0; + bottom: 0; + left: 0; + transform: translateX(-200%); + line-height: 2; + height: 100%; + } + @media (max-width: 768px) { + &.featured::before { + transform: translateX(-150%); + } + } + } + + .read-more { + display: flex; + justify-content: end; + margin: 1.5em 15px 0px; + + a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1.5px solid var(--primary-color); + } + } +} + +/* theme toggle + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#theme-toggle { + .sun-icon { + display: none; + } +} + +body.dark #theme-toggle { + .sun-icon { + display: initial; + } + .moon-icon { + display: none; + } +} + +/* homepage + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.homepage { + #wrapper { + min-height: 100vh; + min-height: 100dvh; + max-width: var(--homepage-max-width); + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: center; + } + + main { + width: 100%; + min-height: 75vh; + min-height: 75dvh; + padding: 4em 0; + @media (max-width: 425px) { + & { + padding: 2em 0; + } + } + } + + #info { + padding: 0 15px; + display: flex; + align-items: center; + gap: 1em; + + img { + height: var(--avatar-size); + width: var(--avatar-size); + border-radius: 50%; + } + + #text { + display: flex; + flex-direction: column; + justify-content: space-around; + line-height: 1.5; + gap: 1em; + } + + #id { + margin-left: 0.75em; + color: var(--primary-color); + } + + #bio { + color: var(--text-pale-color); + } + } + + #links { + padding: 0 15px; + margin: 2em 0 2.5em; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1em; + + a, + button { + -webkit-tap-highlight-color: transparent; + text-decoration: none; + line-height: 0; + color: var(--text-color); + + &:hover { + color: var(--primary-color); + } + } + + #left { + display: flex; + flex-wrap: wrap; + gap: 1em; + + a { + border-bottom: 1.5px solid var(--primary-color); + line-height: 1.5; + } + } + + #right { + display: flex; + gap: 0.7em; + + button { + padding: 0; + border: none; + background-color: transparent; + cursor: pointer; + } + } + + @media (max-width: 425px) { + #left { + gap: 0.75em; + } + #right { + gap: 0.5em; + } + } + } + + #brief { + padding: 0 15px; + } +} + +/* header + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +header { + font-size: 0.8em; + padding: 0 15px; + a { + -webkit-tap-highlight-color: transparent; + display: inline-block; + margin-top: 1em; + padding: 1em 1em 1em 0; + color: var(--text-pale-color); + text-decoration: none; + &:hover { + color: var(--primary-color); + } + } +} + +.section-title { + padding: 0 15px; + h1 { + font-size: 1.15em; + margin: 1.5em 0 0.75em; + } + p { + margin: 0; + margin-bottom: 1em; + font-size: 0.9em; + color: var(--text-pale-color); + } +} + +/* rss mask + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +dialog#rss-mask { + margin: 0 auto; + padding: 0px; + border: none; + overflow: visible; + background: transparent; + + &::backdrop { + background-color: initial; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + } + + div { + pointer-events: none; + height: 100vh; + height: 100dvh; + padding: 2em 0; + width: fit-content; + display: flex; + justify-content: center; + align-items: end; + flex-wrap: wrap; + gap: 1em; + font-size: 0.9em; + } + + a { + pointer-events: auto; + border-bottom: 1.5px solid var(--primary-color); + color: var(--primary-color); + text-decoration: none; + line-height: 1.5; + line-break: anywhere; + } + + button { + pointer-events: auto; + background: transparent; + border: none; + color: var(--text-pale-color); + padding: 0; + cursor: pointer; + &.copied, &:hover { + color: var(--primary-color); + } + } +} + +/* footer + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +footer { + font-size: 0.8em; + line-height: 18px; + margin-top: auto; + padding: 15px; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0 0.5em; + color: var(--text-pale-color); + + .left { + margin: 15px 0; + margin-right: auto; + display: flex; + align-items: center; + span { + padding: 0 0.5em; + } + a { + color: var(--text-pale-color); + text-decoration: none; + text-underline-offset: 4px; + &:hover { + color: var(--text-pale-color); + text-decoration: underline; + } + } + } + + .right { + display: flex; + align-items: center; + gap: 4px; + margin: 11px 0; + + #rss-btn, + #theme-toggle { + -webkit-tap-highlight-color: transparent; + border: none; + background-color: transparent; + text-decoration: none; + cursor: pointer; + color: var(--text-pale-color); + &:hover { + color: var(--primary-color); + } + } + + #rss-btn { + padding: 4px 0; + font-size: 0.85em; + line-height: 18px; + } + #theme-toggle { + padding: 4px; + transform: translateX(6px); + svg { + scale: 0.7; + } + line-height: 0; + } + } +} + +/* blog page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.blog { + #wrapper { + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + margin: 1em 0; + } +} + +/* post page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.post { + #wrapper { + display: flex; + justify-content: space-between; + } + + #blank { + order: 1; + position: sticky; + width: calc((100% - var(--main-max-width)) / 2); + } + + main { + order: 2; + width: 100%; + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + article { + padding: 0 15px 2em; + + #post-info { + display: flex; + align-items: center; + flex-wrap: wrap; + margin-bottom: 1.5em; + font-size: 0.9em; + } + + #date { + color: var(--text-pale-color); + margin-bottom: 1em; + + #publish, + #updated { + margin-right: 1em; + } + } + + #tags { + margin-bottom: 1em; + display: flex; + gap: 1em; + flex-wrap: wrap; + + a { + color: var(--primary-color); + text-decoration: none; + line-height: 1.25; + span { + font-size: 0.95em; + margin-right: 2px; + } + + border-bottom: 1.5px solid transparent; + &:hover { + opacity: 1; + border-bottom-color: var(--primary-color); + } + } + } + + #outdate_alert { + font-style: italic; + &.hidden { + display: none; + } + } + } + + .mermaid { + background: #fff; + } + + .giscus { + padding: 0 15px; + } + + aside { + order: 3; + width: calc((100% - var(--main-max-width)) / 2); + position: sticky; + margin-top: 13em; + top: 0; + height: min-content; + font-size: 0.9em; + + nav { + padding: 10px 1em 10px 2em; + min-width: 60%; + overflow-y: auto; + max-height: calc(100vh - 6em); + scrollbar-width: none; + + &::-webkit-scrollbar { + width: 0; + } + } + + ul { + list-style-type: none; + padding: 0; + line-height: 2; + margin: 0; + } + + a { + text-decoration: none; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--text-pale-color); + position: relative; + 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; + } + + &:hover { + color: var(--primary-color); + } + + &:hover::before { + background-color: var(--primary-color); + } + } + + #back-to-top { + line-height: 0; + z-index: 99; + position: fixed; + bottom: 15px; + margin-left: 1.5em; + color: var(--text-pale-color); + background: transparent; + border: none; + cursor: pointer; + padding: 15px; + transform: translateY(-5px) scale(0); + transition: transform 0.15s; + + svg { + scale: 0.8; + } + + &.shown { + transform: translateY(0px) scale(1); + } + + &:hover { + color: var(--primary-color); + } + } + } + + @media (max-width: 1024px) { + aside, #blank { + display: none; + } + } +} + +/* tag list page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.tag-list { + #wrapper { + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + margin: 0 15px; + } + + .title { + font-size: 1.15em; + margin: 1.5em 0 0.75em; + } + + .tags { + margin: 2em 0; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 1em 2em; + + a { + color: var(--primary-color); + text-decoration: none; + line-height: 1.25; + + border-bottom: 1.5px solid transparent; + &:hover { + border-bottom-color: var(--primary-color); + } + } + } + +} + +/* tag single page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.tag-single { + #wrapper { + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + width: 100%; + margin: 1em 0; + } + + .title { + font-size: 1.1em; + margin: 0.5em 15px 2em; + } +} + +/* 404 page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.not-found { + height: 100vh; + height: 100dvh; + display: flex; + justify-content: center; + align-items: center; + + .wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 2em; + margin-bottom: 6em; + } + + .error { + margin: 0; + color: var(--text-pale-color); + } + + a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1.5px solid transparent; + &:hover { + border-color: var(--primary-color); + } + } +} + +/* reaction + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.reaction { + font-size: 0.8em; + padding: 15px 15px 30px; + min-height: 75px; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + + &.left { + justify-content: start; + } + + &.right { + justify-content: end; + } + + &.center { + justify-content: center; + } + + &.error { + button { + color: var(--callout-caution-color) !important; + } + } + + button { + line-height: 1; + padding: 0.5em; + color: var(--text-pale-color); + display: flex; + justify-content: center; + align-items: center; + border: none; + gap: 4px; + background: transparent; + user-select: none; + cursor: pointer; + + span { + min-width: 16px; + } + + &:hover { + color: var(--primary-color); + } + + &.reacted { + color: var(--primary-color); + } + } +} + + +/* normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; +} + +body { + margin: 0; +} + +main { + display: block; +} + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +pre { + font-family: monospace, monospace; + font-size: 1em; +} + +a { + background-color: transparent; +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted; +} + +b, +strong { + font-weight: bolder; +} + +code, +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +img { + border-style: none; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; + appearance: button; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +textarea { + overflow: auto; +} + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + padding: 0; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + -webkit-appearance: textfield; + appearance: textfield; + outline-offset: -2px; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +details { + display: block; +} + +summary { + display: list-item; +} + +template { + display: none; +} + +[hidden] { + display: none; +} diff --git a/src/themes/serene/screenshot.png b/src/themes/serene/screenshot.png new file mode 100644 index 0000000..b824cde Binary files /dev/null and b/src/themes/serene/screenshot.png differ diff --git a/src/themes/serene/static/icon/arrow-up.svg b/src/themes/serene/static/icon/arrow-up.svg new file mode 100644 index 0000000..f2430d4 --- /dev/null +++ b/src/themes/serene/static/icon/arrow-up.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/bluesky.svg b/src/themes/serene/static/icon/bluesky.svg new file mode 100644 index 0000000..faa3039 --- /dev/null +++ b/src/themes/serene/static/icon/bluesky.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/caution.svg b/src/themes/serene/static/icon/caution.svg new file mode 100644 index 0000000..c8bb5ee --- /dev/null +++ b/src/themes/serene/static/icon/caution.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/check.svg b/src/themes/serene/static/icon/check.svg new file mode 100644 index 0000000..9e030ce --- /dev/null +++ b/src/themes/serene/static/icon/check.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/copy.svg b/src/themes/serene/static/icon/copy.svg new file mode 100644 index 0000000..e5fb178 --- /dev/null +++ b/src/themes/serene/static/icon/copy.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/email.svg b/src/themes/serene/static/icon/email.svg new file mode 100644 index 0000000..1593667 --- /dev/null +++ b/src/themes/serene/static/icon/email.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/facebook.svg b/src/themes/serene/static/icon/facebook.svg new file mode 100644 index 0000000..991d924 --- /dev/null +++ b/src/themes/serene/static/icon/facebook.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/fingerprint.svg b/src/themes/serene/static/icon/fingerprint.svg new file mode 100644 index 0000000..a15985b --- /dev/null +++ b/src/themes/serene/static/icon/fingerprint.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/github.svg b/src/themes/serene/static/icon/github.svg new file mode 100644 index 0000000..e15be38 --- /dev/null +++ b/src/themes/serene/static/icon/github.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/important.svg b/src/themes/serene/static/icon/important.svg new file mode 100644 index 0000000..b3dcb13 --- /dev/null +++ b/src/themes/serene/static/icon/important.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/instagram.svg b/src/themes/serene/static/icon/instagram.svg new file mode 100644 index 0000000..6323fa8 --- /dev/null +++ b/src/themes/serene/static/icon/instagram.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/linkedin.svg b/src/themes/serene/static/icon/linkedin.svg new file mode 100644 index 0000000..dfb0d9f --- /dev/null +++ b/src/themes/serene/static/icon/linkedin.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/mastodon.svg b/src/themes/serene/static/icon/mastodon.svg new file mode 100644 index 0000000..601640f --- /dev/null +++ b/src/themes/serene/static/icon/mastodon.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/moon.svg b/src/themes/serene/static/icon/moon.svg new file mode 100644 index 0000000..fe7bdc8 --- /dev/null +++ b/src/themes/serene/static/icon/moon.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/note.svg b/src/themes/serene/static/icon/note.svg new file mode 100644 index 0000000..6afebac --- /dev/null +++ b/src/themes/serene/static/icon/note.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/question.svg b/src/themes/serene/static/icon/question.svg new file mode 100644 index 0000000..2aaafe7 --- /dev/null +++ b/src/themes/serene/static/icon/question.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/quote.svg b/src/themes/serene/static/icon/quote.svg new file mode 100644 index 0000000..24156f7 --- /dev/null +++ b/src/themes/serene/static/icon/quote.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/themes/serene/static/icon/sun.svg b/src/themes/serene/static/icon/sun.svg new file mode 100644 index 0000000..1025381 --- /dev/null +++ b/src/themes/serene/static/icon/sun.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/threads.svg b/src/themes/serene/static/icon/threads.svg new file mode 100644 index 0000000..deccc24 --- /dev/null +++ b/src/themes/serene/static/icon/threads.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/tip.svg b/src/themes/serene/static/icon/tip.svg new file mode 100644 index 0000000..21b107a --- /dev/null +++ b/src/themes/serene/static/icon/tip.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/twitter.svg b/src/themes/serene/static/icon/twitter.svg new file mode 100644 index 0000000..5ba5a47 --- /dev/null +++ b/src/themes/serene/static/icon/twitter.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/icon/warning.svg b/src/themes/serene/static/icon/warning.svg new file mode 100644 index 0000000..0d70434 --- /dev/null +++ b/src/themes/serene/static/icon/warning.svg @@ -0,0 +1 @@ + diff --git a/src/themes/serene/static/js/lightense.min.js b/src/themes/serene/static/js/lightense.min.js new file mode 100644 index 0000000..1f979a6 --- /dev/null +++ b/src/themes/serene/static/js/lightense.min.js @@ -0,0 +1,2 @@ +/*! lightense-images v1.0.17 | ยฉ Tunghsiao Liu | MIT */ +!function (e, t) { "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.Lightense = t() : e.Lightense = t() }(this, (function () { return e = { 352: e => { function t(e, t) { var n = Object.keys(e); if (Object.getOwnPropertySymbols) { var r = Object.getOwnPropertySymbols(e); t && (r = r.filter((function (t) { return Object.getOwnPropertyDescriptor(e, t).enumerable }))), n.push.apply(n, r) } return n } function n(e) { for (var n = 1; n < arguments.length; n++) { var i = null != arguments[n] ? arguments[n] : {}; n % 2 ? t(Object(i), !0).forEach((function (t) { r(e, t, i[t]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(i)) : t(Object(i)).forEach((function (t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(i, t)) })) } return e } function r(e, t, n) { return t in e ? Object.defineProperty(e, t, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = n, e } function i(e) { return (i = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (e) { return typeof e } : function (e) { return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e })(e) } var o = function () { "use strict"; var e, t = { time: 300, padding: 40, offset: 40, keyboard: !0, cubicBezier: "cubic-bezier(.2, 0, .1, 1)", background: "var(--bg-color-80, rgba(255, 255, 255, .98))", zIndex: 1e6, beforeShow: void 0, afterShow: void 0, beforeHide: void 0, afterHide: void 0 }, r = {}; function o(e) { var t = r[e]; if (t) { if ("function" != typeof t) throw "config.".concat(e, " must be a function!"); Reflect.apply(t, r, [r]) } } function a(e) { switch (i(e)) { case "undefined": throw "You need to pass an element!"; case "string": return document.querySelectorAll(e); case "object": return e } } function c(e) { var t = e.length; if (t) for (var n = 0; n < t; n++)s(e[n]); else s(e) } function s(e) { e.src && !e.classList.contains("lightense-target") && (e.classList.add("lightense-target"), e.addEventListener("click", (function (i) { if (r.keyboard && (i.metaKey || i.ctrlKey)) return window.open(e.src, "_blank"); !function (e) { if (r.target = e, r.target.classList.contains("lightense-open")) return g(); o("beforeShow"), r.scrollY = window.scrollY, function (e, t, n) { e.addEventListener(t, (function r(i) { Reflect.apply(n, this, i), e.removeEventListener(t, r) })) }(r.target, "transitionend", (function () { o("afterShow") })); var i = new Image; i.onload = function () { !function (e) { var n = e.width, i = e.height, o = window.pageYOffset || document.documentElement.scrollTop || 0, a = window.pageXOffset || document.documentElement.scrollLeft || 0, c = r.target.getBoundingClientRect(), s = n / c.width, d = window.innerWidth || document.documentElement.clientWidth || 0, l = window.innerHeight || document.documentElement.clientHeight || 0, u = r.target.getAttribute("data-lightense-padding") || r.target.getAttribute("data-padding") || r.padding, g = d > u ? d - u : d - t.padding, p = l > u ? l - u : l - t.padding, f = n / i, b = g / p; r.scaleFactor = n < g && i < p ? s : f < b ? p / i * s : g / n * s; var h = d / 2, m = o + l / 2, v = c.left + a + c.width / 2, y = c.top + o + c.height / 2; r.translateX = Math.round(h - v), r.translateY = Math.round(m - y) }(this), function () { r.target.classList.add("lightense-open"), r.wrap = document.createElement("div"), r.wrap.className = "lightense-wrap", setTimeout((function () { r.target.style.transform = "scale(" + r.scaleFactor + ")" }), 20), r.target.parentNode.insertBefore(r.wrap, r.target), r.wrap.appendChild(r.target), setTimeout((function () { r.wrap.style.transform = "translate3d(" + r.translateX + "px, " + r.translateY + "px, 0)" }), 20); var e = { cubicBezier: r.target.getAttribute("data-lightense-cubic-bezier") || r.cubicBezier, background: r.target.getAttribute("data-lightense-background") || r.target.getAttribute("data-background") || r.background, zIndex: r.target.getAttribute("data-lightense-z-index") || r.zIndex }, t = n(n({}, r), e); d("lightense-images-css-computed", "\n :root {\n --lightense-z-index: ".concat(t.zIndex - 1, ";\n --lightense-backdrop: ").concat(t.background, ";\n --lightense-duration: ").concat(t.time, "ms;\n --lightense-timing-func: ").concat(t.cubicBezier, ";\n }")), r.container.style.visibility = "visible", setTimeout((function () { r.container.style.opacity = "1" }), 20) }(), window.addEventListener("keyup", f, !1), window.addEventListener("scroll", p, !1), r.container.addEventListener("click", g, !1) }, i.src = r.target.src }(this) }), !1)) } function d(e, t) { var n = document.head || document.getElementsByTagName("head")[0]; document.getElementById(e) && document.getElementById(e).remove(); var r = document.createElement("style"); r.id = e, r.styleSheet ? r.styleSheet.cssText = t : r.appendChild(document.createTextNode(t)), n.appendChild(r) } function l() { d("lightense-images-css", "\n:root {\n --lightense-z-index: ".concat(r.zIndex - 1, ";\n --lightense-backdrop: ").concat(r.background, ";\n --lightense-duration: ").concat(r.time, "ms;\n --lightense-timing-func: ").concat(r.cubicBezier, ";\n}\n\n.lightense-backdrop {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n position: fixed;\n top: 0;\n left: 0;\n overflow: hidden;\n z-index: calc(var(--lightense-z-index) - 1);\n padding: 0;\n margin: 0;\n transition: opacity var(--lightense-duration) ease;\n cursor: zoom-out;\n opacity: 0;\n background-color: var(--lightense-backdrop);\n visibility: hidden;\n}\n\n@supports (-webkit-backdrop-filter: blur(30px)) {\n .lightense-backdrop {\n background-color: var(--lightense-backdrop);\n -webkit-backdrop-filter: blur(30px);\n }\n}\n\n@supports (backdrop-filter: blur(30px)) {\n .lightense-backdrop {\n background-color: var(--lightense-backdrop);\n backdrop-filter: blur(30px);\n }\n}\n\n.lightense-wrap {\n position: relative;\n transition: transform var(--lightense-duration) var(--lightense-timing-func);\n z-index: var(--lightense-z-index);\n pointer-events: none;\n}\n\n.lightense-target {\n cursor: zoom-in;\n transition: transform var(--lightense-duration) var(--lightense-timing-func);\n pointer-events: auto;\n}\n\n.lightense-open {\n cursor: zoom-out;\n}\n\n.lightense-transitioning {\n pointer-events: none;\n}")) } function u() { null === document.querySelector(".lightense-backdrop") ? (r.container = document.createElement("div"), r.container.className = "lightense-backdrop", document.body.appendChild(r.container)) : r.container = document.querySelector(".lightense-backdrop") } function g() { o("beforeHide"), window.removeEventListener("keyup", f, !1), window.removeEventListener("scroll", p, !1), r.container.removeEventListener("click", g, !1), r.target.classList.remove("lightense-open"), r.wrap.style.transform = "", r.target.style.transform = "", r.target.classList.add("lightense-transitioning"), r.container.style.opacity = "", setTimeout((function () { o("afterHide"), r.container.style.visibility = "", r.container.style.backgroundColor = "", r.wrap.parentNode.replaceChild(r.target, r.wrap), r.target.classList.remove("lightense-transitioning") }), r.time) } function p() { Math.abs(r.scrollY - window.scrollY) >= r.offset && g() } function f(e) { e.preventDefault(), 27 === e.keyCode && g() } return function (i) { var o = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; e = a(i), r = n(n({}, t), o), l(), u(), c(e) } }(); e.exports = o } }, t = {}, function n(r) { var i = t[r]; if (void 0 !== i) return i.exports; var o = t[r] = { exports: {} }; return e[r](o, o.exports, n), o.exports }(352); var e, t })); \ No newline at end of file diff --git a/src/themes/serene/static/js/main.js b/src/themes/serene/static/js/main.js new file mode 100644 index 0000000..b0aee0c --- /dev/null +++ b/src/themes/serene/static/js/main.js @@ -0,0 +1,259 @@ +function enableThemeToggle() { + const themeToggle = document.querySelector('#theme-toggle'); + if (!themeToggle) return; + const hlLink = document.querySelector('link#hl'); + 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`; + sessionStorage.setItem("theme", theme); + toggleGiscusTheme(theme); + } + function toggleGiscusTheme(theme) { + const iframe = document.querySelector('iframe.giscus-frame'); + if (iframe) iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: `${location.origin}/giscus_${theme}.css` } } }, 'https://giscus.app'); + } + function initGiscusTheme(evt) { + if (evt.origin !== 'https://giscus.app') return; + if (!(typeof evt.data === 'object' && evt.data.giscus)) return; + toggleGiscusTheme(sessionStorage.getItem("theme") || (preferDark.matches ? "dark" : "light")); + window.removeEventListener('message', initGiscusTheme); + } + window.addEventListener('message', initGiscusTheme); + themeToggle.addEventListener('click', () => toggleTheme(sessionStorage.getItem("theme") == "dark" ? "light" : "dark")); + preferDark.addEventListener("change", e => toggleTheme(e.matches ? "dark" : "light")); + if (!sessionStorage.getItem("theme") && preferDark.matches) toggleTheme("dark"); + if (sessionStorage.getItem("theme") == "dark") toggleTheme("dark"); +} + +function enablePrerender() { + const prerender = (a) => { + if (!a.classList.contains('instant')) return; + const script = document.createElement('script'); + script.type = 'speculationrules'; + script.textContent = JSON.stringify({ prerender: [{ source: 'list', urls: [a.href] }] }); + document.body.append(script); + a.classList.remove('instant'); + } + const prefetch = (a) => { + if (!a.classList.contains('instant')) return; + const link = document.createElement('link'); + link.rel = 'prefetch'; + link.href = a.href; + document.head.append(link); + a.classList.remove('instant'); + } + const support = HTMLScriptElement.supports && HTMLScriptElement.supports('speculationrules'); + const handle = support ? prerender : prefetch; + document.querySelectorAll('a.instant').forEach(a => { + if (a.href.endsWith(window.location.pathname)) return; + let timer; + a.addEventListener('mouseenter', () => { + timer = setTimeout(() => handle(a), 50); + }); + a.addEventListener('mouseleave', () => clearTimeout(timer)); + a.addEventListener('touchstart', () => handle(a), { passive: true }); + }); +} + +function enableRssMask() { + const rssBtn = document.querySelector('#rss-btn'); + const mask = document.querySelector('#rss-mask'); + const copyBtn = document.querySelector('#rss-mask button'); + if (!rssBtn || !mask) return; + rssBtn.addEventListener('click', (e) => { + e.preventDefault(); + mask.showModal(); + }); + const close = (e) => { + if (e.target == mask) mask.close(); + }; + mask.addEventListener('click', close); + const copy = () => { + navigator.clipboard.writeText(copyBtn.dataset.link).then(() => { + copyBtn.innerHTML = copyBtn.dataset.checkIcon; + copyBtn.classList.add('copied'); + copyBtn.removeEventListener('click', copy); + setTimeout(() => { + mask.close(); + copyBtn.innerHTML = copyBtn.dataset.copyIcon; + copyBtn.classList.remove('copied'); + copyBtn.addEventListener('click', copy); + }, 400); + }); + } + copyBtn.addEventListener('click', copy); +} + +function enableOutdateAlert() { + const alert = document.querySelector('#outdate_alert'); + if (!alert) return; + const publish = document.querySelector('#publish'); + const updated = document.querySelector('#updated'); + const updateDate = new Date(updated ? updated.textContent : publish.textContent); + const intervalDays = Math.floor((Date.now() - updateDate.getTime()) / (24 * 60 * 60 * 1000)); + const alertDays = parseInt(alert.dataset.days); + if (intervalDays >= alertDays) { + const msg = alert.dataset.alertTextBefore + intervalDays + alert.dataset.alertTextAfter; + alert.querySelector('.content').textContent = msg; + alert.classList.remove('hidden'); + } +} + +function enableTocTooltip() { + const anchors = document.querySelectorAll('aside nav a'); + if (anchors.length == 0) return; + const toggleTooltip = () => { + anchors.forEach(anchor => { + if (anchor.offsetWidth < anchor.scrollWidth) { + anchor.setAttribute('title', anchor.textContent); + } else { + anchor.removeAttribute('title'); + } + }); + }; + window.addEventListener('resize', toggleTooltip); + toggleTooltip(); +} + +function addCopyBtns() { + const cfg = document.querySelector('#copy-cfg'); + if (!cfg) return; + const copyIcon = cfg.dataset.copyIcon; + const checkIcon = cfg.dataset.checkIcon; + document.querySelectorAll('pre').forEach(block => { + if (block.classList.contains('mermaid')) return; + const wrapper = document.createElement('div'); + wrapper.className = 'codeblock'; + const btn = document.createElement('button'); + btn.className = 'copy'; + btn.ariaLabel = 'copy'; + btn.innerHTML = copyIcon; + const copy = () => { + navigator.clipboard.writeText(block.textContent).then(() => { + btn.innerHTML = checkIcon; + btn.classList.add('copied'); + btn.removeEventListener('click', copy); + setTimeout(() => { + btn.innerHTML = copyIcon; + btn.classList.remove('copied'); + btn.addEventListener('click', copy); + }, 1500); + }); + }; + btn.addEventListener('click', copy); + wrapper.appendChild(block.cloneNode(true)); + wrapper.appendChild(btn); + block.replaceWith(wrapper); + }); +} + +function addBackToTopBtn() { + const backBtn = document.querySelector('#back-to-top'); + if (!backBtn) return; + const toTop = () => window.scrollTo({ top: 0 }); + const toggle = () => { + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + if (scrollTop > 200 && !backBtn.classList.contains('shown')) { + backBtn.classList.add('shown'); + backBtn.setAttribute('tabindex', 0); + backBtn.addEventListener('click', toTop); + } else if (scrollTop <= 200 && backBtn.classList.contains('shown')) { + backBtn.classList.remove('shown'); + backBtn.setAttribute('tabindex', -1); + backBtn.removeEventListener('click', toTop); + } + }; + window.addEventListener('scroll', toggle); + toggle(); +} + +function addFootnoteBacklink() { + const footnotes = document.querySelectorAll('.footnote-definition'); + footnotes.forEach(footnote => { + const backlink = document.createElement('button'); + backlink.className = 'backlink'; + backlink.ariaLabel = 'backlink'; + backlink.innerHTML = 'โ†ฉ๏ธŽ'; + backlink.addEventListener('click', () => window.scrollTo({ + top: document.querySelector(`.footnote-reference a[href="#${footnote.id}"]`).getBoundingClientRect().top + window.scrollY, + })); + const lastEl = footnote.lastElementChild || footnote; + lastEl.appendChild(backlink); + }); +} + +function enableImgLightense() { + window.addEventListener("load", () => Lightense(".prose img:not(.no-lightense)", { background: 'rgba(43, 43, 43, 0.19)' })); +} + +function enableReaction() { + const container = document.querySelector('.reaction'); + if (!container) return; + const endpoint = container.dataset.endpoint; + const slug = location.pathname.split('/').filter(Boolean).pop(); + let state = { error: false, reaction: {} }; + const render = () => { + const btns = Object.entries(state.reaction).map(([emoji, [count, reacted]])=> { + const span = document.createElement('span'); + span.textContent = count; + const btn = document.createElement('button'); + if (reacted) btn.classList.add('reacted'); + btn.append(emoji, span); + btn.onclick = () => toggle(emoji); + return btn; + }); + if (state.error) { + container.classList.add('error'); + } else { + container.classList.remove('error'); + } + container.replaceChildren(...btns); + }; + const toggle = async (target) => { + const [count, reacted] = state.reaction[target]; + state.reaction[target] = reacted ? [count - 1, false] : [count + 1, true]; + render(); + try { + const resp = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ slug, target, reacted: !reacted }), + }); + if (resp.status === 200) { + error = false; + } else { + throw new Error(); + } + } catch (err) { + state.error = true; + state.reaction[target] = [count, reacted]; + render(); + } + }; + const init = async () => { + const resp = await fetch(`${endpoint}?slug=${slug}`); + if (resp.status === 200) { + state.reaction = await resp.json(); + render(); + } + }; + init(); +} + +enableThemeToggle(); +enablePrerender(); +enableRssMask(); +if (document.body.classList.contains('post')) { + enableOutdateAlert(); + addBackToTopBtn(); + enableTocTooltip(); +} +if (document.querySelector('.prose')) { + addCopyBtns(); + addFootnoteBacklink(); + enableImgLightense(); + enableReaction(); +} diff --git a/src/themes/serene/templates/404.html b/src/themes/serene/templates/404.html new file mode 100644 index 0000000..1e924de --- /dev/null +++ b/src/themes/serene/templates/404.html @@ -0,0 +1,14 @@ +{% extends "_base.html" %} + +{% block page %}not-found{% endblock page%} +{% block lang %}{{ config.default_language }}{% endblock lang %} +{% block title %}404{% endblock title %} + +{% block content %} +
+

+ {{ config.extra.not_found_error_text }} +

+ {{ config.extra.not_found_recover_text }} +
+{% endblock %} diff --git a/src/themes/serene/templates/_base.html b/src/themes/serene/templates/_base.html new file mode 100644 index 0000000..0bd7b3b --- /dev/null +++ b/src/themes/serene/templates/_base.html @@ -0,0 +1,38 @@ + + + + + + + + + {% block desc %}{% endblock desc %} + {% block title %}{% endblock title %} + + + + {% include "_custom_font.html" %} + {% include "_custom_css.html" %} + + {% block head %}{% endblock head %} + {% include "_head_extend.html" %} + + + + {% if not config.extra.force_theme %} + + {% endif %} + {% block content %}{% endblock content %} + {% block script %}{% endblock script %} + + + + diff --git a/src/themes/serene/templates/_custom_css.html b/src/themes/serene/templates/_custom_css.html new file mode 100644 index 0000000..f767a33 --- /dev/null +++ b/src/themes/serene/templates/_custom_css.html @@ -0,0 +1,59 @@ + diff --git a/src/themes/serene/templates/_custom_font.html b/src/themes/serene/templates/_custom_font.html new file mode 100644 index 0000000..e69de29 diff --git a/src/themes/serene/templates/_footer.html b/src/themes/serene/templates/_footer.html new file mode 100644 index 0000000..e9c1dd1 --- /dev/null +++ b/src/themes/serene/templates/_footer.html @@ -0,0 +1,52 @@ +{% if config.extra.blog_section_path is defined %} +{% set blog_section_path = config.extra.blog_section_path %} +{% endif %} +
+
+ +
+ +
+ {% 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 %} + RSS + {% endif %} + {% endif %} + + {% if not config.extra.force_theme %} + {% set moon_icon = load_data(path="icon/moon.svg") %} + {% set sun_icon = load_data(path="icon/sun.svg") %} + + {% endif %} +
+
+ +{% if blog_section_path is defined and section.path is starting_with(blog_section_path) %} +{% if section.generate_feeds or config.generate_feeds %} +{% set link = get_url(path=rss_path) %} + +
+ {{ link }} + {% set copy_icon = load_data(path="icon/copy.svg") %} + {% set check_icon = load_data(path="icon/check.svg") %} + +
+
+{% endif %} +{% endif %} diff --git a/src/themes/serene/templates/_giscus_script.html b/src/themes/serene/templates/_giscus_script.html new file mode 100644 index 0000000..e69de29 diff --git a/src/themes/serene/templates/_head_extend.html b/src/themes/serene/templates/_head_extend.html new file mode 100644 index 0000000..e69de29 diff --git a/src/themes/serene/templates/_section_title.html b/src/themes/serene/templates/_section_title.html new file mode 100644 index 0000000..191e079 --- /dev/null +++ b/src/themes/serene/templates/_section_title.html @@ -0,0 +1,10 @@ +{% if section.extra.title or section.extra.subtitle %} +
+ {% if section.extra.title %} +

{{ section.extra.title }}

+ {% endif %} + {% if section.extra.subtitle %} +

{{ section.extra.subtitle }}

+ {% endif %} +
+{% endif %} diff --git a/src/themes/serene/templates/anchor-link.html b/src/themes/serene/templates/anchor-link.html new file mode 100644 index 0000000..91e875b --- /dev/null +++ b/src/themes/serene/templates/anchor-link.html @@ -0,0 +1 @@ + diff --git a/src/themes/serene/templates/blog.html b/src/themes/serene/templates/blog.html new file mode 100644 index 0000000..c654e86 --- /dev/null +++ b/src/themes/serene/templates/blog.html @@ -0,0 +1,50 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}blog{% endblock page%} +{% block lang %}{% if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ section.lang }}{% endif %}{% endblock lang %} +{% block title %}{{ section.title }}{% endblock title %} +{% block desc %} + {% if section.description %} + {% set desc = section.description %} + {% else %} + {% set desc = config.description %} + {% endif %} + +{% endblock desc %} + +{% block content %} +
+ {{ macros::back_link(path = get_url(path="/")) }} + {% include "_section_title.html" %} +
+ {% if section.extra.categorized %} + {% for category,posts in section.pages | sort(attribute="taxonomies.categories.0") | group_by(attribute="taxonomies.categories.0") %} + {% set category_name = category %} + {% if category is matching("^__[0-9]{2}__") %} + {% set category_name = category | split(pat="") | slice(start=7) | join(sep="") %} + {% endif %} +

{{ category_name }}

+
+ {% for post in posts %} + + {{ post.title }} + {{ post.date | date(format=section.extra.date_format) }} + + {% endfor %} +
+ {% endfor %} + {% else %} +
+ {% for post in section.pages %} + + {{ post.title }} + {{ post.date | date(format=section.extra.date_format) }} + + {% endfor %} +
+ {% endif %} +
+ {% include "_footer.html" %} +
+{% endblock content %} diff --git a/src/themes/serene/templates/categories/list.html b/src/themes/serene/templates/categories/list.html new file mode 100644 index 0000000..9069f6b --- /dev/null +++ b/src/themes/serene/templates/categories/list.html @@ -0,0 +1,3 @@ + diff --git a/src/themes/serene/templates/categories/single.html b/src/themes/serene/templates/categories/single.html new file mode 100644 index 0000000..9069f6b --- /dev/null +++ b/src/themes/serene/templates/categories/single.html @@ -0,0 +1,3 @@ + diff --git a/src/themes/serene/templates/feed.xml b/src/themes/serene/templates/feed.xml new file mode 100644 index 0000000..fe1f139 --- /dev/null +++ b/src/themes/serene/templates/feed.xml @@ -0,0 +1,32 @@ + + + {% if section.title %}{{ section.title }}{% else %}{{ config.title }}{% endif %} + {%- if section.description %} + {{ section.description }} + {%- elif config.description %} + {{ config.description }} + {%- endif %} + + + {{ last_updated | date(format="%+") }} + {{ feed_url | safe }} + {%- for page in pages %} + + {{ page.title }} + {{ page.date | date(format="%+") }} + {{ page.updated | default(value=page.date) | date(format="%+") }} + {%- if page.summary %} + {{ page.summary }} + {%- endif %} + + {{ page.permalink | safe }} + {{ page.content }} + + {%- endfor %} + \ No newline at end of file diff --git a/src/themes/serene/templates/home.html b/src/themes/serene/templates/home.html new file mode 100644 index 0000000..6f4c3ac --- /dev/null +++ b/src/themes/serene/templates/home.html @@ -0,0 +1,119 @@ +{% extends "_base.html" %} + +{% block page %}homepage{% endblock page%} +{% block lang %}{% if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ section.lang }}{% endif %}{% endblock lang %} +{% block title %}{{ config.title }}{% endblock title %} +{% block desc %} + +{% endblock desc %} + +{% block head %} +{% if config.markdown.highlight_theme == "css" %} + {% if config.extra.force_theme == "dark" %} + + {% else %} + + {% endif %} +{% endif %} +{% if section.extra.math %} + + + + + +{% endif %} +{% endblock head %} + +{% block content %} +
+
+
+ {% if section.extra.avatar %} + avatar + {% endif %} +
+
+ {{ section.extra.name }} + {% if section.extra.id %} + @{{ section.extra.id }} + {% endif %} +
+ {% if section.extra.bio %} +
{{ section.extra.bio }}
+ {% endif %} +
+
+ +
+ {{ section.content | trim | safe }} +
+ {% if section.extra.recent %} + {% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %} + {% set section_md_path = blog_section_path ~ "/_index.md" %} + {% set blog_section = get_section(path=section_md_path) %} +
+
+ {% for post in blog_section.pages | slice(end=section.extra.recent_max) %} + + {{ post.title }} + + {{ post.date | date(format=section.extra.date_format) }} + + {% endfor %} +
+ +
+ {% endif %} +
+ {% if section.extra.footer %} + {% include "_footer.html" %} + {% endif %} +
+{% endblock content %} + +{% block script %} + +{% if section.extra.mermaid %} + +{% endif %} +{% endblock script %} diff --git a/src/themes/serene/templates/macros/collection.html b/src/themes/serene/templates/macros/collection.html new file mode 100644 index 0000000..796eae1 --- /dev/null +++ b/src/themes/serene/templates/macros/collection.html @@ -0,0 +1,153 @@ +{% macro card(item) %} +
+
+ {% if item.icon %} +
+ icon +
+ {% endif %} + {% if item.link %} + {{ item.title }} + {% else %} +
{{ item.title }}
+ {% endif %} + {% if item.date %} +
{{ item.date }}
+ {% endif %} +
+ {% if item.subtitle %} +
{{ item.subtitle }}
+ {% endif %} +
{{ item.content | trim | markdown | safe }}
+ {% if item.tags %} +
+ {% for tag in item.tags %} +
#{{ tag }}
+ {% endfor %} +
+ {% endif %} +
+{% endmacro %} + + +{% macro card_simple(item) %} +
+
+ {% if item.icon %} +
+ icon +
+ {% endif %} + {% if item.link %} + {{ item.title }} + {% else %} +
{{ item.title }}
+ {% endif %} +
{{ item.content | trim | markdown | safe }}
+ {% if item.date %} +
{{ item.date }}
+ {% endif %} +
+
{{ item.content | trim | markdown | safe }}
+
+{% endmacro %} + + +{% macro entry(item) %} +{% if item.link %} + +{% else %} +
+{% endif %} + {% if item.icon %} +
+ icon +
+ {% endif %} + {% if item.title or item.subtitle %} +
+ {% if item.title %} +
{{ item.title }}
+ {% endif %} + {% if item.subtitle %} +
{{ item.subtitle }}
+ {% endif %} +
+ {% endif %} +{% if item.link %} +
+{% else %} +
+{% endif %} +{% endmacro %} + + +{% macro box(item) %} + +{% endmacro %} + + +{% macro art(item) %} +
+
+ {{ item.title }} +
+
+ {% if item.link %} + {{ item.title }} + {% else %} +
{{ item.title }}
+ {% endif %} + {% if item.subtitle %} +
{{ item.subtitle }}
+ {% endif %} + {% if item.content %} +
{{ item.content }}
+ {% endif %} + {% if item.footer %} + + {% endif %} +
+
+{% endmacro %} + + +{% macro art_simple(item) %} +
+
+ {{ item.title }} +
+
+ {% if item.link %} + {{ item.title }} + {% else %} +
{{ item.title }}
+ {% endif %} + {% if item.subtitle %} +
{{ item.subtitle }}
+ {% endif %} +
+
+{% endmacro %} diff --git a/src/themes/serene/templates/macros/prose.html b/src/themes/serene/templates/macros/prose.html new file mode 100644 index 0000000..bd9a894 --- /dev/null +++ b/src/themes/serene/templates/macros/prose.html @@ -0,0 +1,32 @@ +{% macro back_link(path) %} +
+ +
+{% endmacro %} + + +{% macro callout(name) %} +
+ {% set icon = load_data(path="icon/" ~ name ~ ".svg") %} + {% if title %} +

+ + {{ icon | safe }} + + {{ title }} +

+
+ {{ body | markdown | safe }} +
+ {% else %} +
+ {{ icon | safe }} +
+
+ {{ body | markdown | safe }} +
+ {% endif %} +
+{% endmacro %} diff --git a/src/themes/serene/templates/post.html b/src/themes/serene/templates/post.html new file mode 100644 index 0000000..f31810a --- /dev/null +++ b/src/themes/serene/templates/post.html @@ -0,0 +1,151 @@ +{% 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) -%} +{%- 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 %} + {% if page.summary %} + {% set desc = page.summary %} + {% elif page.description %} + {% set desc = page.description %} + {% elif section.description %} + {% set desc = section.description %} + {% else %} + {% set desc = config.description %} + {% endif %} + +{% endblock desc %} + +{% block head %} +{% if config.markdown.highlight_theme == "css" %} + +{% endif %} +{% if page.extra.math %} + + + + + +{% endif %} +{% endblock head %} + +{% block content %} +
+
+ +
+ {{ macros::back_link(path = get_url(path=section.path)) }} + +
+ {% if page.extra.copy is defined %}{% set allow_copy = page.extra.copy %}{% else %}{% set allow_copy = section.extra.copy %}{% endif %} + {% if allow_copy %} + {% set copy_icon = load_data(path="icon/copy.svg") %} + {% set check_icon = load_data(path="icon/check.svg") %} + + {% endif %} +
+

{{ page.title }}

+
+
+ {{ page.date | date(format=section.extra.date_format) }} + {% if page.updated and page.updated != page.date -%} + Updated on {{ page.updated | date(format=section.extra.date_format) }} + {% endif -%} +
+ + {% if page.taxonomies.tags is defined %} +
+ {% for tag in page.taxonomies.tags -%} + {% set tag_slugify = tag | slugify -%} + #{{ tag }} + {%- endfor %} +
+ {% endif %} +
+ + {% 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 %} + + {% if show_outdate_alert -%} + + {% endif %} + + {{ page.content | safe }} +
+ + {% if page.extra.reaction is defined %}{% set show_reaction = page.extra.reaction %}{% else %}{% set show_reaction = config.extra.reaction %}{% endif %} + {% if show_reaction %} +
+ {% endif %} + + {% if page.extra.comment is defined %}{% set show_comment = page.extra.comment %}{% else %}{% set show_comment = section.extra.comment %}{% endif %} + {% if show_comment %} +
+ {% include "_giscus_script.html" %} + {% endif %} +
+ + {% include "_footer.html" %} +
+
+{% endblock content %} + +{% block script %} + +{% if page.extra.mermaid %} + +{% endif %} +{% endblock script %} diff --git a/src/themes/serene/templates/prose.html b/src/themes/serene/templates/prose.html new file mode 100644 index 0000000..661c408 --- /dev/null +++ b/src/themes/serene/templates/prose.html @@ -0,0 +1,82 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}prose-page{% endblock page %} +{% block lang -%} +{%- if section.extra.lang %}{{section.extra.lang}}{% else %}{{section.lang}}{% endif -%} +{%- endblock lang %} +{% block title %}{{ section.title }}{% endblock title %} +{% block desc %} + {% if section.description %} + {% set desc = section.description %} + {% else %} + {% set desc = config.description %} + {% endif %} + +{% endblock desc %} + +{% block head %} +{% if config.markdown.highlight_theme == "css" %} + +{% endif %} +{% if section.extra.math %} + + + + + +{% endif %} +{% endblock head %} + +{% block content %} +
+
+ {{ macros::back_link(path = get_url(path="/")) }} + {% include "_section_title.html" %} +
+ {% if section.extra.copy %} + {% set copy_icon = load_data(path="icon/copy.svg") %} + {% set check_icon = load_data(path="icon/check.svg") %} + + {% endif %} +
+ {{ section.content | safe }} +
+ + {% if section.extra.reaction is defined %}{% set show_reaction = section.extra.reaction %}{% else %}{% set show_reaction = config.extra.reaction %}{% endif %} + {% if show_reaction %} +
+ {% endif %} + + {% if section.extra.comment %} +
+ {% include "_giscus_script.html" %} + {% endif %} +
+ + {% include "_footer.html" %} +
+
+{% endblock content %} + +{% block script %} + +{% if section.extra.mermaid %} + +{% endif %} +{% endblock script %} diff --git a/src/themes/serene/templates/shortcodes/caution.html b/src/themes/serene/templates/shortcodes/caution.html new file mode 100644 index 0000000..7d1797f --- /dev/null +++ b/src/themes/serene/templates/shortcodes/caution.html @@ -0,0 +1,2 @@ +{% 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 new file mode 100644 index 0000000..c217a0c --- /dev/null +++ b/src/themes/serene/templates/shortcodes/collection.html @@ -0,0 +1,23 @@ +{% import 'macros/collection.html' as marcos -%} + +{% set data = load_data(path="content" ~ section.path ~ file, format="toml") %} + +
+{% 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" %} +
+ {% endif %} +{% endfor %} +
diff --git a/src/themes/serene/templates/shortcodes/detail.html b/src/themes/serene/templates/shortcodes/detail.html new file mode 100644 index 0000000..0d34a74 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/detail.html @@ -0,0 +1,4 @@ +
+ {{ title }} + {{ body | markdown | safe }} +
diff --git a/src/themes/serene/templates/shortcodes/figure.html b/src/themes/serene/templates/shortcodes/figure.html new file mode 100644 index 0000000..8a4123f --- /dev/null +++ b/src/themes/serene/templates/shortcodes/figure.html @@ -0,0 +1,8 @@ +
+ + {% if via %} +
via
+ {% else %} +
{{ caption }}
+ {% endif %} +
\ No newline at end of file diff --git a/src/themes/serene/templates/shortcodes/important.html b/src/themes/serene/templates/shortcodes/important.html new file mode 100644 index 0000000..451c141 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/important.html @@ -0,0 +1,2 @@ +{% 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 new file mode 100644 index 0000000..3d3725d --- /dev/null +++ b/src/themes/serene/templates/shortcodes/mermaid.html @@ -0,0 +1,3 @@ +
+  {{ body }}
+
\ No newline at end of file diff --git a/src/themes/serene/templates/shortcodes/note.html b/src/themes/serene/templates/shortcodes/note.html new file mode 100644 index 0000000..b16657b --- /dev/null +++ b/src/themes/serene/templates/shortcodes/note.html @@ -0,0 +1,2 @@ +{% 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 new file mode 100644 index 0000000..c7e3c37 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/quote.html @@ -0,0 +1,10 @@ +
+ {% set icon = load_data(path="icon/quote.svg") %} + +
{{ body | markdown | safe }}
+ {% if cite %} +
+ {{ "โ€” " ~ cite | markdown | safe }} +
+ {% endif %} +
\ No newline at end of file diff --git a/src/themes/serene/templates/shortcodes/tip.html b/src/themes/serene/templates/shortcodes/tip.html new file mode 100644 index 0000000..8904437 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/tip.html @@ -0,0 +1,2 @@ +{% 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 new file mode 100644 index 0000000..0964b15 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/warning.html @@ -0,0 +1,2 @@ +{% 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 new file mode 100644 index 0000000..ff41838 --- /dev/null +++ b/src/themes/serene/templates/tags/list.html @@ -0,0 +1,29 @@ +{% 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 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 %} + +{% endblock desc %} + +{% block content %} +
+ {{ macros::back_link(path = get_url(path="/")) }} +
+

Tags

+
+ {% for tag in terms -%} + # {{ tag.name | lower }} + {% endfor %} +
+
+ {% include "_footer.html" %} +
+{% endblock content %} diff --git a/src/themes/serene/templates/tags/single.html b/src/themes/serene/templates/tags/single.html new file mode 100644 index 0000000..f8b9fc4 --- /dev/null +++ b/src/themes/serene/templates/tags/single.html @@ -0,0 +1,34 @@ +{% 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 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 %} + +{% endblock desc %} + +{% block content %} +
+ {{ macros::back_link(path = get_url(path="/tags")) }} +
+
+ # {{ term.name }} +
+
+ {% for post in term.pages %} + + {{ post.title }} + {{ post.date | date(format=section.extra.date_format) }} + + {% endfor %} +
+
+ {% include "_footer.html" %} +
+{% endblock content %} diff --git a/src/themes/serene/theme.toml b/src/themes/serene/theme.toml new file mode 100644 index 0000000..30efe2e --- /dev/null +++ b/src/themes/serene/theme.toml @@ -0,0 +1,12 @@ +name = "serene" +description = "A spiffy blog theme for zola" +license = "MIT" +homepage = "https://github.com/isunjn/serene" +min_version = "0.20.0" +demo = "https://serene-demo.pages.dev" + +[author] +name = "isunjn" +homepage = "https://github.com/isunjn" + +[extra] -- cgit 1.4.1