Files
jiang13-bbs/frontend/app/globals.css
freefire 1bb565c687 feat(frontend+backend): 实现SSR登录态优化与字体自托管
1. 后端调整refresh cookie路径与注释,完善认证cookie处理逻辑
2. 前端全局layout新增next/font自托管字体,消除FOUT闪动
3. 实现SSR直出登录态与未读通知数,避免页面加载闪动
4. 新增中间件处理access token静默轮转,保障SSR登录态
5. 优化登录、改密流程,添加页面刷新同步登录态
6. 重构通知铃铛组件支持初始未读数传入
2026-09-12 04:59:26 +08:00

475 lines
12 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";
/* ============================================================
姜十三论坛 · WARM CORNER 设计系统
暖白纸面 / 圆润卡片 / 柔和投影 / 青绿主色 / 陶土辅色
============================================================ */
:root {
/* 浅色(默认) */
--bg: #f6f2ea;
--panel: #ffffff;
--panel-2: #faf6ef;
--tint: #f1ebe0;
--ink: #2c2620;
--ink-2: #6b6257;
--ink-3: #7c7365;
--line: #ece4d8;
--line-2: #ddd2c1;
--accent: #0e6f66;
--accent-hi: #0b5c54;
--accent-soft: rgba(14, 111, 102, 0.1);
--accent-on: #fdfbf6; /* 暖白,与暖纸底色协调,而非冷纯白 */
--clay: #b85c42;
--clay-soft: rgba(200, 105, 77, 0.12);
--gold: #9a6b1f;
--gold-soft: rgba(176, 124, 30, 0.13);
--danger: #d23b32;
--danger-soft: rgba(210, 59, 50, 0.09);
--ok: #2f8f4e;
--ok-soft: rgba(47, 143, 78, 0.1);
--shadow-sm: 0 1px 2px rgba(87, 66, 36, 0.05);
--shadow: 0 2px 8px rgba(87, 66, 36, 0.05), 0 10px 28px rgba(87, 66, 36, 0.05);
--shadow-lg: 0 6px 16px rgba(87, 66, 36, 0.07), 0 20px 48px rgba(87, 66, 36, 0.1);
/* 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: #181510;
--panel: #221e18;
--panel-2: #2a251e;
--tint: #312b22;
--ink: #f3ede2;
--ink-2: #c3b9a9;
--ink-3: #9d9383;
--line: rgba(243, 237, 226, 0.09);
--line-2: rgba(243, 237, 226, 0.17);
--accent: #2dd4bf;
--accent-hi: #5eead4;
--accent-soft: rgba(45, 212, 191, 0.15);
--accent-on: #0c2420;
--clay: #e89070;
--clay-soft: rgba(232, 144, 112, 0.16);
--gold: #d8ae5e;
--gold-soft: rgba(216, 174, 94, 0.16);
--danger: #f26b5e;
--danger-soft: rgba(242, 107, 94, 0.15);
--ok: #6fcf8e;
--ok-soft: rgba(111, 207, 142, 0.15);
--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;
}
* {
border-color: var(--line);
}
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 {
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);
}
/* ----- 按钮(胶囊) ----- */
.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(87, 66, 36, 0.12), 0 2px 8px rgba(14, 111, 102, 0.16);
}
.btn-primary:hover:not(:disabled) {
background: var(--accent-hi);
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(87, 66, 36, 0.1), 0 8px 20px rgba(14, 111, 102, 0.24);
}
.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;
}
.btn-sm { padding: 7px 14px; font-size: 13px; }
/* ----- 表单 ----- */
.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;
}
.field::placeholder { color: var(--ink-3); opacity: 0.8; }
.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);
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;
}
}
/* ============================================================
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(15, 118, 110, 0.12); color: #0f766e; }
.av-1 { background: rgba(200, 105, 77, 0.14); color: #b85c42; }
.av-2 { background: rgba(176, 124, 30, 0.15); color: #9a6b1f; }
.av-3 { background: rgba(91, 111, 181, 0.14); color: #4c61ab; }
.av-4 { background: rgba(122, 92, 168, 0.14); color: #6f50a0; }
.av-5 { background: rgba(79, 138, 95, 0.15); color: #477e55; }
.av-6 { background: rgba(192, 106, 139, 0.14); color: #b1557a; }
.av-7 { background: rgba(63, 134, 147, 0.14); color: #357985; }
.dark .av-0 { background: rgba(45, 212, 191, 0.16); color: #5eead4; }
.dark .av-1 { background: rgba(232, 144, 112, 0.18); color: #f0a484; }
.dark .av-2 { background: rgba(216, 174, 94, 0.18); color: #e3bf74; }
.dark .av-3 { background: rgba(130, 150, 224, 0.18); color: #a7b8f0; }
.dark .av-4 { background: rgba(178, 150, 224, 0.18); color: #c3abe8; }
.dark .av-5 { background: rgba(129, 201, 148, 0.18); color: #99ddac; }
.dark .av-6 { background: rgba(235, 148, 180, 0.18); color: #f0a5c2; }
.dark .av-7 { background: rgba(102, 190, 206, 0.18); color: #84d4e0; }
/* ============================================================
微交互
============================================================ */
@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.26s 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.3s 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;
}