@humanforest/tokens 0.3.7 → 0.4.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 +71 -0
- package/dataviz/unovis.css +22 -24
- package/package.json +1 -1
- package/type/type.css +12 -2
- package/type/type.json +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,77 @@
|
|
|
2
2
|
|
|
3
3
|
Every @humanforest package shares one version, so one entry covers all seven packages. Newest first.
|
|
4
4
|
|
|
5
|
+
## 0.4.0 — 2026-09-15
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **docs**: the llms mirror prints a section kicker under its heading
|
|
10
|
+
- **frames**: FFitFrame fits a slot that sizes itself
|
|
11
|
+
- **charts**: a sparkline's area fades from its line to the floor
|
|
12
|
+
- **charts**: the calendar month rule boxes each month in a toned line
|
|
13
|
+
- **docs**: the llms mirror heads a non-prop table by its own column
|
|
14
|
+
- **docs**: the llms mirror names the module a page destructures
|
|
15
|
+
- **docs**: the llms mirror renders section loops
|
|
16
|
+
- **docs**: the llms mirror resolves plain strings a page imports
|
|
17
|
+
- **docs**: DemoStage puts a demo's controls above the specimen
|
|
18
|
+
- **docs**: the llms mirror names each code tab in a ProseCodeGroup
|
|
19
|
+
- **docs**: the llms mirror renders guidance cards and imported arrays
|
|
20
|
+
|
|
21
|
+
### Fixes
|
|
22
|
+
|
|
23
|
+
- **ui**: the scroll fade follows content that changes after mount
|
|
24
|
+
- **ui**: map drawing places a corner only for a press on the map
|
|
25
|
+
- **ui**: the scale bar measures at the centre and hides off the globe
|
|
26
|
+
- **ui**: geolocate stays active through a location error while tracking
|
|
27
|
+
- **ui**: map search names only a rendered row in aria-activedescendant
|
|
28
|
+
- **ui**: a context menu that reopens elsewhere takes focus again
|
|
29
|
+
- **ui**: map overlays add layers after the frame, not during placement
|
|
30
|
+
- **ui**: the map context menu wires nothing on a non-interactive map
|
|
31
|
+
- **ui**: map search rings kept focus and never sends a pair to suggest
|
|
32
|
+
- **ui**: fullscreen reads nothing without a fullscreen API
|
|
33
|
+
- **ui**: geolocate is a plain action with track off
|
|
34
|
+
- **ui**: coordinates read Mapbox's 512px world and name the copied pair
|
|
35
|
+
- **docs**: chord, treemap and sankey demos keep their heights
|
|
36
|
+
- **charts**: resolveVar leaves the document alone without a DOM
|
|
37
|
+
- **docs**: the battery-swap stage keeps its start edge reachable
|
|
38
|
+
- **docs**: foundations and layout diagrams fit a phone
|
|
39
|
+
- **docs**: device frames scale down to fit a phone's pane
|
|
40
|
+
- **docs**: component specimens fit a phone's preview pane
|
|
41
|
+
- **filter**: the chip's number segments drop their steppers
|
|
42
|
+
- **docs**: the table page's pager drops its arrows on a phone
|
|
43
|
+
- **filter**: a chip wider than its bar wraps its segments
|
|
44
|
+
- **tokens**: the display role steps down to 4xl below 40rem
|
|
45
|
+
- **tokens**: long inline code wraps instead of overflowing its column
|
|
46
|
+
- **docs**: long inline code and prop types wrap instead of widening main
|
|
47
|
+
- **ui**: a vehicle mark's badge outline follows its edge
|
|
48
|
+
- **ui**: ramp swatches abut and round only the ramp's ends
|
|
49
|
+
- **docs**: a context switch keeps a page's live demos mounted
|
|
50
|
+
- **ui**: a bound popup closes on a second click and unbinds on unmount
|
|
51
|
+
- **ui**: a marker is a button only when it acts as one
|
|
52
|
+
- **ui**: FMap applies every padding side and logs each map error
|
|
53
|
+
- **ui**: a track slice no longer bridges a signal gap
|
|
54
|
+
- **ui**: track playback keeps the playhead when the speed changes
|
|
55
|
+
- **ui**: balance maps paint by magnitude, and legends label exactly
|
|
56
|
+
- **charts**: the tooltip snap looks for its panel on pointer entry
|
|
57
|
+
- **ui**: unavailable reasons name a state by its label
|
|
58
|
+
- **ui**: zones ignore a kind they do not know
|
|
59
|
+
- **ui**: a selected vehicle takes its new state on resync
|
|
60
|
+
- **docs**: a cross-page anchored link lands on its target
|
|
61
|
+
- **ui**: useMapReady never runs for a caller that has unmounted
|
|
62
|
+
- **tokens**: area fills keep their opacity under data-theme dark
|
|
63
|
+
- **charts**: crosshair dots sit on stacked band tops
|
|
64
|
+
- **charts**: a fitted London map labels only the areas it draws
|
|
65
|
+
- **charts**: a missing reading breaks a line sparkline
|
|
66
|
+
- **charts**: a dotted map's missing values add nothing
|
|
67
|
+
- **charts**: a selectable London map is reachable at every size
|
|
68
|
+
- **charts**: cell tracks and grids take one Tab stop, props read live
|
|
69
|
+
- **docs**: urgent vehicles draw on top of the fleet
|
|
70
|
+
- **ui**: alt shortcuts select, and deleted features leave the selection
|
|
71
|
+
- **ui**: a crossing edit springs back, and a drag reports once
|
|
72
|
+
- **ui**: selected markers rise, popups are named, one button opens them
|
|
73
|
+
- **ui**: the coordinate tile stays copyable as the pointer moves onto it
|
|
74
|
+
- **ui**: the scale bar's fill is as long as the distance it names
|
|
75
|
+
|
|
5
76
|
## 0.3.7 — 2026-09-13
|
|
6
77
|
|
|
7
78
|
### Features
|
package/dataviz/unovis.css
CHANGED
|
@@ -90,36 +90,34 @@
|
|
|
90
90
|
--vis-tooltip-title-border-bottom: 1px solid var(--ui-border);
|
|
91
91
|
--vis-tooltip-label-color: var(--ui-text);
|
|
92
92
|
--vis-tooltip-value-color: var(--ui-text-highlighted);
|
|
93
|
+
--vis-area-fill-opacity: 1;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
/* Treemap concentric corners. Unovis draws group and leaf tiles as flat sibling rects
|
|
96
|
-
|
|
97
|
-
concentric rule is inner = outer − gap: with the house
|
|
98
|
-
|
|
96
|
+
/* Treemap concentric corners. Unovis draws group and leaf tiles as flat sibling rects under one
|
|
97
|
+
corner rule, so nesting gets no radius of its own and equal radii read as pinched inner corners.
|
|
98
|
+
The concentric rule is inner = outer − gap: with the house 12px corner and 4px gap, leaves take
|
|
99
|
+
8px. Leaves are the only tiles carrying the clickable class (treemapDefaults turns the affordance
|
|
99
100
|
on for exactly this reason); rx/ry are SVG geometry properties, so CSS overrides the attribute
|
|
100
101
|
Unovis sets. Matched on the emotion class suffix — Unovis hashes the prefix per build, the label
|
|
101
|
-
is stable. Separation comes from the padding gap
|
|
102
|
-
|
|
102
|
+
is stable. Separation comes from the padding gap, not from a stroke: inside a group the gap shows
|
|
103
|
+
the group tile beneath, and a stroke on every tile double-borders the nesting. */
|
|
103
104
|
rect[class*="-clickable-tile"] {
|
|
104
|
-
rx: calc(var(--vis-treemap-tile-radius,
|
|
105
|
-
ry: calc(var(--vis-treemap-tile-radius,
|
|
105
|
+
rx: calc(var(--vis-treemap-tile-radius, 12px) - var(--vis-treemap-tile-gap, 4px));
|
|
106
|
+
ry: calc(var(--vis-treemap-tile-radius, 12px) - var(--vis-treemap-tile-gap, 4px));
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
/* Re-centre
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
label
|
|
112
|
-
|
|
113
|
-
sets, so it is the one lever here.
|
|
109
|
+
/* Re-centre each donut's central label pair. Unovis anchors both texts on the centre with dy
|
|
110
|
+
±0.55em on each text's own font size, so the pair sits above the centre; dy is an attribute CSS
|
|
111
|
+
cannot reach, and translate composes with the transform Unovis sets. The flat donut classes both
|
|
112
|
+
texts -central-label and sizes them from --vis-donut-*; the nested donut names its second text
|
|
113
|
+
-central-sub-label and sizes from --vis-nested-donut-*. Both texts of a pair shift equally.
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
label
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
their spacing while re-centring the block. (No backticks in this comment: it lives inside a
|
|
122
|
-
template literal.) */
|
|
115
|
+
On a top half donut Unovis has already set the pair on the flat edge, so the shift ends the
|
|
116
|
+
sub-label about 11px below it at house sizes. In a box less than twice as wide as it is tall the
|
|
117
|
+
edge sits half the height less a quarter of the width above the SVG's bottom, so the sub-label
|
|
118
|
+
clips once the width passes twice the height less about 46px (474px at the frame's 260). The markup separates that layout from a bottom half donut or a partial-range gauge only
|
|
119
|
+
by numbers in its attributes. (No backticks in this comment: it lives inside a template
|
|
120
|
+
literal.) */
|
|
123
121
|
g[class*="-donut-component"]:not([class*="-nested-donut-component"]) text[class*="-central-label"] {
|
|
124
122
|
translate: 0 calc(0.5 * var(--vis-donut-central-label-font-size, 16px));
|
|
125
123
|
}
|
|
@@ -131,8 +129,8 @@ g[class*="-nested-donut-component"] text[class*="-central-sub-label"] {
|
|
|
131
129
|
|
|
132
130
|
/* Group labels get their own size. Unovis scales label type to tile area for LEAVES only
|
|
133
131
|
(enableTileLabelFontSizeVariation, gated on the node having no children), so an internal node
|
|
134
|
-
keeps the flat default
|
|
135
|
-
|
|
132
|
+
keeps the flat default however large its group is, and a group heading can end up smaller than
|
|
133
|
+
the leaf labels inside it.
|
|
136
134
|
Internal nodes carry their own class, so the heading can be sized here; it has to stay within
|
|
137
135
|
the tilePaddingTop strip the group reserves for it. */
|
|
138
136
|
text[class*="-internal-label"] {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanforest/tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Forest design tokens — the OKLCH colour engine (src/colourEngine.ts) plus the CSS + JSON it generates via scripts/{scales,type,motion,deck,dataviz,logo}.ts, and the logo SVGs. The generated files are framework-agnostic; do not edit them by hand, run `bun run build`.",
|
|
5
5
|
"files": [
|
|
6
6
|
"CHANGELOG.md",
|
package/type/type.css
CHANGED
|
@@ -89,6 +89,14 @@
|
|
|
89
89
|
--type-overline-transform: uppercase;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/* phone widths, below Tailwind's sm: a single long word in display Mohr is wider than the column and Mohr
|
|
93
|
+
does not hyphenate, so the role steps down the ramp rather than break the word */
|
|
94
|
+
@media (width < 40rem) {
|
|
95
|
+
:root {
|
|
96
|
+
--type-display-size: 2.25rem;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
/* role utility classes — set the whole bundle in one class (text-transform baked in for Mohr/overline) */
|
|
93
101
|
.type-display {
|
|
94
102
|
font-family: var(--type-display-family);
|
|
@@ -213,13 +221,15 @@
|
|
|
213
221
|
}
|
|
214
222
|
/* Inline <code> renders as a boxed chip — the system's inline-code treatment, distinct from the bare
|
|
215
223
|
.type-mono treatment (which is for data: hex, token names, numbers, never boxed). Use <code> for
|
|
216
|
-
actual code/syntax in prose. Shiki blocks (pre code) opt out below.
|
|
217
|
-
|
|
224
|
+
actual code/syntax in prose. Shiki blocks (pre code) opt out below. A long unbroken token (a file
|
|
225
|
+
path, a generic) breaks anywhere rather than overflowing its column, and box-decoration-break keeps
|
|
226
|
+
the box intact on every line it wraps across. */
|
|
218
227
|
:not(pre) > code {
|
|
219
228
|
background: var(--ui-bg-elevated);
|
|
220
229
|
border: 1px solid var(--ui-border-muted);
|
|
221
230
|
border-radius: 0.25rem;
|
|
222
231
|
padding: 0.05em 0.35em;
|
|
232
|
+
overflow-wrap: anywhere;
|
|
223
233
|
box-decoration-break: clone;
|
|
224
234
|
-webkit-box-decoration-break: clone;
|
|
225
235
|
}
|
package/type/type.json
CHANGED
|
@@ -228,6 +228,7 @@
|
|
|
228
228
|
"transform": "uppercase",
|
|
229
229
|
"italicCut": true,
|
|
230
230
|
"fluid": "clamp(2.25rem, 1.5rem + 3vw, 4.5rem)",
|
|
231
|
+
"narrow": "4xl",
|
|
231
232
|
"cssVar": "--type-display",
|
|
232
233
|
"class": "type-display",
|
|
233
234
|
"contexts": {
|
|
@@ -242,7 +243,7 @@
|
|
|
242
243
|
"marketing": 1,
|
|
243
244
|
"deck": 1
|
|
244
245
|
},
|
|
245
|
-
"note": "Hero statement, the big Mohr moment. Marketing keeps the full fluid clamp; product web trims it away."
|
|
246
|
+
"note": "Hero statement, the big Mohr moment. Marketing keeps the full fluid clamp; product web trims it away. Below 40rem it steps down to 4xl, because a single long word in Mohr is wider than a phone column and Mohr does not hyphenate."
|
|
246
247
|
},
|
|
247
248
|
{
|
|
248
249
|
"name": "headline",
|
|
@@ -258,6 +259,7 @@
|
|
|
258
259
|
"transform": "uppercase",
|
|
259
260
|
"italicCut": true,
|
|
260
261
|
"fluid": null,
|
|
262
|
+
"narrow": null,
|
|
261
263
|
"cssVar": "--type-headline",
|
|
262
264
|
"class": "type-headline",
|
|
263
265
|
"contexts": {
|
|
@@ -288,6 +290,7 @@
|
|
|
288
290
|
"transform": "none",
|
|
289
291
|
"italicCut": false,
|
|
290
292
|
"fluid": null,
|
|
293
|
+
"narrow": null,
|
|
291
294
|
"cssVar": "--type-title",
|
|
292
295
|
"class": "type-title",
|
|
293
296
|
"contexts": {
|
|
@@ -318,6 +321,7 @@
|
|
|
318
321
|
"transform": "none",
|
|
319
322
|
"italicCut": false,
|
|
320
323
|
"fluid": null,
|
|
324
|
+
"narrow": null,
|
|
321
325
|
"cssVar": "--type-heading-sm",
|
|
322
326
|
"class": "type-heading-sm",
|
|
323
327
|
"contexts": {
|
|
@@ -348,6 +352,7 @@
|
|
|
348
352
|
"transform": "none",
|
|
349
353
|
"italicCut": false,
|
|
350
354
|
"fluid": null,
|
|
355
|
+
"narrow": null,
|
|
351
356
|
"cssVar": "--type-heading-xs",
|
|
352
357
|
"class": "type-heading-xs",
|
|
353
358
|
"contexts": {
|
|
@@ -378,6 +383,7 @@
|
|
|
378
383
|
"transform": "none",
|
|
379
384
|
"italicCut": false,
|
|
380
385
|
"fluid": null,
|
|
386
|
+
"narrow": null,
|
|
381
387
|
"cssVar": "--type-subtitle",
|
|
382
388
|
"class": "type-subtitle",
|
|
383
389
|
"contexts": {
|
|
@@ -408,6 +414,7 @@
|
|
|
408
414
|
"transform": "none",
|
|
409
415
|
"italicCut": false,
|
|
410
416
|
"fluid": null,
|
|
417
|
+
"narrow": null,
|
|
411
418
|
"cssVar": "--type-body",
|
|
412
419
|
"class": "type-body",
|
|
413
420
|
"contexts": {
|
|
@@ -438,6 +445,7 @@
|
|
|
438
445
|
"transform": "none",
|
|
439
446
|
"italicCut": false,
|
|
440
447
|
"fluid": null,
|
|
448
|
+
"narrow": null,
|
|
441
449
|
"cssVar": "--type-body-sm",
|
|
442
450
|
"class": "type-body-sm",
|
|
443
451
|
"contexts": {
|
|
@@ -468,6 +476,7 @@
|
|
|
468
476
|
"transform": "none",
|
|
469
477
|
"italicCut": false,
|
|
470
478
|
"fluid": null,
|
|
479
|
+
"narrow": null,
|
|
471
480
|
"cssVar": "--type-label",
|
|
472
481
|
"class": "type-label",
|
|
473
482
|
"contexts": {
|
|
@@ -498,6 +507,7 @@
|
|
|
498
507
|
"transform": "none",
|
|
499
508
|
"italicCut": false,
|
|
500
509
|
"fluid": null,
|
|
510
|
+
"narrow": null,
|
|
501
511
|
"cssVar": "--type-caption",
|
|
502
512
|
"class": "type-caption",
|
|
503
513
|
"contexts": {
|
|
@@ -528,6 +538,7 @@
|
|
|
528
538
|
"transform": "uppercase",
|
|
529
539
|
"italicCut": false,
|
|
530
540
|
"fluid": null,
|
|
541
|
+
"narrow": null,
|
|
531
542
|
"cssVar": "--type-overline",
|
|
532
543
|
"class": "type-overline",
|
|
533
544
|
"contexts": {
|