hexo-theme-stellar 1.40.0 → 1.42.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 (146) hide show
  1. package/.claude/skills/stellar-theme-dev/SKILL.md +1 -2
  2. package/CHANGELOG.md +90 -0
  3. package/CONTRIBUTING.md +2 -1
  4. package/README.md +50 -21
  5. package/README_EN.md +113 -0
  6. package/_config.yml +40 -12
  7. package/_data/icons.yml +248 -37
  8. package/languages/en.yml +6 -0
  9. package/languages/zh-CN.yml +6 -0
  10. package/languages/zh-TW.yml +6 -0
  11. package/layout/_partial/comments/artalk/script.ejs +25 -8
  12. package/layout/_partial/comments/beaudar/script.ejs +1 -0
  13. package/layout/_partial/comments/twikoo/script.ejs +1 -0
  14. package/layout/_partial/comments/utterances/script.ejs +1 -0
  15. package/layout/_partial/comments/waline/script.ejs +1 -0
  16. package/layout/_partial/cover/index.ejs +0 -3
  17. package/layout/_partial/head.ejs +43 -7
  18. package/layout/_partial/main/article/article_tags.ejs +16 -0
  19. package/layout/_partial/main/navbar/article_banner.ejs +33 -20
  20. package/layout/_partial/main/notebook/note_card.ejs +1 -1
  21. package/layout/_partial/main/notebook/paginator.ejs +2 -2
  22. package/layout/_partial/main/pin_slider.ejs +61 -30
  23. package/layout/_partial/main/post_list/latest_post_card.ejs +25 -0
  24. package/layout/_partial/main/post_list/paginator.ejs +2 -2
  25. package/layout/_partial/main/post_list/post_card.ejs +16 -64
  26. package/layout/_partial/main/post_list/topic_card.ejs +26 -5
  27. package/layout/_partial/menubtn.ejs +2 -2
  28. package/layout/_partial/scripts/bootstrap.ejs +35 -0
  29. package/layout/_partial/scripts/defines.ejs +27 -2
  30. package/layout/_partial/scripts.ejs +39 -7
  31. package/layout/_partial/sidebar/logo.ejs +1 -1
  32. package/layout/_partial/sidebar/menu.ejs +1 -1
  33. package/layout/_partial/sidebar/search.ejs +1 -1
  34. package/layout/_partial/widgets/author.ejs +1 -1
  35. package/layout/_partial/widgets/components/link.ejs +2 -2
  36. package/layout/_partial/widgets/ghuser.ejs +1 -1
  37. package/layout/_partial/widgets/related.ejs +1 -1
  38. package/layout/_partial/widgets/toc.ejs +3 -3
  39. package/layout/_partial/widgets/tree.ejs +1 -1
  40. package/layout/_plugins/adaptive_text.ejs +11 -0
  41. package/layout/_plugins/copycode.ejs +2 -2
  42. package/layout/_plugins/fancybox.ejs +4 -2
  43. package/layout/_plugins/mermaid.ejs +3 -2
  44. package/layout/_plugins/scrollreveal.ejs +22 -16
  45. package/layout/_plugins/search/local_search.ejs +4 -3
  46. package/layout/_plugins/swiper.ejs +2 -1
  47. package/layout/categories.ejs +1 -1
  48. package/layout/index.ejs +10 -2
  49. package/layout/index_topic.ejs +4 -4
  50. package/layout/page.ejs +3 -0
  51. package/layout/tags.ejs +1 -0
  52. package/package.json +3 -3
  53. package/scripts/events/lib/doc_tree.js +12 -208
  54. package/scripts/events/lib/notebooks.js +8 -148
  55. package/scripts/events/lib/utils.js +10 -1
  56. package/scripts/filters/index.js +1 -0
  57. package/scripts/filters/lib/img_lazyload.js +4 -0
  58. package/scripts/filters/lib/img_onerror.js +4 -0
  59. package/scripts/filters/lib/md_table.js +46 -0
  60. package/scripts/generators/search.js +12 -18
  61. package/scripts/generators/stellar-icons.js +47 -0
  62. package/scripts/helpers/ai_label.js +29 -0
  63. package/scripts/helpers/icon.js +2 -2
  64. package/scripts/helpers/json_ld.js +10 -13
  65. package/scripts/helpers/related_posts.js +0 -7
  66. package/scripts/helpers/seo.js +9 -0
  67. package/scripts/helpers/subtitle.js +8 -0
  68. package/scripts/lib/ai_label.js +55 -0
  69. package/scripts/lib/doc_tree.js +267 -0
  70. package/scripts/lib/notebooks.js +181 -0
  71. package/scripts/lib/search_index.js +82 -0
  72. package/scripts/lib/seo.js +79 -0
  73. package/scripts/lib/subtitle.js +24 -0
  74. package/scripts/tags/lib/about.js +2 -2
  75. package/scripts/tags/lib/albums.js +1 -1
  76. package/scripts/tags/lib/banner.js +5 -4
  77. package/scripts/tags/lib/chat.js +49 -49
  78. package/scripts/tags/lib/copy.js +1 -1
  79. package/scripts/tags/lib/friends.js +1 -1
  80. package/scripts/tags/lib/gallery.js +1 -1
  81. package/scripts/tags/lib/hashtag.js +1 -1
  82. package/scripts/tags/lib/image.js +3 -3
  83. package/scripts/tags/lib/posters.js +1 -1
  84. package/scripts/tags/lib/sites.js +2 -2
  85. package/source/css/_common/base.styl +5 -9
  86. package/source/css/_common/cover-overlay.styl +111 -0
  87. package/source/css/_common/device.styl +3 -3
  88. package/source/css/_common/title.styl +17 -0
  89. package/source/css/_components/layout.styl +9 -13
  90. package/source/css/_components/list.styl +135 -64
  91. package/source/css/_components/main.styl +2 -2
  92. package/source/css/_components/md-table.styl +23 -0
  93. package/source/css/_components/pages/archives.styl +5 -22
  94. package/source/css/_components/pages/article-story.styl +5 -14
  95. package/source/css/_components/partial/article-banner.styl +61 -37
  96. package/source/css/_components/partial/article-tags.styl +13 -0
  97. package/source/css/_components/partial/bread-nav.styl +19 -12
  98. package/source/css/_components/partial/navbar.styl +11 -3
  99. package/source/css/_components/partial/post-panel.styl +76 -0
  100. package/source/css/_components/pin-slider.styl +38 -41
  101. package/source/css/_components/sidebar/footer.styl +1 -1
  102. package/source/css/_components/sidebar/logo.styl +2 -2
  103. package/source/css/_components/sidebar/search.styl +25 -5
  104. package/source/css/_components/sidebar/sidebar.styl +7 -7
  105. package/source/css/_components/tag-plugins/banner.styl +53 -11
  106. package/source/css/_components/tag-plugins/friends_posts.styl +5 -86
  107. package/source/css/_components/tag-plugins/gallery.styl +3 -0
  108. package/source/css/_components/tag-plugins/read/paper.styl +3 -3
  109. package/source/css/_components/tag-plugins/read/reel.styl +4 -4
  110. package/source/css/_components/tag-plugins/table.styl +16 -11
  111. package/source/css/_components/tag-plugins/timeline.styl +4 -0
  112. package/source/css/_components/widgets/components.styl +6 -0
  113. package/source/css/_components/widgets/list.styl +6 -0
  114. package/source/css/_components/widgets/timeline.styl +2 -2
  115. package/source/css/_components/widgets/toc.styl +13 -7
  116. package/source/css/_components/widgets/widgets.styl +8 -7
  117. package/source/css/_custom.styl +9 -21
  118. package/source/css/_defines/func.styl +28 -3
  119. package/source/css/_plugins/index.styl +0 -18
  120. package/source/css/_plugins/lazyload.styl +1 -0
  121. package/source/css/{_plugins/comments → comments}/artalk.styl +4 -0
  122. package/source/css/{_plugins/comments → comments}/twikoo.styl +4 -0
  123. package/source/css/{_plugins → plugins}/mermaid.styl +3 -0
  124. package/source/css/{_plugins → plugins}/swiper.styl +5 -1
  125. package/source/js/color.js +325 -0
  126. package/source/js/icons.js +60 -0
  127. package/source/js/main.js +115 -1
  128. package/source/js/plugins/adaptive-text.js +146 -0
  129. package/source/js/search/highlight.js +82 -0
  130. package/source/js/search/local-search.js +294 -103
  131. package/source/js/services/friends_and_posts.js +1 -1
  132. package/source/js/services/timeline.js +1 -1
  133. package/source/js/services/weibo.js +3 -3
  134. package/source/js/services.js +179 -0
  135. package/source/js/tagtree.js +29 -0
  136. package/source/js/theme.js +61 -0
  137. package/{layout/_partial/scripts/utils.ejs → source/js/utils.js} +27 -11
  138. package/layout/_partial/cover/post_cover.ejs +0 -37
  139. package/layout/_partial/scripts/services.ejs +0 -178
  140. package/layout/_partial/scripts/sidebar.ejs +0 -25
  141. package/layout/_partial/scripts/tagtree.ejs +0 -29
  142. package/layout/_partial/scripts/theme.ejs +0 -67
  143. /package/source/css/{_plugins/comments → comments}/beaudar.styl +0 -0
  144. /package/source/css/{_plugins/comments → comments}/utterances.styl +0 -0
  145. /package/source/css/{_plugins/comments → comments}/waline.styl +0 -0
  146. /package/source/css/{_plugins → plugins}/fancybox.styl +0 -0
@@ -47,10 +47,10 @@ module.exports = ctx => function(args) {
47
47
  if (style.length > 0 && !args.ratio) {
48
48
  img += ' style="' + style + '"'
49
49
  }
50
- img += `onerror="this.src="${ctx.theme.config.default.image_onerror}""`
50
+ img += `onerror="this.src="${ctx.theme.config.default.image_onerror || ctx.utils.iconData('image:onerror')}""`
51
51
  img += '/>'
52
52
  // loading
53
- img += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading});"></div>`
53
+ img += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')});"></div>`
54
54
  if (fancyboxHref) {
55
55
  a += ` href="${fancyboxHref}">${img}</a>`
56
56
  return a
@@ -93,7 +93,7 @@ module.exports = ctx => function(args) {
93
93
  if (args.alt) {
94
94
  download = ' download="' + args.alt + '"'
95
95
  }
96
- el += '<a class="image-download blur" style="opacity:0" target="_blank"' + download + ' href="' + href + '"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a>'
96
+ el += '<a class="image-download blur" style="opacity:0" target="_blank"' + download + ' href="' + href + '">' + ctx.utils.icon('image:download') + '</a>'
97
97
  }
98
98
  el += '</div>'
99
99
 
@@ -32,7 +32,7 @@ module.exports = ctx => function(args) {
32
32
  el += `<div class="grid-cell poster-card">`
33
33
  el += `<a class="card-link lazy-box" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
34
34
  el += `<img class="lazy" data-src="${item.cover || item.icon || item.avatar || ctx.theme.config.default.cover}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
35
- el += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading});"></div>`
35
+ el += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')});"></div>`
36
36
  el += `<div class="image-meta">`
37
37
  if (item.title) {
38
38
  el += `<span class="image-caption">${item.title}</span>`
@@ -33,12 +33,12 @@ module.exports = ctx => function(args) {
33
33
  el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
34
34
  el += `<div class="lazy-box snapshot">`
35
35
  el += `<img class="lazy" data-src="${item.cover || item.snapshot || item.screenshot || ('https://image.thum.io/get/width/1280/crop/720/' + item.url)}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
36
- el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading}&quot;);"></div>`
36
+ el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')}&quot;);"></div>`
37
37
  el += `</div>`
38
38
  el += `<div class="info">`
39
39
  el += `<div class="lazy-box icon">`
40
40
  el += `<img class="lazy" data-src="${item.icon || item.avatar || ctx.theme.config.default.link}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${item.icon || item.avatar || ctx.theme.config.default.link}&quot;;"/>`
41
- el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading}&quot;);"></div>`
41
+ el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')}&quot;);"></div>`
42
42
  el += `</div>`
43
43
  el += `<span class="title">${item.title}</span>`
44
44
  el += `<span class="desc">${item.description || item.url}</span>`
@@ -34,14 +34,15 @@ ul,ol
34
34
  // table
35
35
  table:not([class])
36
36
  border-collapse: collapse
37
- overflow: auto
37
+ display: block
38
+ overflow-x: auto
39
+ -webkit-overflow-scrolling: touch
38
40
  margin: 1rem 0
39
41
  max-width: 100%
40
42
  vertical-align: text-top
41
43
  --fsp: $fsp2
42
44
  font-size: var(--fsp)
43
45
  scrollbar(0,0)
44
- display: table
45
46
  th
46
47
  background: var(--block)
47
48
  border-top: 1px solid var(--block-border)
@@ -50,6 +51,8 @@ table:not([class])
50
51
  padding: 4px 1em
51
52
  line-height: 1.5
52
53
  tr
54
+ word-break: keep-all
55
+ white-space: nowrap
53
56
  border-bottom: 1px dashed var(--block-border)
54
57
  trans()
55
58
  &:hover
@@ -57,10 +60,3 @@ table:not([class])
57
60
 
58
61
  @media screen and (min-width: $device-mobile-max)
59
62
  width 100%
60
-
61
- @media screen and (max-width: $device-mobile-max)
62
- display: block
63
- tr
64
- word-break: keep-all
65
- white-space:nowrap
66
-
@@ -0,0 +1,111 @@
1
+ // 背景图覆盖层通用能力:同图渐变模糊层 + 黑色渐变蒙版 + hover 变暗
2
+ // $url-var: 背景图 CSS 变量(--cover-url / --pin-cover-url / --bg-url),模糊层取同图背景
3
+ // $sides: top | bottom | both —— 文字所在边缘;both 依赖宿主内的 .banner-mask-top/bottom 蒙版元素
4
+ // $layer: 覆盖层宿主选择器(相对当前元素),缺省为当前元素自身;图片等 hover 目标由调用方按 DOM 自行接入
5
+ cover-overlay($url-var, $sides = bottom, $layer = null)
6
+ $host = $layer ? $layer : '&'
7
+ // 统一默认观感变量(与文章列表 hero 封面一致)
8
+ --img-br: 100%
9
+ --img-sat: 100%
10
+ --blur-px: 1em
11
+ --blur-height: 128px
12
+ --blur-sat: 120%
13
+ --cover-zoom: 1.05
14
+ // hover 整体变暗:亮度 75%、饱和度 120%,模糊层饱和度提升至 200%
15
+ &:hover
16
+ --img-br: 75%
17
+ --img-sat: 120%
18
+ --blur-sat: 200%
19
+ if $sides == both
20
+ // 双侧::before 顶部模糊层 + :after 底部模糊层,黑色蒙版由 .banner-mask-top/bottom 承担
21
+ {$host}:before, {$host}:after
22
+ content: ''
23
+ position: absolute
24
+ top: 0
25
+ left: 0
26
+ width: 100%
27
+ height: 100%
28
+ z-index: 0
29
+ background-image: $url-var
30
+ background-size: cover
31
+ background-position: center
32
+ filter: brightness(var(--img-br)) saturate(var(--img-sat)) blur(var(--blur-px)) saturate(var(--blur-sat))
33
+ // 常驻恒等 transform:hover 放大时 Safari 正确应用父级圆角裁剪(同 list.styl 注释)
34
+ transform: translateZ(0)
35
+ pointer-events: none
36
+ transition: all .2s ease-out, transform .5s ease-out
37
+ -moz-transition: all .2s ease-out, transform .5s ease-out
38
+ -webkit-transition: all .2s ease-out, transform .5s ease-out
39
+ -o-transition: all .2s ease-out, transform .5s ease-out
40
+ {$host}:before
41
+ mask: linear-gradient(black, rgba(black, 0.75) calc(var(--blur-height-top, var(--blur-height)) * 0.5), transparent var(--blur-height-top, var(--blur-height)))
42
+ -webkit-mask: linear-gradient(black, rgba(black, 0.75) calc(var(--blur-height-top, var(--blur-height)) * 0.5), transparent var(--blur-height-top, var(--blur-height)))
43
+ {$host}:after
44
+ mask: linear-gradient(to top, black, rgba(black, 0.75) calc(var(--blur-height-bottom, var(--blur-height)) * 0.5), transparent var(--blur-height-bottom, var(--blur-height)))
45
+ -webkit-mask: linear-gradient(to top, black, rgba(black, 0.75) calc(var(--blur-height-bottom, var(--blur-height)) * 0.5), transparent var(--blur-height-bottom, var(--blur-height)))
46
+ if $layer
47
+ &:hover
48
+ {$host}:before, {$host}:after
49
+ transform: translateZ(0) scale(var(--cover-zoom))
50
+ else
51
+ {$host}:hover:before, {$host}:hover:after
52
+ transform: translateZ(0) scale(var(--cover-zoom))
53
+ // 黑色渐变蒙版:文字所在边缘不透明度约 0.25,垂直中线为 0
54
+ {$host} .banner-mask
55
+ position: absolute
56
+ inset: 0
57
+ z-index: 1
58
+ pointer-events: none
59
+ &.banner-mask-top
60
+ background-image: linear-gradient(rgba(0,0,0,0.25), rgba(0,0,0,0) 50%)
61
+ &.banner-mask-bottom
62
+ background-image: linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,0.25))
63
+ else
64
+ // 单侧::before 同图模糊层 + :after 黑色渐变蒙版
65
+ {$host}:before
66
+ content: ''
67
+ position: absolute
68
+ top: 0
69
+ left: 0
70
+ width: 100%
71
+ height: 100%
72
+ z-index: 1
73
+ background-image: $url-var
74
+ background-size: cover
75
+ background-position: center
76
+ filter: brightness(var(--img-br)) saturate(var(--img-sat)) blur(var(--blur-px)) saturate(var(--blur-sat))
77
+ // 常驻恒等 transform:hover 放大时 Safari 正确应用父级圆角裁剪(同 list.styl 注释)
78
+ transform: translateZ(0)
79
+ pointer-events: none
80
+ transition: all .2s ease-out, transform .5s ease-out
81
+ -moz-transition: all .2s ease-out, transform .5s ease-out
82
+ -webkit-transition: all .2s ease-out, transform .5s ease-out
83
+ -o-transition: all .2s ease-out, transform .5s ease-out
84
+ if $sides == top
85
+ {$host}:before
86
+ mask: linear-gradient(black, rgba(black, 0.75) calc(var(--blur-height-top, var(--blur-height)) * 0.5), transparent var(--blur-height-top, var(--blur-height)))
87
+ -webkit-mask: linear-gradient(black, rgba(black, 0.75) calc(var(--blur-height-top, var(--blur-height)) * 0.5), transparent var(--blur-height-top, var(--blur-height)))
88
+ else
89
+ {$host}:before
90
+ mask: linear-gradient(to top, black, rgba(black, 0.75) calc(var(--blur-height-bottom, var(--blur-height)) * 0.5), transparent var(--blur-height-bottom, var(--blur-height)))
91
+ -webkit-mask: linear-gradient(to top, black, rgba(black, 0.75) calc(var(--blur-height-bottom, var(--blur-height)) * 0.5), transparent var(--blur-height-bottom, var(--blur-height)))
92
+ if $layer
93
+ &:hover
94
+ {$host}:before
95
+ transform: translateZ(0) scale(var(--cover-zoom))
96
+ else
97
+ {$host}:hover:before
98
+ transform: translateZ(0) scale(var(--cover-zoom))
99
+ {$host}:after
100
+ content: ''
101
+ position: absolute
102
+ top: 0
103
+ left: 0
104
+ width: 100%
105
+ height: 100%
106
+ z-index: 1
107
+ pointer-events: none
108
+ if $sides == top
109
+ background-image: linear-gradient(rgba(0,0,0,0.25), rgba(0,0,0,0) 50%)
110
+ else
111
+ background-image: linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,0.25))
@@ -12,7 +12,7 @@
12
12
  grid-column-end: span 3
13
13
  --inset: 2rem
14
14
  right: 0
15
- bottom: calc(var(--inset) * 2)
15
+ bottom: calc(var(--inset) * 1)
16
16
  margin-left: auto
17
17
  margin-right: var(--inset)
18
18
  float: right
@@ -40,8 +40,8 @@
40
40
  box-sizing: border-box
41
41
  flex: 0 0 auto
42
42
  margin: 0 auto
43
- width: 36px
44
- height: 36px
43
+ width: 40px
44
+ height: 40px
45
45
  padding: 4px
46
46
  line-height: 0
47
47
  font-size: 28px
@@ -15,6 +15,23 @@ h5
15
15
  h6
16
16
  font-size: 'calc(%s + 0px)' % var(--fsp)
17
17
 
18
+ // story 文章段落标题(h2):居中 + 两侧 accent 斜杠装饰
19
+ // 专栏列表页专栏标题(.topic-title)与 story 文章 h2 共用
20
+ story-title()
21
+ color: var(--text)
22
+ line-height: 1.2
23
+ text-align: center
24
+ &:before,&:after
25
+ content: ''
26
+ display: inline-block
27
+ width: 12px
28
+ height: 1.2em
29
+ background: $c-accent
30
+ transform: skewX(-20deg)
31
+ margin: 0 0.5em
32
+ vertical-align: top
33
+ position: relative
34
+
18
35
  // 次级段落字号
19
36
  .fs15
20
37
  --fsp: $fs-15
@@ -1,9 +1,7 @@
1
1
  .l_body
2
2
  display: grid
3
3
  grid-template-columns: 1fr minmax(200px,var(--width-main)) 1fr
4
- grid-gap: calc(var(--gap-margin) * 4)
5
- @media screen and (max-width: $device-desktop)
6
- grid-gap: calc(var(--gap-margin) * 2)
4
+ grid-gap: calc(var(--gap-page) * 1)
7
5
  margin: auto
8
6
  font-size: var(--fsp)
9
7
 
@@ -13,36 +11,34 @@
13
11
  position: sticky
14
12
  position: -webkit-sticky
15
13
  // 侧边栏容器宽度
16
- --width-sidebar: calc(var(--gap-margin) * 2 + var(--gap-padding) * 2 + var(--side-content-width))
14
+ --width-sidebar: calc(var(--gap-base) * 4 + var(--side-content-width))
17
15
  width: var(--width-sidebar)
18
16
 
19
17
  .l_body .l_left
20
18
  justify-self: right
21
- top: var(--gap-margin)
19
+ top: var(--gap-page)
22
20
  .l_body .l_right
23
21
  justify-self: left
24
- max-height: calc(100% - var(--gap-margin) * 2)
22
+ width: calc(var(--width-sidebar) + var(--rightbar-width-extra))
23
+ max-height: calc(100% - var(--gap-page) * 2)
25
24
  .widgets
26
25
  height: 100%
27
26
  overflow visible
28
27
 
29
28
  // 平板布局
30
29
  @media screen and (max-width: $device-laptop)
31
- .l_body .l_right
32
- --gap-margin: 16px
33
30
  .laptop-only
34
31
  display: block !important
35
32
  .l_body
36
- grid-gap: calc(var(--gap-margin) * 1)
37
33
  .l_right
38
34
  position: fixed
39
- transform: translateX(320px)
35
+ transform: translateX(calc(100% + var(--inset) * 2))
40
36
  transition: transform .38s ease-out
41
37
  margin: 0
42
38
  --inset: 8px
43
39
  right: var(--inset)
44
- top: var(--gap-margin)
45
- max-height: calc(100vh - var(--gap-margin) * 2)
40
+ top: var(--gap-page)
41
+ max-height: calc(100vh - var(--gap-page) * 2)
46
42
  box-shadow: $boxshadow-card-float
47
43
  z-index: 10
48
44
  background: var(--background)
@@ -66,7 +62,7 @@
66
62
  display: block
67
63
  .l_left
68
64
  position: fixed
69
- transform: translateX(-320px)
65
+ transform: translateX(calc(-100% - var(--inset) * 2))
70
66
  transition: transform .38s ease-out
71
67
  margin: 0
72
68
  --inset: 8px
@@ -51,14 +51,27 @@
51
51
  display: block
52
52
  border-radius: $border-card-l
53
53
  background: var(--card)
54
+ // hero 封面卡片:封面图同时作为卡片自身背景承载,角落跟随全局 corner-shape 圆角裁剪;
55
+ // 子图保留用于 hover 变暗/放大滤镜,角落楔形区由容器背景(同图)无缝补位
56
+ background-image: var(--cover-url)
57
+ background-size: cover
58
+ background-position: center
54
59
  overflow: hidden
55
60
  z-index: 0
56
61
  position: relative
62
+ // Safari 26.4/26.5 合成层裁剪 bug(WebKit 312584/319993):带 filter:blur() 的合成层
63
+ // (.cover:before 同图模糊层)不执行父级 overflow:hidden + border-radius 圆角裁剪,静止时底部两角漏方角;
64
+ // 用与圆角同源的 clip-path 直接裁剪(hover 时模糊层带 transform 反而正常,见 .cover:before 的 translateZ(0))。
65
+ // clip-path 会把角落硬裁为普通圆角,覆盖 corner-shape;因此在支持 corner-shape 的浏览器中移除,
66
+ // 仅在不支持的浏览器(Safari/Firefox)保留圆角兜底
67
+ @supports not (corner-shape: superellipse(1))
68
+ clip-path: 'inset(0 round %s)' % $border-card-l
57
69
  --img-br: 100%
58
70
  --img-sat: 100%
59
71
  --blur-px: 1em
60
72
  --blur-height: 128px
61
73
  --blur-sat: 120%
74
+ --cover-zoom: 1.05
62
75
  &:hover
63
76
  --img-br: 75%
64
77
  --img-sat: 120%
@@ -67,7 +80,7 @@
67
80
  --blur-sat: 200%
68
81
  // 图片卡片 hover 封面放大:卡片本身不动,放大后的图片由圆角静态裁剪裁出圆角
69
82
  .post-cover img, .cover img, .cover:before
70
- transform: scale(1.05)
83
+ transform: scale(var(--cover-zoom))
71
84
  img
72
85
  filter: brightness(var(--img-br)) saturate(var(--img-sat))
73
86
  // 封面放大用 1.5s 缓动,其余属性(亮度/饱和度)保持主题默认 0.2s
@@ -111,6 +124,11 @@
111
124
  flex-wrap: wrap
112
125
  align-items: center
113
126
  gap: 0 .5rem
127
+ svg
128
+ width: 1em
129
+ height: 1em
130
+ margin-right: .25em
131
+ opacity: .4
114
132
 
115
133
 
116
134
  // common article
@@ -140,71 +158,60 @@
140
158
  border-radius: 0
141
159
  aspect-ratio: hexo-config('article.cover_ratio')
142
160
 
143
- .post-list .post-card.post.photo
144
- .cover
145
- position: relative
146
- line-height: 0
147
- img
148
- width: 100%
149
- object-fit: cover
150
- aspect-ratio: hexo-config('article.cover_ratio')
151
- z-index 0
152
- &:before
153
- content: ''
154
- z-index 1
155
- position: absolute
156
- top: 0
157
- left: 0
158
- width: 100%
159
- height: 100%
160
- // Chromium: corner-shape + overflow:hidden 的合成层裁剪与 backdrop-filter 不兼容,
161
- // 渐变模糊改用同图模糊层(filter + mask)实现,观感与 backdrop-filter 等价
162
- background-image: var(--cover-url)
163
- background-size: cover
164
- background-position: center
165
- filter: brightness(var(--img-br)) saturate(var(--img-sat)) blur(var(--blur-px)) saturate(var(--blur-sat))
166
- // 与封面 img 同步:缩放 0.5s 缓动,亮度/饱和度仍按 0.2s
167
- transition: all .2s ease-out, transform .5s ease-out
168
- -moz-transition: all .2s ease-out, transform .5s ease-out
169
- -webkit-transition: all .2s ease-out, transform .5s ease-out
170
- -o-transition: all .2s ease-out, transform .5s ease-out
171
- mask: linear-gradient(black, rgba(black, 0.75) calc(var(--blur-height) * 0.5), transparent var(--blur-height))
172
- -webkit-mask: linear-gradient(black, rgba(black, 0.75) calc(var(--blur-height) * 0.5), transparent var(--blur-height))
173
- &[position=bottom]:before
174
- mask: linear-gradient(to top, black, rgba(black, 0.75) calc(var(--blur-height) * 0.5), transparent var(--blur-height))
175
- -webkit-mask: linear-gradient(to top, black, rgba(black, 0.75) calc(var(--blur-height) * 0.5), transparent var(--blur-height))
176
- .cover-info
177
- padding: 1rem
178
- position: absolute
179
- line-height: 1.2
180
- width: 'calc(100% - %s * 2)' % 1rem
181
- --text-banner: white
182
- color: var(--text-banner)
183
- z-index: 2
184
- &[position=top]
185
- top: 0
186
- background-image: linear-gradient(rgba(0,0,0,0.2),rgba(0,0,0,0))
187
- &[position=bottom]
188
- bottom: 0
189
- background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.2))
190
- div+div
191
- margin-top: 4px
192
- .text
193
- color: unset
194
- trans1 all
195
- text-shadow: 0 2px 4px rgba(0,0,0,0.2)
196
- .topic
197
- font-size: $fs-13
161
+ // 封面渐变模糊卡片:文章 photo 卡片与专栏最新文章卡片共用(接入 cover-overlay 统一覆盖层)
162
+ .post-list .post-card .cover
163
+ position: relative
164
+ line-height: 0
165
+ img
166
+ width: 100%
167
+ object-fit: cover
168
+ z-index 0
169
+ // 与通用覆盖层能力统一:文字区固定底部(hero 卡片),渐变模糊层 + 黑色蒙版(视觉参数不变)
170
+ cover-overlay(var(--cover-url), bottom)
171
+ .cover-info
172
+ padding: 1rem
173
+ position: absolute
174
+ line-height: 1.2
175
+ width: 'calc(100% - %s * 2)' % 1rem
176
+ --text-banner: white
177
+ color: var(--text-banner)
178
+ z-index: 2
179
+ bottom: 0
180
+ div+div
181
+ margin-top: 4px
182
+ .text
183
+ color: unset
184
+ trans1 all
185
+ text-shadow: 0 2px 4px rgba(0,0,0,0.2)
186
+ .topic
187
+ font-size: $fs-13
188
+ // 小字用主题色变体(split 模式下由插件写入 --text-banner-theme)
189
+ color: var(--text-banner-theme, inherit)
190
+ .headline
191
+ font-weight: 500
192
+ font-size: $fsh3
193
+ .caption
194
+ font-size: $fs-14
195
+ // 小字用主题色变体(split 模式下由插件写入 --text-banner-theme)
196
+ color: var(--text-banner-theme, inherit)
197
+ // 只显示一行,超出省略
198
+ white-space: nowrap
199
+ overflow: hidden
200
+ text-overflow: ellipsis
201
+ @media screen and (max-width: $device-mobile)
198
202
  .headline
199
- font-weight: 500
200
- font-size: $fsh3
203
+ font-size: $fsh4
201
204
  .caption
202
- font-size: $fs-14
203
- @media screen and (max-width: $device-mobile)
204
- .headline
205
- font-size: $fsh4
206
- .caption
207
- font-size: $fs-13
205
+ font-size: $fs-13
206
+
207
+ .post-list .post-card.post.photo
208
+ .cover img
209
+ aspect-ratio: hexo-config('article.cover_ratio')
210
+ // hover 放大覆盖:基规则的 `transform: translateZ(0)` 特异性高于上方 `.post-card:hover` 的 scale,
211
+ // 需用更高特异性选择器在基规则之后覆盖,保持 hover 缩放动画(叠加恒等变换)
212
+ &:hover
213
+ .cover:before
214
+ transform: translateZ(0) scale(var(--cover-zoom))
208
215
  h2
209
216
  margin: .25rem 0
210
217
  font-size: $fsh4
@@ -249,6 +256,70 @@
249
256
  .cap+.cap
250
257
  margin-left: 4px
251
258
 
259
+
260
+ // topic 专栏列表:平铺容器 + 上下布局(h2 专栏标题 → 最新文章卡片 → 文章链接列表)
261
+ .post-list.topic
262
+ .post-card-wrap
263
+ margin: 0
264
+ box-shadow: none
265
+ border-radius: 0
266
+ & + .post-card-wrap
267
+ border-top: 1px solid var(--block-border)
268
+ .post-card
269
+ background: none
270
+ box-shadow: none
271
+ border-radius: 0
272
+ overflow: visible
273
+ clip-path: none
274
+ .md-text
275
+ padding: 2.25rem 0
276
+ @media screen and (max-width: $device-mobile)
277
+ padding: 1.5rem 0
278
+ .post-card.topic article
279
+ display: flex
280
+ flex-direction: column
281
+ gap: 1rem
282
+ .topic-title
283
+ story-title()
284
+ margin: 0 0 0.5rem
285
+ .topic-desc
286
+ text-align: center
287
+ color: var(--text-p2)
288
+ font-size: $fs-14
289
+ line-height: 1.5
290
+ // 左右内边距与文章列表(.post-panel padding: 0 1rem)一致
291
+ padding: 0 1rem
292
+ margin: 0 0 1rem
293
+ .cover
294
+ flex: none
295
+ width: 100%
296
+ aspect-ratio: 2 / 1
297
+ border-radius: $border-card-l
298
+ overflow: hidden
299
+ // 背景图由 .cover 自身背景承载(跟随 corner-shape),子图保留用于 hover 滤镜
300
+ background-image: var(--cover-url)
301
+ background-size: cover
302
+ background-position: center
303
+ // 与 .post-card 相同的 Safari 圆角裁剪兜底(模糊层合成层不跟随 overflow:hidden);
304
+ // 支持 corner-shape 的浏览器移除 clip-path,由自身背景跟随曲率
305
+ @supports not (corner-shape: superellipse(1))
306
+ clip-path: 'inset(0 round %s)' % $border-card-l
307
+ display: block
308
+ img
309
+ width: 100%
310
+ height: 100%
311
+ .post-panel
312
+ flex: none
313
+ width: 100%
314
+ // 左右内边距与文章封面文字区(.cover-info padding: 1rem)一致
315
+ padding: 0 1rem
316
+ .posts
317
+ margin: 0
318
+ // hover 放大覆盖(与 photo 卡片一致,见 .post-card.post.photo:hover 注释)
319
+ .post-card.topic:hover
320
+ .cover:before
321
+ transform: translateZ(0) scale(var(--cover-zoom))
322
+
252
323
  .post-list.archives
253
324
  @media screen and (max-width: $device-mobile)
254
325
  margin: 1rem 0
@@ -1,8 +1,8 @@
1
1
  .l_main
2
2
  position: relative
3
- padding-bottom: var(--gap-max)
3
+ padding-bottom: var(--gap-page)
4
4
  @media screen and (min-width: $device-mobile-max)
5
- padding-top: var(--gap-margin)
5
+ padding-top: var(--gap-page)
6
6
  header
7
7
  margin: 2rem 1rem 1rem
8
8
  .logo-wrap
@@ -0,0 +1,23 @@
1
+ // 普通 Markdown 表格滚动容器:宽度足够时铺满容器,内容超出时横向滚动
2
+ .md-table-scroll
3
+ overflow-x: auto
4
+ -webkit-overflow-scrolling: touch
5
+ max-width: 100%
6
+ margin: 1rem 0
7
+ scrollbar(0,0)
8
+ > table:not([class])
9
+ display: table
10
+ width: 100%
11
+ max-width: none
12
+ margin: 0
13
+ border-collapse: separate
14
+ border-spacing: 0
15
+ border: 1px solid var(--block-border)
16
+ border-radius: $border-card
17
+ overflow: hidden
18
+ th
19
+ border-top: none
20
+ th, td
21
+ border-left: 1px solid var(--block-border)
22
+ &:first-child
23
+ border-left: none
@@ -81,29 +81,12 @@
81
81
  display: flex
82
82
  flex-wrap: wrap
83
83
  padding: 0
84
- margin: 0 -4px
84
+ margin: 0
85
+ column-gap: 1rem
85
86
  a.tag
86
- display: inline-flex
87
- align-items: center
88
- position: relative
89
- color: var(--text-p2)
90
- margin: 4px
91
- padding: .5em .75rem
92
- border-radius: 4px
93
- background: var(--block)
94
- font-size: $fs-13
95
- font-weight: 500
96
- &:before
97
- content: "#"
98
- margin-left: -2px
99
- margin-right: 2px
100
- opacity: .4
101
- &:hover
102
- &:before
103
- color var(--theme)
104
- opacity: 1
105
- color: var(--text)
106
- background: var(--block-border)
87
+ tag-chip()
88
+ margin-left: 0
89
+ margin-right: 0
107
90
 
108
91
  @media screen and (min-width: $device-mobile)
109
92
  .post-list.author #archive
@@ -25,16 +25,7 @@
25
25
  &:after
26
26
  font-size: 0.25em
27
27
  h2:not([class])
28
- &:before,&:after
29
- content: ''
30
- display: inline-block
31
- width: 12px
32
- height: 1.2em
33
- background: $c-accent
34
- transform: skewX(-20deg)
35
- margin: 0 0.5em
36
- vertical-align: top
37
- position: relative
28
+ story-title()
38
29
 
39
30
  h3:not([class])
40
31
  position: relative
@@ -51,9 +42,9 @@
51
42
  vertical-align: middle
52
43
  &:before
53
44
  margin-left: -1em
54
- svg-mask-icon($iH3Left)
45
+ svg-mask-icon(var(--icon-h3-left))
55
46
  &:after
56
- svg-mask-icon($iH3Right)
47
+ svg-mask-icon(var(--icon-h3-right))
57
48
 
58
49
  h4:not([class])
59
50
  position: relative
@@ -115,10 +106,10 @@
115
106
  trans1 all
116
107
  &:before
117
108
  left: -0.3em
118
- svg-mask-icon($iQuoteLeft)
109
+ svg-mask-icon(var(--icon-quote-left))
119
110
  &:after
120
111
  right: -0.3em
121
- svg-mask-icon($iQuoteRight)
112
+ svg-mask-icon(var(--icon-quote-right))
122
113
  p:not([class])
123
114
  color: var(--text-p2)
124
115
  font-size: $fs-body