Files
jiang13-bbs/frontend/app/globals.css
freefire 52124f2682 feat: 新增帖子最后回复展示、文章目录与三栏布局,优化点赞按钮与导航栏
这是一次包含多个功能升级的提交:
1. 后端新增批量查询帖子最后回复的逻辑,在前端帖子列表项中展示最后回复信息
2. 重构点赞按钮,新增文章样式变体并统一样式逻辑
3. 重写工作台导航栏,精简默认展示的菜单选项
4. 新增文章目录组件,为帖子详情页添加文章目录导航
5. 重构帖子详情页,实现三栏响应式布局,新增板块卡片、作者信息栏与同板块热议推荐
6. 重构帖子列表行组件,优化排版与信息展示逻辑
7. 重构帖子管理操作组件,使用下拉菜单整合所有管理功能
8. 新增全局通用的滚动容器与点赞按钮样式
2026-09-13 04:06:56 +08:00

774 lines
21 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@import "tailwindcss";
/* ============================================================
姜十三论坛 · GRAPHITE INDIGO 设计系统
冷调石墨灰阶 / 纯白卡片 / 靛蓝主色 / 近黑锌灰暗色
============================================================ */
:root {
/* 浅色(默认)——冷调石墨:底色微蓝灰,面板纯白浮起,层次干净锐利 */
--bg: #f6f7f9;
--panel: #ffffff;
--panel-2: #f2f4f7;
--tint: #eceff3;
--ink: #161a22;
--ink-2: #4b5260;
--ink-3: #5b6270;
--line: #e4e7ec; /* 装饰性分隔/卡片边(非交互,配合阴影与底色高差) */
--line-2: #7c8698; /* 功能性边界:输入框/按钮/分页等可交互控件,满足 1.4.11 ≥3:1 */
--accent: #3b5bdb;
--accent-hi: #3451c8;
--accent-soft: rgba(59, 91, 219, 0.1);
--accent-on: #ffffff;
--clay: #a5420a;
--clay-soft: rgba(165, 66, 10, 0.12);
--gold: #825700;
--gold-soft: rgba(130, 87, 0, 0.13);
--danger: #c8281e;
--danger-soft: rgba(200, 40, 30, 0.09);
--ok: #137333;
--ok-soft: rgba(19, 115, 51, 0.1);
/* 工作台语义:导航选中态 / 榜单奖牌色 */
--nav-active-bg: var(--accent-soft);
--nav-active-ink: var(--accent);
--nav-active-line: rgba(59, 91, 219, 0.22);
--rank-1: #96620f;
--rank-2: #5d6676;
--rank-3: #a05a2c;
--shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.05);
--shadow: 0 2px 8px rgba(16, 24, 40, 0.05), 0 10px 28px rgba(16, 24, 40, 0.06);
--shadow-lg: 0 6px 16px rgba(16, 24, 40, 0.08), 0 20px 48px rgba(16, 24, 40, 0.12);
/* next/font 构建期自托管,变量由 layout.tsx 注入;
本地系统字体仅作网络字体分片就绪前的回退(PingFang/雅黑与 Noto 观感接近) */
--font-display: var(--font-jakarta), var(--font-noto-sc), -apple-system,
"PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
--font-sans: var(--font-jakarta), var(--font-noto-sc), -apple-system,
"PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
--font-mono: var(--font-jetbrains), var(--font-noto-sc), ui-monospace,
"Cascadia Code", Consolas, monospace;
--r: 18px;
--r-sm: 12px;
color-scheme: light;
}
.dark {
/* 近黑锌灰:面板逐层浮起,靛蓝提亮为长春花蓝 */
--bg: #0f1115;
--panel: #181b21;
--panel-2: #20242c;
--tint: #282d37;
--ink: #eef1f6;
--ink-2: #b6bdc9;
--ink-3: #98a1b2;
--line: rgba(238, 241, 246, 0.08); /* 装饰性分隔/卡片边 */
--line-2: rgba(238, 241, 246, 0.38); /* 功能性控件边界,1.4.11 ≥3:1 */
--accent: #8ea2ff;
--accent-hi: #a9b8ff;
--accent-soft: rgba(141, 163, 255, 0.16);
--accent-on: #10162b;
--clay: #f0875a;
--clay-soft: rgba(240, 135, 90, 0.16);
--gold: #e0b259;
--gold-soft: rgba(224, 178, 89, 0.16);
--danger: #ff6b60;
--danger-soft: rgba(255, 107, 96, 0.15);
--ok: #58d18a;
--ok-soft: rgba(88, 209, 138, 0.15);
/* 工作台语义 */
--nav-active-bg: rgba(141, 163, 255, 0.14);
--nav-active-ink: #a9b8ff;
--nav-active-line: rgba(141, 163, 255, 0.3);
--rank-1: #e3bf74;
--rank-2: #b9c1cd;
--rank-3: #e0a06e;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 12px 32px rgba(0, 0, 0, 0.22);
--shadow-lg: 0 6px 18px rgba(0, 0, 0, 0.35), 0 24px 56px rgba(0, 0, 0, 0.3);
color-scheme: dark;
}
html {
scrollbar-color: var(--line-2) transparent;
}
body {
background-color: var(--bg);
color: var(--ink);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
::selection {
background: var(--accent);
color: var(--accent-on);
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background: var(--line-2);
border-radius: 99px;
border: 3px solid var(--bg);
}
/* 链接重置放入 base 层:避免未分层规则击败 components 层的 .btn-primary 等类
(Tailwind v4 中未分层样式优先级高于任何 @layer) */
@layer base {
/* 必须在 base 层:未分层的 * 选择器会击败 @layer components 中所有
border-color(.field/.btn-line/.pager-btn 等曾因此实际渲染成 --line) */
*,
::before,
::after {
border-color: var(--line);
}
a {
color: inherit;
text-decoration: none;
}
}
h1, h2, h3, h4 {
font-family: var(--font-display);
letter-spacing: -0.015em;
}
:focus-visible {
outline: 2.5px solid var(--accent);
outline-offset: 2px;
border-radius: 6px;
}
@layer components {
/* ----- 小标签(旧名 .kicker 保留兼容) ----- */
.kicker {
font-family: var(--font-sans);
font-size: 12.5px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--ink-3);
}
/* ----- 卡片 ----- */
.panel {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--r);
box-shadow: var(--shadow-sm);
}
.hover-lift {
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.hover-lift:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
border-color: var(--line-2);
}
/* 整行可点:标题链接的 ::after 铺满最近的 relative 行容器,
行内其他交互元素加 .above-link 抬到点击层之上 */
.stretch-link::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
}
.above-link {
position: relative;
z-index: 2;
}
/* ----- 按钮(胶囊) ----- */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
padding: 10px 20px;
border-radius: 999px;
font-size: 14px;
font-weight: 600;
line-height: 1;
border: 1px solid transparent;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, color 0.15s,
transform 0.12s ease, box-shadow 0.15s;
white-space: nowrap;
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary {
background: var(--accent);
color: var(--accent-on);
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.12), 0 2px 8px rgba(59, 91, 219, 0.22);
}
.btn-primary:hover:not(:disabled) {
background: var(--accent-hi);
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(16, 24, 40, 0.1), 0 8px 20px rgba(59, 91, 219, 0.3);
}
.btn-primary:active:not(:disabled) {
transform: translateY(0);
}
.btn-line {
background: var(--panel);
border-color: var(--line-2);
color: var(--ink);
}
.btn-line:hover:not(:disabled) {
border-color: var(--accent);
color: var(--accent);
}
.btn-danger {
background: var(--danger-soft);
color: var(--danger);
}
.btn-danger:hover:not(:disabled) {
background: var(--danger);
color: #fff;
}
.dark .btn-danger:hover:not(:disabled) {
/* 暗色 danger 为亮红,白字仅 2.79:1,改用深色文字 */
color: #1a0c0b;
}
.btn-sm { padding: 7px 14px; font-size: 13px; }
/* ----- 点赞按钮:未赞描边态,hover 同色系填充;已赞为浅底实心态 ----- */
.j13-like {
border-color: var(--line-2);
color: var(--ink-2);
background: var(--panel);
transition:
border-color 0.15s ease,
color 0.15s ease,
background-color 0.15s ease,
box-shadow 0.15s ease;
}
.j13-like svg {
transition: transform 0.15s ease;
}
.j13-like:hover:not(:disabled),
.j13-like.is-liked {
border-color: var(--accent);
color: var(--accent);
background: var(--accent-soft);
}
/* 已赞态 hover 再加深一档,并给同色系投影;心形轻微放大(不改变布局) */
.j13-like:hover:not(:disabled) {
box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 22%, transparent);
}
.j13-like.is-liked:hover:not(:disabled) {
background: color-mix(in srgb, var(--accent) 17%, transparent);
}
.j13-like:hover:not(:disabled) svg {
transform: scale(1.15);
}
.j13-like:active:not(:disabled) svg {
transform: scale(0.95);
}
.j13-like:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.j13-like,
.j13-like svg {
transition: none;
}
.j13-like:hover:not(:disabled) svg,
.j13-like:active:not(:disabled) svg {
transform: none;
}
}
/* ----- 表单 ----- */
.field-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--ink-2);
margin-bottom: 8px;
}
.field {
width: 100%;
padding: 12px 15px;
background: var(--bg);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
color: var(--ink);
font-size: 15px;
font-family: var(--font-sans);
transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
/* 占位符不使用 opacity:透明度与底色合成后浅色仅 3.7:1,实色 ink-3 达 5.72:1 */
.field::placeholder { color: var(--ink-3); }
.field:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3.5px var(--accent-soft);
background: var(--panel);
}
textarea.field {
font-family: var(--font-sans);
font-size: 15px;
line-height: 1.75;
resize: vertical;
}
/* ----- 标签 / 徽标 ----- */
.chip {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12.5px;
font-weight: 500;
padding: 4px 12px;
background: var(--tint);
color: var(--ink-2);
border-radius: 999px;
white-space: nowrap;
transition: background 0.15s, color 0.15s;
}
a.chip:hover {
background: var(--accent-soft);
color: var(--accent);
}
.badge-pin {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
font-weight: 700;
padding: 4px 11px;
background: var(--clay-soft);
color: var(--clay);
border-radius: 999px;
white-space: nowrap;
}
.badge-rec {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
font-weight: 700;
padding: 4px 11px;
background: var(--gold-soft);
color: var(--gold);
border-radius: 999px;
white-space: nowrap;
}
/* ----- 分页 ----- */
.pager-btn {
background: var(--panel);
border-color: var(--line-2);
color: var(--ink-2);
}
.pager-btn:hover {
border-color: var(--accent);
color: var(--accent);
transform: translateY(-1px);
}
.pager-btn.is-current,
.pager-btn.is-current:hover {
background: var(--accent);
border-color: var(--accent);
color: var(--accent-on);
font-weight: 700;
}
/* ----- 辅助 ----- */
.meta {
font-family: var(--font-sans);
font-size: 12.5px;
color: var(--ink-3);
font-variant-numeric: tabular-nums;
}
.link-accent { color: var(--accent); }
.link-accent:hover { color: var(--accent-hi); }
.alert-error {
padding: 12px 15px;
border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
background: var(--danger-soft);
color: var(--danger);
border-radius: var(--r-sm);
font-size: 14px;
font-weight: 500;
}
.alert-ok {
padding: 12px 15px;
border: 1px solid color-mix(in srgb, var(--ok) 30%, transparent);
background: var(--ok-soft);
color: var(--ok);
border-radius: var(--r-sm);
font-size: 14px;
font-weight: 500;
}
/* ============================================================
工作台组件(WORKSPACE EDITION)
============================================================ */
/* 分段控件:凹陷轨道 + 凸起的白色选中胶囊 */
.j13-seg {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 4px;
background: var(--tint);
border: 1px solid var(--line);
border-radius: 999px;
}
.j13-seg [role="tab"] {
appearance: none;
border: 1px solid transparent;
background: transparent;
border-radius: 999px;
padding: 6.5px 15px;
font-family: var(--font-sans);
font-size: 13.5px;
font-weight: 600;
color: var(--ink-3);
cursor: pointer;
white-space: nowrap;
transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease,
transform 0.12s ease;
}
.j13-seg [role="tab"]:hover {
color: var(--ink);
}
.j13-seg [role="tab"][aria-selected="true"] {
background: var(--panel);
color: var(--accent);
box-shadow: var(--shadow-sm);
font-weight: 700;
}
.j13-seg [role="tab"]:active {
transform: scale(0.97);
}
/* 侧栏导航项 */
.j13-raillink {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border: 1px solid transparent;
border-radius: var(--r-sm);
font-size: 14px;
font-weight: 500;
color: var(--ink-2);
transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;
}
.j13-raillink:hover {
background: var(--panel-2);
color: var(--ink);
}
.j13-raillink[aria-current="page"] {
background: var(--nav-active-bg);
border-color: var(--nav-active-line);
color: var(--nav-active-ink);
font-weight: 700;
}
/* 工作台模块卡 */
.j13-module {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--r);
box-shadow: var(--shadow-sm);
padding: 16px;
}
.j13-module-title {
display: flex;
align-items: center;
gap: 7px;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.02em;
color: var(--ink);
}
/* 统计数字格 */
.j13-stat {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
padding: 11px 13px;
background: var(--panel-2);
border: 1px solid var(--line);
border-radius: var(--r-sm);
}
.j13-stat .j13-stat-num {
font-size: 22px;
font-weight: 800;
line-height: 1.15;
letter-spacing: -0.015em;
color: var(--ink);
font-variant-numeric: tabular-nums;
}
.j13-stat .j13-stat-lab {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--ink-3);
white-space: nowrap;
}
/* 热门榜单 */
.j13-rank {
position: relative;
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px 8px;
border-radius: 10px;
transition: background 0.16s ease;
}
.j13-rank:hover {
background: var(--panel-2);
}
.j13-rank .j13-rank-no {
width: 18px;
padding-top: 2px;
text-align: center;
font-size: 13px;
font-weight: 800;
font-variant-numeric: tabular-nums;
color: var(--ink-3);
flex-shrink: 0;
}
.j13-rank:nth-child(1) .j13-rank-no { color: var(--rank-1); }
.j13-rank:nth-child(2) .j13-rank-no { color: var(--rank-2); }
.j13-rank:nth-child(3) .j13-rank-no { color: var(--rank-3); }
/* 精选置顶卡 */
.j13-feature {
position: relative;
display: block;
padding: 15px 17px;
background: linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 100%);
border: 1px solid var(--line);
border-radius: var(--r);
box-shadow: var(--shadow-sm);
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.j13-feature:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
border-color: color-mix(in srgb, var(--accent) 32%, var(--line));
}
/* 移动端横向板块条(滚动条隐藏,保留触控惯性) */
.j13-chips {
display: flex;
gap: 8px;
overflow-x: auto;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
padding-bottom: 2px;
}
.j13-chips::-webkit-scrollbar {
display: none;
}
.j13-chip {
display: inline-flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
padding: 7px 14px;
border: 1px solid var(--line-2);
background: var(--panel);
border-radius: 999px;
font-size: 13px;
font-weight: 600;
color: var(--ink-2);
white-space: nowrap;
transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;
}
.j13-chip:hover {
border-color: var(--line-2);
color: var(--ink);
}
.j13-chip[aria-current="page"] {
background: var(--nav-active-bg);
border-color: var(--nav-active-line);
color: var(--nav-active-ink);
}
/* 帖子详情侧栏内的滚动承载区(目录 / 作者帖子列表):
配合栏容器的 max-height 独立滚动,细滚动条、不产生滚动链 */
.j13-rail-scroll {
min-height: 3.5rem;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
}
.j13-rail-scroll::-webkit-scrollbar {
width: 6px;
}
.j13-rail-scroll::-webkit-scrollbar-thumb {
background: var(--line-2);
border-radius: 99px;
border: none;
}
.j13-rail-scroll::-webkit-scrollbar-track {
background: transparent;
}
}
/* ============================================================
Markdown 正文
============================================================ */
.prose-content { font-size: 16.5px; line-height: 1.85; color: var(--ink); }
.prose-content h1 { font-size: 1.75rem; font-weight: 800; margin: 1.6rem 0 0.8rem; }
.prose-content h2 {
font-size: 1.4rem;
font-weight: 700;
margin: 1.9rem 0 0.7rem;
padding-bottom: 0.45rem;
border-bottom: 1px solid var(--line);
}
.prose-content h3 { font-size: 1.18rem; font-weight: 700; margin: 1.5rem 0 0.6rem; }
.prose-content p { margin: 0.9rem 0; }
.prose-content ul { list-style: disc; padding-left: 1.5rem; margin: 0.9rem 0; }
.prose-content ol { list-style: decimal; padding-left: 1.5rem; margin: 0.9rem 0; }
.prose-content li { margin: 0.35rem 0; }
.prose-content code {
font-family: var(--font-mono);
background: var(--tint);
padding: 0.12rem 0.45rem;
border-radius: 6px;
font-size: 0.86em;
}
.prose-content pre {
background: var(--panel-2) !important;
border: 1px solid var(--line);
padding: 1.05rem 1.15rem;
border-radius: var(--r-sm);
overflow-x: auto;
margin: 1.15rem 0;
}
.prose-content pre code { background: none; padding: 0; border-radius: 0; }
.prose-content blockquote {
border-left: 3px solid var(--accent);
background: var(--accent-soft);
border-radius: 0 var(--r-sm) var(--r-sm) 0;
padding: 0.7rem 1.1rem;
color: var(--ink-2);
margin: 1.2rem 0;
}
.prose-content a {
color: var(--accent);
text-decoration: underline;
text-underline-offset: 3px;
font-weight: 500;
}
.prose-content img {
max-width: 100%;
border-radius: var(--r-sm);
border: 1px solid var(--line);
}
.prose-content hr { border: none; border-top: 1px solid var(--line); margin: 2.2rem 0; }
.prose-content strong { font-weight: 700; }
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
/* 头像配色(浅/暗各自保证可读) */
.av-0 { background: rgba(52, 81, 200, 0.12); color: #3451c8; }
.av-1 { background: rgba(10, 99, 117, 0.12); color: #0a6375; }
.av-2 { background: rgba(109, 71, 192, 0.12); color: #6d47c0; }
.av-3 { background: rgba(27, 99, 158, 0.12); color: #1b639e; }
.av-4 { background: rgba(12, 102, 95, 0.13); color: #0c665f; }
.av-5 { background: rgba(171, 56, 93, 0.12); color: #ab385d; }
.av-6 { background: rgba(133, 88, 0, 0.14); color: #855800; }
.av-7 { background: rgba(82, 96, 122, 0.13); color: #52607a; }
.dark .av-0 { background: rgba(169, 184, 255, 0.18); color: #a9b8ff; }
.dark .av-1 { background: rgba(92, 201, 224, 0.18); color: #5cc9e0; }
.dark .av-2 { background: rgba(192, 168, 245, 0.18); color: #c0a8f5; }
.dark .av-3 { background: rgba(125, 184, 238, 0.18); color: #7db8ee; }
.dark .av-4 { background: rgba(94, 211, 189, 0.18); color: #5ed3bd; }
.dark .av-5 { background: rgba(240, 147, 181, 0.18); color: #f093b5; }
.dark .av-6 { background: rgba(224, 178, 89, 0.18); color: #e0b259; }
.dark .av-7 { background: rgba(170, 182, 207, 0.18); color: #aab6cf; }
/* ============================================================
微交互
============================================================ */
@keyframes j13-toast-in {
from { opacity: 0; transform: translateY(10px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.j13-toast-in { animation: j13-toast-in 0.2s cubic-bezier(0.2, 0.9, 0.3, 1.2); }
@keyframes j13-heart-pop {
0% { transform: scale(1); }
45% { transform: scale(1.4); }
100% { transform: scale(1); }
}
.j13-heart-pop { animation: j13-heart-pop 0.18s ease-out; }
@keyframes j13-fade-up {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.j13-fade-up { animation: j13-fade-up 0.2s ease-out both; }
/* 跳到主内容 */
.skip-link {
position: fixed;
top: 10px;
left: 10px;
z-index: 10001;
padding: 10px 18px;
font-size: 13.5px;
font-weight: 600;
background: var(--accent);
color: var(--accent-on);
border-radius: 999px;
transform: translateY(-200%);
transition: transform 0.18s ease;
}
.skip-link:focus-visible {
transform: translateY(0);
outline: none;
}
/* 阅读进度条 */
.reading-progress {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 3px;
z-index: 60;
background: var(--accent);
transform-origin: 0 50%;
pointer-events: none;
}