NucleusKit
The whole Nucleus Stack in one package — every element, provider, Quark, and Valence.css behind a single import.
Features
- One JS import Registers every NucleusKit element
- One CSS import Valence.css theme + shared element styles (
basic.css) - App-ready Routing, sheets, forms, drawers, providers, and more
- À la carte Every package is published on its own; if you only use a few elements, install just those
Installation
This package is available in the
<script src="https://unpkg.com/@excom/nucleus-kit@0.5.1/dist/index.umd.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@excom/nucleus-kit@0.5.1/dist/basic.css">npm install @excom/nucleus-kitimport "@excom/nucleus-kit";@import "@excom/nucleus-kit/basic.css";Peer dependencies (0)
Packages a consumer must install alongside this one. Workspace deps are bundled.
| Package | Version |
|---|---|
|
Usage
import "@excom/nucleus-kit";import "@excom/nucleus-kit";@import "@excom/nucleus-kit/basic.css";@import "@excom/nucleus-kit/basic.css";That loads Valence.css (basic theme) plus element CSS and registers the
packages below.
Progressive bundle (experimental)
nucleus-kit.progressive.min.js registers nothing up front. It watches the
document for NucleusKit element tags and imports each element's package the
first time its tag appears (initial scan, then every inserted subtree), so a
page pays only for the elements it uses. Packages shared by several elements
(neutron, kit-utils, quark, the element bases) are separate chunks
under dist/progressive/, fetched once. The entry is 3.5 kB; a page using
quark-sheet and content-drawer loads ~55 kB gzip less than the all-in
build.
<script type="module" src="/node_modules/@excom/nucleus-kit/dist/nucleus-kit.progressive.min.js"></script><script type="module" src="/node_modules/@excom/nucleus-kit/dist/nucleus-kit.progressive.min.js"></script>With a bundler, import "@excom/nucleus-kit/nucleus-kit.progressive"; loads the same entry.
Trade-offs: elements upgrade one network round-trip later (style the
pre-upgrade state with :not(:defined)), it is ES modules only, and elements
inside a shadow root need observeElements(shadowRoot) from the same module.
The all-in index.umd.min.js and the ESM index.js are unchanged.
On a
Idle loading
Prefetch the remaining packages once the page has loaded, so later views, dialogs and SPA navigations upgrade instantly with no round-trip. Packages load one per browser idle period; tags already on the page load first and are never fetched twice.
Opt in on <body> (works with inline / bundled imports) or on the entry's own <script>:
<body nucleus-kit-idle> <!-- every package -->
<body nucleus-kit-idle="spa-route super-form data-table"> <!-- only these -->
<script type="module" src="/node_modules/@excom/nucleus-kit/dist/nucleus-kit.progressive.min.js" data-idle></script><body nucleus-kit-idle> <!-- every package -->
<body nucleus-kit-idle="spa-route super-form data-table"> <!-- only these -->
<script type="module" src="/node_modules/@excom/nucleus-kit/dist/nucleus-kit.progressive.min.js" data-idle></script>An empty value loads everything; a space-separated list loads only the packages behind those tags (unknown tags log a warning). data-idle is read only from the <script> whose src is the entry itself and wins over the body attribute. Nothing is prefetched in data-saver mode (Save-Data). From JS, idleLoadElements(tags?) does the same.
Server entry
@excom/nucleus-kit/server is the kit for detect-browser, detect-features, detect-media, gesture-handler, network-status, provider-geolocation, provider-orientation, provider-storage, service-worker, web-authn). Those stay as written in the prerendered page and upgrade in the browser; SERVER_EXCLUDED_TAGS lists their tags. It also exports the hooks a prerender runs around each page (beforeRender, settle, afterRender), so it is the whole entry of a entry: () => import("@excom/nucleus-kit/server"). ES modules only, not for the browser.
À la carte
NucleusKit is a convenience, not a requirement. If you find you are not using most of its elements, install the packages you do use individually and drop it:
npm install @excom/quark-sheet @excom/provider-fetch @excom/content-drawernpm install @excom/quark-sheet @excom/provider-fetch @excom/content-drawerimport "@excom/quark-sheet";
import "@excom/provider-fetch";
import "@excom/content-drawer";import "@excom/quark-sheet";
import "@excom/provider-fetch";
import "@excom/content-drawer";Each package is self-contained — same elements, same versions, same CSS hooks —
and its README documents the slim install. Valence.css is @excom/valence.
TypeScript
Generally, you are advised to avoid TypeScript unless your app starts having a lot of complex JS customization. In that case, NucleusKit elements declare global types: their HTML…Element interfaces, HTMLElementTagNameMap entries (so querySelector("spa-manager") and closest(…) are typed) and Quark's element.quark. TypeScript loads them only when it sees an import of the package, so an app that loads NucleusKit from a <script type="module"> or a CDN gets "Property does not exist" on element.closest("spa-manager")?.router or element.quark.
Add one declaration file that the app's tsconfig.json includes:
// globals.d.ts
import "@excom/nucleus-kit";// globals.d.ts
import "@excom/nucleus-kit";A .d.ts file emits nothing: no runtime import, no bundle cost. À la carte apps import each package they use instead (import "@excom/spa-route"; import "@excom/quark";). If a TypeScript file in the app already imports NucleusKit or the packages, nothing is needed.
What's included
Core
— custom element factoryneutron /quark — DOM orchestrationquark-sheet — Valence.css, semantic CSS (viavalencebasic.css)
Layout / chrome
content-carouselcontent-drawercontent-tabsdialog-anchordismiss-watcherdata-table
Content / routing
include-contentspa-routescroll-into-view
Forms / auth
super-formsuper-inputweb-authn
Providers
provider-fetchprovider-geolocationprovider-orientationprovider-storage
Platform
detect-browserdetect-featuresdetect-mediadom-observerevent-handlergesture-handlernetwork-statusservice-worker
Not in NucleusKit
Install separately when needed: mapbox-view, super-img, element bases
(abortable-element, fetchable-element, …), and editor tooling such as
nucleus-quark-highlighter
Attributes
Role —option is configurable, state
is managed by the element (read-only), or hybrid which is both.
Provision
Thisprovision property lives on the DOM node.
Read / watch it with Quark’s prop("provision"), or listen
for the neutron-provision event from app JS.
Events
Type
command event (HTML Command API): from a
<button command commandfor>, an <event-handler
command-name>, or a CommandEvent.
Recognized Elements
Child or descendant elements are recognized by and relevant to its functionality.Styles
@custom-selector synonyms so custom elements can share
semantic styles. Element aliases match the host tag / .tag-* class; state
aliases match attributes (nested under the element alias).
Release notes (6)
0.5.1
- Fix sheets that
@useone module with a top-levelawaiton Safari: on a page's first load the module's functions could run before it had finished loading (a highlighter that was not ready, so no highlighted code until the next navigation)
0.5.0
- Add the prerender hooks to
@excom/nucleus-kit/server(beforeRender,settle,afterRender): a kit app prerenders withentry: () => import("@excom/nucleus-kit/server")and no entry module of its own
0.4.0
- Add
@excom/nucleus-kit/server: the Kit without the elements that read the device or the person, for prerendering with@excom/nucleus-ssr;SERVER_EXCLUDED_TAGSlists their tags - Improve the progressive entry for prerendered pages: hydration stays open until the packages for the Kit tags present at startup are imported, so those elements keep their prerendered content
- Add the
@excom/nucleus-kit/nucleus-kit.progressiveimport path: the progressive entry is exported under its plain name as well asnucleus-kit.progressive.min
0.3.0
- Add
Quark.meter, the engine's work counters (counts,reset(),scopeSelectors()), always on, for complexity snapshots with@excom/nucleus-test - Add a warning when a
spa-managerupdate settles byrender-timeoutwhile a route is still pending, so an empty first paint has a cause in the console at log level 2 - Update
content-carouselinslide-animation="track"to render only the active slide and its two DOM neighbours (a wrap with three or more slides cuts instead of sliding), and to animate nothing before the first move, so an initialis-activeappears in place - Update
gesture-handlerpan-x/pan-yhandlers to hold the page still for the rest of a touch whose first 3 px run along the handler's axis, through a non-passivetouchmovelistener;touch-actionis unchanged, a touch that starts across the axis scrolls as before, and a same-axis scroller under the finger keeps scrolling - Fix
web-authnafter a failed ceremony or options request:is-loadingis cleared,is-erroris set with the message inprovision, and a retry clearsis-error - Fix an issue where an
is-fallbackroute stayed off after leaving a nested route for a URL no route matches: the fallback now checks which routes match the path, so its position among its siblings no longer matters, a page without aspa-managergets a working fallback, and a fallback inside a layout can activate on the layout's unmatched child paths - Fix the published type declarations of
neutron,quark,quark-parser,quark-formatterandkit-devtools, which pointed at a folder outside their tarballs, so the Kit's re-exported types resolve instead ofany
0.2.0
- Add idle loading to the progressive entry: opt in with
<body nucleus-kit-idle>ordata-idleon its<script>(empty = every package, a space-separated tag list = those packages) to prefetch packages one per idle period after page load; tags already on the page load first and are never fetched twice, data-saver mode is respected, andidleLoadElements(tags?)does the same from JS - Remove promise support from
content:in Quark sheets: a module function returns a value or a node (seeAsynchronous workin the Quark docs) - Update
handle:in@on:eventandtargetare not in scope inside ahandle:expression, and the listener receives the event - Add
KitLoggerto the exports of Nucleus Kit and its progressive entry:KitLogger.level = 2shows the warnings Nucleus Kit elements log - Add
QuarkLoggerto the exports of Nucleus Kit, its ESM entry and its UMD global, next toKitLogger:QuarkLogger.level = 2shows the warnings Quark logs, independently ofKitLogger.level; the progressive entry does not export it, as that would load all of Quark up front - Add
did-loadtoprovider-fetch,super-form,web-authnandquark-sheet: set on the first success, kept while a refresh loads, cleared on an error, so content can stay on screen with[did-load]instead of[is-success] - Add relative
route-hrefvalues tospa-routeandspa-a:checkoutresolves against the page's<base> - Add the key names
Space/Spacebarandplusto Quark'skey:and tokeycode-filter(Shift+Space) - Add absolute
http(s)URLs to Quark's@use: the module loads from that URL, and any other scheme (data:,blob:) is refused with a logged error - Update
match-nestedonspa-routeandspa-ato match its own path as well as child paths:route-href="/users"is active at/users, so a separate route for that path renders beside it and a 404 fallback stays off there - Update a reload to report
move: nullin the route data: nospa-manager-<move>event fires for it, and the saved scroll offset is restored - Update scroll handling so the outermost
spa-managerowns it: the browser's own scroll restoration is off while one is connected (the page's value returns after the last one disconnects), aspa-routewithout aspa-managerno longer scrolls, andspa-route.setScroll()is removed - Update scroll reset to happen only when a route rendered: a push or replace that renders nothing (a query change on a
reuseroute) keeps its position, and a#fragmenttarget wins over the reset - Update nested
spa-managerelements to join the outermost manager's View Transition: one transition per navigation, and only the outermost manager firesspa-manager-will-transitionandspa-manager-transitionand carriesis-transitioning - Update
spa-manager-will-transitionandspa-manager-renderedto fire when no View Transition runs too (reduced motion, no API,no-transition, first paint), once per update chain, as a navigation that arrives before the update settles joins it; a nested manager'sspa-manager-renderedno longer bubbles, so a listener on the outermost manager or above gets one per chain, andpreventDefault()onspa-manager-will-transitionholds the update, the first paint included, untilupdateRoutes(true)resumes it (with a View Transition where the page allows one) orupdateRoutes()runs it without one - Update a
same-route="reuse"param change to run no View Transition: an update that only changes provisions no longer animates - Update a query change to provision the route again:
spa-route-provisionfires, the provision carries a newqueryobject, and asame-route="refresh"route renders again - Update
has-renderedonspa-managerto be set once the routes of the first update have rendered, inside that update: a loading shell hidden on[has-rendered]stays until the first view is on screen (it was set when the first update started) - Update a closed
content-drawerto bevisibility: hiddenonce its slide-out ends: a customtransitionon it must keepvisibility 0s <duration>, and a closed drawer shown in the layout needsvisibility: visible - Update
content-drawerso only an.absolutedrawer styles its parent, asposition: relative; overflow: clip(was every drawer, withoverflow: hidden) - Update failed-request logging to one line: an error status logs a warning, which the default
KitLoggerlevel hides, and a network or parse failure logs an error - Update a template or sheet URL that answers with an error status to give
is-errorand paint nothing: the error page is no longer rendered as content - Update a
keycode-filterholding only spaces to match no key and warn once; writespacefor the space bar - Fix an issue where a rule reading a
$bindingorprop()did not apply its value again when an attribute or child change made it match again, so an inverse rule left the old value in place - Fix scroll restoration on back and forward: it no longer snaps before the transition, a restore follows late content for about 2 seconds or until the user scrolls, taps, types or drags the scrollbar, and a
#fragmenttarget rendered by a route is scrolled to - Fix an issue where a navigation started inside a
spa-manager-renderedlistener was dropped - Fix an issue where
spa-managerhung in a browser whosestartViewTransition()throws: the update now runs without a transition - Fix
provision.paramson aroute-regexroute: named groups ((?<id>\d+)) giveparams.id, URL-decoded, unmatched groups are left out, and unnamed groups are read fromprovision.match(every group used to land under the keyundefined) - Fix
provision.paramson aspa-routewhoseroute-hrefholds a capture group of its own, such as/(a|b)/:id: placeholders keep their names (the group shifted them, so a param landed under the keyundefined), a group the author names, such as(?<lang>en|de), becomes a param, and a placeholder that did not take part in the match is left out (it was the string"undefined") - Fix an issue where a
route-hrefwith a non-capturing group, such as/(?:a|b)/:id, madespa-routeandspa-athrow - Fix an issue where
include-contentandspa-routeelements on the same template URL stayedis-loadingwhen one of them disconnected, reloaded or changedtemplate-refduring the load: the shared request now finishes for the others - Add a TypeScript section to the README: an app that loads Nucleus Kit from HTML or a CDN gets its global element types from a
globals.d.tscontainingimport "@excom/nucleus-kit";
0.1.1
- Fix the
typescondition of the./nucleus-kit.progressive.minexport to point atdist/nucleus-kit.progressive.d.tsinstead of adist/nucleus-kit.d.tsthat is never published - Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
Release notes
e.preventDefault() is not
synchronously called on the event.
all: revert-layer.
Valence.css ships this as .unstyled and .unstyled-all for the entire tree.