@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; }

Selectors

Quark rides the native selector engine: whatever querySelectorAll matches, a rule matches. What Quark adds is observation — knowing which changes can flip a match and where the subjects are afterwards.

What is observed

Attributes, classes and ids named anywhere in a selector are observed on the element they sit on: the matched element, an ancestor or earlier sibling on the selector's path, or a descendant named in :has(). Structure is observed too: elements inserted or removed under the host re-check the rules whose match depends on children or sibling position. Generated.

Combinators

Combinator Observed
a b Descendant. An attribute change on a re-runs the rule for the matching bs below it.
a > b Child. Same observation as the descendant combinator.
a + b Next sibling. An attribute change on a re-runs the rule from the parent; elements inserted or removed under that parent re-run it too.
a ~ b Subsequent siblings. Same observation as +.

Logical

Pseudo-class Observed
:is(…) Attributes anywhere in the argument list are observed like the compound's own. A complex argument (:is(section[x] li)) re-runs the rule from the changed element down.
:where(…) Same as :is() (Quark has no specificity).
:not(…) Same observation as :is().

Relational

Pseudo-class Observed
:has(…) Attributes named in the argument are observed on descendants, and elements inserted or removed below a candidate re-check it: the rule re-runs for every matching ancestor of the change. Sibling-relative arguments (:has(+ …), :has(~ …)) and a :has() nested in a complex :is() / :not() argument re-run the whole rule from the host instead. Without rule reversion, pair it with the inverse :not(:has(…)) rule.

Structural (sibling position, children)

Pseudo-class Observed
:first-child Sibling position: re-runs when elements are inserted or removed under the parent.
:last-child Same as :first-child.
:only-child Same as :first-child.
:nth-child(An+B [of S]) Same as :first-child. With of S, attributes in S are observed on the siblings and each change re-runs the rule from the parent.
:nth-last-child(An+B [of S]) Same as :nth-child().
:first-of-type Same as :first-child.
:last-of-type Same as :first-child.
:only-of-type Same as :first-child.
:nth-of-type(An+B) Same as :first-child.
:nth-last-of-type(An+B) Same as :first-child.
:empty Re-checked when elements are inserted or removed below the element. Text-only changes are not observed.

Attribute-backed

Pseudo-class Observed
:disabled Observes disabled on the element and on ancestors (a disabled <fieldset>).
:enabled Same as :disabled.
:required Observes required.
:optional Same as :required.
:read-only Observes readonly, disabled and contenteditable on the element and its ancestors.
:read-write Same as :read-only.
:any-link Observes href.
:lang(…) Observes lang on the element and its ancestors.
:open Observes the open attribute (<details>, <dialog>). A <select> / <input> picker opening is not observed.

Static

Pseudo-class Observed
:scope The host; never changes.
:root The document element; never changes.

Not observed — :hover, :focus, :focus-within, :focus-visible, :active, :visited, :link, :target, :checked, :indeterminate, :default, :valid, :invalid, :in-range, :out-of-range, :placeholder-shown, :popover-open, :modal, :fullscreen, :defined, :dir(…): Interaction or browser state with no attribute behind it: matches on the first run only (warned at build). Select on reflected attributes instead. Any pseudo-class not listed above is treated the same way.

:has()

:has() costs a native subtree scan per candidate on every fan-out that includes the rule, plus one ancestor walk per observed change — keep its arguments shallow and off rules that match many rows. Without rule reversion a :has() rule needs its inverse (:not(:has(…))) like any other state:

/* an aggregate over the rows becomes a fact on the host */
:scope:has(li[data-is-selected]) { data-has-selection: ""; }
:scope:not(:has(li[data-is-selected])) { data-has-selection: none; }

Classes and ids

Class (.x) and id (#x) selectors are observed like attributes (a class change counts only when it adds or removes a class the sheet names). Setting attributes is still recommended over toggling / mutating classes and ids, since the latter has a heavier impact on Quark's performance: a sheet that names any class wakes on every class change under its host (styling churn included).

When a rule re-runs, and what stays unobserved: Reactivity.

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.