<p align="center">
  <img src="https://img.shields.io/badge/version-1.19.1-2383e2?style=flat-square" alt="Version">
  <img src="https://img.shields.io/badge/CSS-only-37352f?style=flat-square" alt="CSS only">
  <img src="https://img.shields.io/badge/components-~140-448361?style=flat-square" alt="Components">
  <img src="https://img.shields.io/badge/gzip-15.1%20KB-d9730d?style=flat-square" alt="Size">
  <img src="https://img.shields.io/badge/license-MIT-lightgrey?style=flat-square" alt="License">
  <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.19.1-9065b0?style=flat-square" alt="Changelog"></a>
  <a href="https://www.npmjs.com/package/@jungherz-de/notionkit"><img src="https://img.shields.io/npm/v/@jungherz-de/notionkit?style=flat-square&color=cb3837&label=npm" alt="npm"></a>
  <a href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit/"><img src="https://img.shields.io/badge/CDN-jsDelivr-blue?style=flat-square" alt="jsDelivr"></a>
</p>

<h1 align="center">🗂️ NotionKit</h1>

<p align="center">
  <a href="https://notionkit.jungherz.com"><img src="https://raw.githubusercontent.com/JUNGHERZ/NotionKit/main/og.png" alt="NotionKit preview" width="720"></a>
</p>

<p align="center">
  <strong>The calm workspace look, as a CSS design system.</strong><br>
  ~140 Components · Light &amp; Dark Mode · Design Tokens · No Dependencies · No Build Step
</p>

<p align="center">
  <a href="#-installation">Installation</a> ·
  <a href="#-quick-start">Quick Start</a> ·
  <a href="#-components">Components</a> ·
  <a href="#-theming">Theming</a> ·
  <a href="#-editor-integration">Editor</a> ·
  <a href="#-web-components--shadow-dom">Web Components</a> ·
  <a href="#-documentation">Docs</a> ·
  <a href="CHANGELOG.md">Changelog</a> ·
  <a href="#-license">License</a>
</p>

<p align="center">
  <sub>Part of the NotionKit family:
  <a href="https://notionkit-elements.jungherz.com">NotionKit Elements</a> (web components for app UIs) ·
  <a href="https://notionkit-web.jungherz.com">NotionKit Web</a> (Astro website template)</sub>
</p>

---

## ✨ What is NotionKit?

NotionKit is a **pure CSS component library** in the idiom of Notion: the quiet, document-centric surface – sidebar and page tree, document shell, callouts and todos, database tables and boards, settings, command palette, comment threads and AI conversations – as a ready-made, drop-in design system.

**One CSS file. No build tools. No JavaScript. No framework lock-in.**

There is no established UI framework that ships the Notion look *as a system*. What exists are editors (BlockNote, Novel), generic component kits (shadcn/ui) or full clones. NotionKit fills the middle: an opinionated design system with a clear optical signature that lets you build your own product in that ecosystem – familiar feel, own application.

NotionKit is the **CSS foundation** of a three-layer family: [NotionKit Elements](https://notionkit-elements.jungherz.com) wraps this markup in 88 vanilla web components for application UIs, and [NotionKit Web](https://notionkit-web.jungherz.com) is the official Astro template for complete websites on the same foundation – three layers, one design language.

<br>

### Why NotionKit?

- 🎨 **Pure CSS** – works with plain HTML, Flask, Rails, Laravel, Astro, React, anything that emits class names
- 🌗 **Light & dark, complete** – both themes specified down to tag colours and shadows; switch with one attribute on `<html>`
- 🎛️ **Design tokens** – every value is a `--nk-*` custom property; re-branding is one declaration (`--nk-accent`), everything else is mixed from it with `color-mix()`
- 📐 **Desktop and mobile equally** – fluid layouts, exactly one breakpoint (860px), horizontal scrolling where tables and boards need it; no device mockups, no fixed viewport widths
- 🧩 **Shadow DOM ready** – ships a constructable stylesheet split into tokens and components, plus `::slotted()` twins, so web components adopt it without breaking branding
- ✍️ **Editor adapter** – no editor of its own; a themed adapter layer for TipTap, BlockNote and Novel inside `.nk-block-host`
- 🤖 **AI-ready** – ships with [`SKILL.md`](SKILL.md), a structured reference for LLMs and coding agents, including eight complete app skeletons
- 🪶 **Lightweight** – 131 KB raw / 83 KB minified / 15.1 KB gzipped, no dependencies. The budget is 18 KB gzipped – up to 1.9.0 it was 14 KB, what a new connection delivers in its first round trip; CI keeps to it
- 🎯 **Prefixed naming** – `nk-*` classes, `--nk-*` tokens, no unprefixed global rules

---

## 📥 Installation

### CDN (recommended for quick start)

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1/notionkit.min.css">
```

Pin a version for production:

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
```

### npm / yarn / pnpm

```bash
# npm
npm install @jungherz-de/notionkit

# yarn
yarn add @jungherz-de/notionkit

# pnpm
pnpm add @jungherz-de/notionkit
```

```js
import '@jungherz-de/notionkit/notionkit.css';
```

### Direct download

Grab `notionkit.css` or `notionkit.min.css` from the [latest release](https://github.com/JUNGHERZ/NotionKit/releases) and link it locally.

---

## 🚀 Quick Start

### 1. Set the theme

```html
<html lang="en" data-theme="light">   <!-- light is the default; the attribute may be omitted -->
<html lang="en" data-theme="dark">
```

```js
document.documentElement.setAttribute('data-theme', 'dark');
```

### 2. Start building

```html
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1/notionkit.min.css">
</head>
<body class="nk-body">
  <div class="nk-app">
    <aside class="nk-sidebar">
      <div class="nk-workspace"><div class="avatar">A</div>Acme Inc<span class="chev">⌄</span></div>
      <div class="nk-sidebar-scroll">
        <div class="nk-tree-item active"><span class="icon">🚀</span><span class="label">Roadmap</span></div>
        <div class="nk-tree-item"><span class="icon">📚</span><span class="label">Knowledge base</span></div>
      </div>
    </aside>
    <main class="nk-main">
      <header class="nk-topbar">
        <div class="nk-breadcrumb"><span class="crumb current">🚀 Roadmap</span></div>
      </header>
      <div class="nk-page-scroll">
        <div class="nk-page">
          <h1 class="nk-page-title">Roadmap</h1>
          <div class="nk-callout"><span class="c-icon">💡</span><div>One stylesheet, no build step.</div></div>
          <label class="nk-todo"><input type="checkbox" checked><span>Ship v1.0</span></label>
        </div>
      </div>
    </main>
  </div>
</body>
</html>
```

`class="nk-body"` is opt-in: it sets font, size, background and text colour on `<body>`. Without it NotionKit touches nothing outside its own classes.

---

## 📦 Components

About 140 component classes across eleven groups. Every one of them is documented with a live preview, copy-paste markup and its behaviour on a small screen.

### App shell & navigation
| Class | Description |
|---|---|
| `nk-app`, `nk-sidebar`, `nk-main` | Full-height app shell: sidebar left, main column right |
| `nk-sidebar` (`open`), `nk-sidebar-backdrop`, `nk-sidebar-toggle` | On phones the sidebar as a drawer behind the ☰, sliding in, CSS only |
| `nk-sidebar-head`, `nk-sidebar-collapse`, `nk-sidebar` / `nk-sidebar-toggle` (`collapsed`) | On the desktop Notion's « collapses the sidebar, the ☰ brings it back |
| `nk-workspace`, `nk-sidebar-scroll`, `nk-sidebar-footer` | Workspace switcher, scrolling tree area, pinned footer |
| `nk-topbar`, `nk-topbar-actions`, `nk-topbar-btn`, `nk-share-btn`, `nk-theme-toggle` | 45px top bar with actions |
| `nk-breadcrumb` (`crumb`, `sep`, `current`) | Page trail |
| `nk-tab-bar`, `nk-tab-bar-item` (`icon`, `label`, `active`, `always`, `fixed`, `floating`), `nk-tab-bar-spacer` | Bottom tab bar for phones and installed PWAs, visible below 860px |
| `nk-section-label` | Small uppercase caption between sidebar groups |
| `nk-tree-item` (`icon`, `label`, `actions`, `active`) | Page-tree row with hover actions |
| `nk-tree-children` (`collapsed`), `nk-toggle-arrow` (`open`) | Nested subtree and its arrow |
| `nk-kbd-hint`, `nk-kbd` | Keyboard shortcut caps |

### Page & content
| Class | Description |
|---|---|
| `nk-page-scroll`, `nk-cover`, `nk-page`, `nk-page-icon`, `nk-page-title`, `nk-page-meta` | Document shell: 760px column with auto margins; an `<img>` in the cover is cropped to fill it, the band as tall as `--nk-cover-height` |
| `nk-page` (`full`, `small`) | Notion's page options: full width – capped by `--nk-page-full-max` if you like – and small text (14px), per page |
| `nk-props` (`flush`), `nk-prop` (`p-name`, `p-icon`, `p-value`, `text`) | Page properties under the title, name beside value – under it in a column narrower than 380px |
| `nk-heading`, `p.lead` | Section heading, intro paragraph |
| `nk-callout` (`c-icon`) | Tinted block with icon |
| `nk-bookmark` (`bm-text`, `bm-title`, `bm-desc`, `bm-url`, `bm-favicon`, `bm-cover`) | Link block: title, description, address, preview image |
| `nk-todo` | Checkbox with custom mark and strike-through |
| `nk-toggle` (`toggle-body`) | `<details>`-based collapsible block |
| `nk-divider`, `nk-quote` (`q-cite`) | Horizontal rule, block quote |
| `nk-mention` (`person`, `page`, `date`) | Inline mentions |
| `nk-inline-code`, `nk-code` (`lang`, `tag`, `attr`) | Inline and block code |
| `nk-prose` | Rendered Markdown and saved editor HTML – the editor adapter's rules, one class on the container |

### Database views
| Class | Description |
|---|---|
| `nk-database`, `nk-db-tabs`, `nk-db-tab` (`active`, `badge`, `add`) | View tabs with counts |
| `nk-db-toolbar` (`tools`), `nk-db-tool` (`active`) | Tabs left, Filter / Sort / search / New right |
| `nk-filter-row`, `nk-filter-pill` (`active`, `add`, `fp-remove`) | The filters in effect as pills |
| `nk-table-wrap`, `nk-table` (`th-icon`, `row-title`, `date-cell`, `person-cell`, `num`, `row-actions`, `th.actions`, `td-text`, `td-desc`), `nk-new-row` | Horizontally scrolling table with add row, right-aligned numbers, buttons per row and text with a tone and a second line |
| `nk-list`, `nk-list-item` (`l-icon`, `l-title`, `l-meta`) | List view: one line per row |
| `nk-tag` (nine colours) | Notion's select options, tuned per theme |
| `nk-progress` (`wide`), `nk-progress-label` | Progress bar in a cell or across its row |
| `nk-board` (`active`), `nk-board-col`, `nk-board-col-header`, `nk-card` | Board view with scrolling columns |
| `nk-calendar-view` (`weeks`, `cv-head`, `cv-title`, `cv-grid`, `cv-wd`, `cv-week`, `cv-day`, `cv-num`, `cv-item`) | Calendar view: a month, the rows as cards on their dates |
| `nk-gallery` (`small`, `large`, `fit`) with `nk-card` and `nk-cover` | Gallery view: the rows as cards with a picture on top, in three sizes |

### Forms & settings
| Class | Description |
|---|---|
| `nk-input`, `nk-textarea`, `nk-select` (`wide`) | Text controls with a token-derived focus ring |
| `nk-btn` (`primary`, `secondary`, `danger`, `danger-solid`, `small`) | Buttons |
| `nk-switch`, `nk-switch-label` (`aria-checked` / `:checked`) | iOS-style switch |
| `nk-check` | Checkbox and radio |
| `nk-slider`, `nk-slider-value` | Native range input |
| `nk-field` (`f-label`, `f-desc`, `f-control`, `stacked`, `compact`), `nk-fields` | Settings row, stacked field, field grid |
| `nk-profile-row` (`big-avatar`, `square`, `pr-actions`, `pr-remove`) | Avatar or workspace icon with its actions; an `<img>` in it is a chosen picture |
| `nk-copy-field` (`cf-value`, `cf-btn`, `copied`, `mono`, `wrap`, `wide`) | A value to copy – link, address, key – with its actions inside |
| `nk-calendar` (`weeks`, `cal-head`, `cal-nav`, `cal-grid`, `cal-week`, `cal-day`, `cal-marks`, `cal-foot`) | Date picker: a day or a range, calendar weeks, days not worked, marks |
| `nk-model-card` (`selected`), `nk-danger-zone`, `nk-member-list`, `nk-member-row` | Settings building blocks |
| `nk-modal-backdrop` (`open`), `nk-modal`, `nk-settings-nav`, `nk-settings-user`, `nk-settings-content`, `nk-settings-pane` (`active`) | Settings modal |

### Overlays, productivity, collaboration
| Class | Description |
|---|---|
| `nk-pop`, `nk-menu`, `nk-menu-item` (`m-icon`, `m-shortcut`, `danger`), `nk-menu-sep`, `nk-menu-label` | Popover and context menu, a switch in a row sits on the right |
| `nk-pop` (`floating`, `open`, `sheet`) | A menu that fades in like the palette and becomes a bottom sheet on a phone |
| `nk-sheet-backdrop` (`open`), `nk-sheet` (`sh-grabber`, `sh-title`) | Bottom sheet, the phone's twin of the modal |
| `nk-peek-backdrop` (`open`), `nk-peek` (`pk-resize`, `pk-bar`, `pk-body`), `nk-app.peek-inset` | Side peek: a row beside the table, resizable, optionally insetting the page; a bottom sheet on a phone |
| `nk-dialog-backdrop` (`open`), `nk-dialog` (`wide`, `dl-title`, `dl-body`, `dl-actions`) | Dialog for a question or a short form; a bottom sheet on a phone |
| `nk-tooltip` (`open`, `tt-key`) | Hover hint with an optional shortcut |
| `nk-emoji-search`, `nk-emoji-grid`, `nk-emoji-cats` | Emoji picker |
| `nk-cmdk-backdrop` (`open`), `nk-cmdk`, `nk-cmdk-input-row`, `nk-cmdk-list`, `nk-cmdk-group`, `nk-cmdk-item` (`selected`), `nk-cmdk-empty`, `nk-cmdk-footer` | Command palette |
| `nk-toast` (`show`) | Toast |
| `nk-gallery-grid`, `nk-g-item`, `nk-tabs` (`scroll`), `nk-tab`, `nk-tab-panel`, `nk-template-btn` | Gallery grid, tabs – many in a row that scrolls – template buttons |
| `nk-panels` (`flush`), `nk-panel` (`p-head`, `p-end`) | Neutral surfaces in a grid; with cover and page icon a page tile, a tag or button beside the title |
| `nk-steps` (`horizontal`), `nk-step` (`st-mark`, `st-label`, `st-desc`, `done`, `skipped`, `current`) | Calm steps through a short flow, vertical or in a row, states in any order |
| `nk-avatar` (`small`, `large`, `xlarge`, `square`, nine colours) | Initials, emoji or photo in a circle |
| `nk-stats`, `nk-stat` (`up`, `down`), `nk-synced`, `nk-segmented` (`scroll`, `wrap`), `nk-banner` (`info`, `success`, `warning`), `nk-avatar-group`, `nk-skeleton`, `nk-empty` | Productivity blocks |
| `nk-comments`, `nk-comment`, `nk-comment-input` | Comment thread |
| `nk-ai-thread`, `nk-ai-msg` (`user`, `bubble`), `nk-ai-actions`, `nk-ai-input-row`, `nk-ai-send` | AI conversation, your own question as a grey bubble |

### Editor adapter
| Class | Description |
|---|---|
| `nk-block-host` (`nk-block-actions`, `nk-block-handle`, `nk-drop-target`) | Optical shell an editor is mounted into |
| `nk-slash-menu`, `nk-slash-item`, `nk-bubble-menu` | Slash menu and bubble toolbar in the popover look |
| *(scoped inside the host)* `.ProseMirror`, `.bn-*`, `.novel-*` | Themed overrides for TipTap, BlockNote, Novel; `.ProseMirror` shares every rule with `.nk-prose` |

---

## 🌗 Theming

### Dark / light mode

The theme is one attribute on `<html>`. The token blocks also set `color-scheme`, so browser-drawn UI – date pickers, selects, scrollbars, autofill – follows without extra work.

```html
<html data-theme="dark">
```

### Custom brand colours

Re-branding is one declaration. Focus rings, checked states, the selected model card, the primary button and the danger hover are all `color-mix()`ed from tokens:

```css
:root {
  --nk-accent: #16a34a;
}
```

Declare on `:root`, not on a subtree. [`theme-override.css`](theme-override.css) ships three example palettes (Forest, Slate, Sunset), a high-contrast block and templates for metrics and typography. The 🎨 button on the demo pages applies them live.

### Contrast

Body text clears WCAG AA in both themes (14.0:1 light, 11.8:1 dark), so do sidebar text (6.1:1), the danger colour and all nine tag pairs (≥ 10:1 in light mode). Secondary text (4.27:1), tertiary text and white on the accent keep Notion's own 2025 values and sit below 4.5:1. The full measured table is in the docs; the high-contrast block in `theme-override.css` lifts those pairs with the rest of the design untouched.

---

## 🎛️ Design Tokens

All visual values are custom properties on `:root` (light) and `[data-theme="dark"]`. A selection – the full table, read straight out of the stylesheet, is in the docs and in `SKILL.md`:

| Token | Light | Dark |
|---|---|---|
| `--nk-bg` | `#ffffff` | `#191919` |
| `--nk-bg-sidebar` | `#f9f8f7` | `#202020` |
| `--nk-bg-hover` | `rgba(55,53,47,0.06)` | `rgba(255,255,255,0.055)` |
| `--nk-bg-input` | `rgba(242,241,238,0.6)` | `rgba(255,255,255,0.055)` |
| `--nk-text` | `#2c2c2b` | `rgba(255,255,255,0.81)` |
| `--nk-text-secondary` | `#7d7a75` | `rgba(255,255,255,0.46)` |
| `--nk-text-sidebar` | `#5f5e5b` | `rgba(255,255,255,0.62)` |
| `--nk-border` | `rgba(28,19,1,0.11)` | `rgba(255,255,255,0.094)` |
| `--nk-accent` | `#2383e2` | `#529CCA` |
| `--nk-danger` | `#cd3c3a` | `#df5452` |
| `--nk-tag-blue-bg` / `-text` | `#d3e5ef` / `#183347` | `#28456c` / `rgba(255,255,255,0.85)` |
| `--nk-tint-blue` / `--nk-color-blue` | `#e7f3f8` / `#337ea9` | `#1f282d` / `#5e87c9` |
| `--nk-sidebar-width` | `260px` | – |
| `--nk-radius` | `6px` | – |
| `--nk-font` | system stack | – |

One property is no token: `--nk-block-space`. Unset, every block keeps the outer margin it has for the flow of a page. A column that spaces its children with a `gap` of its own sets `--nk-block-space: 0` once, and every callout, quote, code block, divider, database, panel grid and property list inside drops its margin – `.flush` does the same for one block. Headings keep theirs.

---

## ✍️ Editor Integration

NotionKit ships **no editor** – a block editor is its own product, and half an editor would damage the library in real apps. It ships `.nk-block-host`, the optical shell, plus a themed adapter layer scoped inside it. The contract is one class on the host:

```html
<div class="nk-block-host" id="editor"></div>

<script type="module">
  import { Editor } from 'https://esm.sh/@tiptap/core@2.27.3';
  import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.27.3';
  new Editor({ element: document.getElementById('editor'), extensions: [StarterKit], content: '<p>Hello NotionKit.</p>' });
</script>
```

TipTap/ProseMirror is the vanilla-JS path; BlockNote (React) and Novel (Next.js) are covered by the same adapter. What the editor saves shows read-only in `<div class="nk-prose">`: prose and editor read the same rules, so a page looks the same in both – the demo's “Edit · Read” switch proves it to the pixel. The full recipe with slash menu, bubble toolbar and a Notion-style ＋ / ⠿ block handle with drag & drop is [`docs-editor.js`](docs-editor.js) – about 300 lines of plain JavaScript that only put NotionKit classes on their DOM. See it running in the [docs](https://notionkit.jungherz.com/docs.html#editor) and the [demo](https://notionkit.jungherz.com/app.html).

---

## 🧩 Web Components / Shadow DOM

### The problem

A shadow root that adopts the *whole* stylesheet also adopts the `:root` / `[data-theme]` token blocks. Inside that root the selectors match the element's own theme wrapper, every token is re-declared locally – and a matching rule always beats an inherited value. A consumer's own `:root { --nk-accent: … }` never arrives.

### The solution: `notionkit-styles.js`

The build splits the stylesheet at the two token blocks and ships both halves as constructable stylesheets:

```js
import { componentsSheet, tokensCss } from '@jungherz-de/notionkit/notionkit-styles.js';

// Tokens once, on the document, inside a layer – a brand stylesheet wins regardless of load order.
const tokens = new CSSStyleSheet();
tokens.replaceSync(`@layer notionkit-defaults { ${tokensCss} }`);
document.adoptedStyleSheets = [...document.adoptedStyleSheets, tokens];

class NkCallout extends HTMLElement {
  constructor() {
    super();
    const root = this.attachShadow({ mode: 'open' });
    root.adoptedStyleSheets = [componentsSheet];   // components only
    root.innerHTML = `<div class="nk-callout"><slot name="icon"></slot><div><slot></slot></div></div>`;
  }
}
customElements.define('nk-callout', NkCallout);
```

### Exports

| Export | Content |
|---|---|
| `css` / `nkSheet` | The whole stylesheet |
| `tokensCss` / `tokensSheet` | Only the `:root` and `[data-theme="dark"]` blocks |
| `componentsCss` / `componentsSheet` | Everything else |

### `::slotted()` twins

Content passed in from outside stays in the light DOM, so `.nk-callout .c-icon` cannot reach it. Every such rule has a twin next to it – `.nk-callout ::slotted(.c-icon)` – 92 in total, including a `[slot="…"]` twin for every part NotionKit Elements exposes as a named slot. Two limits: `::slotted()` matches only the assigned node (pass icons directly, never wrapped), and for slotted content the outer document's rules win.

A working proof of concept ships as [`elements-poc.html`](elements-poc.html) / [`nk-callout.js`](nk-callout.js): it measures live that class and element render identically, re-theme together and take the page's branding.

---

## 📁 Project Structure

```
notionkit/
├── notionkit.css            # Source of truth: token block + component block
├── notionkit.min.css        # Generated by npm run build (gitignored)
├── notionkit.min.css.map    # Its source map, published with it (gitignored)
├── notionkit-styles.js      # Generated: constructable stylesheet + split exports (gitignored)
├── build-styles-js.mjs      # Build + split verification
├── theme-override.css       # Theme template: 3 palettes, high-contrast block
├── SKILL.md                 # AI/LLM reference, generated from the same sources as the docs
├── index.html               # Landing page              (de/index.html)
├── app.html                 # Demo app with live editor (de/app.html)
├── showcase.html            # Every component, isolated (de/showcase.html)
├── docs.html                # Documentation            (de/docs.html)
├── docs-editor.js           # TipTap recipe used by docs and demo
├── covers/                  # Cover pictures of the demo's start view
├── elements-poc.html        # Web-components proof of concept
├── nk-callout.js            #   … its two elements
├── tools/                   # Page/SKILL generators, catalog, contrast helper
├── test/                    # Playwright regression tests for the stylesheet
└── .github/workflows/       # verify-build · release · pages
```

### Local development

```bash
npm install
npm run build            # minify, split, pages, SKILL.md
npm run check:coverage   # every class documented?
npm test                 # regression tests (once: npx playwright install chromium)
npx http-server -p 8080 -c-1
```

Serve the folder rather than opening files directly: the elements proof of concept imports an ES module, which browsers block on `file://`. The `-c-1` flag disables caching – without it the demo iframes on the landing page may show a stale `app.html` for an hour.

---

## 📖 Documentation

- **[Landing page](https://notionkit.jungherz.com)** – the library as a full-surface app preview, desktop and mobile side by side
- **[Documentation](https://notionkit.jungherz.com/docs.html)** – every component with live preview, markup, classes, mobile behaviour; tokens, states, contrast, editor and web-component integration
- **[Showcase](https://notionkit.jungherz.com/showcase.html)** – every component isolated, both themes
- **[Demo](https://notionkit.jungherz.com/app.html)** – a realistic workspace app with a live TipTap editor
- German versions under [`/de/`](https://notionkit.jungherz.com/de/)

---

## 🧩 The NotionKit Family

NotionKit is the CSS foundation of a three-layer family. The layers share one design language and one naming stem: `.nk-callout` becomes `<nk-callout>`, a modifier class becomes an attribute, a state class becomes a boolean attribute.

### NotionKit Elements – the app layer

If you want to use NotionKit through **drop-in web components** instead of writing the class markup yourself, take a look at **NotionKit Elements**: 88 vanilla-JS custom elements (`<nk-app>`, `<nk-sidebar>`, `<nk-tree-item>`, `<nk-callout>`, `<nk-database>`, `<nk-cmdk>`, `<nk-block-host>` …) with Shadow DOM, native form participation and pixel parity with the class markup – built on the constructable stylesheet shipped here. The [proof of concept](elements-poc.html) in this repository is its seed.

Useful links:
- GitHub: https://github.com/JUNGHERZ/NotionKit-Elements
- Live demo: https://notionkit-elements.jungherz.com/
- Docs: https://notionkit-elements.jungherz.com/docs.html
- npm: [`@jungherz-de/notionkit-elements`](https://www.npmjs.com/package/@jungherz-de/notionkit-elements)

### NotionKit Web – the website layer

If you want to build a **complete website** rather than assemble components yourself, **NotionKit Web** is the intended path: the official Astro template – docs from Markdown with page tree, bilingual blog with RSS, marketing sections, dark mode without a flash, no external requests, a Playwright smoke suite – on the NotionKit foundation.

Useful links:
- GitHub: https://github.com/JUNGHERZ/NotionKit-Web
- Website: https://notionkit-web.jungherz.com/
- Docs: https://notionkit-web.jungherz.com/docs.html

---

## 🤖 AI / LLM Reference (`SKILL.md`)

[`SKILL.md`](SKILL.md) is a structured reference for coding agents and AI assistants, generated from the same catalog as the documentation so it cannot drift:

- copy-paste markup for every component with nesting rules and classes
- complete token tables for both themes, read straight from the stylesheet
- the state-class contract
- **eight complete app skeletons** – workspace app, database app, settings modal, AI chat page, form/onboarding page, landing/docs page, home page, sign-in page – each with a "When to use this skeleton?" block
- editor recipes, rules and common mistakes, the web-component contract, the CSS-to-Elements mapping

Point your assistant at it: *"Use NotionKit classes as documented in SKILL.md."* It has been validated blind – an agent given nothing but the file produced a working app whose 61 classes all existed.

---

## 🌐 Browser Compatibility

| Browser | Version |
|---|---|
| Chrome / Edge | 111+ |
| Safari | 16.4+ |
| Firefox | 113+ |

The floor is set by `color-mix()` and constructable stylesheets. `:where()`, `inset`, `min()`, `accent-color` and `color-scheme` are older. Emoji are rendered by the operating system's emoji font; NotionKit ships no icon set and no web font – nothing to license.

---

## 📋 States & Modifiers – Cheat Sheet

| Class / attribute | Applies to | Effect |
|---|---|---|
| `hidden` (attribute) | every nk- element and slotted node | Hidden, always – even where a component sets its own `display` |
| `.active` | `nk-tree-item`, `nk-db-tab`, `nk-tab`, `nk-settings-pane`, `nk-segmented button`, `nk-board` | Where the user is |
| `.open` | `nk-modal-backdrop`, `nk-cmdk-backdrop`, `nk-sheet-backdrop`, `nk-dialog-backdrop`, `nk-peek-backdrop`, `nk-pop.floating`, `nk-tooltip`, `nk-sidebar` (phones), `nk-toggle-arrow` | Overlay visible, peek in, menu or tooltip shown, drawer in / arrow rotated |
| `.floating` `.sheet` | `nk-pop` | Fades in with `.open`; a bottom sheet below 860px |
| `.collapsed` | `nk-tree-children`, `nk-sidebar`, `nk-sidebar-toggle` | Subtree folded; sidebar slid out on the desktop, its ☰ shown |
| `.peek-inset` | `nk-app` | The page moves aside for the open side peek |
| `.selected` | `nk-cmdk-item`, `nk-model-card`, `nk-slash-item`, `nk-calendar .cal-day` | Transient highlight or chosen option – on a calendar the chosen day |
| `.start` `.end` `.in-range` | `nk-calendar .cal-day` | A range: its ends in the accent, the days between tinted |
| `.today` `.off` `.out` | `nk-calendar .cal-day`, `nk-calendar-view .cv-day` | Today in red; a day not worked or of another month greyed |
| `.weeks` | `nk-calendar`, `nk-calendar-view` | ISO calendar weeks in front of the days |
| `.show` | `nk-toast` | Toast slides in |
| `aria-checked="true"` | `nk-switch` (button form) | Switch on |
| `.full` `.small` | `nk-page` | Page options: full width, small text |
| `.primary` `.secondary` `.danger` `.danger-solid` `.small` | `nk-btn` | Button variants |
| `.gray` `.brown` `.orange` `.yellow` `.green` `.blue` `.purple` `.pink` `.red` | `nk-tag`, `nk-avatar` | Notion's nine colours; a tag without one is gray, an avatar the gradient |
| `.small` `.large` `.xlarge` `.square` | `nk-avatar` | 20 / 32 / 56px instead of 24px; workspace-icon corners |
| `.info` `.success` `.warning` `.danger` | `nk-banner` | Banner tones |

NotionKit ships states, not behaviour: toggle these yourself. The demo pages contain reference implementations in plain JavaScript.

---

## 🤝 Contributing

Issues and pull requests are welcome.

### Guidelines

- Keep the two-block structure: no `--nk-*` declaration outside the token blocks (the build fails otherwise).
- No hex literals in component rules – derive from a token with `color-mix()`.
- Every rule that styles slottable content gets a `::slotted()` twin.
- Add new components to `tools/catalog.mjs`; docs, showcase and `SKILL.md` are generated from it, and `npm run check:coverage` insists every class is documented.
- Code and comments in English; the pages exist in English and German.

### Releasing

A plain push to `main` updates the website (Verify Build + Deploy Pages) and touches nothing on npm. A release is a tag:

```bash
# 1. write the CHANGELOG entry  ## [1.1.0] – YYYY-MM-DD  (the release workflow refuses to publish without it)
# 2. bump – this also updates the version header in notionkit.css, commits and tags
npm version minor          # or patch / major
# 3. push branch and tag
git push && git push --tags
```

The tag triggers `release.yml`: it checks that tag, `package.json` and `CHANGELOG.md` agree, builds, creates the GitHub release with the artefacts and publishes to npm through trusted publishing (OIDC, provenance, no token).

---

## 📄 License

[MIT](LICENSE) © 2026 [Jungherz GmbH](https://www.jungherz.com)

---

## 📋 Changelog

See [CHANGELOG.md](CHANGELOG.md).

---

## 🏢 Credits

Built by [Jungherz GmbH](https://www.jungherz.com). NotionKit is not affiliated with or endorsed by Notion Labs, Inc.; it is an independent design system in a familiar idiom.
