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 @@