@quillmark/svelte 0.5.1 → 0.6.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 (53) hide show
  1. package/README.md +6 -0
  2. package/THEMING.md +12 -16
  3. package/dist/core/address.d.ts +17 -3
  4. package/dist/core/address.js +20 -3
  5. package/dist/core/codec/blocks.d.ts +37 -0
  6. package/dist/core/codec/blocks.js +163 -0
  7. package/dist/core/codec/field.js +3 -5
  8. package/dist/core/codec/index.d.ts +1 -1
  9. package/dist/core/codec/index.js +3 -2
  10. package/dist/core/codec/inputrules.js +24 -144
  11. package/dist/core/codec/prose.css +14 -25
  12. package/dist/core/codec/schema.d.ts +8 -0
  13. package/dist/core/codec/schema.js +33 -1
  14. package/dist/core/codec/slash.d.ts +18 -16
  15. package/dist/core/codec/slash.js +0 -0
  16. package/dist/core/codec/table-view.js +127 -157
  17. package/dist/core/codec/table.d.ts +0 -10
  18. package/dist/core/codec/table.js +0 -15
  19. package/dist/core/theme.css +350 -344
  20. package/dist/preset/recipes.css +10 -17
  21. package/dist/preview/Preview.svelte +4 -2
  22. package/dist/preview/geometry.d.ts +10 -9
  23. package/dist/preview/geometry.js +11 -13
  24. package/dist/visual/ArrayField.svelte +242 -63
  25. package/dist/visual/ArrayField.svelte.d.ts +5 -1
  26. package/dist/visual/Card.svelte +21 -2
  27. package/dist/visual/EnumField.svelte +33 -1
  28. package/dist/visual/Field.svelte +28 -4
  29. package/dist/visual/FieldLabel.svelte +2 -2
  30. package/dist/visual/FormatPopover.svelte +13 -1
  31. package/dist/visual/ObjectField.svelte +157 -35
  32. package/dist/visual/ObjectField.svelte.d.ts +13 -4
  33. package/dist/visual/ProseArrayElement.svelte +22 -2
  34. package/dist/visual/ProseArrayElement.svelte.d.ts +1 -0
  35. package/dist/visual/TipsCard.svelte +33 -13
  36. package/dist/visual/VariantField.svelte +98 -0
  37. package/dist/visual/VariantField.svelte.d.ts +21 -0
  38. package/dist/visual/VisualEditorInner.svelte +15 -10
  39. package/dist/visual/controls.css +282 -283
  40. package/dist/visual/diagnostics.d.ts +13 -11
  41. package/dist/visual/diagnostics.js +11 -9
  42. package/dist/visual/domid.d.ts +13 -0
  43. package/dist/visual/domid.js +16 -0
  44. package/dist/visual/leaves.d.ts +9 -7
  45. package/dist/visual/links.d.ts +5 -2
  46. package/dist/visual/links.js +9 -3
  47. package/dist/visual/strings.d.ts +15 -0
  48. package/dist/visual/strings.js +3 -0
  49. package/dist/visual/structure.d.ts +32 -6
  50. package/dist/visual/structure.js +49 -3
  51. package/package.json +2 -2
  52. package/dist/core/codec/clipboard.d.ts +0 -59
  53. package/dist/core/codec/clipboard.js +0 -202
@@ -8,7 +8,7 @@
8
8
  every element marked `data-qm-root`. The package imports it itself, so there is
9
9
  no `import './theme.css'` for a consumer to forget. A stylesheet rather than an
10
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
11
+ carry: a selector consumer CSS outranks without `!important`, real properties
12
12
  on the root rule (`font-family`) that every root then inherits, and a free
13
13
  `style` attribute for the consumer.
14
14
 
@@ -26,10 +26,12 @@
26
26
  declared on the root, so they are reachable only by a rule targeting it:
27
27
  THEMING.md §"What is deliberately not public" states the terms.
28
28
 
29
- Layers: `qm` is the top-level layer name, and it is contract — a consumer
30
- pins the order against it with `@layer qm, app;` in their own CSS, which wins
31
- in both bundler orders (THEMING §"Your CSS beats ours"). Unlayered consumer
32
- CSS beats every layer here for free.
29
+ Rank: no cascade layer, and every selector here and in the shared recipes is
30
+ `:where()`-wrapped, so each computes at specificity zero. Any rule of a consumer's
31
+ outranks them at any specificity, and no layer of a consumer's outranks them at all —
32
+ unlayered beats layered whatever order the sheets arrive in, which is what makes the
33
+ rank independent of the bundler (THEMING §"Your CSS beats ours"). `check:style` holds
34
+ both halves over the sheets it names.
33
35
 
34
36
  `@property` registers the three entry rungs, the ones reading a dial, and
35
37
  nothing below them. An `initial-value` must be computationally independent,
@@ -64,366 +66,370 @@
64
66
  initial-value: 14px;
65
67
  }
66
68
 
67
- @layer qm.scale, qm.chrome;
69
+ :where([data-qm-root]) {
70
+ /* ── Poles ───────────────────────────────────────────────────────────────
71
+ Each dial's default, stated once. Every rung below reads a pole, never a
72
+ dial, so both schemes cost two declarations rather than fifteen; and the
73
+ dial sits in front of the default, so a consumer's ancestor value wins in
74
+ either scheme, which it would not if a scheme rule set `--qm-bg` instead.
68
75
 
69
- @layer qm.scale {
70
- :where([data-qm-root]) {
71
- /* ── Poles ───────────────────────────────────────────────────────────────
72
- Each dial's default, stated once. Every rung below reads a pole, never a
73
- dial, so both schemes cost two declarations rather than fifteen; and the
74
- dial sits in front of the default, so a consumer's ancestor value wins in
75
- either scheme, which it would not if a scheme rule set `--qm-bg` instead.
76
-
77
- `light-dark()` keys the default off the `color-scheme` this root inherits,
78
- which is the host's: an app that declares `dark` gets a dark surface
79
- whatever the OS says, and one that declares nothing gets light, matching
80
- its own unstyled text. So the package owns no mode logic at all: light or
81
- dark is the mounting site's, like every other value here (THEMING.md
82
- §"The surface follows your colour scheme").
76
+ `light-dark()` keys the default off the `color-scheme` this root inherits,
77
+ which is the host's: an app that declares `dark` gets a dark surface
78
+ whatever the OS says, and one that declares nothing gets light, matching
79
+ its own unstyled text. So the package owns no mode logic at all: light or
80
+ dark is the mounting site's, like every other value here (THEMING.md
81
+ §"The surface follows your colour scheme").
83
82
 
84
- `--qm-bg` is the card, not the column behind it: the plane content is
85
- written on, which is also the preview's paper. Everything else is a step
86
- off it, and the column is a step down. */
87
- --_qm-bg: var(--qm-bg, light-dark(#fff, #272729));
88
- --_qm-fg: var(--qm-fg, light-dark(#1c1c1c, #d7dadc));
89
- /* Neutral: the surface spends no hue on a state it can draw in grey. The pair
90
- clears WCAG 2.4.11's 3:1 against the card at both poles, at 4.8:1 and 5.8:1. */
91
- --_qm-accent: var(--qm-accent, light-dark(#71717a, #a1a1aa));
92
- /* The one hue the surface spends, and it spends it on a state. Each pole is
93
- its own figure rather than one ink shown twice: a hue calibrated against
94
- white reads as a stain on a dark card. Both clear WCAG 1.4.3's 4.5:1 against
95
- the card, which is the bar the label rung asks for and the one the glyphs
96
- drawn in this hue clear on the way past 1.4.11's 3:1. Dark enough at its own
97
- pole to carry a label at 4.5:1, so a filled chip needs no third figure. The
98
- figures are the host scale's (`preset/scale.css`), so a page's status line
99
- and a card's diagnostic carry one colour meaning error. */
100
- --_qm-danger: var(--qm-danger, light-dark(#9e1518, #f9786c));
101
- --_qm-font: var(--qm-font, ui-sans-serif, system-ui, sans-serif);
102
- --_qm-font-mono: var(--qm-font-mono, ui-monospace, monospace);
83
+ `--qm-bg` is the card, not the column behind it: the plane content is
84
+ written on, which is also the preview's paper. Everything else is a step
85
+ off it, and the column is a step down. */
86
+ --_qm-bg: var(--qm-bg, light-dark(#fff, #272729));
87
+ --_qm-fg: var(--qm-fg, light-dark(#1c1c1c, #d7dadc));
88
+ /* Neutral: the surface spends no hue on a state it can draw in grey. The pair
89
+ clears WCAG 2.4.11's 3:1 against the card at both poles, at 4.8:1 and 5.8:1. */
90
+ --_qm-accent: var(--qm-accent, light-dark(#71717a, #a1a1aa));
91
+ /* The one hue the surface spends, and it spends it on a state. Each pole is
92
+ its own figure rather than one ink shown twice: a hue calibrated against
93
+ white reads as a stain on a dark card. Both clear WCAG 1.4.3's 4.5:1 against
94
+ the card, which is the bar the label rung asks for and the one the glyphs
95
+ drawn in this hue clear on the way past 1.4.11's 3:1. Dark enough at its own
96
+ pole to carry a label at 4.5:1, so a filled chip needs no third figure. The
97
+ figures are the host scale's (`preset/scale.css`), so a page's status line
98
+ and a card's diagnostic carry one colour meaning error. */
99
+ --_qm-danger: var(--qm-danger, light-dark(#9e1518, #f9786c));
100
+ --_qm-font: var(--qm-font, ui-sans-serif, system-ui, sans-serif);
101
+ --_qm-font-mono: var(--qm-font-mono, ui-monospace, monospace);
103
102
 
104
- /* ── Colour ──────────────────────────────────────────────────────────────
105
- Ink steps fg → bg, the border and hover rungs step bg → fg, all mixed in
106
- oklab; sRGB muddies the mid-tones and the label/meta/border rungs are
107
- exactly mid-tone. So inverting the poles inverts that half of the scale.
108
- The percentages are calibrated to the light-mode literals they stand in
109
- for (17% ≈ #d4d4d4, 35% ≈ #555).
103
+ /* ── Colour ──────────────────────────────────────────────────────────────
104
+ Ink steps fg → bg, the border and hover rungs step bg → fg, all mixed in
105
+ oklab; sRGB muddies the mid-tones and the label/meta/border rungs are
106
+ exactly mid-tone. So inverting the poles inverts that half of the scale.
107
+ The percentages are calibrated to the light-mode literals they stand in
108
+ for (17% ≈ #d4d4d4, 35% ≈ #555).
110
109
 
111
- Planes are the half that does not read the ink pole, because what makes a
112
- card read as a card is that it is the brighter plane, in both schemes
113
- alike (ARCHITECTURE §Styling). A plane stepped toward the ink is right in
114
- dark and inverted in light, where it darkens the card below the page it
115
- floats on. So `sunken` and `well` step toward shadow and `raised` toward
116
- light, and the base plane — a card, the preview's paper — is the pole
117
- itself.
110
+ Planes are the half that does not read the ink pole, because what makes a
111
+ card read as a card is that it is the brighter plane, in both schemes
112
+ alike (ARCHITECTURE §Styling). A plane stepped toward the ink is right in
113
+ dark and inverted in light, where it darkens the card below the page it
114
+ floats on. So `sunken` and `well` step toward shadow and `raised` toward
115
+ light, and the base plane — a card, the preview's paper — is the pole
116
+ itself.
118
117
 
119
- Two planes sink. `sunken` is the ground — the column behind the cards, the
120
- desk behind the sheets — and lifting the plane above it takes very little
121
- at the light pole. `well` is the hole cut into a card: a field, a table's
122
- header band, the one native select, none of which draw an edge, so the
123
- step is the whole of what says a value is typed there. The order at the
124
- light pole is card, ground, well; at the dark pole the two sink to within
125
- a channel of each other, which is where a card with little light left to
126
- give up leaves them.
118
+ Two planes sink. `sunken` is the ground — the column behind the cards, the
119
+ desk behind the sheets — and lifting the plane above it takes very little
120
+ at the light pole. `well` is the hole cut into a card: a field, a table's
121
+ header band, the one native select, none of which draw an edge, so the
122
+ step is the whole of what says a value is typed there. The order at the
123
+ light pole is card, ground, well; at the dark pole the two sink to within
124
+ a channel of each other, which is where a card with little light left to
125
+ give up leaves them.
127
126
 
128
- How far each sinks is a function of the card's own L, not of the scheme the
129
- host declared: a dark surface has to spend more of what little it has
130
- before the card above it reads as lifted, so the two calibrated distances
131
- — 0.0214 off white, 0.0553 off the dark default — interpolate linearly
132
- between. A card pinned at neither pole takes a proportionate step whatever
133
- `color-scheme` says, which is the half of the derivation that reads a
134
- colour rather than a declaration.
127
+ How far each sinks is a function of the card's own L, not of the scheme the
128
+ host declared: a dark surface has to spend more of what little it has
129
+ before the card above it reads as lifted, so the two calibrated distances
130
+ — 0.0214 off white, 0.0553 off the dark default — interpolate linearly
131
+ between. A card pinned at neither pole takes a proportionate step whatever
132
+ `color-scheme` says, which is the half of the derivation that reads a
133
+ colour rather than a declaration.
135
134
 
136
- Both sink by subtraction where every other rung mixes: a mix toward black
137
- is a ratio, so it scales what it takes and pulls the result toward
138
- neutral, and a ground is a fixed distance. `a b` pass through, so the
139
- plane keeps the pole's hue and a turned `--qm-bg` tints it. The defaults
140
- land within a channel of `#f7f8f9` and `#1a1a1b`. Below L ≈ 0.065 the
141
- ground clamps to black, which is where a near-black card leaves no room
142
- for a ground of its own. */
143
- --_qm-surface: var(--_qm-bg);
144
- --_qm-surface-sunken: oklab(from var(--_qm-bg) calc(l - (0.0214 + 0.0467 * (1 - l))) a b);
145
- --_qm-surface-well: oklab(from var(--_qm-bg) calc(l - (0.04 + 0.0202 * (1 - l))) a b);
146
- /* `raised` is the same ramp spelled as a mix, its step 0.08 × (1 − l): the light
147
- arm is zero, a white card having nowhere further to go, so a floating surface
148
- separates on its hairline and `--_qm-lift` there and the step joins them in
149
- dark. */
150
- --_qm-surface-raised: color-mix(in oklab, var(--_qm-bg), #fff 8%);
151
- --_qm-surface-hover: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 8%);
152
- --_qm-border: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 17%);
153
- /* A rule that separates without structuring: the lines between a table's cells,
154
- where the frame around them is the structure. Half the border, and read off
155
- it rather than restated as a percentage of its own, so the two hold their
156
- relationship at any mix: a stroke against the surface behind it, not a fill
157
- stacked on one. */
158
- --_qm-border-faint: color-mix(in oklab, var(--_qm-bg), var(--_qm-border) 50%);
159
- /* Two rungs, and there is no third: a step below `label` lands under 3:1 on the
160
- well a field draws it against, and what would take it — a placeholder, an
161
- unset value, the resolved `default:` — is content a reader has to read
162
- (WCAG 1.4.3), not decoration. So tone carries hierarchy alone, and a leaf
163
- saying shown-never-written says it against the `ink` a written value takes,
164
- in italic where it has no written neighbour to be read against. */
165
- --_qm-ink: var(--_qm-fg);
166
- --_qm-ink-label: color-mix(in oklab, var(--_qm-fg), var(--_qm-bg) 35%);
167
- /* The arrival wash (`core/bloom.ts`). Its alpha is a peak, not a tint: the
168
- wash animates from and back to zero, never held, so it reads over the leaf it
169
- lands on while the leaf rests clean. */
170
- --_qm-accent-wash: color-mix(in oklab, var(--_qm-accent) 18%, transparent);
171
- /* The accent held: what a selected line of a table is tinted with, and what its
172
- grip is filled with, so the two ends of one gesture are one colour. A second
173
- name rather than the wash reused, because the two answer different questions
174
- and the numbers follow: a peak passes over paper and can be strong, where a
175
- tint is held under body text and cannot. Translucent, so it
176
- stacks over whichever surface rung the cell already draws instead of replacing
177
- it. */
178
- --_qm-accent-tint: color-mix(in oklab, var(--_qm-accent) 12%, transparent);
179
- /* The danger hue held: what a destructive control fills with where the family's
180
- neutral hover rung would read as one more button. Mixed at the accent tint's
181
- strength, so a held tint is a held tint whichever hue it carries. */
182
- --_qm-danger-tint: color-mix(in oklab, var(--_qm-danger) 12%, transparent);
183
- /* The label ink held: what a trigger fills with whose own ink never steps, so
184
- the fill arriving under it is that tone at a strength a surface reads
185
- through. */
186
- --_qm-add-fill: color-mix(in oklab, var(--_qm-ink-label) 15%, transparent);
135
+ Both sink by subtraction where every other rung mixes: a mix toward black
136
+ is a ratio, so it scales what it takes and pulls the result toward
137
+ neutral, and a ground is a fixed distance. `a b` pass through, so the
138
+ plane keeps the pole's hue and a turned `--qm-bg` tints it. The defaults
139
+ land within a channel of `#f7f8f9` and `#1a1a1b`. Below L ≈ 0.065 the
140
+ ground clamps to black, which is where a near-black card leaves no room
141
+ for a ground of its own. */
142
+ --_qm-surface: var(--_qm-bg);
143
+ --_qm-surface-sunken: oklab(from var(--_qm-bg) calc(l - (0.0214 + 0.0467 * (1 - l))) a b);
144
+ --_qm-surface-well: oklab(from var(--_qm-bg) calc(l - (0.04 + 0.0202 * (1 - l))) a b);
145
+ /* `raised` is the same ramp spelled as a mix, its step 0.08 × (1 − l): the light
146
+ arm is zero, a white card having nowhere further to go, so a floating surface
147
+ separates on its hairline and `--_qm-lift` there and the step joins them in
148
+ dark. */
149
+ --_qm-surface-raised: color-mix(in oklab, var(--_qm-bg), #fff 8%);
150
+ --_qm-surface-hover: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 8%);
151
+ /* The one rung whose arms are not a single ratio: a stroke is read against what it
152
+ separates, and the mix that reads as a hairline on the dark default draws the
153
+ light card in a line heavier than the type it frames. The light arm is
154
+ calibrated rather than derived, which makes this the one rung under the poles
155
+ that reads the declared scheme rather than only the colours it resolves to. */
156
+ --_qm-border: light-dark(
157
+ color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 6%),
158
+ color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 17%)
159
+ );
160
+ /* A rule that separates without structuring: the lines between a table's cells,
161
+ where the frame around them is the structure. Half the border, and read off
162
+ it rather than restated as a percentage of its own, so the two hold their
163
+ relationship at any mix: a stroke against the surface behind it, not a fill
164
+ stacked on one. */
165
+ --_qm-border-faint: color-mix(in oklab, var(--_qm-bg), var(--_qm-border) 50%);
166
+ /* Two rungs, and there is no third: a step below `label` lands under 3:1 on the
167
+ well a field draws it against, and what would take it — a placeholder, an
168
+ unset value, the resolved `default:` — is content a reader has to read
169
+ (WCAG 1.4.3), not decoration. So tone carries hierarchy alone, and a leaf
170
+ saying shown-never-written says it against the `ink` a written value takes,
171
+ in italic where it has no written neighbour to be read against. */
172
+ --_qm-ink: var(--_qm-fg);
173
+ --_qm-ink-label: color-mix(in oklab, var(--_qm-fg), var(--_qm-bg) 35%);
174
+ /* The arrival wash (`core/bloom.ts`). Its alpha is a peak, not a tint: the
175
+ wash animates from and back to zero, never held, so it reads over the leaf it
176
+ lands on while the leaf rests clean. */
177
+ --_qm-accent-wash: color-mix(in oklab, var(--_qm-accent) 18%, transparent);
178
+ /* The accent held: what a selected line of a table is tinted with, and what its
179
+ grip is filled with, so the two ends of one gesture are one colour. A second
180
+ name rather than the wash reused, because the two answer different questions
181
+ and the numbers follow: a peak passes over paper and can be strong, where a
182
+ tint is held under body text and cannot. Translucent, so it
183
+ stacks over whichever surface rung the cell already draws instead of replacing
184
+ it. */
185
+ --_qm-accent-tint: color-mix(in oklab, var(--_qm-accent) 12%, transparent);
186
+ /* The danger hue held: what a destructive control fills with where the family's
187
+ neutral hover rung would read as one more button. Mixed at the accent tint's
188
+ strength, so a held tint is a held tint whichever hue it carries. */
189
+ --_qm-danger-tint: color-mix(in oklab, var(--_qm-danger) 12%, transparent);
190
+ /* The label ink held: what a trigger fills with whose own ink never steps, so
191
+ the fill arriving under it is that tone at a strength a surface reads
192
+ through. */
193
+ --_qm-add-fill: color-mix(in oklab, var(--_qm-ink-label) 15%, transparent);
187
194
 
188
- /* The tips card'S hue: the surface's one exit from the greyscale that is not
189
- a status, and what the amber and the typewriter face say together is
190
- draft (VISUAL_EDITOR §"Card operations"). A literal rather than a dial
191
- because there is nothing left to turn: the rungs under it mix over
192
- the surface and the ink a consumer already tuned, so the card follows a
193
- brand untold. The mixes are faint on purpose: a warmth, not a badge.
195
+ /* The tips card'S hue: the surface's one exit from the greyscale that is not
196
+ a status, and what the amber and the typewriter face say together is
197
+ draft (VISUAL_EDITOR §"Card operations"). A literal rather than a dial
198
+ because there is nothing left to turn: the rungs under it mix over
199
+ the surface and the ink a consumer already tuned, so the card follows a
200
+ brand untold. The mixes are faint on purpose: a warmth, not a badge.
194
201
 
195
- The one group mixed in sRGB. oklab holds perceived lightness across a mix and
196
- spends chroma to do it, which is right where both endpoints are grey and wrong
197
- for a hue: at either pole it lands the ink a channel short of warm. */
198
- --_qm-tips: #e88c0a;
199
- --_qm-tips-surface: color-mix(in srgb, var(--_qm-surface), var(--_qm-tips) 3%);
200
- --_qm-tips-ink: color-mix(in srgb, var(--_qm-ink), var(--_qm-tips) 24%);
201
- /* The ink and the amber stepped up, and the pair is spent on the two runs that
202
- are not the sentence: a token in a tip, and the foot's control under the
203
- pointer. One rung each rather than one per run, a chip and a hover four
204
- percent apart being a number with no second decision under it. */
205
- --_qm-tips-ink-warm: color-mix(in srgb, var(--_qm-ink), var(--_qm-tips) 45%);
206
- --_qm-tips-fill: color-mix(in srgb, var(--_qm-tips) 12%, transparent);
207
- /* The typewriter, the half of the slip the amber cannot carry: a modern mono
208
- reads as code where a Courier reads as typed. Its own rung rather than
209
- `--_qm-font-mono`, the chrome's readout face, which a consumer turns for
210
- tabular figures and not to dress this card. Courier Prime leads and is nobody's
211
- system font, so a host that loads it lands on it and every other host falls to
212
- the Courier the platform already carries; the package fetches nothing, a preset
213
- costing a network request being one a consumer cannot decline. */
214
- --_qm-tips-font: 'Courier Prime', 'Courier New', Courier, ui-monospace, monospace;
202
+ The one group mixed in sRGB. oklab holds perceived lightness across a mix and
203
+ spends chroma to do it, which is right where both endpoints are grey and wrong
204
+ for a hue: at either pole it lands the ink a channel short of warm. */
205
+ --_qm-tips: #e88c0a;
206
+ --_qm-tips-surface: color-mix(in srgb, var(--_qm-surface), var(--_qm-tips) 3%);
207
+ --_qm-tips-ink: color-mix(in srgb, var(--_qm-ink), var(--_qm-tips) 24%);
208
+ /* The ink and the amber stepped up, and the pair is spent on the two runs that
209
+ are not the sentence: a token in a tip, and the foot's control under the
210
+ pointer. One rung each rather than one per run, a chip and a hover four
211
+ percent apart being a number with no second decision under it. */
212
+ --_qm-tips-ink-warm: color-mix(in srgb, var(--_qm-ink), var(--_qm-tips) 45%);
213
+ --_qm-tips-fill: color-mix(in srgb, var(--_qm-tips) 12%, transparent);
214
+ /* The typewriter, the half of the slip the amber cannot carry: a modern mono
215
+ reads as code where a Courier reads as typed. Its own rung rather than
216
+ `--_qm-font-mono`, the chrome's readout face, which a consumer turns for
217
+ tabular figures and not to dress this card. Courier Prime leads and is nobody's
218
+ system font, so a host that loads it lands on it and every other host falls to
219
+ the Courier the platform already carries; the package fetches nothing, a preset
220
+ costing a network request being one a consumer cannot decline. */
221
+ --_qm-tips-font: 'Courier Prime', 'Courier New', Courier, ui-monospace, monospace;
215
222
 
216
- /* ── Ratios and effects ──────────────────────────────────────────────────
217
- No palette fixes these, so they are their own rungs. */
218
- --_qm-blur: 8px;
219
- /* The popover's translucency: the raised surface mixed toward transparent, so
220
- the page reads faintly through the pill. */
221
- --_qm-surface-popover: color-mix(in oklab, var(--_qm-surface-raised) 82%, transparent);
222
- /* What a surface with no plane under it lifts on. A tone rung cannot carry that at
223
- both poles: `raised` is a no-op against a white card (§Colour), so in light the
224
- hairline is otherwise the whole separation over the body prose these float on.
225
- Offset-free is the whole of it: an offset states where the light is and no pole
226
- carries that, where a halo states occlusion. Toward black at both poles for the
227
- same reason — a crevice is darker than what surrounds it in a dark room too — so
228
- the cue means one thing whichever way the scale runs. Spent only on what portals;
229
- `check:style` holds the offsets at zero here and the rung as the only shadow
230
- anywhere. */
231
- --_qm-lift: 0 0 12px color-mix(in oklab, #000 16%, transparent);
232
- /* The recede ladder: a hover-revealed or disabled affordance at rest, and a
233
- muted one. Opacity recedes toward whatever is behind, so these invert
234
- correctly given the element's own colour is a rung. */
235
- --_qm-opacity-idle: 0.35;
236
- --_qm-opacity-muted: 0.5;
223
+ /* ── Ratios and effects ──────────────────────────────────────────────────
224
+ No palette fixes these, so they are their own rungs. */
225
+ --_qm-blur: 8px;
226
+ /* The popover's translucency: the raised surface mixed toward transparent, so
227
+ the page reads faintly through the pill. */
228
+ --_qm-surface-popover: color-mix(in oklab, var(--_qm-surface-raised) 82%, transparent);
229
+ /* What a surface with no plane under it lifts on. A tone rung cannot carry that at
230
+ both poles: `raised` is a no-op against a white card (§Colour), so in light the
231
+ hairline is otherwise the whole separation over the body prose these float on.
232
+ Offset-free is the whole of it: an offset states where the light is and no pole
233
+ carries that, where a halo states occlusion. Toward black at both poles for the
234
+ same reason — a crevice is darker than what surrounds it in a dark room too — so
235
+ the cue means one thing whichever way the scale runs. Spent only on what portals;
236
+ `check:style` holds the offsets at zero here and the rung as the only shadow
237
+ anywhere. */
238
+ --_qm-lift: 0 0 12px color-mix(in oklab, #000 16%, transparent);
239
+ /* The recede ladder: a hover-revealed or disabled affordance at rest, and a
240
+ muted one. Opacity recedes toward whatever is behind, so these invert
241
+ correctly given the element's own colour is a rung. */
242
+ --_qm-opacity-idle: 0.35;
243
+ --_qm-opacity-muted: 0.5;
237
244
 
238
- /* ── Focus ───────────────────────────────────────────────────────────────
239
- These fix the ring's value, so no control mints it. Which properties draw a
240
- ring, and on what state, is the other half: a rule, `.qm-focus-ring` in
241
- `visual/controls.css`, since a rung each control reassembles is a scale
242
- only up to the point where it is assembled. One width: focus is the only
243
- state that draws a ring anywhere, so there is no held-back tier under it.
244
- Sub-pixel, because at two it reads as a second border on a card whose every
245
- other edge is a hairline. */
246
- --_qm-ring-width: 1.5px;
247
- /* Every ring the surface draws, focus and selection alike, which is why the name
248
- carries neither. Assembled here because the parts spelled out at a use site pass
249
- every axis — each reads a rung — while drawing a ring nothing else agrees with. */
250
- --_qm-ring: var(--_qm-ring-width) solid var(--_qm-accent);
251
- --_qm-ring-offset: 1px;
252
- /* What a clip box carries past its content for a focused control inside it to
253
- keep the whole mark. */
254
- --_qm-ring-reach: calc(var(--_qm-ring-width) + var(--_qm-ring-offset));
245
+ /* ── Focus ───────────────────────────────────────────────────────────────
246
+ These fix the ring's value, so no control mints it. Which properties draw a
247
+ ring, and on what state, is the other half: a rule, `.qm-focus-ring` in
248
+ `visual/controls.css`, since a rung each control reassembles is a scale
249
+ only up to the point where it is assembled. One width: focus is the only
250
+ state that draws a ring anywhere, so there is no held-back tier under it.
251
+ Sub-pixel, because at two it reads as a second border on a card whose every
252
+ other edge is a hairline. */
253
+ --_qm-ring-width: 1.5px;
254
+ /* Every ring the surface draws, focus and selection alike, which is why the name
255
+ carries neither. Assembled here because the parts spelled out at a use site pass
256
+ every axis — each reads a rung — while drawing a ring nothing else agrees with. */
257
+ --_qm-ring: var(--_qm-ring-width) solid var(--_qm-accent);
258
+ --_qm-ring-offset: 1px;
259
+ /* What a clip box carries past its content for a focused control inside it to
260
+ keep the whole mark. */
261
+ --_qm-ring-reach: calc(var(--_qm-ring-width) + var(--_qm-ring-offset));
255
262
 
256
- /* ── Motion ──────────────────────────────────────────────────────────────
257
- Two rungs, by what the trip disturbs: `fast` stays inside the element's own
258
- box, `slow` moves the layout around it. */
259
- --_qm-duration-fast: 120ms;
260
- --_qm-duration-slow: 200ms;
261
- /* The bloom's whole rise-hold-decay, and deliberately not a third rung. A
262
- custom property rather than a constant in `core/bloom.ts` because that module
263
- animates over WAAPI and reads it from here. */
264
- --_qm-bloom-dwell: 1100ms;
263
+ /* ── Motion ──────────────────────────────────────────────────────────────
264
+ Two rungs, by what the trip disturbs: `fast` stays inside the element's own
265
+ box, `slow` moves the layout around it. */
266
+ --_qm-duration-fast: 120ms;
267
+ --_qm-duration-slow: 200ms;
268
+ /* The bloom's whole rise-hold-decay, and deliberately not a third rung. A
269
+ custom property rather than a constant in `core/bloom.ts` because that module
270
+ animates over WAAPI and reads it from here. */
271
+ --_qm-bloom-dwell: 1100ms;
265
272
 
266
- /* One curve, symmetric because an interruption turns these trips around: a hover
267
- leaves before it arrives, a card collapses mid-expand. Every trip at `fast` or
268
- `slow` takes it; a one-way shape is legible only over a longer one. */
269
- --_qm-ease-reverse: cubic-bezier(0.4, 0, 0.6, 1);
270
- /* A trip rather than a rung, duration and curve composed the way `--_qm-ring`
271
- composes its three: neither half is reachable alone. Weighted to its end, so an
272
- arrival settles into its rest instead of stopping at it, and long enough that a
273
- control resting at nothing reads as arriving rather than as switching. */
274
- --_qm-reveal: 500ms cubic-bezier(0.4, 0, 0.2, 1);
273
+ /* One curve, symmetric because an interruption turns these trips around: a hover
274
+ leaves before it arrives, a card collapses mid-expand. Every trip at `fast` or
275
+ `slow` takes it; a one-way shape is legible only over a longer one. */
276
+ --_qm-ease-reverse: cubic-bezier(0.4, 0, 0.6, 1);
277
+ /* A trip rather than a rung, duration and curve composed the way `--_qm-ring`
278
+ composes its three: neither half is reachable alone. Weighted to its end, so an
279
+ arrival settles into its rest instead of stopping at it, and long enough that a
280
+ control resting at nothing reads as arriving rather than as switching. */
281
+ --_qm-reveal: 500ms cubic-bezier(0.4, 0, 0.2, 1);
275
282
 
276
- /* ── Geometry ──────────────────────────────────────────────────────────*/
277
- /* One stroke width for every edge the chrome draws: a card's, a control's,
278
- and the metadata bracket's three rules alike. A threshold rather than a
279
- rhythm choice, like the tap floor: a hairline is a hairline at any
280
- `--qm-space`, so it does not derive from a dial. It exists as a rung because
281
- the bracket's whole claim is that its vertical and its horizontals are the
282
- same stroke, and `check:style`'s border-width axis can only hold that
283
- against a token. */
284
- --_qm-border-width: 1px;
285
- --_qm-radius: var(--qm-radius, 8px);
286
- /* The interior corner, and half is the relationship rather than the number: a
287
- corner inside a corner reads as nested only while it is visibly tighter, and
288
- halving holds that at every setting of the dial, zero included. */
289
- --_qm-radius-inner: calc(var(--_qm-radius) / 2);
290
- /* A fully-rounded end cap: a shape tier, not a step on the radius ramp, so
291
- it does not derive from the dial: a pill stays a pill at any radius. */
292
- --_qm-radius-pill: 999px;
293
- --_qm-space: var(--qm-space, 0.25rem);
294
- --_qm-space-half: calc(var(--_qm-space) / 2);
295
- --_qm-space-2: calc(var(--_qm-space) * 2);
296
- --_qm-space-3: calc(var(--_qm-space) * 3);
297
- --_qm-space-4: calc(var(--_qm-space) * 4);
298
- --_qm-space-5: calc(var(--_qm-space) * 5);
299
- /* A rung because the two axes are two decisions and the ladder alone cannot hold
300
- them together: `check:style`'s rhythm axis sees each box read a rung, never that
301
- they read the same one, which is the drift `visual/controls.css` already names
302
- for the type half. Asymmetric because the vertical is what leading leaves once
303
- it has taken the line box, and the horizontal is how far in the text starts,
304
- which nothing else fixes. */
305
- --_qm-inset-control: var(--_qm-space) var(--_qm-space-3);
306
- /* The scroll tail: dead space under the last card, so it can be read at the
307
- middle of a pane rather than at its bottom edge. A fraction of the viewport
308
- rather than a step on the ramp, because what it answers is how far the eye
309
- sits from the bottom of the screen, which no spacing dial knows. Spent by
310
- the editor alone (`.qm-pane`): a stack of cards is what has a last one to
311
- reach, where a page and a text mirror end where the document ends. */
312
- --_qm-tail: 40vh;
313
- /* WCAG 2.5.8's 24x24 pointer-target floor. A rung rather than a sixth step on
314
- the space scale: it is a threshold the spec fixes, not a rhythm choice, so
315
- it does not move when `--qm-space` does. Both button families read it, so the
316
- floor is one number instead of one expression per control. */
317
- --_qm-tap-min: 24px;
318
- /* The reorder/delete glyph. A rung because the card header's
319
- right hang is half the difference between this and the tap floor: the glyph
320
- centres in a target sized by the floor, so what stands between it and the
321
- card's gutter is an arithmetic of the two, and a literal on either side is a
322
- balance that silently breaks when the glyph moves. Sized in CSS rather than
323
- through the icon's `size`, so the hang and the glyph read one number. Scoped to
324
- the control pair: the hint and the format marks take their own sizes, and a
325
- rung claiming to be every glyph would be false at three of them. */
326
- --_qm-glyph-control: 14px;
283
+ /* ── Geometry ──────────────────────────────────────────────────────────*/
284
+ /* One stroke width for every edge the chrome draws: a card's, a control's,
285
+ and the metadata bracket's three rules alike. A threshold rather than a
286
+ rhythm choice, like the tap floor: a hairline is a hairline at any
287
+ `--qm-space`, so it does not derive from a dial. It exists as a rung because
288
+ the bracket's whole claim is that its vertical and its horizontals are the
289
+ same stroke, and `check:style`'s border-width axis can only hold that
290
+ against a token. */
291
+ --_qm-border-width: 1px;
292
+ --_qm-radius: var(--qm-radius, 8px);
293
+ /* The interior corner, and half is the relationship rather than the number: a
294
+ corner inside a corner reads as nested only while it is visibly tighter, and
295
+ halving holds that at every setting of the dial, zero included. */
296
+ --_qm-radius-inner: calc(var(--_qm-radius) / 2);
297
+ /* A fully-rounded end cap: a shape tier, not a step on the radius ramp, so
298
+ it does not derive from the dial: a pill stays a pill at any radius. */
299
+ --_qm-radius-pill: 999px;
300
+ --_qm-space: var(--qm-space, 0.25rem);
301
+ --_qm-space-half: calc(var(--_qm-space) / 2);
302
+ --_qm-space-2: calc(var(--_qm-space) * 2);
303
+ --_qm-space-3: calc(var(--_qm-space) * 3);
304
+ --_qm-space-4: calc(var(--_qm-space) * 4);
305
+ --_qm-space-5: calc(var(--_qm-space) * 5);
306
+ /* A rung because the two axes are two decisions and the ladder alone cannot hold
307
+ them together: `check:style`'s rhythm axis sees each box read a rung, never that
308
+ they read the same one, which is the drift `visual/controls.css` already names
309
+ for the type half. Asymmetric because the vertical is what leading leaves once
310
+ it has taken the line box, and the horizontal is how far in the text starts,
311
+ which nothing else fixes. */
312
+ --_qm-inset-control: var(--_qm-space) var(--_qm-space-3);
313
+ /* The scroll tail: dead space under the last card, so it can be read at the
314
+ middle of a pane rather than at its bottom edge. A fraction of the viewport
315
+ rather than a step on the ramp, because what it answers is how far the eye
316
+ sits from the bottom of the screen, which no spacing dial knows. Spent by
317
+ the editor alone (`.qm-pane`): a stack of cards is what has a last one to
318
+ reach, where a page and a text mirror end where the document ends. */
319
+ --_qm-tail: 40vh;
320
+ /* WCAG 2.5.8's 24x24 pointer-target floor. A rung rather than a sixth step on
321
+ the space scale: it is a threshold the spec fixes, not a rhythm choice, so
322
+ it does not move when `--qm-space` does. Both button families read it, so the
323
+ floor is one number instead of one expression per control. */
324
+ --_qm-tap-min: 24px;
325
+ /* The reorder/delete glyph. A rung because the card header's
326
+ right hang is half the difference between this and the tap floor: the glyph
327
+ centres in a target sized by the floor, so what stands between it and the
328
+ card's gutter is an arithmetic of the two, and a literal on either side is a
329
+ balance that silently breaks when the glyph moves. Sized in CSS rather than
330
+ through the icon's `size`, so the hang and the glyph read one number. Scoped to
331
+ the control pair: the hint and the format marks take their own sizes, and a
332
+ rung claiming to be every glyph would be false at three of them. */
333
+ --_qm-glyph-control: 14px;
327
334
 
328
- /* ── Type ────────────────────────────────────────────────────────────────
329
- `--qm-font-size` anchors the chrome rung; 1.125 is the ratio between adjacent
330
- rungs: a step up (title) and one down (label). The ratio and the
331
- weight are fixed conventions, not dials: no consumer tunes a typographic
332
- ratio, and a dial nobody turns is a name every reader still holds. Three chrome
333
- rungs: a second step below `label` lands near 11px, under a size anything here
334
- wants to be read at, and the supporting text that would take it is what `label`
335
- already is. */
336
- --_qm-text-body: var(--qm-font-size, 0.875rem);
337
- --_qm-text-title: calc(var(--_qm-text-body) * 1.125);
338
- --_qm-text-label: calc(var(--_qm-text-body) / 1.125);
339
- /* Paper: the document body answers to the preview's paper where every other
340
- surface here answers to the control beside it, so a rung shared with the chrome
341
- makes a consumer asking for larger prose take larger inputs with it. 16/15 where
342
- the ramp steps at 9/8: small enough to read as a change of register rather than
343
- a rung of hierarchy, and clear of the title rung, which one 9/8 step lands on
344
- exactly. */
345
- --_qm-text-paper: calc(var(--_qm-text-body) * 16 / 15);
346
- /* Two weights over the regular, and only one of them is chrome. `mid` names
347
- everything the surface names — a card title, a section header, a field
348
- label, a nested property — so a stack of them reads as one register and
349
- hierarchy stays the size ramp's, which is the axis that can carry it.
350
- 500 rather than 600 because the family dial resolves to the platform UI
351
- face, whose 600 is heavier at the same number than the webfont a surface
352
- is usually drawn against: at the label rung it goes smudgy, and one
353
- number across three sizes does not read as one weight.
335
+ /* ── Type ────────────────────────────────────────────────────────────────
336
+ `--qm-font-size` anchors the chrome rung; 1.125 is the ratio between adjacent
337
+ rungs: a step up (title) and one down (label). The ratio and the
338
+ weight are fixed conventions, not dials: no consumer tunes a typographic
339
+ ratio, and a dial nobody turns is a name every reader still holds. Three chrome
340
+ rungs: a second step below `label` lands near 11px, under a size anything here
341
+ wants to be read at, and the supporting text that would take it is what `label`
342
+ already is. */
343
+ --_qm-text-body: var(--qm-font-size, 0.875rem);
344
+ --_qm-text-title: calc(var(--_qm-text-body) * 1.125);
345
+ --_qm-text-label: calc(var(--_qm-text-body) / 1.125);
346
+ /* Paper: the document body answers to the preview's paper where every other
347
+ surface here answers to the control beside it, so a rung shared with the chrome
348
+ makes a consumer asking for larger prose take larger inputs with it. 16/15 where
349
+ the ramp steps at 9/8: small enough to read as a change of register rather than
350
+ a rung of hierarchy, and clear of the title rung, which one 9/8 step lands on
351
+ exactly. */
352
+ --_qm-text-paper: calc(var(--_qm-text-body) * 16 / 15);
353
+ /* Two weights over the regular, and only one of them is chrome. `mid` names
354
+ everything the surface names — a card title, a section header, a field
355
+ label, a nested property — so a stack of them reads as one register and
356
+ hierarchy stays the size ramp's, which is the axis that can carry it.
357
+ 500 rather than 600 because the family dial resolves to the platform UI
358
+ face, whose 600 is heavier at the same number than the webfont a surface
359
+ is usually drawn against: at the label rung it goes smudgy, and one
360
+ number across three sizes does not read as one weight.
354
361
 
355
- `strong` is not a rung of that ramp. It is the mark a run wears where
356
- weight is the only signal it has: a heading inside a prose leaf, drawn at
357
- no size step of its own because the quill sets the paper's; a table
358
- island's header row; the stored value in an enum listbox. Reaching for it
359
- where a size or a tone could speak is the drift the split exists to
360
- stop. */
361
- --_qm-weight-strong: 600;
362
- --_qm-weight-mid: 500;
363
- /* Leading: the ramp's third axis, and it does not track the sizes one for one:
364
- two rungs, because a wrapped label and a wrapped paragraph want different
365
- rhythms and nothing wants four. `body` is reading rhythm (both prose leaves,
366
- every control beside them, the tips card's guidance); `tight` is a line that is
367
- a label rather than a passage. Unitless, so a rung inherits multiplicatively
368
- and holds against whichever size rung the surface reads: a `rem` leading
369
- would freeze at one size and disagree with the rest of the ramp. It is the
370
- axis paper and chrome share: both read `body`, and the size rung is what
371
- separates them. `line-height: 1` is outside the axis: it collapses the
372
- line box onto its content, a glyph or a button's one-line label. */
373
- --_qm-leading-body: 1.5;
374
- --_qm-leading-tight: 1.3;
362
+ `strong` is not a rung of that ramp. It is the mark a run wears where
363
+ weight is the only signal it has: a heading inside a prose leaf, drawn at
364
+ no size step of its own because the quill sets the paper's; a table
365
+ island's header row; the stored value in an enum listbox. Reaching for it
366
+ where a size or a tone could speak is the drift the split exists to
367
+ stop. */
368
+ --_qm-weight-strong: 600;
369
+ --_qm-weight-mid: 500;
370
+ /* Leading: the ramp's third axis, and it does not track the sizes one for one:
371
+ two rungs, because a wrapped label and a wrapped paragraph want different
372
+ rhythms and nothing wants four. `body` is reading rhythm (both prose leaves,
373
+ every control beside them, the tips card's guidance); `tight` is a line that is
374
+ a label rather than a passage. Unitless, so a rung inherits multiplicatively
375
+ and holds against whichever size rung the surface reads: a `rem` leading
376
+ would freeze at one size and disagree with the rest of the ramp. It is the
377
+ axis paper and chrome share: both read `body`, and the size rung is what
378
+ separates them. `line-height: 1` is outside the axis: it collapses the
379
+ line box onto its content, a glyph or a button's one-line label. */
380
+ --_qm-leading-body: 1.5;
381
+ --_qm-leading-tight: 1.3;
375
382
 
376
- /* ── The baseline every root inherits ────────────────────────────────────
377
- Real properties, not only custom ones: the thing an inline attribute
378
- could carry but never did, which is why `--qm-font` reached one root of
379
- five. `color-scheme` is deliberately absent: it inherits, so the host's
380
- reaches this root untouched and native chrome (scrollbars, the date
381
- picker, the caret) lands on the same pole the poles above resolve to.
382
- Declaring it here would break that lockstep in the one direction that
383
- matters: a host that declares `dark` would get light native chrome under
384
- a dark surface. A consumer that pins a palette against its own scheme
385
- declares `color-scheme` with it, on the root (THEMING.md).
383
+ /* ── The baseline every root inherits ────────────────────────────────────
384
+ Real properties, not only custom ones: the thing an inline attribute
385
+ could carry but never did, which is why `--qm-font` reached one root of
386
+ five. `color-scheme` is deliberately absent: it inherits, so the host's
387
+ reaches this root untouched and native chrome (scrollbars, the date
388
+ picker, the caret) lands on the same pole the poles above resolve to.
389
+ Declaring it here would break that lockstep in the one direction that
390
+ matters: a host that declares `dark` would get light native chrome under
391
+ a dark surface. A consumer that pins a palette against its own scheme
392
+ declares `color-scheme` with it, on the root (THEMING.md).
386
393
 
387
- Face, ink and leading are the three axes a root carries this way. Leading
388
- belongs with the other two because a unitless ratio inherits
389
- multiplicatively: it holds against whatever size rung each surface reads, so
390
- it imposes less than the ink beside it, and the reason `font-size` stays off
391
- does not transfer: a ratio has no size to sweep a consumer's content with.
392
- What it buys is that `normal` (the UA figure near 1.14, varying by font) is
393
- unreachable under a marker, so a type-bearing surface lands on the ramp
394
- whether or not it remembers to say so, and a per-surface
395
- `--_qm-leading-tight` reads as the deliberate override it is.
394
+ Face, ink and leading are the three axes a root carries this way. Leading
395
+ belongs with the other two because a unitless ratio inherits
396
+ multiplicatively: it holds against whatever size rung each surface reads, so
397
+ it imposes less than the ink beside it, and the reason `font-size` stays off
398
+ does not transfer: a ratio has no size to sweep a consumer's content with.
399
+ What it buys is that `normal` (the UA figure near 1.14, varying by font) is
400
+ unreachable under a marker, so a type-bearing surface lands on the ramp
401
+ whether or not it remembers to say so, and a per-surface
402
+ `--_qm-leading-tight` reads as the deliberate override it is.
396
403
 
397
- Deliberately not `font-size`, and the reason is about the root rule rather
398
- than about size: a root-level body size sweeps every descendant a consumer
399
- may have put inside the marker, which is a reach the derivation does not
400
- have. The rung is not withheld from the surfaces: every control and every
401
- prose leaf reads `--_qm-text-body` in its own recipe, which is
402
- what closes the ramp and makes `--qm-font-size` move an input. What would
403
- break a mounted leaf is a size that changes under it; a rung is fixed at
404
- mount unless the consumer turns the dial, and a consumer turning a dial is
405
- entitled to the reflow. So: the root imposes no size, each surface reads the
406
- rung it wants. */
407
- font-family: var(--_qm-font);
408
- color: var(--_qm-ink);
409
- line-height: var(--_qm-leading-body);
410
- }
404
+ Deliberately not `font-size`, and the reason is about the root rule rather
405
+ than about size: a root-level body size sweeps every descendant a consumer
406
+ may have put inside the marker, which is a reach the derivation does not
407
+ have. The rung is not withheld from the surfaces: every control and every
408
+ prose leaf reads `--_qm-text-body` in its own recipe, which is
409
+ what closes the ramp and makes `--qm-font-size` move an input. What would
410
+ break a mounted leaf is a size that changes under it; a rung is fixed at
411
+ mount unless the consumer turns the dial, and a consumer turning a dial is
412
+ entitled to the reflow. So: the root imposes no size, each surface reads the
413
+ rung it wants. */
414
+ font-family: var(--_qm-font);
415
+ color: var(--_qm-ink);
416
+ line-height: var(--_qm-leading-body);
417
+ }
411
418
 
412
- /* Reduced motion collapses the scale, never a list of selectors.
413
- Two values here are not the rung they look like:
419
+ /* Reduced motion collapses the scale, never a list of selectors.
420
+ Two values here are not the rung they look like:
414
421
 
415
- `1ms` rather than `none`, because a collapsed transition still fires: a surface
416
- waiting on `transitionend` keeps its event, where `transition: none` would strand
417
- it. The beat is under perception either way.
422
+ `1ms` rather than `none`, because a collapsed transition still fires: a surface
423
+ waiting on `transitionend` keeps its event, where `transition: none` would strand
424
+ it. The beat is under perception either way.
418
425
 
419
- `200ms` rather than `1ms` for the dwell, because the bloom's reduced form is a
420
- hold and a cut (`core/bloom.ts` swaps the ramps out), and a hold is a duration:
421
- collapsing it deletes the gesture instead of shortening it. */
422
- @media (prefers-reduced-motion: reduce) {
423
- :where([data-qm-root]) {
424
- --_qm-duration-fast: 1ms;
425
- --_qm-duration-slow: 1ms;
426
- --_qm-bloom-dwell: 200ms;
427
- }
426
+ `200ms` rather than `1ms` for the dwell, because the bloom's reduced form is a
427
+ hold and a cut (`core/bloom.ts` swaps the ramps out), and a hold is a duration:
428
+ collapsing it deletes the gesture instead of shortening it. */
429
+ @media (prefers-reduced-motion: reduce) {
430
+ :where([data-qm-root]) {
431
+ --_qm-duration-fast: 1ms;
432
+ --_qm-duration-slow: 1ms;
433
+ --_qm-bloom-dwell: 200ms;
428
434
  }
429
435
  }