There are two distinct ways to contribute:
- Writing a blog post — add a Markdown file and verify it renders. Jump to that section.
- Changing the site — UI, CI, or dependencies. Requires a full dev setup. Jump to that section.
-
Fork the repository.
-
Copy
templates/blog-post.mdtocontent/blogs/your-slug.md. The filename becomes the URL slug (my-post.md→/blogs/my-post), so choose a descriptive topic name and avoid redundant prefixes likeblog-. -
Fill in the YAML front matter at the top of the file:
--- title: "Your Post Title" date: "YYYY-MM-DD" author: "Your Name" excerpt: "One sentence shown on the blog listing and cards." tags: ["tag1", "tag2", "etc"] --- Your Markdown content starts here.
-
Install dependencies and verify your post locally:
npm install npm run lint:md # Markdown style check — must pass npm run dev # preview at http://localhost:4000/blogs/your-slug
-
Open a PR against
mainon this repository. A member of the mellea-maintainers team must approve the PR before it can be merged. -
Once merged, the CI pipeline builds and deploys the site automatically — your post will be live at
mellea.ai/blogs/your-slugwithin a few minutes.
| Field | Required | Description |
|---|---|---|
title |
Yes | Post title |
date |
Yes | Publication date (YYYY-MM-DD), used for sort order |
author |
Yes | Author display name |
excerpt |
Yes | Short summary shown on cards and the listing page |
tags |
No | Array of tag strings |
Place image files in public/images/ and reference them with an absolute path from the site root:
No config changes or code edits are needed — just the Markdown file and any images.
npm install
npm run dev # http://localhost:4000For E2E tests, install the Playwright browser once:
npx playwright install chromium| Command | What it does |
|---|---|
npm run dev |
Start dev server on http://localhost:4000 |
npm run build |
Static export to ./out/ |
npm run lint |
ESLint (flat config, ESLint 9) |
npm run typecheck |
TypeScript type check (tsc --noEmit) |
npm run test:unit |
Vitest unit tests |
npm run test:e2e |
Playwright E2E tests (uses dev server locally) |
Before pushing, run:
npm run lint
npm run typecheck
npm run test:unit
npm run test:e2eThe build is also a correctness check — npm run build must succeed with no errors.
External links in Markdown files are checked in CI by lychee and will block deployment if broken. To run the same check locally (requires brew install lychee):
lychee --exclude 'localhost' --exclude '^#' content/blogs/**/*.md *.mdThis is optional before pushing — CI will catch broken links. If you need to bypass a transient failure (e.g. a flaky external server), add [skip link check] to your commit message or set SKIP_LINK_CHECK=true in the repository variables.
Tests live in tests/unit/. They run against the source directly with no browser needed.
Cover the server-side blog parsing utilities in src/lib/blogs.ts.
Tests live in tests/e2e/. Locally, they start the dev server automatically via the webServer config in playwright.config.ts (port 4000). In CI, they run against the pre-built ./out/ served on port 3000.
If a test fails, Playwright writes a trace and HTML report to playwright-report/. Open it with:
npx playwright show-reportEvery push and PR runs the following jobs in order:
lint (ESLint + tsc)
│
├── test-unit (Vitest) ← runs in parallel with build
│
└── build (next build)
│
└── test-e2e (Playwright against ./out)
│
└── deploy (GitHub Pages, main branch only)
PRs must pass lint, test-unit, build, and test-e2e before merging. Deployment only happens on push to main.
On E2E failure, a playwright-report artifact is uploaded to the GitHub Actions run for inspection.
Next.js 15 App Router, fully static (output: 'export'). Nothing runs at request time — all pages are pre-rendered at build time or handled client-side.
- No
next/headers, no route handlers, no server actions Imagecomponent usesunoptimized: true(required for static export)trailingSlash: trueis set innext.config.mjs- Blog data fetching uses Node.js
fsinside Server Components only —src/lib/blogs.tsis not safe to import in Client Components
src/config/site.ts is the single source of truth for the GitHub repo slug, site name, and external URLs.
Single global CSS file: src/app/globals.css. IBM Plex fonts via Google Fonts. Dark/light theme via CSS custom properties and @media (prefers-color-scheme: light). No CSS modules, no Tailwind.