diff --git a/README.md b/README.md
index 4fe7156..f89bdca 100644
--- a/README.md
+++ b/README.md
@@ -19,13 +19,15 @@
- 页面与布局:`src/pages/`、`src/layouts/`
- 通用组件:`src/components/`(如 `Img.astro` 支持外部图床)
- 内容扩展抽象:`src/lib/content/`(为未来 Typst 等格式预留)
- - 标签规范:根目录 `tags.config.yaml`(建议统一文章标签使用)
+- 标签规范:根目录 `tags.config.yaml`(建议统一文章标签使用)
+ - 全局样式:`src/styles/theme.css`, `src/styles/prose.css`(支持浅/深色与容器宽度等变量)
## 配置与类型
- `PUBLIC_MEDIA_BASE_URL`:外部图床前缀(避免图片入库)
- `PUBLIC_SITE_URL`:站点地址(可选,用于 canonical 等)
- 修改内容集合(src/content/config.ts)后,需在本机运行 `pnpm astro sync`(Node >= 18.20.8,建议 >= 20),以生成 `.astro/types.d.ts`,否则类型可能退化为 any。
+ - 主题:`PUBLIC_THEME`(可选:`light`/`dark`/`system`,默认 `system`)
## 约定
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index cc9c3fa..87ac014 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -4,25 +4,50 @@ interface Props {
}
const { title } = Astro.props;
const site = import.meta.env.PUBLIC_SITE_URL ?? '';
+const theme = import.meta.env.PUBLIC_THEME ?? 'system'; // 'light' | 'dark' | 'system'
---
-
+
{title ? `${title} | 学习博客` : '学习博客'}
+
+
-
+
-
+
+
+
-
diff --git a/src/styles/prose.css b/src/styles/prose.css
new file mode 100644
index 0000000..8118bdb
--- /dev/null
+++ b/src/styles/prose.css
@@ -0,0 +1,71 @@
+.container {
+ max-width: var(--container-max);
+ margin: 0 auto;
+ padding: var(--space-6) var(--space-4);
+}
+
+body {
+ background: var(--color-bg);
+ color: var(--color-text);
+ font-family: var(--font-sans);
+ line-height: var(--line-height);
+ font-size: var(--text-base);
+}
+
+a { color: var(--color-primary); }
+
+header.site-header {
+ display: flex;
+ gap: var(--space-4);
+ align-items: center;
+ padding: var(--space-3) var(--space-4);
+ border-bottom: 1px solid var(--color-border);
+}
+
+select.theme-select {
+ appearance: none;
+ background: var(--color-bg);
+ color: var(--color-text);
+ border: 1px solid var(--color-border);
+ border-radius: 6px;
+ padding: 6px 28px 6px 10px;
+ position: relative;
+}
+select.theme-select:focus { outline: 2px solid color-mix(in oklab, var(--color-primary) 30%, transparent) }
+.select-wrap { position: relative; }
+.select-wrap::after {
+ content: '▾';
+ position: absolute;
+ right: 10px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: var(--color-muted);
+ pointer-events: none;
+}
+
+.prose {
+ line-height: var(--line-height);
+}
+.prose h1, .prose h2, .prose h3 {
+ margin: var(--space-6) 0 var(--space-3);
+}
+.prose p { margin: var(--space-3) 0; }
+.prose ul, .prose ol { padding-left: var(--space-6); }
+.prose code {
+ font-family: var(--font-mono);
+ background: color-mix(in oklab, var(--color-border) 60%, transparent);
+ padding: 0 0.25em;
+ border-radius: 4px;
+}
+.prose pre {
+ padding: var(--space-3);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ overflow: auto;
+}
+.prose blockquote {
+ margin: var(--space-4) 0;
+ padding-left: var(--space-4);
+ border-left: 3px solid var(--color-border);
+ color: var(--color-muted);
+}
diff --git a/src/styles/theme.css b/src/styles/theme.css
new file mode 100644
index 0000000..9e47658
--- /dev/null
+++ b/src/styles/theme.css
@@ -0,0 +1,52 @@
+:root {
+ /* Colors */
+ --color-bg: #ffffff;
+ --color-text: #1f2937;
+ --color-muted: #6b7280;
+ --color-primary: #2563eb;
+ --color-border: #e5e7eb;
+
+ /* Layout */
+ --container-max: 860px;
+ --radius: 8px;
+ --space-1: 4px;
+ --space-2: 8px;
+ --space-3: 12px;
+ --space-4: 16px;
+ --space-6: 24px;
+ --space-8: 32px;
+
+ /* Typography */
+ --font-sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue, Arial, "Apple Color Emoji", "Segoe UI Emoji";
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ --text-base: 16px;
+ --line-height: 1.65;
+}
+
+/* Follow system when no override is set */
+@media (prefers-color-scheme: dark) {
+ :root {
+ --color-bg: #0b1020;
+ --color-text: #e5e7eb;
+ --color-muted: #9ca3af;
+ --color-primary: #60a5fa;
+ --color-border: #1f2937;
+ }
+}
+
+/* Explicit overrides take precedence */
+[data-theme="dark"] {
+ --color-bg: #0b1020;
+ --color-text: #e5e7eb;
+ --color-muted: #9ca3af;
+ --color-primary: #60a5fa;
+ --color-border: #1f2937;
+}
+
+[data-theme="light"] {
+ --color-bg: #ffffff;
+ --color-text: #1f2937;
+ --color-muted: #6b7280;
+ --color-primary: #2563eb;
+ --color-border: #e5e7eb;
+}