@quillmark/svelte 0.0.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 (143) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +58 -0
  3. package/README.md +241 -0
  4. package/THEMING.md +153 -0
  5. package/dist/core/address.d.ts +56 -0
  6. package/dist/core/address.js +95 -0
  7. package/dist/core/bloom.d.ts +37 -0
  8. package/dist/core/bloom.js +116 -0
  9. package/dist/core/codec/anchor.d.ts +19 -0
  10. package/dist/core/codec/anchor.js +36 -0
  11. package/dist/core/codec/code.d.ts +12 -0
  12. package/dist/core/codec/code.js +118 -0
  13. package/dist/core/codec/decode.d.ts +24 -0
  14. package/dist/core/codec/decode.js +270 -0
  15. package/dist/core/codec/encode.d.ts +71 -0
  16. package/dist/core/codec/encode.js +480 -0
  17. package/dist/core/codec/field.d.ts +171 -0
  18. package/dist/core/codec/field.js +493 -0
  19. package/dist/core/codec/index.d.ts +22 -0
  20. package/dist/core/codec/index.js +43 -0
  21. package/dist/core/codec/inputrules.d.ts +7 -0
  22. package/dist/core/codec/inputrules.js +168 -0
  23. package/dist/core/codec/islands.d.ts +36 -0
  24. package/dist/core/codec/islands.js +82 -0
  25. package/dist/core/codec/keymap.d.ts +13 -0
  26. package/dist/core/codec/keymap.js +29 -0
  27. package/dist/core/codec/lists.d.ts +22 -0
  28. package/dist/core/codec/lists.js +104 -0
  29. package/dist/core/codec/marks.d.ts +37 -0
  30. package/dist/core/codec/marks.js +74 -0
  31. package/dist/core/codec/positions.d.ts +17 -0
  32. package/dist/core/codec/positions.js +108 -0
  33. package/dist/core/codec/prose.css +458 -0
  34. package/dist/core/codec/reconcile.d.ts +26 -0
  35. package/dist/core/codec/reconcile.js +64 -0
  36. package/dist/core/codec/schema.d.ts +13 -0
  37. package/dist/core/codec/schema.js +155 -0
  38. package/dist/core/codec/slash.d.ts +79 -0
  39. package/dist/core/codec/slash.js +0 -0
  40. package/dist/core/codec/table-view.d.ts +50 -0
  41. package/dist/core/codec/table-view.js +928 -0
  42. package/dist/core/codec/table.d.ts +85 -0
  43. package/dist/core/codec/table.js +224 -0
  44. package/dist/core/errors.d.ts +73 -0
  45. package/dist/core/errors.js +24 -0
  46. package/dist/core/index.d.ts +5 -0
  47. package/dist/core/index.js +19 -0
  48. package/dist/core/lifecycle.d.ts +7 -0
  49. package/dist/core/lifecycle.js +21 -0
  50. package/dist/core/rebind.svelte.d.ts +18 -0
  51. package/dist/core/rebind.svelte.js +44 -0
  52. package/dist/core/teardown.d.ts +20 -0
  53. package/dist/core/teardown.js +37 -0
  54. package/dist/core/theme.css +276 -0
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.js +9 -0
  57. package/dist/preset/index.css +10 -0
  58. package/dist/preset/recipes.css +188 -0
  59. package/dist/preset/scale.css +97 -0
  60. package/dist/preview/Preview.svelte +118 -0
  61. package/dist/preview/Preview.svelte.d.ts +39 -0
  62. package/dist/preview/bridge.d.ts +13 -0
  63. package/dist/preview/bridge.js +88 -0
  64. package/dist/preview/controller.d.ts +40 -0
  65. package/dist/preview/controller.js +141 -0
  66. package/dist/preview/geometry.d.ts +37 -0
  67. package/dist/preview/geometry.js +45 -0
  68. package/dist/preview/index.d.ts +5 -0
  69. package/dist/preview/index.js +14 -0
  70. package/dist/preview/overlay.d.ts +10 -0
  71. package/dist/preview/overlay.js +82 -0
  72. package/dist/preview/paint.d.ts +17 -0
  73. package/dist/preview/paint.js +262 -0
  74. package/dist/preview/strings.d.ts +14 -0
  75. package/dist/preview/strings.js +22 -0
  76. package/dist/visual/ArrayField.svelte +362 -0
  77. package/dist/visual/ArrayField.svelte.d.ts +24 -0
  78. package/dist/visual/BooleanField.svelte +88 -0
  79. package/dist/visual/BooleanField.svelte.d.ts +20 -0
  80. package/dist/visual/Card.svelte +849 -0
  81. package/dist/visual/Card.svelte.d.ts +43 -0
  82. package/dist/visual/CardControls.svelte +92 -0
  83. package/dist/visual/CardControls.svelte.d.ts +11 -0
  84. package/dist/visual/DateField.svelte +203 -0
  85. package/dist/visual/DateField.svelte.d.ts +28 -0
  86. package/dist/visual/DiagnosticList.svelte +50 -0
  87. package/dist/visual/DiagnosticList.svelte.d.ts +7 -0
  88. package/dist/visual/EnumField.svelte +169 -0
  89. package/dist/visual/EnumField.svelte.d.ts +25 -0
  90. package/dist/visual/Field.svelte +270 -0
  91. package/dist/visual/Field.svelte.d.ts +37 -0
  92. package/dist/visual/FieldHint.svelte +185 -0
  93. package/dist/visual/FieldHint.svelte.d.ts +13 -0
  94. package/dist/visual/FieldLabel.svelte +141 -0
  95. package/dist/visual/FieldLabel.svelte.d.ts +19 -0
  96. package/dist/visual/FormatPopover.svelte +396 -0
  97. package/dist/visual/FormatPopover.svelte.d.ts +9 -0
  98. package/dist/visual/NumberField.svelte +72 -0
  99. package/dist/visual/NumberField.svelte.d.ts +19 -0
  100. package/dist/visual/ObjectField.svelte +131 -0
  101. package/dist/visual/ObjectField.svelte.d.ts +17 -0
  102. package/dist/visual/ProseArrayElement.svelte +98 -0
  103. package/dist/visual/ProseArrayElement.svelte.d.ts +19 -0
  104. package/dist/visual/ProseField.svelte +213 -0
  105. package/dist/visual/ProseField.svelte.d.ts +50 -0
  106. package/dist/visual/SlashMenu.svelte +126 -0
  107. package/dist/visual/SlashMenu.svelte.d.ts +14 -0
  108. package/dist/visual/TextField.svelte +63 -0
  109. package/dist/visual/TextField.svelte.d.ts +23 -0
  110. package/dist/visual/TipsCard.svelte +191 -0
  111. package/dist/visual/TipsCard.svelte.d.ts +13 -0
  112. package/dist/visual/VisualEditor.svelte +96 -0
  113. package/dist/visual/VisualEditor.svelte.d.ts +14 -0
  114. package/dist/visual/VisualEditorInner.svelte +1032 -0
  115. package/dist/visual/VisualEditorInner.svelte.d.ts +17 -0
  116. package/dist/visual/controls.css +272 -0
  117. package/dist/visual/diagnostics.d.ts +52 -0
  118. package/dist/visual/diagnostics.js +83 -0
  119. package/dist/visual/domid.d.ts +21 -0
  120. package/dist/visual/domid.js +36 -0
  121. package/dist/visual/ext.d.ts +19 -0
  122. package/dist/visual/ext.js +29 -0
  123. package/dist/visual/icons/Icon.svelte +39 -0
  124. package/dist/visual/icons/Icon.svelte.d.ts +12 -0
  125. package/dist/visual/icons/nodes.d.ts +103 -0
  126. package/dist/visual/icons/nodes.js +55 -0
  127. package/dist/visual/index.d.ts +9 -0
  128. package/dist/visual/index.js +24 -0
  129. package/dist/visual/motion.d.ts +18 -0
  130. package/dist/visual/motion.js +53 -0
  131. package/dist/visual/props.d.ts +77 -0
  132. package/dist/visual/props.js +1 -0
  133. package/dist/visual/signals.d.ts +59 -0
  134. package/dist/visual/signals.js +1 -0
  135. package/dist/visual/strings.d.ts +123 -0
  136. package/dist/visual/strings.js +80 -0
  137. package/dist/visual/structure.d.ts +224 -0
  138. package/dist/visual/structure.js +270 -0
  139. package/dist/visual/synced.svelte.d.ts +15 -0
  140. package/dist/visual/synced.svelte.js +34 -0
  141. package/dist/visual/tips.d.ts +24 -0
  142. package/dist/visual/tips.js +50 -0
  143. package/package.json +102 -0
@@ -0,0 +1,276 @@
1
+ /* The one derivation: public dials in, a closed private scale out (THEMING.md).
2
+ Everything below reads a rung rather than minting a value, which is this file's
3
+ whole reason for existing and what `check:style` holds everywhere else. Colour,
4
+ geometry, type and motion are all derived here, so each is minted in ONE place
5
+ instead of once per detached root.
6
+
7
+ TRANSPORT: a stylesheet, side-effect imported by `core/index.ts` and applied to
8
+ every element marked `data-qm-root`. The package imports it itself, so there is
9
+ no `import './theme.css'` for a consumer to forget. A stylesheet rather than an
10
+ inline `style` attribute is what buys the three things an attribute cannot
11
+ carry: a cascade layer consumer CSS beats without `!important`, real properties
12
+ on the root rule (`font-family`) that every root then inherits, and a free
13
+ `style` attribute for the consumer.
14
+
15
+ Every value stays a `var()` reference, so the dials resolve against the CASCADE
16
+ at each root: a consumer's `[data-theme="dark"]` ancestor rule flows in
17
+ unchanged, the light/dark default follows the `color-scheme` the host declares
18
+ with no JS and no media query, and `color-mix` runs at paint time. The rungs are
19
+ declared ON the root, so they are reachable only by a rule targeting it:
20
+ THEMING.md §"What is deliberately not public" states the terms.
21
+
22
+ LAYERS: `qm` is the top-level layer name, and it is contract — a consumer
23
+ pins the order against it with `@layer qm, app;` in their own CSS, which wins
24
+ in both bundler orders (THEMING §"Your CSS beats ours"). Unlayered consumer
25
+ CSS beats every layer here for free.
26
+
27
+ `@property` registers the three ENTRY rungs, the ones reading a dial, and
28
+ nothing below them. An `initial-value` must be computationally independent,
29
+ which `rem` is not, so the DIALS cannot be registered: their defaults are
30
+ `rem`. A rung can, because registration reads the initial value and never the
31
+ declared one, so a `px` initial sits under a `calc()` chain over a `rem` dial
32
+ without contradiction. What it buys is the poison case: `--qm-space: 4` is a
33
+ valid custom property and an invalid length, and unguarded it substitutes
34
+ through every `calc()` below and collapses the surface to zero. Registered,
35
+ the entry rung is invalid at computed-value time and lands on its initial
36
+ instead, so the chain under it computes from a length and the surface holds at
37
+ its default. The derived rungs need no registration of their own: they read a
38
+ computed length either way. The initials are the `rem` defaults against a 16px
39
+ root, so a host on another root size falls back to the package's figure rather
40
+ than its own — reachable only once a dial is already invalid. Where
41
+ `@property` is unsupported the at-rule is skipped and the collapse returns,
42
+ which is the unguarded behaviour rather than a new one. */
43
+
44
+ @property --_qm-space {
45
+ syntax: '<length>';
46
+ inherits: true;
47
+ initial-value: 4px;
48
+ }
49
+ @property --_qm-radius {
50
+ syntax: '<length>';
51
+ inherits: true;
52
+ initial-value: 8px;
53
+ }
54
+ @property --_qm-text-body {
55
+ syntax: '<length>';
56
+ inherits: true;
57
+ initial-value: 14px;
58
+ }
59
+
60
+ @layer qm.scale, qm.chrome;
61
+
62
+ @layer qm.scale {
63
+ :where([data-qm-root]) {
64
+ /* ── Poles ───────────────────────────────────────────────────────────────
65
+ Each dial's default, stated ONCE. Every rung below reads a pole, never a
66
+ dial, so both schemes cost two declarations rather than fifteen; and the
67
+ dial sits in FRONT of the default, so a consumer's ancestor value wins in
68
+ either scheme, which it would not if a scheme rule set `--qm-bg` instead.
69
+
70
+ `light-dark()` keys the default off the `color-scheme` this root INHERITS,
71
+ which is the host's: an app that declares `dark` gets a dark surface
72
+ whatever the OS says, and one that declares nothing gets light, matching
73
+ its own unstyled text. So the package owns no mode logic at all: light or
74
+ dark is the mounting site's, like every other value here (THEMING.md
75
+ §"The surface follows your colour scheme"). */
76
+ --_qm-bg: var(--qm-bg, light-dark(#fff, #14171c));
77
+ --_qm-fg: var(--qm-fg, light-dark(#1a1a1a, #e8eaed));
78
+ --_qm-accent: var(--qm-accent, #2563eb);
79
+ --_qm-danger: var(--qm-danger, #c5221f);
80
+ --_qm-warning: var(--qm-warning, #b25000);
81
+ --_qm-font: var(--qm-font, ui-sans-serif, system-ui, sans-serif);
82
+ --_qm-font-mono: var(--qm-font-mono, ui-monospace, monospace);
83
+
84
+ /* ── Colour ──────────────────────────────────────────────────────────────
85
+ Surfaces step bg → fg, ink steps fg → bg, mixed in oklab; sRGB muddies
86
+ the mid-tones and the label/meta/border rungs are exactly mid-tone. So
87
+ inverting the poles inverts the scale. The percentages are calibrated to
88
+ the light-mode literals they stand in for (17% ≈ #d4d4d4, 35% ≈ #555).
89
+
90
+ The three surface rungs sit EVENLY 4pp apart, which is what keeps them three
91
+ distinguishable steps: uneven spacing spends one rung to buy another:
92
+ widening `raised` alone would leave `hover` 1pp above it. `raised` steps
93
+ toward the INK pole rather than away from it, so a card is darker than the
94
+ page in light and lighter in dark. */
95
+ --_qm-surface: var(--_qm-bg);
96
+ --_qm-surface-raised: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 4%);
97
+ --_qm-surface-hover: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 8%);
98
+ --_qm-border: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 17%);
99
+ /* A rule that separates without structuring: the lines BETWEEN a table's cells,
100
+ where the frame around them is the structure. Half the border's mix rather
101
+ than a fourth surface rung, because it is a stroke and reads against the
102
+ surface behind it, not a fill stacked on one. */
103
+ --_qm-border-faint: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 9%);
104
+ --_qm-ink: var(--_qm-fg);
105
+ --_qm-ink-label: color-mix(in oklab, var(--_qm-fg), var(--_qm-bg) 35%);
106
+ --_qm-ink-meta: color-mix(in oklab, var(--_qm-fg), var(--_qm-bg) 45%);
107
+ --_qm-ink-ghost: color-mix(in oklab, var(--_qm-fg), var(--_qm-bg) 60%);
108
+ /* The correlation wash (`core/bloom.ts`). Its alpha is a PEAK, not a tint: the
109
+ wash animates from and back to zero, never held, so it reads at pane scale
110
+ over the preview's paper while the paper rests clean. */
111
+ --_qm-accent-wash: color-mix(in oklab, var(--_qm-accent) 18%, transparent);
112
+
113
+ /* ── Ratios and effects ──────────────────────────────────────────────────
114
+ No palette fixes these, so they are their own rungs. There is no shadow
115
+ rung: elevation is a tone rung and a hairline, which say the same thing at
116
+ both poles, where a shadow's offset states a light source the poles do not
117
+ carry. `check:style` forbids the property outright,
118
+ here as well as over the surfaces, so the absence is gated rather than
119
+ remembered. */
120
+ --_qm-blur: 8px;
121
+ /* The popover's translucency: the raised surface mixed toward transparent, so
122
+ the page reads faintly through the pill. */
123
+ --_qm-surface-popover: color-mix(in oklab, var(--_qm-surface-raised) 82%, transparent);
124
+ /* The recede ladder: a hover-revealed or disabled affordance at rest, and a
125
+ muted one. Opacity recedes toward whatever is behind, so these invert
126
+ correctly given the element's own colour is a rung. */
127
+ --_qm-opacity-idle: 0.35;
128
+ --_qm-opacity-muted: 0.5;
129
+
130
+ /* ── Focus ───────────────────────────────────────────────────────────────
131
+ These fix the ring's VALUE, so no control mints it. Which properties draw a
132
+ ring, and on what state, is the other half: a rule, `.qm-focus-ring` in
133
+ `visual/controls.css`, since a rung each control reassembles is a scale
134
+ only up to the point where it is assembled. ONE width: focus is the only
135
+ state that draws a ring anywhere, so there is no held-back tier under it. */
136
+ --_qm-ring-width: 2px;
137
+ --_qm-ring-focus: var(--_qm-ring-width) solid var(--_qm-accent);
138
+ --_qm-ring-offset: 1px;
139
+
140
+ /* ── Motion ──────────────────────────────────────────────────────────────
141
+ Two rungs, by what the trip disturbs: `fast` stays inside the element's own
142
+ box, `slow` moves the layout around it. */
143
+ --_qm-duration-fast: 120ms;
144
+ --_qm-duration-slow: 200ms;
145
+ /* The bloom's whole rise-hold-decay, and deliberately not a third rung. A
146
+ custom property rather than a constant in `core/bloom.ts` because that module
147
+ animates over WAAPI and reads it from here. */
148
+ --_qm-bloom-dwell: 1100ms;
149
+
150
+ /* One curve per role, the role being what the trip's two endpoints ARE.
151
+ `reverse` is the only one that has to be symmetric: it is the only one an
152
+ interruption turns around. */
153
+ --_qm-ease-reverse: cubic-bezier(0.4, 0, 0.6, 1);
154
+ --_qm-ease-arrive: cubic-bezier(0, 0, 0.2, 1);
155
+ --_qm-ease-depart: cubic-bezier(0.4, 0, 1, 1);
156
+
157
+ /* ── Geometry ──────────────────────────────────────────────────────────*/
158
+ /* ONE stroke width for every edge the chrome draws: a card's, a control's,
159
+ and the metadata bracket's three rules alike. A threshold rather than a
160
+ rhythm choice, like the tap floor: a hairline is a hairline at any
161
+ `--qm-space`, so it does not derive from a dial. It exists as a rung because
162
+ the bracket's whole claim is that its vertical and its horizontals are the
163
+ same stroke, and `check:style`'s border-width axis can only hold that
164
+ against a token. */
165
+ --_qm-border-width: 1px;
166
+ --_qm-radius: var(--qm-radius, 8px);
167
+ --_qm-radius-inner: calc(var(--_qm-radius) / 2);
168
+ /* A fully-rounded end cap: a shape tier, not a step on the radius ramp, so
169
+ it does not derive from the dial: a pill stays a pill at any radius. */
170
+ --_qm-radius-pill: 999px;
171
+ --_qm-space: var(--qm-space, 0.25rem);
172
+ --_qm-space-half: calc(var(--_qm-space) / 2);
173
+ --_qm-space-2: calc(var(--_qm-space) * 2);
174
+ --_qm-space-3: calc(var(--_qm-space) * 3);
175
+ --_qm-space-4: calc(var(--_qm-space) * 4);
176
+ /* The scroll TAIL: dead space under the last card, so it can be read at the
177
+ middle of a pane rather than at its bottom edge. A fraction of the VIEWPORT
178
+ rather than a step on the ramp, because what it answers is how far the eye
179
+ sits from the bottom of the screen, which no spacing dial knows. Spent by
180
+ the editor alone (`.qm-pane`): a stack of cards is what has a last one to
181
+ reach, where a page and a text mirror end where the document ends. */
182
+ --_qm-tail: 40vh;
183
+ /* WCAG 2.5.8's 24x24 pointer-target floor. A rung rather than a sixth step on
184
+ the space scale: it is a threshold the spec fixes, not a rhythm choice, so
185
+ it does not move when `--qm-space` does. Both button families read it, so the
186
+ floor is one number instead of one expression per control. */
187
+ --_qm-tap-min: 24px;
188
+ /* The reorder/delete glyph. A rung because the card header's
189
+ right hang is HALF the difference between this and the tap floor: the glyph
190
+ centres in a target sized by the floor, so what stands between it and the
191
+ card's gutter is an arithmetic of the two, and a literal on either side is a
192
+ balance that silently breaks when the glyph moves. Sized in CSS rather than
193
+ through the icon's `size`, so the hang and the glyph read one number. Scoped to
194
+ the control pair: the hint and the format marks take their own sizes, and a
195
+ rung claiming to be every glyph would be false at three of them. */
196
+ --_qm-glyph-control: 14px;
197
+
198
+ /* ── Type ────────────────────────────────────────────────────────────────
199
+ `--qm-font-size` anchors the body rung; 1.125 is the ratio between adjacent
200
+ rungs: a step up (title) and two down (label, meta). The ratio and the
201
+ weights are fixed conventions, not dials: no consumer tunes a typographic
202
+ ratio, and a dial nobody turns is a name every reader still holds. */
203
+ --_qm-text-body: var(--qm-font-size, 0.875rem);
204
+ --_qm-text-title: calc(var(--_qm-text-body) * 1.125);
205
+ --_qm-text-label: calc(var(--_qm-text-body) / 1.125);
206
+ --_qm-text-meta: calc(var(--_qm-text-label) / 1.125);
207
+ --_qm-weight-label: 600;
208
+ --_qm-weight-soft: 500;
209
+ /* Leading: the ramp's third axis, and it does not track the sizes one for one:
210
+ two rungs, because a wrapped label and a wrapped paragraph want different
211
+ rhythms and nothing wants four. `body` is reading rhythm (both prose leaves,
212
+ every control beside them, the tips card's guidance); `tight` is a line that is
213
+ a label rather than a passage. Unitless, so a rung inherits MULTIPLICATIVELY
214
+ and holds against whichever size rung the surface reads: a `rem` leading
215
+ would freeze at one size and disagree with the rest of the ramp. Leading is
216
+ also the WHOLE of what separates paper from chrome: the body leaf reads
217
+ `--_qm-text-body`, the same size as the input beside it, and there is no fifth
218
+ size rung over it. `line-height: 1` is outside the axis: it collapses the
219
+ line box onto its content, a glyph or a button's one-line label. */
220
+ --_qm-leading-body: 1.5;
221
+ --_qm-leading-tight: 1.3;
222
+
223
+ /* ── The baseline every root inherits ────────────────────────────────────
224
+ Real properties, not only custom ones: the thing an inline attribute
225
+ could carry but never did, which is why `--qm-font` reached one root of
226
+ five. `color-scheme` is deliberately absent: it inherits, so the host's
227
+ reaches this root untouched and native chrome (scrollbars, the date
228
+ picker, the caret) lands on the same pole the poles above resolve to.
229
+ Declaring it here would break that lockstep in the one direction that
230
+ matters: a host that declares `dark` would get light native chrome under
231
+ a dark surface. A consumer that PINS a palette against its own scheme
232
+ declares `color-scheme` with it, on the root (THEMING.md).
233
+
234
+ Face, ink and LEADING are the three axes a root carries this way. Leading
235
+ belongs with the other two because a unitless ratio inherits
236
+ MULTIPLICATIVELY: it holds against whatever size rung each surface reads, so
237
+ it imposes less than the ink beside it, and the reason `font-size` stays off
238
+ does not transfer: a ratio has no size to sweep a consumer's content with.
239
+ What it buys is that `normal` (the UA figure near 1.14, varying by font) is
240
+ unreachable under a marker, so a type-bearing surface lands on the ramp
241
+ whether or not it remembers to say so, and a per-surface
242
+ `--_qm-leading-tight` reads as the deliberate override it is.
243
+
244
+ Deliberately NOT `font-size`, and the reason is about the ROOT rule rather
245
+ than about size: a root-level body size sweeps every descendant a consumer
246
+ may have put inside the marker, which is a reach the derivation does not
247
+ have. The rung is not withheld from the surfaces: every control and every
248
+ prose leaf reads `--_qm-text-body` in its own recipe, which is
249
+ what closes the ramp and makes `--qm-font-size` move an input. What would
250
+ break a mounted leaf is a size that CHANGES under it; a rung is fixed at
251
+ mount unless the consumer turns the dial, and a consumer turning a dial is
252
+ entitled to the reflow. So: the root imposes no size, each surface reads the
253
+ rung it wants. */
254
+ font-family: var(--_qm-font);
255
+ color: var(--_qm-ink);
256
+ line-height: var(--_qm-leading-body);
257
+ }
258
+
259
+ /* Reduced motion collapses the SCALE, never a list of selectors.
260
+ Two values here are not the rung they look like:
261
+
262
+ `1ms` rather than `none`, because a collapsed transition still FIRES: a surface
263
+ waiting on `transitionend` keeps its event, where `transition: none` would strand
264
+ it. The beat is under perception either way.
265
+
266
+ `200ms` rather than `1ms` for the dwell, because the bloom's reduced form is a
267
+ hold and a cut (`core/bloom.ts` swaps the ramps out), and a hold is a duration:
268
+ collapsing it deletes the gesture instead of shortening it. */
269
+ @media (prefers-reduced-motion: reduce) {
270
+ :where([data-qm-root]) {
271
+ --_qm-duration-fast: 1ms;
272
+ --_qm-duration-slow: 1ms;
273
+ --_qm-bloom-dwell: 200ms;
274
+ }
275
+ }
276
+ }
@@ -0,0 +1 @@
1
+ export * from './core/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // `@quillmark/svelte` root: the framework-free substrate.
2
+ //
3
+ // The headline surfaces mount from their own subpaths (`@quillmark/svelte/preview`,
4
+ // `/visual`) so a bundler pulls only what an entry reaches; the root
5
+ // re-exports `/core` (the address vocabulary, the error channel, `init`) for a
6
+ // consumer that wants one import for the substrate. It carries no framework code.
7
+ // The `@quillmark/wasm` API is imported from the peer dependency directly, never
8
+ // from here.
9
+ export * from './core/index.js';
@@ -0,0 +1,10 @@
1
+ /* `@quillmark/svelte/preset`, the package's one CSS entry point: the endorsed look for
2
+ the page a surface is mounted on. Opt-in and side-effect free until imported:
3
+ `core/index.ts` pulls `core/theme.css` for the surfaces and never this, because this
4
+ styles the DOCUMENT and nothing may put that in a consumer's page uninvited.
5
+
6
+ Two files because one of them is exempt: `scale.css` is where the literals are
7
+ minted, `recipes.css` is held to reading them. */
8
+
9
+ @import './scale.css';
10
+ @import './recipes.css';
@@ -0,0 +1,188 @@
1
+ /* THE ENDORSED LOOK: the rules a host draws around a mounted surface, as classes it
2
+ puts on its own elements. The rungs beside this in `scale.css` are the invisible
3
+ half (a scale alone still leaves a consumer writing every declaration before
4
+ anything on the page looks like anything), so the recipes are what the preset is
5
+ for. This file mints nothing; `check:style` runs its axes here.
6
+
7
+ These are HOST classes. They land on the consumer's DOM, never on a mounted root, so
8
+ they freeze nothing about the surfaces' interior: the reason the package withholds
9
+ its own internal classes does not reach them.
10
+
11
+ UNLAYERED, deliberately. `@layer qm` exists so consumer CSS beats the surfaces
12
+ without `!important`, and the same has to be true here; a layer of the preset's own
13
+ would put a second name in the contract and change what `revert-layer` at the foot
14
+ of this file resolves against. Unlayered, a consumer's own rules win by ordinary
15
+ precedence and the focus rule works with nothing declared. */
16
+
17
+ /* ── The page ──────────────────────────────────────────────────────────────── */
18
+
19
+ /* The baseline, on a CLASS rather than `body`: a preset that restyled the document
20
+ root would land on an app that already has a page and change it out from under
21
+ whatever was there. The host puts this where its own layout starts: `body`, or the
22
+ element the app mounts into.
23
+
24
+ `color-scheme` is declared HERE and not by the package: light or dark is the
25
+ mounting site's, and this file is the mounting site. `light dark` follows the OS;
26
+ a host pinning one declares it on the same element and wins by source order. */
27
+ .qm-page {
28
+ color-scheme: light dark;
29
+ margin: 0;
30
+ background: var(--qmh-page);
31
+ color: var(--qmh-ink);
32
+ font-family: var(--qmh-font);
33
+ font-size: var(--qmh-text);
34
+ line-height: var(--qmh-leading-body);
35
+ -webkit-font-smoothing: antialiased;
36
+ }
37
+
38
+ /* A column of prose at the measure, and the width only: what sits inside it is the
39
+ caller's, since a passage and a stack of panels want different rhythms. */
40
+ .qm-measure {
41
+ max-width: var(--qmh-measure);
42
+ }
43
+
44
+ /* ── Type ──────────────────────────────────────────────────────────────────── */
45
+
46
+ /* A run of chrome type: a section's label, a panel's caption, the name over a readout.
47
+ Uppercase and tracked, which is what keeps a run this small legible at the ghost
48
+ rung. */
49
+ .qm-label {
50
+ font-family: var(--qmh-font-mono);
51
+ font-size: var(--qmh-text-meta);
52
+ line-height: var(--qmh-leading-tight);
53
+ letter-spacing: var(--qmh-track-label);
54
+ text-transform: uppercase;
55
+ color: var(--qmh-ghost);
56
+ margin: 0;
57
+ }
58
+
59
+ /* A value read back out of the running session: an address, a page index, a count.
60
+ Monospace and tabular, because these are read by comparison: two addresses one
61
+ above the other line up character for character. */
62
+ .qm-readout {
63
+ font-family: var(--qmh-font-mono);
64
+ font-size: var(--qmh-text-label);
65
+ line-height: var(--qmh-leading-tight);
66
+ font-variant-numeric: tabular-nums;
67
+ color: var(--qmh-ink);
68
+ overflow-wrap: anywhere;
69
+ }
70
+
71
+ /* The block form: a dump, or a snippet. `pre` keeps the wrapping the value came with;
72
+ the plate is the page's own, one rung under whatever it sits in. How TALL such a
73
+ block may grow before it scrolls is the host's policy rather than the look, so no
74
+ height is stated here. */
75
+ pre.qm-readout {
76
+ box-sizing: border-box;
77
+ background: var(--qmh-page);
78
+ border: var(--qmh-border-width) solid var(--qmh-border);
79
+ border-radius: var(--qmh-radius-inner);
80
+ padding: var(--qmh-space-2);
81
+ margin: 0;
82
+ white-space: pre-wrap;
83
+ word-break: break-all;
84
+ }
85
+
86
+ /* ── Status ────────────────────────────────────────────────────────────────── */
87
+
88
+ /* The boundary's phase while it is not open. An open session says the rest by painting
89
+ the page, so the states worth a word are the ones before and instead of that. */
90
+ .qm-status {
91
+ font-family: var(--qmh-font-mono);
92
+ font-size: var(--qmh-text-meta);
93
+ line-height: var(--qmh-leading-tight);
94
+ letter-spacing: var(--qmh-track-label);
95
+ text-transform: uppercase;
96
+ color: var(--qmh-ghost);
97
+ margin: 0;
98
+ }
99
+
100
+ /* A failure carries a message rather than a state word, so it drops the label treatment
101
+ and reads as a sentence; and it breaks anywhere, because a WASM error is one long
102
+ token in whatever room the line has left. */
103
+ .qm-status-error {
104
+ color: var(--qmh-alert);
105
+ font-size: var(--qmh-text-label);
106
+ text-transform: none;
107
+ letter-spacing: normal;
108
+ overflow-wrap: anywhere;
109
+ }
110
+
111
+ .qm-status-warn {
112
+ color: var(--qmh-warn);
113
+ }
114
+
115
+ /* ── Surfaces ──────────────────────────────────────────────────────────────── */
116
+
117
+ /* The instrument plate: what a host puts its controls and readouts on. One rung off the
118
+ page and a hairline, which is the same way a card steps off the base surface, so a
119
+ panel beside a mounted editor reads as the same material. `border-box`, so a height
120
+ from the caller is the height it draws. */
121
+ .qm-panel {
122
+ box-sizing: border-box;
123
+ background: var(--qmh-surface);
124
+ border: var(--qmh-border-width) solid var(--qmh-border);
125
+ border-radius: var(--qmh-radius);
126
+ padding: var(--qmh-space-4);
127
+ }
128
+
129
+ /* ── Controls ──────────────────────────────────────────────────────────────── */
130
+
131
+ /* A host's controls ARE boxed, where the package's buttons are not: inside a card a
132
+ button sits beside typing and a box would read as a field, and out here there is no
133
+ typing to be confused with, so the box is what says "this is a control".
134
+ `font: inherit` first, since the shorthand resets whatever precedes it. */
135
+ .qm-control {
136
+ font: inherit;
137
+ font-family: var(--qmh-font-mono);
138
+ font-size: var(--qmh-text-label);
139
+ line-height: var(--qmh-leading-tight);
140
+ color: var(--qmh-ink);
141
+ background: var(--qmh-page);
142
+ border: var(--qmh-border-width) solid var(--qmh-border);
143
+ border-radius: var(--qmh-radius-inner);
144
+ padding: var(--qmh-space) var(--qmh-space-2);
145
+ cursor: pointer;
146
+ text-align: left;
147
+ transition: background-color var(--qmh-duration) var(--qmh-ease-reverse);
148
+ }
149
+
150
+ .qm-control:hover {
151
+ background: var(--qmh-surface-hover);
152
+ }
153
+
154
+ /* On, whichever way the control says so: a switch that is pressed, or the link to the
155
+ variant you are already in. */
156
+ .qm-control[aria-pressed='true'],
157
+ .qm-control[aria-current='true'] {
158
+ border-color: var(--qmh-border-strong);
159
+ background: var(--qmh-surface-hover);
160
+ }
161
+
162
+ /* One choice means nothing to pick: the control stays, because what it reads is a fact
163
+ worth having, and it recedes to say it is not a choice. */
164
+ .qm-control:disabled {
165
+ color: var(--qmh-ink-meta);
166
+ background: var(--qmh-surface);
167
+ cursor: default;
168
+ }
169
+
170
+ /* ── Focus ─────────────────────────────────────────────────────────────────── */
171
+
172
+ /* One visible indicator for the whole host, since none of these controls draws a ring
173
+ of its own. It reads the INK rung rather than a hue: the page is monochrome, and a
174
+ focus ring is a position, not a destination. */
175
+ :focus-visible {
176
+ outline: var(--qmh-ring-width) solid var(--qmh-ink);
177
+ outline-offset: var(--qmh-space-half);
178
+ }
179
+
180
+ /* The mounted surfaces are the exception, and the rule above is why: an unlayered host
181
+ declaration beats `@layer qm.chrome` by design, so a ring written for controls that
182
+ draw none repaints the ones that do. `revert-layer` hands each control back to the
183
+ layer under this one, which is the package's own ring at its own offset, and is
184
+ what makes this file's own lack of a layer load-bearing rather than a preference. */
185
+ [data-qm-root] :focus-visible {
186
+ outline: revert-layer;
187
+ outline-offset: revert-layer;
188
+ }
@@ -0,0 +1,97 @@
1
+ /* THE HOST'S DERIVATION: the same public dials in, a closed `--qmh-*` scale for the
2
+ page AROUND the surfaces out. `core/theme.css` is the same shape one level down
3
+ (dials in, `--_qm-*` out), and the two are calibrated against each other: the mixes
4
+ here are the percentages there (4pp between surface steps, 17% border, 45% and 60%
5
+ ink), so a host plate and a card on it agree about which way "raised" goes and land
6
+ on the same tone at both poles.
7
+
8
+ TWO SCALES rather than one because they answer to different roots. `--_qm-*` is
9
+ declared ON each mounted root, out of reach from an ancestor, so a surface holds its
10
+ scale wherever it is dropped. A host scale has the opposite job: it is the document,
11
+ so it is declared at `:root` and everything on the page reads it. A single namespace
12
+ could not be both.
13
+
14
+ Literals live here and nowhere else in the preset: `check:style` runs its axes over
15
+ `recipes.css` beside it, with this file exempt as the place the defaults are minted.
16
+
17
+ The poles fall back to the SYSTEM colours, so a host that turns no dial still lands
18
+ where its own unstyled text does. The dials sit in front, so `--qm-bg`/`--qm-fg` on
19
+ an ancestor retunes page and surface together. */
20
+
21
+ :root {
22
+ /* ── Colour ──────────────────────────────────────────────────────────────── */
23
+ --qmh-page: var(--qm-bg, Canvas);
24
+ --qmh-ink: var(--qm-fg, CanvasText);
25
+ --qmh-ink-meta: color-mix(in oklab, var(--qmh-ink), var(--qmh-page) 45%);
26
+ --qmh-ghost: color-mix(in oklab, var(--qmh-ink), var(--qmh-page) 60%);
27
+ /* Two planes behind content, 4pp apart exactly as the surfaces step, so a host
28
+ plate and a card on it agree about which way "raised" goes in both schemes. */
29
+ --qmh-surface: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 4%);
30
+ --qmh-surface-hover: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 8%);
31
+ --qmh-border: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 17%);
32
+ /* The edge that says "engaged" where the hairline says "edge": a pressed switch,
33
+ the underline under a link. */
34
+ --qmh-border-strong: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 40%);
35
+
36
+ /* The two hues a host spends, and it spends them on a message. Both read the DIAL,
37
+ so a page carries one colour meaning error and one meaning warning rather than a
38
+ second pair beside the surfaces'. The dark half is a lift the dials do not carry:
39
+ a hue calibrated against white reads as a stain on a dark page. */
40
+ --qmh-alert: light-dark(var(--qm-danger, #c5221f), #ff9c94);
41
+ --qmh-warn: light-dark(var(--qm-warning, #b25000), #e0b44c);
42
+
43
+ /* ── Rhythm ──────────────────────────────────────────────────────────────── */
44
+ /* One base and its multiples, the same four steps the surfaces derive. A host
45
+ separating SECTIONS down a page wants steps above these; it derives them from
46
+ this base rather than minting them, which is what keeps its ramp one ramp. */
47
+ --qmh-space: var(--qm-space, 0.25rem);
48
+ --qmh-space-half: calc(var(--qmh-space) / 2);
49
+ --qmh-space-2: calc(var(--qmh-space) * 2);
50
+ --qmh-space-3: calc(var(--qmh-space) * 3);
51
+ --qmh-space-4: calc(var(--qmh-space) * 4);
52
+ --qmh-border-width: 1px;
53
+ --qmh-ring-width: 2px;
54
+ --qmh-radius: var(--qm-radius, 8px);
55
+ --qmh-radius-inner: calc(var(--qmh-radius) / 2);
56
+
57
+ /* The measure a passage wraps at. Reading geometry (what a line of text needs),
58
+ so it does not derive from the spacing dial. */
59
+ --qmh-measure: 34rem;
60
+
61
+ /* ── Type ────────────────────────────────────────────────────────────────── */
62
+ /* Two faces with a job each: prose is sans, chrome is mono. Both are system stacks,
63
+ so the preset costs no network request. The sans face reads the same dial the
64
+ surfaces do, so one override moves page and card together; the mono stack is
65
+ spelled out past the dial's default, because a host's chrome leans on it for
66
+ tabular readouts and the two-name fallback is thin for that. */
67
+ --qmh-font: var(--qm-font, ui-sans-serif, system-ui, sans-serif);
68
+ --qmh-font-mono: var(--qm-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
69
+
70
+ /* The page's own ramp, and it does NOT read `--qm-font-size`: that dial anchors the
71
+ surfaces' ramp, which is chrome inside a card, and a page's body text is a
72
+ different job at a different size. A host that wants them locked sets both. */
73
+ --qmh-text: 1rem;
74
+ --qmh-text-label: 0.8125rem;
75
+ --qmh-text-meta: 0.6875rem;
76
+ --qmh-weight-strong: 600;
77
+ --qmh-weight-mid: 500;
78
+ --qmh-leading-body: 1.6;
79
+ --qmh-leading-tight: 1.25;
80
+ --qmh-track-label: 0.08em;
81
+
82
+ /* ── Motion ──────────────────────────────────────────────────────────────── */
83
+ /* One rung of each: a host's chrome is states toggling under the pointer that
84
+ nobody watches, so every trip is between two rest states and reverses mid-flight.
85
+ A second duration would need something the eye tracks, and a second curve
86
+ something that arrives or leaves. */
87
+ --qmh-duration: 140ms;
88
+ --qmh-ease-reverse: cubic-bezier(0.4, 0, 0.6, 1);
89
+ }
90
+
91
+ /* Collapse the SCALE, not each transition: every animated recipe reads the rung, so one
92
+ override reaches all of them with no `*` rule touching mounted package elements. */
93
+ @media (prefers-reduced-motion: reduce) {
94
+ :root {
95
+ --qmh-duration: 1ms;
96
+ }
97
+ }