feat: 浅色/暗色主题并支持跟随系统
顶栏循环切换,localStorage 记忆;head 内联脚本避免首屏闪烁。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
## A. 浏览与布局
|
||||
|
||||
- [x] 三栏布局:左导航 / 中 Feed / 右栏小组件 — SSR(桌面;≤900px 隐藏右栏)
|
||||
- [ ] 浅色 / 暗色主题;跟随系统偏好并本地记忆
|
||||
- [x] 浅色 / 暗色主题;跟随系统偏好并本地记忆 — `data-theme` + `localStorage`(`j13-theme`);顶栏循环:跟随系统 / 浅色 / 暗色
|
||||
- [ ] 响应式:平板/手机收起侧栏
|
||||
- [ ] 长列表虚拟滚动或等价流畅方案
|
||||
- [x] Feed 排序:`latest`(最新发帖)/ `reply`(最新回复)/ `hot`(热门)
|
||||
|
||||
@@ -105,7 +105,10 @@
|
||||
|
||||
### 2.5 主题
|
||||
|
||||
- 浅色 / 暗色;`localStorage` 记忆;可跟随系统
|
||||
- 浅色 / 暗色;默认 **跟随系统**;`localStorage` 键 `j13-theme`(`system` | `light` | `dark`)
|
||||
- `<html data-theme="light|dark" data-theme-pref="…">`;`<head>` 内联脚本防首屏闪烁
|
||||
- 顶栏按钮循环切换;系统偏好变化时在 `system` 模式下即时更新
|
||||
- 兼容旧 SPA 仅存 `light`/`dark` 的值
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| **上一刀** | Markdown 编辑器(工具栏 + 预览,共用 md_editor) |
|
||||
| **下一刀** | 需点名(OIDC / S3 / Gitea / 主题 / 表格表情…) |
|
||||
| **上一刀** | 浅色/暗色主题(跟随系统 + localStorage) |
|
||||
| **下一刀** | 需点名(OIDC / S3 / Gitea / 表格表情…) |
|
||||
| **工作区** | 应干净 |
|
||||
|
||||
---
|
||||
|
||||
## 一句话状态
|
||||
|
||||
核心论坛、Admin、Limits/伪静态、嵌套评论、**Markdown 编辑器**已齐。TipTap / 表格 / 表情贴纸仍后置。
|
||||
核心论坛、Admin、Limits/伪静态、嵌套评论、Markdown 编辑器、**主题**已齐。余量多为后置与体验打磨。
|
||||
|
||||
---
|
||||
|
||||
@@ -30,12 +30,13 @@
|
||||
| Limits / 伪静态 / 品牌图 | 已迁 |
|
||||
| 评论嵌套树 UI | 已迁 |
|
||||
| Markdown 工具栏 + preview | 已迁 |
|
||||
| 浅色 / 暗色 / 跟随系统 | 已迁 |
|
||||
|
||||
---
|
||||
|
||||
## 未完成
|
||||
|
||||
- P3:主题、TipTip、游客评论、修订 diff
|
||||
- P3:TipTip、游客评论、修订 diff、响应式侧栏抽屉
|
||||
- 编辑器后置:TipTap、表格、图片组、表情贴纸
|
||||
- 后置:OIDC、S3 热切换、Gitea `/projects`
|
||||
|
||||
|
||||
@@ -1,13 +1,66 @@
|
||||
/* 姜十三论坛 SSR 骨架样式(web_src → public/assets) */
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--j13-bg: #f6f4ef;
|
||||
--j13-surface: #fffdf8;
|
||||
--j13-text: #1c1917;
|
||||
--j13-muted: #78716c;
|
||||
--j13-accent: #0f766e;
|
||||
--j13-on-accent: #fff;
|
||||
--j13-border: #e7e5e4;
|
||||
--j13-radius: 8px;
|
||||
--j13-font: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
--j13-danger: #b91c1c;
|
||||
--j13-ok-bg: #ecfdf5;
|
||||
--j13-ok-fg: #065f46;
|
||||
--j13-warn-bg: #fff7ed;
|
||||
--j13-warn-fg: #9a3412;
|
||||
--j13-warn-border: #fed7aa;
|
||||
--j13-err-bg: #fef2f2;
|
||||
--j13-err-fg: #991b1b;
|
||||
--j13-err-border: #fecaca;
|
||||
--j13-pin-bg: #fef3c7;
|
||||
--j13-pin-fg: #92400e;
|
||||
--j13-feat-bg: #ede9fe;
|
||||
--j13-feat-fg: #5b21b6;
|
||||
--j13-neutral-bg: #f8fafc;
|
||||
--j13-neutral-fg: #475569;
|
||||
--j13-self-bg: #eff6ff;
|
||||
--j13-self-border: #bfdbfe;
|
||||
--j13-delta-pos: #15803d;
|
||||
--j13-delta-neg: #b91c1c;
|
||||
--j13-overlay: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
--j13-bg: #1c1917;
|
||||
--j13-surface: #292524;
|
||||
--j13-text: #f5f5f4;
|
||||
--j13-muted: #a8a29e;
|
||||
--j13-accent: #2dd4bf;
|
||||
--j13-on-accent: #134e4a;
|
||||
--j13-border: #44403c;
|
||||
--j13-danger: #f87171;
|
||||
--j13-ok-bg: #064e3b;
|
||||
--j13-ok-fg: #a7f3d0;
|
||||
--j13-warn-bg: #7c2d12;
|
||||
--j13-warn-fg: #fed7aa;
|
||||
--j13-warn-border: #9a3412;
|
||||
--j13-err-bg: #7f1d1d;
|
||||
--j13-err-fg: #fecaca;
|
||||
--j13-err-border: #991b1b;
|
||||
--j13-pin-bg: #78350f;
|
||||
--j13-pin-fg: #fde68a;
|
||||
--j13-feat-bg: #4c1d95;
|
||||
--j13-feat-fg: #ddd6fe;
|
||||
--j13-neutral-bg: #334155;
|
||||
--j13-neutral-fg: #e2e8f0;
|
||||
--j13-self-bg: #1e3a5f;
|
||||
--j13-self-border: #1d4ed8;
|
||||
--j13-delta-pos: #4ade80;
|
||||
--j13-delta-neg: #f87171;
|
||||
--j13-overlay: rgba(28, 25, 23, 0.85);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
@@ -62,9 +115,25 @@ body.j13-body {
|
||||
.j13-brand__text { display: flex; flex-direction: column; }
|
||||
.j13-brand__text strong { font-size: 1rem; }
|
||||
.j13-brand__slogan { font-size: 0.75rem; color: var(--j13-muted); }
|
||||
.j13-nav { display: flex; gap: 0.85rem; flex-wrap: wrap; }
|
||||
.j13-nav { display: flex; gap: 0.85rem; flex-wrap: wrap; align-items: center; }
|
||||
.j13-nav a { color: var(--j13-accent); text-decoration: none; font-size: 0.9rem; }
|
||||
.j13-nav a:hover { text-decoration: underline; }
|
||||
.j13-theme-toggle {
|
||||
appearance: none;
|
||||
border: 1px solid var(--j13-border);
|
||||
background: var(--j13-surface);
|
||||
color: var(--j13-muted);
|
||||
border-radius: var(--j13-radius);
|
||||
padding: 0.2rem 0.55rem;
|
||||
font: inherit;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.j13-theme-toggle:hover {
|
||||
color: var(--j13-accent);
|
||||
border-color: var(--j13-accent);
|
||||
}
|
||||
|
||||
.j13-layout {
|
||||
max-width: 1200px;
|
||||
@@ -270,7 +339,7 @@ body.j13-body {
|
||||
}
|
||||
.j13-search__err {
|
||||
margin: 0.5rem 0 0;
|
||||
color: #b91c1c;
|
||||
color: var(--j13-danger);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
@@ -297,15 +366,15 @@ body.j13-body {
|
||||
align-items: center;
|
||||
}
|
||||
.j13-tag {
|
||||
background: #ecfdf5;
|
||||
background: var(--j13-ok-bg);
|
||||
color: var(--j13-accent);
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.j13-tag--pin { background: #fef3c7; color: #92400e; }
|
||||
.j13-tag--pending { background: #fff7ed; color: #9a3412; }
|
||||
.j13-tag--rejected { background: #fef2f2; color: #991b1b; }
|
||||
.j13-tag--pin { background: var(--j13-pin-bg); color: var(--j13-pin-fg); }
|
||||
.j13-tag--pending { background: var(--j13-warn-bg); color: var(--j13-warn-fg); }
|
||||
.j13-tag--rejected { background: var(--j13-err-bg); color: var(--j13-err-fg); }
|
||||
.j13-mod-banner {
|
||||
margin: 0.5rem 0 0.75rem;
|
||||
padding: 0.65rem 0.85rem;
|
||||
@@ -314,18 +383,18 @@ body.j13-body {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.j13-mod-banner--pending {
|
||||
background: #fff7ed;
|
||||
color: #9a3412;
|
||||
border: 1px solid #fed7aa;
|
||||
background: var(--j13-warn-bg);
|
||||
color: var(--j13-warn-fg);
|
||||
border: 1px solid var(--j13-warn-border);
|
||||
}
|
||||
.j13-mod-banner--rejected {
|
||||
background: #fef2f2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
background: var(--j13-err-bg);
|
||||
color: var(--j13-err-fg);
|
||||
border: 1px solid var(--j13-err-border);
|
||||
}
|
||||
.j13-tag--feat { background: #ede9fe; color: #5b21b6; }
|
||||
.j13-tag--resolved { background: #ecfdf5; color: #065f46; }
|
||||
.j13-tag--unresolved { background: #f8fafc; color: #475569; }
|
||||
.j13-tag--feat { background: var(--j13-feat-bg); color: var(--j13-feat-fg); }
|
||||
.j13-tag--resolved { background: var(--j13-ok-bg); color: var(--j13-ok-fg); }
|
||||
.j13-tag--unresolved { background: var(--j13-neutral-bg); color: var(--j13-neutral-fg); }
|
||||
|
||||
.j13-empty { color: var(--j13-muted); }
|
||||
.j13-pager {
|
||||
@@ -351,8 +420,8 @@ body.j13-body {
|
||||
padding: 0.65rem 1rem;
|
||||
border-radius: var(--j13-radius);
|
||||
}
|
||||
.j13-flash { background: #ecfdf5; color: #065f46; }
|
||||
.j13-alert--error { background: #fef2f2; color: #991b1b; }
|
||||
.j13-flash { background: var(--j13-ok-bg); color: var(--j13-ok-fg); }
|
||||
.j13-alert--error { background: var(--j13-err-bg); color: var(--j13-err-fg); }
|
||||
|
||||
.j13-main--solo {
|
||||
max-width: 480px;
|
||||
@@ -372,6 +441,8 @@ body.j13-body {
|
||||
border: 1px solid var(--j13-border);
|
||||
border-radius: var(--j13-radius);
|
||||
font: inherit;
|
||||
background: var(--j13-surface);
|
||||
color: var(--j13-text);
|
||||
}
|
||||
.j13-form button, .j13-post__actions button, .j13-comment-form button {
|
||||
margin-top: 0.5rem;
|
||||
@@ -379,7 +450,7 @@ body.j13-body {
|
||||
border: 0;
|
||||
border-radius: var(--j13-radius);
|
||||
background: var(--j13-accent);
|
||||
color: #fff;
|
||||
color: var(--j13-on-accent);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
@@ -478,7 +549,8 @@ body.j13-body {
|
||||
border: 1px solid var(--j13-border);
|
||||
border-radius: var(--j13-radius);
|
||||
font: inherit;
|
||||
background: #fff;
|
||||
background: var(--j13-surface);
|
||||
color: var(--j13-text);
|
||||
}
|
||||
.j13-form__row {
|
||||
display: flex;
|
||||
@@ -770,8 +842,8 @@ body.j13-body {
|
||||
vertical-align: top;
|
||||
}
|
||||
.j13-profile__ledger th { color: var(--j13-muted); font-weight: 600; }
|
||||
.j13-delta--pos { color: #15803d; }
|
||||
.j13-delta--neg { color: #b91c1c; }
|
||||
.j13-delta--pos { color: var(--j13-delta-pos); }
|
||||
.j13-delta--neg { color: var(--j13-delta-neg); }
|
||||
.j13-btn {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.85rem;
|
||||
@@ -862,8 +934,8 @@ body.j13-body {
|
||||
}
|
||||
.j13-msg-bubble.is-self {
|
||||
align-self: flex-end;
|
||||
background: #eff6ff;
|
||||
border-color: #bfdbfe;
|
||||
background: var(--j13-self-bg);
|
||||
border-color: var(--j13-self-border);
|
||||
}
|
||||
.j13-msg-bubble.is-system { max-width: 100%; }
|
||||
.j13-msg-bubble__subj { font-weight: 600; margin-bottom: 0.25rem; }
|
||||
@@ -980,7 +1052,7 @@ points-only[data-locked="false"] {
|
||||
height: 48px;
|
||||
object-fit: contain;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
background: var(--j13-surface);
|
||||
}
|
||||
.j13-links__logo--sm {
|
||||
width: 24px;
|
||||
@@ -1211,7 +1283,7 @@ points-only[data-locked="false"] {
|
||||
top: 0.4rem;
|
||||
left: 0.4rem;
|
||||
z-index: 1;
|
||||
background: rgba(255,255,255,0.85);
|
||||
background: var(--j13-overlay);
|
||||
border-radius: 3px;
|
||||
padding: 0.1rem 0.2rem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,73 @@
|
||||
// 姜十三论坛 SSR 渐进增强
|
||||
document.documentElement.dataset.j13Ssr = "1";
|
||||
|
||||
(function themeToggle() {
|
||||
var KEY = "j13-theme";
|
||||
var LABELS = { system: "跟随系统", light: "浅色", dark: "暗色" };
|
||||
var ORDER = ["system", "light", "dark"];
|
||||
|
||||
function normalizePref(raw) {
|
||||
if (raw === "light" || raw === "dark" || raw === "system") return raw;
|
||||
return "system";
|
||||
}
|
||||
|
||||
function resolve(pref) {
|
||||
if (pref === "dark") return "dark";
|
||||
if (pref === "light") return "light";
|
||||
try {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
} catch (e) {
|
||||
return "light";
|
||||
}
|
||||
}
|
||||
|
||||
function apply(pref) {
|
||||
pref = normalizePref(pref);
|
||||
var resolved = resolve(pref);
|
||||
document.documentElement.setAttribute("data-theme", resolved);
|
||||
document.documentElement.setAttribute("data-theme-pref", pref);
|
||||
document.documentElement.style.colorScheme = resolved;
|
||||
try {
|
||||
localStorage.setItem(KEY, pref);
|
||||
} catch (e) {}
|
||||
var btn = document.getElementById("j13-theme-toggle");
|
||||
if (btn) {
|
||||
btn.textContent = LABELS[pref] || LABELS.system;
|
||||
btn.setAttribute("aria-label", "主题:" + (LABELS[pref] || ""));
|
||||
btn.title = "当前:" + (LABELS[pref] || "") + "(点击切换)";
|
||||
}
|
||||
}
|
||||
|
||||
function currentPref() {
|
||||
try {
|
||||
return normalizePref(localStorage.getItem(KEY) || document.documentElement.getAttribute("data-theme-pref") || "system");
|
||||
} catch (e) {
|
||||
return "system";
|
||||
}
|
||||
}
|
||||
|
||||
apply(currentPref());
|
||||
|
||||
var btn = document.getElementById("j13-theme-toggle");
|
||||
if (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
var cur = currentPref();
|
||||
var idx = ORDER.indexOf(cur);
|
||||
var next = ORDER[(idx + 1) % ORDER.length];
|
||||
apply(next);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
var mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
var onChange = function () {
|
||||
if (currentPref() === "system") apply("system");
|
||||
};
|
||||
if (mq.addEventListener) mq.addEventListener("change", onChange);
|
||||
else if (mq.addListener) mq.addListener(onChange);
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
||||
(function mdEditors() {
|
||||
function insertAtCursor(textarea, text) {
|
||||
const start = textarea.selectionStart || 0;
|
||||
|
||||
@@ -10,6 +10,20 @@
|
||||
<meta property="og:title" content="{{.Title}}"/>
|
||||
{{if .Description}}<meta property="og:description" content="{{.Description}}"/>{{end}}
|
||||
{{if .OGImageURL}}<meta property="og:image" content="{{.OGImageURL}}"/>{{end}}
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
var key = "j13-theme";
|
||||
var pref = localStorage.getItem(key) || "system";
|
||||
if (pref !== "light" && pref !== "dark" && pref !== "system") pref = "system";
|
||||
var dark = pref === "dark" || (pref === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
var resolved = dark ? "dark" : "light";
|
||||
document.documentElement.setAttribute("data-theme", resolved);
|
||||
document.documentElement.setAttribute("data-theme-pref", pref);
|
||||
document.documentElement.style.colorScheme = resolved;
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="/ssr-assets/site.css"/>
|
||||
</head>
|
||||
<body class="j13-body">
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
</span>
|
||||
</a>
|
||||
<nav class="j13-nav">
|
||||
<button type="button" class="j13-theme-toggle" id="j13-theme-toggle" aria-label="切换主题" title="切换主题">主题</button>
|
||||
<a href="/#search">搜索</a>
|
||||
<a href="/boards">板块</a>
|
||||
{{if .ShowFriendLinksNav}}<a href="/links">友链</a>{{end}}
|
||||
|
||||
@@ -1,13 +1,66 @@
|
||||
/* 姜十三论坛 SSR 骨架样式(web_src → public/assets) */
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--j13-bg: #f6f4ef;
|
||||
--j13-surface: #fffdf8;
|
||||
--j13-text: #1c1917;
|
||||
--j13-muted: #78716c;
|
||||
--j13-accent: #0f766e;
|
||||
--j13-on-accent: #fff;
|
||||
--j13-border: #e7e5e4;
|
||||
--j13-radius: 8px;
|
||||
--j13-font: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
--j13-danger: #b91c1c;
|
||||
--j13-ok-bg: #ecfdf5;
|
||||
--j13-ok-fg: #065f46;
|
||||
--j13-warn-bg: #fff7ed;
|
||||
--j13-warn-fg: #9a3412;
|
||||
--j13-warn-border: #fed7aa;
|
||||
--j13-err-bg: #fef2f2;
|
||||
--j13-err-fg: #991b1b;
|
||||
--j13-err-border: #fecaca;
|
||||
--j13-pin-bg: #fef3c7;
|
||||
--j13-pin-fg: #92400e;
|
||||
--j13-feat-bg: #ede9fe;
|
||||
--j13-feat-fg: #5b21b6;
|
||||
--j13-neutral-bg: #f8fafc;
|
||||
--j13-neutral-fg: #475569;
|
||||
--j13-self-bg: #eff6ff;
|
||||
--j13-self-border: #bfdbfe;
|
||||
--j13-delta-pos: #15803d;
|
||||
--j13-delta-neg: #b91c1c;
|
||||
--j13-overlay: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
--j13-bg: #1c1917;
|
||||
--j13-surface: #292524;
|
||||
--j13-text: #f5f5f4;
|
||||
--j13-muted: #a8a29e;
|
||||
--j13-accent: #2dd4bf;
|
||||
--j13-on-accent: #134e4a;
|
||||
--j13-border: #44403c;
|
||||
--j13-danger: #f87171;
|
||||
--j13-ok-bg: #064e3b;
|
||||
--j13-ok-fg: #a7f3d0;
|
||||
--j13-warn-bg: #7c2d12;
|
||||
--j13-warn-fg: #fed7aa;
|
||||
--j13-warn-border: #9a3412;
|
||||
--j13-err-bg: #7f1d1d;
|
||||
--j13-err-fg: #fecaca;
|
||||
--j13-err-border: #991b1b;
|
||||
--j13-pin-bg: #78350f;
|
||||
--j13-pin-fg: #fde68a;
|
||||
--j13-feat-bg: #4c1d95;
|
||||
--j13-feat-fg: #ddd6fe;
|
||||
--j13-neutral-bg: #334155;
|
||||
--j13-neutral-fg: #e2e8f0;
|
||||
--j13-self-bg: #1e3a5f;
|
||||
--j13-self-border: #1d4ed8;
|
||||
--j13-delta-pos: #4ade80;
|
||||
--j13-delta-neg: #f87171;
|
||||
--j13-overlay: rgba(28, 25, 23, 0.85);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
@@ -62,9 +115,25 @@ body.j13-body {
|
||||
.j13-brand__text { display: flex; flex-direction: column; }
|
||||
.j13-brand__text strong { font-size: 1rem; }
|
||||
.j13-brand__slogan { font-size: 0.75rem; color: var(--j13-muted); }
|
||||
.j13-nav { display: flex; gap: 0.85rem; flex-wrap: wrap; }
|
||||
.j13-nav { display: flex; gap: 0.85rem; flex-wrap: wrap; align-items: center; }
|
||||
.j13-nav a { color: var(--j13-accent); text-decoration: none; font-size: 0.9rem; }
|
||||
.j13-nav a:hover { text-decoration: underline; }
|
||||
.j13-theme-toggle {
|
||||
appearance: none;
|
||||
border: 1px solid var(--j13-border);
|
||||
background: var(--j13-surface);
|
||||
color: var(--j13-muted);
|
||||
border-radius: var(--j13-radius);
|
||||
padding: 0.2rem 0.55rem;
|
||||
font: inherit;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.j13-theme-toggle:hover {
|
||||
color: var(--j13-accent);
|
||||
border-color: var(--j13-accent);
|
||||
}
|
||||
|
||||
.j13-layout {
|
||||
max-width: 1200px;
|
||||
@@ -270,7 +339,7 @@ body.j13-body {
|
||||
}
|
||||
.j13-search__err {
|
||||
margin: 0.5rem 0 0;
|
||||
color: #b91c1c;
|
||||
color: var(--j13-danger);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
@@ -297,15 +366,15 @@ body.j13-body {
|
||||
align-items: center;
|
||||
}
|
||||
.j13-tag {
|
||||
background: #ecfdf5;
|
||||
background: var(--j13-ok-bg);
|
||||
color: var(--j13-accent);
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.j13-tag--pin { background: #fef3c7; color: #92400e; }
|
||||
.j13-tag--pending { background: #fff7ed; color: #9a3412; }
|
||||
.j13-tag--rejected { background: #fef2f2; color: #991b1b; }
|
||||
.j13-tag--pin { background: var(--j13-pin-bg); color: var(--j13-pin-fg); }
|
||||
.j13-tag--pending { background: var(--j13-warn-bg); color: var(--j13-warn-fg); }
|
||||
.j13-tag--rejected { background: var(--j13-err-bg); color: var(--j13-err-fg); }
|
||||
.j13-mod-banner {
|
||||
margin: 0.5rem 0 0.75rem;
|
||||
padding: 0.65rem 0.85rem;
|
||||
@@ -314,18 +383,18 @@ body.j13-body {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.j13-mod-banner--pending {
|
||||
background: #fff7ed;
|
||||
color: #9a3412;
|
||||
border: 1px solid #fed7aa;
|
||||
background: var(--j13-warn-bg);
|
||||
color: var(--j13-warn-fg);
|
||||
border: 1px solid var(--j13-warn-border);
|
||||
}
|
||||
.j13-mod-banner--rejected {
|
||||
background: #fef2f2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
background: var(--j13-err-bg);
|
||||
color: var(--j13-err-fg);
|
||||
border: 1px solid var(--j13-err-border);
|
||||
}
|
||||
.j13-tag--feat { background: #ede9fe; color: #5b21b6; }
|
||||
.j13-tag--resolved { background: #ecfdf5; color: #065f46; }
|
||||
.j13-tag--unresolved { background: #f8fafc; color: #475569; }
|
||||
.j13-tag--feat { background: var(--j13-feat-bg); color: var(--j13-feat-fg); }
|
||||
.j13-tag--resolved { background: var(--j13-ok-bg); color: var(--j13-ok-fg); }
|
||||
.j13-tag--unresolved { background: var(--j13-neutral-bg); color: var(--j13-neutral-fg); }
|
||||
|
||||
.j13-empty { color: var(--j13-muted); }
|
||||
.j13-pager {
|
||||
@@ -351,8 +420,8 @@ body.j13-body {
|
||||
padding: 0.65rem 1rem;
|
||||
border-radius: var(--j13-radius);
|
||||
}
|
||||
.j13-flash { background: #ecfdf5; color: #065f46; }
|
||||
.j13-alert--error { background: #fef2f2; color: #991b1b; }
|
||||
.j13-flash { background: var(--j13-ok-bg); color: var(--j13-ok-fg); }
|
||||
.j13-alert--error { background: var(--j13-err-bg); color: var(--j13-err-fg); }
|
||||
|
||||
.j13-main--solo {
|
||||
max-width: 480px;
|
||||
@@ -372,6 +441,8 @@ body.j13-body {
|
||||
border: 1px solid var(--j13-border);
|
||||
border-radius: var(--j13-radius);
|
||||
font: inherit;
|
||||
background: var(--j13-surface);
|
||||
color: var(--j13-text);
|
||||
}
|
||||
.j13-form button, .j13-post__actions button, .j13-comment-form button {
|
||||
margin-top: 0.5rem;
|
||||
@@ -379,7 +450,7 @@ body.j13-body {
|
||||
border: 0;
|
||||
border-radius: var(--j13-radius);
|
||||
background: var(--j13-accent);
|
||||
color: #fff;
|
||||
color: var(--j13-on-accent);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
@@ -478,7 +549,8 @@ body.j13-body {
|
||||
border: 1px solid var(--j13-border);
|
||||
border-radius: var(--j13-radius);
|
||||
font: inherit;
|
||||
background: #fff;
|
||||
background: var(--j13-surface);
|
||||
color: var(--j13-text);
|
||||
}
|
||||
.j13-form__row {
|
||||
display: flex;
|
||||
@@ -770,8 +842,8 @@ body.j13-body {
|
||||
vertical-align: top;
|
||||
}
|
||||
.j13-profile__ledger th { color: var(--j13-muted); font-weight: 600; }
|
||||
.j13-delta--pos { color: #15803d; }
|
||||
.j13-delta--neg { color: #b91c1c; }
|
||||
.j13-delta--pos { color: var(--j13-delta-pos); }
|
||||
.j13-delta--neg { color: var(--j13-delta-neg); }
|
||||
.j13-btn {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.85rem;
|
||||
@@ -862,8 +934,8 @@ body.j13-body {
|
||||
}
|
||||
.j13-msg-bubble.is-self {
|
||||
align-self: flex-end;
|
||||
background: #eff6ff;
|
||||
border-color: #bfdbfe;
|
||||
background: var(--j13-self-bg);
|
||||
border-color: var(--j13-self-border);
|
||||
}
|
||||
.j13-msg-bubble.is-system { max-width: 100%; }
|
||||
.j13-msg-bubble__subj { font-weight: 600; margin-bottom: 0.25rem; }
|
||||
@@ -980,7 +1052,7 @@ points-only[data-locked="false"] {
|
||||
height: 48px;
|
||||
object-fit: contain;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
background: var(--j13-surface);
|
||||
}
|
||||
.j13-links__logo--sm {
|
||||
width: 24px;
|
||||
@@ -1211,7 +1283,7 @@ points-only[data-locked="false"] {
|
||||
top: 0.4rem;
|
||||
left: 0.4rem;
|
||||
z-index: 1;
|
||||
background: rgba(255,255,255,0.85);
|
||||
background: var(--j13-overlay);
|
||||
border-radius: 3px;
|
||||
padding: 0.1rem 0.2rem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,73 @@
|
||||
// 姜十三论坛 SSR 渐进增强
|
||||
document.documentElement.dataset.j13Ssr = "1";
|
||||
|
||||
(function themeToggle() {
|
||||
var KEY = "j13-theme";
|
||||
var LABELS = { system: "跟随系统", light: "浅色", dark: "暗色" };
|
||||
var ORDER = ["system", "light", "dark"];
|
||||
|
||||
function normalizePref(raw) {
|
||||
if (raw === "light" || raw === "dark" || raw === "system") return raw;
|
||||
return "system";
|
||||
}
|
||||
|
||||
function resolve(pref) {
|
||||
if (pref === "dark") return "dark";
|
||||
if (pref === "light") return "light";
|
||||
try {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
} catch (e) {
|
||||
return "light";
|
||||
}
|
||||
}
|
||||
|
||||
function apply(pref) {
|
||||
pref = normalizePref(pref);
|
||||
var resolved = resolve(pref);
|
||||
document.documentElement.setAttribute("data-theme", resolved);
|
||||
document.documentElement.setAttribute("data-theme-pref", pref);
|
||||
document.documentElement.style.colorScheme = resolved;
|
||||
try {
|
||||
localStorage.setItem(KEY, pref);
|
||||
} catch (e) {}
|
||||
var btn = document.getElementById("j13-theme-toggle");
|
||||
if (btn) {
|
||||
btn.textContent = LABELS[pref] || LABELS.system;
|
||||
btn.setAttribute("aria-label", "主题:" + (LABELS[pref] || ""));
|
||||
btn.title = "当前:" + (LABELS[pref] || "") + "(点击切换)";
|
||||
}
|
||||
}
|
||||
|
||||
function currentPref() {
|
||||
try {
|
||||
return normalizePref(localStorage.getItem(KEY) || document.documentElement.getAttribute("data-theme-pref") || "system");
|
||||
} catch (e) {
|
||||
return "system";
|
||||
}
|
||||
}
|
||||
|
||||
apply(currentPref());
|
||||
|
||||
var btn = document.getElementById("j13-theme-toggle");
|
||||
if (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
var cur = currentPref();
|
||||
var idx = ORDER.indexOf(cur);
|
||||
var next = ORDER[(idx + 1) % ORDER.length];
|
||||
apply(next);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
var mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
var onChange = function () {
|
||||
if (currentPref() === "system") apply("system");
|
||||
};
|
||||
if (mq.addEventListener) mq.addEventListener("change", onChange);
|
||||
else if (mq.addListener) mq.addListener(onChange);
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
||||
(function mdEditors() {
|
||||
function insertAtCursor(textarea, text) {
|
||||
const start = textarea.selectionStart || 0;
|
||||
|
||||
Reference in New Issue
Block a user