hexo-theme-gnix 7.0.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +6 -2
  2. package/include/hexo/encrypt.js +42 -0
  3. package/include/hexo/feed.js +330 -0
  4. package/include/util/common.js +7 -16
  5. package/languages/en.yml +5 -2
  6. package/languages/zh-CN.yml +5 -2
  7. package/layout/archive.jsx +8 -204
  8. package/layout/comment/twikoo.jsx +2 -11
  9. package/layout/common/article.jsx +45 -32
  10. package/layout/common/article_cover.jsx +11 -1
  11. package/layout/common/article_media.jsx +2 -4
  12. package/layout/common/footer.jsx +10 -14
  13. package/layout/common/head.jsx +7 -15
  14. package/layout/common/navbar.jsx +3 -18
  15. package/layout/common/scripts.jsx +6 -5
  16. package/layout/common/theme_selector.jsx +5 -6
  17. package/layout/common/toc.jsx +8 -14
  18. package/layout/index.jsx +2 -4
  19. package/layout/misc/open_graph.jsx +4 -4
  20. package/layout/misc/paginator.jsx +10 -4
  21. package/layout/misc/structured_data.jsx +3 -4
  22. package/layout/plugin/busuanzi.jsx +1 -1
  23. package/layout/plugin/cookie_consent.jsx +40 -31
  24. package/layout/plugin/swup.jsx +2 -22
  25. package/layout/search/insight.jsx +16 -3
  26. package/package.json +12 -8
  27. package/scripts/hot-reload.js +92 -0
  28. package/scripts/index.js +2 -0
  29. package/source/css/archive.css +251 -0
  30. package/source/css/default.css +300 -309
  31. package/source/css/encrypt.css +55 -0
  32. package/source/css/responsive/desktop.css +0 -119
  33. package/source/css/responsive/mobile.css +2 -22
  34. package/source/css/responsive/touch.css +9 -103
  35. package/source/css/twikoo.css +265 -249
  36. package/source/img/og_image.webp +0 -0
  37. package/source/js/archive-breadcrumb.js +1 -5
  38. package/source/js/busuanzi.js +1 -12
  39. package/source/js/components/chat.js +239 -0
  40. package/source/js/components/image-carousel.js +410 -0
  41. package/source/js/components/text-image-section.js +180 -0
  42. package/source/js/components/theme-stacked.js +165 -246
  43. package/source/js/components/tree.js +437 -0
  44. package/source/js/decrypt.js +112 -0
  45. package/source/js/insight.js +75 -65
  46. package/source/js/main.js +48 -31
  47. package/source/js/mdit/mermaid.js +12 -4
  48. package/source/js/swup.bundle.js +1 -0
  49. package/source/js/theme-selector.js +94 -113
  50. package/source/img/og_image.png +0 -0
  51. package/source/js/host/swup/Swup.umd.min.js +0 -1
  52. package/source/js/host/swup/head-plugin.umd.min.js +0 -1
  53. package/source/js/host/swup/scripts-plugin.umd.min.js +0 -2
package/layout/index.jsx CHANGED
@@ -5,16 +5,14 @@ const Article = require("./common/article");
5
5
  module.exports = class extends Component {
6
6
  render() {
7
7
  const { config, page, helper } = this.props;
8
- const { __, url_for } = helper;
8
+ const { url_for } = helper;
9
9
 
10
10
  return (
11
11
  <Fragment>
12
12
  {page.posts.map((post) => (
13
13
  <Article config={config} page={post} helper={helper} index={true} />
14
14
  ))}
15
- {page.total > 1 ? (
16
- <Paginator current={page.current} total={page.total} baseUrl={page.base} path={config.pagination_dir} urlFor={url_for} prevTitle={__("common.prev")} nextTitle={__("common.next")} />
17
- ) : null}
15
+ {page.total > 1 ? <Paginator current={page.current} total={page.total} baseUrl={page.base} path={config.pagination_dir} urlFor={url_for} /> : null}
18
16
  </Fragment>
19
17
  );
20
18
  }
@@ -1,6 +1,6 @@
1
- const { isDate, parseISO, isValid } = require("date-fns");
2
- const { Component } = require("inferno");
1
+ const { Component, isValidDate, parseISO } = require("../../include/util/common");
3
2
  const { encodeURL, stripHTML, escapeHTML } = require("hexo-util");
3
+
4
4
  const localeMap = {
5
5
  en: "en_US",
6
6
  de: "de_DE",
@@ -63,14 +63,14 @@ module.exports = class extends Component {
63
63
 
64
64
  if (date) {
65
65
  const d = typeof date === "string" ? parseISO(date) : date;
66
- if ((isDate(d) || d instanceof Date) && isValid(d)) {
66
+ if (isValidDate(d)) {
67
67
  htmlTags.push(<meta property="article:published_time" content={d.toISOString()} />);
68
68
  }
69
69
  }
70
70
 
71
71
  if (updated) {
72
72
  const u = typeof updated === "string" ? parseISO(updated) : updated;
73
- if ((isDate(u) || u instanceof Date) && isValid(u)) {
73
+ if (isValidDate(u)) {
74
74
  htmlTags.push(<meta property="article:modified_time" content={u.toISOString()} />);
75
75
  }
76
76
  }
@@ -2,7 +2,7 @@ const { Component } = require("inferno");
2
2
 
3
3
  module.exports = class extends Component {
4
4
  render() {
5
- const { current, total, baseUrl, path, urlFor, prevTitle, nextTitle } = this.props;
5
+ const { current, total, baseUrl, path, urlFor } = this.props;
6
6
 
7
7
  function getPageUrl(i) {
8
8
  return urlFor(i === 1 ? baseUrl : `${baseUrl + path}/${i}/`);
@@ -55,12 +55,18 @@ module.exports = class extends Component {
55
55
  }
56
56
 
57
57
  return (
58
- <nav class="pagination is-centered" aria-label="pagination" style="padding-top: 1.5em;">
58
+ <nav class="pagination card" aria-label="pagination">
59
59
  <a href={getPageUrl(current - 1)} class={`pagination-previous`} style={current > 1 ? {} : { visibility: "hidden" }}>
60
- {prevTitle}
60
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
61
+ <title>go to previous page</title>
62
+ <path fill="currentColor" d="m4 10l9 9l1.4-1.5L7 10l7.4-7.5L13 1z" />
63
+ </svg>
61
64
  </a>
62
65
  <a href={getPageUrl(current + 1)} class={`pagination-next`} style={current < total ? {} : { visibility: "hidden" }}>
63
- {nextTitle}
66
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
67
+ <title>go to next page</title>
68
+ <path fill="currentColor" d="M7 1L5.6 2.5L13 10l-7.4 7.5L7 19l9-9z" />
69
+ </svg>
64
70
  </a>
65
71
  <ul class="pagination-list is-hidden-mobile">{pagination(current, total)}</ul>
66
72
  </nav>
@@ -1,5 +1,4 @@
1
- const { isDate, parseISO, isValid } = require("date-fns");
2
- const { Component } = require("inferno");
1
+ const { Component, isValidDate, parseISO } = require("../../include/util/common");
3
2
  const { stripHTML, escapeHTML } = require("hexo-util");
4
3
 
5
4
  module.exports = class extends Component {
@@ -28,14 +27,14 @@ module.exports = class extends Component {
28
27
 
29
28
  if (date) {
30
29
  const d = typeof date === "string" ? parseISO(date) : date;
31
- if ((isDate(d) || d instanceof Date) && isValid(d)) {
30
+ if (isValidDate(d)) {
32
31
  date = d.toISOString();
33
32
  }
34
33
  }
35
34
 
36
35
  if (updated) {
37
36
  const u = typeof updated === "string" ? parseISO(updated) : updated;
38
- if ((isDate(u) || u instanceof Date) && isValid(u)) {
37
+ if (isValidDate(u)) {
39
38
  updated = u.toISOString();
40
39
  }
41
40
  }
@@ -4,7 +4,7 @@ class Busuanzi extends Component {
4
4
  render() {
5
5
  return (
6
6
  // busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js
7
- <script defer src="/js/busuanzi.js"></script>
7
+ <script data-swup-reload-script defer src="/js/busuanzi.js"></script>
8
8
  // <script src="https://vercount.one/js" defer={true}></script>
9
9
  );
10
10
  }
@@ -1,46 +1,55 @@
1
1
  const { Component, cacheComponent } = require("../../include/util/common");
2
2
 
3
3
  class CookieConsent extends Component {
4
+ // https://www.osano.com/cookieconsent/documentation/javascript-api/
4
5
  render() {
5
6
  const { head, text, jsUrl, cssUrl } = this.props;
6
7
  const { type, theme, position, policyLink } = this.props;
7
8
  const { message, dismiss, allow, deny, link, policy } = text;
8
9
 
9
- const js = `window.addEventListener("load", () => {
10
- window.cookieconsent.initialise({
11
- type: ${JSON.stringify(type)},
12
- theme: ${JSON.stringify(theme)},
13
- static: ${JSON.stringify(this.props.static)},
14
- position: ${JSON.stringify(position)},
15
- content: {
16
- message: ${JSON.stringify(message)},
17
- dismiss: ${JSON.stringify(dismiss)},
18
- allow: ${JSON.stringify(allow)},
19
- deny: ${JSON.stringify(deny)},
20
- link: ${JSON.stringify(link)},
21
- policy: ${JSON.stringify(policy)},
22
- href: ${JSON.stringify(policyLink)},
23
- },
24
- palette: {
25
- popup: {
26
- background: "var(--base)",
27
- text: "var(--text)"
28
- },
29
- button: {
30
- background: "var(--blue)"
31
- },
32
- },
33
- });
34
- });`;
35
-
36
10
  if (head) {
37
11
  return <link rel="preload" href={cssUrl} as="style" onload="this.onload=null;this.rel='stylesheet'" />;
38
12
  }
39
13
  return (
40
- <>
41
- <script src={jsUrl} defer={true} onLoad={js}></script>
42
- {/* <script dangerouslySetInnerHTML={{ __html: js }}></script> */}
43
- </>
14
+ <script
15
+ dangerouslySetInnerHTML={{
16
+ __html: `
17
+ (function() {
18
+ var s = document.createElement('script');
19
+ s.src = ${JSON.stringify(jsUrl)};
20
+ s.defer = true;
21
+ s.onload = function() {
22
+ window.cookieconsent.initialise(${JSON.stringify({
23
+ type,
24
+ theme,
25
+ static: this.props.static,
26
+ position,
27
+ content: {
28
+ message,
29
+ dismiss,
30
+ allow,
31
+ deny,
32
+ link,
33
+ policy,
34
+ href: policyLink,
35
+ },
36
+ palette: {
37
+ popup: {
38
+ background: "var(--base)",
39
+ text: "var(--text)",
40
+ },
41
+ button: {
42
+ background: "var(--lavender)",
43
+ text: "var(--base)",
44
+ },
45
+ },
46
+ })});
47
+ };
48
+ document.body.appendChild(s);
49
+ })();
50
+ `,
51
+ }}
52
+ />
44
53
  );
45
54
  }
46
55
  }
@@ -1,4 +1,4 @@
1
- const { Component, Fragment } = require("../../include/util/common");
1
+ const { Component } = require("../../include/util/common");
2
2
 
3
3
  class Swup extends Component {
4
4
  render() {
@@ -6,27 +6,7 @@ class Swup extends Component {
6
6
  return null;
7
7
  }
8
8
 
9
- const swupScript = `
10
- const swup = new Swup({
11
- containers: ["#swup"],
12
- cache: true,
13
- native: true,
14
- plugins: [
15
- new SwupHeadPlugin({
16
- persistTags: true
17
- }),
18
- new SwupScriptsPlugin()
19
- ]
20
- });
21
- `;
22
- return (
23
- <Fragment>
24
- <script src="/js/host/swup/Swup.umd.min.js"></script>
25
- <script src="/js/host/swup/head-plugin.umd.min.js"></script>
26
- <script src="/js/host/swup/scripts-plugin.umd.min.js"></script>
27
- <script data-swup-ignore-script dangerouslySetInnerHTML={{ __html: swupScript }}></script>
28
- </Fragment>
29
- );
9
+ return <script defer src="/js/swup.bundle.js"></script>;
30
10
  }
31
11
  }
32
12
 
@@ -13,12 +13,25 @@ class Insight extends Component {
13
13
 
14
14
  return (
15
15
  <>
16
- <div class="searchbox">
16
+ <div class="searchbox" id="searchbox" popover="auto">
17
17
  <div class="searchbox-container">
18
18
  <div class="searchbox-input-container">
19
- <input type="text" name="search-input" class="searchbox-input" placeholder={translation.hint} />
19
+ <input
20
+ type="text"
21
+ name="search-input"
22
+ class="searchbox-input"
23
+ placeholder={translation.hint}
24
+ autofocus
25
+ autocomplete="off"
26
+ role="combobox"
27
+ aria-haspopup="listbox"
28
+ aria-autocomplete="list"
29
+ aria-controls="searchbox-results"
30
+ aria-expanded="false"
31
+ aria-label={translation.hint}
32
+ />
20
33
  </div>
21
- <div class="searchbox-body"></div>
34
+ <div class="searchbox-body" id="searchbox-results" role="listbox"></div>
22
35
  </div>
23
36
  </div>
24
37
  <script defer src={jsUrl}></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-gnix",
3
- "version": "7.0.0",
3
+ "version": "9.0.0",
4
4
  "author": "Efterklang <gaojiaxing0220@gmail.com>",
5
5
  "license": "MIT",
6
6
  "description": "Second generation of Hexo theme Icarus, now with Catppuccin flavor and night mode support.",
@@ -21,7 +21,8 @@
21
21
  "lint": "biome check --write .",
22
22
  "format": "biome format .",
23
23
  "check": "biome check",
24
- "test": "echo \"No tests configured\" && exit 0"
24
+ "test": "echo \"No tests configured\" && exit 0",
25
+ "build:swup": "bun build src/swup.js --outfile source/js/swup.bundle.js --minify"
25
26
  },
26
27
  "files": [
27
28
  "include",
@@ -33,13 +34,16 @@
33
34
  "README.md"
34
35
  ],
35
36
  "dependencies": {
36
- "date-fns": "^4.1.0",
37
- "esbuild": "^0.27.2",
37
+ "@swup/head-plugin": "^2.3.1",
38
+ "@swup/scripts-plugin": "^2.1.0",
39
+ "esbuild": "^0.28.0",
40
+ "feedsmith": "^2.9.1",
38
41
  "hexo-pagination": "^4.0.0",
39
- "inferno": "^9.0.10",
40
- "inferno-create-element": "^9.0.10",
41
- "inferno-server": "^9.0.10",
42
- "js-yaml": "^4.1.1"
42
+ "inferno": "^9.1.0",
43
+ "inferno-create-element": "^9.1.0",
44
+ "inferno-server": "^9.1.0",
45
+ "js-yaml": "^4.1.1",
46
+ "swup": "^4.8.3"
43
47
  },
44
48
  "peerDependencies": {
45
49
  "hexo": "^8.1.1",
@@ -0,0 +1,92 @@
1
+ const LIVE_RELOAD_PATH = "/__hexo_live_reload";
2
+ const clients = new Set();
3
+ const reloadDebounceMs = 150;
4
+ const reloadCooldownMs = 500;
5
+
6
+ function isHexoServerCommand(ctx) {
7
+ const command = ctx.env?.cmd;
8
+ const aliases = ctx.extend?.console?.alias ?? {};
9
+
10
+ return command === "server" || aliases[command] === "server";
11
+ }
12
+
13
+ function broadcastReload() {
14
+ const payload = `data: ${JSON.stringify({ type: "reload", at: Date.now() })}\n\n`;
15
+
16
+ for (const client of clients) {
17
+ client.write(payload);
18
+ }
19
+ }
20
+
21
+ if (isHexoServerCommand(hexo)) {
22
+ let reloadTimer = null;
23
+ let lastBroadcastAt = 0;
24
+
25
+ function scheduleReload() {
26
+ if (reloadTimer) {
27
+ clearTimeout(reloadTimer);
28
+ }
29
+
30
+ reloadTimer = setTimeout(() => {
31
+ reloadTimer = null;
32
+
33
+ const now = Date.now();
34
+ if (now - lastBroadcastAt < reloadCooldownMs) {
35
+ return;
36
+ }
37
+
38
+ lastBroadcastAt = now;
39
+ broadcastReload();
40
+ }, reloadDebounceMs);
41
+ }
42
+
43
+ hexo.on("generateAfter", () => {
44
+ scheduleReload();
45
+ });
46
+
47
+ hexo.extend.filter.register("server_middleware", (app) => {
48
+ app.use((req, res, next) => {
49
+ if (req.url !== LIVE_RELOAD_PATH) {
50
+ next();
51
+ return;
52
+ }
53
+
54
+ res.writeHead(200, {
55
+ "Cache-Control": "no-cache, no-transform",
56
+ Connection: "keep-alive",
57
+ "Content-Type": "text/event-stream",
58
+ "X-Accel-Buffering": "no",
59
+ });
60
+
61
+ res.write("retry: 1000\n\n");
62
+ clients.add(res);
63
+
64
+ req.on("close", () => {
65
+ clients.delete(res);
66
+ res.end();
67
+ });
68
+ });
69
+ });
70
+
71
+ hexo.extend.injector.register(
72
+ "body_end",
73
+ `
74
+ <script>
75
+ (() => {
76
+ if (!window.EventSource) return;
77
+
78
+ const source = new EventSource('${LIVE_RELOAD_PATH}');
79
+ source.addEventListener('message', (event) => {
80
+ try {
81
+ const data = JSON.parse(event.data);
82
+ if (data.type === 'reload') {
83
+ window.location.reload();
84
+ }
85
+ } catch (_) {
86
+ window.location.reload();
87
+ }
88
+ });
89
+ })();
90
+ </script>`,
91
+ );
92
+ }
package/scripts/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  require("../include/hexo/filter")(hexo);
2
2
  require("../include/hexo/generator")(hexo);
3
+ require("../include/hexo/feed")(hexo);
4
+ require("../include/hexo/encrypt")(hexo);
3
5
  require("../include/hexo/view").init(hexo);
4
6
  require("../include/hexo/helper")(hexo);
5
7
  require("../include/hexo/renderer")(hexo);
@@ -0,0 +1,251 @@
1
+ @keyframes blink {
2
+ 0%,
3
+ 100% {
4
+ opacity: 1;
5
+ }
6
+ 50% {
7
+ opacity: 0;
8
+ }
9
+ }
10
+
11
+ .article-meta {
12
+ font-size: 0.8rem;
13
+ color: var(--subtext1);
14
+ font-family: var(--font-handwriting);
15
+ white-space: nowrap;
16
+ }
17
+
18
+ a.archive-title {
19
+ font-family: var(--font-sans-serif);
20
+ font-weight: 400;
21
+ color: var(--text);
22
+ }
23
+
24
+ a.archive-title:hover {
25
+ color: var(--accent);
26
+ }
27
+
28
+ /* Breadcrumb navigation */
29
+ .archive-breadcrumb {
30
+ color: var(--blue);
31
+ font-family: var(--font-mono);
32
+ margin: 0 0 1rem 0;
33
+ padding-left: 1em;
34
+ display: flex;
35
+ flex-wrap: wrap;
36
+ align-items: center;
37
+ gap: 0;
38
+ }
39
+
40
+ .archive-breadcrumb .prompt {
41
+ color: var(--green);
42
+ user-select: none;
43
+ margin-right: 0.25em;
44
+ }
45
+
46
+ .archive-breadcrumb .cursor {
47
+ display: inline-block;
48
+ color: var(--mauve);
49
+ font-weight: bold;
50
+ margin-left: 2px;
51
+ animation: blink 1s step-end infinite;
52
+ user-select: none;
53
+ }
54
+
55
+ .archive-breadcrumb__cmd {
56
+ color: var(--yellow);
57
+ user-select: none;
58
+ padding-right: 0.5em;
59
+ }
60
+
61
+ .archive-breadcrumb__picker {
62
+ position: relative;
63
+ display: inline-flex;
64
+ align-items: center;
65
+ }
66
+
67
+ .archive-breadcrumb__trigger {
68
+ display: inline-flex;
69
+ align-items: center;
70
+ border: none;
71
+ background: transparent;
72
+ color: var(--yellow);
73
+ font-family: var(--font-mono);
74
+ padding: 0.05em 0.15em;
75
+ border-radius: 4px;
76
+ text-decoration: underline;
77
+ text-decoration-color: hsl(from var(--yellow) h s l / 0.6);
78
+ text-decoration-thickness: 1px;
79
+ text-underline-offset: 0.22em;
80
+ cursor: pointer;
81
+ transition:
82
+ transform 120ms ease,
83
+ color 120ms ease,
84
+ background 120ms ease,
85
+ text-decoration-color 120ms ease;
86
+ }
87
+
88
+ .archive-breadcrumb__trigger:hover {
89
+ color: var(--mauve);
90
+ background: hsl(from var(--base) h s l / 0.35);
91
+ text-decoration-color: hsl(from var(--mauve) h s l / 0.75);
92
+ }
93
+
94
+ .archive-breadcrumb__trigger:focus-visible {
95
+ outline: 2px solid hsl(from var(--mauve) h s l / 0.7);
96
+ outline-offset: 2px;
97
+ background: hsl(from var(--base) h s l / 0.35);
98
+ }
99
+
100
+ .archive-breadcrumb__trigger[aria-expanded="true"] {
101
+ transform: translateY(-1px);
102
+ color: var(--mauve);
103
+ text-decoration-color: hsl(from var(--mauve) h s l / 0.9);
104
+ }
105
+
106
+ .archive-breadcrumb__trigger[disabled] {
107
+ opacity: 0.45;
108
+ cursor: not-allowed;
109
+ text-decoration-color: hsl(from var(--subtext1) h s l / 0.4);
110
+ }
111
+
112
+ .archive-breadcrumb__menu {
113
+ position: absolute;
114
+ top: calc(100% + 6px);
115
+ left: 0;
116
+ min-width: 12rem;
117
+ max-height: 15rem;
118
+ overflow: auto;
119
+ background: hsl(from var(--base) h s l / 0.9);
120
+ border: 1px solid hsl(from var(--surface1) h s l / 0.9);
121
+ border-radius: 10px;
122
+ padding: 0.35rem;
123
+ box-shadow: 0 18px 50px hsl(from var(--crust) h s l / 0.45);
124
+ opacity: 0;
125
+ transform: translateY(-6px);
126
+ visibility: hidden;
127
+ pointer-events: none;
128
+ transition:
129
+ opacity 160ms ease,
130
+ transform 160ms ease,
131
+ visibility 0s linear 160ms;
132
+ z-index: 20;
133
+ }
134
+
135
+ .archive-breadcrumb__trigger[aria-expanded="true"] + .archive-breadcrumb__menu {
136
+ opacity: 1;
137
+ transform: translateY(0);
138
+ visibility: visible;
139
+ pointer-events: auto;
140
+ transition:
141
+ opacity 160ms ease,
142
+ transform 160ms ease,
143
+ visibility 0s;
144
+ }
145
+
146
+ .archive-breadcrumb__option {
147
+ width: 100%;
148
+ display: flex;
149
+ justify-content: space-between;
150
+ align-items: center;
151
+ gap: 0.5rem;
152
+ padding: 0.35rem 0.5rem;
153
+ border-radius: 8px;
154
+ border: none;
155
+ background: transparent;
156
+ color: var(--text);
157
+ font-family: var(--font-mono);
158
+ font-size: 0.9rem;
159
+ cursor: pointer;
160
+ transition:
161
+ background 120ms ease,
162
+ color 120ms ease;
163
+ text-align: left;
164
+ }
165
+
166
+ .archive-breadcrumb__option:hover,
167
+ .archive-breadcrumb__option:focus-visible {
168
+ background: hsl(from var(--surface1) h s l / 0.55);
169
+ color: var(--yellow);
170
+ outline: none;
171
+ }
172
+
173
+ .archive-breadcrumb__option[aria-selected="true"] {
174
+ background: hsl(from var(--surface1) h s l / 0.75);
175
+ color: var(--mauve);
176
+ }
177
+
178
+ /* Card with year overlay */
179
+ .card-content {
180
+ position: relative;
181
+ overflow: hidden;
182
+ }
183
+
184
+ span.year {
185
+ position: absolute;
186
+ top: 1.5rem;
187
+ right: 1.5rem;
188
+ z-index: 0;
189
+ pointer-events: none;
190
+ font-weight: bolder;
191
+ font-family: var(--font-handwriting);
192
+ font-size: 4em;
193
+ font-style: italic;
194
+ color: hsl(from var(--accent, var(--lavender)) h s l / 0.3);
195
+ line-height: 1;
196
+ user-select: none;
197
+ }
198
+
199
+ /* Season colors */
200
+ .winter {
201
+ --accent: var(--blue);
202
+ }
203
+
204
+ .autumn {
205
+ --accent: var(--red);
206
+ }
207
+
208
+ .summer {
209
+ --accent: var(--green);
210
+ }
211
+
212
+ .spring {
213
+ --accent: var(--peach);
214
+ }
215
+
216
+ /* Archive item */
217
+ .archive-item {
218
+ position: relative;
219
+ display: flex;
220
+ text-align: left;
221
+ align-items: flex-start;
222
+ }
223
+
224
+ .archive-item > div {
225
+ display: flex;
226
+ align-items: baseline;
227
+ gap: 0.75rem;
228
+ }
229
+
230
+ .archive-item + .archive-item {
231
+ border: none;
232
+ margin-top: 0;
233
+ padding-top: 1em;
234
+ }
235
+
236
+ /* Responsive */
237
+ @media (max-width: 480px) {
238
+ .archive-breadcrumb {
239
+ padding-left: 0.5em;
240
+ }
241
+
242
+ .archive-breadcrumb__menu {
243
+ min-width: 8rem;
244
+ max-width: calc(100vw - 2rem);
245
+ }
246
+
247
+ span.year {
248
+ font-size: 2.5em;
249
+ right: 0.5rem;
250
+ }
251
+ }