Documentation
Curated documentation of NotionKit Web: quick start, structure, conventions, sections, hero choice, branding, i18n, blog, contact form, tests and update recipe.
Quick start
NotionKit Web is a GitHub template (“Use this template”), not an npm package. The NotionKit foundation comes via npm and is never edited locally.
bashnpm create astro -- --template JUNGHERZ/NotionKit-Web my-site
cd my-site && npm install
npm run dev # http://localhost:4321/demo/ (base from astro.config.mjs)
npm run build # static build into dist/
npm test # mirror check, colour guard, Playwright
Ten steps to your own site
- Derive the template,
npm install. Deletesite/(the NotionKit Web product page) and remove the assemble step in.github/workflows/deploy.yml(marked “NotionKit-Web only”). KeepSKILL.md. astro.config.mjs: setbaseto'/<repo>'– or remove it for a custom domain, setsite, addpublic/CNAME.src/data/site.ts: name, title, description, repo URL,headerBadges, languages.- Choose the hero:
HeroProduct(app panel),HeroDocs(search + tree) orHeroEditorial(typographic) – swap the import insrc/pages/index.astro. - Replace copy per section in
src/components/(grep “Atlas”); nav entries are defined once in the header and render desktop + mobile. - Content: replace
src/content/docs/andsrc/content/blog/, areas insrc/data/docs.ts. - Replace
public/favicon.svgand runnpm run build:assets(PNG icons + og.png fromscripts/assets/og-demo.html). - Brand in
src/styles/brand.css(token overrides, commented themes) – never innotionkit.cssorsite.css. - Fill imprint/privacy with real content.
- Push to
main– GitHub Actions tests, builds and deploys (once: Settings → Pages → Source “GitHub Actions”).
Structure
textsrc/data/site.ts constants, languages, headerBadges, contactForm, href()
src/data/docs.ts docs areas and UI labels per language
src/styles/site.css web layer (.nkw-*), --nk-* colours only
src/styles/brand.css your brand (token overrides)
src/styles/generated/ no-JS dark mirror (generated, committed)
src/layouts/ BaseLayout, DocsLayout
src/components/ sections (copy inside), en/ = language branch
src/content/docs|blog/ Markdown per language
src/pages/ index, docs, blog, impressum, datenschutz, 404, rss, robots, llms + en/
scripts/ build-dark-mirror, check-no-hex, build-assets
tests/smoke.spec.ts smoke suite (CI gate)
Conventions
- Own classes carry the
.nkw-prefix and use--nk-*tokens only. No colour literals insrc/(guard:npm run check:colors).--nkw-*properties only for non-colours (font, widths). - No scoped CSS in
.astrofiles – everything global insite.css. - Theme:
data-themeon<html>, bootstrap script before first paint; without JS the generatedprefers-color-schememirror applies. - Links always via
href()fromsite.ts(base-safe, trailing slash). Bare#anchorlinks break on subpages. - Fluid layouts, exactly one breakpoint at 860px, no device mockups. Visuals are built from
nk-*components, never screenshotted. - Accessibility:
prefers-reduced-motiondisables reveal/animations, focus rings from--nk-accent, decorative demo UI isaria-hidden.
Sections
| Component | Purpose | Note |
|---|---|---|
| SiteHeader / SiteFooter | Sticky header with nav, badges, language pill, theme toggle, mobile menu; footer columns | Rendered by BaseLayout per language branch |
| HeroProduct | Headline + CTAs + app panel | Default. Panel = pure foundation markup |
| HeroDocs | Search + page tree preview | Title filter without dependency |
| HeroEditorial | Purely typographic | Gradient word via <Highlight gradient> |
| LogoCloud | Grey logo row | Slot for own SVGs, aria-hidden |
| FeatureGrid / Bento | Feature cards, bento with media slots | Media = built nk mini UIs |
| Steps | Process steps without JS | With CodeWindow per step |
| CodeWindow | Code block with copy button | Props: code, lang, title, copyLabel |
| StatsRow | Figures on nk-stats | Opt-in |
| Pricing / Testimonials / FAQ | Plans, quotes, FAQ with JSON-LD | Opt-in; FAQ = details.nk-toggle |
| Cta / Contact | Closing band, contact form | Provider via contactForm |
| Decor | Dot grid, lines, dithering | Opt-in, slot positioning |
| DocsTree / SectionNav / DocsPager | Docs navigation | Used by DocsLayout |
Choosing a hero and filling the app panel
The app panel (src/components/hero/AppPanel.astro) is a browser frame (.nkw-appframe) containing foundation classes only: sidebar with page tree, topbar with breadcrumb, page with a database as table or board. No .nk-app (100vh), no fixed height – only max-height with a fade. A smoke test asserts that the panel body has no .nkw-* classes. To fill it: change the rows array and the labels object; set view="board" for a board.
Style guide: highlight pill (default) or gradient word (opt-in) in the headline, announcement pill above, CTA group primary + ghost, optional serif type via --nkw-font-display in brand.css. Pattern level: notion.com (pill, panel, logo cloud), twenty.com (serif, browser frame, badges), chatprd.ai (announcement, gradient, stats). Copy nothing.
Branding
css:root { --nk-accent: #2f8f5b; }
[data-theme="dark"] { --nk-accent: #5cbf87; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) { --nk-accent: #5cbf87; } /* no-JS path */
}
:root { --nkw-font-display: ui-serif, Georgia, serif; } /* serif headlines */
Only in brand.css. The third block is needed because without JavaScript no data-theme is set and the generated mirror only knows the foundation values.
Docs & blog
Docs pages live under src/content/docs/<lang>/<area>/<slug>.md with frontmatter title, description, section, order, icon, updated, draft. Areas and labels are in src/data/docs.ts. Blog posts under src/content/blog/<lang>/<slug>.md (title, description, pubDate, author, icon, tags); the same slug in all languages = translation with hreflang. RSS per language (rss.xml, en/rss.xml), llms.txt is generated from the docs tree and the blog.
Removing the blog: src/content/blog/, src/pages/blog/ + en/blog/, both rss.xml.ts, src/lib/{blog,rss}.ts, the blog collection in content.config.ts, blog links in header/footer, the blog block in llms.txt.ts, the RSS link in BaseLayout, the blog tests in smoke.spec.ts.
Languages
Default language at the root, others under /<code>/ as their own branches: src/pages/<code>/, src/components/<code>/, src/content/{docs,blog}/<code>/. Configured in languages (site.ts) and the i18n block of astro.config.mjs. The language switcher is a link pill (no JS toggle); from three languages on a flag dropdown.
Removing a language: delete the three en/ folders, remove the entry from languages and astro.config.mjs, drop the EN imports in BaseLayout and the EN tests. Adding a language: copy en/, translate, extend languages/i18n/docSections/docsLabels, add the branch in BaseLayout.
Contact form
| Target | endpoint | hiddenFields |
|---|---|---|
| Demo (default) | '' | – (UI works, nothing is sent) |
| n8n webhook | https://<host>/webhook/contact | – (workflow: IF botcheck empty → target, e.g. Notion node) |
| Web3Forms | https://api.web3forms.com/submit | { access_key } |
| Formspree | https://formspree.io/f/<id> | – |
Re-check the botcheck honeypot server-side; name the recipient in the privacy policy. The endpoint is the only external connection the template allows.
Tests & deploy
npm test = mirror check + colour guard + Playwright (desktop 1280 × mobile 390): every page without console errors, theme follows the OS, toggle persists, no-JS dark fallback, docs navigation, search, copy button, mobile menu, language switch, hreflang, blog, RSS/sitemap/robots/llms, 404 DE/EN, contact form, FAQ, app-panel rule. Headless Chrome needs reducedMotion: 'reduce' because of view transitions – the suite enforces it.
Deploy: .github/workflows/deploy.yml – jobs build, test, deploy (GitHub Pages). In the template repo this product page sits at the root, the demo under /demo/.
Pulling template updates
- Foundation: bump
@jungherz-de/notionkitinpackage.json, runnpm run build:mirror, read the foundation changelog. - Template:
git remote add template https://github.com/JUNGHERZ/NotionKit-Web.git,git fetch template. - Read
git show template/main:SKILL.mdandCHANGELOG.mdfirst – the entries since the last sync are the update guide. - Take mechanics files deliberately:
site.css,site.js,scripts/,SKILL.md;BaseLayout,tests,playwright.config.tsonly after a diff. - Never overwrite:
site.ts,brand.css,components/**,content/**,public/**, legal pages. - Decide new opt-in sections one by one, then
npm testand committemplate-sync: <sha>.
AI prompt
For Claude Code, Cursor & co.: SKILL.md is the machine-readable reference. Prompt template:
textRead SKILL.md from https://github.com/JUNGHERZ/NotionKit-Web and derive a new
project for <brand> from it (recipe §5). Hero: <Product|Docs|Editorial>.
Languages: <de, en>. Blog: <yes|no>. Follow the rules in §4:
.nkw- prefix, --nk-* tokens only, no scoped CSS, href() helper, one breakpoint.