@ponchia/ui 0.5.0 → 0.6.3

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 (196) hide show
  1. package/CHANGELOG.md +386 -4
  2. package/MIGRATIONS.json +14 -0
  3. package/README.md +29 -6
  4. package/annotations/index.d.ts +398 -276
  5. package/annotations/index.d.ts.map +1 -0
  6. package/annotations/index.js +350 -77
  7. package/behaviors/carousel.d.ts +28 -0
  8. package/behaviors/carousel.d.ts.map +1 -0
  9. package/behaviors/carousel.js +20 -16
  10. package/behaviors/combobox.d.ts +40 -0
  11. package/behaviors/combobox.d.ts.map +1 -0
  12. package/behaviors/combobox.js +111 -29
  13. package/behaviors/command.d.ts +41 -0
  14. package/behaviors/command.d.ts.map +1 -0
  15. package/behaviors/command.js +27 -15
  16. package/behaviors/connectors.d.ts +17 -0
  17. package/behaviors/connectors.d.ts.map +1 -0
  18. package/behaviors/connectors.js +7 -5
  19. package/behaviors/crosshair.d.ts +42 -0
  20. package/behaviors/crosshair.d.ts.map +1 -0
  21. package/behaviors/crosshair.js +23 -6
  22. package/behaviors/dialog.d.ts +20 -0
  23. package/behaviors/dialog.d.ts.map +1 -0
  24. package/behaviors/dialog.js +6 -2
  25. package/behaviors/disclosure.d.ts +10 -0
  26. package/behaviors/disclosure.d.ts.map +1 -0
  27. package/behaviors/disclosure.js +6 -2
  28. package/behaviors/dismissible.d.ts +10 -0
  29. package/behaviors/dismissible.d.ts.map +1 -0
  30. package/behaviors/dismissible.js +6 -2
  31. package/behaviors/forms.d.ts +27 -0
  32. package/behaviors/forms.d.ts.map +1 -0
  33. package/behaviors/forms.js +54 -13
  34. package/behaviors/glyph.d.ts +14 -0
  35. package/behaviors/glyph.d.ts.map +1 -0
  36. package/behaviors/glyph.js +28 -5
  37. package/behaviors/index.d.ts +31 -237
  38. package/behaviors/index.d.ts.map +1 -0
  39. package/behaviors/index.js +17 -0
  40. package/behaviors/inert.d.ts +20 -0
  41. package/behaviors/inert.d.ts.map +1 -0
  42. package/behaviors/inert.js +46 -0
  43. package/behaviors/internal.d.ts +25 -0
  44. package/behaviors/internal.d.ts.map +1 -0
  45. package/behaviors/internal.js +77 -1
  46. package/behaviors/legend.d.ts +35 -0
  47. package/behaviors/legend.d.ts.map +1 -0
  48. package/behaviors/legend.js +32 -2
  49. package/behaviors/menu.d.ts +16 -0
  50. package/behaviors/menu.d.ts.map +1 -0
  51. package/behaviors/menu.js +6 -2
  52. package/behaviors/modal.d.ts +41 -0
  53. package/behaviors/modal.d.ts.map +1 -0
  54. package/behaviors/modal.js +124 -0
  55. package/behaviors/popover.d.ts +28 -0
  56. package/behaviors/popover.d.ts.map +1 -0
  57. package/behaviors/popover.js +78 -7
  58. package/behaviors/spotlight.d.ts +17 -0
  59. package/behaviors/spotlight.d.ts.map +1 -0
  60. package/behaviors/spotlight.js +7 -5
  61. package/behaviors/table.d.ts +36 -0
  62. package/behaviors/table.d.ts.map +1 -0
  63. package/behaviors/table.js +84 -17
  64. package/behaviors/tabs.d.ts +20 -0
  65. package/behaviors/tabs.d.ts.map +1 -0
  66. package/behaviors/tabs.js +17 -14
  67. package/behaviors/theme.d.ts +54 -0
  68. package/behaviors/theme.d.ts.map +1 -0
  69. package/behaviors/theme.js +22 -3
  70. package/behaviors/toast.d.ts +49 -0
  71. package/behaviors/toast.d.ts.map +1 -0
  72. package/behaviors/toast.js +47 -3
  73. package/classes/classes.json +2527 -0
  74. package/classes/index.d.ts +134 -15
  75. package/classes/index.js +280 -80
  76. package/classes/vscode.css-custom-data.json +12 -0
  77. package/connectors/index.d.ts +201 -69
  78. package/connectors/index.d.ts.map +1 -0
  79. package/connectors/index.js +142 -25
  80. package/css/app.css +69 -13
  81. package/css/base.css +15 -10
  82. package/css/bullet.css +108 -0
  83. package/css/code.css +98 -0
  84. package/css/connectors.css +17 -0
  85. package/css/content.css +22 -3
  86. package/css/crosshair.css +7 -7
  87. package/css/dataviz.css +5 -1
  88. package/css/diff.css +153 -0
  89. package/css/disclosure.css +53 -7
  90. package/css/dots.css +94 -7
  91. package/css/feedback.css +97 -7
  92. package/css/forms.css +113 -4
  93. package/css/legend.css +16 -9
  94. package/css/marks.css +38 -8
  95. package/css/motion.css +98 -53
  96. package/css/navigation.css +7 -0
  97. package/css/overlay.css +90 -3
  98. package/css/primitives.css +158 -13
  99. package/css/report.css +73 -56
  100. package/css/sidenote.css +67 -0
  101. package/css/site.css +16 -2
  102. package/css/sources.css +43 -1
  103. package/css/spark.css +62 -0
  104. package/css/spotlight.css +1 -1
  105. package/css/table.css +9 -2
  106. package/css/term.css +110 -0
  107. package/css/textref.css +63 -0
  108. package/css/toc.css +91 -0
  109. package/css/tokens.css +49 -1
  110. package/css/tree.css +134 -0
  111. package/css/workbench.css +1 -1
  112. package/dist/bronto.css +1 -1
  113. package/dist/css/analytical.css +1 -1
  114. package/dist/css/app.css +1 -1
  115. package/dist/css/base.css +1 -1
  116. package/dist/css/bullet.css +1 -0
  117. package/dist/css/code.css +1 -0
  118. package/dist/css/connectors.css +1 -1
  119. package/dist/css/content.css +1 -1
  120. package/dist/css/crosshair.css +1 -1
  121. package/dist/css/diff.css +1 -0
  122. package/dist/css/disclosure.css +1 -1
  123. package/dist/css/dots.css +1 -1
  124. package/dist/css/feedback.css +1 -1
  125. package/dist/css/forms.css +1 -1
  126. package/dist/css/legend.css +1 -1
  127. package/dist/css/marks.css +1 -1
  128. package/dist/css/motion.css +1 -1
  129. package/dist/css/navigation.css +1 -1
  130. package/dist/css/overlay.css +1 -1
  131. package/dist/css/primitives.css +1 -1
  132. package/dist/css/report.css +1 -1
  133. package/dist/css/sidenote.css +1 -0
  134. package/dist/css/site.css +1 -1
  135. package/dist/css/sources.css +1 -1
  136. package/dist/css/spark.css +1 -0
  137. package/dist/css/spotlight.css +1 -1
  138. package/dist/css/table.css +1 -1
  139. package/dist/css/term.css +1 -0
  140. package/dist/css/textref.css +1 -0
  141. package/dist/css/toc.css +1 -0
  142. package/dist/css/tokens.css +1 -1
  143. package/dist/css/tree.css +1 -0
  144. package/dist/css/workbench.css +1 -1
  145. package/docs/adr/0003-theme-model.md +1 -1
  146. package/docs/annotations.md +133 -14
  147. package/docs/architecture.md +49 -6
  148. package/docs/bullet.md +78 -0
  149. package/docs/code.md +76 -0
  150. package/docs/contrast.md +116 -92
  151. package/docs/d2.md +196 -0
  152. package/docs/diff.md +146 -0
  153. package/docs/legends.md +23 -3
  154. package/docs/marks.md +9 -2
  155. package/docs/mermaid.md +169 -0
  156. package/docs/reference.md +201 -26
  157. package/docs/reporting.md +416 -57
  158. package/docs/sidenote.md +64 -0
  159. package/docs/sources.md +27 -0
  160. package/docs/spark.md +78 -0
  161. package/docs/stability.md +10 -2
  162. package/docs/term.md +81 -0
  163. package/docs/textref.md +78 -0
  164. package/docs/theming.md +44 -5
  165. package/docs/toc.md +83 -0
  166. package/docs/tree.md +74 -0
  167. package/docs/usage.md +354 -16
  168. package/docs/vega.md +244 -0
  169. package/docs/workbench.md +7 -1
  170. package/glyphs/glyphs.js +13 -5
  171. package/llms.txt +285 -14
  172. package/package.json +95 -17
  173. package/qwik/index.d.ts +44 -59
  174. package/qwik/index.d.ts.map +1 -0
  175. package/qwik/index.js +65 -3
  176. package/react/index.d.ts +41 -61
  177. package/react/index.d.ts.map +1 -0
  178. package/react/index.js +63 -3
  179. package/solid/index.d.ts +68 -61
  180. package/solid/index.d.ts.map +1 -0
  181. package/solid/index.js +66 -3
  182. package/tokens/d2.d.ts +38 -0
  183. package/tokens/d2.js +71 -0
  184. package/tokens/d2.json +43 -0
  185. package/tokens/index.d.ts +5 -5
  186. package/tokens/index.js +15 -1
  187. package/tokens/index.json +9 -0
  188. package/tokens/mermaid.d.ts +23 -0
  189. package/tokens/mermaid.js +181 -0
  190. package/tokens/mermaid.json +163 -0
  191. package/tokens/resolved.json +45 -1
  192. package/tokens/skins.js +3 -2
  193. package/tokens/tokens.dtcg.json +26 -0
  194. package/tokens/vega.d.ts +34 -0
  195. package/tokens/vega.js +155 -0
  196. package/tokens/vega.json +179 -0
package/css/app.css CHANGED
@@ -6,7 +6,7 @@
6
6
  .ui-app-shell {
7
7
  display: grid;
8
8
  grid-template-columns: var(--app-rail, 14rem) minmax(0, 1fr);
9
- min-block-size: 100vh;
9
+ min-block-size: 100dvh;
10
10
  }
11
11
 
12
12
  .ui-app-shell--full {
@@ -24,7 +24,7 @@
24
24
  padding: var(--space-md);
25
25
  position: sticky;
26
26
  inset-block-start: 0;
27
- block-size: 100vh;
27
+ block-size: 100svh;
28
28
  overflow-y: auto;
29
29
  }
30
30
 
@@ -34,6 +34,7 @@
34
34
  display: flex;
35
35
  font-family: var(--display);
36
36
  font-size: 1.05rem;
37
+ font-weight: var(--display-weight);
37
38
  gap: 0.5rem;
38
39
  letter-spacing: var(--tracking-wide);
39
40
  padding: 0.35rem 0.5rem;
@@ -87,21 +88,55 @@
87
88
  inline-size: 0.34rem;
88
89
  }
89
90
 
90
- .ui-app-nav a:hover {
91
- background: var(--bg-accent);
92
- color: var(--text);
91
+ @media (hover: hover) {
92
+ .ui-app-nav a:hover {
93
+ background: var(--bg-accent);
94
+ color: var(--text);
95
+ }
93
96
  }
94
97
 
95
- .ui-app-nav a.is-active {
98
+ /* Current page honours BOTH the `.is-active` class and `aria-current` — the
99
+ sibling `.ui-sitenav` signals current-page with `aria-current="page"`, so the
100
+ app-shell nav now accepts the same programmatic cue (author it for AT, not
101
+ just the visual class — C19). */
102
+ .ui-app-nav a.is-active,
103
+ .ui-app-nav a[aria-current]:not([aria-current='false']) {
96
104
  background: var(--accent-soft);
97
105
  border-inline-start-color: var(--accent);
98
106
  color: var(--accent-text);
99
107
  }
100
108
 
101
- .ui-app-nav a.is-active::before {
109
+ @media (pointer: coarse) {
110
+ .ui-app-nav a {
111
+ min-block-size: 2.9rem;
112
+ padding-inline: 0.9rem;
113
+ }
114
+ }
115
+
116
+ .ui-app-nav a.is-active::before,
117
+ .ui-app-nav a[aria-current]:not([aria-current='false'])::before {
102
118
  opacity: 1;
103
119
  }
104
120
 
121
+ /* Forced colours flatten var(--accent) (border + bg + text) to a single system
122
+ colour, so the active link collapses into its inactive siblings — only a
123
+ near-imperceptible tint/dot opacity delta survives, and current-page becomes
124
+ invisible to HCM users. Re-assert "current" on channels HCM preserves: a
125
+ Highlight start-border and marker dot, plus a NON-colour bold weight so the
126
+ cue does not rely on colour alone (WCAG 1.4.1). (component audit C8.) */
127
+ @media (forced-colors: active) {
128
+ .ui-app-nav a.is-active,
129
+ .ui-app-nav a[aria-current]:not([aria-current='false']) {
130
+ border-inline-start-color: Highlight;
131
+ font-weight: 700;
132
+ }
133
+
134
+ .ui-app-nav a.is-active::before,
135
+ .ui-app-nav a[aria-current]:not([aria-current='false'])::before {
136
+ background: Highlight;
137
+ }
138
+ }
139
+
105
140
  .ui-app-rail__foot {
106
141
  border-block-start: 1px solid var(--line);
107
142
  color: var(--text-dim);
@@ -153,6 +188,7 @@
153
188
  color: var(--text);
154
189
  font-family: var(--display);
155
190
  font-size: 1.1rem;
191
+ font-weight: var(--display-weight);
156
192
  letter-spacing: var(--tracking-wide);
157
193
  margin: 0;
158
194
  text-transform: uppercase;
@@ -207,6 +243,7 @@
207
243
  color: var(--text);
208
244
  font-family: var(--display);
209
245
  font-size: 0.95rem;
246
+ font-weight: var(--display-weight);
210
247
  letter-spacing: var(--tracking-wide);
211
248
  margin: 0;
212
249
  text-transform: uppercase;
@@ -230,15 +267,18 @@
230
267
  permanent admin-shell alias (grouped on the canonical rules there —
231
268
  identical output). Nothing app-specific left to define here. */
232
269
 
233
- /* --- Mobile rail collapse --- */
234
-
235
- .ui-app-rail__toggle {
236
- display: none;
237
- }
270
+ /* --- Mobile rail: collapses to a horizontal scrolling strip --- */
238
271
 
239
272
  @media (max-width: 880px) {
240
273
  .ui-app-shell {
241
274
  grid-template-columns: minmax(0, 1fr);
275
+
276
+ /* The shell keeps `min-block-size: 100dvh`, so with two auto rows the default
277
+ `align-content: stretch` distributes the leftover viewport across BOTH
278
+ tracks — ballooning the horizontal rail to ~half the screen. Pin tracks to
279
+ their content and let the content row absorb the slack. (component audit C7.) */
280
+ align-content: start;
281
+ grid-template-rows: auto 1fr;
242
282
  }
243
283
 
244
284
  .ui-app-rail {
@@ -254,6 +294,15 @@
254
294
  display: none;
255
295
  }
256
296
 
297
+ /* The rail is flex-row here, so the base `margin-block-start: auto` (which
298
+ pushed account to the bottom in the column layout) is inert and account can
299
+ scroll off. Push it to the inline end instead so sign-out stays reachable.
300
+ (layout review C21.) */
301
+ .ui-app-rail__account {
302
+ margin-block-start: 0;
303
+ margin-inline-start: auto;
304
+ }
305
+
257
306
  .ui-app-nav {
258
307
  grid-auto-flow: column;
259
308
  gap: 0.15rem;
@@ -269,11 +318,18 @@
269
318
  white-space: nowrap;
270
319
  }
271
320
 
272
- .ui-app-nav a.is-active {
321
+ .ui-app-nav a.is-active,
322
+ .ui-app-nav a[aria-current]:not([aria-current='false']) {
273
323
  border-inline-start: 0;
274
324
  border-block-end-color: var(--accent);
275
325
  }
276
326
 
327
+ /* No --app-rail-height token exists, so drop the redundant second sticky;
328
+ only the horizontal rail stays pinned. */
329
+ .ui-app-topbar {
330
+ position: static;
331
+ }
332
+
277
333
  .ui-app-content {
278
334
  padding: var(--space-md);
279
335
  }
package/css/base.css CHANGED
@@ -12,7 +12,10 @@ html {
12
12
  background: var(--bg);
13
13
  color: var(--text);
14
14
  font-family: var(--sans);
15
- font-size: 15px;
15
+
16
+ /* = 15px at the 16px default, but rem honours a user's browser font-size
17
+ preference (which a px root would ignore). */
18
+ font-size: 0.9375rem;
16
19
  line-height: 1.55;
17
20
  scroll-behavior: smooth;
18
21
  text-rendering: optimizeLegibility;
@@ -24,12 +27,16 @@ body {
24
27
  background: var(--bg);
25
28
  color: var(--text);
26
29
  margin: 0;
27
- min-block-size: 100vh;
30
+ min-block-size: 100dvh;
28
31
  position: relative;
29
32
  }
30
33
 
34
+ :root {
35
+ --scroll-offset: 6rem;
36
+ }
37
+
31
38
  main [id] {
32
- scroll-margin-top: 6rem;
39
+ scroll-margin-block-start: var(--scroll-offset);
33
40
  }
34
41
 
35
42
  ::selection {
@@ -176,6 +183,7 @@ textarea:focus-visible,
176
183
 
177
184
  .ui-display {
178
185
  font-family: var(--display);
186
+ font-weight: var(--display-weight);
179
187
  text-transform: uppercase;
180
188
  }
181
189
 
@@ -219,13 +227,10 @@ textarea:focus-visible,
219
227
  outline-offset: -2px;
220
228
  }
221
229
 
222
- /* The active tab signals selection only via accent colour/border, both
223
- flattened by the forced palettere-assert it with a system colour
224
- so the selected tab stays distinguishable (a11y review L3). */
225
- .ui-tab.is-active {
226
- border-block-end-color: Highlight;
227
- color: Highlight;
228
- }
230
+ /* NB: the active-tab forced-colors re-assert lives in disclosure.css, right
231
+ after the `.ui-tab.is-active` defaultplacing it here (an earlier bundle
232
+ leaf) let the later default win even in forced-colors mode, since @media
233
+ adds no specificity. (a11y review C10.) */
229
234
 
230
235
  /* Keyboard focus must never depend on a colour that gets overridden. */
231
236
  a:focus-visible,
package/css/bullet.css ADDED
@@ -0,0 +1,108 @@
1
+ /* ==========================================================================
2
+ bullet — opt-in Stephen-Few bullet graph (measure vs target vs bands).
3
+
4
+ The canonical "is this measure inside its qualitative budget, and how does it
5
+ compare to target?" figure: a thin measure bar over 2–3 grayscale qualitative
6
+ range bands, with a perpendicular target tick. The SLO / error-budget figure
7
+ `ui-meter` (report.css) structurally cannot encode — a meter is a single
8
+ label|bar|value reading, a bullet carries bands + target too. Few designed it
9
+ grayscale on purpose, which is exactly the Nothing palette. Pure CSS,
10
+ SSR-static, print-survivable. Not imported by core.css.
11
+
12
+ Boundary: the HOST normalises EVERY value to 0..1 and sets custom props —
13
+ `--v` (measure), `--t` (target), `--b1`/`--b2` (the band boundaries). Bronto
14
+ only paints geometry; it refuses raw values and min/max/scale computation —
15
+ the same contract as `ui-spark`. A bare bullet is opaque to assistive tech, so
16
+ `.ui-bullet` MUST carry a host-written `role="img"` + `aria-label` text
17
+ equivalent (e.g. "uptime 99.62%, inside target 99.9%, in the warning band").
18
+ Colour is never the only channel — the reading lives in that label.
19
+ ========================================================================== */
20
+
21
+ .ui-bullet {
22
+ --band-lo: 0.5;
23
+ --band-hi: 0.8;
24
+
25
+ background: linear-gradient(
26
+ to right,
27
+ var(--surface-4) 0 calc(var(--band-lo) * 100%),
28
+ var(--surface-3) calc(var(--band-lo) * 100%) calc(var(--band-hi) * 100%),
29
+ var(--surface-2) calc(var(--band-hi) * 100%) 100%
30
+ );
31
+ block-size: 1.25rem;
32
+ border-radius: var(--radius-sm);
33
+ inline-size: 100%;
34
+ position: relative;
35
+ }
36
+
37
+ /* The measure bar — a thinner dark bar centred in the track, the primary value.
38
+ `--v` is the normalised reading; with none it collapses to a hairline. */
39
+ .ui-bullet__measure {
40
+ background: var(--text);
41
+ border-radius: var(--radius-sm);
42
+ inline-size: max(2px, calc(var(--v, 0) * 100%));
43
+ inset-block: 30%;
44
+ inset-inline-start: 0;
45
+ position: absolute;
46
+ }
47
+
48
+ /* Tone a measure for emphasis or status — the meaning still has to be in the
49
+ aria-label (WCAG 1.4.1), tone is decoration. */
50
+ .ui-bullet__measure--accent {
51
+ background: var(--accent);
52
+ }
53
+
54
+ .ui-bullet__measure--pos {
55
+ background: var(--success);
56
+ }
57
+
58
+ .ui-bullet__measure--neg {
59
+ background: var(--danger);
60
+ }
61
+
62
+ /* The target tick — a full-height perpendicular mark at `--t`. Shape, not
63
+ colour, distinguishes it from the measure bar (Few's grayscale rule). */
64
+ .ui-bullet__target {
65
+ background: var(--text);
66
+ inline-size: 2px;
67
+ inset-block: 8%;
68
+ inset-inline-start: calc(var(--t, 0) * 100%);
69
+ position: absolute;
70
+ transform: translateX(-50%);
71
+ }
72
+
73
+ /* Optional caption row beneath the track — label + reading in mono, matching
74
+ the report meter's value treatment. */
75
+ .ui-bullet__label {
76
+ color: var(--text-dim);
77
+ display: flex;
78
+ font-family: var(--mono);
79
+ font-size: var(--text-2xs);
80
+ gap: var(--space-sm);
81
+ justify-content: space-between;
82
+ letter-spacing: var(--tracking-wide);
83
+ margin-block-start: 0.3rem;
84
+ }
85
+
86
+ /* Forced colours flatten the band gradient to one system surface — the bands
87
+ would merge. Re-assert a track border so the figure keeps a frame, and repaint
88
+ the marks in the system text colour so measure + target survive (the band
89
+ distinction is carried by the required aria-label). */
90
+ @media (forced-colors: active) {
91
+ .ui-bullet {
92
+ border: 1px solid CanvasText;
93
+ }
94
+
95
+ .ui-bullet__measure,
96
+ .ui-bullet__target {
97
+ background: CanvasText;
98
+ }
99
+ }
100
+
101
+ /* Print: the bands + marks are background/currentColor fills the print economy
102
+ would drop. */
103
+ @media print {
104
+ .ui-bullet {
105
+ -webkit-print-color-adjust: exact;
106
+ print-color-adjust: exact;
107
+ }
108
+ }
package/css/code.css ADDED
@@ -0,0 +1,98 @@
1
+ /* ==========================================================================
2
+ code — opt-in fenced-code evidence chrome.
3
+
4
+ Paints already-tokenized code: a head bar, an optional line-number gutter
5
+ (CSS counters), and add / del / highlight line states. It NEVER parses or
6
+ tokenizes — the host (or Shiki, via the shipped `shiki/nothing.json` token
7
+ theme) supplies the coloured spans; Bronto owns only the chrome. Code-as-
8
+ evidence for changelogs, version history, and generated reports; pairs with
9
+ the `.ui-src` provenance pill and marks. Not imported by core.css.
10
+
11
+ Long lines wrap (no horizontal scroll) so the surface prints cleanly. Put a
12
+ `.ui-code__line` around each line to opt into numbering / line states; plain
13
+ text inside `.ui-code__body` works too (no numbers).
14
+ ========================================================================== */
15
+
16
+ .ui-code {
17
+ border: 1px solid var(--line);
18
+ border-radius: var(--radius-sm);
19
+ font-family: var(--mono);
20
+ font-size: var(--text-xs);
21
+ overflow: hidden;
22
+ }
23
+
24
+ .ui-code__head {
25
+ background: var(--surface-2);
26
+ border-block-end: 1px solid var(--line);
27
+ color: var(--text-dim);
28
+ display: flex;
29
+ gap: var(--space-sm);
30
+ justify-content: space-between;
31
+ padding: var(--space-2xs) var(--space-sm);
32
+ }
33
+
34
+ .ui-code__body {
35
+ counter-reset: ui-code-ln;
36
+ margin: 0;
37
+ overflow-wrap: anywhere;
38
+ padding: var(--space-sm) 0;
39
+ white-space: pre-wrap;
40
+ }
41
+
42
+ .ui-code__line {
43
+ display: block;
44
+ padding-inline: var(--space-sm);
45
+ }
46
+
47
+ /* Opt-in line-number gutter — counts each `.ui-code__line`. */
48
+ .ui-code--numbered .ui-code__line {
49
+ padding-inline-start: 0;
50
+ }
51
+
52
+ .ui-code--numbered .ui-code__line::before {
53
+ color: var(--text-dim);
54
+ content: counter(ui-code-ln);
55
+ counter-increment: ui-code-ln;
56
+ display: inline-block;
57
+ inline-size: 3ch;
58
+ margin-inline-end: var(--space-sm);
59
+ text-align: end;
60
+ /* stylelint-disable-next-line property-no-vendor-prefix -- WebKit still requires the prefixed slot for this selection opt-out. */
61
+ -webkit-user-select: none;
62
+ user-select: none;
63
+ }
64
+
65
+ /* Line states — the host classifies; Bronto tints. Kept as a faint wash so the
66
+ token colours stay legible on top. */
67
+ .ui-code__line--add {
68
+ background: color-mix(in srgb, var(--success) 14%, transparent);
69
+ }
70
+
71
+ .ui-code__line--remove {
72
+ background: color-mix(in srgb, var(--danger) 14%, transparent);
73
+ }
74
+
75
+ .ui-code__line--hl {
76
+ background: color-mix(in srgb, var(--accent) 14%, transparent);
77
+ }
78
+
79
+ /* Forced colours drop the tint — add an inline-start border so the line state
80
+ still reads without colour. */
81
+ @media (forced-colors: active) {
82
+ .ui-code__line--add,
83
+ .ui-code__line--remove,
84
+ .ui-code__line--hl {
85
+ border-inline-start: 3px solid currentColor;
86
+ }
87
+ }
88
+
89
+ /* Print: the line-state tints are meaning-carrying backgrounds. */
90
+ @media print {
91
+ .ui-code__head,
92
+ .ui-code__line--add,
93
+ .ui-code__line--remove,
94
+ .ui-code__line--hl {
95
+ -webkit-print-color-adjust: exact;
96
+ print-color-adjust: exact;
97
+ }
98
+ }
@@ -91,3 +91,20 @@
91
91
  fill: CanvasText;
92
92
  }
93
93
  }
94
+
95
+ /* Print: keep the relationship lines and arrowheads in their meaning-carrying
96
+ colour, and settle any draw-on to a solid line (the draw needs JS to set
97
+ pathLength, which never runs for a static PDF). */
98
+ @media print {
99
+ .ui-connector__path,
100
+ .ui-connector__end {
101
+ -webkit-print-color-adjust: exact;
102
+ print-color-adjust: exact;
103
+ }
104
+
105
+ .ui-connector--draw .ui-connector__path {
106
+ animation: none;
107
+ stroke-dasharray: none;
108
+ stroke-dashoffset: 0;
109
+ }
110
+ }
package/css/content.css CHANGED
@@ -13,6 +13,11 @@
13
13
  font-size: var(--text-base);
14
14
  line-height: 1.7;
15
15
 
16
+ /* Machine-generated Markdown carries long unbreakable tokens (URLs, hashes,
17
+ code) that otherwise force horizontal page scroll on the prose surface
18
+ itself. Break them. (css-robustness review C9; the table cell already does.) */
19
+ overflow-wrap: break-word;
20
+
16
21
  /* Readable measure; the container can still be wider for tables/media. */
17
22
  --prose-measure: 72ch;
18
23
  }
@@ -75,7 +80,7 @@
75
80
 
76
81
  /* Anchored headings clear a sticky chrome and expose a dot on hover. */
77
82
  .ui-prose :is(h1, h2, h3, h4, h5, h6)[id] {
78
- scroll-margin-block-start: 6rem;
83
+ scroll-margin-block-start: var(--scroll-offset);
79
84
  }
80
85
 
81
86
  /* --- Text-level --- */
@@ -99,7 +104,12 @@
99
104
  color: var(--text);
100
105
  }
101
106
 
102
- .ui-prose mark {
107
+ /* The bare prose `<mark>` is the plain UA-reset highlight. A `<mark class="ui-mark">`
108
+ is the opt-in marks leaf (marks.css) and must win: this rule is higher-specificity
109
+ (0,1,1) than `.ui-mark` (0,1,0) and sits in the same `bronto` layer, so without the
110
+ `:not(.ui-mark)` it silently overrides every `.ui-mark` tone/draw modifier in prose —
111
+ the most-documented mark usage. (component audit C1.) */
112
+ .ui-prose mark:not(.ui-mark) {
103
113
  background: var(--accent-soft);
104
114
  color: var(--text);
105
115
  padding: 0.02em 0.22em;
@@ -237,7 +247,15 @@
237
247
  text-transform: uppercase;
238
248
  }
239
249
 
240
- /* --- Table — raw Markdown tables look like ui-table --- */
250
+ /* --- Table — raw Markdown tables look like ui-table ---
251
+ `display: block` makes a wide markdown table scroll horizontally (markdown
252
+ emits a bare <table> with no wrappable container, so the scroll has to live on
253
+ the element itself). Caveat: `display: block` can drop the implicit `table`
254
+ ARIA role in WebKit/Safari (intact in Chromium/Firefox). It is acceptable here
255
+ because `.ui-prose` is generated prose, not a data grid — but for a table whose
256
+ semantics MUST survive on WebKit, author the `.ui-table` component inside a
257
+ `.ui-table-wrap` instead (the wrap scrolls, the <table> keeps `display: table`),
258
+ or add `role="table"` to the markdown output. (component audit C29.) */
241
259
 
242
260
  .ui-prose table {
243
261
  border: 1px solid var(--line);
@@ -300,6 +318,7 @@
300
318
  display: grid;
301
319
  font-family: var(--display);
302
320
  font-size: var(--text-xl);
321
+ font-weight: var(--display-weight);
303
322
  gap: var(--space-sm);
304
323
  line-height: 1.2;
305
324
  margin-block: var(--space-xl);
package/css/crosshair.css CHANGED
@@ -40,18 +40,19 @@
40
40
  position: absolute;
41
41
  }
42
42
 
43
+ /* Positioned with logical insets, not a physical translateX/Y: --crosshair-x
44
+ is the offset from the inline-start edge (initCrosshair flips it in RTL), so
45
+ the vertical rule tracks the pointer correctly in both directions. */
43
46
  .ui-crosshair__line--x {
44
47
  inline-size: 1px;
45
48
  inset-block: 0;
46
- inset-inline-start: 0;
47
- transform: translateX(var(--crosshair-x));
49
+ inset-inline-start: var(--crosshair-x);
48
50
  }
49
51
 
50
52
  .ui-crosshair__line--y {
51
53
  block-size: 1px;
52
- inset-block-start: 0;
54
+ inset-block-start: var(--crosshair-y);
53
55
  inset-inline: 0;
54
- transform: translateY(var(--crosshair-y));
55
56
  }
56
57
 
57
58
  /* An axis value chip (host sets its text + which edge it sits on). On the
@@ -84,13 +85,12 @@
84
85
  color: var(--text);
85
86
  font-family: var(--mono);
86
87
  font-size: var(--text-xs);
87
- inset-block-start: 0;
88
- inset-inline-start: 0;
88
+ inset-block-start: var(--crosshair-y);
89
+ inset-inline-start: var(--crosshair-x);
89
90
  padding-block: 0.2rem;
90
91
  padding-inline: 0.4rem;
91
92
  pointer-events: none;
92
93
  position: absolute;
93
- transform: translate(var(--crosshair-x), var(--crosshair-y));
94
94
  }
95
95
 
96
96
  @media (forced-colors: active) {
package/css/dataviz.css CHANGED
@@ -30,7 +30,11 @@
30
30
  --chart-div-6: oklch(66% 0.13 55deg);
31
31
  --chart-div-7: oklch(56% 0.15 45deg);
32
32
 
33
- /* Dot-matrix pattern fills — pair with the matching colour (WCAG 1.4.1). */
33
+ /* Dot-matrix pattern fills — pair with the matching colour (WCAG 1.4.1).
34
+ Series 1 (the accent) is intentionally `none`: absence-of-pattern IS its
35
+ redundant channel. The colour palette is CVD-safe on its own (gated by
36
+ check:charts), so a colour+pattern chart stays distinguishable; a
37
+ pattern-ONLY chart must still give series 1 a fill or a labelled legend. */
34
38
  --chart-pattern-size: 8px;
35
39
  --chart-pattern-ink: rgb(0 0 0 / 0.34);
36
40
  --chart-pattern-1: none;