NotionKit Web v1.0.0
DE

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

  1. Derive the template, npm install. Delete site/ (the NotionKit Web product page) and remove the assemble step in .github/workflows/deploy.yml (marked “NotionKit-Web only”). Keep SKILL.md.
  2. astro.config.mjs: set base to '/<repo>' – or remove it for a custom domain, set site, add public/CNAME.
  3. src/data/site.ts: name, title, description, repo URL, headerBadges, languages.
  4. Choose the hero: HeroProduct (app panel), HeroDocs (search + tree) or HeroEditorial (typographic) – swap the import in src/pages/index.astro.
  5. Replace copy per section in src/components/ (grep “Atlas”); nav entries are defined once in the header and render desktop + mobile.
  6. Content: replace src/content/docs/ and src/content/blog/, areas in src/data/docs.ts.
  7. Replace public/favicon.svg and run npm run build:assets (PNG icons + og.png from scripts/assets/og-demo.html).
  8. Brand in src/styles/brand.css (token overrides, commented themes) – never in notionkit.css or site.css.
  9. Fill imprint/privacy with real content.
  10. 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 in src/ (guard: npm run check:colors). --nkw-* properties only for non-colours (font, widths).
  • No scoped CSS in .astro files – everything global in site.css.
  • Theme: data-theme on <html>, bootstrap script before first paint; without JS the generated prefers-color-scheme mirror applies.
  • Links always via href() from site.ts (base-safe, trailing slash). Bare #anchor links break on subpages.
  • Fluid layouts, exactly one breakpoint at 860px, no device mockups. Visuals are built from nk-* components, never screenshotted.
  • Accessibility: prefers-reduced-motion disables reveal/animations, focus rings from --nk-accent, decorative demo UI is aria-hidden.

Sections

ComponentPurposeNote
SiteHeader / SiteFooterSticky header with nav, badges, language pill, theme toggle, mobile menu; footer columnsRendered by BaseLayout per language branch
HeroProductHeadline + CTAs + app panelDefault. Panel = pure foundation markup
HeroDocsSearch + page tree previewTitle filter without dependency
HeroEditorialPurely typographicGradient word via <Highlight gradient>
LogoCloudGrey logo rowSlot for own SVGs, aria-hidden
FeatureGrid / BentoFeature cards, bento with media slotsMedia = built nk mini UIs
StepsProcess steps without JSWith CodeWindow per step
CodeWindowCode block with copy buttonProps: code, lang, title, copyLabel
StatsRowFigures on nk-statsOpt-in
Pricing / Testimonials / FAQPlans, quotes, FAQ with JSON-LDOpt-in; FAQ = details.nk-toggle
Cta / ContactClosing band, contact formProvider via contactForm
DecorDot grid, lines, ditheringOpt-in, slot positioning
DocsTree / SectionNav / DocsPagerDocs navigationUsed 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

TargetendpointhiddenFields
Demo (default)''– (UI works, nothing is sent)
n8n webhookhttps://<host>/webhook/contact– (workflow: IF botcheck empty → target, e.g. Notion node)
Web3Formshttps://api.web3forms.com/submit{ access_key }
Formspreehttps://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

  1. Foundation: bump @jungherz-de/notionkit in package.json, run npm run build:mirror, read the foundation changelog.
  2. Template: git remote add template https://github.com/JUNGHERZ/NotionKit-Web.git, git fetch template.
  3. Read git show template/main:SKILL.md and CHANGELOG.md first – the entries since the last sync are the update guide.
  4. Take mechanics files deliberately: site.css, site.js, scripts/, SKILL.md; BaseLayout, tests, playwright.config.ts only after a diff.
  5. Never overwrite: site.ts, brand.css, components/**, content/**, public/**, legal pages.
  6. Decide new opt-in sections one by one, then npm test and commit template-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.