EmDash 博客模板深度指南:基于 Astro 的全栈 CMS 站点搭建与定制
CMS后端前端插件系统【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址https://gitcode.com/gh_mirrors/emdas/emdash点击查看免费下载EmDash 是一个基于 Astro 构建的全栈 TypeScript CMS被视为 WordPress 的精神继承者。本文以仓库内的 博客模板 为骨架系统讲解该模板的目录结构、开发命令、内容 Schema、页面路由、视觉体系与定制方法并结合模板源码与 seed 数据给出可运行的实战细节。读完本文你将能够独立启动、扩展并重新设计一个具备全文搜索、RSS、分类/标签、评论与多作者署名能力的 EmDash 博客站点。模板定位一个“以写作为产品”的博客博客模板 在仓库中定位为“A blog with posts, pages, categories, tags, full-text search, and RSS”即一个内置文章、页面、分类、标签、全文搜索与 RSS 订阅的完整博客目标场景是个人写作、技术写作、独立 newsletter以及任何“内容本身就是产品”的站点。它的视觉取向是“编辑/社论式美学”editorial-tech aesthetic自信的无衬线字体、克制的强调色、真实的文章结构署名与阅读时长。整个模板由三部分构成Astro 配置与集成astro.config.mjs、内容与 Schema 定义seed/seed.json、以及前端页面与样式src/。下面依次展开。快速开始两条命令与后台入口模板的日常开发只需要两条命令定义在 package.json 的 scripts 中pnpm dev # 启动 Astro 开发服务器 npx emdash types # 根据运行中的站点重新生成 TypeScript 类型pnpm dev实际执行astro dev启动 Astro 开发服务器。astro.config.mjs中devToolbar: { enabled: false }关闭了 Astro 自带的开发工具栏避免与 EmDash 管理后台冲突。npx emdash types由emdash包提供会根据当前运行中的站点读取数据库中的集合与字段重新生成 emdash-env.d.ts 类型声明。开发服务器启动时会自动触发一次类型生成因此该文件属于“自动生成物”不建议手改。管理后台地址为http://localhost:4321/_emdash/admin。登录后即可创建文章、页面、管理分类/标签与菜单。在 Base.astro 中模板通过Astro.locals.user判断登录状态登录用户会在导航栏看到 Admin 入口链接。关键文件一览模板的 AGENTS 文档用一张表概括了核心文件职责结合源码整理如下文件用途astro.config.mjsAstro 配置emdash()集成、数据库SQLite、存储本地目录、字体与插件src/live.config.tsEmDash loader 注册样板代码勿修改seed/seed.jsonSchema 定义 演示内容集合、字段、分类法、菜单、widgetemdash-env.d.ts集合类型开发服务器启动时自动重新生成src/layouts/Base.astro基础布局菜单、搜索、页面贡献page contributionssrc/pages/Astro 页面全部服务端渲染其中 src/live.config.ts 是整个模板与 EmDash 数据层之间的桥梁全文只有几行import { defineLiveCollection } from astro:content; import { emdashLoader } from emdash/runtime; export const collections { _emdash: defineLiveCollection({ loader: emdashLoader() }), };它通过 Astro Content Collections 的 live 机制暴露_emdash集合页面里再通过getEmDashCollection()/getEmDashEntry()查询具体内容类型。该文件被注释明确标注为“boilerplate -- dont modify”日常开发不应改动。Astro 配置详解集成、数据库与存储astro.config.mjs 是模板运行的枢纽值得逐段解读import node from astrojs/node; import react from astrojs/react; import auditLog from emdash-cms/plugin-audit-log; import { defineConfig, fontProviders } from astro/config; import emdash, { local } from emdash/astro; import { sqlite } from emdash/db; export default defineConfig({ output: server, adapter: node({ mode: standalone }), image: { layout: constrained, responsiveStyles: true, }, integrations: [ react(), emdash({ database: sqlite({ url: file:./data.db }), storage: local({ directory: ./uploads, baseUrl: /_emdash/api/media/file, }), plugins: [auditLog], }), ], fonts: [ /* ... */ ], devToolbar: { enabled: false }, });output: serverastrojs/nodestandalone 模式站点以 Node 服务方式运行所有 CMS 内容页面服务端渲染——这与 AGENTS 文档中“All content pages must be server-rendered”的硬性规则一致模板中没有任何getStaticPaths()。emdash({ database, storage, plugins })核心集成。database使用sqlite({ url: file:./data.db })即本地 SQLite 文件storage使用local({ directory: ./uploads, baseUrl: /_emdash/api/media/file })上传的图片落在./uploads目录通过/ _emdash/api/media/file路由对外提供访问plugins: [auditLog]挂载了审计日志插件emdash-cms/plugin-audit-log仓库内工作区包。image.layout: constrained图片组件默认使用 constrained 布局并输出响应式样式。内容模型Seed 文件即 Schemaseed/seed.json 既是 Schema 定义也是演示数据源其顶层结构为meta、settings、collections、taxonomies、bylines、menus、widgetAreas、sections、content。集合Collections模板定义了两个集合与 AGENTS 文档描述的 Schema 一一对应poststitlestring必填、可搜索、featured_imageimage、contentportableText可搜索、excerpttextsupports开启drafts、revisions、search、seo且commentsEnabled: true文章页渲染评论区。pagestitle、contentportableTextsupports为drafts、revisions、search用于/about等静态页面。值得注意supports: [search]与字段上的searchable: true共同决定了全文搜索索引范围——标题与正文被标记为可搜索这正是/search页面与头部 LiveSearch 的数据基础。分类法Taxonomiescategoryhierarchical: true层级分类作用于posts内置development、design、notes三个术语。taghierarchical: false扁平标签作用于posts内置webdev、opinion、tools、creativity四个术语。AGENTS 文档中的一条重要规则“Taxonomy names in queries must match the seedsnamefield exactly (e.g.,categorynotcategories)”即查询时分类名必须与 seed 中name字段完全一致。菜单与 Widget 区单个primary菜单默认包含 Home/、About/pages/about、Posts/posts三个custom类型条目。widgetAreas定义了两个区域sidebar文章单页右侧栏挂载core:search、core:categories、core:tags、core:recent-postscount: 5、showDate: true、core:archivestype: monthly、limit: 6五个组件 widgetfooter页脚一个content类型的“About”介绍块正文以 Portable Text 块结构_type: blockchildrenspan存储。sections预置了两个可复用内容块newsletter-signupnewsletter 订阅 CTA与about-author作者简介均为source: theme的 Portable Text 内容。演示内容与 Bylinescontent.posts内置 6 篇已发布文章与 1 篇status: draft的草稿work-in-progress不会出现在公开列表。文章通过featured_image.$media引用外部图片并携带 alt 与 filenamebylines数组把文章与作者身份关联seed 预置了emdash-editorial与isGuest: true的guest-contributor两个署名。每篇文章的taxonomies以{ category: [...], tag: [...] }形式绑定分类与标签。页面路由从首页到 RSS 的完整地图AGENTS 文档的 Pages 表格定义了全部路由模板中对应 src/pages/ 下的文件页面路径文件展示内容首页/index.astro置顶文章 hero大图 摘要 最新文章网格全部文章/postsposts/index.astro文章计数、带摘要与标签 chip 的完整列表文章详情/posts/[slug]posts/[slug].astro头图、标题、正文、左侧元信息列作者日期、右侧 TOC搜索分类栏搜索/searchsearch.astro全文搜索界面页面/pages/[slug]pages/[slug].astro静态页面内容Portable Text分类/category/[slug]category/[slug].astro按分类过滤的文章标签/tag/[slug]tag/[slug].astro按标签过滤的文章RSS/rss.xmlrss.xml.ts生成的订阅源404/404404.astro未找到页面首页的查询模式首页源码 展示了 EmDash 推荐的查询写法几个细节值得学习const POSTS_PER_PAGE 7; const [{ entries: posts, cacheHint }, settings] await Promise.all([ getEmDashCollection(posts, { orderBy: { published_at: desc }, limit: POSTS_PER_PAGE 1, // 1 用于探测是否需要 view all }), getSiteSettings(), ]); if (Astro.cache?.enabled) Astro.cache.set(cacheHint);数据库分页而非 JS 截断limit: POSTS_PER_PAGE 1多取一条用于探测溢出随后posts.slice(0, POSTS_PER_PAGE)裁剪避免“取回全部文章再丢弃”的浪费。cacheHint是查询返回的缓存提示配合规则“Always callAstro.cache.set(cacheHint)on pages that query content”页面据此设置缓存头。标签查询使用getTermsForEntries(posts, tagEntryIds, tag)一次批量获取多篇文章的标签注释明确说明这是为了避免对每篇文章单独调用getEntryTerms()造成 N1 查询。文章详情页的三栏布局posts/[slug].astro 是模板最核心的页面完整呈现了 AGENTS 文档强调的“三栏阅读视图”左栏meta-col180px作者署名头像 姓名 角色标签、发布时间、阅读时长、标签整栏 sticky 跟随滚动。中栏content680px标题、摘要、正文PortableText value{post.data.content} /以及Comments/CommentForm评论组件threaded模式集合为posts。右栏gutter200px由 JS 从正文 h2/h3 动态构建的目录TOC含IntersectionObserver高亮当前章节WidgetArea namesidebar /渲染侧边栏 widget。页面还演示了 EmDash 的 SEO 与编辑能力getSeoMeta(post, {...})生成 title/OG/robots/canonicalpost.edit.featured_image、post.edit.title、post.edit.excerpt展开为可视化编辑的标记属性Astro.params.slug需经decodeSlug()解码。关于 id 的两条关键规则AGENTS 文档的 Rules 中有两条极易踩坑的 id 约定源码中反复印证entry.id是 slug用于 URL如/posts/${post.id}entry.data.id是数据库 ULID用于 API 调用如getTermsForEntries传入的post.data.id。首页与详情页的代码严格遵循这一区分链接拼接用post.id批量查询标签用post.data.id。基础布局菜单、搜索与主题切换Base.astro 是所有页面的外壳其数据获取集中展示了 EmDash 的公共 APIconst { siteTitle, siteTagline, siteLogo } resolveBlogSiteIdentity(await getSiteSettings()); const menu await getMenu(primary); const socialMenu await getMenu(social); const { entries: pages } await getEmDashCollection(pages);getSiteSettings()读取站点设置seed 中settings.title/settings.tagline两者都会渲染在页头与页脚。getMenu(primary)渲染主导航模板还预留了可选的social菜单——若 seed 定义了 social 菜单页脚“Connect”列会展示其条目否则只保留 RSS 链接。createPublicPageContext({...})构建“公共页面上下文”交给EmDashHead安全渲染 SEO 元数据、EmDashBodyStart/EmDashBodyEnd完成插件页面贡献page contributions。导航栏内嵌LiveSearch collections{[posts, pages]} /实时搜索组件并注册了 ⌘K / CtrlK 聚焦搜索框的快捷键。页脚内置三态主题切换器light / dark / system通过themecookie 持久化内联脚本在head中提前读取 cookie 以“防闪烁”无 cookie 时color-scheme: light dark跟随操作系统。视觉体系字体、颜色与版式约束AGENTS 文档的 “Visual character” 一节定义了模板的视觉底线其具体实现落在 tokens.css字体正文与标题共用Inter--font-body字重 400/500/600/700--font-heading默认指向 body 字体标题层级靠字重与字距--font-weight-heading600、--font-weight-display700区分JetBrains Mono--font-mono字重 400/500用于行内代码与代码块。两种字体都在 astro.config.mjs 的fonts:数组中通过fontProviders.google()配置加载。品牌色#0066cc--color-brand用于链接、文章卡片标题 hover、搜索框聚焦环配套--color-brand-hover、--color-on-brand、--color-brand-ring。文档明确要求“Dont add a second accent”——页面就是黑、白、一种蓝。三栏文章布局--content-width680px正文列、--wide-width1200px最大容器、--gutter-width200px右侧 TOC 栏、--meta-col-width180px左侧元信息列。文档强调桌面端绝不压平为单栏“这个布局在传达‘这是值得阅读的内容’”。颜色双模式所有颜色以light-dark(light, dark)定义一份 token 同时携带明暗两套值tokens.css还为不支持light-dark()的旧浏览器Safari 17.5、Chrome 123提供了supports not回退。定制指南tokens.css 与 theme.css 的分工模板的定制哲学是“两层 token”tokens.css承载全部设计 token 默认值位于layer base层属于“勿编辑”文件theme.css是唯一的定制入口其中声明“unlayered”无层因此无论源码顺序如何theme.css 的声明永远压过layer base的默认值无需提升特异性。theme.css开头的注释给出了覆盖示例:root { --color-brand: light-dark(#0f766e, #2dd4bf); --font-heading: Iowan Old Style, Georgia, serif; --radius: 8px; }关键规则用纯色覆盖会同时改变明暗两模式用light-dark(light, dark)覆盖才能保持两模式独立。换字体则分两步——更换加载的 webfont 改astro.config.mjs的fonts:条目绑定到cssVariable: --font-body的那一项仅想用系统字体或给标题单独配字体则在theme.css覆盖--font-body/--font-heading。文档推荐的字体备选包括 Geist、IBM Plex Sans、Public Sans无衬线以及 Source Serif、Crimson Pro、Lora衬线——若切换为衬线正文应把--font-size-base提到1.0625rem保证可读性。常用 token 速查tokens.css中的核心变量完整清单见文件颜色--color-brand、--color-brand-hover、--color-on-brand、--color-brand-ring--color-bg、--color-bg-subtle、--color-surface、--color-text、--color-text-secondary、--color-muted、--color-border、--color-border-subtle字体--font-body、--font-heading、--font-mono字重--font-weight-heading600/--font-weight-display700——切换衬线字体时可考虑调低字距--tracking-tight/--tracking-snug/--tracking-wide/--tracking-wider布局--content-width680px、--wide-width1200px、--gutter-width200px、--meta-col-width180px、--nav-height64px头像--avatar-size-{xs,sm,md,lg}18/20/24/32pxAgent 技能与文档生态模板为 AI 开发代理预置了三份技能Skills位于仓库根目录的skills/下AGENTS 文档建议“在从事具体任务时加载”building-emdash-site内容查询、Portable Text 渲染、Schema 设计、seed 文件、站点特性菜单、widget、搜索、SEO、评论、署名——从它开始。creating-plugins用 hooks、存储、管理 UI、API 路由与 Portable Text 块类型构建 EmDash 插件。emdash-cli内容管理、seeding、类型生成与可视化编辑流程的 CLI 命令。此外模板随附.mcp.json、.cursor/mcp.json与.vscode/mcp.json让 Claude Code、Cursor、VS Code 自动发现 EmDash 的文档 MCP 服务器其他工具OpenCode、Windsurf 等需要一次性手动配置。底线清单模板的“不要做什么”AGENTS 文档以 “What not to do” 收尾这些约束共同保护模板的编辑美学与可用性不要引入第二种强调色或彩色区块背景——页面保持黑、白、一种蓝。不要用展示型无衬线字体Bebas、Anton 等替换 Inter——标题依赖字重对比而非新奇字型。桌面端不要折叠文章侧栏——它是阅读体验的一部分。不要使用套话博客文案“Welcome to my blog”“Stay tuned for more”——写一个真正说明这个博客主题的 tagline。不要在首页 seed 三篇雷同的占位文章——只有一篇真文章就展示一篇真文章seed 中的 6 篇文章即示范了差异化内容。没有配套审核计划就不要开启评论——模板默认不内置评论系统是有原因的尽管 posts 集合已将commentsEnabled置为 true 并在详情页渲染评论区。小结EmDash 博客模板是理解整个 EmDash CMS 工作方式的最佳起点它以 seed 文件 声明 Schema 与内容以 astro.config.mjs 完成数据库、存储、插件与字体装配以 Base.astro 统一菜单、搜索、SEO 与主题切换并以 tokens.css theme.css 实现“默认克制、覆盖自由”的定制模型。围绕它skills/下的三份技能文档与随附的 MCP 文档服务器构成了完整的开发与 AI 辅助工作流让你在保持编辑级阅读体验的同时快速产出真正属于自己的写作站点。赞分享CMS后端前端插件系统【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址https://gitcode.com/gh_mirrors/emdas/emdash点击查看免费下载相关推荐EmDash 博客模板Cloudflare 版实战指南基于 Astro D1 R2 的编辑器优先 CMS 站点搭建EmDash 博客模板Cloudflare 版实战指南基于 Astro D1 R2 的编辑器优先 CMS 站点搭建 本指南围绕 EmDash 官方CMS后端前端插件系统在 Graph 日志中检测 Entra 攻击工具基于 Sentinel 的 ROADtools / AADInternals / AzureHound 猎捕实战在 Graph 日志中检测 Entra 攻击工具基于 Sentinel 的 ROADtools / AADInternals / AzureHound 猎捕实CMS后端前端插件系统EmDash Blank 模板实战基于 Astro 从零搭建无预设 CMS 站点的最小基底EmDash Blank 模板实战基于 Astro 从零搭建无预设 CMS 站点的最小基底 导读 templates/blank 是 EmDash一个构建在CMS后端前端插件系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考