overflow-x:auto 会强制 overflow-y 也建滚动盒,裁切 table 向外 spread 的 1px box-shadow 描边,导致不需要滚动的窄表格右下角边框断裂。 给 .table-wrap 加 padding:1px 为描边预留空间,负 margin:-1px 抵消外部尺寸 偏移,使 wrap 不撑大父容器、左上仍贴齐正文边缘。
1109 lines
29 KiB
CSS
1109 lines
29 KiB
CSS
@import "tailwindcss";
|
||
|
||
@custom-variant dark (&:where(.dark, .dark *));
|
||
|
||
@theme {
|
||
/* 覆盖 Tailwind v4 默认 --font-sans(ui-sans-serif, system-ui, sans-serif, ...):
|
||
默认栈不含 CJK sans,font-sans 类一旦生效,body 的中文字体栈会被绕过,
|
||
中文 fallback 到系统衬线体。这里对齐 body 的 CJK sans 栈,根治全项目。 */
|
||
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif, 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei';
|
||
--font-serif: 'Source Serif 4', 'Noto Serif SC', 'Source Han Serif SC', 'Songti SC', Georgia, serif;
|
||
|
||
/* Catppuccin Latte(亮色)。语义 token → 调色板角色:
|
||
theme=base entry=mantle primary=text secondary=subtext1
|
||
tertiary=overlay0 content=subtext0 code-block=crust code-bg=surface0
|
||
border=surface1 accent=green accent-2=teal(主/次强调色) */
|
||
--color-paper-theme: #eff1f5;
|
||
--color-paper-entry: #e6e9ef;
|
||
--color-paper-primary: #4c4f69;
|
||
--color-paper-secondary: #5c5f77;
|
||
--color-paper-tertiary: #9ca0b0;
|
||
--color-paper-content: #6c6f85;
|
||
--color-paper-code-block: #dce0e8;
|
||
--color-paper-code-bg: #ccd0da;
|
||
--color-paper-border: #bcc0cc;
|
||
--color-paper-accent: #40a02b;
|
||
--color-paper-accent-soft: #ccd0da;
|
||
--color-paper-accent-2: #179299;
|
||
--color-paper-accent-2-soft: #ccd0da;
|
||
|
||
/* 圆角三档梯度:容器 32 → 内容 16 → 微元素 8。
|
||
按钮/头像/徽章另用胶囊(Tailwind rounded-full),独立于本梯度。 */
|
||
--radius-card: 2rem; /* 32px:文章卡片、封面、admin 主面板 */
|
||
--radius-paper: 16px; /* 16px:代码块、正文图片、iframe、表格、输入框、admin 卡片 */
|
||
--radius-mini: 8px; /* 8px:标签、复制按钮、toc、paginav */
|
||
--gap-paper: 24px;
|
||
--content-gap-paper: 20px;
|
||
--nav-width-paper: 1024px;
|
||
--main-width-paper: 720px;
|
||
}
|
||
|
||
@layer base {
|
||
html {
|
||
scroll-behavior: smooth;
|
||
background-color: var(--color-paper-theme);
|
||
}
|
||
|
||
body {
|
||
background-color: var(--color-paper-theme);
|
||
color: var(--color-paper-primary);
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
}
|
||
|
||
@keyframes page-enter {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(16px) scale(0.995);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
}
|
||
|
||
.animate-page-enter {
|
||
animation: page-enter 400ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
}
|
||
|
||
/* Popover / 确认框淡入缩放(比 page-enter 更轻、更快) */
|
||
@keyframes popover-enter {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateX(-50%) scale(0.95);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateX(-50%) scale(1);
|
||
}
|
||
}
|
||
|
||
.animate-popover-enter {
|
||
animation: popover-enter 150ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
}
|
||
|
||
/* Minimalist custom scrollbars */
|
||
* {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--color-paper-border) transparent;
|
||
}
|
||
|
||
::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background-color: var(--color-paper-border);
|
||
border-radius: 10px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background-color: var(--color-paper-tertiary);
|
||
}
|
||
|
||
.dark {
|
||
/* Catppuccin Mocha(暗色)。accent-soft 带绿调、accent-2-soft 带 teal 调,
|
||
在 surface0 暗底上透出微弱主/次强调色。 */
|
||
--color-paper-theme: #1e1e2e;
|
||
--color-paper-entry: #181825;
|
||
--color-paper-primary: #cdd6f4;
|
||
--color-paper-secondary: #bac2de;
|
||
--color-paper-tertiary: #6c7086;
|
||
--color-paper-content: #a6adc8;
|
||
--color-paper-code-block: #313244;
|
||
--color-paper-code-bg: #313244;
|
||
--color-paper-border: #45475a;
|
||
--color-paper-accent: #a6e3a1;
|
||
--color-paper-accent-soft: #1e2e22;
|
||
--color-paper-accent-2: #94e2d5;
|
||
--color-paper-accent-2-soft: #1e2a28;
|
||
}
|
||
}
|
||
|
||
@layer components {
|
||
.post-single {
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.post-header {
|
||
margin: 48px auto 48px auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.post-title {
|
||
font-size: clamp(2.5rem, 6vw, 4.5rem);
|
||
font-weight: 800;
|
||
line-height: 1.1;
|
||
color: var(--color-paper-primary);
|
||
word-break: break-word;
|
||
letter-spacing: -0.04em;
|
||
margin: 0;
|
||
}
|
||
|
||
.post-description {
|
||
color: var(--color-paper-secondary);
|
||
font-size: 1.125rem;
|
||
font-weight: 500;
|
||
line-height: 1.7;
|
||
margin: 0;
|
||
}
|
||
|
||
.post-meta {
|
||
margin-top: 8px;
|
||
color: var(--color-paper-tertiary);
|
||
font-size: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
flex-wrap: wrap;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.post-meta a {
|
||
color: var(--color-paper-secondary);
|
||
text-decoration-style: dotted;
|
||
text-underline-offset: 0.3rem;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.post-meta a:hover {
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
.breadcrumbs {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.2rem;
|
||
color: var(--color-paper-secondary);
|
||
font-size: 14px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.breadcrumbs a {
|
||
font-size: 16px;
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
.breadcrumbs a:hover {
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.2rem;
|
||
}
|
||
|
||
.entry-hint {
|
||
display: inline-flex;
|
||
margin-left: 0.5rem;
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
details.toc {
|
||
margin-bottom: var(--content-gap-paper);
|
||
background: var(--color-paper-code-bg);
|
||
border-radius: var(--radius-mini);
|
||
border: 1px solid var(--color-paper-border);
|
||
}
|
||
|
||
details.toc summary {
|
||
padding: 0.3rem 1.2rem;
|
||
border-radius: var(--radius-mini);
|
||
cursor: pointer;
|
||
display: list-item;
|
||
width: 100%;
|
||
user-select: none;
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
details.toc .title {
|
||
display: inline;
|
||
font-weight: 500;
|
||
margin-inline-start: 0.2rem;
|
||
}
|
||
|
||
details.toc .inner {
|
||
margin: 0 2.4rem;
|
||
padding-bottom: 0.6rem;
|
||
}
|
||
|
||
details.toc ul {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
details.toc li {
|
||
margin: 0.3rem 0;
|
||
}
|
||
|
||
details.toc a {
|
||
color: var(--color-paper-secondary);
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
}
|
||
|
||
details.toc a:hover {
|
||
color: var(--color-paper-primary);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.3rem;
|
||
}
|
||
|
||
details.toc li ul {
|
||
margin-inline-start: var(--gap-paper);
|
||
}
|
||
|
||
details {
|
||
interpolate-size: allow-keywords;
|
||
}
|
||
|
||
details::details-content {
|
||
height: 0;
|
||
opacity: 0;
|
||
overflow: clip;
|
||
transition: height 150ms ease, opacity 150ms ease, content-visibility 150ms allow-discrete;
|
||
}
|
||
|
||
details[open]::details-content {
|
||
height: auto;
|
||
opacity: 1;
|
||
}
|
||
|
||
/* 正文折叠块(作者手写的 <details>,区别于 .toc 导航块)。
|
||
复用代码块/blockquote 的视觉语言:同款底色 + 边框 + 16px 圆角;
|
||
自绘 chevron 替代浏览器原生三角,展开时旋转 90°。
|
||
:not(.toc) 避免与上面的 TOC 专用样式冲突。 */
|
||
.md-content details:not(.toc) {
|
||
margin: 1rem 0;
|
||
background: var(--color-paper-code-bg);
|
||
border: 1px solid var(--color-paper-border);
|
||
border-radius: var(--radius-paper);
|
||
}
|
||
|
||
.md-content details:not(.toc) > summary {
|
||
padding: 0.6rem 1rem;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
list-style: none;
|
||
color: var(--color-paper-primary);
|
||
font-weight: 500;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
transition: background-color 120ms ease;
|
||
}
|
||
|
||
/* 去掉 WebKit/Blink 原生三角,统一用 ::before 自绘 chevron。 */
|
||
.md-content details:not(.toc) > summary::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
/* 自绘 chevron:实心三角,展开时旋转 90° 平滑过渡。
|
||
用 currentColor 继承 summary 文字色,随主题变化。 */
|
||
.md-content details:not(.toc) > summary::before {
|
||
content: "";
|
||
display: inline-block;
|
||
flex-shrink: 0;
|
||
width: 0;
|
||
height: 0;
|
||
border-style: solid;
|
||
border-width: 0.32rem 0.42rem 0.32rem 0;
|
||
border-color: transparent currentColor transparent transparent;
|
||
transform: rotate(0deg);
|
||
transition: transform 150ms ease;
|
||
margin-inline-start: 0.1rem;
|
||
}
|
||
|
||
.md-content details:not(.toc)[open] > summary::before {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.md-content details:not(.toc) > summary:hover {
|
||
background-color: color-mix(in srgb, var(--color-paper-accent) 8%, transparent);
|
||
}
|
||
|
||
/* 键盘可访问:focus-visible 给可见轮廓(WCAG 2.4.7)。
|
||
.dark 前缀走暗色主题色。 */
|
||
.md-content details:not(.toc) > summary:focus-visible {
|
||
outline: 2px solid var(--color-paper-accent);
|
||
outline-offset: -2px;
|
||
border-radius: var(--radius-paper);
|
||
}
|
||
|
||
/* 展开内容左右内边距(代码块/段落不贴边),末元素留底部呼吸空间。 */
|
||
.md-content details:not(.toc)[open] > *:not(summary) {
|
||
padding-inline: 1rem;
|
||
}
|
||
|
||
/* 首元素紧接 summary 去顶部 margin,末元素去底部 margin(容器已留白)。 */
|
||
.md-content details:not(.toc)[open] > *:not(summary):first-of-type {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.md-content details:not(.toc)[open] > *:not(summary):last-child {
|
||
margin-bottom: 0;
|
||
padding-bottom: 0.8rem;
|
||
}
|
||
|
||
.entry-cover {
|
||
margin-bottom: var(--content-gap-paper);
|
||
}
|
||
|
||
/* 封面用 blur-img 结构:去 margin、保持圆角,内层 img cover 填充 */
|
||
.entry-cover .blur-img {
|
||
margin: 0;
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
}
|
||
.entry-cover img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.post-content {
|
||
color: var(--color-paper-content);
|
||
margin: 30px 0;
|
||
}
|
||
|
||
.md-content h1 {
|
||
margin: 40px auto 32px;
|
||
font-size: 40px;
|
||
color: var(--color-paper-primary);
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
.md-content h2 {
|
||
margin: 32px auto 24px;
|
||
font-size: 32px;
|
||
color: var(--color-paper-primary);
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
.md-content h3 {
|
||
margin: 24px 0 16px;
|
||
font-size: 24px;
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
.md-content h4 {
|
||
margin: 24px 0 16px;
|
||
font-size: 16px;
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
.md-content h5 {
|
||
margin: 24px 0 16px;
|
||
font-size: 14px;
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
.md-content h6 {
|
||
margin: 24px 0 16px;
|
||
font-size: 12px;
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
/* 锚点目标偏移:scrollIntoView / 原生 fragment-scroll 落点避开 80px sticky header。
|
||
[id] 限定只作用于带锚点 id 的标题;6rem = 80px header + 16px 呼吸空间。 */
|
||
.md-content :is(h1, h2, h3, h4, h5, h6)[id] {
|
||
scroll-margin-top: 6rem;
|
||
}
|
||
|
||
.md-content a:not(.anchor) {
|
||
text-underline-offset: 0.3rem;
|
||
text-decoration: underline;
|
||
color: var(--color-paper-accent);
|
||
}
|
||
|
||
.md-content a:not(.anchor):hover {
|
||
color: var(--color-paper-accent-2);
|
||
}
|
||
|
||
.md-content p {
|
||
margin-bottom: var(--content-gap-paper);
|
||
line-height: 1.85;
|
||
}
|
||
|
||
.md-content p:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.md-content blockquote {
|
||
margin: 1rem 0;
|
||
padding-inline-start: 1rem;
|
||
border-inline-start: 0.3rem solid var(--color-paper-content);
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
.md-content blockquote p:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 脚注引用上标:[^id] 渲染成的 <sup class="fn-ref">。
|
||
* line-height: 0 防止上标撑高所在行行高;font-size 缩小为 0.75em。 */
|
||
.md-content .fn-ref {
|
||
font-size: 0.75em;
|
||
line-height: 0;
|
||
vertical-align: super;
|
||
margin-inline-start: 0.1em;
|
||
}
|
||
|
||
/* 引用数字链接:不加下划线(上标下划线极难看),用 accent 色区分。 */
|
||
.md-content .fn-ref-link {
|
||
text-decoration: none;
|
||
color: var(--color-paper-accent);
|
||
}
|
||
|
||
.md-content .fn-ref-link:hover {
|
||
color: var(--color-paper-accent-2);
|
||
}
|
||
|
||
/* 脚注定义块:仿 blockquote 的左侧边框视觉语言,但更轻(0.2rem + 稍小字号)。 */
|
||
.md-content .footnote-definition {
|
||
margin: 1rem 0;
|
||
padding-inline-start: 1rem;
|
||
border-inline-start: 0.2rem solid var(--color-paper-content);
|
||
font-size: 0.875em;
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
.md-content .footnote-definition:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 定义内末段去底距,避免与 back-link 间距过大。 */
|
||
.md-content .footnote-definition > p:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 定义编号上标:与正文引用数字呼应。 */
|
||
.md-content .footnote-definition-label {
|
||
font-size: 0.75em;
|
||
line-height: 0;
|
||
vertical-align: super;
|
||
margin-inline-end: 0.25em;
|
||
color: var(--color-paper-tertiary);
|
||
}
|
||
|
||
/* 返回正文链接:↩ 符号,弱色,hover 转 accent。 */
|
||
.md-content .fn-backref {
|
||
margin-inline-start: 0.25em;
|
||
text-decoration: none;
|
||
color: var(--color-paper-tertiary);
|
||
}
|
||
|
||
.md-content .fn-backref:hover {
|
||
color: var(--color-paper-accent);
|
||
}
|
||
|
||
/* 多引用时的数字上标(↩² 中的 ²)。 */
|
||
.md-content .fn-backref-num {
|
||
font-size: 0.75em;
|
||
vertical-align: super;
|
||
}
|
||
|
||
.md-content ol,
|
||
.md-content ul {
|
||
padding-inline-start: 1.25rem;
|
||
margin-bottom: var(--content-gap-paper);
|
||
}
|
||
|
||
/* Tailwind Preflight 把 list-style 清成 none,这里恢复被清掉的 marker。
|
||
* 任务列表项(含 checkbox 的 li)另有规则覆盖为 none,不受影响。 */
|
||
.md-content ul {
|
||
list-style-type: disc;
|
||
}
|
||
|
||
.md-content ol {
|
||
list-style-type: decimal;
|
||
}
|
||
|
||
.md-content li {
|
||
margin-top: 0.3rem;
|
||
}
|
||
|
||
.md-content li p {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 任务列表:pulldown-cmark 把 - [ ] / - [x] 渲染成裸 <li><input type="checkbox">…
|
||
* (无 <label> 包裹,也无 data-type 标记)。用 :has() 识别含 checkbox 的 li,
|
||
* 布局成 flex 让复选框与文本对齐;不支持 :has() 的浏览器降级为普通列表,仍可读。 */
|
||
.md-content li:has(> input[type="checkbox"]) {
|
||
list-style: none;
|
||
margin-left: -1.25rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.md-content li > input[type="checkbox"] {
|
||
/* appearance:none 全自定义:告别浏览器原生方框形状/边框差异。
|
||
对勾用内联 SVG background(<input> 是替换元素,吃不到 ::after),
|
||
颜色按主题烘焙——亮色中绿底配白对勾,暗色浅绿底配深对勾,对比最强。
|
||
文章内 checkbox 是 disabled 的纯展示态,故不加 focus/cursor 交互。 */
|
||
appearance: none;
|
||
-webkit-appearance: none;
|
||
flex-shrink: 0;
|
||
width: 1.15rem;
|
||
height: 1.15rem;
|
||
margin: 0;
|
||
border: 1.5px solid var(--color-paper-border);
|
||
border-radius: 4px;
|
||
background-color: transparent;
|
||
background-position: center;
|
||
background-repeat: no-repeat;
|
||
background-size: 0.75rem;
|
||
cursor: default;
|
||
transition: background-color 0.15s ease, border-color 0.15s ease;
|
||
}
|
||
|
||
.md-content li > input[type="checkbox"]:checked {
|
||
background-color: var(--color-paper-accent);
|
||
border-color: var(--color-paper-accent);
|
||
/* 亮色:中绿 #40a02b 底 + 白对勾(对比 ≥ 4.5:1) */
|
||
background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%20fill='none'%20stroke='%23ffffff'%20stroke-width='2.2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M3.5%208.5l3%203%206-6.5'/%3E%3C/svg%3E");
|
||
}
|
||
|
||
/* 暗色:浅绿 #a6e3a1 底上白对勾对比不足,改深色对勾(主题底色 #1e1e2e) */
|
||
.dark .md-content li > input[type="checkbox"]:checked {
|
||
background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%20fill='none'%20stroke='%231e1e2e'%20stroke-width='2.2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M3.5%208.5l3%203%206-6.5'/%3E%3C/svg%3E");
|
||
}
|
||
|
||
.md-content hr {
|
||
margin: 30px 0;
|
||
height: 2px;
|
||
background: var(--color-paper-tertiary);
|
||
border: 0;
|
||
}
|
||
|
||
/* table-wrap:渲染器给宽表格套的滚动容器,移动端窄屏可横向滚动。
|
||
(见 src/api/markdown.rs::wrap_tables)。min-width 让窄表仍撑满、宽表撑开触发滚动。
|
||
padding 为 table 的 1px box-shadow 描边预留空间:overflow 容器(overflow-x:auto 会
|
||
强制 overflow-y 也建滚动盒)会裁切向外 spread 的阴影,导致右下边框断裂。
|
||
负 margin 抵消 padding 的外部尺寸,使 wrap 不撑大父容器、左上仍贴齐正文边缘。 */
|
||
.md-content .table-wrap {
|
||
margin: -1px;
|
||
padding: 1px;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.md-content table {
|
||
margin-bottom: var(--content-gap-paper);
|
||
width: 100%;
|
||
min-width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
border-radius: var(--radius-paper);
|
||
box-shadow: 0 0 0 1px var(--color-paper-border);
|
||
}
|
||
|
||
.md-content table thead th {
|
||
background: var(--color-paper-code-block);
|
||
font-weight: 600;
|
||
color: var(--color-paper-primary);
|
||
padding: 16px 24px;
|
||
text-align: left;
|
||
border-bottom: 1px solid var(--color-paper-border);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.md-content table thead th:first-child {
|
||
border-top-left-radius: var(--radius-paper);
|
||
}
|
||
|
||
.md-content table thead th:last-child {
|
||
border-top-right-radius: var(--radius-paper);
|
||
}
|
||
|
||
.md-content table tbody tr:last-child td:first-child {
|
||
border-bottom-left-radius: var(--radius-paper);
|
||
}
|
||
|
||
.md-content table tbody tr:last-child td:last-child {
|
||
border-bottom-right-radius: var(--radius-paper);
|
||
}
|
||
|
||
.md-content table tbody td {
|
||
padding: 16px 24px;
|
||
line-height: 1.6;
|
||
border-bottom: 1px solid var(--color-paper-border);
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
.md-content table tbody tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.md-content table tbody tr {
|
||
transition: background-color 0.2s ease;
|
||
}
|
||
|
||
.md-content table tbody tr:hover {
|
||
background: var(--color-paper-entry);
|
||
}
|
||
|
||
/* 排除 CodeMirror 编辑器内的 <img>:CM 用 <img class="cm-widgetBuffer">(width:0、
|
||
height:1em 的占位符)对齐折叠 widget 的基线。若被这里的 margin/height 命中,
|
||
会把折叠那行 .cm-line 撑高约 32px(上下各 1rem margin)。用 :where() 把
|
||
.cm-editor 后代的 img 优先级压到 0,确保 CM 内部任何 img 都不受正文图片样式影响。 */
|
||
.md-content img:where(:not(.cm-widgetBuffer)) {
|
||
border-radius: var(--radius-paper);
|
||
margin: 1rem 0;
|
||
max-width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.dark .md-content img:not(.blur-img-placeholder):not(.blur-img-full),
|
||
.dark .md-content .blur-img,
|
||
.dark .lightbox-img {
|
||
filter: brightness(0.9);
|
||
}
|
||
|
||
.md-content-img-zoomable {
|
||
cursor: zoom-in;
|
||
transition: opacity 0.2s ease-out;
|
||
}
|
||
|
||
.md-content-img-zoomable:hover {
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.md-content iframe {
|
||
max-width: 100%;
|
||
border-radius: var(--radius-paper);
|
||
}
|
||
|
||
.md-content code {
|
||
padding: 0.2rem 0.3rem;
|
||
font-size: 0.85em;
|
||
line-height: 1.5;
|
||
background: var(--color-paper-code-bg);
|
||
border-radius: 0.2rem;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||
}
|
||
|
||
.md-content pre {
|
||
position: relative;
|
||
margin-bottom: var(--content-gap-paper);
|
||
background: var(--color-paper-code-block);
|
||
border-radius: var(--radius-paper);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.md-content pre code {
|
||
display: block;
|
||
padding: 16px;
|
||
background: transparent;
|
||
border-radius: 0;
|
||
overflow-x: auto;
|
||
white-space: pre;
|
||
font-size: 1rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.md-content pre code span {
|
||
display: inline !important;
|
||
}
|
||
|
||
.copy-code {
|
||
display: none;
|
||
position: absolute;
|
||
top: 8px;
|
||
right: 8px;
|
||
color: var(--color-paper-theme);
|
||
background: var(--color-paper-tertiary);
|
||
border-radius: var(--radius-mini);
|
||
padding: 4px 8px;
|
||
font-size: 12px;
|
||
user-select: none;
|
||
cursor: pointer;
|
||
border: none;
|
||
font-family: inherit;
|
||
transition: background 0.2s ease-out;
|
||
}
|
||
|
||
.copy-code:hover {
|
||
background: var(--color-paper-accent);
|
||
}
|
||
|
||
pre:hover .copy-code,
|
||
.highlight:hover .copy-code {
|
||
display: block;
|
||
}
|
||
|
||
/* ── mermaid 图表容器 ──
|
||
data-mermaid-rendered 由 yggdrasil-core/mermaid.ts 渲染成功后标记在 <pre> 上。
|
||
渲染后 pre 唯一子元素是 <svg>(mermaid 块不注入 copy-code 按钮),flex 安全。
|
||
居中 + 缩放适配宽度,消除常规图的横向滚动;超宽图 overflow-x:auto 兜底。 */
|
||
.md-content pre[data-mermaid-rendered] {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 24px 16px;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.md-content pre[data-mermaid-rendered] > svg {
|
||
max-width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
}
|
||
|
||
/* 渲染失败:保留原始源码,加 accent 色左边条提示(站点 accent 为绿,沿用配色体系) */
|
||
.md-content pre.mermaid-error {
|
||
border-left: 3px solid var(--color-paper-accent);
|
||
}
|
||
|
||
.anchor {
|
||
display: none;
|
||
text-decoration: none;
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
.md-content h1:hover .anchor,
|
||
.md-content h2:hover .anchor,
|
||
.md-content h3:hover .anchor,
|
||
.md-content h4:hover .anchor,
|
||
.md-content h5:hover .anchor,
|
||
.md-content h6:hover .anchor {
|
||
display: inline-flex;
|
||
margin-inline-start: 0.5em;
|
||
font-weight: 500;
|
||
user-select: none;
|
||
text-decoration: none;
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
.anchor:hover {
|
||
color: var(--color-paper-primary) !important;
|
||
}
|
||
|
||
.post-footer {
|
||
margin-top: var(--content-gap-paper);
|
||
}
|
||
|
||
.post-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0 0 var(--content-gap-paper) 0;
|
||
}
|
||
|
||
.post-tags li {
|
||
display: inline-block;
|
||
}
|
||
|
||
.post-tags a {
|
||
display: block;
|
||
padding: 0 14px;
|
||
color: var(--color-paper-secondary);
|
||
font-size: 14px;
|
||
line-height: 34px;
|
||
background: var(--color-paper-code-bg);
|
||
border-radius: var(--radius-mini);
|
||
border: 1px solid var(--color-paper-border);
|
||
text-decoration: none;
|
||
transition: all 0.2s ease-out;
|
||
}
|
||
|
||
.post-tags a:hover {
|
||
background: var(--color-paper-accent-soft);
|
||
border-color: var(--color-paper-accent);
|
||
color: var(--color-paper-accent);
|
||
}
|
||
|
||
.paginav {
|
||
display: flex;
|
||
border-radius: var(--radius-mini);
|
||
background: var(--color-paper-code-bg);
|
||
border: 1px solid var(--color-paper-border);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.paginav a {
|
||
width: 50%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
padding: 0.8rem;
|
||
text-decoration: none;
|
||
color: var(--color-paper-primary);
|
||
transition: background 0.2s ease-out;
|
||
}
|
||
|
||
.paginav a:hover {
|
||
background: var(--color-paper-border);
|
||
}
|
||
|
||
.paginav .next {
|
||
margin-inline-start: auto;
|
||
text-align: right;
|
||
}
|
||
|
||
.paginav .title {
|
||
letter-spacing: 1px;
|
||
font-size: 0.8rem;
|
||
color: var(--color-paper-secondary);
|
||
}
|
||
|
||
.paginav .post-title-nav {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.back-to-home {
|
||
margin-top: calc(var(--content-gap-paper) * 1.5);
|
||
padding-top: var(--content-gap-paper);
|
||
border-top: 1px solid var(--color-paper-border);
|
||
}
|
||
|
||
.back-to-home a,
|
||
.back-to-home button {
|
||
color: var(--color-paper-secondary);
|
||
font-size: 14px;
|
||
text-decoration: none;
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.back-to-home a:hover,
|
||
.back-to-home button:hover {
|
||
color: var(--color-paper-primary);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.3rem;
|
||
}
|
||
|
||
/* 卡片封面:blur-img 结构,固定 21:9 比例,hover 缩放。
|
||
双类选择器提升特异性,可靠覆盖 .blur-img 的 aspect-ratio: var(--ar)
|
||
(卡片不写 --ar,var(--ar) 解析为空会导致高度塌陷)。
|
||
21:9 与后台编辑器封面区统一比例,宽屏横幅式。 */
|
||
.blur-img.post-card-cover-blur {
|
||
margin: 0;
|
||
aspect-ratio: 21 / 9;
|
||
}
|
||
.post-card-cover-blur img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
transition: transform 0.3s ease-out;
|
||
}
|
||
.group:hover .post-card-cover-blur img {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
a.accent-link {
|
||
color: var(--color-paper-accent);
|
||
text-decoration: none;
|
||
transition: color 0.2s ease-out;
|
||
}
|
||
a.accent-link:hover {
|
||
color: var(--color-paper-primary);
|
||
}
|
||
|
||
.post-card-accent {
|
||
position: relative;
|
||
}
|
||
.post-card-accent::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 0;
|
||
height: 2px;
|
||
background: var(--color-paper-accent);
|
||
transition: width 0.3s ease-out;
|
||
}
|
||
.post-card-accent:hover::after {
|
||
width: 100%;
|
||
}
|
||
|
||
/* ========== Blur-up 渐进图片加载 ==========
|
||
放在 @layer components 内,与 .blur-img.post-card-cover-blur 同层,
|
||
这样特异性能正常生效(layer 外规则会无条件覆盖 layer 内规则,
|
||
导致卡片的 aspect-ratio:4/3 败给全局 var(--ar))。 */
|
||
.blur-img {
|
||
position: relative;
|
||
display: block;
|
||
overflow: hidden;
|
||
aspect-ratio: var(--ar);
|
||
/* 加载中灰底(占位图未加载完时) */
|
||
background: var(--color-paper-code-bg);
|
||
border-radius: var(--radius-mini);
|
||
margin: 1em 0;
|
||
}
|
||
.blur-img-placeholder {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
/* 重置 .md-content img 的 margin/max-width/height,避免把 absolute 图层推偏 */
|
||
margin: 0;
|
||
max-width: none;
|
||
/* 20px 占位图放大后模糊,scale 遮边缘 */
|
||
filter: blur(20px) saturate(1.2);
|
||
transform: scale(1.1);
|
||
transition: opacity 0.4s ease;
|
||
}
|
||
.blur-img-full {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
/* 重置 .md-content img 的 margin/max-width/height,避免把 absolute 图层推偏 */
|
||
margin: 0;
|
||
max-width: none;
|
||
opacity: 0;
|
||
z-index: 1;
|
||
}
|
||
/* full 加载完成后由 JS 直接设 opacity:1(不依赖 CSS transition,
|
||
避免合成层重绘时机导致 opacity 卡在 0)。 */
|
||
.blur-img-full.is-loaded,
|
||
.blur-img.is-loaded .blur-img-full {
|
||
opacity: 1;
|
||
}
|
||
/* 容器标记 loaded 后:显式隐藏占位图 + 去掉灰底,避免重绘时机导致残留/灰边 */
|
||
.blur-img.is-loaded .blur-img-placeholder {
|
||
opacity: 0;
|
||
}
|
||
.blur-img.is-loaded {
|
||
background: transparent;
|
||
}
|
||
|
||
/* ========== 主题切换图标进入动画 ==========
|
||
点击切换主题时 theme.set 立即换 SVG(配合 RSX 的 key 强制重挂载),
|
||
该动画为新挂载的图标播放一次缩放+淡入,给用户即时反馈。
|
||
仅作用于 .theme-toggle 内的 svg,不影响页面其它 svg。 */
|
||
.theme-toggle svg {
|
||
animation: theme-icon-enter 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes theme-icon-enter {
|
||
from {
|
||
opacity: 0;
|
||
transform: scale(0.5) rotate(-60deg);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: scale(1) rotate(0deg);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@media screen and (max-width: 768px) {
|
||
.post-title {
|
||
font-size: 32px;
|
||
}
|
||
|
||
.md-content h1 {
|
||
font-size: 32px;
|
||
}
|
||
|
||
.md-content h2 {
|
||
font-size: 26px;
|
||
}
|
||
|
||
.md-content h3 {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.paginav {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.paginav a {
|
||
width: 100%;
|
||
}
|
||
|
||
.paginav .next {
|
||
margin-inline-start: 0;
|
||
text-align: left;
|
||
border-top: 1px solid var(--color-paper-border);
|
||
}
|
||
|
||
details.toc .inner {
|
||
margin: 0 1rem;
|
||
}
|
||
|
||
/* 旧文章兜底:未重建的文章是裸 table(无 .table-wrap 包裹),移动端需 block 化才能滚动。
|
||
选择器 .md-content > div > table 精确命中「片段容器直接子代的裸 table」,
|
||
不会误伤新文章 .table-wrap 内的 table(后者多一层 div,圆角视觉完整)。
|
||
block 化会损 thead/tbody 圆角,但仅移动端 + 仅旧文章,可接受。 */
|
||
.md-content > div > table {
|
||
display: block;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
}
|
||
|
||
@keyframes pageEnter {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(6px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.page-enter {
|
||
animation: pageEnter 0.15s ease-out;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
html {
|
||
scroll-behavior: auto;
|
||
}
|
||
|
||
details::details-content {
|
||
transition: none;
|
||
}
|
||
|
||
.page-enter {
|
||
animation: none;
|
||
}
|
||
|
||
.theme-toggle svg {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* ========== Blur-up 渐进图片加载(已移入 @layer components) ========== */
|
||
@source "./src/**/*.{rs,html}";
|
||
|
||
/* ========== CodeMirror 代码运行器高度自适应优化 ========== */
|
||
.code-runner-editor {
|
||
min-height: 120px;
|
||
max-height: 400px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.code-runner-editor .cm-editor {
|
||
flex: 1 1 auto !important;
|
||
}
|
||
.code-runner-editor .cm-scroller {
|
||
overflow: auto;
|
||
}
|
||
|