npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jungherz-de/notionkit-elements

v1.21.0

Published

Vanilla JavaScript Web Components wrapping the NotionKit CSS foundation – <nk-*> custom elements with Shadow DOM, automatic theme sync and native form participation.

Downloads

579

Readme


✨ What is NotionKit Elements?

NotionKit Elements is the companion library to NotionKit CSS. It provides 88 Web Components – from buttons, form controls and the date picker over the sidebar, page tree and page shell with its properties to the settings modal, the bottom sheet, the side peek, dialogs, tooltips, ⌘K palette, database views – a calendar and a gallery among them – with Notion’s toolbar and AI thread – that encapsulate the class markup of the foundation into simple, declarative custom elements.

It is the app layer of the NotionKit family – three layers, one design language: NotionKit is the pure-CSS foundation, NotionKit Elements wraps it into web components for application UIs, and NotionKit Web is the Astro template for complete websites on top of the same foundation. The word stem stays the same across the layers: .nk-callout becomes <nk-callout>, a modifier class becomes an attribute, a state class becomes a boolean attribute.

<!-- Before: 6 elements, 6 classes -->
<div class="nk-field">
  <div>
    <div class="f-label">Email notifications</div>
    <div class="f-desc">Shown next to your comments.</div>
  </div>
  <div class="f-control"><button class="nk-switch" role="switch" aria-checked="true"></button></div>
</div>

<!-- After: 2 elements, 0 classes -->
<nk-field label="Email notifications" desc="Shown next to your comments.">
  <nk-switch checked></nk-switch>
</nk-field>

🎯 Why NotionKit Elements?

| Feature | Details | |---|---| | 🎯 Pixel parity | Every element renders identically to its class markup – verified by a Playwright pixel test in light and dark; the reference app differs by 0.00 % from the class version | | 🔌 Shadow DOM | Each root adopts the NotionKit components sheet via adoptedStyleSheets; tokens are inherited from the document, so one :root { --nk-accent: … } re-brands everything | | 🧩 88 Elements | Forms with copy field, image picker and date picker, content blocks with bookmarks, app shell & tree with the ☰ drawer and the collapsing sidebar, page shell with properties, panels, avatars and steps, overlays (modal, sheet, dialog, resizable side peek, tooltip, ⌘K palette, menus that float in and become sheets on a phone, popover, emoji picker, toast), database table, board, list, calendar & gallery with toolbar and filter pills, comments, AI thread | | 🎛️ Form participation | Input, textarea, select, switch, check, radio, slider, segmented and model card work natively with <form> via ElementInternals; radios group across shadow roots | | 🌗 Theme sync | One observer mirrors data-theme on <html> into every element | | 🪶 Lightweight | 292 KB minified / 59 KB gzipped (IIFE, foundation sheet included), no external dependencies | | 📦 Five bundle shapes | IIFE, minified IIFE, ESM, minified ESM with a source map (esm.min.js), and per-component ESM entries on a stable base.js, which import NotionKit's sheet instead of carrying a copy | | 🤖 AI-ready | SKILL.md with copy-paste markup, six app skeletons and the rules – an agent given only that file built a working app in the release test |


Quick start

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/[email protected]/notionkit.min.css">
  <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/[email protected]/dist/notionkit-elements.min.js"></script>
</head>
<body class="nk-body">
  <nk-field label="Display name" desc="Shown next to your comments">
    <nk-input name="name" value="Ada Lovelace"></nk-input>
  </nk-field>
  <nk-btn variant="primary">Save</nk-btn>
</body>
</html>
npm install @jungherz-de/notionkit-elements @jungherz-de/notionkit
import '@jungherz-de/notionkit/notionkit.css';
import '@jungherz-de/notionkit-elements';                              // everything
import '@jungherz-de/notionkit-elements/components/nk-btn.js';         // or one element (shared code: base.js)
import { componentsSheet } from '@jungherz-de/notionkit-elements';     // the sheet the elements adopt, for your own views

The per-component files import NotionKit's sheet as @jungherz-de/notionkit/notionkit-styles.js rather than carrying a copy, so a project that adopts the sheet in its own views has it once. A bundler resolves it from node_modules; a build-free page adds one import-map entry:

<script type="importmap">{ "imports": { "@jungherz-de/notionkit/notionkit-styles.js": "/node_modules/@jungherz-de/notionkit/notionkit-styles.js" } }</script>

Without a build step, an import map can point at the minified module instead: "@jungherz-de/notionkit-elements": "/node_modules/@jungherz-de/notionkit-elements/dist/notionkit-elements.esm.min.js" – the same exports as notionkit-elements.esm.js, its source map beside it.

Do not mix the full bundle with the per-component files: each brings its own NkElement. With the bundle, take componentsSheet and NkElement from the bundle (NotionKitElements.componentsSheet from the <script> build).

Two prerequisites, always: notionkit.css on the document (the peer dependency, >= 1.21.0; 1.21.0 brings the select-all header of a table and the dash of an indeterminate checkbox and keeps a row with a checkbox at 36px, 1.20.0 brings the appearance glyph of a three-state theme toggle, 1.19.1 keeps a toast with an × at its height, 1.19.0 brings the toast with an action, 1.18.0 brings a bar and its label in one row, stacked properties that keep to their card on a phone and icons in the copy field, 1.17.0 brings a step's label and a gallery card as links, 1.16.0 brings --nk-block-space and flush for blocks in a column, keeps a wide progress bar and a stretched button's label in shape and lays floating menus above dialogs, 1.15.0 brings table text with a tone and a second line, 1.14.0 brings the gallery view and a banner action that stands at the right edge again, 1.13.0 keeps fields inside narrow panels, leaves a page without icon its padding under the cover and makes cover heights tokens, 1.12.0 brings a tag or button beside a panel's title, lists and grids without outer margin, text values, steps with states in any order and in a row, scrolling tabs, a cap for full pages and buttons per table row, 1.11.0 keeps a floating menu inside an open modal or dialog in place and inside the window and brings the banner's action as a button, fields that share a row and tooltips of several lines, 1.10.1 lets a disabled button show its tooltip, 1.10.0 brings the collapsing sidebar, the resizable side peek, the dialog and the tooltip, 1.9.0 the date picker and the calendar view, 1.8.0 the side peek, the bookmark, the copy field and pictures on the profile row, 1.7.0 the sheet, menus that float in and become sheets on a phone, the sidebar drawer, the database toolbar with filter pills and the steps, 1.7.1 minified files that carry all of it, 1.6.0 page properties, page options, list view, panels, prose and the avatar, 1.5.1 the safe areas, 1.5.0 the Notion-2025 tokens, the nine tag colours and the filled inputs; from 1.5.0 on, Elements and the foundation share one version number – the elements are built and tested against the same release) and class="nk-body" on <body>. The elements ship no visual CSS of their own – every rule comes from the foundation. The bundle injects the design tokens once as @layer notionkit-defaults, so any plain :root { --nk-accent: … } of yours re-brands every element in both themes.

Elements

| Group | Elements | |---|---| | Forms & controls | nk-btn nk-input nk-textarea nk-select nk-switch nk-check nk-radio nk-slider nk-field nk-fields nk-copy-field nk-image-picker nk-calendar | | Content | nk-tag nk-progress nk-callout nk-bookmark nk-divider nk-heading nk-toggle nk-todo nk-kbd nk-code nk-quote | | App shell & navigation | nk-app nk-sidebar nk-workspace-switcher nk-section-label nk-tree nk-tree-item nk-topbar nk-breadcrumb nk-theme-toggle nk-tab-bar nk-tab-bar-item | | Page shell & blocks | nk-page nk-page-cover nk-page-title nk-page-actions nk-props nk-prop nk-panels nk-panel nk-avatar nk-block-host nk-banner nk-empty nk-skeleton nk-synced nk-tabs nk-tab nk-segmented nk-steps nk-stats nk-stat nk-avatar-group nk-mention nk-template-btn nk-model-card nk-profile-row nk-danger-zone nk-member-list nk-member-row | | Overlays | nk-modal nk-sheet nk-dialog nk-peek nk-tooltip nk-settings-pane nk-settings-user nk-cmdk nk-menu nk-menu-item nk-pop nk-emoji-picker nk-toast | | Data & collaboration | nk-database nk-table-view nk-board-view nk-list-view nk-calendar-view nk-gallery-view nk-filter-bar nk-comments nk-comment nk-ai-thread nk-ai-msg nk-ai-input-row (+ the exported renderPropertyCell()) |

Static content goes in through attributes and slots, dynamic data through properties (tree.data, database.rows, palette.commands). Interactions fire custom events with fixed names: nk-select, nk-change, nk-view-change, nk-command, nk-toggle, nk-submit, nk-action. No fetching, no two-way binding – the elements render what they get.

Languages

The texts the elements bring along – button labels, placeholders and the names a screen reader announces – come in English and German. The language is the element's own: its nearest lang attribute, across shadow roots, else <html lang>; de, de-DE or de-AT give German, everything else English. An attribute on the element still wins, so a label with a meaning of its own stays (choose-label="Upload logo").

import { setStrings, builtInStrings } from '@jungherz-de/notionkit-elements'; // or from '…/base.js' with single elements
setStrings({ newPage: '+ New course' });                    // every language
setStrings({ copy: 'Copier', close: 'Fermer' }, 'fr');       // a language of its own
builtInStrings('de');                                        // the keys and the German texts, a copy

The elements on the page take new texts at once, after setStrings() and when <html lang> changes. An element of your own built on NkElement reads the same dictionary with this.str('key') – your keys too – and applies it again in onStringsChanged(). Percentages follow the language as well: a progress value of 45 reads 45% in English and 45 % in German, with a no-break space, as Intl.NumberFormat writes it (a column's locale first). Dates and month names already did.

| Key | Element | Attribute | English | German | |---|---|---|---|---| | copy | nk-copy-field | copy-label | Copy | Kopieren | | copied | nk-copy-field | copied-label | Copied | Kopiert | | show | nk-copy-field | show-label | Show | Zeigen | | hide | nk-copy-field | hide-label | Hide | Verbergen | | uploadImage | nk-image-picker | choose-label | Upload image | Bild hochladen | | changeImage | nk-image-picker | change-label | Change image | Bild ändern | | remove | nk-image-picker | remove-label | Remove | Entfernen | | close | nk-peek | close-label | Close | Schließen | | resize | nk-peek | resize-label | Resize | Breite ändern | | today | nk-calendar, nk-calendar-view | today-label | Today | Heute | | previousMonth | nk-calendar, nk-calendar-view | prev-label | Previous month | Voriger Monat | | nextMonth | nk-calendar, nk-calendar-view | next-label | Next month | Nächster Monat | | clear | nk-calendar | clear-label | Clear | Leeren | | time | nk-calendar | time-label | Time | Uhrzeit | | filter | nk-filter-bar | filter-label | Filter | Filter | | sort | nk-filter-bar | sort-label | Sort | Sortieren | | addFilter | nk-filter-bar | add-label | + Filter | + Filter | | removeFilter | nk-filter-bar | remove-label | Remove filter | Filter entfernen | | search | nk-filter-bar | placeholder | Search … | Suchen … | | closeSidebar | nk-sidebar | collapse-label | Close sidebar | Seitenleiste schließen | | newPage | nk-table-view, nk-list-view, nk-gallery-view | new-row-label | + New page | + Neue Seite | | commentPlaceholder | nk-comments | placeholder | Comment … | Kommentieren … | | send | nk-comments, nk-ai-input-row | send-label (comments) | Send | Senden | | askPlaceholder | nk-ai-input-row | placeholder | Ask something … | Frag etwas … | | commandPlaceholder | nk-cmdk | placeholder | Search or type a command … | Suchen oder Befehl eingeben … | | emojiPlaceholder | nk-emoji-picker | placeholder | Search… | Suchen … | | toggleTheme | nk-theme-toggle | title | Toggle light / dark | Hell / Dunkel | | selectAll | nk-table-view (a checkbox column with selectAll) | – | Select all | Alle auswählen | | appearance | nk-theme-toggle (modes with auto) | title | Appearance: {mode} – click: {next} | Darstellung: {mode} – Klick: {next} | | themeAuto | nk-theme-toggle (modes with auto) | – | automatic (follows the system) | automatisch (wie das System) | | themeLight | nk-theme-toggle (modes with auto) | – | light | hell | | themeDark | nk-theme-toggle (modes with auto) | – | dark | dunkel | | synced | nk-synced | badge | ⟳ synced | ⟳ synchronisiert | | message | nk-ai-input-row | – | Message | Nachricht | | comment | nk-comments | – | Comment | Kommentar | | commandPalette | nk-cmdk | – | Command palette | Befehlspalette | | searchEmoji | nk-emoji-picker | – | Search emoji | Emoji suchen | | settings | nk-modal | – | Settings | Einstellungen | | title | nk-page-title (editable) | – | Title | Titel | | changeIcon | nk-page | – | Change icon | Symbol ändern | | breadcrumb | nk-breadcrumb | – | Breadcrumb | Pfad | | add | nk-tree-item | – | Add | Hinzufügen | | more | nk-tree-item | – | More | Mehr |

Links

A step of <nk-steps> with href and a card of <nk-gallery-view> whose row has an address in the field named by href-key are real links: they open in a new tab with a middle click and offer "Copy link" in the context menu. A plain left click, and Enter, fire the cancelable nk-select as before; cancel it and the browser stays, for a router of your own. A middle click or one with Cmd, Ctrl, Shift or Alt fires nothing and does what a link does. Steps and rows without an address stay buttons and cards.

How it works

  • Each element's shadow root adopts the NotionKit components sheet only; tokens are inherited from the document. A .nk-wrapper inside the root mirrors data-theme from <html> – one observer for all instances.
  • Every host is display: contents: the inner .nk-* element sits in the parent layout exactly where the class markup would. That is what makes the pixel parity hold – and why spacing belongs on a wrapper you own, not on the host.
  • Form controls are form-associated custom elements: FormData, reset, required and <fieldset disabled> just work. <nk-radio>s with the same name form a real group across shadow roots, with one tab stop and arrow keys.
  • Elements that copy light-DOM children (nk-select options, breadcrumb crumbs) watch them; element.refresh() is the escape hatch. Listeners are re-armed on every connect, so a moved element keeps working.
  • ::slotted() matches only the assigned node: pass icons directly (<span slot="icon">📁</span>), never wrapped. For the same reason there is no <nk-prose>: rendered Markdown goes into a light-DOM <div class="nk-prose">, which reads the same rules as the editor adapter.
  • Overlays (nk-modal, nk-sheet, nk-peek, nk-cmdk, nk-toast, a floating nk-menu) go directly under <body>. The modal, the sheet and the palette lock scroll and make the rest of the page inert – the side peek only as a sheet on a phone, beside the page it leaves it usable; all of them return focus to the control that had it, inside a shadow root too. Escape closes the layer that opened last: a date picker or menu opened from a dialog first, then the dialog.
  • The editor stays an adapter: nk-block-host is the shell, docs-editor.js the TipTap recipe (used in the demo app). <nk-editor> follows in v1.1 as an optional import, never in the core bundle.

Development

npm install
npm run build        # dist/: IIFE, minified IIFE, ESM, per-component ESM on dist/components/base.js
npm run build:all    # + docs.html, showcase.html, index.html, de/, SKILL.md, llms.txt
npm test             # Playwright: pixel parity vs. class markup (both themes), branding, reconnect, radio groups, light-DOM drift, forms, overlays, data views, per-component import
npm run check:coverage && npm run check:versions

tools/catalog.mjs is the single source for docs, showcase, SKILL.md, llms.txt, the coverage check and the parity test. dist/ and the generated pages are committed; CI rebuilds them and fails on drift. dist/ embeds the installed NotionKit version – release order: publish the CSS package first, npm install here, rebuild, commit, tag.

The NotionKit family

| Layer | Repository | Website | |---|---|---| | NotionKit – CSS foundation | JUNGHERZ/NotionKit | notionkit.jungherz.com | | NotionKit Elements – Web Components (this package) | JUNGHERZ/NotionKit-Elements | notionkit-elements.jungherz.com | | NotionKit Web – Astro template for complete websites | JUNGHERZ/NotionKit-Web | notionkit-web.jungherz.com |

npm: @jungherz-de/notionkit · @jungherz-de/notionkit-elements

MIT · Jungherz GmbH