@jirawatpyk/aura-react 5.80.0 → 5.82.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/README.md CHANGED
@@ -248,6 +248,14 @@ DataTable with sort and page in the URL (4.17): `onStateChange={({ sort, page })
248
248
  - **Time zone**: `timeZone` on AuraProvider, DatePicker, DateRangePicker and Calendar decides "today" (the marker, `min`/`max="today"`, the first month shown); or pass `today` as an ISO date. `todayIn('Asia/Bangkok')` is exported from the root and `/server`.
249
249
  - **Toasts** queue past three instead of dropping: six in a row all show, in order, three at a time.
250
250
 
251
+ ## 5.82 — DxT Monitor 25 (Badge xs)
252
+
253
+ - **Badge `size="xs"`**: 16px high (a larger default font grows it), weight 500, 6px inset — a mark under a cell's main value that keeps a `rowHeight="density"` row at 48px (a 12px line with line-height 12px, 4px gap, the badge; a 12px text at its normal line-height is about 18px and the row grows). Thai text stays 11px at this size — the one exception to the 5.30 rule that Thai labels are at least 12px, so stacked tone marks stay off the 16px outline. Every tone and variant; no icon at this size (a dev warning says so). Also from `@jirawatpyk/aura-react/server`. `<Badge size="xs" variant="outline">Confirm</Badge>`.
254
+
255
+ ## 5.81 — DxT Monitor 24 (the tab bar hides after a tap)
256
+
257
+ - **BottomNav `hideOnScroll` and AppShell `headerHideOnScroll` after a tap**: with client-side navigation the bar stays mounted, and on Chrome / Android a tap leaves focus on the tapped tab — the bar then never hid on the next page. Focus in the bar now holds it only when it came from the keyboard: focus that enters right after a pointer press doesn't count, and focus moved with the keyboard does (Tab into or within the bar brings it back and keeps it; keys that only scroll the page, such as Space or PageDown, don't). A text field in the bar holds it however it was focused. No new prop; drop any wrapper that blurs the tab on click.
258
+
251
259
  ## 5.79 — visual audit, set 3 (component by component)
252
260
 
253
261
  - **One disabled look for every Button variant:** a faint fill (`--aura-button-disabled-bg`: zinc-100 light, none in dark), a faint edge (`--aura-button-disabled-border` — the variant's own border, or 1px inside for primary and danger) and the tertiary label, at full opacity — primary, secondary, danger, creative and `aria-disabled` alike; a ghost button has neither fill nor edge. (5.30 gave filled buttons a grey fill that read as a To Do pill and faded outline ones.)
@@ -560,7 +568,7 @@ What Chamber-OS's own page header did that PageHeader didn't, so it can be rebui
560
568
 
561
569
  - **Breadcrumb touch targets** (request 11): on a touch screen (`pointer: coarse`) every crumb link or button (and "…") gets a hit area 44px tall and as wide as its text plus 24px; the text doesn't move. It reaches 12px into the gutter on each side, so short neighbours never share a spot, and only 4px below the text, so it stops short of a PageHeader title right underneath — the rest of the height is above, so keep about 20px clear above a breadcrumb (AppShell's content padding already is). When the crumbs wrap, the rows sit 24px apart on touch screens so one row never covers the next. The current page (not a link) gets none; a link component must render its `<a>` directly in the crumb's `li`. Apps that added their own `.aura-crumbs a::after` rule should drop it.
562
570
  - **StatusTile `status="unknown"`** (request 12): for a thing with no data yet (a site before its first check) — a dashed circle (circle-dot-dashed) in `aura-fg-tertiary` on the plain surface and border, status word "No data" (th: ยังไม่มีข้อมูล, sv: Ingen data), value in the text colour. An unrecognised `status` now shows as unknown rather than OK, with a dev warning.
563
- - **AppShell `headerHideOnScroll`** (request 13): below 1024px the top bar slides up out of view while the page scrolls down (after it has moved past the bar's height and kept going 8px) and comes back after 8px up, near the top, and whenever focus enters it. `--aura-shell-bar-height` is 0 while it is away, so a sticky table header pinned under the bar moves up with it. No slide with reduced motion. The page must scroll the window (AppShell's does).
571
+ - **AppShell `headerHideOnScroll`** (request 13): below 1024px the top bar slides up out of view while the page scrolls down (after it has moved past the bar's height and kept going 8px) and comes back after 8px up, near the top, and whenever focus enters it (5.81: a tap's focus doesn't keep it there; keyboard focus does). `--aura-shell-bar-height` is 0 while it is away, so a sticky table header pinned under the bar moves up with it. No slide with reduced motion. The page must scroll the window (AppShell's does).
564
572
  - **BottomNav `hideOnScroll`** (request 14): the same for the tab bar, sliding down. Its spacer stays (nothing shifts) and `--aura-bottomnav-offset` drops while it is away, so viewport ActionBars and toasts follow it down. **ActionBar `hidesBottomNav`**: while that bar is on the page the BottomNav and its spacer are gone and the bar sits on the bottom edge — for a page whose own actions (Acknowledge, Visit site) replace the tabs. Any element can do the same with `data-aura-hide-bottomnav`.
565
573
 
566
574
  ## 5.34 — StatusTile, TileGrid, Sparkline (DxT Monitor Overview S06)
@@ -923,7 +923,10 @@ window.Aura = (() => {
923
923
  );
924
924
  }
925
925
  function badgeElement(props, ref) {
926
- const rest = omit(props, ["tone", "variant", "icon", "className", "children"]);
926
+ const rest = omit(props, ["tone", "variant", "icon", "className", "children", "size"]);
927
+ const xs = props.size === "xs";
928
+ if (xs && props.icon)
929
+ devWarnOnce("badge-xs-icon", 'Badge size="xs" has no icon (16px leaves no room for one); the icon is not shown.');
927
930
  return /* @__PURE__ */ React4.createElement(
928
931
  "span",
929
932
  {
@@ -934,10 +937,11 @@ window.Aura = (() => {
934
937
  "aura-badge--" + tone(props.tone),
935
938
  props.variant === "solid" && "is-solid",
936
939
  props.variant === "outline" && "is-outline",
940
+ xs && "aura-badge--xs",
937
941
  props.className
938
942
  )
939
943
  },
940
- props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon, size: 12 }) : null,
944
+ props.icon && !xs ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon, size: 12 }) : null,
941
945
  props.children
942
946
  );
943
947
  }
@@ -8751,8 +8755,10 @@ window.Aura = (() => {
8751
8755
  // src/scrollHide.ts
8752
8756
  var React48 = __toESM(require_react(), 1);
8753
8757
  var STEP = 8;
8758
+ var TEXT_ENTRY = 'textarea, select, [contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"], input:not([type]), input[type="text"], input[type="search"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"], input[type="password"]';
8754
8759
  function useHideOnScroll(enabled, el, media, from) {
8755
8760
  const st = React48.useState(false), hidden = st[0], setHidden = st[1];
8761
+ const tap = React48.useRef({ pressedAt: -1e9, focus: false });
8756
8762
  React48.useEffect(
8757
8763
  function() {
8758
8764
  if (!enabled || typeof window === "undefined") {
@@ -8762,6 +8768,11 @@ window.Aura = (() => {
8762
8768
  const mq = window.matchMedia ? window.matchMedia(media) : null;
8763
8769
  let last = window.scrollY, lastH = window.innerHeight, resizedAt = -1e9, run = 0;
8764
8770
  let restore = null;
8771
+ function holdsBar(node2) {
8772
+ const a = document.activeElement;
8773
+ if (!node2 || !a || !node2.contains(a)) return false;
8774
+ return !tap.current.focus || a.matches(TEXT_ENTRY);
8775
+ }
8765
8776
  function update() {
8766
8777
  if (restore) {
8767
8778
  const r2 = restore;
@@ -8776,7 +8787,7 @@ window.Aura = (() => {
8776
8787
  const y = window.scrollY, dy = y - last;
8777
8788
  last = y;
8778
8789
  const node2 = el.current;
8779
- if (mq && !mq.matches || y <= from || node2 && document.activeElement && node2.contains(document.activeElement)) {
8790
+ if (mq && !mq.matches || y <= from || holdsBar(node2)) {
8780
8791
  run = 0;
8781
8792
  setHidden(false);
8782
8793
  return;
@@ -8796,7 +8807,12 @@ window.Aura = (() => {
8796
8807
  function disarm() {
8797
8808
  restore = null;
8798
8809
  }
8810
+ function onPress(e) {
8811
+ restore = null;
8812
+ tap.current.pressedAt = e.timeStamp;
8813
+ }
8799
8814
  function onKey(e) {
8815
+ tap.current.pressedAt = -1e9;
8800
8816
  if (e.key !== "Tab" && e.code !== "Tab") {
8801
8817
  if (e.key !== "Shift") restore = null;
8802
8818
  return;
@@ -8806,7 +8822,8 @@ window.Aura = (() => {
8806
8822
  tabbing = false;
8807
8823
  });
8808
8824
  }
8809
- function reveal() {
8825
+ function reveal(e) {
8826
+ tap.current.focus = e.timeStamp - tap.current.pressedAt < 1e3;
8810
8827
  const n3 = el.current;
8811
8828
  if (n3 && n3.classList.contains("is-away")) {
8812
8829
  n3.classList.remove("is-away");
@@ -8822,7 +8839,7 @@ window.Aura = (() => {
8822
8839
  document.addEventListener("keydown", onKey, true);
8823
8840
  window.addEventListener("wheel", disarm, { passive: true, capture: true });
8824
8841
  window.addEventListener("touchstart", disarm, { passive: true, capture: true });
8825
- window.addEventListener("pointerdown", disarm, true);
8842
+ window.addEventListener("pointerdown", onPress, true);
8826
8843
  const node = el.current;
8827
8844
  if (node) node.addEventListener("focusin", reveal);
8828
8845
  if (mq && mq.addEventListener) mq.addEventListener("change", update);
@@ -8832,7 +8849,7 @@ window.Aura = (() => {
8832
8849
  document.removeEventListener("keydown", onKey, true);
8833
8850
  window.removeEventListener("wheel", disarm, true);
8834
8851
  window.removeEventListener("touchstart", disarm, true);
8835
- window.removeEventListener("pointerdown", disarm, true);
8852
+ window.removeEventListener("pointerdown", onPress, true);
8836
8853
  if (node) node.removeEventListener("focusin", reveal);
8837
8854
  if (mq && mq.removeEventListener) mq.removeEventListener("change", update);
8838
8855
  };
@@ -905,7 +905,10 @@ function spinnerElement(props, ref) {
905
905
  );
906
906
  }
907
907
  function badgeElement(props, ref) {
908
- const rest = omit(props, ["tone", "variant", "icon", "className", "children"]);
908
+ const rest = omit(props, ["tone", "variant", "icon", "className", "children", "size"]);
909
+ const xs = props.size === "xs";
910
+ if (xs && props.icon)
911
+ devWarnOnce("badge-xs-icon", 'Badge size="xs" has no icon (16px leaves no room for one); the icon is not shown.');
909
912
  return /* @__PURE__ */ React4.createElement(
910
913
  "span",
911
914
  {
@@ -916,10 +919,11 @@ function badgeElement(props, ref) {
916
919
  "aura-badge--" + tone(props.tone),
917
920
  props.variant === "solid" && "is-solid",
918
921
  props.variant === "outline" && "is-outline",
922
+ xs && "aura-badge--xs",
919
923
  props.className
920
924
  )
921
925
  },
922
- props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon, size: 12 }) : null,
926
+ props.icon && !xs ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon, size: 12 }) : null,
923
927
  props.children
924
928
  );
925
929
  }
@@ -8733,8 +8737,10 @@ var React49 = __toESM(require("react"), 1);
8733
8737
  // src/scrollHide.ts
8734
8738
  var React48 = __toESM(require("react"), 1);
8735
8739
  var STEP = 8;
8740
+ var TEXT_ENTRY = 'textarea, select, [contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"], input:not([type]), input[type="text"], input[type="search"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"], input[type="password"]';
8736
8741
  function useHideOnScroll(enabled, el, media, from) {
8737
8742
  const st = React48.useState(false), hidden = st[0], setHidden = st[1];
8743
+ const tap = React48.useRef({ pressedAt: -1e9, focus: false });
8738
8744
  React48.useEffect(
8739
8745
  function() {
8740
8746
  if (!enabled || typeof window === "undefined") {
@@ -8744,6 +8750,11 @@ function useHideOnScroll(enabled, el, media, from) {
8744
8750
  const mq = window.matchMedia ? window.matchMedia(media) : null;
8745
8751
  let last = window.scrollY, lastH = window.innerHeight, resizedAt = -1e9, run = 0;
8746
8752
  let restore = null;
8753
+ function holdsBar(node2) {
8754
+ const a = document.activeElement;
8755
+ if (!node2 || !a || !node2.contains(a)) return false;
8756
+ return !tap.current.focus || a.matches(TEXT_ENTRY);
8757
+ }
8747
8758
  function update() {
8748
8759
  if (restore) {
8749
8760
  const r2 = restore;
@@ -8758,7 +8769,7 @@ function useHideOnScroll(enabled, el, media, from) {
8758
8769
  const y = window.scrollY, dy = y - last;
8759
8770
  last = y;
8760
8771
  const node2 = el.current;
8761
- if (mq && !mq.matches || y <= from || node2 && document.activeElement && node2.contains(document.activeElement)) {
8772
+ if (mq && !mq.matches || y <= from || holdsBar(node2)) {
8762
8773
  run = 0;
8763
8774
  setHidden(false);
8764
8775
  return;
@@ -8778,7 +8789,12 @@ function useHideOnScroll(enabled, el, media, from) {
8778
8789
  function disarm() {
8779
8790
  restore = null;
8780
8791
  }
8792
+ function onPress(e) {
8793
+ restore = null;
8794
+ tap.current.pressedAt = e.timeStamp;
8795
+ }
8781
8796
  function onKey(e) {
8797
+ tap.current.pressedAt = -1e9;
8782
8798
  if (e.key !== "Tab" && e.code !== "Tab") {
8783
8799
  if (e.key !== "Shift") restore = null;
8784
8800
  return;
@@ -8788,7 +8804,8 @@ function useHideOnScroll(enabled, el, media, from) {
8788
8804
  tabbing = false;
8789
8805
  });
8790
8806
  }
8791
- function reveal() {
8807
+ function reveal(e) {
8808
+ tap.current.focus = e.timeStamp - tap.current.pressedAt < 1e3;
8792
8809
  const n3 = el.current;
8793
8810
  if (n3 && n3.classList.contains("is-away")) {
8794
8811
  n3.classList.remove("is-away");
@@ -8804,7 +8821,7 @@ function useHideOnScroll(enabled, el, media, from) {
8804
8821
  document.addEventListener("keydown", onKey, true);
8805
8822
  window.addEventListener("wheel", disarm, { passive: true, capture: true });
8806
8823
  window.addEventListener("touchstart", disarm, { passive: true, capture: true });
8807
- window.addEventListener("pointerdown", disarm, true);
8824
+ window.addEventListener("pointerdown", onPress, true);
8808
8825
  const node = el.current;
8809
8826
  if (node) node.addEventListener("focusin", reveal);
8810
8827
  if (mq && mq.addEventListener) mq.addEventListener("change", update);
@@ -8814,7 +8831,7 @@ function useHideOnScroll(enabled, el, media, from) {
8814
8831
  document.removeEventListener("keydown", onKey, true);
8815
8832
  window.removeEventListener("wheel", disarm, true);
8816
8833
  window.removeEventListener("touchstart", disarm, true);
8817
- window.removeEventListener("pointerdown", disarm, true);
8834
+ window.removeEventListener("pointerdown", onPress, true);
8818
8835
  if (node) node.removeEventListener("focusin", reveal);
8819
8836
  if (mq && mq.removeEventListener) mq.removeEventListener("change", update);
8820
8837
  };
@@ -137,7 +137,10 @@ function spinnerElement(props, ref) {
137
137
  );
138
138
  }
139
139
  function badgeElement(props, ref) {
140
- const rest = omit(props, ["tone", "variant", "icon", "className", "children"]);
140
+ const rest = omit(props, ["tone", "variant", "icon", "className", "children", "size"]);
141
+ const xs = props.size === "xs";
142
+ if (xs && props.icon)
143
+ devWarnOnce("badge-xs-icon", 'Badge size="xs" has no icon (16px leaves no room for one); the icon is not shown.');
141
144
  return /* @__PURE__ */ React.createElement(
142
145
  "span",
143
146
  {
@@ -148,10 +151,11 @@ function badgeElement(props, ref) {
148
151
  "aura-badge--" + badgeTone(props.tone),
149
152
  props.variant === "solid" && "is-solid",
150
153
  props.variant === "outline" && "is-outline",
154
+ xs && "aura-badge--xs",
151
155
  props.className
152
156
  )
153
157
  },
154
- props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, size: 12 }) : null,
158
+ props.icon && !xs ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, size: 12 }) : null,
155
159
  props.children
156
160
  );
157
161
  }
@@ -1,8 +1,10 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  const STEP = 8;
4
+ const TEXT_ENTRY = 'textarea, select, [contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"], input:not([type]), input[type="text"], input[type="search"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"], input[type="password"]';
4
5
  function useHideOnScroll(enabled, el, media, from) {
5
6
  const st = React.useState(false), hidden = st[0], setHidden = st[1];
7
+ const tap = React.useRef({ pressedAt: -1e9, focus: false });
6
8
  React.useEffect(
7
9
  function() {
8
10
  if (!enabled || typeof window === "undefined") {
@@ -12,6 +14,11 @@ function useHideOnScroll(enabled, el, media, from) {
12
14
  const mq = window.matchMedia ? window.matchMedia(media) : null;
13
15
  let last = window.scrollY, lastH = window.innerHeight, resizedAt = -1e9, run = 0;
14
16
  let restore = null;
17
+ function holdsBar(node2) {
18
+ const a = document.activeElement;
19
+ if (!node2 || !a || !node2.contains(a)) return false;
20
+ return !tap.current.focus || a.matches(TEXT_ENTRY);
21
+ }
15
22
  function update() {
16
23
  if (restore) {
17
24
  const r = restore;
@@ -26,7 +33,7 @@ function useHideOnScroll(enabled, el, media, from) {
26
33
  const y = window.scrollY, dy = y - last;
27
34
  last = y;
28
35
  const node2 = el.current;
29
- if (mq && !mq.matches || y <= from || node2 && document.activeElement && node2.contains(document.activeElement)) {
36
+ if (mq && !mq.matches || y <= from || holdsBar(node2)) {
30
37
  run = 0;
31
38
  setHidden(false);
32
39
  return;
@@ -46,7 +53,12 @@ function useHideOnScroll(enabled, el, media, from) {
46
53
  function disarm() {
47
54
  restore = null;
48
55
  }
56
+ function onPress(e) {
57
+ restore = null;
58
+ tap.current.pressedAt = e.timeStamp;
59
+ }
49
60
  function onKey(e) {
61
+ tap.current.pressedAt = -1e9;
50
62
  if (e.key !== "Tab" && e.code !== "Tab") {
51
63
  if (e.key !== "Shift") restore = null;
52
64
  return;
@@ -56,7 +68,8 @@ function useHideOnScroll(enabled, el, media, from) {
56
68
  tabbing = false;
57
69
  });
58
70
  }
59
- function reveal() {
71
+ function reveal(e) {
72
+ tap.current.focus = e.timeStamp - tap.current.pressedAt < 1e3;
60
73
  const n = el.current;
61
74
  if (n && n.classList.contains("is-away")) {
62
75
  n.classList.remove("is-away");
@@ -72,7 +85,7 @@ function useHideOnScroll(enabled, el, media, from) {
72
85
  document.addEventListener("keydown", onKey, true);
73
86
  window.addEventListener("wheel", disarm, { passive: true, capture: true });
74
87
  window.addEventListener("touchstart", disarm, { passive: true, capture: true });
75
- window.addEventListener("pointerdown", disarm, true);
88
+ window.addEventListener("pointerdown", onPress, true);
76
89
  const node = el.current;
77
90
  if (node) node.addEventListener("focusin", reveal);
78
91
  if (mq && mq.addEventListener) mq.addEventListener("change", update);
@@ -82,7 +95,7 @@ function useHideOnScroll(enabled, el, media, from) {
82
95
  document.removeEventListener("keydown", onKey, true);
83
96
  window.removeEventListener("wheel", disarm, true);
84
97
  window.removeEventListener("touchstart", disarm, true);
85
- window.removeEventListener("pointerdown", disarm, true);
98
+ window.removeEventListener("pointerdown", onPress, true);
86
99
  if (node) node.removeEventListener("focusin", reveal);
87
100
  if (mq && mq.removeEventListener) mq.removeEventListener("change", update);
88
101
  };
package/dist/index.d.cts CHANGED
@@ -1719,6 +1719,9 @@ export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
1719
1719
  /** soft (default) · solid · outline */
1720
1720
  variant?: "soft" | "solid" | "outline" | undefined;
1721
1721
  icon?: IconInput | undefined;
1722
+ /** 5.82 (DxT Monitor 25): `xs` — 16px high, weight 500, 6px inset: a mark under a value inside a `rowHeight="density"`
1723
+ * row. No icon at this size (a dev warning says so). Default `sm` (20px). */
1724
+ size?: "xs" | "sm" | undefined;
1722
1725
  }
1723
1726
  export interface TagProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onClick"> {
1724
1727
  children: React$1.ReactNode;
package/dist/index.d.ts CHANGED
@@ -1719,6 +1719,9 @@ export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
1719
1719
  /** soft (default) · solid · outline */
1720
1720
  variant?: "soft" | "solid" | "outline" | undefined;
1721
1721
  icon?: IconInput | undefined;
1722
+ /** 5.82 (DxT Monitor 25): `xs` — 16px high, weight 500, 6px inset: a mark under a value inside a `rowHeight="density"`
1723
+ * row. No icon at this size (a dev warning says so). Default `sm` (20px). */
1724
+ size?: "xs" | "sm" | undefined;
1722
1725
  }
1723
1726
  export interface TagProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onClick"> {
1724
1727
  children: React$1.ReactNode;
@@ -1308,7 +1308,10 @@ function spinnerElement(props, ref) {
1308
1308
  );
1309
1309
  }
1310
1310
  function badgeElement(props, ref) {
1311
- const rest = omit(props, ["tone", "variant", "icon", "className", "children"]);
1311
+ const rest = omit(props, ["tone", "variant", "icon", "className", "children", "size"]);
1312
+ const xs = props.size === "xs";
1313
+ if (xs && props.icon)
1314
+ devWarnOnce("badge-xs-icon", 'Badge size="xs" has no icon (16px leaves no room for one); the icon is not shown.');
1312
1315
  return /* @__PURE__ */ React3.createElement(
1313
1316
  "span",
1314
1317
  {
@@ -1319,10 +1322,11 @@ function badgeElement(props, ref) {
1319
1322
  "aura-badge--" + tone(props.tone),
1320
1323
  props.variant === "solid" && "is-solid",
1321
1324
  props.variant === "outline" && "is-outline",
1325
+ xs && "aura-badge--xs",
1322
1326
  props.className
1323
1327
  )
1324
1328
  },
1325
- props.icon ? /* @__PURE__ */ React3.createElement(Icon, { name: props.icon, size: 12 }) : null,
1329
+ props.icon && !xs ? /* @__PURE__ */ React3.createElement(Icon, { name: props.icon, size: 12 }) : null,
1326
1330
  props.children
1327
1331
  );
1328
1332
  }
@@ -222,6 +222,9 @@ export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
222
222
  /** soft (default) · solid · outline */
223
223
  variant?: "soft" | "solid" | "outline" | undefined;
224
224
  icon?: IconInput | undefined;
225
+ /** 5.82 (DxT Monitor 25): `xs` — 16px high, weight 500, 6px inset: a mark under a value inside a `rowHeight="density"`
226
+ * row. No icon at this size (a dev warning says so). Default `sm` (20px). */
227
+ size?: "xs" | "sm" | undefined;
225
228
  }
226
229
  export interface EmptyStateProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, "title" | "children"> {
227
230
  title: React$1.ReactNode;
@@ -222,6 +222,9 @@ export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
222
222
  /** soft (default) · solid · outline */
223
223
  variant?: "soft" | "solid" | "outline" | undefined;
224
224
  icon?: IconInput | undefined;
225
+ /** 5.82 (DxT Monitor 25): `xs` — 16px high, weight 500, 6px inset: a mark under a value inside a `rowHeight="density"`
226
+ * row. No icon at this size (a dev warning says so). Default `sm` (20px). */
227
+ size?: "xs" | "sm" | undefined;
225
228
  }
226
229
  export interface EmptyStateProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, "title" | "children"> {
227
230
  title: React$1.ReactNode;
@@ -1241,7 +1241,10 @@ function spinnerElement(props, ref) {
1241
1241
  );
1242
1242
  }
1243
1243
  function badgeElement(props, ref) {
1244
- const rest = omit(props, ["tone", "variant", "icon", "className", "children"]);
1244
+ const rest = omit(props, ["tone", "variant", "icon", "className", "children", "size"]);
1245
+ const xs = props.size === "xs";
1246
+ if (xs && props.icon)
1247
+ devWarnOnce("badge-xs-icon", 'Badge size="xs" has no icon (16px leaves no room for one); the icon is not shown.');
1245
1248
  return /* @__PURE__ */ React3.createElement(
1246
1249
  "span",
1247
1250
  {
@@ -1252,10 +1255,11 @@ function badgeElement(props, ref) {
1252
1255
  "aura-badge--" + tone(props.tone),
1253
1256
  props.variant === "solid" && "is-solid",
1254
1257
  props.variant === "outline" && "is-outline",
1258
+ xs && "aura-badge--xs",
1255
1259
  props.className
1256
1260
  )
1257
1261
  },
1258
- props.icon ? /* @__PURE__ */ React3.createElement(Icon, { name: props.icon, size: 12 }) : null,
1262
+ props.icon && !xs ? /* @__PURE__ */ React3.createElement(Icon, { name: props.icon, size: 12 }) : null,
1259
1263
  props.children
1260
1264
  );
1261
1265
  }
package/dist/styles.css CHANGED
@@ -1671,6 +1671,12 @@ a.aura-upload__name:focus-visible { outline: var(--aura-focus-ring-width) solid
1671
1671
  .aura-badge--neutral.is-outline { border-color: var(--aura-border-control); }
1672
1672
  .aura-badge.is-solid { background: var(--aura-button-primary-bg); color: var(--aura-button-primary-fg); }
1673
1673
  .aura-badge--danger.is-solid { background: var(--aura-status-blocked-solid-bg); color: var(--aura-status-blocked-solid-fg); }
1674
+ /* 5.82 (DxT Monitor 25): xs — a mark under a cell's value in a density row (48px: a 12px line, 4px, this). 16px at the
1675
+ * default font (min-, so a larger default font grows it), weight 500, 6px inset. Same tones and variants. */
1676
+ .aura-badge--xs { min-height: 16px; padding: 0 6px; font-weight: 500; }
1677
+ /* Thai keeps 11px at xs — the one exception to the 5.30 Thai 12px floor: at 12px with line-height 1 the stacked tone
1678
+ * marks (ซ้ำ) touch the outline; 11px keeps them off the border. */
1679
+ .aura-badge.aura-badge--xs:lang(th) { font-size: max(11px, .6875rem); }
1674
1680
 
1675
1681
  /* ---------- Tag ---------- */
1676
1682
  .aura-tag {
@@ -1673,6 +1673,12 @@ a.aura-upload__name:focus-visible { outline: var(--aura-focus-ring-width) solid
1673
1673
  .aura-badge--neutral.is-outline { border-color: var(--aura-border-control); }
1674
1674
  .aura-badge.is-solid { background: var(--aura-button-primary-bg); color: var(--aura-button-primary-fg); }
1675
1675
  .aura-badge--danger.is-solid { background: var(--aura-status-blocked-solid-bg); color: var(--aura-status-blocked-solid-fg); }
1676
+ /* 5.82 (DxT Monitor 25): xs — a mark under a cell's value in a density row (48px: a 12px line, 4px, this). 16px at the
1677
+ * default font (min-, so a larger default font grows it), weight 500, 6px inset. Same tones and variants. */
1678
+ .aura-badge--xs { min-height: 16px; padding: 0 6px; font-weight: 500; }
1679
+ /* Thai keeps 11px at xs — the one exception to the 5.30 Thai 12px floor: at 12px with line-height 1 the stacked tone
1680
+ * marks (ซ้ำ) touch the outline; 11px keeps them off the border. */
1681
+ .aura-badge.aura-badge--xs:lang(th) { font-size: max(11px, .6875rem); }
1676
1682
 
1677
1683
  /* ---------- Tag ---------- */
1678
1684
  .aura-tag {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.80.0",
3
+ "version": "5.82.0",
4
4
  "description": "AURA Design System — React components (Enterprise Standard, Human Creative). Thai-first: Buddhist-era dates, Thai collation.",
5
5
  "license": "MIT",
6
6
  "type": "module",