8.5 KiB
This is an EmDash site -- a CMS built on Astro with a full admin UI.
Commands
npx emdash dev # Start dev server (runs migrations, seeds, generates types)
npx emdash types # Regenerate TypeScript types from schema
The admin UI is at http://localhost:4321/_emdash/admin.
Key Files
| File | Purpose |
|---|---|
astro.config.mjs |
Astro config with emdash() integration, database, and storage |
src/live.config.ts |
EmDash loader registration (boilerplate -- don't modify) |
seed/seed.json |
Schema definition + demo content (collections, fields, taxonomies, menus, widgets) |
emdash-env.d.ts |
Generated types for collections (auto-regenerated on dev server start) |
src/layouts/Base.astro |
Base layout with EmDash wiring (menus, search, page contributions) |
src/pages/ |
Astro pages -- all server-rendered |
Skills
Agent skills are in .agents/skills/. Load them when working on specific tasks:
- building-emdash-site -- Querying content, rendering Portable Text, schema design, seed files, site features (menus, widgets, search, SEO, comments, bylines). Start here.
- creating-plugins -- Building EmDash plugins with hooks, storage, admin UI, API routes, and Portable Text block types.
- emdash-cli -- CLI commands for content management, seeding, type generation, and visual editing flow.
Documentation
The EmDash docs are available as an MCP server at https://docs.emdashcms.com/mcp. When you need to verify an API, hook, config option, field type, or pattern, call search_docs against the live documentation rather than relying on training-data recall. The docs reflect current behaviour; assumptions may not.
This template ships with .mcp.json, .cursor/mcp.json, and .vscode/mcp.json so Claude Code, Cursor, and VS Code auto-discover the docs server. Other tools (OpenCode, Windsurf, etc.) need a manual one-time setup -- see docs.emdashcms.com/docs-mcp.
Rules
- All content pages must be server-rendered (
output: "server"). NogetStaticPaths()for CMS content. - Image fields are objects (
{ src, alt }), not strings. Use<Image image={...} />from"emdash/ui". entry.idis the slug (for URLs).entry.data.idis the database ULID (for API calls likegetEntryTerms).- Always call
Astro.cache.set(cacheHint)on pages that query content. - Taxonomy names in queries must match the seed's
"name"field exactly (e.g.,"category"not"categories").
This Template
A blog with posts, pages, categories, tags, full-text search, and RSS. Designed for personal writing, technical writing, indie newsletters, and anything where the writing is the product. Editorial-tech aesthetic: confident sans-serif, restrained accent, real article structure with bylines and reading time.
Pages
| Page | Path | What it shows |
|---|---|---|
| Home | / |
Featured post hero (large image + excerpt), latest posts grid |
| All posts | /posts |
Article count, full post list with excerpts and tag chips |
| Post detail | /posts/[slug] |
Featured image, title, body, left meta column (authors + date), right TOC + search + categories gutter |
| Search | /search |
Full-text search UI |
| Page | /pages/[slug] |
Static page content (Portable Text) |
| Category | /category/[slug] |
Posts filtered by category |
| Tag | /tag/[slug] |
Posts filtered by tag |
| RSS | /rss.xml |
Generated feed |
Schema
postscollection:title,featured_image,content(Portable Text),excerpt(text).pagescollection:title,content(Portable Text). Used for/aboutetc.- Taxonomies:
category,tag. - Single
primarymenu (Home, About, Posts by default).
Site settings have title and tagline -- both render in the header / footer.
Visual character
Single typeface: Inter on --font-body, used for everything including headings (--font-heading defaults to the body face; tighter letter-spacing on h1/h2). JetBrains Mono on --font-mono for inline code and code blocks. Body and headings share the same family; weight and size carry the hierarchy (--font-weight-heading 600, --font-weight-display 700 for h1/page titles).
The brand colour is #0066cc (--color-brand) -- used for links, the post-card title hover, and the search input focus ring. There's also a secondary text colour (--color-text-secondary) and a --color-muted for meta info. Don't add a second accent.
The article layout is the standout feature: a three-column reading view with a left meta column (author bylines, date), centred 680px body column, and a right gutter for search, table of contents, and categories. Don't flatten that into one column on desktop -- the layout signals "this is something to read".
Customisation
Design tokens live in src/styles/tokens.css with their default values. To restyle the site, override tokens in src/styles/theme.css -- declarations there are unlayered, so they always beat the @layer base defaults. Don't edit tokens.css or Base.astro for visual changes.
Colours are defined with light-dark(<light>, <dark>), so each token carries both modes. Overriding with a plain colour changes light and dark at once; use light-dark() in the override to keep them distinct. There is no separate dark palette to maintain.
Webfonts are configured in astro.config.mjs under fonts:. To swap the body face, change the name: for the entry bound to cssVariable: "--font-body". Good alternatives: Geist, IBM Plex Sans, Söhne (if you have a licence), Public Sans. If you want a serif-bodied blog, swap to a humanist serif like Source Serif, Crimson Pro, or Lora -- but then also raise --font-size-base to 1.0625rem for readability. To give headings their own face (or use a system font) without touching the font pipeline, override --font-heading or --font-body in theme.css.
CSS variables worth knowing (see tokens.css for the full list):
--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-heading(600) /--font-weight-display(700) -- heading weights; lower them if you switch to a serif--tracking-tight/--tracking-snug/--tracking-wide/--tracking-wider-- letter-spacing tokens used across headings and meta labels--content-width(680px) -- article body column--wide-width(1200px) -- max container--gutter-width(200px) -- right sidebar (TOC) on article pages--meta-col-width(180px) -- left meta column on article pages--avatar-size-{xs,sm,md,lg}-- byline avatar sizes at different scales
What not to do
- Don't add a second accent colour or coloured section backgrounds. The page should be black, white, and one blue.
- Don't replace Inter with a display sans (Bebas, Anton, etc.). Headings rely on weight contrast, not novelty faces.
- Don't collapse the article gutter on desktop -- it's part of the reading experience.
- Don't use stock blog copy ("Welcome to my blog", "Stay tuned for more"). Write a real tagline that says what this blog is about.
- Don't seed the home page with three identical placeholder posts. If you only have one real post, show one real post.
- Don't enable comments without a plan to moderate them. The template doesn't ship a comments system by default for a reason.