@ponchia/ui 0.6.3 → 0.6.4
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 +18 -0
- package/behaviors/glyph.d.ts +7 -0
- package/behaviors/glyph.d.ts.map +1 -1
- package/behaviors/glyph.js +58 -4
- package/classes/classes.json +66 -4
- package/classes/index.d.ts +12 -1
- package/classes/index.js +15 -1
- package/css/dots.css +210 -3
- package/css/report.css +3 -4
- package/css/skins.css +9 -0
- package/css/spark.css +14 -0
- package/css/table.css +7 -1
- package/dist/bronto.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/report.css +1 -1
- package/dist/css/skins.css +1 -1
- package/dist/css/spark.css +1 -1
- package/dist/css/table.css +1 -1
- package/docs/dots.md +146 -0
- package/docs/glyphs.md +114 -0
- package/docs/reference.md +42 -1
- package/docs/reporting.md +19 -8
- package/glyphs/glyphs.d.ts +61 -0
- package/glyphs/glyphs.js +593 -30
- package/llms.txt +11 -3
- package/package.json +5 -1
- package/tokens/skins.js +22 -9
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
|> `^0` / `*` wildcard does **not** protect you. See README → Versioning, and
|
|
6
6
|
|> the deprecation policy in CONTRIBUTING.md.
|
|
7
7
|
|
|
8
|
+
## 0.6.4 — 2026-06-08
|
|
9
|
+
|
|
10
|
+
Patch release for the dot-matrix expansion and static report hardening shipped
|
|
11
|
+
in #116. No breaking changes, no `MIGRATIONS.json` entry.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Dot-matrix/glyph expansion** — adds the generated glyph metadata surface,
|
|
16
|
+
docs, and contract tests for the expanded dot/readout vocabulary.
|
|
17
|
+
- **Report authoring guidance** — documents the static report grammar around
|
|
18
|
+
captions, alert bodies, table wrapping, and local/CDN asset handling.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Static report print/layout behavior** — hardens report print margins and
|
|
23
|
+
table wrapping so standalone HTML reports and PDF exports degrade more
|
|
24
|
+
predictably across long tokens and normal prose.
|
|
25
|
+
|
|
8
26
|
## 0.6.3 — 2026-06-08
|
|
9
27
|
|
|
10
28
|
Patch release to publish the WebKit release fix after the `v0.6.1` and `v0.6.2`
|
package/behaviors/glyph.d.ts
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
* name is left untouched. Idempotent (skips an already-expanded host); the
|
|
8
8
|
* returned cleanup removes the cells and restores the original attributes.
|
|
9
9
|
*
|
|
10
|
+
* `data-bronto-glyph-render="mask"` takes the cheap one-node path instead:
|
|
11
|
+
* the host becomes a single `.ui-icon` masked by the glyph (no GLYPH_SIZE²
|
|
12
|
+
* cells), inheriting `currentColor` and scaling with the text — the DOM
|
|
13
|
+
* counterpart to renderGlyph's `render: 'mask'`, for an icon in every table
|
|
14
|
+
* row where 256 cells per glyph is too heavy. `data-bronto-glyph-size` sets
|
|
15
|
+
* `--icon-size`. The cell-mode attributes (solid/anim) don't apply.
|
|
16
|
+
*
|
|
10
17
|
* @param {import('./internal.js').DelegateOpts} [opts]
|
|
11
18
|
* @returns {import('./internal.js').Cleanup}
|
|
12
19
|
*/
|
package/behaviors/glyph.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAyJ3C"}
|
package/behaviors/glyph.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { hasDom, resolveHost, noop, collectHosts } from './internal.js';
|
|
2
|
-
import { GLYPH_SIZE, glyphCells } from '../glyphs/glyphs.js';
|
|
2
|
+
import { GLYPH_SIZE, glyphCells, glyphMask } from '../glyphs/glyphs.js';
|
|
3
3
|
|
|
4
4
|
function restoreAttr(el, name, prev) {
|
|
5
5
|
if (prev === null) el.removeAttribute(name);
|
|
6
6
|
else el.setAttribute(name, prev);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
// `dot`/`gap`/`size` land in inline CSS, so allow only length/calc syntax —
|
|
10
|
+
// drop anything with a `;`/`{` that could open a second declaration (mirrors
|
|
11
|
+
// glyphs.js cssLen). Used for the mask path's --icon-size.
|
|
12
|
+
function cssLen(v) {
|
|
13
|
+
return v && /^[\w.%+\-*/()\s,]+$/.test(v) ? v : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
/**
|
|
10
17
|
* Expand `[data-bronto-glyph="name"]` placeholders into a `.ui-dotmatrix`
|
|
11
18
|
* grid of GLYPH_SIZE² cells — the DOM counterpart to renderGlyph() from
|
|
@@ -15,6 +22,13 @@ function restoreAttr(el, name, prev) {
|
|
|
15
22
|
* name is left untouched. Idempotent (skips an already-expanded host); the
|
|
16
23
|
* returned cleanup removes the cells and restores the original attributes.
|
|
17
24
|
*
|
|
25
|
+
* `data-bronto-glyph-render="mask"` takes the cheap one-node path instead:
|
|
26
|
+
* the host becomes a single `.ui-icon` masked by the glyph (no GLYPH_SIZE²
|
|
27
|
+
* cells), inheriting `currentColor` and scaling with the text — the DOM
|
|
28
|
+
* counterpart to renderGlyph's `render: 'mask'`, for an icon in every table
|
|
29
|
+
* row where 256 cells per glyph is too heavy. `data-bronto-glyph-size` sets
|
|
30
|
+
* `--icon-size`. The cell-mode attributes (solid/anim) don't apply.
|
|
31
|
+
*
|
|
18
32
|
* @param {import('./internal.js').DelegateOpts} [opts]
|
|
19
33
|
* @returns {import('./internal.js').Cleanup}
|
|
20
34
|
*/
|
|
@@ -26,14 +40,54 @@ export function initDotGlyph({ root } = {}) {
|
|
|
26
40
|
const cleanups = [];
|
|
27
41
|
|
|
28
42
|
for (const el of els) {
|
|
43
|
+
const name = el.getAttribute('data-bronto-glyph');
|
|
44
|
+
const label = el.getAttribute('data-bronto-glyph-label');
|
|
45
|
+
|
|
46
|
+
// One-node mask path — the icon-at-scale counterpart to the 256-cell grid.
|
|
47
|
+
if (el.getAttribute('data-bronto-glyph-render') === 'mask') {
|
|
48
|
+
if (el.classList.contains('ui-icon') && el.style.getPropertyValue('--icon-mask')) continue;
|
|
49
|
+
const mask = glyphMask(name);
|
|
50
|
+
if (!mask) continue; // unknown glyph — leave the placeholder as-is
|
|
51
|
+
const hadIcon = el.classList.contains('ui-icon');
|
|
52
|
+
const hadMask = el.style.getPropertyValue('--icon-mask');
|
|
53
|
+
const hadSize = el.style.getPropertyValue('--icon-size');
|
|
54
|
+
const hadAriaHiddenM = el.getAttribute('aria-hidden');
|
|
55
|
+
const hadRoleM = el.getAttribute('role');
|
|
56
|
+
const hadAriaLabelM = el.getAttribute('aria-label');
|
|
57
|
+
const sizeM = cssLen(el.getAttribute('data-bronto-glyph-size'));
|
|
58
|
+
|
|
59
|
+
el.classList.add('ui-icon');
|
|
60
|
+
el.style.setProperty('--icon-mask', mask);
|
|
61
|
+
if (sizeM) el.style.setProperty('--icon-size', sizeM);
|
|
62
|
+
if (label) {
|
|
63
|
+
el.setAttribute('role', 'img');
|
|
64
|
+
el.setAttribute('aria-label', label);
|
|
65
|
+
el.removeAttribute('aria-hidden');
|
|
66
|
+
} else {
|
|
67
|
+
el.setAttribute('aria-hidden', 'true');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
cleanups.push(() => {
|
|
71
|
+
if (!hadIcon) el.classList.remove('ui-icon');
|
|
72
|
+
if (hadMask) el.style.setProperty('--icon-mask', hadMask);
|
|
73
|
+
else el.style.removeProperty('--icon-mask');
|
|
74
|
+
if (sizeM && !hadSize) el.style.removeProperty('--icon-size');
|
|
75
|
+
else if (hadSize) el.style.setProperty('--icon-size', hadSize);
|
|
76
|
+
restoreAttr(el, 'aria-hidden', hadAriaHiddenM);
|
|
77
|
+
restoreAttr(el, 'role', hadRoleM);
|
|
78
|
+
restoreAttr(el, 'aria-label', hadAriaLabelM);
|
|
79
|
+
if (el.getAttribute('class') === '') el.removeAttribute('class');
|
|
80
|
+
if (el.getAttribute('style') === '') el.removeAttribute('style');
|
|
81
|
+
});
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
29
85
|
// Scope to DIRECT-child cells (the ones we append) — so a placeholder that
|
|
30
86
|
// legitimately nests its own .ui-dotmatrix is neither mis-read as already
|
|
31
87
|
// expanded here nor have its inner cells removed by cleanup below.
|
|
32
88
|
if (el.querySelector(':scope > .ui-dotmatrix__cell')) continue; // already expanded
|
|
33
|
-
const cells = glyphCells(
|
|
89
|
+
const cells = glyphCells(name);
|
|
34
90
|
if (!cells.length) continue; // unknown glyph — leave the placeholder as-is
|
|
35
|
-
|
|
36
|
-
const label = el.getAttribute('data-bronto-glyph-label');
|
|
37
91
|
// `data-bronto-glyph-solid` → square, gapless pixel glyph (legible small),
|
|
38
92
|
// the DOM counterpart to renderGlyph's `solid` option. Implies glyph-only.
|
|
39
93
|
const solid = el.hasAttribute('data-bronto-glyph-solid');
|
package/classes/classes.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "@ponchia/ui class vocabulary as language-neutral data — validate emitted markup without executing the ESM cls map or parsing the .d.ts. Generated from classes/index.js — do not edit by hand; run `npm run classes:json:build`. Drift-checked in CI. `groups[].base` is null for a parts-only namespace (no standalone base class — do NOT emit it). A modifier whose name contains `__` (e.g. `ui-spark__bar--pos`) attaches to THAT part, not the base host. `states` is the author-applied `is-*` hooks (runtime/behavior-managed hooks are excluded); `behaviorAttributes` are the `data-bronto-*` wiring hooks the optional behaviors delegate on; `requiredAria` is the role/aria a generator must emit per component. `states` + `customProperties` are documented in docs/reference.md and ship outside `cls` by design.",
|
|
3
3
|
"counts": {
|
|
4
|
-
"classes":
|
|
5
|
-
"groups":
|
|
4
|
+
"classes": 540,
|
|
5
|
+
"groups": 167
|
|
6
6
|
},
|
|
7
7
|
"groups": {
|
|
8
8
|
"ui-accordion": {
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"ui-accordion__summary"
|
|
15
15
|
]
|
|
16
16
|
},
|
|
17
|
+
"ui-activity": {
|
|
18
|
+
"base": "ui-activity",
|
|
19
|
+
"modifiers": [],
|
|
20
|
+
"parts": []
|
|
21
|
+
},
|
|
17
22
|
"ui-alert": {
|
|
18
23
|
"base": "ui-alert",
|
|
19
24
|
"modifiers": [
|
|
@@ -460,6 +465,16 @@
|
|
|
460
465
|
"modifiers": [],
|
|
461
466
|
"parts": []
|
|
462
467
|
},
|
|
468
|
+
"ui-dotfit": {
|
|
469
|
+
"base": "ui-dotfit",
|
|
470
|
+
"modifiers": [],
|
|
471
|
+
"parts": []
|
|
472
|
+
},
|
|
473
|
+
"ui-dotgauge": {
|
|
474
|
+
"base": "ui-dotgauge",
|
|
475
|
+
"modifiers": [],
|
|
476
|
+
"parts": []
|
|
477
|
+
},
|
|
463
478
|
"ui-dotgrid": {
|
|
464
479
|
"base": "ui-dotgrid",
|
|
465
480
|
"modifiers": [
|
|
@@ -549,6 +564,11 @@
|
|
|
549
564
|
"modifiers": [],
|
|
550
565
|
"parts": []
|
|
551
566
|
},
|
|
567
|
+
"ui-halftone": {
|
|
568
|
+
"base": "ui-halftone",
|
|
569
|
+
"modifiers": [],
|
|
570
|
+
"parts": []
|
|
571
|
+
},
|
|
552
572
|
"ui-hint": {
|
|
553
573
|
"base": "ui-hint",
|
|
554
574
|
"modifiers": [
|
|
@@ -644,6 +664,14 @@
|
|
|
644
664
|
"ui-legend__value"
|
|
645
665
|
]
|
|
646
666
|
},
|
|
667
|
+
"ui-level": {
|
|
668
|
+
"base": "ui-level",
|
|
669
|
+
"modifiers": [
|
|
670
|
+
"ui-level--danger",
|
|
671
|
+
"ui-level--warn"
|
|
672
|
+
],
|
|
673
|
+
"parts": []
|
|
674
|
+
},
|
|
647
675
|
"ui-lightbox": {
|
|
648
676
|
"base": "ui-lightbox",
|
|
649
677
|
"modifiers": [],
|
|
@@ -849,7 +877,9 @@
|
|
|
849
877
|
"ui-readout": {
|
|
850
878
|
"base": "ui-readout",
|
|
851
879
|
"modifiers": [],
|
|
852
|
-
"parts": [
|
|
880
|
+
"parts": [
|
|
881
|
+
"ui-readout__spacer"
|
|
882
|
+
]
|
|
853
883
|
},
|
|
854
884
|
"ui-reasoning": {
|
|
855
885
|
"base": "ui-reasoning",
|
|
@@ -1018,6 +1048,7 @@
|
|
|
1018
1048
|
"ui-spark": {
|
|
1019
1049
|
"base": "ui-spark",
|
|
1020
1050
|
"modifiers": [
|
|
1051
|
+
"ui-spark--dots",
|
|
1021
1052
|
"ui-spark__bar--accent",
|
|
1022
1053
|
"ui-spark__bar--neg",
|
|
1023
1054
|
"ui-spark__bar--pos"
|
|
@@ -1143,6 +1174,7 @@
|
|
|
1143
1174
|
"ui-table": {
|
|
1144
1175
|
"base": "ui-table",
|
|
1145
1176
|
"modifiers": [
|
|
1177
|
+
"ui-table--break-anywhere",
|
|
1146
1178
|
"ui-table--comfortable",
|
|
1147
1179
|
"ui-table--dense",
|
|
1148
1180
|
"ui-table--lined",
|
|
@@ -1296,6 +1328,11 @@
|
|
|
1296
1328
|
"base": "ui-vt",
|
|
1297
1329
|
"modifiers": [],
|
|
1298
1330
|
"parts": []
|
|
1331
|
+
},
|
|
1332
|
+
"ui-waffle": {
|
|
1333
|
+
"base": "ui-waffle",
|
|
1334
|
+
"modifiers": [],
|
|
1335
|
+
"parts": []
|
|
1299
1336
|
}
|
|
1300
1337
|
},
|
|
1301
1338
|
"classes": [
|
|
@@ -1303,6 +1340,7 @@
|
|
|
1303
1340
|
"ui-accordion__body",
|
|
1304
1341
|
"ui-accordion__item",
|
|
1305
1342
|
"ui-accordion__summary",
|
|
1343
|
+
"ui-activity",
|
|
1306
1344
|
"ui-alert",
|
|
1307
1345
|
"ui-alert--accent",
|
|
1308
1346
|
"ui-alert--danger",
|
|
@@ -1508,6 +1546,8 @@
|
|
|
1508
1546
|
"ui-dotbar",
|
|
1509
1547
|
"ui-dotbar--indeterminate",
|
|
1510
1548
|
"ui-dotfield",
|
|
1549
|
+
"ui-dotfit",
|
|
1550
|
+
"ui-dotgauge",
|
|
1511
1551
|
"ui-dotgrid",
|
|
1512
1552
|
"ui-dotgrid--accent",
|
|
1513
1553
|
"ui-dotgrid--dense",
|
|
@@ -1537,6 +1577,7 @@
|
|
|
1537
1577
|
"ui-glossary__def",
|
|
1538
1578
|
"ui-glossary__term",
|
|
1539
1579
|
"ui-grid",
|
|
1580
|
+
"ui-halftone",
|
|
1540
1581
|
"ui-hint",
|
|
1541
1582
|
"ui-hint--error",
|
|
1542
1583
|
"ui-icon",
|
|
@@ -1581,6 +1622,9 @@
|
|
|
1581
1622
|
"ui-legend__title",
|
|
1582
1623
|
"ui-legend__track",
|
|
1583
1624
|
"ui-legend__value",
|
|
1625
|
+
"ui-level",
|
|
1626
|
+
"ui-level--danger",
|
|
1627
|
+
"ui-level--warn",
|
|
1584
1628
|
"ui-lightbox",
|
|
1585
1629
|
"ui-lightbox__close",
|
|
1586
1630
|
"ui-link",
|
|
@@ -1656,6 +1700,7 @@
|
|
|
1656
1700
|
"ui-range",
|
|
1657
1701
|
"ui-ratio",
|
|
1658
1702
|
"ui-readout",
|
|
1703
|
+
"ui-readout__spacer",
|
|
1659
1704
|
"ui-reasoning",
|
|
1660
1705
|
"ui-reasoning__body",
|
|
1661
1706
|
"ui-report",
|
|
@@ -1719,6 +1764,7 @@
|
|
|
1719
1764
|
"ui-source-list",
|
|
1720
1765
|
"ui-source-list__item",
|
|
1721
1766
|
"ui-spark",
|
|
1767
|
+
"ui-spark--dots",
|
|
1722
1768
|
"ui-spark__bar",
|
|
1723
1769
|
"ui-spark__bar--accent",
|
|
1724
1770
|
"ui-spark__bar--neg",
|
|
@@ -1768,6 +1814,7 @@
|
|
|
1768
1814
|
"ui-syncbar",
|
|
1769
1815
|
"ui-tab",
|
|
1770
1816
|
"ui-table",
|
|
1817
|
+
"ui-table--break-anywhere",
|
|
1771
1818
|
"ui-table--comfortable",
|
|
1772
1819
|
"ui-table--dense",
|
|
1773
1820
|
"ui-table--lined",
|
|
@@ -1827,7 +1874,8 @@
|
|
|
1827
1874
|
"ui-tree__leaf",
|
|
1828
1875
|
"ui-tree__summary",
|
|
1829
1876
|
"ui-visually-hidden",
|
|
1830
|
-
"ui-vt"
|
|
1877
|
+
"ui-vt",
|
|
1878
|
+
"ui-waffle"
|
|
1831
1879
|
],
|
|
1832
1880
|
"states": [
|
|
1833
1881
|
{
|
|
@@ -2500,6 +2548,20 @@
|
|
|
2500
2548
|
"value": "'reveal' | 'pulse'",
|
|
2501
2549
|
"behavior": "initDotGlyph",
|
|
2502
2550
|
"note": "decorative entrance/idle animation (honours prefers-reduced-motion)."
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
"name": "data-bronto-glyph-render",
|
|
2554
|
+
"on": "a [data-bronto-glyph] placeholder",
|
|
2555
|
+
"value": "'mask'",
|
|
2556
|
+
"behavior": "initDotGlyph",
|
|
2557
|
+
"note": "take the one-node .ui-icon mask path (inherits currentColor, scales with text) instead of the 256-cell grid — the icon-at-scale path."
|
|
2558
|
+
},
|
|
2559
|
+
{
|
|
2560
|
+
"name": "data-bronto-glyph-size",
|
|
2561
|
+
"on": "a [data-bronto-glyph-render='mask'] placeholder",
|
|
2562
|
+
"value": "CSS length",
|
|
2563
|
+
"behavior": "initDotGlyph",
|
|
2564
|
+
"note": "sets --icon-size on the masked glyph (sanitized to a length/calc allowlist)."
|
|
2503
2565
|
}
|
|
2504
2566
|
],
|
|
2505
2567
|
"requiredAria": [
|
package/classes/index.d.ts
CHANGED
|
@@ -79,6 +79,16 @@ export declare const cls: {
|
|
|
79
79
|
readonly dotspinner: 'ui-dotspinner';
|
|
80
80
|
readonly dotspinnerSm: 'ui-dotspinner--sm';
|
|
81
81
|
readonly dotspinnerLg: 'ui-dotspinner--lg';
|
|
82
|
+
readonly waffle: 'ui-waffle';
|
|
83
|
+
readonly activity: 'ui-activity';
|
|
84
|
+
readonly level: 'ui-level';
|
|
85
|
+
readonly levelWarn: 'ui-level--warn';
|
|
86
|
+
readonly levelDanger: 'ui-level--danger';
|
|
87
|
+
readonly dotgauge: 'ui-dotgauge';
|
|
88
|
+
readonly readout: 'ui-readout';
|
|
89
|
+
readonly readoutSpacer: 'ui-readout__spacer';
|
|
90
|
+
readonly halftone: 'ui-halftone';
|
|
91
|
+
readonly dotfit: 'ui-dotfit';
|
|
82
92
|
readonly field: 'ui-field';
|
|
83
93
|
readonly label: 'ui-label';
|
|
84
94
|
readonly input: 'ui-input';
|
|
@@ -189,6 +199,7 @@ export declare const cls: {
|
|
|
189
199
|
readonly tableDense: 'ui-table--dense';
|
|
190
200
|
readonly tableComfortable: 'ui-table--comfortable';
|
|
191
201
|
readonly tableLined: 'ui-table--lined';
|
|
202
|
+
readonly tableBreakAnywhere: 'ui-table--break-anywhere';
|
|
192
203
|
readonly tableWrap: 'ui-table-wrap';
|
|
193
204
|
readonly tableLoading: 'ui-table-wrap--loading';
|
|
194
205
|
readonly tableEmpty: 'ui-table__empty';
|
|
@@ -372,7 +383,6 @@ export declare const cls: {
|
|
|
372
383
|
readonly crosshairLineX: 'ui-crosshair__line--x';
|
|
373
384
|
readonly crosshairLineY: 'ui-crosshair__line--y';
|
|
374
385
|
readonly crosshairBadge: 'ui-crosshair__badge';
|
|
375
|
-
readonly readout: 'ui-readout';
|
|
376
386
|
readonly sel: 'ui-sel';
|
|
377
387
|
readonly selOn: 'ui-sel--on';
|
|
378
388
|
readonly selOff: 'ui-sel--off';
|
|
@@ -416,6 +426,7 @@ export declare const cls: {
|
|
|
416
426
|
readonly codeLineRemove: 'ui-code__line--remove';
|
|
417
427
|
readonly codeLineHl: 'ui-code__line--hl';
|
|
418
428
|
readonly spark: 'ui-spark';
|
|
429
|
+
readonly sparkDots: 'ui-spark--dots';
|
|
419
430
|
readonly sparkBar: 'ui-spark__bar';
|
|
420
431
|
readonly sparkBarAccent: 'ui-spark__bar--accent';
|
|
421
432
|
readonly sparkBarPos: 'ui-spark__bar--pos';
|
package/classes/index.js
CHANGED
|
@@ -84,6 +84,17 @@ export const cls = Object.freeze({
|
|
|
84
84
|
dotspinner: 'ui-dotspinner',
|
|
85
85
|
dotspinnerSm: 'ui-dotspinner--sm',
|
|
86
86
|
dotspinnerLg: 'ui-dotspinner--lg',
|
|
87
|
+
// data-bound dot surfaces (reporting/dashboard family)
|
|
88
|
+
waffle: 'ui-waffle',
|
|
89
|
+
activity: 'ui-activity',
|
|
90
|
+
level: 'ui-level',
|
|
91
|
+
levelWarn: 'ui-level--warn',
|
|
92
|
+
levelDanger: 'ui-level--danger',
|
|
93
|
+
dotgauge: 'ui-dotgauge',
|
|
94
|
+
readout: 'ui-readout',
|
|
95
|
+
readoutSpacer: 'ui-readout__spacer',
|
|
96
|
+
halftone: 'ui-halftone',
|
|
97
|
+
dotfit: 'ui-dotfit',
|
|
87
98
|
// forms
|
|
88
99
|
field: 'ui-field',
|
|
89
100
|
label: 'ui-label',
|
|
@@ -199,6 +210,7 @@ export const cls = Object.freeze({
|
|
|
199
210
|
tableDense: 'ui-table--dense',
|
|
200
211
|
tableComfortable: 'ui-table--comfortable',
|
|
201
212
|
tableLined: 'ui-table--lined',
|
|
213
|
+
tableBreakAnywhere: 'ui-table--break-anywhere',
|
|
202
214
|
tableWrap: 'ui-table-wrap',
|
|
203
215
|
// Loading state goes on the WRAP, so the modifier is named for the wrap (C19).
|
|
204
216
|
tableLoading: 'ui-table-wrap--loading',
|
|
@@ -441,6 +453,7 @@ export const cls = Object.freeze({
|
|
|
441
453
|
codeLineHl: 'ui-code__line--hl',
|
|
442
454
|
// spark — inline datawords / microcharts (css/spark.css)
|
|
443
455
|
spark: 'ui-spark',
|
|
456
|
+
sparkDots: 'ui-spark--dots',
|
|
444
457
|
sparkBar: 'ui-spark__bar',
|
|
445
458
|
sparkBarAccent: 'ui-spark__bar--accent',
|
|
446
459
|
sparkBarPos: 'ui-spark__bar--pos',
|
|
@@ -737,12 +750,13 @@ export const ui = {
|
|
|
737
750
|
dot: ({ tone, live } = {}) => j(cls.dot, dotTone(tone), live && cls.dotLive),
|
|
738
751
|
dotgrid: ({ accent, dense } = {}) =>
|
|
739
752
|
j(cls.dotgrid, accent && cls.dotgridAccent, dense && cls.dotgridDense),
|
|
740
|
-
table: ({ density, lined } = {}) =>
|
|
753
|
+
table: ({ density, lined, breakAnywhere } = {}) =>
|
|
741
754
|
j(
|
|
742
755
|
cls.table,
|
|
743
756
|
density === 'dense' && cls.tableDense,
|
|
744
757
|
density === 'comfortable' && cls.tableComfortable,
|
|
745
758
|
lined && cls.tableLined,
|
|
759
|
+
breakAnywhere && cls.tableBreakAnywhere,
|
|
746
760
|
),
|
|
747
761
|
eyebrow: ({ muted, sm } = {}) => j(cls.eyebrow, muted && cls.eyebrowMuted, sm && cls.eyebrowSm),
|
|
748
762
|
hint: ({ error } = {}) => j(cls.hint, error && cls.hintError),
|
package/css/dots.css
CHANGED
|
@@ -348,6 +348,177 @@
|
|
|
348
348
|
animation-delay: 0.63s;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
/* ==========================================================================
|
|
352
|
+
Data-bound dot surfaces — the reporting/dashboard family. Each is a thin,
|
|
353
|
+
token-driven leaf over the same lit/dim/accent dot vocabulary: the HOST
|
|
354
|
+
normalises the data (lights `is-on`, sets `data-level`, or writes `--v`
|
|
355
|
+
0..1) and the leaf only lays out + tones. None compute a scale, bin, or
|
|
356
|
+
threshold — that stays with the host (the spark/meter boundary).
|
|
357
|
+
|
|
358
|
+
a11y: every one is opaque to assistive tech, so the container MUST carry a
|
|
359
|
+
host-written `role="img"` + `aria-label` with the exact value/meaning
|
|
360
|
+
(rounding to whole cells is presentation-only; keep the figure in the label).
|
|
361
|
+
========================================================================== */
|
|
362
|
+
|
|
363
|
+
/* Waffle / unit chart — part-to-whole as an N×N field of lit dots ("73 of
|
|
364
|
+
100"). Host marks the lit cells with `is-on`. */
|
|
365
|
+
.ui-waffle {
|
|
366
|
+
display: grid;
|
|
367
|
+
gap: var(--waffle-gap, 0.18em);
|
|
368
|
+
grid-template-columns: repeat(var(--waffle-cols, 10), 1fr);
|
|
369
|
+
inline-size: var(--waffle-size, 7em);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.ui-waffle i {
|
|
373
|
+
aspect-ratio: 1;
|
|
374
|
+
background: var(--field-dot);
|
|
375
|
+
border-radius: var(--dotmatrix-dot-radius, 50%);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.ui-waffle i.is-on {
|
|
379
|
+
background: var(--field-dot-accent);
|
|
380
|
+
box-shadow: 0 0 var(--dotmatrix-glow, 0) var(--field-dot-accent);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* Activity / contribution grid — density-over-time. A flat list of day cells
|
|
384
|
+
flows down each weekday column via grid-auto-flow; intensity is a 5-step
|
|
385
|
+
ramp on `data-level="0..4"` (the host bins data → level). */
|
|
386
|
+
.ui-activity {
|
|
387
|
+
display: grid;
|
|
388
|
+
gap: var(--activity-gap, 0.18em);
|
|
389
|
+
grid-auto-columns: var(--activity-cell, 0.82em);
|
|
390
|
+
grid-auto-flow: column;
|
|
391
|
+
grid-template-rows: repeat(var(--activity-rows, 7), 1fr);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.ui-activity i {
|
|
395
|
+
aspect-ratio: 1;
|
|
396
|
+
background: var(--field-dot);
|
|
397
|
+
border-radius: var(--dotmatrix-dot-radius, 2px);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.ui-activity i[data-level='1'] {
|
|
401
|
+
background: color-mix(in oklab, var(--field-dot-accent) 30%, var(--field-dot));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.ui-activity i[data-level='2'] {
|
|
405
|
+
background: color-mix(in oklab, var(--field-dot-accent) 55%, var(--field-dot));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.ui-activity i[data-level='3'] {
|
|
409
|
+
background: color-mix(in oklab, var(--field-dot-accent) 78%, var(--field-dot));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.ui-activity i[data-level='4'] {
|
|
413
|
+
background: var(--field-dot-accent);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* LED level meter — a column of discrete segments lit to a threshold (signal /
|
|
417
|
+
load / VU). Fills from the bottom; host marks lit segments with `is-on`.
|
|
418
|
+
`--warn`/`--danger` re-point the lit colour for the whole meter when the host
|
|
419
|
+
crosses a threshold (the host owns the threshold, not the leaf). */
|
|
420
|
+
.ui-level {
|
|
421
|
+
block-size: var(--level-height, 4em);
|
|
422
|
+
display: flex;
|
|
423
|
+
flex-direction: column-reverse;
|
|
424
|
+
gap: var(--level-gap, 2px);
|
|
425
|
+
inline-size: var(--level-size, 0.7em);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.ui-level i {
|
|
429
|
+
background: var(--field-dot);
|
|
430
|
+
border-radius: var(--radius-sm);
|
|
431
|
+
flex: 1;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.ui-level i.is-on {
|
|
435
|
+
background: var(--accent);
|
|
436
|
+
box-shadow: 0 0 var(--dotmatrix-glow, 0) var(--accent);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.ui-level--warn i.is-on {
|
|
440
|
+
background: var(--warning);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.ui-level--danger i.is-on {
|
|
444
|
+
background: var(--danger);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* Dot gauge — a 0..1 reading (`--v`) as a ring of dots filling along an arc.
|
|
448
|
+
A conic-gradient sweep (accent up to --v, dim beyond) intersected with a
|
|
449
|
+
donut-ring × dot-pattern mask, so the lit arc reads as discrete dots. */
|
|
450
|
+
.ui-dotgauge {
|
|
451
|
+
--_v: var(--v, 0);
|
|
452
|
+
--_sweep: var(--gauge-sweep, 270deg);
|
|
453
|
+
|
|
454
|
+
aspect-ratio: 1;
|
|
455
|
+
background: conic-gradient(
|
|
456
|
+
from var(--gauge-from, 135deg),
|
|
457
|
+
var(--field-dot-accent) calc(var(--_v) * var(--_sweep)),
|
|
458
|
+
var(--field-dot) calc(var(--_v) * var(--_sweep)) var(--_sweep),
|
|
459
|
+
transparent var(--_sweep)
|
|
460
|
+
);
|
|
461
|
+
border-radius: 50%;
|
|
462
|
+
inline-size: var(--gauge-size, 5em);
|
|
463
|
+
|
|
464
|
+
/* ring (donut) ∩ dot lattice → a ring of dots */
|
|
465
|
+
/* stylelint-disable property-no-vendor-prefix -- Safari still needs the prefixed mask props. */
|
|
466
|
+
-webkit-mask:
|
|
467
|
+
radial-gradient(closest-side, transparent 58%, #000 59%),
|
|
468
|
+
radial-gradient(circle, #000 34%, transparent 36%) 0 0 / var(--gauge-dot, 0.5em) var(--gauge-dot, 0.5em);
|
|
469
|
+
-webkit-mask-composite: source-in;
|
|
470
|
+
/* stylelint-enable property-no-vendor-prefix */
|
|
471
|
+
mask:
|
|
472
|
+
radial-gradient(closest-side, transparent 58%, #000 59%),
|
|
473
|
+
radial-gradient(circle, #000 34%, transparent 36%) 0 0 / var(--gauge-dot, 0.5em) var(--gauge-dot, 0.5em);
|
|
474
|
+
mask-composite: intersect;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Readout — a row of dot-matrix glyphs forming a big numeric (renderReadout).
|
|
478
|
+
The glyphs are decorative; the row carries role=img + the value as its name. */
|
|
479
|
+
.ui-readout {
|
|
480
|
+
align-items: flex-end;
|
|
481
|
+
display: inline-flex;
|
|
482
|
+
gap: var(--readout-gap, 0.12em);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.ui-readout__spacer {
|
|
486
|
+
display: inline-block;
|
|
487
|
+
inline-size: var(--readout-space, 0.5em);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Halftone — render host content (an <img> or a box with its own background)
|
|
491
|
+
through a dot lattice so a thumbnail/cover takes on the dot look. A style
|
|
492
|
+
filter, NOT a data viz: the dots are a fixed lattice, not value-modulated. */
|
|
493
|
+
.ui-halftone {
|
|
494
|
+
/* stylelint-disable-next-line property-no-vendor-prefix -- Safari still needs the prefixed mask property. */
|
|
495
|
+
-webkit-mask: radial-gradient(circle, #000 var(--halftone-dot, 38%), transparent calc(var(--halftone-dot, 38%) + 1%)) 0 0 /
|
|
496
|
+
var(--halftone-gap, 0.5em) var(--halftone-gap, 0.5em);
|
|
497
|
+
mask: radial-gradient(circle, #000 var(--halftone-dot, 38%), transparent calc(var(--halftone-dot, 38%) + 1%)) 0 0 /
|
|
498
|
+
var(--halftone-gap, 0.5em) var(--halftone-gap, 0.5em);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* Density wrapper — make any dot surface inside respond to the CARD it sits in,
|
|
502
|
+
not the viewport, so the same component reads well in a wide hero and a
|
|
503
|
+
narrow tile. Opt-in: wrap the surface and the breakpoint below densifies it. */
|
|
504
|
+
.ui-dotfit {
|
|
505
|
+
container: dotfit / inline-size;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
@container dotfit (width < 18rem) {
|
|
509
|
+
.ui-dotfit .ui-dotgrid {
|
|
510
|
+
--dot-gap: 8px;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.ui-dotfit .ui-activity {
|
|
514
|
+
--activity-cell: 0.6em;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.ui-dotfit .ui-waffle {
|
|
518
|
+
--waffle-size: 5em;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
351
522
|
/* Matrix-reveal wrapper — content wipes in left→right on .is-in. Gate the
|
|
352
523
|
clipped from-state on `scripting: enabled`: with JS off, `.is-in` is never
|
|
353
524
|
toggled, so without the gate the content stays permanently clipped away and
|
|
@@ -443,16 +614,52 @@
|
|
|
443
614
|
forced-color-adjust: none;
|
|
444
615
|
background: LinkText;
|
|
445
616
|
}
|
|
617
|
+
|
|
618
|
+
/* The data-bound dot surfaces encode their value via background-color, which
|
|
619
|
+
HCM flattens. Opt out and pin the lit state to a system colour so the
|
|
620
|
+
reading survives. The activity ramp's 5 steps can't all stay distinct under
|
|
621
|
+
HCM — collapse to present (CanvasText) vs absent (the unstyled track). */
|
|
622
|
+
.ui-waffle i.is-on,
|
|
623
|
+
.ui-level i.is-on {
|
|
624
|
+
forced-color-adjust: none;
|
|
625
|
+
background: LinkText;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.ui-level--warn i.is-on {
|
|
629
|
+
background: Mark;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.ui-level--danger i.is-on {
|
|
633
|
+
background: Highlight;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.ui-activity i[data-level='1'],
|
|
637
|
+
.ui-activity i[data-level='2'],
|
|
638
|
+
.ui-activity i[data-level='3'],
|
|
639
|
+
.ui-activity i[data-level='4'] {
|
|
640
|
+
forced-color-adjust: none;
|
|
641
|
+
background: CanvasText;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/* The gauge is a masked gradient; keep its paint rather than let HCM drop it. */
|
|
645
|
+
.ui-dotgauge {
|
|
646
|
+
forced-color-adjust: none;
|
|
647
|
+
}
|
|
446
648
|
}
|
|
447
649
|
|
|
448
650
|
/* Print: the dot surfaces carry data (heatmap cells, the segmented meter, the
|
|
449
|
-
status dot, the masked glyph
|
|
450
|
-
"economy" default that drops
|
|
651
|
+
status dot, the masked glyph, the waffle/activity/level/gauge readings), so
|
|
652
|
+
their painted fills must survive the print "economy" default that drops
|
|
653
|
+
backgrounds. */
|
|
451
654
|
@media print {
|
|
452
655
|
.ui-dotmatrix__cell,
|
|
453
656
|
.ui-dotbar i,
|
|
454
657
|
.ui-dot,
|
|
455
|
-
.ui-icon
|
|
658
|
+
.ui-icon,
|
|
659
|
+
.ui-waffle i,
|
|
660
|
+
.ui-activity i,
|
|
661
|
+
.ui-level i,
|
|
662
|
+
.ui-dotgauge {
|
|
456
663
|
-webkit-print-color-adjust: exact;
|
|
457
664
|
print-color-adjust: exact;
|
|
458
665
|
}
|
package/css/report.css
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
--report-padding-block: var(--space-2xl);
|
|
13
13
|
--report-gap: var(--space-lg);
|
|
14
14
|
--report-measure: 74ch;
|
|
15
|
-
--report-page-margin: 18mm;
|
|
16
15
|
|
|
17
16
|
color: var(--text-soft);
|
|
18
17
|
display: grid;
|
|
@@ -312,10 +311,10 @@
|
|
|
312
311
|
}
|
|
313
312
|
|
|
314
313
|
@media print {
|
|
315
|
-
/*
|
|
316
|
-
|
|
314
|
+
/* Chromium-class print engines do not reliably resolve custom properties in
|
|
315
|
+
@page rules. Keep this literal so reports cannot print edge-to-edge. */
|
|
317
316
|
@page {
|
|
318
|
-
margin:
|
|
317
|
+
margin: 18mm;
|
|
319
318
|
}
|
|
320
319
|
|
|
321
320
|
.ui-report {
|