@use "/shell" as *; @use "quark:list" as list; #release-notice { /* opens once: sheet re-runs restart a delay, so gate it on the fact it writes */ &:not([data-did-open]) { @delay 7000 { is-open: ""; data-did-open: ""; } } /* prevent clicks from bubbling to the sheet */ @on mouseup, click (stop-propagation); } provider-fetch[api-url*="package-metas/index.json"][is-success] { $package-indices: prop("provision").body; /* `excom.navGroup` moves a package out of its type's list, into that group */ $ungrouped: list.reject($package-indices.packages, "navGroup"); .package-links { [bind-elements] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "kit-element")); } [bind-element-bases] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "element-base")); } [bind-tools] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "tool")); } /* the standalone libraries: same rows, rendered flat (shell.css) */ [bind-libraries] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "library")); } [bind-library-group] ul:not(ul ul) { content: iterate(list.filter($package-indices.packages, "navGroup", "libraries")); } ul:not(ul ul) > li { $pkg: item.shortName; > spa-a { route-href: "/packages/#{item.shortName}"; } } /* one `content` rule per link: a second one would rewrite the first's text on every pass */ ul:not(ul ul, [bind-libraries] ul) > li > spa-a { content: item.shortName; } [bind-libraries] ul:not(ul ul) > li > spa-a { content: displayName(item.shortName); } /* packages whose docs span several pages: one collapsible group per section */ [bind-sections] { content: iterate(item.docSections); summary { content: item.title; } details > ul { content: iterate(item.docs); spa-a { route-href: "/packages/#{$pkg}/#{item.name}"; content: item.title; } } details:not(:has(spa-a[is-active])) { open: none; } } } /* page titles: the page, then the site. The docs home keeps the title in the document head; the other routes carry theirs in markup */ spa-route[is-active] { /* a site guide */ &[route-href$=":name"] { $title-guide: list.find($package-indices.docs, "name", $route.params.name); document-title: "#{$title-guide.title or $route.params.name} · Nucleus · docs"; } /* a package README */ &[route-href$=":packageName"] { document-title: "#{displayName($route.params.packageName)} · Nucleus · docs"; } /* one page of a package's docs */ &[route-href$=":docName"] { $title-package: list.find($package-indices.packages, "shortName", $route.params.packageName); document-title: "#{docTitle($title-package, $route.params.docName)} · #{displayName($route.params.packageName)} · Nucleus · docs"; } } /* the page's markdown file, for the footer link: the docs home and each guide the index lists, and a package's page whose index entry says `markdown` (build-docs-index sets it; the dev server's index has none, so no link there). siteDocHref and the path check say which URL is the page's own: the 404 and a package's doc pages have none */ > spa-manager[active-url] { $page-path: attr("active-url").split("#").at(0).split("?").at(0); $page-name: if($page-path == SITE_HOME: SITE_HOME_DOC; else: $page-path.split("/").at(-1)); $page-guide: list.find($package-indices.docs, "name", $page-name); $page-package: list.find($package-indices.packages, "shortName", $page-name); $page-markdown: if($page-guide and siteDocHref($page-guide.name) == $page-path: "/docs/#{$page-guide.name}.md"; $page-package.markdown and "#{SITE_BASE}/packages/#{$page-package.shortName}" == $page-path: "/#{$page-package.shortName}.md"); footer [bind-page-markdown] { href: $page-markdown; content: ternary($page-markdown, "Markdown version of this page"); } } } spa-route { $route: prop("provision"); /* paramless routes name their guide in markup (the docs home, `/`); /docs/:name gets it from params */ $route-doc-name: attr("data-doc-name"); } /* the desktop aside and the mobile sheet stamp the same nav template */ [data-site-nav] { details:has(spa-a[is-active]) { /* not kosher */ open: ""; } spa-a[is-active] { aria-current: ""; } spa-a:not([is-active]) { aria-current: none; } } /* mobile sheet: Escape / back gesture while open; a tapped link closes it */ #site-menu { &[is-open] dismiss-watcher { is-active: ""; } &:not([is-open]) dismiss-watcher { is-active: none; } @on click (target: "spa-a") { is-open: none; } } #site-menu-gesture { &:has(> #site-menu[is-open]) { progress-offset: 1; } &:not(:has(> #site-menu[is-open])) { progress-offset: 0; } @on gesture-handler-start { #site-menu { is-scrubbing: ""; } } @on gesture-handler-end { #site-menu { is-open: event.detail.snap == 1; is-scrubbing: none; } } } main { @on copy-source (handle: copySource); } #search-dialog[open] { > include-content { is-active: ""; /* for some reason this is necessary on first render */ @on include-content-did-render (handle: focusInput); } input[type="search"] { /* works on all subsequent opens */ autofocus: ""; } } #search-dialog:not([open]) input[type="search"] { autofocus: none; }

Roadmap

What is being worked on, what is queued behind it, and what is only an idea. No dates.

Where it stands today

The Nucleus Stack is in beta. Breaking changes still land, and they land as removals rather than deprecations.

What is already settled:

  • The architecture. Adapter, State, Orchestrator is not up for major revision. The document is the state, elements bridge protocols, rules coordinate.
  • Element API shape. Attributes in, events out, commands to invoke. Individual attributes come and go; the contract does not.
  • The zero-build path. Serving files as-is is the default way to use the stack, not a degraded mode.

What "stable" will mean, when the beta ends: a public API freeze on the Quark language and on element attributes, events and commands, with semantic versioning honoured from that point on.

Until then: pin your versions, and read the change notes before you upgrade.

Sooner

Work that is done or nearly done, landing in the next releases.

  • More elements in the catalog. May include some of the following elements. Interaction: content-sortable, content-splitter, content-popover, super-file-input. Actions: clipboard-copy, web-share, file-download. Sensors: detect-visibility, detect-size, detect-scroll, detect-document, detect-permission. Providers: provider-url-params, provider-sse, provider-websocket, provider-worker.
  • Polyfills Some features will be broken for certain browsers whose versions are older than a year (mainly Firefox & Safari, mid-2025). This will be remedied in the first stable release.
  • The small-bug backlog. A coverage sweep across every package surfaced a list of minor defects. They are being triaged and fixed ahead of a stable release.
  • Sophisticated mobile features These features - implemented via CSS and custom elements - will make a Nucleus app behave like a polished native app.
  • More themes for Valence.css. Broader theme and scheme coverage, so a view's --v-* tokens carry further without custom CSS.
  • HTML/Quark "type checking" Editor integration and optional build tool to raise warnings about unknown elements/attributes/provisions.

Later

Wanted, thought about, not scheduled. Nothing here is a commitment.

  • Quark rule reversion & specificity. There are open architectural and performance questions, not just an implementation cost. It is wanted. It is not scheduled. Do not build on it.
  • More of SSR. Rendering per request, a worker pool for large sites (one renderer per process today), prerendered shadow / iframe render hosts, and a hydration check that runs in a real browser.
  • Optional build-time wins. For teams who already run a bundler, shipping pre-parsed sheets would let the parser drop out of the runtime. Noted, not scheduled — and the zero-build path stays the default either way.
  • Scoped view transitions When/if this lands in browsers, it is a very easy change to scope Quark's @view-transition rule.
  • A standards track. If Quark finds real adoption, the intention is to draft a proposal for a native platform capability along these lines. See Origin Story.

How priorities get decided

When two good ideas compete, these break the tie.

  • Composability first. If a change would make two pieces harder to combine, it does not land — even when it would be convenient. This is the reason there is no component concept and why elements never render or mutate children.
  • No cooperation required. A mechanism that works on any element, including ones we did not write, beats a contract that only holds when everyone opts in.
  • If it is not in the document, it does not exist. Features that route state or behaviour around the page are invisible to devtools, to serialisation and to every other sheet — which are the things the stack is for. Would rather not ship such a feature than ship one that hides state.

How to influence it

Priorities here are set by what people actually hit, so the most useful thing you can send is a concrete case.

  • Report what broke. Open an issue with the smallest HTML, CSS and Quark that reproduces it. A reduced case moves faster than anything else. Check Troubleshooting and Limitations first — some surprises are documented trade-offs with a stated workaround.
  • Propose an element by describing the protocol it bridges and why existing elements plus a Quark rule cannot already do it. Contributing has the questions a proposal should answer.
  • Tell us what you had to write JavaScript for. That is the single most valuable signal on this page. Every gap between "I could express this as a rule" and "I had to write a function" is a candidate for the language or the catalog, and the examples come from real applications, not from guessing.
  • Send a pull request. Setup, conventions and what a reviewable change looks like are in Contributing.

Beta. The Nucleus Stack is in beta for a few weeks until features are stabilized and optimized.

Thanks — we'll email you when it ships.

Something went wrong. Please try again.