diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index c1c7e38..8b48452 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -54,9 +54,18 @@ const consoleSites = [
@@ -257,25 +266,53 @@ const consoleSites = [
const button = document.querySelector('[data-theme-toggle]');
const media = window.matchMedia('(prefers-color-scheme: dark)');
const getSystemTheme = () => (media.matches ? 'dark' : 'light');
- const getTheme = () => localStorage.getItem(storageKey) || getSystemTheme();
+ const getSavedTheme = () => {
+ try {
+ const theme = localStorage.getItem(storageKey);
+ return theme === 'dark' || theme === 'light' ? theme : null;
+ } catch {
+ return null;
+ }
+ };
+ const setSavedTheme = (theme) => {
+ try {
+ if (theme === getSystemTheme()) localStorage.removeItem(storageKey);
+ else localStorage.setItem(storageKey, theme);
+ } catch {
+ // localStorage can be unavailable in strict privacy modes.
+ }
+ };
+ const getTheme = () => getSavedTheme() || getSystemTheme();
function applyTheme(theme) {
root.dataset.theme = theme;
+ root.dataset.themeMode = getSavedTheme() ? 'manual' : 'auto';
button?.setAttribute('aria-label', theme === 'dark' ? '切换到日间模式' : '切换到夜间模式');
- button?.setAttribute('title', theme === 'dark' ? '切换到日间模式' : '切换到夜间模式');
+ button?.setAttribute(
+ 'title',
+ getSavedTheme()
+ ? (theme === 'dark' ? '切换到日间模式' : '切换到夜间模式')
+ : `跟随系统:${theme === 'dark' ? '夜间' : '日间'}模式`
+ );
}
applyTheme(getTheme());
button?.addEventListener('click', () => {
const nextTheme = root.dataset.theme === 'dark' ? 'light' : 'dark';
- localStorage.setItem(storageKey, nextTheme);
+ setSavedTheme(nextTheme);
applyTheme(nextTheme);
});
- media.addEventListener('change', () => {
- if (!localStorage.getItem(storageKey)) applyTheme(getSystemTheme());
- });
+ const handleSystemThemeChange = () => {
+ if (!getSavedTheme()) applyTheme(getSystemTheme());
+ };
+
+ if (typeof media.addEventListener === 'function') {
+ media.addEventListener('change', handleSystemThemeChange);
+ } else if (typeof media.addListener === 'function') {
+ media.addListener(handleSystemThemeChange);
+ }
})();
(() => {
diff --git a/src/pages/posts/[slug].astro b/src/pages/posts/[slug].astro
index e088bfc..5eddfe4 100644
--- a/src/pages/posts/[slug].astro
+++ b/src/pages/posts/[slug].astro
@@ -390,9 +390,17 @@ const relatedPosts = allPosts
toggleButton.textContent = '展开';
toggleButton.setAttribute('aria-expanded', 'false');
+ const expandCue = document.createElement('button');
+ expandCue.className = 'code-block-expand-cue';
+ expandCue.type = 'button';
+ expandCue.setAttribute('aria-label', 'Expand code block');
+ expandCue.setAttribute('aria-expanded', 'false');
+ expandCue.innerHTML = '';
+
toolbar.append(copyButton, toggleButton);
header.append(language, toolbar);
wrapper.appendChild(header);
+ wrapper.appendChild(expandCue);
const codeLines = [...code.querySelectorAll(':scope > .line')];
if (codeLines.length > 0) {
@@ -421,11 +429,15 @@ const relatedPosts = allPosts
}
});
- toggleButton.addEventListener('click', () => {
+ function toggleExpanded() {
const isExpanded = wrapper.classList.toggle('is-expanded');
toggleButton.textContent = isExpanded ? '收起' : '展开';
toggleButton.setAttribute('aria-expanded', String(isExpanded));
- });
+ expandCue.setAttribute('aria-expanded', String(isExpanded));
+ }
+
+ toggleButton.addEventListener('click', toggleExpanded);
+ expandCue.addEventListener('click', toggleExpanded);
});
})();
diff --git a/src/styles/site.css b/src/styles/site.css
index 909f449..b26cf26 100644
--- a/src/styles/site.css
+++ b/src/styles/site.css
@@ -19,14 +19,14 @@
:root[data-theme='dark'] {
color-scheme: dark;
- --bg: #0f171a;
- --surface: #162225;
- --text: #e9f0ee;
- --muted: #a7b4b1;
- --line: rgba(174, 205, 197, 0.18);
- --accent: #72ded2;
- --accent-strong: #9af0d6;
- --shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
+ --bg: #0b1014;
+ --surface: #141b21;
+ --text: #e7edf0;
+ --muted: #9ba9b1;
+ --line: rgba(166, 184, 194, 0.2);
+ --accent: #7dd3c7;
+ --accent-strong: #b7e7d4;
+ --shadow: 0 18px 52px rgba(0, 0, 0, 0.36);
}
* {
@@ -49,7 +49,7 @@ body {
:root[data-theme='dark'] body {
background:
- linear-gradient(rgba(8, 13, 16, 0.86), rgba(10, 17, 20, 0.94)),
+ linear-gradient(rgba(7, 10, 14, 0.9), rgba(8, 13, 17, 0.96)),
url('/images/background.png') center top / cover fixed;
}
@@ -563,20 +563,20 @@ a {
}
:root[data-theme='dark'] .nav {
- border-color: rgba(174, 205, 197, 0.2);
+ border-color: rgba(166, 184, 194, 0.2);
background:
- linear-gradient(135deg, rgba(29, 47, 51, 0.78), rgba(14, 23, 27, 0.54)),
- rgba(12, 19, 22, 0.62);
+ linear-gradient(135deg, rgba(25, 35, 42, 0.82), rgba(10, 15, 20, 0.66)),
+ rgba(10, 15, 20, 0.68);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.12),
inset 0 -18px 38px rgba(0, 0, 0, 0.18),
- 0 18px 54px rgba(0, 0, 0, 0.34);
+ 0 18px 54px rgba(0, 0, 0, 0.42);
}
:root[data-theme='dark'] .nav::before {
background:
- radial-gradient(circle at 18% 0%, rgba(114, 222, 210, 0.18), transparent 32%),
- linear-gradient(90deg, rgba(255, 255, 255, 0.08), transparent 42%, rgba(114, 222, 210, 0.1));
+ radial-gradient(circle at 18% 0%, rgba(125, 211, 199, 0.16), transparent 32%),
+ linear-gradient(90deg, rgba(255, 255, 255, 0.07), transparent 42%, rgba(125, 211, 199, 0.08));
}
:root[data-theme='dark'] .nav::after {
@@ -589,8 +589,8 @@ a {
:root[data-theme='dark'] .nav-console:hover,
:root[data-theme='dark'] .theme-toggle:hover,
:root[data-theme='dark'] .nav-menu-toggle:hover {
- border-color: rgba(114, 222, 210, 0.26);
- background: rgba(114, 222, 210, 0.12);
+ border-color: rgba(125, 211, 199, 0.28);
+ background: rgba(125, 211, 199, 0.11);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.12),
0 8px 18px rgba(0, 0, 0, 0.18);
@@ -601,14 +601,14 @@ a {
}
:root[data-theme='dark'] .control-console-panel {
- border-color: rgba(174, 205, 197, 0.18);
+ border-color: rgba(166, 184, 194, 0.2);
background:
- radial-gradient(circle at 14% 0%, rgba(114, 222, 210, 0.1), transparent 34%),
- linear-gradient(145deg, rgba(28, 43, 47, 0.88), rgba(12, 22, 25, 0.8)),
- rgba(17, 28, 31, 0.82);
+ radial-gradient(circle at 14% 0%, rgba(125, 211, 199, 0.08), transparent 34%),
+ linear-gradient(145deg, rgba(24, 34, 42, 0.9), rgba(10, 17, 22, 0.84)),
+ rgba(14, 22, 28, 0.84);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.1),
- 0 28px 80px rgba(0, 0, 0, 0.38);
+ 0 28px 80px rgba(0, 0, 0, 0.46);
}
:root[data-theme='dark'] .control-console-head h2,
@@ -620,8 +620,8 @@ a {
:root[data-theme='dark'] .control-site-card,
:root[data-theme='dark'] .control-console-info section,
:root[data-theme='dark'] .control-console-close {
- border-color: rgba(114, 222, 210, 0.2);
- background: rgba(114, 222, 210, 0.1);
+ border-color: rgba(125, 211, 199, 0.2);
+ background: rgba(125, 211, 199, 0.08);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
@@ -631,35 +631,35 @@ a {
}
:root[data-theme='dark'] .custom-context-menu {
- border-color: rgba(174, 205, 197, 0.2);
+ border-color: rgba(166, 184, 194, 0.2);
color: #d8e5e2;
background:
- linear-gradient(145deg, rgba(31, 48, 52, 0.88), rgba(13, 23, 26, 0.78)),
- rgba(15, 25, 28, 0.82);
+ linear-gradient(145deg, rgba(27, 39, 47, 0.9), rgba(10, 17, 22, 0.82)),
+ rgba(14, 22, 28, 0.84);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.1),
- 0 18px 42px rgba(0, 0, 0, 0.34);
+ 0 18px 42px rgba(0, 0, 0, 0.42);
}
:root[data-theme='dark'] .context-menu-nav :where(a, button) {
- border-color: rgba(174, 205, 197, 0.16);
- color: #b7c7c4;
+ border-color: rgba(166, 184, 194, 0.16);
+ color: #b7c3c8;
background: rgba(255, 255, 255, 0.06);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
:root[data-theme='dark'] .context-menu-group {
- border-top-color: rgba(174, 205, 197, 0.16);
+ border-top-color: rgba(166, 184, 194, 0.16);
}
:root[data-theme='dark'] .custom-context-menu :where(a, button):hover,
:root[data-theme='dark'] .custom-context-menu :where(a, button):focus-visible {
color: var(--accent);
- background: rgba(114, 222, 210, 0.11);
+ background: rgba(125, 211, 199, 0.1);
}
:root[data-theme='dark'] .custom-context-menu i {
- color: #a8b8b5;
+ color: #a8b5ba;
}
.hero {
@@ -2235,6 +2235,7 @@ time {
.prose .code-block.is-collapsible:not(.is-expanded)::after {
content: "";
position: absolute;
+ z-index: 1;
right: 0;
bottom: 0;
left: 0;
@@ -2243,6 +2244,52 @@ time {
pointer-events: none;
}
+.code-block-expand-cue {
+ appearance: none;
+ -webkit-appearance: none;
+ position: absolute;
+ z-index: 3;
+ right: 50%;
+ bottom: 10px;
+ display: none;
+ width: 36px;
+ height: 28px;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ border-radius: 999px;
+ padding: 0;
+ background: rgba(22, 31, 36, 0.72);
+ box-shadow: 0 8px 18px rgba(0, 0, 0, 0.18);
+ cursor: pointer;
+ transform: translateX(50%);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease,
+ transform 0.2s ease;
+}
+
+.code-block-expand-cue span {
+ width: 10px;
+ height: 10px;
+ border-right: 2px solid rgba(255, 255, 255, 0.9);
+ border-bottom: 2px solid rgba(255, 255, 255, 0.9);
+ transform: translateY(-2px) rotate(45deg);
+}
+
+.code-block-expand-cue:hover,
+.code-block-expand-cue:focus-visible {
+ border-color: rgba(255, 255, 255, 0.36);
+ background: rgba(62, 184, 190, 0.82);
+ transform: translateX(50%) translateY(-1px);
+}
+
+.prose .code-block.is-collapsible:not(.is-expanded) .code-block-expand-cue {
+ display: inline-flex;
+}
+
.prose .code-block code {
color: #e8edf2;
line-height: 1.42;
@@ -3882,11 +3929,11 @@ meting-js {
.pagination-link,
.pagination-number
) {
- border-color: rgba(174, 205, 197, 0.18);
+ border-color: rgba(166, 184, 194, 0.18);
background:
- radial-gradient(circle at 14% 0%, rgba(114, 222, 210, 0.1), transparent 34%),
- linear-gradient(145deg, rgba(28, 43, 47, 0.82), rgba(12, 22, 25, 0.72)),
- rgba(17, 28, 31, 0.74);
+ radial-gradient(circle at 14% 0%, rgba(125, 211, 199, 0.08), transparent 34%),
+ linear-gradient(145deg, rgba(24, 34, 42, 0.84), rgba(10, 17, 22, 0.76)),
+ rgba(14, 22, 28, 0.78);
box-shadow: var(--shadow);
}
@@ -3931,12 +3978,12 @@ meting-js {
}
:root[data-theme='dark'] .chart-legend-item {
- border-color: rgba(174, 205, 197, 0.16);
+ border-color: rgba(166, 184, 194, 0.16);
background: rgba(255, 255, 255, 0.05);
}
:root[data-theme='dark'] .pie-chart-track {
- stroke: rgba(174, 205, 197, 0.16);
+ stroke: rgba(166, 184, 194, 0.16);
}
:root[data-theme='dark'] .line-chart-dot {
@@ -3954,9 +4001,9 @@ meting-js {
.post-copyright-action,
.post-copyright-icon
) {
- border-color: rgba(114, 222, 210, 0.22);
+ border-color: rgba(125, 211, 199, 0.22);
color: var(--accent);
- background: rgba(114, 222, 210, 0.1);
+ background: rgba(125, 211, 199, 0.1);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
@@ -3980,30 +4027,30 @@ meting-js {
}
:root[data-theme='dark'] .article-header-cover {
- background: #0d1f21;
+ background: #0d171d;
}
:root[data-theme='dark'] .article-header-cover::before {
background:
- linear-gradient(180deg, rgba(5, 12, 14, 0.54), rgba(7, 19, 21, 0.22) 38%, rgba(3, 10, 11, 0.82)),
- linear-gradient(90deg, rgba(5, 18, 20, 0.62), rgba(12, 60, 57, 0.16) 54%, rgba(5, 18, 20, 0.44));
+ linear-gradient(180deg, rgba(5, 9, 12, 0.58), rgba(8, 16, 20, 0.28) 38%, rgba(3, 8, 11, 0.86)),
+ linear-gradient(90deg, rgba(5, 13, 17, 0.66), rgba(20, 57, 58, 0.14) 54%, rgba(5, 13, 17, 0.48));
}
:root[data-theme='dark'] .prose .code-block {
- border-color: rgba(114, 222, 210, 0.2);
- background: rgba(5, 10, 12, 0.96);
- box-shadow: 0 14px 32px rgba(0, 0, 0, 0.24);
+ border-color: rgba(125, 211, 199, 0.18);
+ background: rgba(5, 8, 12, 0.96);
+ box-shadow: 0 14px 32px rgba(0, 0, 0, 0.32);
}
:root[data-theme='dark'] .prose :not(pre) > code {
- border-color: rgba(114, 222, 210, 0.22);
+ border-color: rgba(125, 211, 199, 0.22);
color: var(--accent);
- background: rgba(114, 222, 210, 0.1);
+ background: rgba(125, 211, 199, 0.1);
}
:root[data-theme='dark'] .butterfly-note {
color: #dce9e6;
- background: rgba(114, 222, 210, 0.08);
+ background: rgba(125, 211, 199, 0.08);
}
:root[data-theme='dark'] .butterfly-note--disabled {
@@ -4015,7 +4062,7 @@ meting-js {
}
:root[data-theme='dark'] .butterfly-hide-block__content {
- border-left-color: rgba(114, 222, 210, 0.22);
+ border-left-color: rgba(125, 211, 199, 0.22);
}
:root[data-theme='dark'] .prose table {
@@ -4025,43 +4072,43 @@ meting-js {
:root[data-theme='dark'] .prose thead th {
color: #effaf7;
background:
- linear-gradient(135deg, rgba(40, 70, 70, 0.86), rgba(22, 41, 44, 0.76)),
- rgba(13, 24, 27, 0.8);
+ linear-gradient(135deg, rgba(35, 55, 61, 0.86), rgba(18, 31, 38, 0.78)),
+ rgba(12, 21, 27, 0.84);
}
:root[data-theme='dark'] .prose tbody tr:nth-child(even) td {
- background: rgba(114, 222, 210, 0.07);
+ background: rgba(125, 211, 199, 0.06);
}
:root[data-theme='dark'] .prose tbody tr:hover td {
- background: rgba(114, 222, 210, 0.12);
+ background: rgba(125, 211, 199, 0.1);
}
:root[data-theme='dark'] .prose :where(th, td) {
- border-color: rgba(114, 222, 210, 0.12);
+ border-color: rgba(125, 211, 199, 0.12);
}
:root[data-theme='dark'] .site-footer {
- color: rgba(197, 214, 211, 0.74);
+ color: rgba(199, 211, 216, 0.76);
background:
- linear-gradient(180deg, rgba(12, 20, 23, 0.68), rgba(8, 14, 17, 0.88)),
- rgba(8, 14, 17, 0.84);
+ linear-gradient(180deg, rgba(10, 16, 21, 0.7), rgba(6, 10, 14, 0.9)),
+ rgba(7, 11, 15, 0.86);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
:root[data-theme='dark'] .footer-socials a {
- border-color: rgba(174, 205, 197, 0.18);
+ border-color: rgba(166, 184, 194, 0.18);
color: rgba(218, 234, 231, 0.82);
background:
- linear-gradient(145deg, rgba(34, 51, 55, 0.78), rgba(14, 24, 27, 0.72)),
- rgba(18, 29, 32, 0.72);
+ linear-gradient(145deg, rgba(28, 40, 48, 0.8), rgba(10, 17, 22, 0.74)),
+ rgba(14, 22, 28, 0.74);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.08),
- 0 10px 24px rgba(0, 0, 0, 0.22);
+ 0 10px 24px rgba(0, 0, 0, 0.3);
}
:root[data-theme='dark'] .footer-bottom {
- border-top-color: rgba(174, 205, 197, 0.16);
+ border-top-color: rgba(166, 184, 194, 0.16);
}
@media (min-width: 960px) {