diff --git a/frontend/index.html b/frontend/index.html
index 1f8e75f..87e1aeb 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,7 +2,7 @@
-
+
ONE · 一个博客
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index f6580c1..bc69ada 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -6,13 +6,15 @@ import { installCustomCss } from './customCss'
import './styles.css'
// vivid(余白)是第二套 UI,全部规则都以 html[data-ui="vivid"] 前缀收敛;
// 必须排在 styles.css 之后,才能在同特指度时按源码顺序取胜。
-// 顺序即层叠顺序:令牌 → 骨架 → 各视图。不要把 tokens 挪到后面。
+// 顺序即层叠顺序:令牌 → 骨架 → 各视图 → 手机收口。不要把 tokens/mobile 挪位置。
import './ui/yohaku/tokens.css'
import './ui/yohaku/chrome.css'
import './ui/yohaku/home.css'
-import './ui/yohaku/panels.css'
import './ui/yohaku/article.css'
import './ui/yohaku/pages.css'
+// mobile.css 必须排最后:它修正的是上面几个文件里同特指度的基准尺寸,
+// 媒体查询不加特指度,「后导入者赢」是它唯一的凭仗。
+import './ui/yohaku/mobile.css'
loadSite().then(() => {
installCustomCss(router)
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 9cb335b..5eceea3 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -1,7 +1,4 @@
-/* ONE — 设计 token(沿用 ROOT-2 第 7 套「融合 + Twitter 信息流」) */
-
-:root {
- /* 皮肤:米白纸感 */
+:root {/* 皮肤:米白纸感 */
--paper: #faf7f1;
--card: #fffdf8;
--paper-sunken: #f3efe6;
@@ -33,15 +30,8 @@
--col-left: 236px;
--col-main: 640px;
--col-right: 264px;
- --gutter: 28px;
-}
-
-/* ---------- 主题:前台视图响应 site.theme_id ----------
- 后台自己的 token(--admin-*)保持米白纸感不变。
- 切换 [data-theme] 即可换肤;默认 paper 不写在选择器里。 */
-
-:root[data-theme='ink'] {
- --paper: #1f1d1a;
+ --gutter: 28px;}
+:root[data-theme='ink'] {--paper: #1f1d1a;
--card: #25221e;
--paper-sunken: #2b2722;
--ink: #ece5d2;
@@ -52,11 +42,8 @@
--line-soft: #2f2b25;
--accent: #c3a972;
--accent-soft: rgba(195, 169, 114, 0.14);
- --accent-line: rgba(195, 169, 114, 0.4);
-}
-
-:root[data-theme='sage'] {
- --paper: #eef0e6;
+ --accent-line: rgba(195, 169, 114, 0.4);}
+:root[data-theme='sage'] {--paper: #eef0e6;
--card: #f6f7ef;
--paper-sunken: #e2e6d6;
--ink: #2a3127;
@@ -67,11 +54,8 @@
--line-soft: #e6e9d9;
--accent: #5f7b5c;
--accent-soft: rgba(95, 123, 92, 0.12);
- --accent-line: rgba(95, 123, 92, 0.38);
-}
-
-:root[data-theme='rose'] {
- --paper: #f7eee6;
+ --accent-line: rgba(95, 123, 92, 0.38);}
+:root[data-theme='rose'] {--paper: #f7eee6;
--card: #fdf5ee;
--paper-sunken: #ecdfd3;
--ink: #3a2922;
@@ -82,15 +66,8 @@
--line-soft: #f0e2d5;
--accent: #a55a4a;
--accent-soft: rgba(165, 90, 74, 0.1);
- --accent-line: rgba(165, 90, 74, 0.38);
-}
-
-/* ---------- 后台主题:跟随访客 mode 切换明暗 ----------
- data-admin-theme 与 data-theme 是两套轴:data-theme 控制前台皮肤,
- data-admin-theme 只切后台的明/暗。后台暗色用深棕墨感,跟前台 ink 协调。 */
-
-:root[data-admin-theme='dark'] {
- --admin-bg: #1a1815;
+ --accent-line: rgba(165, 90, 74, 0.38);}
+:root[data-admin-theme='dark'] {--admin-bg: #1a1815;
--admin-paper: var(--admin-bg);
--admin-card: #25221e;
--admin-paper-sunken: #2f2b25;
@@ -106,38 +83,13 @@
--admin-on-accent: #1a1815;
--admin-danger: #d68d75;
--admin-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
- --admin-overlay: rgba(0, 0, 0, 0.6);
-}
-
-/* 主题切换时让背景渐变,避免硬切 */
-body {
- transition: background-color 0.2s ease, color 0.2s ease;
-}
-
-a,
-.btn,
-.tag-chip,
-.pill,
-.editor-mode button,
-.chip,
-input.input,
-input.select,
-textarea.textarea {
- transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
-}
-
-* {
- box-sizing: border-box;
-}
-
-html,
-body {
- margin: 0;
- padding: 0;
-}
-
-body {
- background: var(--paper);
+ --admin-overlay: rgba(0, 0, 0, 0.6);}
+body {transition: background-color 0.2s ease, color 0.2s ease;}
+a, .btn, .tag-chip, .pill, .editor-mode button, .chip, input.input, input.select, textarea.textarea {transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;}
+* {box-sizing: border-box;}
+html, body {margin: 0;
+ padding: 0;}
+body {background: var(--paper);
color: var(--ink);
font-family: var(--sans);
font-size: 16px;
@@ -145,401 +97,201 @@ body {
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
/* 移动端不留横向滚动 */
- overflow-x: hidden;
+ overflow-x: hidden;}
+a {color: inherit;
+ text-decoration: none;}
+@media (hover: hover) and (pointer: fine) {
+ a:hover {color: var(--accent);}
}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-a:hover {
- color: var(--accent);
-}
-
-img {
- max-width: 100%;
-}
-
-button,
-input,
-textarea,
-select {
- font-family: inherit;
+img {max-width: 100%;}
+button, input, textarea, select {font-family: inherit;
font-size: inherit;
line-height: inherit;
- color: inherit;
-}
-
-h1,
-h2,
-h3,
-h4 {
- font-family: var(--serif);
+ color: inherit;}
+h1, h2, h3, h4 {font-family: var(--serif);
font-weight: 600;
line-height: 1.4;
margin: 0;
- letter-spacing: 0.01em;
-}
-
-::selection {
- background: rgba(61, 127, 156, 0.18);
-}
-
-/* ---------- 通用容器 ---------- */
-
-.shell {
- max-width: 1238px;
+ letter-spacing: 0.01em;}
+::selection {background: rgba(61, 127, 156, 0.18);}
+.shell {max-width: 1238px;
margin: 0 auto;
- padding: 0 20px;
-}
-
-.layout {
- display: grid;
+ padding: 0 20px;}
+.layout {display: grid;
grid-template-columns: var(--col-left) minmax(0, var(--col-main)) var(--col-right);
gap: var(--gutter);
- align-items: start;
-}
-
+ align-items: start;}
@media (max-width: 1080px) {
- .layout {
- grid-template-columns: var(--col-left) minmax(0, 1fr);
- }
- .layout > .rail-right {
- display: none;
- }
+ .layout {grid-template-columns: var(--col-left) minmax(0, 1fr);}
+ .layout > .rail-right {display: none;}
}
-
@media (max-width: 780px) {
- .layout {
- grid-template-columns: minmax(0, 1fr);
- }
- .layout > .rail-left {
- display: none;
- }
+ .layout {grid-template-columns: minmax(0, 1fr);}
+ .layout > .rail-left {display: none;}
}
-
-/* ---------- 小组件 ---------- */
-
-.eyebrow {
- font-size: 12px;
+.eyebrow {font-size: 12px;
letter-spacing: 0.16em;
text-transform: uppercase;
- color: var(--muted);
-}
-
-.divider {
- height: 1px;
+ color: var(--muted);}
+.divider {height: 1px;
background: var(--line);
border: 0;
- margin: 0;
-}
-
-.muted {
- color: var(--muted);
-}
-
-.mono {
- font-family: var(--mono);
- font-size: 13px;
-}
-
-.tag-chip {
- display: inline-block;
+ margin: 0;}
+.muted {color: var(--muted);}
+.mono {font-family: var(--mono);
+ font-size: 13px;}
+.tag-chip {display: inline-block;
padding: 1px 8px;
border: 1px solid var(--line);
border-radius: 999px;
font-size: 12px;
line-height: 1.8;
color: var(--ink-soft);
- background: transparent;
+ background: transparent;}
+@media (hover: hover) and (pointer: fine) {
+ .tag-chip:hover {border-color: var(--accent-line);
+ color: var(--accent);}
}
-
-.tag-chip:hover {
- border-color: var(--accent-line);
- color: var(--accent);
-}
-
-.pill {
- display: inline-block;
+.pill {display: inline-block;
padding: 7px 18px;
border-radius: 999px;
background: var(--accent);
color: #fff;
font-size: 14px;
border: 1px solid var(--accent);
- cursor: pointer;
-}
-
-.pill:hover {
- background: var(--accent-soft);
+ cursor: pointer;}
+@media (hover: hover) and (pointer: fine) {
+ .pill:hover {background: var(--accent-soft);
color: var(--accent);
- border-color: var(--accent-line);
+ border-color: var(--accent-line);}
}
-
-.pill-ghost {
- background: transparent;
- color: var(--accent);
+.pill-ghost {background: transparent;
+ color: var(--accent);}
+@media (hover: hover) and (pointer: fine) {
+ .pill-ghost:hover {background: var(--accent-soft);
+ color: var(--accent);}
}
-
-.pill-ghost:hover {
- background: var(--accent-soft);
- color: var(--accent);
-}
-
-/* ---------- 表单 ---------- */
-
-.field {
- margin-bottom: 18px;
-}
-
-.field > label {
- display: block;
+.field {margin-bottom: 18px;}
+.field > label {display: block;
font-size: 13px;
color: var(--muted);
- margin-bottom: 6px;
-}
-
-.input,
-.textarea,
-.select {
- width: 100%;
+ margin-bottom: 6px;}
+.input, .textarea, .select {width: 100%;
padding: 9px 12px;
background: var(--card);
border: 1px solid var(--line);
border-radius: 3px;
- outline: none;
-}
-
-.input:focus,
-.textarea:focus,
-.select:focus {
- border-color: var(--accent-line);
-}
-
-.textarea {
- resize: vertical;
-}
-
-.btn {
- padding: 8px 18px;
+ outline: none;}
+.input:focus, .textarea:focus, .select:focus {border-color: var(--accent-line);}
+.textarea {resize: vertical;}
+.btn {padding: 8px 18px;
border: 1px solid var(--line);
background: var(--card);
border-radius: 3px;
- cursor: pointer;
+ cursor: pointer;}
+@media (hover: hover) and (pointer: fine) {
+ .btn:hover {border-color: var(--accent-line);
+ color: var(--accent);}
}
-
-.btn:hover {
- border-color: var(--accent-line);
- color: var(--accent);
-}
-
-.btn-primary {
- background: var(--accent);
+.btn-primary {background: var(--accent);
border-color: var(--accent);
- color: #fff;
-}
-
-.btn-primary:hover {
- background: var(--accent-soft);
+ color: #fff;}
+@media (hover: hover) and (pointer: fine) {
+ .btn-primary:hover {background: var(--accent-soft);
color: var(--accent);
- border-color: var(--accent-line);
+ border-color: var(--accent-line);}
}
-
-.btn-danger:hover {
- border-color: #b4553f;
- color: #b4553f;
+@media (hover: hover) and (pointer: fine) {
+ .btn-danger:hover {border-color: #b4553f;
+ color: #b4553f;}
}
-
-.btn:disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-/* ---------- 正文排版(长文:首字下沉 + 1.95 行高) ---------- */
-
-.prose {
- font-size: 16.5px;
+.btn:disabled {opacity: 0.5;
+ cursor: not-allowed;}
+.prose {font-size: 16.5px;
line-height: var(--lh);
- color: var(--ink);
-}
-
-.prose > :first-child {
- margin-top: 22px;
-}
-
-.prose p {
- margin: 0 0 1.1em;
-}
-
-/* 首段首字下沉 */
-.prose > p:first-of-type::first-letter {
- float: left;
+ color: var(--ink);}
+.prose > :first-child {margin-top: 22px;}
+.prose p {margin: 0 0 1.1em;}
+.prose > p:first-of-type::first-letter {float: left;
font-family: var(--serif);
font-size: 52px;
line-height: 1;
margin: 6px 10px 0 0;
- color: var(--accent);
-}
-
-.prose h1,
-.prose h2,
-.prose h3,
-.prose h4 {
- font-family: var(--serif);
+ color: var(--accent);}
+.prose h1, .prose h2, .prose h3, .prose h4 {font-family: var(--serif);
color: var(--ink);
- margin: 1.8em 0 0.6em;
-}
-
-.prose h1 {
- font-size: 26px;
-}
-
-.prose h2 {
- font-size: 22px;
+ margin: 1.8em 0 0.6em;}
+.prose h1 {font-size: 26px;}
+.prose h2 {font-size: 22px;
padding-bottom: 6px;
- border-bottom: 1px solid var(--line);
-}
-
-.prose h3 {
- font-size: 18px;
-}
-
-.prose h3::after {
- content: '';
+ border-bottom: 1px solid var(--line);}
+.prose h3 {font-size: 18px;}
+.prose h3::after {content: '';
display: block;
width: 28px;
height: 2px;
margin-top: 8px;
- background: var(--accent);
-}
-
-.prose h4 {
- font-size: 16.5px;
-}
-
-/* 引言用衬线,和正文区分开 */
-.prose blockquote {
- margin: 1.4em 0;
+ background: var(--accent);}
+.prose h4 {font-size: 16.5px;}
+.prose blockquote {margin: 1.4em 0;
padding: 2px 0 2px 18px;
border-left: 2px solid var(--accent-line);
font-family: var(--serif);
- color: var(--ink-soft);
+ color: var(--ink-soft);}
+.prose blockquote p:last-child {margin-bottom: 0;}
+.prose ul, .prose ol {margin: 0 0 1.1em;
+ padding-left: 1.4em;}
+.prose li {margin-bottom: 0.35em;}
+.prose a {color: var(--accent);
+ border-bottom: 1px solid var(--accent-line);}
+@media (hover: hover) and (pointer: fine) {
+ .prose a:hover {border-bottom-color: var(--accent);}
}
-
-.prose blockquote p:last-child {
- margin-bottom: 0;
-}
-
-.prose ul,
-.prose ol {
- margin: 0 0 1.1em;
- padding-left: 1.4em;
-}
-
-.prose li {
- margin-bottom: 0.35em;
-}
-
-.prose a {
- color: var(--accent);
- border-bottom: 1px solid var(--accent-line);
-}
-
-.prose a:hover {
- border-bottom-color: var(--accent);
-}
-
-.prose code {
- font-family: var(--mono);
+.prose code {font-family: var(--mono);
font-size: 13.5px;
background: var(--paper-sunken);
padding: 1px 5px;
- border-radius: 3px;
-}
-
-.prose pre {
- background: var(--paper-sunken);
+ border-radius: 3px;}
+.prose pre {background: var(--paper-sunken);
border: 1px solid var(--line-soft);
border-radius: 4px;
padding: 14px 16px;
overflow-x: auto;
- line-height: 1.7;
-}
-
-.prose pre code {
- background: none;
+ line-height: 1.7;}
+.prose pre code {background: none;
padding: 0;
- font-size: 13.5px;
-}
-
-.prose img {
- border-radius: 3px;
- display: block;
-}
-
-.prose hr {
- height: 1px;
+ font-size: 13.5px;}
+.prose img {border-radius: 3px;
+ display: block;}
+.prose hr {height: 1px;
border: 0;
background: var(--line);
- margin: 2em 0;
-}
-
-.prose table {
- width: 100%;
+ margin: 2em 0;}
+.prose table {width: 100%;
border-collapse: collapse;
margin: 1.4em 0;
- font-size: 15px;
-}
-
-.prose th,
-.prose td {
- border: 1px solid var(--line);
+ font-size: 15px;}
+.prose th, .prose td {border: 1px solid var(--line);
padding: 7px 10px;
- text-align: left;
-}
-
-.prose th {
- background: var(--paper-sunken);
- font-weight: 600;
-}
-
-/* 短文:放大正文,不做首字下沉 */
-.prose-short {
- font-size: 20px;
- line-height: 1.9;
-}
-
-.prose-short > p:first-of-type::first-letter {
- float: none;
+ text-align: left;}
+.prose th {background: var(--paper-sunken);
+ font-weight: 600;}
+.prose-short {font-size: 20px;
+ line-height: 1.9;}
+.prose-short > p:first-of-type::first-letter {float: none;
font-size: inherit;
line-height: inherit;
margin: 0;
- color: inherit;
-}
-
-/* ---------- 空状态 / 加载 ---------- */
-
-.empty {
- padding: 48px 0;
+ color: inherit;}
+.empty {padding: 48px 0;
text-align: center;
color: var(--muted);
- font-size: 14px;
-}
-
-.loading {
- padding: 48px 0;
+ font-size: 14px;}
+.loading {padding: 48px 0;
text-align: center;
color: var(--muted);
- font-size: 14px;
-}
-
-/* ============================================================
- ADMIN — 后台管理界面(侧边栏布局 + 卡片化)
- ============================================================ */
-
-:root {
- --admin-sidebar: 224px;
+ font-size: 14px;}
+:root {--admin-sidebar: 224px;
--admin-topbar-h: 56px;
--admin-bg: #f6f2ea;
--admin-paper: var(--admin-bg);
@@ -557,67 +309,43 @@ h4 {
--admin-on-accent: #ffffff;
--admin-danger: #b4553f;
--admin-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
- --admin-overlay: rgba(0, 0, 0, 0.4);
-}
-
-.admin-shell {
- display: grid;
+ --admin-overlay: rgba(0, 0, 0, 0.4);}
+.admin-shell {display: grid;
grid-template-columns: var(--admin-sidebar) minmax(0, 1fr);
min-height: 100vh;
- background: var(--admin-bg);
-}
-
-/* ---------- sidebar ---------- */
-
-.admin-side {
- background: var(--admin-card);
+ min-height: 100dvh;
+ background: var(--admin-bg);}
+.admin-side {background: var(--admin-card);
border-right: 1px solid var(--admin-line);
display: flex;
flex-direction: column;
position: sticky;
top: 0;
height: 100vh;
-}
-
-.admin-side .brand {
- padding: 18px 20px 16px;
+ height: 100dvh;}
+.admin-side .brand {padding: 18px 20px 16px;
border-bottom: 1px solid var(--admin-line);
display: flex;
align-items: baseline;
- gap: 6px;
-}
-
-.admin-side .brand .name {
- font-family: var(--serif);
+ gap: 6px;}
+.admin-side .brand .name {font-family: var(--serif);
font-size: 18px;
font-weight: 600;
- color: var(--admin-ink);
-}
-
-.admin-side .brand .label {
- font-size: 12px;
- color: var(--admin-muted);
-}
-
-.admin-side .nav {
- padding: 14px 10px;
+ color: var(--admin-ink);}
+.admin-side .brand .label {font-size: 12px;
+ color: var(--admin-muted);}
+.admin-side .nav {padding: 14px 10px;
display: flex;
flex-direction: column;
gap: 2px;
flex: 1;
- overflow-y: auto;
-}
-
-.admin-side .group {
- font-size: 11px;
+ overflow-y: auto;}
+.admin-side .group {font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--admin-muted);
- padding: 14px 12px 6px;
-}
-
-.admin-side .item {
- display: flex;
+ padding: 14px 12px 6px;}
+.admin-side .item {display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
@@ -625,84 +353,53 @@ h4 {
font-size: 14px;
color: var(--admin-ink-soft);
border: 1px solid transparent;
- cursor: pointer;
+ cursor: pointer;}
+@media (hover: hover) and (pointer: fine) {
+ .admin-side .item:hover {background: var(--admin-paper-sunken);
+ color: var(--admin-ink);}
}
-
-.admin-side .item:hover {
- background: var(--admin-paper-sunken);
- color: var(--admin-ink);
-}
-
-.admin-side .item.router-link-exact-active {
- background: var(--admin-accent-soft);
+.admin-side .item.router-link-exact-active {background: var(--admin-accent-soft);
color: var(--admin-accent);
- font-weight: 500;
-}
-
-.admin-side .item .ic {
- width: 18px;
+ font-weight: 500;}
+.admin-side .item .ic {width: 18px;
display: inline-flex;
justify-content: center;
font-size: 16px;
- line-height: 1;
-}
-
-.admin-side .item .badge {
- margin-left: auto;
+ line-height: 1;}
+.admin-side .item .badge {margin-left: auto;
font-size: 11px;
color: var(--admin-muted);
background: var(--admin-paper-sunken);
padding: 1px 7px;
border-radius: 999px;
min-width: 22px;
- text-align: center;
-}
-
-.admin-side .footer {
- border-top: 1px solid var(--admin-line);
+ text-align: center;}
+.admin-side .footer {border-top: 1px solid var(--admin-line);
padding: 14px 18px;
font-size: 12.5px;
color: var(--admin-muted);
display: flex;
align-items: center;
justify-content: space-between;
- gap: 12px;
-}
-
-.admin-side .footer .meta {
- min-width: 0;
-}
-
-.admin-side .footer .who {
- color: var(--admin-ink-soft);
- font-weight: 500;
-}
-
-.admin-side .footer .leave {
- display: block;
+ gap: 12px;}
+.admin-side .footer .meta {min-width: 0;}
+.admin-side .footer .who {color: var(--admin-ink-soft);
+ font-weight: 500;}
+.admin-side .footer .leave {display: block;
margin-top: 6px;
color: var(--admin-muted);
background: none;
border: 0;
padding: 0;
cursor: pointer;
- font-size: 12px;
+ font-size: 12px;}
+@media (hover: hover) and (pointer: fine) {
+ .admin-side .footer .leave:hover {color: var(--admin-accent);}
}
-
-.admin-side .footer .leave:hover {
- color: var(--admin-accent);
-}
-
-/* ---------- topbar ---------- */
-
-.admin-main {
- display: flex;
+.admin-main {display: flex;
flex-direction: column;
- min-width: 0;
-}
-
-.admin-topbar {
- height: var(--admin-topbar-h);
+ min-width: 0;}
+.admin-topbar {height: var(--admin-topbar-h);
display: flex;
align-items: center;
justify-content: space-between;
@@ -712,125 +409,66 @@ h4 {
border-bottom: 1px solid var(--admin-line);
position: sticky;
top: 0;
- z-index: 5;
-}
-
-.admin-topbar .crumbs {
- font-size: 14px;
- color: var(--admin-ink-soft);
-}
-
-.admin-topbar .crumbs strong {
- color: var(--admin-ink);
- font-weight: 600;
-}
-
-.admin-topbar .right {
- display: flex;
+ z-index: 5;}
+.admin-topbar .crumbs {font-size: 14px;
+ color: var(--admin-ink-soft);}
+.admin-topbar .crumbs strong {color: var(--admin-ink);
+ font-weight: 600;}
+.admin-topbar .right {display: flex;
align-items: center;
- gap: 10px;
-}
-
-.admin-topbar .cmd-hint {
- font-size: 12px;
+ gap: 10px;}
+.admin-topbar .cmd-hint {font-size: 12px;
color: var(--admin-muted);
border: 1px solid var(--admin-line);
padding: 4px 10px;
border-radius: 6px;
cursor: pointer;
- background: var(--admin-paper);
-}
-
-.admin-topbar .cmd-hint kbd {
- font-family: var(--mono);
+ background: var(--admin-paper);}
+.admin-topbar .cmd-hint kbd {font-family: var(--mono);
font-size: 11px;
border: 1px solid var(--admin-line);
padding: 1px 5px;
border-radius: 3px;
- background: var(--admin-card);
-}
-
-.admin-content {
- padding: 26px 28px 80px;
+ background: var(--admin-card);}
+.admin-content {padding: 26px 28px 80px;
max-width: 1180px;
width: 100%;
- margin: 0 auto;
-}
-
+ margin: 0 auto;}
@media (max-width: 860px) {
- .admin-shell {
- grid-template-columns: 1fr;
- }
- .admin-side {
- position: static;
+ .admin-shell {grid-template-columns: 1fr;}
+ .admin-side {position: static;
height: auto;
border-right: 0;
- border-bottom: 1px solid var(--admin-line);
- }
- .admin-side .nav {
- flex-direction: row;
+ border-bottom: 1px solid var(--admin-line);}
+ .admin-side .nav {flex-direction: row;
flex-wrap: wrap;
- padding: 8px;
- }
- .admin-side .group {
- display: none;
- }
-
- .admin-content {
- padding: 20px 16px 60px;
- }
+ padding: 8px;}
+ .admin-side .group {display: none;}
+ .admin-content {padding: 20px 16px 60px;}
}
-
-/* footer(含主题切换)到 780px 才隐藏,与全局浮动按钮的出现断点对齐 */
@media (max-width: 780px) {
- .admin-side .footer {
- display: none;
- }
+ .admin-side .footer {display: none;}
}
-
-/* ---------- card / panel ---------- */
-
-.panel {
- background: var(--admin-card);
+.panel {background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 8px;
- padding: 18px 20px;
-}
-
-.panel + .panel {
- margin-top: 16px;
-}
-
-.panel-title {
- display: flex;
+ padding: 18px 20px;}
+.panel + .panel {margin-top: 16px;}
+.panel-title {display: flex;
align-items: center;
justify-content: space-between;
- margin-bottom: 14px;
-}
-
-.panel-title h2 {
- font-family: var(--sans);
+ margin-bottom: 14px;}
+.panel-title h2 {font-family: var(--sans);
font-size: 14px;
font-weight: 600;
- color: var(--admin-ink);
-}
-
-.panel-title .meta {
- font-size: 12px;
- color: var(--admin-muted);
-}
-
-/* ---------- dashboard stat cards ---------- */
-
-.stat-grid {
- display: grid;
+ color: var(--admin-ink);}
+.panel-title .meta {font-size: 12px;
+ color: var(--admin-muted);}
+.stat-grid {display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 14px;
- margin-bottom: 24px;
-}
-
-.stat {
- background: var(--admin-card);
+ margin-bottom: 24px;}
+.stat {background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 8px;
padding: 16px 18px;
@@ -838,57 +476,30 @@ h4 {
flex-direction: column;
gap: 6px;
position: relative;
- overflow: hidden;
-}
-
-.stat .label {
- font-size: 12px;
+ overflow: hidden;}
+.stat .label {font-size: 12px;
color: var(--admin-muted);
- letter-spacing: 0.06em;
-}
-
-.stat .value {
- font-family: var(--serif);
+ letter-spacing: 0.06em;}
+.stat .value {font-family: var(--serif);
font-size: 28px;
font-weight: 600;
color: var(--admin-ink);
- line-height: 1;
-}
-
-.stat .hint {
- font-size: 12px;
- color: var(--admin-muted);
-}
-
-.stat.accent {
- background: var(--admin-accent);
+ line-height: 1;}
+.stat .hint {font-size: 12px;
+ color: var(--admin-muted);}
+.stat.accent {background: var(--admin-accent);
border-color: var(--admin-accent);
- color: var(--admin-on-accent);
-}
-
-.stat.accent .label,
-.stat.accent .hint {
- color: rgba(255, 255, 255, 0.78);
-}
-
-.stat.accent .value {
- color: var(--admin-on-accent);
-}
-
-/* ---------- dashboard chart ---------- */
-
-.dash-chart {
- display: flex;
+ color: var(--admin-on-accent);}
+.stat.accent .label, .stat.accent .hint {color: rgba(255, 255, 255, 0.78);}
+.stat.accent .value {color: var(--admin-on-accent);}
+.dash-chart {display: flex;
align-items: flex-end;
gap: 6px;
height: 140px;
padding: 14px 4px 6px;
border-bottom: 1px solid var(--admin-line);
- margin-bottom: 8px;
-}
-
-.dash-chart .bar {
- flex: 1;
+ margin-bottom: 8px;}
+.dash-chart .bar {flex: 1;
background: var(--admin-accent-soft);
border-top: 2px solid var(--admin-accent);
border-radius: 3px 3px 0 0;
@@ -896,75 +507,42 @@ h4 {
min-height: 2px;
display: flex;
align-items: flex-end;
- justify-content: center;
+ justify-content: center;}
+@media (hover: hover) and (pointer: fine) {
+ .dash-chart .bar:hover {background: var(--admin-accent);}
}
-
-.dash-chart .bar:hover {
- background: var(--admin-accent);
-}
-
-.dash-chart .bar .v {
- position: absolute;
+.dash-chart .bar .v {position: absolute;
top: -18px;
font-size: 11px;
- color: var(--admin-muted);
-}
-
-.dash-chart .labels {
- display: flex;
- gap: 6px;
-}
-
-.dash-chart .labels span {
- flex: 1;
+ color: var(--admin-muted);}
+.dash-chart .labels {display: flex;
+ gap: 6px;}
+.dash-chart .labels span {flex: 1;
font-size: 10px;
color: var(--admin-muted);
- text-align: center;
-}
-
-/* ---------- recent list ---------- */
-
-.recent-list {
- display: flex;
- flex-direction: column;
-}
-
-.recent-item {
- display: flex;
+ text-align: center;}
+.recent-list {display: flex;
+ flex-direction: column;}
+.recent-item {display: flex;
align-items: center;
gap: 12px;
padding: 10px 4px;
- border-bottom: 1px solid var(--admin-line);
-}
-
-.recent-item:last-child {
- border-bottom: 0;
-}
-
-.recent-item .title {
- flex: 1;
+ border-bottom: 1px solid var(--admin-line);}
+.recent-item:last-child {border-bottom: 0;}
+.recent-item .title {flex: 1;
font-size: 14px;
color: var(--admin-ink);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
- white-space: nowrap;
+ white-space: nowrap;}
+@media (hover: hover) and (pointer: fine) {
+ .recent-item .title:hover {color: var(--admin-accent);}
}
-
-.recent-item .title:hover {
- color: var(--admin-accent);
-}
-
-.recent-item .meta {
- font-size: 12px;
+.recent-item .meta {font-size: 12px;
color: var(--admin-muted);
- white-space: nowrap;
-}
-
-/* ---------- chip ---------- */
-
-.chip {
- display: inline-flex;
+ white-space: nowrap;}
+.chip {display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
@@ -974,32 +552,22 @@ h4 {
color: var(--admin-ink-soft);
background: var(--admin-paper);
cursor: pointer;
- transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
+ transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;}
+@media (hover: hover) and (pointer: fine) {
+ .chip:hover {border-color: var(--admin-accent);
+ color: var(--admin-accent);}
}
-
-.chip:hover {
+.chip.on {background: var(--admin-accent);
border-color: var(--admin-accent);
- color: var(--admin-accent);
-}
-
-.chip.on {
- background: var(--admin-accent);
- border-color: var(--admin-accent);
- color: var(--admin-on-accent);
-}
-
-.chip .x {
- border: 0;
+ color: var(--admin-on-accent);}
+.chip .x {border: 0;
background: none;
cursor: pointer;
color: inherit;
padding: 0 0 0 2px;
font-size: 14px;
- line-height: 1;
-}
-
-.chip-static {
- display: inline-flex;
+ line-height: 1;}
+.chip-static {display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
@@ -1007,34 +575,19 @@ h4 {
border-radius: 999px;
font-size: 12px;
color: var(--admin-ink-soft);
- background: transparent;
-}
-
-/* tag with color dot */
-.tag-dot {
- width: 8px;
+ background: transparent;}
+.tag-dot {width: 8px;
height: 8px;
border-radius: 50%;
- display: inline-block;
-}
-
-/* ---------- post list (cards) ---------- */
-
-.posts-toolbar {
- display: flex;
+ display: inline-block;}
+.posts-toolbar {display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
- margin-bottom: 14px;
-}
-
-.posts-toolbar .grow {
- flex: 1;
- min-width: 180px;
-}
-
-.post-card {
- display: grid;
+ margin-bottom: 14px;}
+.posts-toolbar .grow {flex: 1;
+ min-width: 180px;}
+.post-card {display: grid;
grid-template-columns: 96px minmax(0, 1fr) 110px 90px;
gap: 16px;
align-items: center;
@@ -1044,150 +597,85 @@ h4 {
border-radius: 8px;
margin-bottom: 10px;
cursor: pointer;
- transition: border-color 0.15s;
+ transition: border-color 0.15s;}
+@media (hover: hover) and (pointer: fine) {
+ .post-card:hover {border-color: var(--admin-accent);}
}
-
-.post-card:hover {
- border-color: var(--admin-accent);
-}
-
-.post-card.selected {
- border-color: var(--admin-accent);
- background: var(--admin-accent-soft);
-}
-
-.post-card .cover {
- width: 96px;
+.post-card.selected {border-color: var(--admin-accent);
+ background: var(--admin-accent-soft);}
+.post-card .cover {width: 96px;
height: 64px;
background: var(--admin-paper-sunken);
border-radius: 4px;
overflow: hidden;
display: flex;
align-items: center;
- justify-content: center;
-}
-
-.post-card .cover img {
- width: 100%;
+ justify-content: center;}
+.post-card .cover img {width: 100%;
height: 100%;
- object-fit: cover;
-}
-
-.post-card .cover .ph {
- font-size: 11px;
+ object-fit: cover;}
+.post-card .cover .ph {font-size: 11px;
color: var(--admin-muted);
- letter-spacing: 0.1em;
-}
-
-.post-card .body {
- min-width: 0;
-}
-
-.post-card .title {
- font-family: var(--serif);
+ letter-spacing: 0.1em;}
+.post-card .body {min-width: 0;}
+.post-card .title {font-family: var(--serif);
font-size: 16px;
color: var(--admin-ink);
font-weight: 600;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.post-card .summary {
- font-size: 13px;
+ white-space: nowrap;}
+.post-card .summary {font-size: 13px;
color: var(--admin-muted);
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
- margin-bottom: 6px;
-}
-
-.post-card .meta {
- display: flex;
+ margin-bottom: 6px;}
+.post-card .meta {display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
font-size: 11.5px;
- color: var(--admin-muted);
-}
-
-.post-card .meta .dot {
- width: 6px;
+ color: var(--admin-muted);}
+.post-card .meta .dot {width: 6px;
height: 6px;
border-radius: 50%;
background: var(--admin-faint);
display: inline-block;
- margin-right: 4px;
-}
-
-.post-card .meta .dot.published {
- background: var(--admin-accent);
-}
-
-.post-card .stats {
- font-size: 12px;
+ margin-right: 4px;}
+.post-card .meta .dot.published {background: var(--admin-accent);}
+.post-card .stats {font-size: 12px;
color: var(--admin-muted);
text-align: center;
- font-family: var(--mono);
-}
-
-.post-card .stats div {
- line-height: 1.4;
-}
-
-.post-card .actions {
- display: flex;
+ font-family: var(--mono);}
+.post-card .stats div {line-height: 1.4;}
+.post-card .actions {display: flex;
flex-direction: column;
gap: 6px;
- align-items: stretch;
-}
-
-.post-card .actions button,
-.post-card .actions a {
- font-size: 12.5px;
+ align-items: stretch;}
+.post-card .actions button, .post-card .actions a {font-size: 12.5px;
padding: 4px 0;
background: none;
border: 0;
color: var(--admin-accent);
cursor: pointer;
text-align: right;
- text-decoration: none;
-}
-
-.post-card .actions .danger {
- color: #b4553f;
-}
-
-.post-card .check {
- width: 18px;
+ text-decoration: none;}
+.post-card .actions .danger {color: #b4553f;}
+.post-card .check {width: 18px;
height: 18px;
accent-color: var(--admin-accent);
- cursor: pointer;
-}
-
-.post-card.has-check {
- grid-template-columns: 24px 96px minmax(0, 1fr) 110px 90px;
-}
-
+ cursor: pointer;}
+.post-card.has-check {grid-template-columns: 24px 96px minmax(0, 1fr) 110px 90px;}
@media (max-width: 760px) {
- .post-card {
- grid-template-columns: 64px minmax(0, 1fr);
- }
- .post-card .cover {
- width: 64px;
- height: 64px;
- }
- .post-card .stats,
- .post-card .actions {
- display: none;
- }
+ .post-card {grid-template-columns: 64px minmax(0, 1fr);}
+ .post-card .cover {width: 64px;
+ height: 64px;}
+ .post-card .stats, .post-card .actions {display: none;}
}
-
-/* bulk action bar */
-.bulk-bar {
- display: flex;
+.bulk-bar {display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
@@ -1195,72 +683,42 @@ h4 {
color: var(--admin-on-accent);
border-radius: 8px;
margin-bottom: 12px;
- font-size: 13px;
-}
-
-.bulk-bar button {
- background: rgba(255, 255, 255, 0.18);
+ font-size: 13px;}
+.bulk-bar button {background: rgba(255, 255, 255, 0.18);
color: var(--admin-on-accent);
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 4px 12px;
border-radius: 4px;
cursor: pointer;
- font-size: 13px;
+ font-size: 13px;}
+@media (hover: hover) and (pointer: fine) {
+ .bulk-bar button:hover {background: rgba(255, 255, 255, 0.28);}
}
-
-.bulk-bar button:hover {
- background: rgba(255, 255, 255, 0.28);
-}
-
-.bulk-bar .grow {
- flex: 1;
-}
-
-.bulk-bar .x {
- background: none;
+.bulk-bar .grow {flex: 1;}
+.bulk-bar .x {background: none;
border: 0;
color: var(--admin-on-accent);
font-size: 16px;
- cursor: pointer;
-}
-
-/* ---------- editor enhancements ---------- */
-
-.editor-grid {
- display: grid;
+ cursor: pointer;}
+.editor-grid {display: grid;
grid-template-columns: minmax(0, 1fr) 280px;
gap: 20px;
- align-items: start;
-}
-
+ align-items: start;}
@media (max-width: 920px) {
- .editor-grid {
- grid-template-columns: minmax(0, 1fr);
- }
+ .editor-grid {grid-template-columns: minmax(0, 1fr);}
}
-
-.cover-block {
- margin-bottom: 16px;
+.cover-block {margin-bottom: 16px;
border: 1px dashed var(--admin-line);
border-radius: 6px;
overflow: hidden;
- background: var(--admin-card);
-}
-
-.cover-block.has {
- border-style: solid;
-}
-
-.cover-block .preview {
- position: relative;
+ background: var(--admin-card);}
+.cover-block.has {border-style: solid;}
+.cover-block .preview {position: relative;
height: 160px;
background: var(--admin-paper-sunken);
background-size: cover;
- background-position: center;
-}
-
-.cover-block .preview .remove {
- position: absolute;
+ background-position: center;}
+.cover-block .preview .remove {position: absolute;
top: 8px;
right: 8px;
background: rgba(0, 0, 0, 0.6);
@@ -1269,118 +727,68 @@ h4 {
border-radius: 4px;
padding: 4px 10px;
cursor: pointer;
- font-size: 12px;
-}
-
-.cover-block .input-row {
- display: flex;
+ font-size: 12px;}
+.cover-block .input-row {display: flex;
gap: 6px;
- padding: 10px 12px;
-}
-
-.cover-block .input-row input {
- flex: 1;
+ padding: 10px 12px;}
+.cover-block .input-row input {flex: 1;
border: 0;
background: transparent;
outline: none;
font-size: 13px;
- color: var(--admin-ink-soft);
-}
-
-.cover-block .input-row button {
- border: 0;
+ color: var(--admin-ink-soft);}
+.cover-block .input-row button {border: 0;
background: var(--admin-accent);
color: var(--admin-on-accent);
padding: 4px 12px;
border-radius: 3px;
cursor: pointer;
- font-size: 12px;
-}
-
-.cover-block .placeholder {
- padding: 26px 18px;
+ font-size: 12px;}
+.cover-block .placeholder {padding: 26px 18px;
text-align: center;
font-size: 13px;
- color: var(--admin-muted);
-}
-
-.editor-mode {
- display: inline-flex;
+ color: var(--admin-muted);}
+.editor-mode {display: inline-flex;
border: 1px solid var(--admin-line);
border-radius: 6px;
overflow: hidden;
- background: var(--admin-card);
-}
-
-.editor-mode button {
- padding: 4px 12px;
+ background: var(--admin-card);}
+.editor-mode button {padding: 4px 12px;
font-size: 12.5px;
border: 0;
background: transparent;
cursor: pointer;
- color: var(--admin-ink-soft);
-}
-
-.editor-mode button.on {
- background: var(--admin-accent);
- color: var(--admin-on-accent);
-}
-
-.editor-stats {
- display: flex;
+ color: var(--admin-ink-soft);}
+.editor-mode button.on {background: var(--admin-accent);
+ color: var(--admin-on-accent);}
+.editor-stats {display: flex;
gap: 14px;
align-items: center;
font-size: 12px;
color: var(--admin-muted);
- padding: 8px 4px 0;
-}
-
-.editor-stats strong {
- color: var(--admin-ink);
+ padding: 8px 4px 0;}
+.editor-stats strong {color: var(--admin-ink);
font-weight: 600;
- font-family: var(--mono);
-}
-
-.save-bar {
- display: flex;
+ font-family: var(--mono);}
+.save-bar {display: flex;
align-items: center;
gap: 12px;
font-size: 12.5px;
color: var(--admin-muted);
- margin-bottom: 8px;
-}
-
-.save-bar .dot {
- width: 8px;
+ margin-bottom: 8px;}
+.save-bar .dot {width: 8px;
height: 8px;
border-radius: 50%;
- background: var(--admin-faint);
-}
-
-.save-bar.saving .dot {
- background: #d39e2f;
- animation: pulse 1.2s ease-in-out infinite;
-}
-
-.save-bar.saved .dot {
- background: #6c8a4f;
-}
-
-.save-bar.dirty .dot {
- background: #b4553f;
-}
-
+ background: var(--admin-faint);}
+.save-bar.saving .dot {background: #d39e2f;
+ animation: pulse 1.2s ease-in-out infinite;}
+.save-bar.saved .dot {background: #6c8a4f;}
+.save-bar.dirty .dot {background: #b4553f;}
@keyframes pulse {
- 0%, 100% {
- opacity: 1;
- }
- 50% {
- opacity: 0.3;
- }
+ 0%, 100% {opacity: 1;}
+ 50% {opacity: 0.3;}
}
-
-.md-pane {
- width: 100%;
+.md-pane {width: 100%;
min-height: 460px;
border: 1px solid var(--admin-line);
border-radius: 3px;
@@ -1391,17 +799,9 @@ h4 {
line-height: 1.7;
color: var(--admin-ink);
resize: vertical;
- outline: none;
-}
-
-.md-pane:focus {
- border-color: var(--admin-accent);
-}
-
-/* ---------- 工具栏:Markdown 模式 ---------- */
-
-.toolbar {
- display: flex;
+ outline: none;}
+.md-pane:focus {border-color: var(--admin-accent);}
+.toolbar {display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
@@ -1409,31 +809,19 @@ h4 {
border: 1px solid var(--admin-line);
border-bottom: 0;
border-radius: 3px 3px 0 0;
- background: var(--admin-paper-sunken);
-}
-
-.tb-group {
- display: inline-flex;
+ background: var(--admin-paper-sunken);}
+.tb-group {display: inline-flex;
align-items: center;
- gap: 2px;
-}
-
-.tb-group + .tb-group {
- border-left: 1px solid var(--admin-line);
+ gap: 2px;}
+.tb-group + .tb-group {border-left: 1px solid var(--admin-line);
padding-left: 10px;
- margin-left: 2px;
-}
-
-.tb-label {
- font-size: 11px;
+ margin-left: 2px;}
+.tb-label {font-size: 11px;
letter-spacing: 0.06em;
color: var(--admin-muted);
margin-right: 4px;
- text-transform: uppercase;
-}
-
-.tb-btn {
- width: 28px;
+ text-transform: uppercase;}
+.tb-btn {width: 28px;
height: 28px;
border: 1px solid transparent;
border-radius: 3px;
@@ -1445,173 +833,102 @@ h4 {
align-items: center;
justify-content: center;
padding: 0;
- transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
-}
-
-.tb-btn:hover {
- background: var(--admin-card);
+ transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;}
+@media (hover: hover) and (pointer: fine) {
+ .tb-btn:hover {background: var(--admin-card);
border-color: var(--admin-line);
- color: var(--admin-ink);
+ color: var(--admin-ink);}
}
-
-.tb-btn.italic {
- font-style: italic;
-}
-
-.tb-btn.mono {
- font-family: var(--mono);
- font-size: 11.5px;
-}
-
-.tb-popover {
- display: flex;
+.tb-btn.italic {font-style: italic;}
+.tb-btn.mono {font-family: var(--mono);
+ font-size: 11.5px;}
+.tb-popover {display: flex;
gap: 6px;
padding: 8px;
border: 1px solid var(--admin-line);
border-top: 0;
- background: var(--admin-paper-sunken);
-}
-
-.tb-popover .input {
- flex: 1;
- font-size: 13px;
-}
-
-.outline {
- position: sticky;
+ background: var(--admin-paper-sunken);}
+.tb-popover .input {flex: 1;
+ font-size: 13px;}
+.outline {position: sticky;
top: 84px;
max-height: calc(100vh - 120px);
+ max-height: calc(100dvh - 120px);
overflow-y: auto;
border-left: 1px solid var(--admin-line);
padding: 6px 0 6px 14px;
- font-size: 12.5px;
-}
-
-.outline .ttl {
- font-size: 11px;
+ font-size: 12.5px;}
+.outline .ttl {font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--admin-muted);
- margin-bottom: 6px;
-}
-
-.outline ul {
- list-style: none;
+ margin-bottom: 6px;}
+.outline ul {list-style: none;
margin: 0;
- padding: 0;
-}
-
-.outline li {
- padding: 3px 0;
+ padding: 0;}
+.outline li {padding: 3px 0;
color: var(--admin-ink-soft);
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
- white-space: nowrap;
+ white-space: nowrap;}
+@media (hover: hover) and (pointer: fine) {
+ .outline li:hover {color: var(--admin-accent);}
}
-
-.outline li:hover {
- color: var(--admin-accent);
-}
-
-.outline li.h3 {
- padding-left: 12px;
- font-size: 12px;
-}
-
-.outline li.active {
- color: var(--admin-accent);
- font-weight: 500;
-}
-
-/* ---------- tag manager ---------- */
-
-.tag-row {
- display: flex;
+.outline li.h3 {padding-left: 12px;
+ font-size: 12px;}
+.outline li.active {color: var(--admin-accent);
+ font-weight: 500;}
+.tag-row {display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border: 1px solid var(--admin-line);
border-radius: 6px;
margin-bottom: 8px;
- background: var(--admin-card);
-}
-
-.tag-row .swatch {
- width: 28px;
+ background: var(--admin-card);}
+.tag-row .swatch {width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid var(--admin-line);
flex-shrink: 0;
cursor: pointer;
position: relative;
- overflow: hidden;
-}
-
-.tag-row .swatch input[type=color] {
- position: absolute;
+ overflow: hidden;}
+.tag-row .swatch input[type=color] {position: absolute;
inset: 0;
opacity: 0;
- cursor: pointer;
-}
-
-.tag-row .name {
- flex: 1;
+ cursor: pointer;}
+.tag-row .name {flex: 1;
font-size: 14px;
border: 0;
background: transparent;
- outline: none;
-}
-
-.tag-row .count {
- font-size: 12px;
+ outline: none;}
+.tag-row .count {font-size: 12px;
color: var(--admin-muted);
- font-family: var(--mono);
-}
-
-.tag-row .acts {
- display: flex;
- gap: 6px;
-}
-
-.tag-row .acts button {
- border: 0;
+ font-family: var(--mono);}
+.tag-row .acts {display: flex;
+ gap: 6px;}
+.tag-row .acts button {border: 0;
background: none;
font-size: 12.5px;
cursor: pointer;
color: var(--admin-accent);
- padding: 2px 6px;
-}
-
-.tag-row .acts .danger {
- color: #b4553f;
-}
-
-/* ---------- settings groups ---------- */
-
-.settings-grid {
- display: grid;
+ padding: 2px 6px;}
+.tag-row .acts .danger {color: #b4553f;}
+.settings-grid {display: grid;
grid-template-columns: 200px minmax(0, 1fr);
gap: 24px;
- align-items: start;
-}
-
+ align-items: start;}
@media (max-width: 760px) {
- .settings-grid {
- grid-template-columns: 1fr;
- }
+ .settings-grid {grid-template-columns: 1fr;}
}
-
-.settings-tabs {
- display: flex;
+.settings-tabs {display: flex;
flex-direction: column;
gap: 2px;
position: sticky;
- top: 84px;
-}
-
-.settings-tabs button {
- text-align: left;
+ top: 84px;}
+.settings-tabs button {text-align: left;
padding: 8px 14px;
border: 0;
background: transparent;
@@ -1619,284 +936,159 @@ h4 {
font-size: 14px;
color: var(--admin-ink-soft);
border-radius: 6px;
- border-left: 2px solid transparent;
+ border-left: 2px solid transparent;}
+@media (hover: hover) and (pointer: fine) {
+ .settings-tabs button:hover {background: var(--admin-paper-sunken);}
}
-
-.settings-tabs button:hover {
- background: var(--admin-paper-sunken);
-}
-
-.settings-tabs button.on {
- background: var(--admin-accent-soft);
+.settings-tabs button.on {background: var(--admin-accent-soft);
color: var(--admin-accent);
border-left-color: var(--admin-accent);
- font-weight: 500;
-}
-
-.theme-swatches {
- display: flex;
+ font-weight: 500;}
+.theme-swatches {display: flex;
gap: 10px;
- flex-wrap: wrap;
-}
-
-.theme-swatches .sw {
- width: 64px;
+ flex-wrap: wrap;}
+.theme-swatches .sw {width: 64px;
height: 64px;
border-radius: 8px;
border: 2px solid var(--admin-line);
cursor: pointer;
position: relative;
- overflow: hidden;
-}
-
-.theme-swatches .sw.on {
- border-color: var(--admin-accent);
-}
-
-.theme-swatches .sw span {
- position: absolute;
+ overflow: hidden;}
+.theme-swatches .sw.on {border-color: var(--admin-accent);}
+.theme-swatches .sw span {position: absolute;
bottom: 4px;
left: 4px;
font-size: 10px;
color: var(--admin-on-accent);
background: rgba(0, 0, 0, 0.4);
padding: 1px 5px;
- border-radius: 3px;
-}
-
-/* ---------- command palette ---------- */
-
-.cmd-overlay {
- position: fixed;
+ border-radius: 3px;}
+.cmd-overlay {position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: flex-start;
justify-content: center;
z-index: 100;
- padding-top: 14vh;
-}
-
-.cmd-panel {
- width: 560px;
+ padding-top: 14vh;}
+.cmd-panel {width: 560px;
max-width: 90vw;
background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 10px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
- overflow: hidden;
-}
-
-.cmd-panel input {
- width: 100%;
+ overflow: hidden;}
+.cmd-panel input {width: 100%;
padding: 16px 18px;
border: 0;
border-bottom: 1px solid var(--admin-line);
outline: none;
font-size: 16px;
- background: var(--admin-card);
-}
-
-.cmd-list {
- max-height: 360px;
- overflow-y: auto;
-}
-
-.cmd-list .row {
- padding: 10px 18px;
+ background: var(--admin-card);}
+.cmd-list {max-height: 360px;
+ overflow-y: auto;}
+.cmd-list .row {padding: 10px 18px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
- border-bottom: 1px solid var(--admin-line);
+ border-bottom: 1px solid var(--admin-line);}
+.cmd-list .row:last-child {border-bottom: 0;}
+.cmd-list .row.on {background: var(--admin-accent-soft);}
+@media (hover: hover) and (pointer: fine) {
+ .cmd-list .row:hover {background: var(--admin-accent-soft);}
}
-
-.cmd-list .row:last-child {
- border-bottom: 0;
-}
-
-.cmd-list .row.on,
-.cmd-list .row:hover {
- background: var(--admin-accent-soft);
-}
-
-.cmd-list .row .label {
- flex: 1;
- font-size: 14px;
-}
-
-.cmd-list .row .hint {
- font-size: 12px;
- color: var(--admin-muted);
-}
-
-.cmd-list .row kbd {
- font-family: var(--mono);
+.cmd-list .row .label {flex: 1;
+ font-size: 14px;}
+.cmd-list .row .hint {font-size: 12px;
+ color: var(--admin-muted);}
+.cmd-list .row kbd {font-family: var(--mono);
font-size: 11px;
border: 1px solid var(--admin-line);
padding: 1px 5px;
- border-radius: 3px;
-}
-
-.cmd-list .empty {
- padding: 24px;
+ border-radius: 3px;}
+.cmd-list .empty {padding: 24px;
text-align: center;
color: var(--admin-muted);
- font-size: 13px;
-}
-
-.shortcut-grid {
- display: grid;
+ font-size: 13px;}
+.shortcut-grid {display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
- gap: 12px;
-}
-
-.shortcut-grid .row {
- display: flex;
+ gap: 12px;}
+.shortcut-grid .row {display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
- font-size: 13px;
-}
-
-.shortcut-grid .row .lab {
- flex: 1;
- color: var(--admin-ink-soft);
-}
-
-.shortcut-grid kbd {
- font-family: var(--mono);
+ font-size: 13px;}
+.shortcut-grid .row .lab {flex: 1;
+ color: var(--admin-ink-soft);}
+.shortcut-grid kbd {font-family: var(--mono);
font-size: 12px;
border: 1px solid var(--admin-line);
background: var(--admin-paper);
padding: 2px 7px;
border-radius: 4px;
min-width: 22px;
- text-align: center;
-}
-
-/* ============================================================
- 全局可访问性 / 主题感知
- ============================================================ */
-
-/* 跟随主题给原生控件(滚动条、表单、autofill)正确色 scheme */
-:root {
- color-scheme: light;
-}
-
-:root[data-theme='ink'],
-:root[data-admin-theme='dark'] {
- color-scheme: dark;
-}
-
-/* 键盘焦点可见(仅键盘触发,不打扰鼠标用户) */
-:where(a, button, input, select, textarea, [tabindex]):focus-visible {
- outline: 2px solid var(--admin-accent);
+ text-align: center;}
+:root {color-scheme: light;}
+:root[data-theme='ink'], :root[data-admin-theme='dark'] {color-scheme: dark;}
+:where(a, button, input, select, textarea, [tabindex]):focus-visible {outline: 2px solid var(--admin-accent);
outline-offset: 2px;
- border-radius: 3px;
-}
-
-/* 前台视图焦点用前台强调色 */
-body:not(.admin-shell-active) :where(a, button, input, select, textarea, [tabindex]):focus-visible {
- outline-color: var(--accent);
-}
-
-/* 编辑器内容区不要画焦点环(Milkdown 内部已经有) */
-.crepe-host :focus-visible {
- outline: none !important;
-}
-
-/* 用户开了「减少动效」时全部动画/过渡降到接近 0 */
+ border-radius: 3px;}
+body:not(.admin-shell-active) :where(a, button, input, select, textarea, [tabindex]):focus-visible {outline-color: var(--accent);}
+.crepe-host :focus-visible {outline: none !important;}
@media (prefers-reduced-motion: reduce) {
- *,
- *::before,
- *::after {
- animation-duration: 0.01ms !important;
+ *, *::before, *::after {animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
- scroll-behavior: auto !important;
- }
- body {
- transition: none !important;
- }
+ scroll-behavior: auto !important;}
+ body {transition: none !important;}
}
-
-/* Modal / overlay 防止滚动穿透到下层 */
-.cmd-overlay {
- overscroll-behavior: contain;
-}
-
-/* ============================================================
- 流体交互层(apple-design 审查落地)
- ============================================================ */
-
-/* 按压反馈:从按下瞬间就有反应,而不是 hover 或松手 */
-button:active:not(:disabled),
-a.btn:active,
-a.pill:active,
-.tab:active,
-.tag-chip:active,
-.rail-left .item:active,
-.admin-side .item:active,
-.cmd-panel .row:active {
- transform: scale(0.97);
-}
-
-/* 触屏:去掉浏览器对双击缩放的猜测延迟 */
-button,
-a.btn,
-a.pill,
-.tab,
-.tag-chip,
-.rail-left .item,
-.admin-side .item {
- touch-action: manipulation;
-}
-
-/* 主题切换:亮度突变改为一次性的 250ms 交叉过渡(仅切换瞬间挂载) */
-html.theme-anim,
-html.theme-anim * {
- transition: background-color 250ms ease, color 250ms ease, border-color 250ms ease !important;
-}
-
-/* 弹层材质化:从触发源方向 scale+fade 进入,退出走同一条路径 */
-.cmd-panel {
- transform-origin: top center;
-}
-.pop-enter-active {
- transition: opacity 160ms ease-out;
-}
-.pop-enter-active .cmd-panel {
- transition: transform 240ms cubic-bezier(0.2, 0.8, 0.25, 1), opacity 160ms ease-out;
-}
-.pop-enter-from {
- opacity: 0;
-}
-.pop-enter-from .cmd-panel {
- transform: scale(0.96) translateY(-10px);
- opacity: 0;
-}
-.pop-leave-active {
- transition: opacity 130ms ease-in;
-}
-.pop-leave-to {
- opacity: 0;
-}
-
-/* reduce ≠ 无反馈:保留短交叉淡入,去掉位移/缩放与主题过渡 */
+.cmd-overlay {overscroll-behavior: contain;}
+button:active:not(:disabled), a.btn:active, a.pill:active, .tab:active, .tag-chip:active, .rail-left .item:active, .admin-side .item:active, .cmd-panel .row:active {transform: scale(0.97);}
+button, a.btn, a.pill, .tab, .tag-chip, .rail-left .item, .admin-side .item {touch-action: manipulation;}
+html.theme-anim, html.theme-anim * {transition: background-color 250ms ease, color 250ms ease, border-color 250ms ease !important;}
+.cmd-panel {transform-origin: top center;}
+.pop-enter-active {transition: opacity 160ms ease-out;}
+.pop-enter-active .cmd-panel {transition: transform 240ms cubic-bezier(0.2, 0.8, 0.25, 1), opacity 160ms ease-out;}
+.pop-enter-from {opacity: 0;}
+.pop-enter-from .cmd-panel {transform: scale(0.96) translateY(-10px);
+ opacity: 0;}
+.pop-leave-active {transition: opacity 130ms ease-in;}
+.pop-leave-to {opacity: 0;}
@media (prefers-reduced-motion: reduce) {
- .pop-enter-active,
- .pop-leave-active {
- transition: opacity 150ms ease !important;
- }
- .pop-enter-active .cmd-panel {
- transition: opacity 150ms ease !important;
- }
- .pop-enter-from .cmd-panel {
- transform: none;
- }
- html.theme-anim,
- html.theme-anim * {
- transition: none !important;
+ .pop-enter-active, .pop-leave-active {transition: opacity 150ms ease !important;}
+ .pop-enter-active .cmd-panel {transition: opacity 150ms ease !important;}
+ .pop-enter-from .cmd-panel {transform: none;}
+ html.theme-anim, html.theme-anim * {transition: none !important;}
+}
+/* ==========================================================================
+ 移动端基线(mobile-native)
+ --------------------------------------------------------------------------
+ 逐条理由:
+ · tap-highlight:触屏点按有系统灰/蓝闪,是最大的「这是网页」信号;
+ 盖掉之后每个可点元素都有自己的 :active 反馈,不会失去点按反馈
+ · text-size-adjust:横屏时 iOS 会自作主张放大字体
+ · touch-action: manipulation:跳过双击缩放等待,点按即时触发
+ · user-select 只给控件:正文要能复制(地址/报错/订单号都是内容)
+ · input 16px:iOS 聚焦字号 <16px 的输入框会缩放页面,且失焦不回弹
+ (overscroll 不设 none:本站是滚动文档,下拉刷新是欢迎的)
+ ========================================================================== */
+html {
+ -webkit-tap-highlight-color: transparent;
+ -webkit-text-size-adjust: 100%;
+}
+button,
+a,
+[role='button'] {
+ touch-action: manipulation;
+ user-select: none;
+ -webkit-user-select: none;
+ -webkit-touch-callout: none;
+}
+@media (pointer: coarse) {
+ input,
+ textarea,
+ select {
+ font-size: 16px;
}
}
diff --git a/frontend/src/ui/yohaku/YohakuHome.vue b/frontend/src/ui/yohaku/YohakuHome.vue
index af72747..36a1cff 100644
--- a/frontend/src/ui/yohaku/YohakuHome.vue
+++ b/frontend/src/ui/yohaku/YohakuHome.vue
@@ -6,7 +6,7 @@ import YohakuFeed from './YohakuFeed.vue'
import YohakuPager from './YohakuPager.vue'
// 首页装配。对应设计稿 viewHome() 的 DOM 顺序:
-// hero → wrap(toolbar + feed + empty + pager) → panels。
+// hero → wrap(toolbar + feed + empty + pager) → showcase(作品前三件 + 查看所有)。
// 数据与筛选状态由 HomeView 持有(它读路由 query),这里只做呈现。
const props = defineProps({
@@ -65,25 +65,33 @@ const emit = defineEmits(['update:kind', 'update:tag', 'update:query', 'go'])
做过的东西
Projects
-
全部作品 →
+
+
全部作品 →
+
-
-
![]()
+
+
{{ p.status }}
-
{{ p.title }}
-
{{ p.summary }}
+
+
+ {{ p.title }}
+ {{ p.status }}
+
+
{{ p.summary }}
+
diff --git a/frontend/src/ui/yohaku/YohakuTop.vue b/frontend/src/ui/yohaku/YohakuTop.vue
index 5896b90..e54eb4c 100644
--- a/frontend/src/ui/yohaku/YohakuTop.vue
+++ b/frontend/src/ui/yohaku/YohakuTop.vue
@@ -1,33 +1,78 @@