@excom/quark 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/.rush/temp/chunked-rush-logs/quark.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/quark.build_package-metas.chunks.jsonl +1 -0
  3. package/.rush/temp/operation/apply-exports/all.log +1 -0
  4. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  5. package/.rush/temp/operation/apply-exports/state.json +3 -0
  6. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  7. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  8. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  9. package/.rush/temp/shrinkwrap-deps.json +3 -0
  10. package/config/rig.json +6 -0
  11. package/index.ts +4 -0
  12. package/language.ts +7 -0
  13. package/package.json +45 -0
  14. package/rush-logs/quark.apply-exports.cache.log +1 -0
  15. package/rush-logs/quark.apply-exports.log +1 -0
  16. package/rush-logs/quark.build_package-metas.cache.log +1 -0
  17. package/rush-logs/quark.build_package-metas.log +1 -0
  18. package/src/actions.ts +445 -0
  19. package/src/ast.ts +187 -0
  20. package/src/bindings.ts +115 -0
  21. package/src/builtin-modules.ts +401 -0
  22. package/src/constants.ts +47 -0
  23. package/src/cycle-check.ts +121 -0
  24. package/src/devtools-hook.ts +303 -0
  25. package/src/element-api.ts +109 -0
  26. package/src/evaluator.ts +317 -0
  27. package/src/form-controls.ts +69 -0
  28. package/src/language-tables.ts +137 -0
  29. package/src/language.ts +1225 -0
  30. package/src/observer.ts +110 -0
  31. package/src/paint.ts +305 -0
  32. package/src/properties.ts +818 -0
  33. package/src/props.ts +94 -0
  34. package/src/quark-element.ts +7 -0
  35. package/src/quark-internal.ts +365 -0
  36. package/src/quark.ts +817 -0
  37. package/src/resolvers.ts +867 -0
  38. package/src/rule.ts +800 -0
  39. package/src/scope-id.ts +47 -0
  40. package/src/selector-utils.ts +430 -0
  41. package/src/settle.ts +117 -0
  42. package/src/types.ts +155 -0
  43. package/src/utils.ts +79 -0
  44. package/src/variables.ts +359 -0
  45. package/support/demos/css-variables.html +18 -0
  46. package/support/demos/dispatch.html +20 -0
  47. package/support/demos/events.html +9 -0
  48. package/support/demos/iterate.html +13 -0
  49. package/support/demos/js-api.html +17 -0
  50. package/support/demos/provider-list.html +16 -0
  51. package/support/demos/toggle-content.html +14 -0
  52. package/support/demos/unset.html +12 -0
  53. package/support/demos/view-transition.html +36 -0
  54. package/support/docs/ATTRIBUTES.md +37 -0
  55. package/support/docs/AT_RULES.md +45 -0
  56. package/support/docs/BUILTINS.md +57 -0
  57. package/support/docs/CONTENT.md +40 -0
  58. package/support/docs/CSS_VARIABLES.md +16 -0
  59. package/support/docs/DECLARATIONS.md +26 -0
  60. package/support/docs/DELAY.md +37 -0
  61. package/support/docs/DIAGNOSTICS.md +32 -0
  62. package/support/docs/DISPATCH.md +81 -0
  63. package/support/docs/ELEMENT_PROPERTIES.md +34 -0
  64. package/support/docs/EXPRESSIONS.md +31 -0
  65. package/support/docs/INTERNAL.md +8 -0
  66. package/support/docs/JS_API.md +53 -0
  67. package/support/docs/JS_WRITES.md +36 -0
  68. package/support/docs/LIMITATIONS.md +26 -0
  69. package/support/docs/LOOP_GUARD.md +30 -0
  70. package/support/docs/METHODS.md +43 -0
  71. package/support/docs/MODULES.md +111 -0
  72. package/support/docs/NO_REVERSION.md +18 -0
  73. package/support/docs/ON.md +87 -0
  74. package/support/docs/REACTIVITY.md +24 -0
  75. package/support/docs/README.md +143 -0
  76. package/support/docs/SELECTORS.md +87 -0
  77. package/support/docs/SHEETS.md +67 -0
  78. package/support/docs/SYNTAX.md +36 -0
  79. package/support/docs/USE.md +24 -0
  80. package/support/docs/VALUES.md +27 -0
  81. package/support/docs/VARIABLES.md +47 -0
  82. package/support/docs/VIEW_TRANSITION.md +64 -0
  83. package/support/docs-sections.json +65 -0
  84. package/support/package-meta.json +208 -0
  85. package/support/scripts/build-language-docs.mjs +196 -0
  86. package/support/tests/__snapshots__/css-variables.view.test.ts.snap +23 -0
  87. package/support/tests/__snapshots__/dispatch.view.test.ts.snap +23 -0
  88. package/support/tests/__snapshots__/events.view.test.ts.snap +23 -0
  89. package/support/tests/__snapshots__/iterate.view.test.ts.snap +23 -0
  90. package/support/tests/__snapshots__/js-api.view.test.ts.snap +23 -0
  91. package/support/tests/__snapshots__/provider-list.view.test.ts.snap +23 -0
  92. package/support/tests/__snapshots__/quark-features.test.ts.snap +1079 -0
  93. package/support/tests/__snapshots__/quark-with-providers.test.ts.snap +89 -0
  94. package/support/tests/__snapshots__/quark.test.ts.snap +221 -0
  95. package/support/tests/__snapshots__/selectors.test.ts.snap +133 -0
  96. package/support/tests/__snapshots__/toggle-content.view.test.ts.snap +23 -0
  97. package/support/tests/__snapshots__/unset.view.test.ts.snap +23 -0
  98. package/support/tests/__snapshots__/view-transition.view.test.ts.snap +23 -0
  99. package/support/tests/actions.test.ts +437 -0
  100. package/support/tests/builtin-modules.test.ts +337 -0
  101. package/support/tests/builtins.test.ts +189 -0
  102. package/support/tests/css-variables.view.test.ts +35 -0
  103. package/support/tests/delay.test.ts +244 -0
  104. package/support/tests/devtools-hook.test.ts +496 -0
  105. package/support/tests/diagnostics.test.ts +224 -0
  106. package/support/tests/dispatch.view.test.ts +43 -0
  107. package/support/tests/element-api.test.ts +202 -0
  108. package/support/tests/evaluator.test.ts +237 -0
  109. package/support/tests/events.view.test.ts +40 -0
  110. package/support/tests/form-controls.test.ts +452 -0
  111. package/support/tests/helpers.ts +354 -0
  112. package/support/tests/internals.test.ts +568 -0
  113. package/support/tests/iterate.view.test.ts +40 -0
  114. package/support/tests/js-api.view.test.ts +45 -0
  115. package/support/tests/language-docs.test.ts +147 -0
  116. package/support/tests/lifecycle.test.ts +440 -0
  117. package/support/tests/loop-guard.test.ts +323 -0
  118. package/support/tests/provider-list.view.test.ts +47 -0
  119. package/support/tests/quark-features.test.ts +2887 -0
  120. package/support/tests/quark-with-providers.test.ts +169 -0
  121. package/support/tests/quark.test.ts +359 -0
  122. package/support/tests/resolvers.test.ts +346 -0
  123. package/support/tests/selector-analysis.test.ts +281 -0
  124. package/support/tests/selectors.test.ts +670 -0
  125. package/support/tests/toggle-content.view.test.ts +40 -0
  126. package/support/tests/unset.view.test.ts +42 -0
  127. package/support/tests/view-helpers.ts +81 -0
  128. package/support/tests/view-transition.test.ts +1069 -0
  129. package/support/tests/view-transition.view.test.ts +76 -0
  130. package/tsconfig.json +5 -0
@@ -0,0 +1,36 @@
1
+ # Syntax
2
+
3
+ Quark is a derivative of CSS with CSS-compatible syntax. A CSS engine tokenizes a Quark sheet and drops what it does not understand, so a Quark construct never breaks the CSS around it. Nested rules, `&`, custom properties and `/* */` comments are CSS's own — Quark inherits them rather than inventing them. On top of CSS it adds `$variables`, expressions, `#{…}` interpolation inside strings, and its own at-rules: `@use`, `@scope`, `@on`, `@dispatch` / `@command`, `@view-transition`, `@delay`, `@warn` / `@debug` / `@error`. It is a derivative, not a superset: those at-rules are the only ones the language has, and every other one — `@media`, `@keyframes`, `@supports`, SCSS's `@if` / `@mixin` and the rest — is a parse error, as are `%placeholder` selectors, `#{…}` outside a string, `!important` / `!default` / `!global`, and nested property blocks.
4
+
5
+ ## Essentials
6
+
7
+ The full grammar (EBNF, precedence, disambiguation rules) is the `quark-parser` package's *Language reference*; the essentials:
8
+
9
+ - **Rules** `selector { … }` nest. A nested selector is a descendant of its parent unless it uses `&` (`&[open]`, `&-active`). Lists use `,`; combinators are whitespace, `>`, `+`, `~`.
10
+ - **Declarations** `key: value;` — the `;` is optional before `}`. Keys are attribute names, `$variables`, `--css-vars`, `content`, `class`, `dataset`, `ariaset`.
11
+ - **At-rules** `@use "url" as *;` imports; `@on click, change (options) { … }` wires listeners inside a rule (a comma list of event names, an optional `(options)` map, then a block applied once per event, or just the options — JS callouts are `handle: fn` in the map); `@dispatch` / `@command` send events from an `@on` block; `@scope { … }` anchors rules to the host.
12
+ - **Literals** `"strings"` / `'strings'` with `\` escapes and `#{$interpolation}`; numbers `42`, `1.5`, `10px` (a unit makes it a string); `#ccc` colors (strings); `true`, `false`, `null`. Bare words are value keywords, `@use` exports, or built-ins.
13
+ - **Operators**, loosest to tightest: `or` · `and` · `not` · `==` `!=` · `<` `>` `<=` `>=` · `+` `-` · `*` `/` `%` · unary `-` `+` · `.` `[…]` `(…)`. Parentheses group.
14
+ - **Accessors and calls** `$obj.field`, `$list[0]`, `$obj["key"]`, `ns.$var`; methods `item.name.trim()`; calls `fn($a, $b)`, named `fn($opt: 1)`, spread `fn($args...)`. Only bare names and member chains are callable, not `$variables`.
15
+ - **Conditionals** `if($cond: a; $other: b; else: c)` or `ternary($cond, a, b)`.
16
+ - **Lists and maps** `1, 2, 3` and `1px solid red` both evaluate to arrays; `(a: 1, b: 2)` is a map. Spacing around a sign matters: `$x +1` is a two-item list, `$x + 1` / `$x+1` add.
17
+ - **Comments** `/* … */` only, anywhere whitespace is allowed. `//` is not a comment: a sheet has to stay tokenizable by a CSS engine, and CSS tokenizers read `//` as text, so a `//` line is a parse error.
18
+ - **Not supported** `? :`, `?.`, `??`, `===`, `||`, `&&`, arrow functions — parse errors by design.
19
+
20
+ ## Example
21
+
22
+ ```quark
23
+ /* a comment */
24
+ [data-cart] {
25
+ $items: prop("provision").items;
26
+ $total: $items.length or 0;
27
+ data-is-empty: $total == 0; /* a boolean writes "" or removes the attribute */
28
+ [bind-summary] { content: if($total == 1: "1 item"; else: "#{$total} items"); }
29
+ &[data-is-empty] [bind-summary] { content: "Empty"; }
30
+ &[data-is-empty] button[data-action="clear"] { disabled: ""; }
31
+ &:not([data-is-empty]) button[data-action="clear"] { disabled: none; }
32
+ button { @on click (prevent-default, handle: clearCart($items)); }
33
+ }
34
+ ```
35
+
36
+ How each declaration key is interpreted: [Declaration kinds](./DECLARATIONS.md). How values evaluate: [Expressions](./EXPRESSIONS.md).
@@ -0,0 +1,24 @@
1
+ # @use
2
+
3
+ Import JS modules directly in the sheet; pure functions are the sanctioned way for logic to enter a sheet.
4
+
5
+ ## Importing
6
+
7
+ `as *` exposes exports bare; `as name` (or the name derived from the url) namespaces them:
8
+
9
+ ```quark
10
+ @use "/helpers.js" as *;
11
+ @use "/api-client.js" as api;
12
+
13
+ #out {
14
+ content: formatPrice(api.getAmount());
15
+ }
16
+ ```
17
+
18
+ Imports start as soon as the sheet is parsed and load in parallel; the first rule run waits until they resolve. A failed import is logged and skipped. A `with (…)` clause is a parse error.
19
+
20
+ `quark:` URLs import Quark's own helpers without a fetch — `@use "quark:math";` derives the namespace `math` — see [Built-in modules](./MODULES.md). Reach for them before writing a module function of your own.
21
+
22
+ ## Writing module functions
23
+
24
+ Exports are plain functions called from expressions (`formatPrice($amount)`), as `@on` handlers (`@on click (handle: addToCart)`, or a call evaluated per event: `@on click (handle: addToCart(element, event))`), or handed the matched node through `element`. Prefer pure functions; a function that needs to write State does it through [`element.quark`](./JS_WRITES.md) or by dispatching an event an `@on` block turns into attributes.
@@ -0,0 +1,27 @@
1
+ # Values & keywords
2
+
3
+ Three bare words steer what a write does — remove, leave alone, or fall through — and every expression result maps onto one of them.
4
+
5
+ ## Keywords
6
+
7
+ Bare words with a fixed meaning in every expression. *Generated.*
8
+
9
+ <!-- generated:value-keywords -->
10
+ | Keyword | Meaning |
11
+ | --- | --- |
12
+ | `none` | `null`: removes the attribute / CSS variable, clears content, or stores `null` in a `$variable`. |
13
+ | `preserve` | Explicit no-op: leaves the attribute / content / binding exactly as it is. Idiom for loading states: `content: $todo.title or preserve`. |
14
+ | `unset` | `$variables` only: deletes the binding from the matched element so consumers fall through to the next ancestor. On any other target it degrades to a wipe. |
15
+ <!-- /generated -->
16
+
17
+ ## Wipes and no-ops
18
+
19
+ - `none` → `null`: removes the target attribute / clears content.
20
+ - An expression resolving to `undefined` or `null` also **wipes** its target (a matched declaration that resolves to nothing clears what it manages).
21
+ - `preserve`: explicit no-op — leaves the current attribute / content / binding untouched. Handy for loading states: `content: $todo.title or preserve;`
22
+ - `unset`: variables only — deletes the binding (falls through to ancestors).
23
+ - A failed expression evaluation never wipes; it logs and no-ops — the Orchestrator never destroys state by mistake.
24
+
25
+ ## Literals
26
+
27
+ Unitless numbers are numbers; a number with a unit (`10px`, `50%`) and a color (`#ccc`) evaluate to strings; `true`, `false`, `null` are themselves. Strings unescape `\n`, `\t`, `\r`, and `\x` → `x`; interpolated strings (`"Hello #{$name}"`) and `url(…)` join their parts, with `null` / `undefined` parts rendered empty. Lists (space or comma separated) evaluate to arrays; maps (`(name: "Ada")`) to objects — see [Expressions](./EXPRESSIONS.md).
@@ -0,0 +1,47 @@
1
+ # Variables
2
+
3
+ `$variables` behave like CSS custom properties: stored on the elements a rule matches, resolved up the DOM, shared across sheets.
4
+
5
+ ## Declaring and reading
6
+
7
+ ```quark
8
+ main {
9
+ $label: "Ready";
10
+ $count: twice(21);
11
+ [bind-label] { content: $label; }
12
+ [bind-count] { content: $count; }
13
+ }
14
+ ```
15
+
16
+ A `$variable` declaration stores its value on each element the rule matches, and a consumer resolves it by walking up from its own element to the nearest ancestor (self included) that holds the binding. They cascade across sheets — a sheet can read a binding another sheet set on an ancestor. Nesting rules is still good style, but a sibling rule's (or another sheet's) variable resolves fine as long as the binding lives on a DOM ancestor of the consumer:
17
+
18
+ ```quark
19
+ main { $theme: "dark"; }
20
+ [bind-theme] { content: $theme; } /* resolves if inside <main> */
21
+ ```
22
+
23
+ ## Shadowing and namespaces
24
+
25
+ When bindings shadow, the nearest DOM ancestor wins (like CSS inheritance). Because storage is shared per element, same-named writers collide (last writer wins) — namespace app bindings (e.g. `$app-theme`) to avoid clashes.
26
+
27
+ ## `unset`
28
+
29
+ Use `unset` to delete a binding from the matched elements so consumers fall through to the next ancestor — here the open `<details>` shadows the host's binding, and closing it falls back:
30
+
31
+ <include-content data-demo="unset"></include-content>
32
+
33
+ ## Raising state
34
+
35
+ A `$variable` declared on a descendant *shadows* the ancestor's; it does not update it. To change an owner's binding from below, write it **on the owner**: an `@on` block on the owner rule, with `target:` delegation when the interaction happens in a descendant:
36
+
37
+ ```quark
38
+ :scope {
39
+ $count: 0;
40
+ [bind-count] { content: $count; }
41
+ @on click (target: "button") { $count: $count + 1; }
42
+ }
43
+ ```
44
+
45
+ A row that raises state reflects a dash-named attribute the owner can read: `ul { @on click (target: "li[data-id]") { $selected: target.getAttribute("data-id"); } }`.
46
+
47
+ Writing a binding from JavaScript: [Writing from JS](./JS_WRITES.md).
@@ -0,0 +1,64 @@
1
+ # @view-transition
2
+
3
+ `@view-transition [(options)] { … }` commits the writes inside it inside `document.startViewTransition()`, so CSS can animate what changed — rows that leave included, which plain CSS transitions cannot reach.
4
+
5
+ ## A paint policy
6
+
7
+ The block decides *how* its declarations and its nested rules' writes land, never *when* rules run.
8
+
9
+ ```quark
10
+ provider-fetch[is-success] {
11
+ $todos: prop("provision").body;
12
+ @view-transition (types: "todo-change") {
13
+ ul { content: iterate($todos, none, "id"); }
14
+ [bind-count] { content: $todos.length; }
15
+ }
16
+ }
17
+ ```
18
+
19
+ ```css
20
+ ul { view-transition-name: todos; }
21
+ li { view-transition-name: match-element; view-transition-class: todo; }
22
+ ::view-transition-new(.todo):only-child { animation: todo-in 250ms; }
23
+ ::view-transition-old(.todo):only-child { animation: todo-out 200ms; }
24
+ :root:active-view-transition-type(todo-change) ::view-transition-old(root) { animation: none; }
25
+ ```
26
+
27
+ <include-content data-demo="view-transition"></include-content>
28
+
29
+ - **One tick, one cut.** Every Quark write of the same tick lands in the same cut, and writes that react to those writes land in it too as long as Quark settles within `timeout`. Writes more than a tick later are a separate fact and a separate transition.
30
+ - **Put the writes of one cut inside the block** (the list, its counter, the empty state). Only a write inside a block starts a transition; writes outside it join the cut when they happen meanwhile. `$variables` do not paint, so a block holding only `$variable` writes never starts one.
31
+ - **Keyed `iterate()` rows persist**, so named rows move instead of leaving and entering again.
32
+ - **Styling is CSS's job.** Name elements (`view-transition-name`, `match-element`, `view-transition-class`) and target `::view-transition-group` / `-old` / `-new` and `:active-view-transition-type()`. While the animation runs, captured elements are drawn from those pseudo-elements, so real-DOM styles only show on elements that are not captured. A name that comes from State is one CSS variable away: `li { --vt-name: "row-#{item.id}"; }` in the sheet, `li { view-transition-name: var(--vt-name); }` in the CSS.
33
+ - **Options belong to the block.** They are evaluated per write, on the block's element — what the rule it sits in matches, or the host for a sheet-level block — so `attr()`, `prop()` and `item` read that element.
34
+
35
+ ## Options
36
+
37
+ | Option | Effect |
38
+ | --- | --- |
39
+ | `types: "a b"` | Names for `:active-view-transition-type()`: a string (space-separated) or a list; `"todo-#{$op}"` interpolates. The types of every write in one transition are combined. |
40
+ | `timeout: <ms>` | How long the transition waits for Quark to settle before the new state is captured. Default 300, or 1000 with `until`. On expiry it captures what is there and warns once per block. |
41
+ | `delay: <ms>` | Hold these writes back first, then commit them in their own transition. Other writes of the same tick are not held and land first. |
42
+ | `first-render` | Also animate the sheet's first render (off by default, like `spa-manager`'s `transition-first-render`). |
43
+ | `if-active: skip` / `replace` | While another view transition runs (a route change, or Quark's own still animating): commit unanimated (`skip`, the default) or start anyway, which ends the running one (`replace`, the browser's own behavior). |
44
+ | `until: "<selector>"` / `until: <promise>` | Keep the transition open until the block's element matches the selector (`"[is-success], [is-error]"`, `":not([is-loading])"`, `":has(li)"`) or the promise settles, capped by `timeout`. |
45
+
46
+ ## `until`
47
+
48
+ **`until` is for short waits.** The page stays frozen on the old state while the transition is open, so wait for a template or view that is about to render, never for a data fetch — animate a fetch as two cuts (`[is-loading]`, then `[is-success]`) instead. Put the block on the element that owns the fact and nest the writes; a block on a descendant waiting for an ancestor's attribute only times out:
49
+
50
+ ```quark
51
+ provider-fetch[is-loading] {
52
+ @view-transition (until: "[is-success], [is-error]", timeout: 800) {
53
+ ul { content: none; }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## When no transition runs
59
+
60
+ Committed without a transition: writes that change nothing, the sheet's first render, `prefers-reduced-motion: reduce`, a hidden document, browsers without the API (the writes land as usual) and, under `if-active: skip`, writes while another transition is active. A document runs one view transition at a time; scoped (per-element) transitions are not available yet.
61
+
62
+ ## Costs
63
+
64
+ Each transition snapshots the page once, its writes land one rendering opportunity later than without the block, and pointer input goes to the transition overlay while it animates — keep animations short. It is the one place Quark stops being "write and forget": a State write waits for a rendering opportunity.
@@ -0,0 +1,65 @@
1
+ {
2
+ "sections": [
3
+ {
4
+ "id": "syntax",
5
+ "title": "Syntax",
6
+ "docs": [
7
+ "sheets",
8
+ "syntax"
9
+ ]
10
+ },
11
+ {
12
+ "id": "selectors",
13
+ "title": "Selectors",
14
+ "docs": [
15
+ "selectors",
16
+ "reactivity",
17
+ "no_reversion"
18
+ ]
19
+ },
20
+ {
21
+ "id": "declarations",
22
+ "title": "Declarations",
23
+ "docs": [
24
+ "declarations",
25
+ "attributes",
26
+ "content",
27
+ "css_variables"
28
+ ]
29
+ },
30
+ {
31
+ "id": "values",
32
+ "title": "Values & Expressions",
33
+ "docs": [
34
+ "variables",
35
+ "js_writes",
36
+ "values",
37
+ "expressions",
38
+ "builtins",
39
+ "modules",
40
+ "methods",
41
+ "element_properties"
42
+ ]
43
+ },
44
+ {
45
+ "id": "at-rules",
46
+ "title": "At-Rules",
47
+ "docs": [
48
+ "at_rules",
49
+ "use",
50
+ "on",
51
+ "dispatch",
52
+ "view_transition"
53
+ ]
54
+ },
55
+ {
56
+ "id": "runtime",
57
+ "title": "Runtime",
58
+ "docs": [
59
+ "js_api",
60
+ "loop_guard",
61
+ "limitations"
62
+ ]
63
+ }
64
+ ]
65
+ }