vantage-md 0.7.0 → 0.8.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/CHANGELOG.md +183 -141
- package/README.md +26 -0
- package/dist/directives.css +671 -0
- package/dist/index.cjs +968 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +130 -34
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +130 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +957 -130
- package/dist/index.js.map +1 -1
- package/dist/inline-svg.css +55 -0
- package/dist/line-anchor.css +22 -0
- package/dist/react.cjs +915 -168
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +87 -42
- package/dist/react.d.cts.map +1 -1
- package/dist/react.d.ts +87 -42
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +909 -164
- package/dist/react.js.map +1 -1
- package/dist/styles.css +3 -1
- package/dist/task-list.css +157 -0
- package/package.json +1 -1
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/* vantage-md/styles/directives.css
|
|
2
|
+
*
|
|
3
|
+
* Theme layer for the `data-vantage-*` directive attributes emitted by
|
|
4
|
+
* rehypeVantageDirectives, and for the file-scope chrome the `vantage:`
|
|
5
|
+
* frontmatter key asks for (`.vantage-chip`, `.vantage-chrome`) — one file
|
|
6
|
+
* because the badge and the status chip are the same visual object and share
|
|
7
|
+
* the same rule. Plain CSS on attribute selectors and hand-written classes only:
|
|
8
|
+
* no Tailwind utilities, no class names computed in JS, no color a document
|
|
9
|
+
* can name.
|
|
10
|
+
*
|
|
11
|
+
* This file must stay UNLAYERED. Every `@tailwindcss/typography` variant
|
|
12
|
+
* utility flattens to exactly one class of specificity — `:where()` and
|
|
13
|
+
* `:not(:where(…))` both contribute zero — and all of them sit inside
|
|
14
|
+
* `@layer utilities`. Unlayered normal declarations outrank every layer
|
|
15
|
+
* regardless of specificity, which is the only reason these plain attribute
|
|
16
|
+
* selectors win at all. Wrapping this file in `@layer components` "to be tidy"
|
|
17
|
+
* makes it lose to every prose utility instead; a test guards that.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/* ── Tone palette (light) ───────────────────────────────────────────────── */
|
|
21
|
+
:root {
|
|
22
|
+
--vantage-tone-note-accent: #2563eb;
|
|
23
|
+
--vantage-tone-note-wash: rgb(37 99 235 / 0.1);
|
|
24
|
+
--vantage-tone-note-chip: rgb(37 99 235 / 0.12);
|
|
25
|
+
--vantage-tone-note-ink: #1d4ed8;
|
|
26
|
+
|
|
27
|
+
--vantage-tone-tip-accent: #15803d;
|
|
28
|
+
--vantage-tone-tip-wash: rgb(22 163 74 / 0.1);
|
|
29
|
+
--vantage-tone-tip-chip: rgb(22 163 74 / 0.12);
|
|
30
|
+
/* A shade past green-700, the other tones' step: green is so luminous
|
|
31
|
+
that green-700 over its own chip fill reads 4.39:1, under the 4.5:1 WCAG
|
|
32
|
+
AA asks of a chip's small text. This reads 5.0:1. */
|
|
33
|
+
--vantage-tone-tip-ink: #137638;
|
|
34
|
+
|
|
35
|
+
--vantage-tone-important-accent: #7c3aed;
|
|
36
|
+
--vantage-tone-important-wash: rgb(124 58 237 / 0.1);
|
|
37
|
+
--vantage-tone-important-chip: rgb(124 58 237 / 0.12);
|
|
38
|
+
--vantage-tone-important-ink: #6d28d9;
|
|
39
|
+
|
|
40
|
+
--vantage-tone-warning-accent: #b45309;
|
|
41
|
+
--vantage-tone-warning-wash: rgb(245 158 11 / 0.13);
|
|
42
|
+
--vantage-tone-warning-chip: rgb(245 158 11 / 0.14);
|
|
43
|
+
--vantage-tone-warning-ink: #b45309;
|
|
44
|
+
|
|
45
|
+
--vantage-tone-caution-accent: #dc2626;
|
|
46
|
+
--vantage-tone-caution-wash: rgb(220 38 38 / 0.1);
|
|
47
|
+
--vantage-tone-caution-chip: rgb(220 38 38 / 0.12);
|
|
48
|
+
--vantage-tone-caution-ink: #b91c1c;
|
|
49
|
+
|
|
50
|
+
--vantage-tone-muted-accent: #64748b;
|
|
51
|
+
--vantage-tone-muted-wash: rgb(100 116 139 / 0.1);
|
|
52
|
+
--vantage-tone-muted-chip: rgb(100 116 139 / 0.12);
|
|
53
|
+
--vantage-tone-muted-ink: #475569;
|
|
54
|
+
|
|
55
|
+
/* 5px, not 3px. At 3px the rule was legible when you looked at it and
|
|
56
|
+
invisible when you scrolled past it — and, worse, three of the six tones
|
|
57
|
+
were indistinguishable from each other at that width, so the treatment
|
|
58
|
+
said "marked" without saying *how*. Hue needs area to read as hue. */
|
|
59
|
+
--vantage-tone-rule-width: 5px;
|
|
60
|
+
--vantage-tone-rule-offset: 0.75rem;
|
|
61
|
+
--vantage-tone-run-bleed: 2.5rem;
|
|
62
|
+
/* An alert draws a real left border rather than a positioned slice, so it
|
|
63
|
+
gets its own width: it is a single box and has no run to line up with. */
|
|
64
|
+
--vantage-alert-rule-width: 4px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ── Tone palette (dark) ────────────────────────────────────────────────── */
|
|
68
|
+
/* `.dark` is the app's dark selector (`@custom-variant dark` in the host
|
|
69
|
+
stylesheet), so a package consumer gets dark tones by toggling that class —
|
|
70
|
+
the same switch the rest of the app already uses. */
|
|
71
|
+
.dark {
|
|
72
|
+
--vantage-tone-note-accent: #60a5fa;
|
|
73
|
+
--vantage-tone-note-wash: rgb(96 165 250 / 0.16);
|
|
74
|
+
--vantage-tone-note-chip: rgb(59 130 246 / 0.22);
|
|
75
|
+
--vantage-tone-note-ink: #93c5fd;
|
|
76
|
+
|
|
77
|
+
--vantage-tone-tip-accent: #4ade80;
|
|
78
|
+
--vantage-tone-tip-wash: rgb(74 222 128 / 0.16);
|
|
79
|
+
--vantage-tone-tip-chip: rgb(34 197 94 / 0.22);
|
|
80
|
+
--vantage-tone-tip-ink: #86efac;
|
|
81
|
+
|
|
82
|
+
--vantage-tone-important-accent: #a78bfa;
|
|
83
|
+
--vantage-tone-important-wash: rgb(167 139 250 / 0.16);
|
|
84
|
+
--vantage-tone-important-chip: rgb(139 92 246 / 0.22);
|
|
85
|
+
--vantage-tone-important-ink: #c4b5fd;
|
|
86
|
+
|
|
87
|
+
--vantage-tone-warning-accent: #fbbf24;
|
|
88
|
+
--vantage-tone-warning-wash: rgb(245 158 11 / 0.16);
|
|
89
|
+
--vantage-tone-warning-chip: rgb(245 158 11 / 0.18);
|
|
90
|
+
--vantage-tone-warning-ink: #fcd34d;
|
|
91
|
+
|
|
92
|
+
--vantage-tone-caution-accent: #f87171;
|
|
93
|
+
--vantage-tone-caution-wash: rgb(248 113 113 / 0.16);
|
|
94
|
+
--vantage-tone-caution-chip: rgb(239 68 68 / 0.2);
|
|
95
|
+
--vantage-tone-caution-ink: #fca5a5;
|
|
96
|
+
|
|
97
|
+
--vantage-tone-muted-accent: #94a3b8;
|
|
98
|
+
--vantage-tone-muted-wash: rgb(148 163 184 / 0.14);
|
|
99
|
+
--vantage-tone-muted-chip: rgb(148 163 184 / 0.22);
|
|
100
|
+
--vantage-tone-muted-ink: #cbd5e1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* ── Token → property resolution ────────────────────────────────────────── */
|
|
104
|
+
/* Six rules so every rule below is tone-agnostic. An unrecognized token sets
|
|
105
|
+
nothing, which is what makes the treatment inert rather than gray (D2). */
|
|
106
|
+
[data-vantage-tone="note"] {
|
|
107
|
+
--vantage-tone-accent: var(--vantage-tone-note-accent);
|
|
108
|
+
--vantage-tone-wash: var(--vantage-tone-note-wash);
|
|
109
|
+
--vantage-tone-chip: var(--vantage-tone-note-chip);
|
|
110
|
+
--vantage-tone-ink: var(--vantage-tone-note-ink);
|
|
111
|
+
}
|
|
112
|
+
[data-vantage-tone="tip"] {
|
|
113
|
+
--vantage-tone-accent: var(--vantage-tone-tip-accent);
|
|
114
|
+
--vantage-tone-wash: var(--vantage-tone-tip-wash);
|
|
115
|
+
--vantage-tone-chip: var(--vantage-tone-tip-chip);
|
|
116
|
+
--vantage-tone-ink: var(--vantage-tone-tip-ink);
|
|
117
|
+
}
|
|
118
|
+
[data-vantage-tone="important"] {
|
|
119
|
+
--vantage-tone-accent: var(--vantage-tone-important-accent);
|
|
120
|
+
--vantage-tone-wash: var(--vantage-tone-important-wash);
|
|
121
|
+
--vantage-tone-chip: var(--vantage-tone-important-chip);
|
|
122
|
+
--vantage-tone-ink: var(--vantage-tone-important-ink);
|
|
123
|
+
}
|
|
124
|
+
[data-vantage-tone="warning"] {
|
|
125
|
+
--vantage-tone-accent: var(--vantage-tone-warning-accent);
|
|
126
|
+
--vantage-tone-wash: var(--vantage-tone-warning-wash);
|
|
127
|
+
--vantage-tone-chip: var(--vantage-tone-warning-chip);
|
|
128
|
+
--vantage-tone-ink: var(--vantage-tone-warning-ink);
|
|
129
|
+
}
|
|
130
|
+
[data-vantage-tone="caution"] {
|
|
131
|
+
--vantage-tone-accent: var(--vantage-tone-caution-accent);
|
|
132
|
+
--vantage-tone-wash: var(--vantage-tone-caution-wash);
|
|
133
|
+
--vantage-tone-chip: var(--vantage-tone-caution-chip);
|
|
134
|
+
--vantage-tone-ink: var(--vantage-tone-caution-ink);
|
|
135
|
+
}
|
|
136
|
+
[data-vantage-tone="muted"] {
|
|
137
|
+
--vantage-tone-accent: var(--vantage-tone-muted-accent);
|
|
138
|
+
--vantage-tone-wash: var(--vantage-tone-muted-wash);
|
|
139
|
+
--vantage-tone-chip: var(--vantage-tone-muted-chip);
|
|
140
|
+
--vantage-tone-ink: var(--vantage-tone-muted-ink);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ── Alert → property resolution ────────────────────────────────────────── */
|
|
144
|
+
/* Separate names from `--vantage-tone-*`, and the separation is a bug fix, not
|
|
145
|
+
tidiness. Custom properties INHERIT. A `> [!CAUTION]` inside
|
|
146
|
+
`<!-- vantage: section tone=important -->` is both an alert and a stamped run
|
|
147
|
+
member, so resolving both onto `--vantage-tone-accent` made the alert's kind
|
|
148
|
+
win on that element — and the section's own vertical rule turned red for the
|
|
149
|
+
height of the alert, and red again for the 2.5rem it bleeds upward. The
|
|
150
|
+
section read as three colors and looked broken. Distinct names let one
|
|
151
|
+
element carry both facts without either overwriting the other.
|
|
152
|
+
|
|
153
|
+
The VALUES are still the tone palette's, which is the whole point: an alert
|
|
154
|
+
and the tone of the same name resolve to the same four properties, so they
|
|
155
|
+
cannot drift and a new theme is still one custom-property block. */
|
|
156
|
+
[data-vantage-alert="note"] {
|
|
157
|
+
--vantage-alert-accent: var(--vantage-tone-note-accent);
|
|
158
|
+
--vantage-alert-wash: var(--vantage-tone-note-wash);
|
|
159
|
+
--vantage-alert-chip: var(--vantage-tone-note-chip);
|
|
160
|
+
--vantage-alert-ink: var(--vantage-tone-note-ink);
|
|
161
|
+
}
|
|
162
|
+
[data-vantage-alert="tip"] {
|
|
163
|
+
--vantage-alert-accent: var(--vantage-tone-tip-accent);
|
|
164
|
+
--vantage-alert-wash: var(--vantage-tone-tip-wash);
|
|
165
|
+
--vantage-alert-chip: var(--vantage-tone-tip-chip);
|
|
166
|
+
--vantage-alert-ink: var(--vantage-tone-tip-ink);
|
|
167
|
+
}
|
|
168
|
+
[data-vantage-alert="important"] {
|
|
169
|
+
--vantage-alert-accent: var(--vantage-tone-important-accent);
|
|
170
|
+
--vantage-alert-wash: var(--vantage-tone-important-wash);
|
|
171
|
+
--vantage-alert-chip: var(--vantage-tone-important-chip);
|
|
172
|
+
--vantage-alert-ink: var(--vantage-tone-important-ink);
|
|
173
|
+
}
|
|
174
|
+
[data-vantage-alert="warning"] {
|
|
175
|
+
--vantage-alert-accent: var(--vantage-tone-warning-accent);
|
|
176
|
+
--vantage-alert-wash: var(--vantage-tone-warning-wash);
|
|
177
|
+
--vantage-alert-chip: var(--vantage-tone-warning-chip);
|
|
178
|
+
--vantage-alert-ink: var(--vantage-tone-warning-ink);
|
|
179
|
+
}
|
|
180
|
+
[data-vantage-alert="caution"] {
|
|
181
|
+
--vantage-alert-accent: var(--vantage-tone-caution-accent);
|
|
182
|
+
--vantage-alert-wash: var(--vantage-tone-caution-wash);
|
|
183
|
+
--vantage-alert-chip: var(--vantage-tone-caution-chip);
|
|
184
|
+
--vantage-alert-ink: var(--vantage-tone-caution-ink);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* ── The section rule ───────────────────────────────────────────────────── */
|
|
188
|
+
/* Every block in a section is stamped, so a per-element border+background
|
|
189
|
+
renders one section as N stacked boxes. Instead each member draws a slice of
|
|
190
|
+
one continuous vertical rule, positioned OUT of its own box (the left gutter
|
|
191
|
+
is already spoken for by line anchors, review highlights and blockquotes) and bled UPWARD to meet its predecessor. Bleeding only upward is
|
|
192
|
+
what makes the run terminate exactly at its first and last member without a
|
|
193
|
+
:last-of-run selector — which is unexpressible here, since members are
|
|
194
|
+
siblings and review mode inserts comment <div>s between them.
|
|
195
|
+
`--vantage-tone-heading-gutter` is set by the host app on toned headings
|
|
196
|
+
only; 1.5em resolves per heading level, which is the only way h2, h3 and p
|
|
197
|
+
share one x. Measured: the pseudo's computed `left` puts all eight block
|
|
198
|
+
types on the same pixel — which is a statement about where the slice would
|
|
199
|
+
go, NOT about whether it paints. Two of the eight clip it away entirely; the
|
|
200
|
+
escape below is what makes them paint, and a pixel scan is the only thing
|
|
201
|
+
that can tell the two apart.
|
|
202
|
+
`background-color` is the longhand, and the accent `var()` has no fallback,
|
|
203
|
+
on purpose: an unrecognized tone leaves the variable unset, the value is
|
|
204
|
+
invalid at computed-value time, and the rule computes to `transparent` — D2
|
|
205
|
+
inertness enforced by the cascade rather than by enumerating six tokens in
|
|
206
|
+
every selector. A "safety" fallback would paint every typo gray instead. */
|
|
207
|
+
/* `left` on an absolutely-positioned pseudo resolves from the PADDING edge of
|
|
208
|
+
its containing block, which is the member itself. So a member with a left
|
|
209
|
+
border of its own has its padding edge pushed inward by that border, and its
|
|
210
|
+
slice lands that far right of everyone else's — measured at 312 against 308
|
|
211
|
+
for the rest of a run, which on a 5px slice reads as a break in the rule
|
|
212
|
+
rather than as a bend. There is no CSS way to read an element's own border
|
|
213
|
+
width, so each bordered case declares the compensation, and it is declared
|
|
214
|
+
WHERE THE BORDER IS: this file for the alert's, the host stylesheet for the
|
|
215
|
+
ones Tailwind gives `blockquote` and `pre`. Same arrangement as
|
|
216
|
+
`--vantage-tone-heading-gutter`, and a test pins each pair equal.
|
|
217
|
+
|
|
218
|
+
The member is the slice's containing block only while it has a box. A
|
|
219
|
+
member written with `display: contents` has none, and its slice was placed
|
|
220
|
+
against an ancestor outside the scroll container: measured at the content
|
|
221
|
+
pane's full height, not scrolling with the document, and on a heading moved
|
|
222
|
+
across the pane by the heading's font size. The sanitizer refuses that
|
|
223
|
+
value (`DISPLAY_VALUES` in `sanitize.ts`), and
|
|
224
|
+
`frontend/e2e/boxless.spec.ts` measures that the slice stays in. */
|
|
225
|
+
[data-vantage-tone] {
|
|
226
|
+
position: relative;
|
|
227
|
+
}
|
|
228
|
+
[data-vantage-tone]::before {
|
|
229
|
+
content: "";
|
|
230
|
+
position: absolute;
|
|
231
|
+
top: 0;
|
|
232
|
+
bottom: 0;
|
|
233
|
+
left: calc(
|
|
234
|
+
var(--vantage-tone-heading-gutter, 0em) - var(--vantage-tone-rule-offset) -
|
|
235
|
+
var(--vantage-tone-border-compensation, 0px)
|
|
236
|
+
);
|
|
237
|
+
width: var(--vantage-tone-rule-width);
|
|
238
|
+
border-radius: var(--vantage-tone-rule-width);
|
|
239
|
+
background-color: var(--vantage-tone-accent);
|
|
240
|
+
pointer-events: none;
|
|
241
|
+
}
|
|
242
|
+
/* Positive, not `:not([data-vantage-run="start"])`: with the attribute missing
|
|
243
|
+
entirely (older plugin, newer CSS) this bleeds nothing and the run reads as
|
|
244
|
+
separate marks — degraded, never a rule hanging above the heading (D3). */
|
|
245
|
+
[data-vantage-tone]:is(
|
|
246
|
+
[data-vantage-run="middle"],
|
|
247
|
+
[data-vantage-run="end"]
|
|
248
|
+
)::before {
|
|
249
|
+
top: calc(-1 * var(--vantage-tone-run-bleed));
|
|
250
|
+
}
|
|
251
|
+
/* Six of the eight stampable tags compute `overflow: visible`. `pre` and `hr`
|
|
252
|
+
do not, and both clip the rule to NOTHING: `@tailwindcss/typography` gives
|
|
253
|
+
`pre` `overflow-x: auto` so wide code scrolls, the UA sheet gives `hr`
|
|
254
|
+
`overflow: hidden`, and CSS forces a computed `overflow` of `visible` to
|
|
255
|
+
`auto` on the other axis when its partner is not `visible`. So each of those
|
|
256
|
+
two is simultaneously the containing block for its own `::before` (the
|
|
257
|
+
`position: relative` above) and a clip box that discards it at the padding
|
|
258
|
+
edge on BOTH axes — the member paints neither its own slice nor its own
|
|
259
|
+
upward bleed. Measured in Chrome over the real Tailwind build of a nine-block
|
|
260
|
+
run: a three-line fence left one contiguous 100px void where the rule should
|
|
261
|
+
be, scaling linearly with the fence (168px for a 133px fence), which the
|
|
262
|
+
fixed 40px bleed can never repair because the void is fence height + 40px.
|
|
263
|
+
That is exactly the "gap taller than the bleed" `data-vantage-run` exists to
|
|
264
|
+
prevent, and the section reads as two sections.
|
|
265
|
+
|
|
266
|
+
So un-clip the box and re-home the scroller on the `code` child. Wide code
|
|
267
|
+
still scrolls horizontally — measured in a 600px viewport,
|
|
268
|
+
`pre.scrollWidth == pre.clientWidth == 518` while `code.scrollWidth` 752 >
|
|
269
|
+
`code.clientWidth` 486 and `code.scrollLeft` reaches 266 — the scrollbar sits
|
|
270
|
+
inside the fence's padding instead of on its border edge. The app already
|
|
271
|
+
sets `display: block` on `.prose pre code`; it is repeated here so the
|
|
272
|
+
package's own stylesheet and an external consumer get a scrollable box too.
|
|
273
|
+
Do NOT "tidy this away" as dead CSS: `left` computes identically with and
|
|
274
|
+
without it, so nothing but a pixel read can see the difference. */
|
|
275
|
+
[data-vantage-tone]:is(pre, hr) {
|
|
276
|
+
overflow: visible;
|
|
277
|
+
}
|
|
278
|
+
pre[data-vantage-tone] > code {
|
|
279
|
+
display: block;
|
|
280
|
+
overflow-x: auto;
|
|
281
|
+
}
|
|
282
|
+
/* An `hr` is 1-2px of box between two 3em margins — 48px, more than the 40px a
|
|
283
|
+
neighbor can bleed across — so a thematic break inside a section is the one
|
|
284
|
+
member whose rule also bleeds DOWNWARD, and by its own margin rather than by
|
|
285
|
+
the shared bleed. 3.5em, not 3em: every other member's 40px bleed overlaps its
|
|
286
|
+
predecessor's border edge by at least 8px, and an exact fit leaves a 1px
|
|
287
|
+
hairline to subpixel rounding — measured, before this was 3.5em.
|
|
288
|
+
Both selectors stay positive for the same D3 reason as the rule above: with
|
|
289
|
+
`data-vantage-run` absent nothing bleeds, and the `start` member never bleeds
|
|
290
|
+
up nor the `end` member down, so the run still terminates exactly at its
|
|
291
|
+
first and last member. */
|
|
292
|
+
hr[data-vantage-tone]:is(
|
|
293
|
+
[data-vantage-run="middle"],
|
|
294
|
+
[data-vantage-run="end"]
|
|
295
|
+
)::before {
|
|
296
|
+
top: -3.5em;
|
|
297
|
+
}
|
|
298
|
+
hr[data-vantage-tone]:is(
|
|
299
|
+
[data-vantage-run="start"],
|
|
300
|
+
[data-vantage-run="middle"]
|
|
301
|
+
)::before {
|
|
302
|
+
bottom: -3.5em;
|
|
303
|
+
}
|
|
304
|
+
/* An image or a drawing written as an HTML block of its own is a member like
|
|
305
|
+
any other — `styleRange` stamps every element in the span — but `img` and
|
|
306
|
+
`svg` are replaced elements, and a replaced element generates no `::before`
|
|
307
|
+
at all. So the rule simply stopped beside one. Measured in Chrome over the
|
|
308
|
+
real build: an 84px hole beside a 92px `<img>`, and 96px beside an 80px bare
|
|
309
|
+
`<svg>` and the row of images under it, against the 40px a neighbor bleeds.
|
|
310
|
+
The same drawing inside a `<div>` never had the problem, because there the
|
|
311
|
+
`<div>` is the member.
|
|
312
|
+
|
|
313
|
+
So these two draw their slice as a border image: one flat color, sliced so
|
|
314
|
+
that only the left edge region has any width, and pushed out of the box by
|
|
315
|
+
`border-image-outset`, which moves ink without moving layout. Both offsets
|
|
316
|
+
are the shared ones. They are measured from the border box, which for a
|
|
317
|
+
member with no border of its own is the padding box every `::before` is
|
|
318
|
+
measured from, so the slice lands on the same x as the rest of the run and
|
|
319
|
+
bleeds upward by the same amount. Not a `box-shadow` clipped to a strip: the
|
|
320
|
+
app rounds every image (`prose-img:rounded-lg`), and a shadow also fills the
|
|
321
|
+
corners outside the radius, which a clip that keeps the image cannot remove.
|
|
322
|
+
|
|
323
|
+
Drawn from the member's own box, the slice is on the rule only if that box
|
|
324
|
+
starts at the column's edge, and CSS cannot see where a line starts. So it is
|
|
325
|
+
withheld wherever the box may not. That is an image floated by `align` or
|
|
326
|
+
`float`, or offset by a `margin` or by `hspace`. `hspace` offsets an image
|
|
327
|
+
only where no stylesheet resets its margin: in the package's own viewer
|
|
328
|
+
`hspace="30"` moves it 30px in, but in the app Tailwind's preflight gives
|
|
329
|
+
every element `margin: 0`, which outranks the attribute, so there the image
|
|
330
|
+
stays at the edge and loses its slice all the same. A selector cannot see
|
|
331
|
+
which of the two it is in. It is also an image that comes after inline
|
|
332
|
+
content, which shares that content's line: in a row of images written on
|
|
333
|
+
consecutive lines that is every image but the first, whose slice already
|
|
334
|
+
covers the line. A gap is what these had before; a slice standing in the
|
|
335
|
+
middle of the column would be a new failure. The elements listed are the
|
|
336
|
+
inline ones the sanitizer admits, less `br`, which ends the line, and less
|
|
337
|
+
`svg`, `picture` and `span.katex-display`, which Tailwind's preflight,
|
|
338
|
+
typography and KaTeX each make a block in the app. And the slice is withheld
|
|
339
|
+
where the document drew a border, because a border image replaces the border
|
|
340
|
+
styles on all four sides: it would erase that border, not join it.
|
|
341
|
+
`frontend/e2e/directive_tone_rule.spec.ts` scans the images, the drawings
|
|
342
|
+
and the row in pixels. */
|
|
343
|
+
[data-vantage-tone]:is(img, svg) {
|
|
344
|
+
border-image-source: linear-gradient(
|
|
345
|
+
var(--vantage-tone-accent),
|
|
346
|
+
var(--vantage-tone-accent)
|
|
347
|
+
);
|
|
348
|
+
border-image-slice: 1;
|
|
349
|
+
border-image-width: 0 0 0 var(--vantage-tone-rule-width);
|
|
350
|
+
border-image-outset: 0 0 0 var(--vantage-tone-rule-offset);
|
|
351
|
+
}
|
|
352
|
+
[data-vantage-tone]:is(img, svg):is(
|
|
353
|
+
[data-vantage-run="middle"],
|
|
354
|
+
[data-vantage-run="end"]
|
|
355
|
+
) {
|
|
356
|
+
border-image-outset: var(--vantage-tone-run-bleed) 0 0
|
|
357
|
+
var(--vantage-tone-rule-offset);
|
|
358
|
+
}
|
|
359
|
+
[data-vantage-tone]:is(img, svg):is(
|
|
360
|
+
[align="left" i],
|
|
361
|
+
[align="right" i],
|
|
362
|
+
[hspace],
|
|
363
|
+
[style*="float" i],
|
|
364
|
+
[style*="margin" i],
|
|
365
|
+
[style*="border" i],
|
|
366
|
+
[border]:not([border="0"])
|
|
367
|
+
),
|
|
368
|
+
:is(
|
|
369
|
+
a,
|
|
370
|
+
b,
|
|
371
|
+
code,
|
|
372
|
+
del,
|
|
373
|
+
em,
|
|
374
|
+
i,
|
|
375
|
+
img,
|
|
376
|
+
input,
|
|
377
|
+
ins,
|
|
378
|
+
kbd,
|
|
379
|
+
math,
|
|
380
|
+
q,
|
|
381
|
+
ruby,
|
|
382
|
+
s,
|
|
383
|
+
samp,
|
|
384
|
+
span:not(.katex-display),
|
|
385
|
+
strike,
|
|
386
|
+
strong,
|
|
387
|
+
sub,
|
|
388
|
+
sup,
|
|
389
|
+
tt,
|
|
390
|
+
var
|
|
391
|
+
)
|
|
392
|
+
+ img[data-vantage-tone] {
|
|
393
|
+
border-image-source: none;
|
|
394
|
+
}
|
|
395
|
+
/* A lone block has no run to stripe, so it gets the wash — this is the
|
|
396
|
+
`<!-- vantage: block tone=important -->` callout. `:where()` holds the
|
|
397
|
+
selector at one-class specificity on purpose: the review and line-anchor
|
|
398
|
+
backgrounds are also one class and are declared later in the host
|
|
399
|
+
stylesheet, so a transient state wins the tie. Writing this as
|
|
400
|
+
[data-vantage-tone][data-vantage-run="only"] silently eats them. */
|
|
401
|
+
[data-vantage-tone]:where([data-vantage-run="only"]) {
|
|
402
|
+
background-color: var(--vantage-tone-wash);
|
|
403
|
+
border-radius: 4px;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* ── GFM alerts ─────────────────────────────────────────────────────────── */
|
|
407
|
+
/* `> [!WARNING]`, compiled to `data-vantage-alert` by `rehypeVantageAlerts`.
|
|
408
|
+
Reads the same four per-tone properties the `tone` vocabulary resolves, so an
|
|
409
|
+
alert and `<!-- vantage: block tone=warning -->` cannot drift apart and a new
|
|
410
|
+
theme is still one custom-property block.
|
|
411
|
+
|
|
412
|
+
Three overrides of `@tailwindcss/typography` are load-bearing, and all three
|
|
413
|
+
are why an unrendered alert looked as bad as it did rather than merely plain:
|
|
414
|
+
typography italicizes `blockquote`, grays its text, and draws `open-quote` /
|
|
415
|
+
`close-quote` around the first paragraph — so a callout rendered as an italic
|
|
416
|
+
*quotation* whose opening words were the literal `[!WARNING]`. This file is
|
|
417
|
+
unlayered, which is the only reason these plain attribute selectors beat the
|
|
418
|
+
`prose-blockquote:*` utilities; see the header. */
|
|
419
|
+
[data-vantage-alert] {
|
|
420
|
+
/* This file declares the alert's left border, so it owns the compensation
|
|
421
|
+
that keeps a toned alert's section slice on the run's x. */
|
|
422
|
+
--vantage-tone-border-compensation: var(--vantage-alert-rule-width);
|
|
423
|
+
margin: 1.25em 0;
|
|
424
|
+
padding: 0.75em 1em;
|
|
425
|
+
border-radius: 6px;
|
|
426
|
+
border: 1px solid var(--vantage-alert-chip);
|
|
427
|
+
border-left: var(--vantage-alert-rule-width) solid var(--vantage-alert-accent);
|
|
428
|
+
background-color: var(--vantage-alert-wash);
|
|
429
|
+
font-style: normal;
|
|
430
|
+
/* Typography gives `blockquote` BOTH `font-style: italic` and
|
|
431
|
+
`font-weight: 500`. Resetting only the italic left every alert's body a
|
|
432
|
+
half-step bolder than the prose around it — subtle enough to read as "this
|
|
433
|
+
callout is shouting" without anyone being able to say why, and measured at
|
|
434
|
+
500 against 400 for an ordinary paragraph. */
|
|
435
|
+
font-weight: 400;
|
|
436
|
+
color: inherit;
|
|
437
|
+
/* Typography sets this on blockquotes to hang the quote mark; an alert has no
|
|
438
|
+
quote mark and the indent leaves its first line looking broken. */
|
|
439
|
+
quotes: none;
|
|
440
|
+
}
|
|
441
|
+
[data-vantage-alert] > :first-child {
|
|
442
|
+
margin-top: 0;
|
|
443
|
+
}
|
|
444
|
+
[data-vantage-alert] > :last-child {
|
|
445
|
+
margin-bottom: 0;
|
|
446
|
+
}
|
|
447
|
+
/* The generated quotation marks. `content: none` rather than `""` so the
|
|
448
|
+
pseudo-element is not generated at all — typography also gives it a
|
|
449
|
+
`margin-right`, which an empty string would keep. */
|
|
450
|
+
[data-vantage-alert] p:first-of-type::before,
|
|
451
|
+
[data-vantage-alert] p:last-of-type::after {
|
|
452
|
+
content: none;
|
|
453
|
+
}
|
|
454
|
+
/* The title is a real element, not CSS `content`: this plugin runs in the shared
|
|
455
|
+
pipeline, so every renderer emits it — unlike the collapse caret, which app
|
|
456
|
+
JS injects and therefore had to stay out of the document's text. A `div`
|
|
457
|
+
rather than a `p` so it carries no `data-source-line` and cannot become the
|
|
458
|
+
block a review comment anchors to. */
|
|
459
|
+
.vantage-alert-title {
|
|
460
|
+
margin: 0 0 0.25em;
|
|
461
|
+
color: var(--vantage-alert-ink);
|
|
462
|
+
font-weight: 600;
|
|
463
|
+
font-style: normal;
|
|
464
|
+
line-height: 1.5;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/* ── Emphasis ───────────────────────────────────────────────────────────── */
|
|
468
|
+
/* `normal` is the absence of a rule — no declaration needed, and none written.
|
|
469
|
+
Emphasis modulates the tone rule rather than drawing one of its own, so
|
|
470
|
+
"what it is" and "how loud" stay separable and the geometry stays in one
|
|
471
|
+
place. Headings, pre and table are excluded from the weight bump because
|
|
472
|
+
this stylesheet is unlayered and would otherwise beat
|
|
473
|
+
`prose-headings:font-semibold` and de-bold a toned heading to 500. */
|
|
474
|
+
[data-vantage-emphasis="strong"] {
|
|
475
|
+
--vantage-tone-rule-width: 6px;
|
|
476
|
+
}
|
|
477
|
+
[data-vantage-emphasis="strong"]:not(:is(h1, h2, h3, h4, h5, h6, pre, table)) {
|
|
478
|
+
font-weight: 500;
|
|
479
|
+
}
|
|
480
|
+
/* `quiet` is quiet, not invisible. It used to fade the whole block to 0.72,
|
|
481
|
+
which on muted gray text put the result close to the page and made a
|
|
482
|
+
de-emphasized paragraph genuinely hard to read — a legibility loss, which is
|
|
483
|
+
nearer content loss than decoration, and the same reason print restores it.
|
|
484
|
+
The volume now lives mostly in the rule width, where it costs no contrast:
|
|
485
|
+
4px against `normal`'s 5px and `strong`'s 6px. The remaining fade is slight
|
|
486
|
+
and deliberate. */
|
|
487
|
+
[data-vantage-emphasis="quiet"] {
|
|
488
|
+
--vantage-tone-rule-width: 4px;
|
|
489
|
+
opacity: 0.88;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/* ── The chip ───────────────────────────────────────────────────────────── */
|
|
493
|
+
/* One rule, two subjects: the `badge=` chip can only be a pseudo-element
|
|
494
|
+
(stamp, don't wrap), and the frontmatter status chip is a real element. A
|
|
495
|
+
pseudo cannot take a class, so the geometry is shared by selector list.
|
|
496
|
+
Numbers lifted from the app's `.review-status-badge` so the two chips in the
|
|
497
|
+
product are the same object. */
|
|
498
|
+
.vantage-chip,
|
|
499
|
+
[data-vantage-badge]::after {
|
|
500
|
+
display: inline-block;
|
|
501
|
+
padding: 1px 8px;
|
|
502
|
+
border-radius: 4px;
|
|
503
|
+
font-family: inherit;
|
|
504
|
+
font-size: var(--vantage-chip-size, 11px);
|
|
505
|
+
font-weight: 600;
|
|
506
|
+
line-height: 1.6;
|
|
507
|
+
letter-spacing: 0.05em;
|
|
508
|
+
text-transform: uppercase;
|
|
509
|
+
white-space: nowrap;
|
|
510
|
+
vertical-align: 0.12em;
|
|
511
|
+
color: var(--vantage-chip-ink, var(--vantage-tone-muted-ink));
|
|
512
|
+
background-color: var(--vantage-chip-wash, var(--vantage-tone-muted-chip));
|
|
513
|
+
}
|
|
514
|
+
/* The badge scales with its heading; the standalone chip stays 11px. */
|
|
515
|
+
[data-vantage-badge]::after {
|
|
516
|
+
content: attr(data-vantage-badge);
|
|
517
|
+
margin-left: 0.5em;
|
|
518
|
+
font-weight: 600;
|
|
519
|
+
--vantage-chip-size: clamp(10px, 0.5em, 12px);
|
|
520
|
+
}
|
|
521
|
+
[data-vantage-badge="wip"]::after,
|
|
522
|
+
.vantage-chip--note {
|
|
523
|
+
--vantage-chip-ink: var(--vantage-tone-note-ink);
|
|
524
|
+
--vantage-chip-wash: var(--vantage-tone-note-chip);
|
|
525
|
+
}
|
|
526
|
+
[data-vantage-badge="done"]::after,
|
|
527
|
+
.vantage-chip--tip {
|
|
528
|
+
--vantage-chip-ink: var(--vantage-tone-tip-ink);
|
|
529
|
+
--vantage-chip-wash: var(--vantage-tone-tip-chip);
|
|
530
|
+
}
|
|
531
|
+
[data-vantage-badge="stale"]::after,
|
|
532
|
+
.vantage-chip--warning {
|
|
533
|
+
--vantage-chip-ink: var(--vantage-tone-warning-ink);
|
|
534
|
+
--vantage-chip-wash: var(--vantage-tone-warning-chip);
|
|
535
|
+
}
|
|
536
|
+
[data-vantage-badge="blocked"]::after,
|
|
537
|
+
.vantage-chip--caution {
|
|
538
|
+
--vantage-chip-ink: var(--vantage-tone-caution-ink);
|
|
539
|
+
--vantage-chip-wash: var(--vantage-tone-caution-chip);
|
|
540
|
+
}
|
|
541
|
+
[data-vantage-badge="draft"]::after,
|
|
542
|
+
.vantage-chip--muted {
|
|
543
|
+
--vantage-chip-ink: var(--vantage-tone-muted-ink);
|
|
544
|
+
--vantage-chip-wash: var(--vantage-tone-muted-chip);
|
|
545
|
+
}
|
|
546
|
+
/* No badge word maps onto `important` — the badge vocabulary is five words and
|
|
547
|
+
the tone vocabulary is six — but a chip consumer names a tone, not a badge,
|
|
548
|
+
so the modifier exists for all six. One per tone, guarded by a test. */
|
|
549
|
+
.vantage-chip--important {
|
|
550
|
+
--vantage-chip-ink: var(--vantage-tone-important-ink);
|
|
551
|
+
--vantage-chip-wash: var(--vantage-tone-important-chip);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/* ── File-scope chrome ──────────────────────────────────────────────────── */
|
|
555
|
+
/* The strip the document status chip sits in — the first element of the content
|
|
556
|
+
column, above the metadata card (`vantage:` frontmatter, §4.5). A row rather
|
|
557
|
+
than a bare chip because this is where any later file-scoped chrome joins it,
|
|
558
|
+
and because the flow needs the gap to the card whether or not a card follows.
|
|
559
|
+
`text-transform` on `.vantage-chip` above is what makes the chip read as a
|
|
560
|
+
label while the element's text content stays the lowercase token every other
|
|
561
|
+
renderer would see (D5). */
|
|
562
|
+
.vantage-chrome {
|
|
563
|
+
display: flex;
|
|
564
|
+
align-items: center;
|
|
565
|
+
gap: 0.5rem;
|
|
566
|
+
margin-bottom: 0.75rem;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/* ── Collapse ───────────────────────────────────────────────────────────── */
|
|
570
|
+
/* The hiding rule is TRIPLE-GATED, and all three gates are load-bearing.
|
|
571
|
+
|
|
572
|
+
`[data-vantage-collapse-ready]` is set on the prose container by the toggle
|
|
573
|
+
JS, *after* it has attached its handlers. A renderer without that JS — the CLI
|
|
574
|
+
checker's `renderMarkdown` HTML, an external consumer of this stylesheet —
|
|
575
|
+
never sets it, so nothing is hidden and the whole document reads. Hidden
|
|
576
|
+
content with no way to reveal it is content loss, not a style (P1/D8).
|
|
577
|
+
|
|
578
|
+
`[data-vantage-collapse-armed]` is the same invariant one block at a time, and
|
|
579
|
+
the container marker is not a substitute for it: "a control exists somewhere in
|
|
580
|
+
this document" is not "this block can be reopened". The toggle JS arms only the
|
|
581
|
+
blocks of a group it actually gave a caret, so a group whose toggle is missing
|
|
582
|
+
or unusable — and a bare `data-vantage-collapsed="true"` written in raw HTML
|
|
583
|
+
with no group at all, which the sanitizer allows by name and value — stays on
|
|
584
|
+
the page instead of becoming text nothing can reveal. It is deliberately an
|
|
585
|
+
attribute no document can write.
|
|
586
|
+
|
|
587
|
+
`@media not print` is the third gate, and it is not the same thing as a
|
|
588
|
+
`display: revert` counter-rule: `not print` means this declaration does not
|
|
589
|
+
exist in the print stylesheet, so no third rule can defeat it. A collapsed
|
|
590
|
+
section that printed closed would be content loss on paper (D7).
|
|
591
|
+
|
|
592
|
+
Residual, recorded honestly: `none` is one of the sanitizer's
|
|
593
|
+
`DISPLAY_VALUES`, so a document can still hide its own block with
|
|
594
|
+
`style="display: none"`. That allowance, not this rule, is the boundary — what
|
|
595
|
+
these gates guarantee is that nothing *this pipeline* stamps is hidden without
|
|
596
|
+
a control that opens it. */
|
|
597
|
+
@media not print {
|
|
598
|
+
[data-vantage-collapse-ready]
|
|
599
|
+
[data-vantage-collapsed="true"][data-vantage-collapse-armed] {
|
|
600
|
+
display: none;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/* The caret is a REAL element the toggle JS injects, not a third pseudo:
|
|
604
|
+
`::before` is already the tone rule and `::after` the badge, and one heading
|
|
605
|
+
can carry all three. It is a `<button>` so that it is keyboard-operable, so
|
|
606
|
+
that `aria-expanded` sits on an element allowed to carry it, and — measured —
|
|
607
|
+
because the app's review-mode click handler bails on `button`, which is what
|
|
608
|
+
keeps a toggle click from also opening the comment popover.
|
|
609
|
+
|
|
610
|
+
The glyph is drawn here rather than written into the element's text: the
|
|
611
|
+
document's text content then stays byte-identical to what every other renderer
|
|
612
|
+
produces, so no block hash, no clipboard payload and no anchor shifts because
|
|
613
|
+
the JS ran (D5). Note the heading itself deliberately gets no `cursor:
|
|
614
|
+
pointer` — it is not the control, and in review mode a click on it belongs to
|
|
615
|
+
the comment popover. */
|
|
616
|
+
.vantage-collapse-caret {
|
|
617
|
+
display: inline-block;
|
|
618
|
+
width: 1em;
|
|
619
|
+
margin: 0 0.15em 0 0;
|
|
620
|
+
padding: 0;
|
|
621
|
+
border: 0;
|
|
622
|
+
background-color: transparent;
|
|
623
|
+
color: var(--vantage-tone-muted-accent);
|
|
624
|
+
font-size: 0.7em;
|
|
625
|
+
line-height: 1;
|
|
626
|
+
vertical-align: 0.15em;
|
|
627
|
+
cursor: pointer;
|
|
628
|
+
transition: transform 0.15s;
|
|
629
|
+
}
|
|
630
|
+
.vantage-collapse-caret::before {
|
|
631
|
+
content: "\25be";
|
|
632
|
+
}
|
|
633
|
+
[data-vantage-collapse-toggle] .vantage-collapse-caret[aria-expanded="false"] {
|
|
634
|
+
transform: rotate(-90deg);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/* ── Print (D7) ─────────────────────────────────────────────────────────── */
|
|
638
|
+
/* Decoration goes to ink-cheap monochrome; nothing informational is dropped.
|
|
639
|
+
`quiet` is restored to full opacity — a faded print is a legibility loss,
|
|
640
|
+
which is nearer content loss than decoration. #57606a is the gray the host
|
|
641
|
+
app's own print block already uses. Tone never sets `color`: the host's
|
|
642
|
+
print block forces `.prose, .prose *` to #1a1a1a with `!important`, so a
|
|
643
|
+
tone color would vanish in one of the three renderings D5 covers. */
|
|
644
|
+
@media print {
|
|
645
|
+
[data-vantage-tone]::before {
|
|
646
|
+
background-color: #57606a;
|
|
647
|
+
}
|
|
648
|
+
/* The same gray for the slice an image or a drawing draws itself. Less
|
|
649
|
+
specific than the rule that withholds the slice, which still wins here. */
|
|
650
|
+
[data-vantage-tone]:is(img, svg) {
|
|
651
|
+
border-image-source: linear-gradient(#57606a, #57606a);
|
|
652
|
+
}
|
|
653
|
+
[data-vantage-tone]:where([data-vantage-run="only"]) {
|
|
654
|
+
background-color: transparent;
|
|
655
|
+
}
|
|
656
|
+
[data-vantage-emphasis="quiet"] {
|
|
657
|
+
opacity: 1;
|
|
658
|
+
}
|
|
659
|
+
.vantage-chip,
|
|
660
|
+
[data-vantage-badge]::after {
|
|
661
|
+
padding: 0 6px;
|
|
662
|
+
border: 1px solid #57606a;
|
|
663
|
+
background-color: transparent;
|
|
664
|
+
color: #1a1a1a;
|
|
665
|
+
}
|
|
666
|
+
/* Every collapsed block prints (the rule above simply does not exist here), so
|
|
667
|
+
the caret would print as a control that cannot be operated on paper. */
|
|
668
|
+
.vantage-collapse-caret {
|
|
669
|
+
display: none;
|
|
670
|
+
}
|
|
671
|
+
}
|