feat(v1.2.0): 主题配色系统 + Hero 文字效果/一言颜色;修复 /douban 500、友链悬浮光圈裁切、瞬间顶图间距;Halo 版本要求 2.26+

- 新增官方预设调色板 11 套(浅/深双套色板,玻璃/阴影自动跟随),强调色留空跟随配色
- Hero 一言独立颜色配置(浅/深);文字显示效果:阴影/光晕/半透明遮罩/渐变遮罩/容器;默认无效果,移除自带散光
- /douban 适配 plugin-douban 1.2.6 SQLite 扁平化数据结构(item.spec.* -> item.*)
- 友链卡片去掉 overflow:hidden,修复悬浮光圈被裁切;徽章简化为仅显示可达性
- 瞬间页顶图与首条卡片增加间距;theme-color 随配色/明暗联动
This commit is contained in:
2026-09-27 15:16:46 +08:00
parent a08782a959
commit a127d5a314
16 changed files with 627 additions and 70 deletions
+2 -1
View File
@@ -7,6 +7,7 @@
## 环境要求
- Halo **2.26.0 及以上**(页面布局契约、微信读书页面均依赖 2.26 的新能力)
- v1.2.0 起内置主题配色系统(纯 CSS 变量实现);更低版本的 Halo 请使用主题 v1.1.x
## 特点
@@ -30,7 +31,7 @@
| [友情链接](https://halo.run/store/apps/app-hfbQg) | `PluginLinks` | `/links` | 友链展示页面,支持随机排列、随机传送门、评论区开关 |
| [朋友圈](https://github.com/halo-sigs/plugin-friends) | `plugin-friends` | `/friends` | RSS 订阅朋友圈页面 |
| [装备管理](https://github.com/chengzhongxue/plugin-equipment) | `equipment` | `/equipments` | 装备展示页面,按分组展示,支持封面图 |
| [豆瓣](https://github.com/chengzhongxue/plugin-douban) | `plugin-douban` | `/douban` | 豆瓣记录页面,支持类型/状态/标签筛选,默认电影 |
| [豆瓣](https://github.com/chengzhongxue/plugin-douban) | `plugin-douban` | `/douban` | 豆瓣记录页面,支持类型/状态/标签筛选,默认音乐;需插件 **v1.2.6+**(SQLite 数据结构) |
| [Steam 信息展示](https://github.com/Tim0x0/halo-plugin-steam) | `PluginSteam` | `/steam` | Steam 游戏库页面,含用户资料、统计数据、最近游玩、游戏库分页 |
| [微信读书](https://github.com/yeora26/halo-weread-plugin) | `halo-weread-plugin` | 自定义页面 | 书架展示页面,见下方「自定义页面模板」 |
| [记得](https://github.com/yyliucha/plugin-important-dates) | `plugin-important-dates` | `/important-dates` | 重要日期 / 牵挂的人 / 爱车双视图页面,见下方「扩展页面」 |
+26
View File
@@ -1,5 +1,31 @@
# 更新日志
## v1.2.0
### 主题配色系统
- ✨ 新增「主题配色」设置(总体样式 → 主题配色),内置 11 套官方预调配色:暖屿·赤陶(默认)、抹茶、黛蓝、樱粉、暮紫、海盐、琥珀、梅酒、石墨、可拿铁、天青
- 🎨 每套配色包含浅色 / 深色两套完整色板,玻璃拟态、遮罩、阴影色调自动跟随当前配色
- 🎨 自定义「强调色」改为留空跟随所选配色;填写后仍可单独覆盖
- 📱 浏览器地址栏 theme-color 随配色与明暗模式联动(含切换按钮)
### Hero 首屏
- ✨ 一言文字新增独立颜色配置(浅色 / 深色模式各一项,此前只能跟随副标题颜色)
- ✨ 新增「文字显示效果」设置:阴影 / 光晕 / 半透明遮罩层 / 渐变遮罩 / 容器(半透明边框圆角矩形 + 加大留白)
- 🐛 默认改为无任何效果:移除标题、副标题与一言自带的白色散光投影
### 问题修复
- 🐛 修复 /douban 页面 500:适配豆瓣插件 v1.2.6 的 SQLite 数据结构(字段由 `item.spec.*` / `item.faves.*` 扁平化为 `item.*` / `item.faves*`)
- 🐛 修复友情链接卡片悬浮光圈被裁切的问题(卡片容器不再裁切悬浮阴影与上移位移)
- 🐛 修复瞬间页面顶图与最新一条瞬间卡片的边框贴合的问题,顶图下方增加间距
- 🐛 友情链接卡片徽章简化为仅展示站点可达性(绿点 = 可访问,红点 = 异常),不再联合反链检测结果判断
### 其他
- ⬆️ Halo 版本要求提升至 **2.26+**
## v1.1.1
### 瞬间页面
+35
View File
@@ -13,7 +13,17 @@ Hero 首屏是 WarmIsland 主题的标志性区域,占据首页首屏的全部
| `hero_hitokoto_api` | URL | `https://v1.hitokoto.cn/` | 一言 API 地址 |
| `hero_hitokoto_categories` | 多选 | `d, i, k` | 一言句子类型 |
| `hero_custom_description` | 多行文本 | — | 自定义描述文案 |
| `hero_hitokoto_auto_refresh` | 开关 | `false` | 一言自动刷新 |
| `hero_hitokoto_refresh_interval` | 下拉 | `10000` | 一言刷新间隔(秒) |
| `hero_background_image` | 附件 | — | 背景图片 |
| `hero_background_image_dark` | 附件 | — | 深色模式背景图片 |
| `hero_title_color_light` | 颜色 | — | 大标题颜色(浅色模式),留空使用主题默认 |
| `hero_title_color_dark` | 颜色 | — | 大标题颜色(深色模式),留空使用主题默认 |
| `hero_subtitle_color_light` | 颜色 | — | 副标题颜色(浅色模式),留空使用主题默认 |
| `hero_subtitle_color_dark` | 颜色 | — | 副标题颜色(深色模式),留空使用主题默认 |
| `hero_quote_color_light` | 颜色 | — | 一言文字颜色(浅色模式),留空使用主题默认 |
| `hero_quote_color_dark` | 颜色 | — | 一言文字颜色(深色模式),留空使用主题默认 |
| `hero_text_effect` | 下拉 | `none` | 文字显示效果:`none` / `shadow` / `glow` / `scrim` / `gradient` / `frame` |
## 详细说明
@@ -102,6 +112,31 @@ hero_custom_description: "用文字记录生活,用代码构建世界。"
背景图片会自动应用模糊和暗化处理,以确保前景文字的可读性。建议选择色彩柔和、不包含大量细节的图片作为背景,避免干扰文案阅读。
:::
### 文字颜色
标题、副标题、一言的颜色均可分别配置浅色 / 深色模式的取值,留空使用主题默认:
- **`hero_title_color_light` / `hero_title_color_dark`** — 大标题颜色。
- **`hero_subtitle_color_light` / `hero_subtitle_color_dark`** — 副标题颜色。
- **`hero_quote_color_light` / `hero_quote_color_dark`** — 一言文字颜色(`hero_description_mode` 为 `hitokoto` 或 `custom` 时生效)。
### 文字显示效果(hero_text_effect)
为 Hero 文字(标题 / 副标题 / 一言)叠加辅助可读效果。**默认 `none` 不使用任何效果**——v1.2.0 起,文字周围默认不再有淡淡的白色散光投影:
| 值 | 效果 | 说明 |
| --- | --- | --- |
| `none` | 无效果(默认) | 纯净文字,无任何附加投影与衬底 |
| `shadow` | 阴影 | 文字下方叠加柔和的深色投影,适合复杂背景图 |
| `glow` | 光晕 | 文字周围叠加白色光晕,暗色背景图上更醒目 |
| `scrim` | 半透明遮罩层 | 文字背后垫一块毛玻璃圆角衬底,整体提升可读性 |
| `gradient` | 渐变遮罩 | 文字背后垫斜向渐变衬底,左深右浅自然过渡 |
| `frame` | 容器 | 把文字"装"进一个带半透明边框的圆角矩形,加大内边距与字距留白 |
::: tip
遮罩类(`scrim` / `gradient`)与容器(`frame`)作用于整个文字区块,浅色模式为白色系衬底、深色模式为黑色系衬底;`frame` 会在浅色模式下使用墨色描边、深色模式下使用白色描边。
:::
## 视觉效果
Hero 区域包含以下精心设计的视觉效果:
+25 -2
View File
@@ -7,7 +7,8 @@
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `color_scheme` | 单选 | `system` | 配色方案:`system` / `light` / `dark` |
| `accent_color` | 颜色 | `#d4764e` | 全局强调色 |
| `color_palette` | 下拉 | `warm-island` | 主题配色(官方预设调色板,含深浅两套色板) |
| `accent_color` | 颜色 | — | 自定义强调色;留空跟随所选主题配色 |
| `border_radius` | 下拉 | `medium` | 圆角风格:`small` / `medium` / `large` |
| `layout_container_width` | 下拉 | `medium` | 容器宽度:`narrow` / `medium` / `wide` |
| `animation_enabled` | 开关 | `true` | 全局动画总开关 |
@@ -32,6 +33,28 @@
选择 `system` 时,主题会监听操作系统的 `prefers-color-scheme` 媒体查询,实时响应用户的系统设置变化。访问者也可以通过导航栏的主题切换按钮手动覆盖当前模式。
:::
### 主题配色(color_palette)
v1.2.0 起主题内置配色系统,提供 11 套官方预调配色,每套均包含浅色与深色两套完整色板(背景、文字、分隔线、强调色、玻璃拟态、阴影色调等会整体联动):
| 值 | 名称 | 色彩倾向 |
| --- | --- | --- |
| `warm-island` | 暖屿 · 赤陶(默认) | 暖橘陶土,米白纸感 |
| `matcha` | 抹茶 | 清新草绿,米绿纸感 |
| `indigo` | 黛蓝 | 沉稳蓝调,冷白纸感 |
| `sakura` | 樱粉 | 柔和粉调,樱白纸感 |
| `violet` | 暮紫 | 温柔紫调,淡紫纸感 |
| `sea` | 海盐 | 青碧蓝绿,浅青纸感 |
| `amber` | 琥珀 | 蜂蜜金黄,暖黄纸感 |
| `plum` | 梅酒 | 酒红梅紫,粉白纸感 |
| `graphite` | 石墨 | 中性灰黑,极简无彩 |
| `cocoa` | 可拿铁 | 咖啡可可,奶咖纸感 |
| `azure` | 天青 | 明快天蓝,浅蓝纸感 |
::: warning
下方「强调色」留空时,将使用所选配色自带的强调色。**老站点升级前如果保存过强调色(默认 `#d4764e`),切换配色后强调色不会变化——清空强调色并保存即可恢复跟随配色。**
:::
### 强调色(accent_color)
强调色是 WarmIsland 主题的灵魂,它会影响全站以下元素的颜色:
@@ -43,7 +66,7 @@
- 选中状态颜色
- Hero 区域渐变色的组成部分
默认值 `#d4764e` 是暖屿标志性的暖橘色,与主题的温暖气质相呼应。
留空时跟随上方「主题配色」自带的强调色(暖屿·赤陶配色即 `#d4764e` 暖橘色)。
::: tip
选择强调色时,建议同时考虑浅色和深色模式下的可读性。可以在 [CSS 变量](/customize/css-variables) 页面查看所有受影响的变量,进行更精细的调整。
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "1.1.12",
"version": "1.2.0",
"type": "module",
"engines": {
"node": ">=22.12.0"
+57 -3
View File
@@ -99,10 +99,38 @@ spec:
- label: 深色
value: dark
value: system
- $formkit: select
name: color_palette
label: 主题配色
options:
- label: 暖屿 · 赤陶(默认)
value: warm-island
- label: 抹茶
value: matcha
- label: 黛蓝
value: indigo
- label: 樱粉
value: sakura
- label: 暮紫
value: violet
- label: 海盐
value: sea
- label: 琥珀
value: amber
- label: 梅酒
value: plum
- label: 石墨
value: graphite
- label: 可拿铁
value: cocoa
- label: 天青
value: azure
value: warm-island
help: 整套官方预调配色(含深浅两套色板);下方的强调色留空时跟随所选配色,填写后将覆盖配色中的强调色
- $formkit: color
name: accent_color
label: 强调色
value: "#d4764e"
help: 自定义强调色;留空使用「主题配色」自带的强调色
- $formkit: select
name: border_radius
label: 圆角风格
@@ -329,11 +357,37 @@ spec:
- $formkit: color
name: hero_subtitle_color_light
label: 浅色模式副标题颜色
help: Hero 副标题和一言文字颜色(浅色模式);留空使用主题默认
help: Hero 副标题文字颜色(浅色模式);留空使用主题默认
- $formkit: color
name: hero_subtitle_color_dark
label: 深色模式副标题颜色
help: Hero 副标题和一言文字颜色(深色模式);留空使用主题默认
help: Hero 副标题文字颜色(深色模式);留空使用主题默认
- $formkit: color
name: hero_quote_color_light
label: 浅色模式一言颜色
help: 一言(描述文案来源为"一言"时)的文字颜色(浅色模式);留空使用主题默认
- $formkit: color
name: hero_quote_color_dark
label: 深色模式一言颜色
help: 一言的文字颜色(深色模式);留空使用主题默认
- $formkit: select
name: hero_text_effect
label: 文字显示效果
options:
- label: 无效果(默认)
value: none
- label: 阴影
value: shadow
- label: 光晕
value: glow
- label: 半透明遮罩层
value: scrim
- label: 渐变遮罩
value: gradient
- label: 容器(半透明边框圆角矩形)
value: frame
value: none
help: 为 Hero 文字(标题/副标题/一言)叠加的辅助可读效果;默认无效果。遮罩类与容器类会在文字背后垫一层衬底并增加留白
- group: home
label: 首页
formSchema:
+90 -16
View File
@@ -5,12 +5,14 @@
<style is:inline th:if="${theme.config?.hero?.hero_title_color_dark != null and theme.config?.hero?.hero_title_color_dark != ''}" th:utext="${' html.dark { --hero-title-text: ' + theme.config?.hero?.hero_title_color_dark + '; } '}">/*hero-title-dark*/</style>
<style is:inline th:if="${theme.config?.hero?.hero_subtitle_color_light != null and theme.config?.hero?.hero_subtitle_color_light != ''}" th:utext="${' :root { --hero-subtitle-text: ' + theme.config?.hero?.hero_subtitle_color_light + '; } '}">/*hero-subtitle-light*/</style>
<style is:inline th:if="${theme.config?.hero?.hero_subtitle_color_dark != null and theme.config?.hero?.hero_subtitle_color_dark != ''}" th:utext="${' html.dark { --hero-subtitle-text: ' + theme.config?.hero?.hero_subtitle_color_dark + '; } '}">/*hero-subtitle-dark*/</style>
<style is:inline th:if="${theme.config?.hero?.hero_quote_color_light != null and theme.config?.hero?.hero_quote_color_light != ''}" th:utext="${' :root { --hero-quote-text: ' + theme.config?.hero?.hero_quote_color_light + '; } '}">/*hero-quote-light*/</style>
<style is:inline th:if="${theme.config?.hero?.hero_quote_color_dark != null and theme.config?.hero?.hero_quote_color_dark != ''}" th:utext="${' html.dark { --hero-quote-text: ' + theme.config?.hero?.hero_quote_color_dark + '; } '}">/*hero-quote-dark*/</style>
<section
class="hero"
id="wi-hero"
th:if="${theme.config?.hero?.hero_enabled != false}"
th:classappend="${theme.config?.animation?.animation_breath == false} ? 'hero--no-breath'"
th:classappend="${(theme.config?.animation?.animation_breath == false ? 'hero--no-breath ' : '') + 'hero--fx-' + (theme.config?.hero?.hero_text_effect ?: 'none')}"
>
<div class="hero__bg">
<div class="hero__orb hero__orb--1"></div>
@@ -313,12 +315,6 @@
letter-spacing: -0.03em;
color: var(--hero-title-text, var(--ink));
line-height: var(--leading-tight);
/* 背景图完整显示后,用轻投影保证文字在复杂图片上仍可读 */
text-shadow: 0 1px 3px rgba(255, 253, 250, 0.6), 0 1px 14px rgba(255, 253, 250, 0.4);
}
html.dark .hero__title {
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55), 0 1px 14px rgba(0, 0, 0, 0.35);
}
.hero__subtitle {
@@ -327,25 +323,93 @@
color: var(--hero-subtitle-text, var(--ink-2));
letter-spacing: 0.05em;
line-height: var(--leading-normal);
text-shadow: 0 1px 3px rgba(255, 253, 250, 0.6), 0 1px 10px rgba(255, 253, 250, 0.4);
}
html.dark .hero__subtitle {
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55), 0 1px 10px rgba(0, 0, 0, 0.35);
}
.hero__description {
font-size: var(--text-lg);
color: var(--ink-2);
color: var(--hero-quote-text, var(--ink-2));
line-height: var(--leading-relaxed);
max-width: 560px;
text-align: center;
transition: opacity 0.3s ease;
text-shadow: 0 1px 3px rgba(255, 253, 250, 0.55);
}
html.dark .hero__description {
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
/* ===== 文字显示效果(后台「Hero 首屏 → 文字显示效果」,默认 none 无任何附加效果) ===== */
.hero--fx-shadow .hero__title,
.hero--fx-shadow .hero__subtitle,
.hero--fx-shadow .hero__description {
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.22), 0 2px 10px rgba(0, 0, 0, 0.16);
}
html.dark .hero--fx-shadow .hero__title,
html.dark .hero--fx-shadow .hero__subtitle,
html.dark .hero--fx-shadow .hero__description {
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), 0 2px 12px rgba(0, 0, 0, 0.4);
}
.hero--fx-glow .hero__title,
.hero--fx-glow .hero__subtitle,
.hero--fx-glow .hero__description {
text-shadow: 0 0 10px rgba(255, 255, 255, 0.85), 0 0 26px rgba(255, 255, 255, 0.5);
}
html.dark .hero--fx-glow .hero__title,
html.dark .hero--fx-glow .hero__subtitle,
html.dark .hero--fx-glow .hero__description {
text-shadow: 0 0 8px rgba(255, 255, 255, 0.45), 0 0 24px rgba(255, 255, 255, 0.22);
}
/* 半透明遮罩层:文字背后垫一块毛玻璃衬底 */
.hero--fx-scrim .hero__content {
background: rgba(255, 255, 255, 0.45);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 24px;
padding: var(--space-2xl);
}
html.dark .hero--fx-scrim .hero__content {
background: rgba(0, 0, 0, 0.4);
}
/* 渐变遮罩:斜向渐变衬底 */
.hero--fx-gradient .hero__content {
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.55) 0%,
rgba(255, 255, 255, 0.18) 100%
);
border-radius: 24px;
padding: var(--space-2xl);
}
html.dark .hero--fx-gradient .hero__content {
background: linear-gradient(
135deg,
rgba(0, 0, 0, 0.5) 0%,
rgba(0, 0, 0, 0.16) 100%
);
}
/* 容器:半透明边框圆角矩形 + 更大留白,把文字"装"进设计好的容器里 */
.hero--fx-frame .hero__content {
border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
background: color-mix(in srgb, var(--bg) 30%, transparent);
border-radius: 24px;
padding: var(--space-2xl) var(--space-3xl);
gap: var(--space-xl);
}
html.dark .hero--fx-frame .hero__content {
border-color: color-mix(in srgb, #ffffff 32%, transparent);
}
.hero--fx-frame .hero__title {
letter-spacing: 0.08em;
}
.hero--fx-frame .hero__subtitle {
letter-spacing: 0.22em;
}
.hero__scroll-indicator {
@@ -441,6 +505,16 @@
gap: var(--space-md);
}
.hero--fx-scrim .hero__content,
.hero--fx-gradient .hero__content {
padding: var(--space-xl) var(--space-lg);
}
.hero--fx-frame .hero__content {
padding: var(--space-xl) var(--space-lg);
gap: var(--space-lg);
}
.hero__description {
max-width: 90vw;
}
+2 -1
View File
@@ -57,8 +57,9 @@
document.documentElement.classList.toggle("dark", isDark);
document.documentElement.setAttribute("data-color-scheme", isDark ? "dark" : "light");
localStorage.setItem("wi-theme", isDark ? "dark" : "light");
var bgPair = window.WI_PALETTE_BG || { light: "#faf7f2", dark: "#1e1c1a" };
var tc = document.querySelector('meta[name="theme-color"]');
if (tc) tc.setAttribute("content", isDark ? "#1e1c1a" : "#faf7f2");
if (tc) tc.setAttribute("content", isDark ? bgPair.dark : bgPair.light);
updateIcons();
setTimeout(function() {
document.documentElement.classList.remove("wi-theme-transition");
+22 -5
View File
@@ -24,6 +24,7 @@ const fragmentAttr = layoutFragment ? "html (head, content)" : undefined;
class={home ? 'wi-home-snap' : ''}
style="overflow-x:hidden"
th:classappend="${theme.config?.animation?.animation_enabled == false} ? 'wi-no-animations'"
th:attr="data-palette=${theme.config?.style?.color_palette ?: 'warm-island'}"
xmlns:th={layoutFragment ? "https://www.thymeleaf.org" : undefined}
th:fragment={fragmentAttr}
>
@@ -32,7 +33,7 @@ const fragmentAttr = layoutFragment ? "html (head, content)" : undefined;
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#faf7f2" />
<meta th:if="${theme.config?.basic?.site_description}" name="description" th:content="${theme.config?.basic?.site_description}" />
<script is:inline th:attr="data-scheme=${theme.config?.style?.color_scheme}">
<script is:inline th:inline="none" th:attr="data-scheme=${theme.config?.style?.color_scheme}">
(function () {
var stored = localStorage.getItem("wi-theme");
var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
@@ -55,14 +56,30 @@ const fragmentAttr = layoutFragment ? "html (head, content)" : undefined;
if (isDark) {
document.documentElement.classList.add("dark");
document.documentElement.setAttribute("data-color-scheme", "dark");
var tc = document.querySelector('meta[name="theme-color"]');
if (tc) tc.setAttribute("content", "#1e1c1a");
} else {
document.documentElement.classList.remove("dark");
document.documentElement.setAttribute("data-color-scheme", "light");
var tc = document.querySelector('meta[name="theme-color"]');
if (tc) tc.setAttribute("content", "#faf7f2");
}
/* 浏览器地址栏颜色跟随当前配色(含主题配色系统) */
var paletteBg = {
"warm-island": ["#faf7f2", "#1e1c1a"],
"matcha": ["#f6f8f1", "#1b1f1a"],
"indigo": ["#f5f7fa", "#191d23"],
"sakura": ["#faf5f6", "#201b1c"],
"violet": ["#f7f5fa", "#1c1a21"],
"sea": ["#f3f8f7", "#181f1e"],
"amber": ["#faf7ef", "#201d16"],
"plum": ["#f9f4f5", "#1f1a1b"],
"graphite": ["#f7f7f6", "#1b1b1a"],
"cocoa": ["#f8f4f0", "#1e1a17"],
"azure": ["#f4f8fb", "#172026"]
};
var pal = document.documentElement.getAttribute("data-palette");
var pair = (pal && paletteBg[pal]) || paletteBg["warm-island"];
var tc = document.querySelector('meta[name="theme-color"]');
if (tc) tc.setAttribute("content", isDark ? pair[1] : pair[0]);
/* 供 ThemeSwitcher 切换深浅色时取当前配色的背景色 */
window.WI_PALETTE_BG = { light: pair[0], dark: pair[1] };
})();
</script>
<link
+21 -21
View File
@@ -119,68 +119,68 @@ import Layout from "../layouts/Layout.astro";
<article
class="wi-douban__card"
th:each="item : ${doubans.items}"
th:if="${!hasGenre or #sets.contains(item.spec?.genres, currentGenre)}"
th:if="${!hasGenre or #sets.contains(item.genres, currentGenre)}"
th:attr="data-type=${currentType}"
>
<a
class="wi-douban__card-link"
th:if="${!#strings.isEmpty(item.spec?.link)}"
th:href="${item.spec?.link}"
th:if="${!#strings.isEmpty(item.link)}"
th:href="${item.link}"
target="_blank"
rel="noopener noreferrer"
tabindex="-1"
aria-hidden="true"
></a>
<div class="wi-douban__poster" th:if="${!#strings.isEmpty(item.spec?.poster)}">
<div class="wi-douban__poster" th:if="${!#strings.isEmpty(item.poster)}">
<img
th:src="${item.spec?.poster}"
th:alt="${item.spec?.name}"
th:src="${item.poster}"
th:alt="${item.name}"
class="wi-douban__poster-img"
loading="lazy"
decoding="async"
/>
</div>
<div class="wi-douban__poster wi-douban__poster--placeholder" th:if="${#strings.isEmpty(item.spec?.poster)}">
<span th:text="${#strings.substring(item.spec?.name ?: '', 0, 1)}"></span>
<div class="wi-douban__poster wi-douban__poster--placeholder" th:if="${#strings.isEmpty(item.poster)}">
<span th:text="${#strings.substring(item.name ?: '', 0, 1)}"></span>
</div>
<div class="wi-douban__body">
<h3 class="wi-douban__name" th:text="${item.spec?.name}"></h3>
<h3 class="wi-douban__name" th:text="${item.name}"></h3>
<p
class="wi-douban__subtitle"
th:if="${!#strings.isEmpty(item.spec?.cardSubtitle)}"
th:text="${item.spec?.cardSubtitle}"
th:if="${!#strings.isEmpty(item.cardSubtitle)}"
th:text="${item.cardSubtitle}"
></p>
<div class="wi-douban__meta">
<span
class="wi-douban__score"
th:if="${!#strings.isEmpty(item.spec?.score) and item.spec?.score != '0'}"
th:if="${!#strings.isEmpty(item.score) and item.score != '0'}"
>
<svg class="wi-douban__star" viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
<th:block th:text="${item.spec?.score}"></th:block>
<th:block th:text="${item.score}"></th:block>
</span>
<span
class="wi-douban__my-score"
th:if="${!#strings.isEmpty(item.faves?.score)}"
th:if="${!#strings.isEmpty(item.favesScore)}"
>
<span th:text="'我的:'">My:</span><th:block th:text="${item.faves?.score}"></th:block>
<span th:text="'我的:'">My:</span><th:block th:text="${item.favesScore}"></th:block>
</span>
<span
class="wi-douban__year"
th:if="${!#strings.isEmpty(item.spec?.year)}"
th:text="${item.spec?.year}"
th:if="${!#strings.isEmpty(item.year)}"
th:text="${item.year}"
></span>
</div>
<div class="wi-douban__tags" th:if="${item.spec?.genres != null and !#sets.isEmpty(item.spec?.genres)}">
<div class="wi-douban__tags" th:if="${item.genres != null and !#sets.isEmpty(item.genres)}">
<span
class="wi-douban__tag"
th:each="genre : ${item.spec?.genres}"
th:each="genre : ${item.genres}"
th:text="${genre}"
></span>
</div>
<p
class="wi-douban__remark"
th:if="${!#strings.isEmpty(item.faves?.remark)}"
th:text="${item.faves?.remark}"
th:if="${!#strings.isEmpty(item.favesRemark)}"
th:text="${item.favesRemark}"
></p>
</div>
</article>
+8 -8
View File
@@ -54,8 +54,7 @@ import Layout from "../layouts/Layout.astro";
rel="noopener noreferrer"
class="wi-links__card"
th:with="verification=${link.status?.verification},
accessState=${verification?.access?.state != null ? verification.access.state.name() : null},
backlinkState=${verification?.backlink != null and verification.backlink.state != null ? verification.backlink.state.name() : null}"
accessState=${verification?.access?.state != null ? verification.access.state.name() : null}"
>
<div class="wi-links__card-glow"></div>
<div class="wi-links__card-inner">
@@ -74,16 +73,16 @@ import Layout from "../layouts/Layout.astro";
th:text="${#strings.substring(link.spec.displayName, 0, 1)}"
></span>
</div>
<!-- 反链 / 可达性检测徽章(plugin-links 后台「友链检测」产生,未检测过则不显示) -->
<!-- 可达性检测徽章(plugin-links 后台「友链检测」产生):绿点=可访问,红点=异常,未检测不显示 -->
<span
class="wi-links__check wi-links__check--ok"
th:if="${accessState == 'ACCESSIBLE' and backlinkState != 'MISSING'}"
th:title="${backlinkState == 'FOUND' ? '访问正常 · 反链有效' : '访问正常'}"
th:if="${accessState == 'ACCESSIBLE'}"
th:title="'站点访问正常'"
></span>
<span
class="wi-links__check wi-links__check--bad"
th:if="${accessState == 'INACCESSIBLE' or backlinkState == 'MISSING'}"
th:title="${accessState == 'INACCESSIBLE' ? '站点访问异常' : '未检测到反链'}"
th:if="${accessState == 'INACCESSIBLE'}"
th:title="'站点访问异常'"
></span>
</span>
<div class="wi-links__info">
@@ -523,8 +522,9 @@ import Layout from "../layouts/Layout.astro";
border-radius: 16px;
text-decoration: none;
color: inherit;
overflow: hidden;
display: flex;
/* 注意:这里绝不能加 overflow:hidden —— 悬浮时内层卡片会上移 4px 并投射大范围
阴影,裁切会把阴影切出一条"断开的光带"、卡片顶部被削平(v1.2.0 修复) */
}
.wi-links__card-glow {
+2
View File
@@ -411,6 +411,8 @@ import LightGallery from "../components/LightGallery.astro";
position: relative;
width: 100%;
height: clamp(220px, 30vw, 380px);
/* 顶图与首条瞬间的卡片边框不能贴死:留出与页面头部一致的呼吸间距 */
margin-bottom: var(--space-2xl);
overflow: hidden;
background: var(--bg-raised);
}
+13 -10
View File
@@ -1,7 +1,9 @@
:root {
--bg: #faf7f2;
--bg-raised: #f3ede5;
--bg-overlay: rgba(250, 247, 242, 0.85);
/* 派生变量基于 --bg / --rule 动态混合:调色板只需覆盖核心变量,
玻璃拟态、遮罩层等会自动跟随当前配色(暖屿默认时与旧值完全一致) */
--bg-overlay: color-mix(in srgb, var(--bg) 85%, transparent);
--ink: #2c2420;
--ink-2: #7a6e64;
--ink-3: #b5a99e;
@@ -12,19 +14,20 @@
--mist-pink: #f0e4de;
--sea-salt: #e8e0d6;
--caramel: #8b6f5e;
--glass-bg: rgba(250, 247, 242, 0.72);
--glass-border: rgba(232, 224, 214, 0.5);
--shadow-sm: 0 1px 3px rgba(44, 36, 32, 0.06);
--shadow-md: 0 4px 16px rgba(44, 36, 32, 0.08);
--shadow-lg: 0 8px 32px rgba(44, 36, 32, 0.12);
--shadow-glow: 0 0 40px rgba(212, 118, 78, 0.15);
--glass-bg: color-mix(in srgb, var(--bg) 72%, transparent);
--glass-border: color-mix(in srgb, var(--rule) 50%, transparent);
/* 阴影色由墨色动态混合,保证换配色后阴影色调一致 */
--shadow-sm: 0 1px 3px color-mix(in srgb, var(--ink) 6%, transparent);
--shadow-md: 0 4px 16px color-mix(in srgb, var(--ink) 8%, transparent);
--shadow-lg: 0 8px 32px color-mix(in srgb, var(--ink) 12%, transparent);
--shadow-glow: 0 0 40px color-mix(in srgb, var(--accent) 15%, transparent);
}
html.dark {
/* 暗色基准:偏中性、只保留一丝暖意,避免大面积黄褐底色带来的闷感 */
--bg: #1e1c1a;
--bg-raised: #292623;
--bg-overlay: rgba(30, 28, 26, 0.85);
--bg-overlay: color-mix(in srgb, var(--bg) 85%, transparent);
--ink: #e9e4dd;
--ink-2: #aaa096;
/* 深色背景下人眼对低对比更敏感,次要文字与分隔线比浅色模式略提亮 */
@@ -36,8 +39,8 @@ html.dark {
--mist-pink: #2e2823;
--sea-salt: #302b26;
--caramel: #a38b7b;
--glass-bg: rgba(30, 28, 26, 0.72);
--glass-border: rgba(59, 54, 47, 0.5);
--glass-bg: color-mix(in srgb, var(--bg) 72%, transparent);
--glass-border: color-mix(in srgb, var(--rule) 50%, transparent);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.24);
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.4);
+320
View File
@@ -0,0 +1,320 @@
/*
* 主题配色系统:官方预设调色板
* ---------------------------------------------------------------
* 每个[调色板]覆盖核心色彩变量(浅色 html[data-palette="x"] /
* 深色 html[data-palette="x"].dark 两套)。
* 派生变量(--bg-overlay、--glass-*、阴影色等)在 _colors.scss 中
* 由 --bg / --rule / --ink / --accent 动态混合而成,会自动跟随。
* data-palette 由 layout 头部根据后台「总体样式 → 主题配色」输出;
* 默认值 warm-island(暖屿·赤陶)即 _colors.scss 的基准值,无需覆盖。
*/
/* 抹茶 */
html[data-palette="matcha"] {
--bg: #f6f8f1;
--bg-raised: #ecf1e3;
--ink: #26302a;
--ink-2: #6d7a6e;
--ink-3: #a8b3a4;
--rule: #dde5d4;
--accent: #6f9d52;
--accent-hover: #5d8943;
--accent-bg: #ecf3e2;
--mist-pink: #e7eddc;
--sea-salt: #dfe8d3;
--caramel: #7d8a68;
}
html[data-palette="matcha"].dark {
--bg: #1b1f1a;
--bg-raised: #252b22;
--ink: #e6e9e0;
--ink-2: #a3ab9c;
--ink-3: #767e70;
--rule: #363d33;
--accent: #9cc47f;
--accent-hover: #aed493;
--accent-bg: #242c1f;
--mist-pink: #262d22;
--sea-salt: #282f24;
--caramel: #98a387;
}
/* 黛蓝 */
html[data-palette="indigo"] {
--bg: #f5f7fa;
--bg-raised: #eaeff5;
--ink: #232a33;
--ink-2: #66707d;
--ink-3: #a3adb8;
--rule: #dce2ea;
--accent: #4a6fa5;
--accent-hover: #3d5d8c;
--accent-bg: #e9eff8;
--mist-pink: #e2e8f0;
--sea-salt: #dbe4ee;
--caramel: #707c8e;
}
html[data-palette="indigo"].dark {
--bg: #191d23;
--bg-raised: #222831;
--ink: #e4e7ec;
--ink-2: #9aa4b1;
--ink-3: #6d7683;
--rule: #323a45;
--accent: #7fa3d1;
--accent-hover: #93b4dc;
--accent-bg: #1f2632;
--mist-pink: #232830;
--sea-salt: #252b34;
--caramel: #8b95a6;
}
/* 樱粉 */
html[data-palette="sakura"] {
--bg: #faf5f6;
--bg-raised: #f3eaec;
--ink: #322a2c;
--ink-2: #7d6e72;
--ink-3: #b4a6aa;
--rule: #ecdfe2;
--accent: #d97387;
--accent-hover: #c85d72;
--accent-bg: #fbeef1;
--mist-pink: #f2e2e6;
--sea-salt: #eddfe3;
--caramel: #96757e;
}
html[data-palette="sakura"].dark {
--bg: #201b1c;
--bg-raised: #2a2425;
--ink: #ece5e6;
--ink-2: #b0a4a7;
--ink-3: #7f7276;
--rule: #3c3436;
--accent: #e89aab;
--accent-hover: #f0aec0;
--accent-bg: #2d2225;
--mist-pink: #2f2729;
--sea-salt: #31282a;
--caramel: #a88d94;
}
/* 暮紫 */
html[data-palette="violet"] {
--bg: #f7f5fa;
--bg-raised: #eeeaf5;
--ink: #2b2733;
--ink-2: #6f6a7c;
--ink-3: #aaa5b6;
--rule: #e2ddeb;
--accent: #8a6fc0;
--accent-hover: #7759ae;
--accent-bg: #f0eaf9;
--mist-pink: #e8e2f0;
--sea-salt: #e3dcee;
--caramel: #7f7590;
}
html[data-palette="violet"].dark {
--bg: #1c1a21;
--bg-raised: #26222f;
--ink: #e8e5ee;
--ink-2: #a49eaf;
--ink-3: #746e80;
--rule: #373241;
--accent: #b49ae0;
--accent-hover: #c3ace8;
--accent-bg: #262033;
--mist-pink: #292434;
--sea-salt: #2b2536;
--caramel: #948caa;
}
/* 海盐 */
html[data-palette="sea"] {
--bg: #f3f8f7;
--bg-raised: #e8f0ee;
--ink: #21302d;
--ink-2: #647872;
--ink-3: #9fb1ac;
--rule: #d9e6e3;
--accent: #3d9c8f;
--accent-hover: #2f8578;
--accent-bg: #e5f3f0;
--mist-pink: #dee9e6;
--sea-salt: #d6e5e1;
--caramel: #6c8680;
}
html[data-palette="sea"].dark {
--bg: #181f1e;
--bg-raised: #212a28;
--ink: #e3eae8;
--ink-2: #9aaba6;
--ink-3: #6d7d79;
--rule: #2f3b39;
--accent: #6cc3b4;
--accent-hover: #82d0c2;
--accent-bg: #1d2a27;
--mist-pink: #222c2a;
--sea-salt: #242e2c;
--caramel: #869894;
}
/* 琥珀 */
html[data-palette="amber"] {
--bg: #faf7ef;
--bg-raised: #f3eddc;
--ink: #322b1e;
--ink-2: #7d7360;
--ink-3: #b5ab94;
--rule: #eae1cb;
--accent: #b97f1c;
--accent-hover: #9f6a0e;
--accent-bg: #f9efd8;
--mist-pink: #f0e8d2;
--sea-salt: #ebe2ca;
--caramel: #96835c;
}
html[data-palette="amber"].dark {
--bg: #201d16;
--bg-raised: #2a261d;
--ink: #ebe6d9;
--ink-2: #b0a893;
--ink-3: #7f7866;
--rule: #3c3729;
--accent: #e0a83e;
--accent-hover: #ebba5c;
--accent-bg: #2c251a;
--mist-pink: #2f2a1e;
--sea-salt: #312c20;
--caramel: #a89a76;
}
/* 梅酒 */
html[data-palette="plum"] {
--bg: #f9f4f5;
--bg-raised: #f1e8ea;
--ink: #2f2427;
--ink-2: #796a6f;
--ink-3: #b1a2a7;
--rule: #e9dde0;
--accent: #a4506b;
--accent-hover: #8d3f59;
--accent-bg: #f7e9ee;
--mist-pink: #ecdee2;
--sea-salt: #e5d8db;
--caramel: #8d6f77;
}
html[data-palette="plum"].dark {
--bg: #1f1a1b;
--bg-raised: #292223;
--ink: #eae2e4;
--ink-2: #ada0a4;
--ink-3: #7c6e73;
--rule: #3a3133;
--accent: #d98aa3;
--accent-hover: #e39fb5;
--accent-bg: #2b2023;
--mist-pink: #2d2527;
--sea-salt: #2f2729;
--caramel: #a08890;
}
/* 石墨 */
html[data-palette="graphite"] {
--bg: #f7f7f6;
--bg-raised: #eeedeb;
--ink: #262626;
--ink-2: #6e6e6c;
--ink-3: #ababa8;
--rule: #e0e0de;
--accent: #3f3f3e;
--accent-hover: #262626;
--accent-bg: #ececea;
--mist-pink: #e9e9e7;
--sea-salt: #e5e5e3;
--caramel: #7b7b78;
}
html[data-palette="graphite"].dark {
--bg: #1b1b1a;
--bg-raised: #252524;
--ink: #e8e8e6;
--ink-2: #a5a5a2;
--ink-3: #757572;
--rule: #353534;
--accent: #c9c9c5;
--accent-hover: #d8d8d4;
--accent-bg: #262625;
--mist-pink: #2a2a29;
--sea-salt: #2c2c2b;
--caramel: #8f8f8c;
}
/* 可拿铁 */
html[data-palette="cocoa"] {
--bg: #f8f4f0;
--bg-raised: #f0e8e1;
--ink: #2e2722;
--ink-2: #786c62;
--ink-3: #b3a89d;
--rule: #e7ddd3;
--accent: #9a6b4f;
--accent-hover: #835539;
--accent-bg: #f5e9e0;
--mist-pink: #ecdfd4;
--sea-salt: #e6d9cd;
--caramel: #8a6f5b;
}
html[data-palette="cocoa"].dark {
--bg: #1e1a17;
--bg-raised: #282320;
--ink: #eae4dd;
--ink-2: #aca199;
--ink-3: #7b7169;
--rule: #3a332e;
--accent: #cd9878;
--accent-hover: #daa98c;
--accent-bg: #2a211c;
--mist-pink: #2d2721;
--sea-salt: #2f2923;
--caramel: #a08d7c;
}
/* 天青 */
html[data-palette="azure"] {
--bg: #f4f8fb;
--bg-raised: #e9f1f7;
--ink: #223038;
--ink-2: #62737e;
--ink-3: #a0b0ba;
--rule: #dbe7ee;
--accent: #3b82c4;
--accent-hover: #2e6ba5;
--accent-bg: #e7f1f9;
--mist-pink: #e0ebf2;
--sea-salt: #d8e6ef;
--caramel: #6d8090;
}
html[data-palette="azure"].dark {
--bg: #172026;
--bg-raised: #202a31;
--ink: #e3eaef;
--ink-2: #99a8b3;
--ink-3: #6b7a85;
--rule: #2e3a42;
--accent: #74b3e0;
--accent-hover: #88c1e8;
--accent-bg: #1d2830;
--mist-pink: #222c34;
--sea-salt: #243038;
--caramel: #8496a3;
}
+1
View File
@@ -1,6 +1,7 @@
@use 'variables' as *;
@use 'mixins';
@use 'colors';
@use 'palettes';
@use 'typography';
@use 'content';
@use 'spacing';
+2 -2
View File
@@ -28,8 +28,8 @@ spec:
description: 微信读书书架(依赖微信读书插件)
screenshot:
file: weread.html
version: "1.1.12"
requires: ">=2.24.0"
version: "1.2.0"
requires: ">=2.26.0"
license:
- name: "GPL-3.0"
url: "https://www.gnu.org/licenses/gpl-3.0.en.html"