This is the Next.js website for Mellea — the landing page and developer blog deployed at mellea.ai. For the Mellea Python library itself, see the mellea repository.
Adding or editing a blog post → go to § Adding Blog Posts. No dev environment, no code changes needed.
Changing the site (UI, components, CI, dependencies) → read everything below.
npm install
npm run dev # http://localhost:4000
npm run lint # ESLint
npm run lint:md # Markdown lint (content files)
npm run typecheck # tsc --noEmit
npm run test:unit # Vitest (no browser required)
npm run test:e2e # Playwright (auto-starts dev server)
npm run build # Static export to ./out/Branches: feat/topic, fix/description, docs/topic
| Path | Contents |
|---|---|
src/app/ |
Next.js App Router pages and layouts |
src/components/ |
React components |
src/lib/ |
Server-side utilities (blog parsing, etc.) |
src/config/ |
Site-wide configuration (site.ts) |
content/blogs/ |
Markdown blog posts with YAML front matter |
public/ |
Static assets (images, CNAME) |
tests/unit/ |
Vitest unit tests |
tests/e2e/ |
Playwright E2E tests |
.github/workflows/ |
CI pipeline |
- TypeScript throughout — no
anywithout a comment explaining why - Server Components by default; add
'use client'only when needed src/lib/blogs.tsuses Node.jsfs— never import it in Client Componentsparamsin Next.js 15 page components is aPromise— alwaysawait params- No CSS modules, no Tailwind — all styles in
src/app/globals.css src/config/site.tsis the single source of truth for URLs and repo slug
Do not add AI assistant attribution to commits, PRs, code, or documentation unless explicitly asked. No Co-Authored-By lines, no AI-generated footers.
Plain descriptive messages: fix: nav link selector in E2E tests, feat: add tags filter to blog listing, docs: update CONTRIBUTING.md.
No Angular-style mandatory types required, but keep messages short and imperative.
Run the appropriate checks before every commit. CI will reject failures; fixing them after the fact wastes pipeline time.
npm run lint # must be clean
npm run typecheck # must be clean
npm run test:unit # must pass
npm run test:e2e # must passIf you rename or remove a CSS class, check tests/e2e/ for selectors that reference it and update them in the same commit.
- No new
anytypes without a comment explaining why - No hardcoded URLs — use
src/config/site.ts - External links in Markdown? CI runs lychee — broken links block deploy
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.mjsparamsin page components is aPromisein Next.js 15 — alwaysawait paramssrc/lib/blogs.tsuses Node.jsfs— Server Components only, never import in Client Components
- Build-time (Server Components):
getAllBlogs()→ landing page + blog listing;getAllBlogSlugs()+getBlog(slug)→ individual post pages - Client-side (Client Components): GitHub API stats via
useGitHubStatshook on mount; image compare slider viareact-compare-slider
- Single global CSS file:
src/app/globals.css - IBM Plex Sans + IBM Plex Mono (Google Fonts)
- Dark/light theme via CSS custom properties +
@media (prefers-color-scheme: light) - No CSS modules, no Tailwind
Pushing to main triggers .github/workflows/nextjs.yml. Pipeline: lint → (test-unit ∥ build) → test-e2e → deploy. Static artifact produced by next build with output: 'export', deployed to GitHub Pages.
Drop a .md file in content/blogs/. The filename becomes the URL slug: my-post.md → /blogs/my-post.
Required front matter:
---
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"]
---| Field | Required | Notes |
|---|---|---|
title |
Yes | Post title |
date |
Yes | YYYY-MM-DD, used for sorting |
author |
Yes | Display name |
excerpt |
Yes | Shown on cards and listing page |
tags |
No | Array of strings |
Verify with npm run build — no config changes or code edits needed.
| Problem | Fix |
|---|---|
params type error in page component |
params is Promise<{slug: string}> in Next.js 15 — use await params |
| E2E test strict mode violation | Scope selector (e.g. page.getByRole('banner').getByRole('link', ...)) |
fs import error in client bundle |
Move the import to a Server Component; never import src/lib/blogs.ts client-side |
| ESLint config error | Uses ESLint 9 flat config (eslint.config.mjs) — no legacy .eslintrc |