@jirawatpyk/aura-tokens 5.9.0 → 5.10.1

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/aura-tailwind.css CHANGED
@@ -1,4 +1,4 @@
1
- /* AURA v5.9.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand.
1
+ /* AURA v5.10.1 — Tailwind v4 theme. GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand.
2
2
  * @import "tailwindcss";
3
3
  * @import "@jirawatpyk/aura-tokens/tailwind.css";
4
4
  * Gives bg-bg-surface, text-fg-primary, border-border-strong, p-aura-6, rounded-aura-xl, shadow-aura-overlay,
@@ -1,4 +1,4 @@
1
- /* AURA v5.9.0 — Tailwind v4 theme with aura- prefixed names. GENERATED by scripts/build-tokens.mts. Do not edit by hand.
1
+ /* AURA v5.10.1 — Tailwind v4 theme with aura- prefixed names. GENERATED by scripts/build-tokens.mts. Do not edit by hand.
2
2
  * For apps that already have a Tailwind theme (shadcn): nothing here redefines an existing name.
3
3
  * @layer aura-tokens, theme, base, aura, components, utilities;
4
4
  * @import "tailwindcss";
package/aura.css CHANGED
@@ -1,4 +1,4 @@
1
- /* AURA v5.9.0 — GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand. */
1
+ /* AURA v5.10.1 — GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand. */
2
2
  /* Fonts: load aura-fonts.css (or the <link> tags in README) separately, so a blocked Google Fonts never breaks this file. */
3
3
 
4
4
  :root, [data-theme="light"] {
@@ -1233,13 +1233,14 @@ window.Aura = (() => {
1233
1233
  // src/Menu.tsx
1234
1234
  var React8 = __toESM(require_react(), 1);
1235
1235
  var import_react_dom = __toESM(require_react_dom(), 1);
1236
+ var ITEMS = '[role^="menuitem"]';
1236
1237
  var Menu = React8.forwardRef(function Menu2(props, ref) {
1237
1238
  const own = React8.useRef(null), merged = useMergedRef(ref, own);
1238
1239
  const posState = React8.useState(null);
1239
1240
  const pos = posState[0], setPos = posState[1];
1240
1241
  const items2 = props.items || [];
1241
1242
  const mounted = useMounted();
1242
- const headerId = uid();
1243
+ const headerId = uid(), itemId = uid();
1243
1244
  const hasHeader = props.header != null && props.header !== false && props.header !== "";
1244
1245
  const Link = useLinkComponent(props.linkComponent);
1245
1246
  useIsoLayoutEffect(
@@ -1267,8 +1268,8 @@ window.Aura = (() => {
1267
1268
  hadHeader.current = hasHeader;
1268
1269
  const a = document.activeElement;
1269
1270
  if (own.current && (!a || a === document.body)) {
1270
- const first = own.current.querySelector('[role^="menuitem"]:not([disabled])');
1271
- if (first) first.focus();
1271
+ const first = own.current.querySelector(ITEMS);
1272
+ (first || own.current.querySelector('[role="menu"]') || own.current).focus();
1272
1273
  }
1273
1274
  },
1274
1275
  [hasHeader]
@@ -1276,8 +1277,11 @@ window.Aura = (() => {
1276
1277
  React8.useEffect(
1277
1278
  function() {
1278
1279
  if (!mounted) return;
1279
- const first = own.current && own.current.querySelector('[role^="menuitem"]:not([disabled])');
1280
- if (first && props.autoFocus !== false) first.focus();
1280
+ const first = own.current && own.current.querySelector(ITEMS);
1281
+ if (props.autoFocus !== false) {
1282
+ if (first) first.focus();
1283
+ else if (own.current) (own.current.querySelector('[role="menu"]') || own.current).focus();
1284
+ }
1281
1285
  function outside(e) {
1282
1286
  if (own.current && !own.current.contains(e.target) && !(props.anchor && props.anchor.contains(e.target)))
1283
1287
  props.onClose(false);
@@ -1297,16 +1301,18 @@ window.Aura = (() => {
1297
1301
  [mounted]
1298
1302
  );
1299
1303
  function onKeyDown(e) {
1300
- const list2 = Array.prototype.slice.call(
1301
- own.current.querySelectorAll('[role^="menuitem"]:not([disabled])')
1302
- );
1304
+ const list2 = Array.prototype.slice.call(own.current.querySelectorAll(ITEMS));
1303
1305
  const i = list2.indexOf(document.activeElement);
1306
+ if (!list2.length && e.key !== "Escape" && e.key !== "Tab") {
1307
+ e.stopPropagation();
1308
+ return;
1309
+ }
1304
1310
  if (e.key === "ArrowDown") {
1305
1311
  e.preventDefault();
1306
1312
  list2[(i + 1) % list2.length].focus();
1307
1313
  } else if (e.key === "ArrowUp") {
1308
1314
  e.preventDefault();
1309
- list2[(i - 1 + list2.length) % list2.length].focus();
1315
+ list2[i < 0 ? list2.length - 1 : (i - 1 + list2.length) % list2.length].focus();
1310
1316
  } else if (e.key === "Home") {
1311
1317
  e.preventDefault();
1312
1318
  list2[0].focus();
@@ -1345,6 +1351,10 @@ window.Aura = (() => {
1345
1351
  it.hint ? /* @__PURE__ */ React8.createElement("span", { key: "h", className: "aura-menu__hint" }, it.hint) : null
1346
1352
  ];
1347
1353
  const cls = cx("aura-menu__item", it.tone === "danger" && "aura-menu__item--danger");
1354
+ const reasonId = it.disabled && it.disabledReason ? itemId + "-r" + i : void 0;
1355
+ if (reasonId)
1356
+ body[2] = /* Hidden from the name (it would be read twice); aria-describedby still reads it. */
1357
+ /* @__PURE__ */ React8.createElement("span", { key: "h", id: reasonId, className: "aura-menu__hint aura-menu__reason", "aria-hidden": true }, it.disabledReason);
1348
1358
  if (it.href && !it.disabled)
1349
1359
  return /* @__PURE__ */ React8.createElement(
1350
1360
  Link,
@@ -1369,11 +1379,13 @@ window.Aura = (() => {
1369
1379
  key: i,
1370
1380
  type: "button",
1371
1381
  tabIndex: -1,
1372
- disabled: it.disabled,
1382
+ "aria-disabled": it.disabled ? true : void 0,
1383
+ "aria-describedby": reasonId,
1373
1384
  role: isRadio ? "menuitemradio" : isCheck ? "menuitemcheckbox" : "menuitem",
1374
1385
  "aria-checked": isRadio || isCheck ? !!it.checked : void 0,
1375
1386
  className: cls,
1376
1387
  onClick: function() {
1388
+ if (it.disabled) return;
1377
1389
  if (it.onSelect) it.onSelect();
1378
1390
  if (!it.keepOpen) props.onClose(true);
1379
1391
  }
@@ -1402,7 +1414,19 @@ window.Aura = (() => {
1402
1414
  }
1403
1415
  },
1404
1416
  props.header
1405
- ), /* @__PURE__ */ React8.createElement("div", { role: "menu", "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React8.createElement("div", { ref: merged, role: "menu", "aria-label": props.label, className: "aura-menu", onKeyDown, style }, list);
1417
+ ), /* @__PURE__ */ React8.createElement("div", { role: "menu", tabIndex: -1, "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React8.createElement(
1418
+ "div",
1419
+ {
1420
+ ref: merged,
1421
+ role: "menu",
1422
+ tabIndex: -1,
1423
+ "aria-label": props.label,
1424
+ className: "aura-menu",
1425
+ onKeyDown,
1426
+ style
1427
+ },
1428
+ list
1429
+ );
1406
1430
  return mounted ? (0, import_react_dom.createPortal)(el, document.body) : null;
1407
1431
  });
1408
1432
 
@@ -1417,7 +1441,7 @@ window.Aura = (() => {
1417
1441
  function() {
1418
1442
  if (!anchor || !toLast.current || !menuRef.current) return;
1419
1443
  toLast.current = false;
1420
- const list = menuRef.current.querySelectorAll('[role^="menuitem"]:not([disabled])');
1444
+ const list = menuRef.current.querySelectorAll('[role^="menuitem"]');
1421
1445
  if (list.length) list[list.length - 1].focus();
1422
1446
  },
1423
1447
  [anchor]
@@ -3859,6 +3883,16 @@ window.Aura = (() => {
3859
3883
 
3860
3884
  // src/Tag.tsx
3861
3885
  var React24 = __toESM(require_react(), 1);
3886
+ function textOf(node) {
3887
+ if (node == null || typeof node === "boolean") return "";
3888
+ if (typeof node === "string" || typeof node === "number") return String(node);
3889
+ if (Array.isArray(node)) return node.map(textOf).join("");
3890
+ if (React24.isValidElement(node)) {
3891
+ const p = node.props;
3892
+ return p["aria-hidden"] === true || p["aria-hidden"] === "true" ? "" : textOf(p.children);
3893
+ }
3894
+ return "";
3895
+ }
3862
3896
  var Tag = React24.forwardRef(function Tag2(props, ref) {
3863
3897
  const t = useStrings();
3864
3898
  const selectable = props.onClick != null || props.selected != null;
@@ -3882,12 +3916,18 @@ window.Aura = (() => {
3882
3916
  inner[1]
3883
3917
  );
3884
3918
  }
3919
+ const name = props.onRemove && !props.removeLabel ? textOf(props.children).replace(/\s+/g, " ").trim() : "";
3920
+ if (props.onRemove && !props.disabled && !props.removeLabel && !name)
3921
+ devWarnOnce(
3922
+ "tag-remove-label",
3923
+ 'Tag: the remove button has no name \u2014 its children have no text. Pass removeLabel (e.g. "Remove Acme AB").'
3924
+ );
3885
3925
  return /* @__PURE__ */ React24.createElement("span", { ...rest, ref, className: cx("aura-tag", props.disabled && "is-disabled", props.className) }, inner, props.onRemove && !props.disabled ? /* @__PURE__ */ React24.createElement(
3886
3926
  "button",
3887
3927
  {
3888
3928
  type: "button",
3889
3929
  className: "aura-tag__remove",
3890
- "aria-label": props.removeLabel || t.remove(typeof props.children === "string" ? props.children : ""),
3930
+ "aria-label": props.removeLabel || t.remove(name),
3891
3931
  onClick: props.onRemove
3892
3932
  },
3893
3933
  /* @__PURE__ */ React24.createElement(Icon, { name: /* @__PURE__ */ React24.createElement(IconX, null), size: 12 })
@@ -5876,6 +5916,12 @@ window.Aura = (() => {
5876
5916
  })[0] || firstOn || items2[0];
5877
5917
  const Link = useLinkComponent(props.linkComponent);
5878
5918
  const manual = props.activation === "manual";
5919
+ const seg = props.variant === "segmented";
5920
+ const listCls = cx("aura-tabs__list", seg && "aura-segmented", seg && props.fullWidth && "is-full");
5921
+ const tabCls = function(on, extra) {
5922
+ return cx("aura-tab", on && "is-active", seg && "aura-segmented__option", seg && on && "is-selected", extra);
5923
+ };
5924
+ const rootCls = cx("aura-tabs", seg && "aura-tabs--segmented");
5879
5925
  const [focusId, setFocusId] = React32.useState(null);
5880
5926
  items2.forEach(function(t) {
5881
5927
  const al = t.tabProps && t.tabProps["aria-label"];
@@ -5891,9 +5937,9 @@ window.Aura = (() => {
5891
5937
  {
5892
5938
  ref,
5893
5939
  "aria-label": props.label,
5894
- className: cx("aura-tabs aura-tabs--links", props.className)
5940
+ className: cx(rootCls, "aura-tabs--links", props.className)
5895
5941
  },
5896
- /* @__PURE__ */ React32.createElement("div", { className: "aura-tabs__list" }, items2.map(function(t) {
5942
+ /* @__PURE__ */ React32.createElement("div", { className: listCls }, items2.map(function(t) {
5897
5943
  const on = t.id === cur;
5898
5944
  const inner = [
5899
5945
  t.icon ? /* @__PURE__ */ React32.createElement(Icon, { key: "i", name: t.icon }) : null,
@@ -5917,7 +5963,7 @@ window.Aura = (() => {
5917
5963
  {
5918
5964
  ...omit(own, ["onClick"]),
5919
5965
  key: t.id,
5920
- className: cx("aura-tab is-disabled", own.className),
5966
+ className: cx(tabCls(false), "is-disabled", own.className),
5921
5967
  "aria-disabled": true
5922
5968
  },
5923
5969
  inner
@@ -5927,7 +5973,7 @@ window.Aura = (() => {
5927
5973
  ...own,
5928
5974
  key: t.id,
5929
5975
  href: t.href,
5930
- className: cx("aura-tab", on && "is-active", own.className),
5976
+ className: tabCls(on, own.className),
5931
5977
  "aria-current": on ? "page" : void 0,
5932
5978
  onClick: function(e) {
5933
5979
  if (own.onClick) own.onClick(e);
@@ -5951,12 +5997,12 @@ window.Aura = (() => {
5951
5997
  const stop = manual && focusId && items2.filter(function(t) {
5952
5998
  return t.id === focusId && !t.disabled;
5953
5999
  })[0] || current2;
5954
- return /* @__PURE__ */ React32.createElement("div", { ref, className: cx("aura-tabs", props.className) }, /* @__PURE__ */ React32.createElement(
6000
+ return /* @__PURE__ */ React32.createElement("div", { ref, className: cx(rootCls, props.className) }, /* @__PURE__ */ React32.createElement(
5955
6001
  "div",
5956
6002
  {
5957
6003
  role: "tablist",
5958
6004
  "aria-label": props.label,
5959
- className: "aura-tabs__list",
6005
+ className: listCls,
5960
6006
  onBlur: manual ? function(e) {
5961
6007
  if (!e.currentTarget.contains(e.relatedTarget)) setFocusId(null);
5962
6008
  } : void 0,
@@ -5999,7 +6045,7 @@ window.Aura = (() => {
5999
6045
  ref: function(el) {
6000
6046
  refs.current[t.id] = el;
6001
6047
  },
6002
- className: cx("aura-tab", on && "is-active", own.className),
6048
+ className: tabCls(on, own.className),
6003
6049
  onFocus: manual ? function(e) {
6004
6050
  if (own.onFocus) own.onFocus(e);
6005
6051
  setFocusId(t.id);
@@ -226,7 +226,13 @@ a.aura-btn { text-decoration: none; }
226
226
  }
227
227
  .aura-menu__item:hover, .aura-menu__item:focus { background: var(--aura-bg-surface-hover); outline: none; }
228
228
  .aura-menu__item:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
229
- .aura-menu__item:disabled { opacity: var(--aura-disabled-opacity); cursor: default; background: transparent; }
229
+ .aura-menu__item:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
230
+ /* 5.10: a disabled item is dimmed but focusable; its reason stays at full strength (it is the information). */
231
+ .aura-menu__item[aria-disabled="true"] { cursor: default; }
232
+ .aura-menu__item[aria-disabled="true"] > :not(.aura-menu__reason) { opacity: var(--aura-disabled-opacity); }
233
+ .aura-menu__reason { padding-left: var(--aura-space-4); }
234
+ .aura-menu__item:disabled, .aura-menu__item[aria-disabled="true"]:hover:not(:focus) { background: transparent; }
235
+ .aura-menu:focus, .aura-menu__list:focus { outline: none; }
230
236
  .aura-menu__item .aura-icon { color: var(--aura-fg-secondary); }
231
237
  .aura-menu__label { flex: 1; }
232
238
  .aura-menu__blank { width: var(--aura-icon-sm); flex: none; }
@@ -460,7 +466,13 @@ a.aura-icon-btn { text-decoration: none; }
460
466
  .aura-radio-group.is-invalid .aura-radio:not(:checked) { border-color: var(--aura-border-danger); }
461
467
 
462
468
  .aura-switch-row { display: flex; align-items: flex-start; gap: var(--aura-space-3); font-family: var(--font-sans); }
463
- .aura-switch-row.is-disabled { opacity: var(--aura-disabled-opacity); }
469
+ /* 5.10.1: disabled rows, on or off, are treated alike and stay readable — the label turns secondary, the description
470
+ * keeps its colour (often the reason it's locked) — and only the switch fades: off to the disabled opacity, on to 0.75,
471
+ * ≥3:1 apart in both themes, so "locked on" never looks like "off". */
472
+ .aura-switch-row.is-disabled { cursor: not-allowed; }
473
+ .aura-switch-row.is-disabled .aura-choice__label { color: var(--aura-fg-secondary); cursor: not-allowed; }
474
+ .aura-switch:disabled { opacity: var(--aura-disabled-opacity); }
475
+ .aura-switch.is-on:disabled { opacity: 0.75; }
464
476
  .aura-switch {
465
477
  position: relative; flex: none; box-sizing: border-box; margin: 0; padding: 0;
466
478
  width: var(--aura-switch-width); height: var(--aura-switch-height); border-radius: var(--aura-radius-full);
@@ -1203,6 +1215,16 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1203
1215
  .aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
1204
1216
  .aura-segmented--sm .aura-segmented__option.is-icon { width: 28px; padding: 0; }
1205
1217
  .aura-segmented.is-disabled { opacity: var(--aura-disabled-opacity); }
1218
+ /* 5.10 (Chamber-OS 72): Tabs variant="segmented" — the tablist wears SegmentedControl's track and pills. The focus
1219
+ * ring is drawn inside the pill, so neither the scrolling list (many tabs on a phone) nor the next pill can cover it;
1220
+ * full-width tabs share the track but never shrink below their label (the track scrolls instead). The phone rule's
1221
+ * wider tab gap doesn't apply. */
1222
+ .aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; }
1223
+ .aura-tabs--segmented .aura-tabs__list.aura-segmented.is-full .aura-tab { min-width: max-content; }
1224
+ .aura-tabs--segmented .aura-tab.aura-segmented__option { margin-bottom: 0; }
1225
+ .aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { border-radius: var(--aura-radius-full); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
1226
+ .aura-tabs--segmented .aura-tab.aura-segmented__option:disabled,
1227
+ .aura-tabs--segmented .aura-tab.aura-segmented__option.is-disabled { opacity: var(--aura-disabled-opacity); cursor: not-allowed; }
1206
1228
 
1207
1229
  /* A field inside a row Stack takes the remaining width (a search box beside its button). */
1208
1230
  .aura-stack > .aura-field { flex: 1 1 auto; min-width: 0; }
@@ -1585,9 +1607,18 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1585
1607
  .aura-switch__thumb { background: CanvasText; forced-color-adjust: none; }
1586
1608
  .aura-switch.is-on { background: Highlight; forced-color-adjust: none; }
1587
1609
  .aura-switch.is-on .aura-switch__thumb { background: HighlightText; }
1610
+ .aura-switch:disabled, .aura-switch.is-on:disabled { border-color: GrayText; opacity: 1; }
1611
+ .aura-switch:disabled .aura-switch__thumb { background: GrayText; }
1612
+ .aura-switch.is-on:disabled { background: GrayText; }
1613
+ .aura-switch.is-on:disabled .aura-switch__thumb { background: Canvas; }
1614
+ .aura-switch-row.is-disabled .aura-choice__label { color: GrayText; }
1588
1615
  .aura-progress__track, .aura-upload__bar { border: 1px solid CanvasText; }
1589
1616
  .aura-progress__bar, .aura-upload__bar > span { background: Highlight; forced-color-adjust: none; }
1590
1617
  .aura-segmented__option.is-selected { outline: 2px solid Highlight; outline-offset: -2px; }
1591
1618
  .aura-tab { border-bottom-color: Canvas; }
1592
1619
  .aura-tab.is-active { border-bottom-color: Highlight; }
1620
+ /* 5.10: segmented tabs mark the selection with a border and focus with a dashed ring inside it, so a focused tab
1621
+ * and the selected one never look alike (manual activation moves focus without selecting). */
1622
+ .aura-tabs--segmented .aura-tab.aura-segmented__option.is-selected { outline: none; border: 2px solid Highlight; }
1623
+ .aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { outline: 2px dashed CanvasText; outline-offset: -5px; }
1593
1624
  }
@@ -278,7 +278,10 @@ export interface MenuItem {
278
278
  icon?: IconInput | undefined;
279
279
  /** Present = a checkable item (menuitemcheckbox). */
280
280
  checked?: boolean | undefined;
281
+ /** Dimmed and does nothing when chosen, but still reachable with the arrow keys and announced (5.10). */
281
282
  disabled?: boolean | undefined;
283
+ /** Why it's disabled ("You can resend in 5 minutes"): shown in place of the hint and read with the item. (5.10) */
284
+ disabledReason?: string | undefined;
282
285
  /** Keep the menu open after choosing (checkbox lists). */
283
286
  keepOpen?: boolean | undefined;
284
287
  /** Short right-aligned hint, e.g. a shortcut. */
@@ -617,6 +620,11 @@ export interface TabsProps {
617
620
  /** `auto` (default): arrow keys select as they move. `manual`: arrows, Home and End only move focus; Enter, Space
618
621
  * or a click selects — for tabs whose selection does work (loads data, starts a payment). (5.9) */
619
622
  activation?: "auto" | "manual" | undefined;
623
+ /** `underline` (default) or `segmented`: the tabs as a pill track with the selected one raised, like
624
+ * SegmentedControl, keeping every Tabs behaviour (panels, keepMounted, activation, tabProps). (5.10, Chamber-OS 72) */
625
+ variant?: "underline" | "segmented" | undefined;
626
+ /** With `variant="segmented"`: stretch the track to the full width, tabs sharing it equally. (5.10) */
627
+ fullWidth?: boolean | undefined;
620
628
  }
621
629
  export interface NavItem {
622
630
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-tokens",
3
- "version": "5.9.0",
3
+ "version": "5.10.1",
4
4
  "description": "AURA Design System — tokens (CSS variables, Tailwind preset, Figma variables), theme contrast checks and token lint.",
5
5
  "main": "tailwind.tokens.cjs",
6
6
  "types": "components/index.d.ts",
package/tokens.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.9.0",
2
+ "version": "5.10.1",
3
3
  "meta": {
4
4
  "name": "AURA Design System",
5
5
  "tier": "3-tier",