@ponchia/ui 0.6.8 → 0.6.10

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 (60) hide show
  1. package/CHANGELOG.md +79 -4
  2. package/README.md +2 -2
  3. package/annotations/index.d.ts.map +1 -1
  4. package/annotations/index.js +5 -6
  5. package/behaviors/carousel.d.ts.map +1 -1
  6. package/behaviors/carousel.js +100 -60
  7. package/behaviors/combobox.d.ts.map +1 -1
  8. package/behaviors/combobox.js +167 -113
  9. package/behaviors/connectors.d.ts.map +1 -1
  10. package/behaviors/connectors.js +39 -23
  11. package/behaviors/forms.d.ts.map +1 -1
  12. package/behaviors/forms.js +211 -207
  13. package/behaviors/glyph.d.ts.map +1 -1
  14. package/behaviors/glyph.js +157 -132
  15. package/behaviors/inert.d.ts +1 -1
  16. package/behaviors/inert.d.ts.map +1 -1
  17. package/behaviors/inert.js +1 -1
  18. package/behaviors/internal.js +2 -2
  19. package/behaviors/modal.js +1 -1
  20. package/behaviors/popover.js +5 -5
  21. package/behaviors/table.d.ts +1 -1
  22. package/behaviors/table.d.ts.map +1 -1
  23. package/behaviors/table.js +7 -8
  24. package/behaviors/tabs.js +2 -2
  25. package/behaviors/toast.js +5 -5
  26. package/classes/classes.json +33 -2
  27. package/classes/index.d.ts +10 -0
  28. package/classes/index.js +59 -35
  29. package/connectors/index.d.ts +2 -2
  30. package/connectors/index.d.ts.map +1 -1
  31. package/connectors/index.js +7 -10
  32. package/css/app.css +3 -4
  33. package/css/base.css +1 -1
  34. package/css/content.css +3 -3
  35. package/css/disclosure.css +3 -3
  36. package/css/dots.css +4 -4
  37. package/css/feedback.css +6 -7
  38. package/css/forms.css +9 -12
  39. package/css/legend.css +1 -1
  40. package/css/marks.css +1 -1
  41. package/css/motion.css +6 -6
  42. package/css/overlay.css +5 -7
  43. package/css/primitives.css +14 -16
  44. package/css/sidenote.css +2 -2
  45. package/css/table.css +2 -2
  46. package/css/workbench.css +128 -0
  47. package/dist/css/workbench.css +1 -1
  48. package/docs/annotations.md +36 -0
  49. package/docs/architecture.md +28 -0
  50. package/docs/interop/react-flow.md +89 -0
  51. package/docs/package-contract.md +2 -0
  52. package/docs/reference.md +21 -1
  53. package/docs/reporting.md +8 -8
  54. package/docs/stability.md +67 -7
  55. package/docs/workbench.md +56 -9
  56. package/glyphs/glyphs.js +43 -33
  57. package/llms.txt +10 -4
  58. package/package.json +5 -2
  59. package/schemas/report-claims.v1.schema.json +1 -1
  60. package/tokens/index.js +2 -2
package/classes/index.js CHANGED
@@ -212,7 +212,7 @@ export const cls = Object.freeze({
212
212
  tableLined: 'ui-table--lined',
213
213
  tableBreakAnywhere: 'ui-table--break-anywhere',
214
214
  tableWrap: 'ui-table-wrap',
215
- // Loading state goes on the WRAP, so the modifier is named for the wrap (C19).
215
+ // Loading state goes on the wrap, so the modifier is named for the wrap.
216
216
  tableLoading: 'ui-table-wrap--loading',
217
217
  tableEmpty: 'ui-table__empty',
218
218
  tableSort: 'ui-table__sort',
@@ -609,10 +609,20 @@ export const cls = Object.freeze({
609
609
  toolCallName: 'ui-tool-call__name',
610
610
  toolCallStatus: 'ui-tool-call__status',
611
611
  toolCallBody: 'ui-tool-call__body',
612
- // workbench — inspector / property / selection bar / splitter (css/workbench.css)
612
+ // workbench — inspector / property / toolstrip / selection bar / splitter (css/workbench.css)
613
613
  inspector: 'ui-inspector',
614
614
  inspectorHead: 'ui-inspector__head',
615
615
  inspectorBody: 'ui-inspector__body',
616
+ toolstrip: 'ui-toolstrip',
617
+ toolstripFloating: 'ui-toolstrip--floating',
618
+ toolstripCompact: 'ui-toolstrip--compact',
619
+ toolstripBrand: 'ui-toolstrip__brand',
620
+ toolstripContext: 'ui-toolstrip__context',
621
+ toolstripGroup: 'ui-toolstrip__group',
622
+ toolstripActions: 'ui-toolstrip__actions',
623
+ toolstripSearch: 'ui-toolstrip__search',
624
+ segmentedButtons: 'ui-segmented-buttons',
625
+ segmentedButtonsButton: 'ui-segmented-buttons__button',
616
626
  property: 'ui-property',
617
627
  propertyLabel: 'ui-property__label',
618
628
  propertyValue: 'ui-property__value',
@@ -739,14 +749,14 @@ const srcTone = (state) =>
739
749
  })[state] || '';
740
750
 
741
751
  // Component tone → modifier class. Same object-literal idiom as srcTone/stateTone
742
- // (still grep-by-class-name; the modifier set differs per component). (Q9.)
752
+ // while keeping modifier classes grep-friendly.
743
753
  //
744
754
  // The set differs PER COMPONENT on purpose — `muted` is a badge/num tone, not an
745
755
  // alert/toast/meter/dot one — so a caller extrapolating a universal tone (e.g.
746
756
  // `ui.alert({ tone: 'muted' })`) used to get a silent no-op: a bare, untoned
747
757
  // element with no signal that the tone was dropped. Warn at dev time instead, so
748
758
  // that "validates-but-no-ops" trap is loud rather than invisible. An omitted tone
749
- // is fine (returns no modifier). (component audit C12.)
759
+ // is fine and returns no modifier.
750
760
  const toneClass = (component, map, tone) => {
751
761
  if (tone == null) return '';
752
762
  const hit = map[tone];
@@ -852,6 +862,43 @@ const claimStatus = (status) =>
852
862
  status,
853
863
  );
854
864
 
865
+ const valueClass = (map, value) => (value == null ? '' : map[value] || '');
866
+
867
+ const annotationVariants = Object.freeze({
868
+ label: cls.annotationLabelVariant,
869
+ callout: cls.annotationCallout,
870
+ elbow: cls.annotationElbow,
871
+ curve: cls.annotationCurve,
872
+ circle: cls.annotationCircle,
873
+ rect: cls.annotationRect,
874
+ threshold: cls.annotationThreshold,
875
+ badge: cls.annotationBadgeVariant,
876
+ bracket: cls.annotationBracket,
877
+ band: cls.annotationBand,
878
+ slope: cls.annotationSlope,
879
+ compare: cls.annotationCompare,
880
+ cluster: cls.annotationCluster,
881
+ axis: cls.annotationAxis,
882
+ timeline: cls.annotationTimeline,
883
+ evidence: cls.annotationEvidence,
884
+ });
885
+
886
+ const annotationTones = Object.freeze({
887
+ accent: cls.annotationAccent,
888
+ muted: cls.annotationMuted,
889
+ success: cls.annotationSuccess,
890
+ warning: cls.annotationWarning,
891
+ danger: cls.annotationDanger,
892
+ info: cls.annotationInfo,
893
+ });
894
+
895
+ const annotationMotions = Object.freeze({
896
+ draw: cls.annotationDraw,
897
+ pulse: cls.annotationPulse,
898
+ reveal: cls.annotationReveal,
899
+ focus: cls.annotationFocus,
900
+ });
901
+
855
902
  export const ui = {
856
903
  button: ({ variant, icon, size } = {}) =>
857
904
  j(
@@ -927,7 +974,7 @@ export const ui = {
927
974
  j(
928
975
  cls.legendSwatch,
929
976
  // Series 1–8 map to ui-legend__swatch--N; the explicit bounds-check keeps a
930
- // 0/9/non-integer from coining a class the stylesheet never defines. (Q9.)
977
+ // 0/9/non-integer from coining a class the stylesheet never defines.
931
978
  Number.isInteger(series) && series >= 1 && series <= 8 && cls[`legendSwatch${series}`],
932
979
  shape === 'circle' && cls.legendSwatchCircle,
933
980
  shape === 'line' && cls.legendSwatchLine,
@@ -935,32 +982,9 @@ export const ui = {
935
982
  annotation: ({ variant = 'callout', tone = 'accent', motion } = {}) =>
936
983
  j(
937
984
  cls.annotation,
938
- variant === 'label' && cls.annotationLabelVariant,
939
- variant === 'callout' && cls.annotationCallout,
940
- variant === 'elbow' && cls.annotationElbow,
941
- variant === 'curve' && cls.annotationCurve,
942
- variant === 'circle' && cls.annotationCircle,
943
- variant === 'rect' && cls.annotationRect,
944
- variant === 'threshold' && cls.annotationThreshold,
945
- variant === 'badge' && cls.annotationBadgeVariant,
946
- variant === 'bracket' && cls.annotationBracket,
947
- variant === 'band' && cls.annotationBand,
948
- variant === 'slope' && cls.annotationSlope,
949
- variant === 'compare' && cls.annotationCompare,
950
- variant === 'cluster' && cls.annotationCluster,
951
- variant === 'axis' && cls.annotationAxis,
952
- variant === 'timeline' && cls.annotationTimeline,
953
- variant === 'evidence' && cls.annotationEvidence,
954
- tone === 'accent' && cls.annotationAccent,
955
- tone === 'muted' && cls.annotationMuted,
956
- tone === 'success' && cls.annotationSuccess,
957
- tone === 'warning' && cls.annotationWarning,
958
- tone === 'danger' && cls.annotationDanger,
959
- tone === 'info' && cls.annotationInfo,
960
- motion === 'draw' && cls.annotationDraw,
961
- motion === 'pulse' && cls.annotationPulse,
962
- motion === 'reveal' && cls.annotationReveal,
963
- motion === 'focus' && cls.annotationFocus,
985
+ valueClass(annotationVariants, variant),
986
+ valueClass(annotationTones, tone),
987
+ valueClass(annotationMotions, motion),
964
988
  ),
965
989
  mark: ({ style, tone, motion } = {}) =>
966
990
  j(
@@ -1049,7 +1073,7 @@ export const ui = {
1049
1073
  // UNITLESS `--value` (0–100) and AT needs role + aria-valuenow/min/max. This sets
1050
1074
  // the painted value and its announced value together so they can't drift, and
1051
1075
  // normalizes an arbitrary {min,max} to the 0–100 `--value` the CSS expects while
1052
- // keeping aria-valuenow in the caller's real units. (component audit C8.)
1076
+ // keeping aria-valuenow in the caller's real units.
1053
1077
  // `busyWhenIndeterminate` — a progressbar advertises aria-busy when its value is
1054
1078
  // unknown; a meter is never indeterminate so it passes false. (Kept a boolean
1055
1079
  // flag rather than testing the role string, so check:recipe-types doesn't read it
@@ -1064,7 +1088,7 @@ const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1064
1088
  // advertises aria-busy; a meter has no indeterminate state, so a non-finite
1065
1089
  // value there is a caller error we still fail safe on by omitting the
1066
1090
  // misleading 0. Pair with `ui.progress({ indeterminate: true })` for the CSS
1067
- // sweep. (component audit C9.)
1091
+ // sweep.
1068
1092
  if (!Number.isFinite(raw)) {
1069
1093
  return busyWhenIndeterminate ? { role, 'aria-busy': 'true' } : { role };
1070
1094
  }
@@ -1086,13 +1110,13 @@ const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1086
1110
  * </div>
1087
1111
  * `value` is in your own units; pass `{ min, max }` (default 0–100) and the
1088
1112
  * `--value` width is normalized for you. Call `attrs.progress()` with no value
1089
- * for an indeterminate bar (omits aria-valuenow, sets aria-busy). (audit C9.)
1113
+ * for an indeterminate bar (omits aria-valuenow, sets aria-busy).
1090
1114
  *
1091
1115
  * `attrs.dotbar(value)` is the segmented analogue of `attrs.progress`: a
1092
1116
  * determinate `.ui-dotbar` carries the same progress data as `.ui-progress` but,
1093
1117
  * without this, was eight empty `<span>`s to AT (the segments are decorative —
1094
1118
  * mark them `aria-hidden`). Same progressbar role + aria-valuenow/min/max;
1095
- * call with no value for the indeterminate sweep. (component audit C10.)
1119
+ * call with no value for the indeterminate sweep.
1096
1120
  */
1097
1121
  export const attrs = Object.freeze({
1098
1122
  meter: (value, { min = 0, max = 100 } = {}) => valueAttrs('meter', value, min, max, false),
@@ -103,8 +103,8 @@ export function arrowHead(p: Point, angle: number, size?: number, spread?: numbe
103
103
  export function dotMark(p: Point, radius?: number): string;
104
104
  /**
105
105
  * An axis-aligned rectangle path from its corners (callers derive the corners
106
- * from a centre or a top-left as they need). Shared by the annotation
107
- * rect/band and evidence-marker subjects. (code-quality audit Q5.)
106
+ * from a centre or a top-left as they need). Shared by annotation rect/band
107
+ * and evidence-marker subjects.
108
108
  * @param {number} left
109
109
  * @param {number} top
110
110
  * @param {number} right
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAkDA;;;;;;GAMG;AACH,6BALW,MAAM,SACN,MAAM,GAAG,IAAI,GAAG,SAAS,aACzB,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,CAMlB;AAED;;;;;;GAMG;AACH,gCALW,MAAM,SACN,MAAM,GAAG,IAAI,GAAG,SAAS,aACzB,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,CAMlB;AAKD;;;GAGG;AACH,mCAHW,MAAM,GACJ,MAAM,CAKlB;AAED;;;GAGG;AACH,2BAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;GAIG;AACH,yBAJW,MAAM,KACN,MAAM,GACJ,MAAM,CAIlB;AAID;;;;;GAKG;AACH,6BALW,MAAM,OACN,MAAM,OACN,MAAM,GACJ,MAAM,CAKlB;AAqBD;;;;;GAKG;AACH,kCAJW,IAAI,SACJ,IAAI,GACF,KAAK,CAoBjB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACd,MAAM,CAgBlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpB,MAAM,CAclB;AAED;;;;GAIG;AACH,qCAHW,oBAAoB,GAClB,MAAM,CAQlB;AAED;;;;;;;;GAQG;AACH,6BAPW,KAAK,SACL,MAAM,SACN,MAAM,WACN,MAAM,GAEJ,MAAM,CAalB;AAED;;;;;GAKG;AACH,2BAJW,KAAK,WACL,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;;;GASG;AACH,+BANW,MAAM,OACN,MAAM,SACN,MAAM,UACN,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;GAKG;AACH,oCAJW,IAAI,UACJ,IAAI,GACF;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAWpC;AAED;;;;;;;;GAQG;AACH,sCALW,KAAK,MACL,KAAK,UACL,cAAc,GACZ,MAAM,CASlB;AAED;;;;;;GAMG;AACH,oCAHW,mBAAmB,GACjB,kBAAkB,CAe9B;AAjWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAMH,wBAAyB,IAAI,CAAC;oBAhCjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;mBACxB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;mBACvD,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ;6BAC9C,UAAU,GAAG,OAAO,GAAG,OAAO;;UAG7B,KAAK;QACL,KAAK;;;;;;;;;;;;cAML,IAAI;YACJ,IAAI;;;;;;;;;;;;;;OAQJ,MAAM;UACN,KAAK;QACL,KAAK;;;;WACL,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAiDA;;;;;;GAMG;AACH,6BALW,MAAM,SACN,MAAM,GAAG,IAAI,GAAG,SAAS,aACzB,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,CAMlB;AAED;;;;;;GAMG;AACH,gCALW,MAAM,SACN,MAAM,GAAG,IAAI,GAAG,SAAS,aACzB,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,CAMlB;AAID;;;GAGG;AACH,mCAHW,MAAM,GACJ,MAAM,CAKlB;AAED;;;GAGG;AACH,2BAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;GAIG;AACH,yBAJW,MAAM,KACN,MAAM,GACJ,MAAM,CAIlB;AAGD;;;;;GAKG;AACH,6BALW,MAAM,OACN,MAAM,OACN,MAAM,GACJ,MAAM,CAKlB;AAqBD;;;;;GAKG;AACH,kCAJW,IAAI,SACJ,IAAI,GACF,KAAK,CAoBjB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACd,MAAM,CAgBlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpB,MAAM,CAclB;AAED;;;;GAIG;AACH,qCAHW,oBAAoB,GAClB,MAAM,CAQlB;AAED;;;;;;;;GAQG;AACH,6BAPW,KAAK,SACL,MAAM,SACN,MAAM,WACN,MAAM,GAEJ,MAAM,CAalB;AAED;;;;;GAKG;AACH,2BAJW,KAAK,WACL,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;;;GASG;AACH,+BANW,MAAM,OACN,MAAM,SACN,MAAM,UACN,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;GAKG;AACH,oCAJW,IAAI,UACJ,IAAI,GACF;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAWpC;AAED;;;;;;;;GAQG;AACH,sCALW,KAAK,MACL,KAAK,UACL,cAAc,GACZ,MAAM,CASlB;AAED;;;;;;GAMG;AACH,oCAHW,mBAAmB,GACjB,kBAAkB,CAe9B;AA9VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAKH,wBAAyB,IAAI,CAAC;oBA/BjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;mBACxB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;mBACvD,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ;6BAC9C,UAAU,GAAG,OAAO,GAAG,OAAO;;UAG7B,KAAK;QACL,KAAK;;;;;;;;;;;;cAML,IAAI;YACJ,IAAI;;;;;;;;;;;;;;OAQJ,MAAM;UACN,KAAK;QACL,KAAK;;;;WACL,MAAM"}
@@ -43,9 +43,8 @@
43
43
  */
44
44
 
45
45
  // Shared scalar/geometry primitives. Exported so the annotations layer composes
46
- // on the SAME kernel instead of copy-pasting it (the copies had silently
47
- // diverged — see `clamp`). Low-level helpers; the documented API is the path
48
- // builders below.
46
+ // on the same kernel as the connector path builders. Low-level helpers; the
47
+ // documented API is the path builders below.
49
48
  export const PRECISION = 1000;
50
49
 
51
50
  /**
@@ -74,9 +73,8 @@ export function dimension(name, value, fallback) {
74
73
  return v;
75
74
  }
76
75
 
77
- // Round to PRECISION, normalising -0 → 0, and return the NUMBER (the numeric
78
- // core `fmt` stringifies). Shared with the annotations layer for the rounded
79
- // coordinates it echoes back to the host. (code-quality audit Q5.)
76
+ // Round to PRECISION, normalising -0 → 0, and return the NUMBER. `fmt`
77
+ // stringifies the numeric core; annotations use the same rounded coordinates.
80
78
  /**
81
79
  * @param {number} value
82
80
  * @returns {number}
@@ -103,8 +101,7 @@ export function point(x, y) {
103
101
  return `${fmt(x)},${fmt(y)}`;
104
102
  }
105
103
 
106
- // Guarded form (returns min when the range is inverted) — the reconciled body;
107
- // connectors only ever calls clamp(v, 0, 1) so this is output-identical here.
104
+ // Guarded form: an inverted range resolves to `min`.
108
105
  /**
109
106
  * @param {number} value
110
107
  * @param {number} min
@@ -284,8 +281,8 @@ export function dotMark(p, radius = 3) {
284
281
 
285
282
  /**
286
283
  * An axis-aligned rectangle path from its corners (callers derive the corners
287
- * from a centre or a top-left as they need). Shared by the annotation
288
- * rect/band and evidence-marker subjects. (code-quality audit Q5.)
284
+ * from a centre or a top-left as they need). Shared by annotation rect/band
285
+ * and evidence-marker subjects.
289
286
  * @param {number} left
290
287
  * @param {number} top
291
288
  * @param {number} right
package/css/app.css CHANGED
@@ -123,7 +123,7 @@
123
123
  near-imperceptible tint/dot opacity delta survives, and current-page becomes
124
124
  invisible to HCM users. Re-assert "current" on channels HCM preserves: a
125
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.) */
126
+ cue does not rely on colour alone (WCAG 1.4.1). */
127
127
  @media (forced-colors: active) {
128
128
  .ui-app-nav a.is-active,
129
129
  .ui-app-nav a[aria-current]:not([aria-current='false']) {
@@ -276,7 +276,7 @@
276
276
  /* The shell keeps `min-block-size: 100dvh`, so with two auto rows the default
277
277
  `align-content: stretch` distributes the leftover viewport across BOTH
278
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.) */
279
+ their content and let the content row absorb the slack. */
280
280
  align-content: start;
281
281
  grid-template-rows: auto 1fr;
282
282
  }
@@ -296,8 +296,7 @@
296
296
 
297
297
  /* The rail is flex-row here, so the base `margin-block-start: auto` (which
298
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.) */
299
+ scroll off. Push it to the inline end instead so sign-out stays reachable. */
301
300
  .ui-app-rail__account {
302
301
  margin-block-start: 0;
303
302
  margin-inline-start: auto;
package/css/base.css CHANGED
@@ -230,7 +230,7 @@ textarea:focus-visible,
230
230
  /* NB: the active-tab forced-colors re-assert lives in disclosure.css, right
231
231
  after the `.ui-tab.is-active` default — placing it here (an earlier bundle
232
232
  leaf) let the later default win even in forced-colors mode, since @media
233
- adds no specificity. (a11y review C10.) */
233
+ adds no specificity. */
234
234
 
235
235
  /* Keyboard focus must never depend on a colour that gets overridden. */
236
236
  a:focus-visible,
package/css/content.css CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  /* Machine-generated Markdown carries long unbreakable tokens (URLs, hashes,
17
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.) */
18
+ itself. Break them; table cells already do this locally. */
19
19
  overflow-wrap: break-word;
20
20
 
21
21
  /* Readable measure; the container can still be wider for tables/media. */
@@ -108,7 +108,7 @@
108
108
  is the opt-in marks leaf (marks.css) and must win: this rule is higher-specificity
109
109
  (0,1,1) than `.ui-mark` (0,1,0) and sits in the same `bronto` layer, so without the
110
110
  `:not(.ui-mark)` it silently overrides every `.ui-mark` tone/draw modifier in prose —
111
- the most-documented mark usage. (component audit C1.) */
111
+ the most-documented mark usage. */
112
112
  .ui-prose mark:not(.ui-mark) {
113
113
  background: var(--accent-soft);
114
114
  color: var(--text);
@@ -255,7 +255,7 @@
255
255
  because `.ui-prose` is generated prose, not a data grid — but for a table whose
256
256
  semantics MUST survive on WebKit, author the `.ui-table` component inside a
257
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.) */
258
+ or add `role="table"` to the markdown output. */
259
259
 
260
260
  .ui-prose table {
261
261
  border: 1px solid var(--line);
@@ -59,7 +59,7 @@
59
59
 
60
60
  /* Forced-colors re-assert MUST sit after the default above (same specificity,
61
61
  @media adds none) or the accent default wins and the selected tab loses its
62
- only cue. Moved here from base.css, an earlier bundle leaf. (a11y review C10.) */
62
+ only cue. Moved here from base.css, an earlier bundle leaf. */
63
63
  @media (forced-colors: active) {
64
64
  .ui-tab.is-active {
65
65
  border-block-end-color: Highlight;
@@ -306,7 +306,7 @@
306
306
  /* Active page keys on BOTH `.is-active` and `[aria-current]` — usage.md calls
307
307
  aria-current "the framework rule", and every nav sibling (breadcrumb, sitenav,
308
308
  app-nav) highlights on it, so a pagination author who sets only aria-current
309
- must still get the highlight. (component audit C17.) */
309
+ must still get the highlight. */
310
310
  .ui-pagination__item.is-active,
311
311
  .ui-pagination__item[aria-current]:not([aria-current='false']) {
312
312
  border-color: var(--accent);
@@ -319,7 +319,7 @@
319
319
  (CSS can't intercept keys). For a fully-inert control prefer native
320
320
  `<button disabled>`, run `initDisabledGuard()` (intercepts Enter/Space), or
321
321
  pair aria-disabled with `tabindex="-1"`. See docs/usage.md "Disabled vs
322
- aria-disabled". (audit C4.) */
322
+ aria-disabled". */
323
323
  .ui-pagination__item[aria-disabled='true'],
324
324
  .ui-pagination__item:disabled {
325
325
  cursor: not-allowed;
package/css/dots.css CHANGED
@@ -283,7 +283,7 @@
283
283
 
284
284
  /* The comet expects exactly 8 `<i>`; a 9th+ child has no rotation rule and
285
285
  would pile up dead-centre. Hide the overflow so extra children fail safe
286
- rather than rendering a stray static dot (C26). */
286
+ rather than rendering a stray static dot. */
287
287
  .ui-dotspinner i:nth-child(n + 9) {
288
288
  display: none;
289
289
  }
@@ -523,7 +523,7 @@
523
523
  clipped from-state on `scripting: enabled`: with JS off, `.is-in` is never
524
524
  toggled, so without the gate the content stays permanently clipped away and
525
525
  invisible to every no-JS/static/print reader. Same graceful default as
526
- `.ui-reveal`. (component audit C12.) */
526
+ `.ui-reveal`. */
527
527
  @media (scripting: enabled) {
528
528
  .ui-matrix {
529
529
  clip-path: inset(0 100% 0 0);
@@ -583,7 +583,7 @@
583
583
 
584
584
  /* Brand/live dots aren't status tones, but they still encode meaning via
585
585
  background-color alone, which HCM flattens. Keep them on a distinct,
586
- opted-out system colour for completeness. (audit C31.) */
586
+ opted-out system colour for completeness. */
587
587
  .ui-dot--accent,
588
588
  .ui-dot--live {
589
589
  forced-color-adjust: none;
@@ -599,7 +599,7 @@
599
599
  glyph vanishes (white-on-white) — yet .ui-icon is the recommended
600
600
  icon-at-scale path AND backs .ui-legend__symbol, and the print block
601
601
  already special-cases it. Opt out and pin the fill to the system text
602
- colour so the glyph stays visible. (audit C1.) */
602
+ colour so the glyph stays visible. */
603
603
  .ui-icon {
604
604
  forced-color-adjust: none;
605
605
  background: CanvasText;
package/css/feedback.css CHANGED
@@ -9,8 +9,7 @@
9
9
  a stray `--value: 50%` is invalid against the typed syntax and falls back to
10
10
  the initial `0` (empty bar) instead of poisoning the `clamp()` and painting a
11
11
  FULL bar (the old failure mode). It inherits so the value set on the host
12
- `.ui-meter` / `.ui-progress` cascades to the inner `__fill`/`__bar`.
13
- (component audit C8.) */
12
+ `.ui-meter` / `.ui-progress` cascades to the inner `__fill`/`__bar`. */
14
13
  @property --value {
15
14
  syntax: '<number>';
16
15
  inherits: true;
@@ -442,7 +441,7 @@
442
441
  /* A still, solid, full-width bar reads as "100% complete" — the opposite of
443
442
  indeterminate. Fall back to a static diagonal hatch that fills the track
444
443
  (so it's clearly active) but doesn't read as done. AT is covered via
445
- aria-busy. (audit C26.) */
444
+ aria-busy. */
446
445
  background: repeating-linear-gradient(
447
446
  -45deg,
448
447
  var(--accent) 0,
@@ -466,7 +465,7 @@
466
465
  Drive the fill with the same --value knob as progress; tone the fill by
467
466
  threshold. Author role="meter" + aria-valuenow/min/max for AT — but role=meter
468
467
  has uneven AT support, so keep the visible .ui-meter__label/__value (they are
469
- the real channel, not just decoration). (component audit C25.) --- */
468
+ the real channel, not just decoration). --- */
470
469
 
471
470
  .ui-meter {
472
471
  background: var(--panel-soft);
@@ -537,7 +536,7 @@
537
536
  /* Prefer the natural one-line width, but never wider than the container: a
538
537
  long step label at `max-content` couldn't shrink and overflowed the page on
539
538
  narrow viewports (tabs scroll; steps didn't). Capping at 100% lets an
540
- over-long label wrap instead of overflowing. (component audit C18.) */
539
+ over-long label wrap instead of overflowing. */
541
540
  min-inline-size: min(100%, max-content);
542
541
  text-transform: uppercase;
543
542
  }
@@ -597,10 +596,10 @@
597
596
  /* Forced-colors flattens the fill's tone to the system palette and can erase it
598
597
  against the track, dropping the only visual cue of the measured proportion.
599
598
  Re-assert a system colour so the bar stays visible; the tone's *semantic* is
600
- carried by the author-written label beside it, not the colour. (a11y C10.)
599
+ carried by the author-written label beside it, not the colour.
601
600
  The toned `.ui-meter--TONE .ui-meter__fill` rules are (0,2,0); the bare
602
601
  `.ui-meter__fill` reset is only (0,1,0), so it lost — a toned fill stayed
603
- `var(--TONE)` and was forced to black-on-black (component-audit C3). Match the
602
+ `var(--TONE)` and was forced to black-on-black. Match the
604
603
  tone specificity here (and set `forced-color-adjust: none`) so every meter,
605
604
  toned or not, paints `Highlight`, mirroring the `.ui-dot` precedent. */
606
605
  @media (forced-colors: active) {
package/css/forms.css CHANGED
@@ -79,8 +79,7 @@
79
79
 
80
80
  /* Read-only is editable-looking but not editable; give it a distinct, quieter
81
81
  cue (muted fill + default cursor) so it doesn't read as a live field. Not
82
- disabled — value still submits and the field stays focusable/selectable.
83
- (component audit C28.) */
82
+ disabled — value still submits and the field stays focusable/selectable. */
84
83
  .ui-input:read-only:not(:disabled),
85
84
  .ui-textarea:read-only:not(:disabled) {
86
85
  background: var(--panel-soft);
@@ -91,7 +90,7 @@
91
90
  the controls that WRAP a native input (switch/check/segmented) showed no
92
91
  disabled cue and their label kept cursor:pointer — a lie. Mirror the cue via
93
92
  :has(input:disabled); the native-element controls (range/file) take :disabled
94
- directly. (a11y/forms review C4.) */
93
+ directly. */
95
94
  .ui-range:disabled,
96
95
  .ui-file:disabled,
97
96
  .ui-switch:has(input:disabled),
@@ -102,7 +101,7 @@
102
101
  }
103
102
 
104
103
  /* Keep autofilled fields on-theme — the UA's yellow fill otherwise paints over
105
- the monochrome surface and breaks the contrast story. (forms review C24.) */
104
+ the monochrome surface and breaks the contrast story. */
106
105
  .ui-input:autofill,
107
106
  .ui-select:autofill,
108
107
  .ui-textarea:autofill,
@@ -121,8 +120,7 @@
121
120
  /* Wrapper controls (switch / check / segmented) hide their native <input>, so
122
121
  the `[aria-invalid]` the validator sets on it paints nothing — a sighted,
123
122
  non-AT user couldn't see the error (WCAG 1.4.1). Mirror the invalid cue onto
124
- the visible surface via :has(), the same way the disabled cue is mirrored.
125
- (component audit C7.) */
123
+ the visible surface via :has(), the same way the disabled cue is mirrored. */
126
124
  .ui-check:has(input[aria-invalid='true']) input {
127
125
  outline: 2px solid var(--danger);
128
126
  outline-offset: 1px;
@@ -141,8 +139,7 @@
141
139
  one and sighted HCM users lose the only error cue (WCAG 1.4.1). The switch got
142
140
  a forced-colors block; the error family did not. Re-assert the state on a
143
141
  NON-colour channel — a thicker, doubled border — that survives HCM, and prefix
144
- the error hint with a glyph so the message itself carries the error.
145
- (component audit C5.) */
142
+ the error hint with a glyph so the message itself carries the error. */
146
143
  @media (forced-colors: active) {
147
144
  .ui-input[aria-invalid='true'],
148
145
  .ui-select[aria-invalid='true'],
@@ -151,7 +148,7 @@
151
148
  border-width: 3px;
152
149
  }
153
150
 
154
- /* Same NON-colour re-assertion for the wrapper controls (C7): a thicker,
151
+ /* Same NON-colour re-assertion for the wrapper controls: a thicker,
155
152
  doubled outline/border survives the HCM colour flattening. */
156
153
  .ui-check:has(input[aria-invalid='true']) input {
157
154
  outline-width: 3px;
@@ -205,7 +202,7 @@
205
202
 
206
203
  .ui-input-group > .ui-input:focus-visible,
207
204
  .ui-input-group > .ui-select:focus-visible {
208
- z-index: 1; /* keep the focus ring above the adjacent addon border (select too — audit C29) */
205
+ z-index: 1; /* keep the focus ring above the adjacent addon border */
209
206
  }
210
207
 
211
208
  .ui-input-group__addon {
@@ -213,7 +210,7 @@
213
210
  background: var(--panel-soft);
214
211
 
215
212
  /* Match the wrapped control's `--line-strong` border so the prefix/suffix seam
216
- isn't a fainter cap than the field it abuts. (component audit C33.) */
213
+ isn't a fainter cap than the field it abuts. */
217
214
  border: 1px solid var(--line-strong);
218
215
  color: var(--text-dim);
219
216
  display: flex;
@@ -358,7 +355,7 @@
358
355
 
359
356
  /* Keyboard focus ring to match every sibling input (which use a 2px ring); the
360
357
  border-colour shift alone read markedly fainter on this one control. Keyed to
361
- :focus-visible on the inner input so it stays keyboard-only. (a11y review C12.) */
358
+ :focus-visible on the inner input so it stays keyboard-only. */
362
359
  .ui-search:has(input:focus-visible) {
363
360
  outline: 2px solid var(--focus-ring);
364
361
  outline-offset: 1px;
package/css/legend.css CHANGED
@@ -93,7 +93,7 @@
93
93
  /* Glyph/symbol swatch — fill an `.ui-icon` mask with the series colour. Match the
94
94
  __swatch fallback chain exactly (--chart-color → --chart-1 → --accent): without
95
95
  the --chart-1 layer, a symbol and a swatch for the SAME series diverge to two
96
- colours whenever a theme overrides --chart-1. (audit C22.) */
96
+ colours whenever a theme overrides --chart-1. */
97
97
  .ui-legend__symbol {
98
98
  block-size: 0.95rem;
99
99
  color: var(--chart-color, var(--chart-1, var(--accent)));
package/css/marks.css CHANGED
@@ -91,7 +91,7 @@
91
91
  @media (prefers-reduced-motion: no-preference) {
92
92
  /* The sweep animates `background-size`, i.e. the highlight FILL — so it is
93
93
  inert on the no-fill styles (underline/box/strike). Scope it out rather
94
- than let `--draw` look applied but do nothing. (content review C14.) */
94
+ than let `--draw` look applied but do nothing. */
95
95
  .ui-mark--draw:not(.ui-mark--underline, .ui-mark--box, .ui-mark--strike) {
96
96
  animation: ui-mark-draw 0.6s var(--ease, ease) both;
97
97
  }
package/css/motion.css CHANGED
@@ -57,7 +57,7 @@
57
57
  scroll far enough to finish its range, so a fade-to-opacity-1 would strand the
58
58
  content permanently transparent. Reach full opacity early (35%) and hold it,
59
59
  so even a partially-driven reveal is fully legible — only the last few px of
60
- the rise are left unfinished. (component audit C9.) */
60
+ the rise are left unfinished. */
61
61
  @keyframes uiScrollReveal {
62
62
  0% {
63
63
  opacity: 0;
@@ -183,7 +183,7 @@
183
183
  /* Stagger children: set --i on each child (or use nth-child cap). Cap the manual
184
184
  --i path at index 6 (360ms) to match the .ui-stagger--auto ceiling — a long
185
185
  list with --i:30 would otherwise hold the last child at opacity:0 for 1.8s
186
- before popping in. (audit C32.) */
186
+ before popping in. */
187
187
  .ui-stagger > * {
188
188
  animation: uiRise var(--duration-slow) var(--ease-spring) both;
189
189
  animation-delay: calc(min(var(--i, 0), 6) * 60ms);
@@ -307,7 +307,7 @@
307
307
  rest to one system colour, so the ring looks uniform with no visible sweep,
308
308
  and the scroll-progress bar can vanish into the canvas. Re-assert distinct
309
309
  system colours so each keeps a visible channel; AT is already covered via
310
- aria-busy / role="status" / role="progressbar". (component audit C35.) */
310
+ aria-busy / role="status" / role="progressbar". */
311
311
  @media (forced-colors: active) {
312
312
  .ui-spinner {
313
313
  border-color: CanvasText;
@@ -321,7 +321,7 @@
321
321
  /* HCM strips the shimmer gradient, leaving an invisible box where a loading
322
322
  placeholder should be. Give it a system-colour border so the skeleton
323
323
  still reads as present. Same decorative-loading family as the spinner +
324
- scroll-progress above. (audit C15.) */
324
+ scroll-progress above. */
325
325
  .ui-skeleton {
326
326
  border: 1px solid CanvasText;
327
327
  }
@@ -346,7 +346,7 @@
346
346
  The range is `entry 0% entry 100%`, NOT the old `cover 40%`: `cover` only
347
347
  completes once the element has scrolled most of the way THROUGH the viewport,
348
348
  which an element near the document bottom can never do — it froze part-way,
349
- leaving a conclusion section permanently semi-transparent (C9). `entry`
349
+ leaving a conclusion section permanently semi-transparent. `entry`
350
350
  completes the moment the element is fully in view, which any scroll-to-bottom
351
351
  reaches. Paired with the early-opacity uiScrollReveal keyframe, an element
352
352
  taller than the viewport is legible even if its range never fully completes. */
@@ -405,7 +405,7 @@
405
405
  /* Zero the delay too: `.ui-stagger` children animate `uiRise` with
406
406
  `fill-mode: both`, so a non-zero `animation-delay` holds them at the
407
407
  `opacity: 0` from-state for the full delay and then pops them in — the
408
- exact late flash a reduced-motion user asked to avoid (C4). */
408
+ exact late flash a reduced-motion user asked to avoid. */
409
409
  animation-delay: 0s !important;
410
410
  transition-duration: 0.01ms !important;
411
411
  }
package/css/overlay.css CHANGED
@@ -91,7 +91,7 @@ dialog.ui-modal[open]::backdrop {
91
91
  /* Background scroll-lock. A native <dialog> top layer does NOT stop the page
92
92
  behind it scrolling, and the no-JS native path can't lock it in script — so
93
93
  lock it in CSS, for both the native [open] dialog and the controlled .is-open
94
- overlay (incl. the drawer modifier, same base). (component audit C13.) */
94
+ overlay (incl. the drawer modifier, same base). */
95
95
  html:has(dialog.ui-modal[open]),
96
96
  html:has(.ui-modal.is-open) {
97
97
  overflow: hidden;
@@ -164,8 +164,7 @@ html:has(.ui-modal.is-open) {
164
164
  /* Comfortable hit target on coarse pointers — this bespoke close button measured
165
165
  ~26px on touch, below the 2.9rem floor the rest of the button family meets
166
166
  (WCAG 2.5.8). Scoped to coarse so the fine-pointer (mouse) rendering, which
167
- already clears 24×24, is unchanged; centre the glyph in the enlarged box.
168
- (audit C3.) */
167
+ already clears 24×24, is unchanged; centre the glyph in the enlarged box. */
169
168
  @media (pointer: coarse) {
170
169
  .ui-modal__close {
171
170
  align-items: center;
@@ -313,7 +312,7 @@ html:has(.ui-modal.is-open) {
313
312
  /* Menu hover/focus paints var(--bg-accent), which HCM strips, so the hovered row
314
313
  is indistinguishable from its siblings (focus keeps the UA ring; hover has no
315
314
  such fallback). Re-assert a system Highlight like the combobox/command rows,
316
- for parity with those sibling surfaces. (component audit C22.) */
315
+ for parity with those sibling surfaces. */
317
316
  @media (forced-colors: active) {
318
317
  .ui-menu__item:hover,
319
318
  .ui-menu__item:focus-visible {
@@ -384,7 +383,7 @@ html:has(.ui-modal.is-open) {
384
383
 
385
384
  /* The active option is tracked by aria-activedescendant — it is NEVER DOM-focused,
386
385
  so it gets no UA focus ring under HCM, and its only cue (--bg-accent) is stripped.
387
- Re-assert a system Highlight like the command palette does. (audit C2.) */
386
+ Re-assert a system Highlight like the command palette does. */
388
387
  @media (forced-colors: active) {
389
388
  .ui-combobox__option.is-active,
390
389
  .ui-combobox__option[aria-selected='true'] {
@@ -454,8 +453,7 @@ html:has(.ui-modal.is-open) {
454
453
  /* Forced-colors drops both `backdrop-filter: blur()` and the `color-mix()`
455
454
  scrim, so the dialog/lightbox would float over an undimmed page with no
456
455
  separation. Re-assert a translucent scrim with `forced-color-adjust: none`
457
- so the layering reads in High Contrast, matching the meter/dot precedent
458
- (C28). */
456
+ so the layering reads in High Contrast, matching the meter/dot precedent. */
459
457
  @media (forced-colors: active) {
460
458
  .ui-modal::backdrop,
461
459
  .ui-lightbox::backdrop {