@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.
- package/LICENSE +201 -0
- package/NOTICE +58 -0
- package/README.md +241 -0
- package/THEMING.md +153 -0
- package/dist/core/address.d.ts +56 -0
- package/dist/core/address.js +95 -0
- package/dist/core/bloom.d.ts +37 -0
- package/dist/core/bloom.js +116 -0
- package/dist/core/codec/anchor.d.ts +19 -0
- package/dist/core/codec/anchor.js +36 -0
- package/dist/core/codec/code.d.ts +12 -0
- package/dist/core/codec/code.js +118 -0
- package/dist/core/codec/decode.d.ts +24 -0
- package/dist/core/codec/decode.js +270 -0
- package/dist/core/codec/encode.d.ts +71 -0
- package/dist/core/codec/encode.js +480 -0
- package/dist/core/codec/field.d.ts +171 -0
- package/dist/core/codec/field.js +493 -0
- package/dist/core/codec/index.d.ts +22 -0
- package/dist/core/codec/index.js +43 -0
- package/dist/core/codec/inputrules.d.ts +7 -0
- package/dist/core/codec/inputrules.js +168 -0
- package/dist/core/codec/islands.d.ts +36 -0
- package/dist/core/codec/islands.js +82 -0
- package/dist/core/codec/keymap.d.ts +13 -0
- package/dist/core/codec/keymap.js +29 -0
- package/dist/core/codec/lists.d.ts +22 -0
- package/dist/core/codec/lists.js +104 -0
- package/dist/core/codec/marks.d.ts +37 -0
- package/dist/core/codec/marks.js +74 -0
- package/dist/core/codec/positions.d.ts +17 -0
- package/dist/core/codec/positions.js +108 -0
- package/dist/core/codec/prose.css +458 -0
- package/dist/core/codec/reconcile.d.ts +26 -0
- package/dist/core/codec/reconcile.js +64 -0
- package/dist/core/codec/schema.d.ts +13 -0
- package/dist/core/codec/schema.js +155 -0
- package/dist/core/codec/slash.d.ts +79 -0
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +50 -0
- package/dist/core/codec/table-view.js +928 -0
- package/dist/core/codec/table.d.ts +85 -0
- package/dist/core/codec/table.js +224 -0
- package/dist/core/errors.d.ts +73 -0
- package/dist/core/errors.js +24 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +19 -0
- package/dist/core/lifecycle.d.ts +7 -0
- package/dist/core/lifecycle.js +21 -0
- package/dist/core/rebind.svelte.d.ts +18 -0
- package/dist/core/rebind.svelte.js +44 -0
- package/dist/core/teardown.d.ts +20 -0
- package/dist/core/teardown.js +37 -0
- package/dist/core/theme.css +276 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +9 -0
- package/dist/preset/index.css +10 -0
- package/dist/preset/recipes.css +188 -0
- package/dist/preset/scale.css +97 -0
- package/dist/preview/Preview.svelte +118 -0
- package/dist/preview/Preview.svelte.d.ts +39 -0
- package/dist/preview/bridge.d.ts +13 -0
- package/dist/preview/bridge.js +88 -0
- package/dist/preview/controller.d.ts +40 -0
- package/dist/preview/controller.js +141 -0
- package/dist/preview/geometry.d.ts +37 -0
- package/dist/preview/geometry.js +45 -0
- package/dist/preview/index.d.ts +5 -0
- package/dist/preview/index.js +14 -0
- package/dist/preview/overlay.d.ts +10 -0
- package/dist/preview/overlay.js +82 -0
- package/dist/preview/paint.d.ts +17 -0
- package/dist/preview/paint.js +262 -0
- package/dist/preview/strings.d.ts +14 -0
- package/dist/preview/strings.js +22 -0
- package/dist/visual/ArrayField.svelte +362 -0
- package/dist/visual/ArrayField.svelte.d.ts +24 -0
- package/dist/visual/BooleanField.svelte +88 -0
- package/dist/visual/BooleanField.svelte.d.ts +20 -0
- package/dist/visual/Card.svelte +849 -0
- package/dist/visual/Card.svelte.d.ts +43 -0
- package/dist/visual/CardControls.svelte +92 -0
- package/dist/visual/CardControls.svelte.d.ts +11 -0
- package/dist/visual/DateField.svelte +203 -0
- package/dist/visual/DateField.svelte.d.ts +28 -0
- package/dist/visual/DiagnosticList.svelte +50 -0
- package/dist/visual/DiagnosticList.svelte.d.ts +7 -0
- package/dist/visual/EnumField.svelte +169 -0
- package/dist/visual/EnumField.svelte.d.ts +25 -0
- package/dist/visual/Field.svelte +270 -0
- package/dist/visual/Field.svelte.d.ts +37 -0
- package/dist/visual/FieldHint.svelte +185 -0
- package/dist/visual/FieldHint.svelte.d.ts +13 -0
- package/dist/visual/FieldLabel.svelte +141 -0
- package/dist/visual/FieldLabel.svelte.d.ts +19 -0
- package/dist/visual/FormatPopover.svelte +396 -0
- package/dist/visual/FormatPopover.svelte.d.ts +9 -0
- package/dist/visual/NumberField.svelte +72 -0
- package/dist/visual/NumberField.svelte.d.ts +19 -0
- package/dist/visual/ObjectField.svelte +131 -0
- package/dist/visual/ObjectField.svelte.d.ts +17 -0
- package/dist/visual/ProseArrayElement.svelte +98 -0
- package/dist/visual/ProseArrayElement.svelte.d.ts +19 -0
- package/dist/visual/ProseField.svelte +213 -0
- package/dist/visual/ProseField.svelte.d.ts +50 -0
- package/dist/visual/SlashMenu.svelte +126 -0
- package/dist/visual/SlashMenu.svelte.d.ts +14 -0
- package/dist/visual/TextField.svelte +63 -0
- package/dist/visual/TextField.svelte.d.ts +23 -0
- package/dist/visual/TipsCard.svelte +191 -0
- package/dist/visual/TipsCard.svelte.d.ts +13 -0
- package/dist/visual/VisualEditor.svelte +96 -0
- package/dist/visual/VisualEditor.svelte.d.ts +14 -0
- package/dist/visual/VisualEditorInner.svelte +1032 -0
- package/dist/visual/VisualEditorInner.svelte.d.ts +17 -0
- package/dist/visual/controls.css +272 -0
- package/dist/visual/diagnostics.d.ts +52 -0
- package/dist/visual/diagnostics.js +83 -0
- package/dist/visual/domid.d.ts +21 -0
- package/dist/visual/domid.js +36 -0
- package/dist/visual/ext.d.ts +19 -0
- package/dist/visual/ext.js +29 -0
- package/dist/visual/icons/Icon.svelte +39 -0
- package/dist/visual/icons/Icon.svelte.d.ts +12 -0
- package/dist/visual/icons/nodes.d.ts +103 -0
- package/dist/visual/icons/nodes.js +55 -0
- package/dist/visual/index.d.ts +9 -0
- package/dist/visual/index.js +24 -0
- package/dist/visual/motion.d.ts +18 -0
- package/dist/visual/motion.js +53 -0
- package/dist/visual/props.d.ts +77 -0
- package/dist/visual/props.js +1 -0
- package/dist/visual/signals.d.ts +59 -0
- package/dist/visual/signals.js +1 -0
- package/dist/visual/strings.d.ts +123 -0
- package/dist/visual/strings.js +80 -0
- package/dist/visual/structure.d.ts +224 -0
- package/dist/visual/structure.js +270 -0
- package/dist/visual/synced.svelte.d.ts +15 -0
- package/dist/visual/synced.svelte.js +34 -0
- package/dist/visual/tips.d.ts +24 -0
- package/dist/visual/tips.js +50 -0
- 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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|