@jirawatpyk/aura-tokens 5.9.0 → 5.10.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/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.0 — 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.0 — 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.0 — 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]
@@ -5876,6 +5900,12 @@ window.Aura = (() => {
5876
5900
  })[0] || firstOn || items2[0];
5877
5901
  const Link = useLinkComponent(props.linkComponent);
5878
5902
  const manual = props.activation === "manual";
5903
+ const seg = props.variant === "segmented";
5904
+ const listCls = cx("aura-tabs__list", seg && "aura-segmented", seg && props.fullWidth && "is-full");
5905
+ const tabCls = function(on, extra) {
5906
+ return cx("aura-tab", on && "is-active", seg && "aura-segmented__option", seg && on && "is-selected", extra);
5907
+ };
5908
+ const rootCls = cx("aura-tabs", seg && "aura-tabs--segmented");
5879
5909
  const [focusId, setFocusId] = React32.useState(null);
5880
5910
  items2.forEach(function(t) {
5881
5911
  const al = t.tabProps && t.tabProps["aria-label"];
@@ -5891,9 +5921,9 @@ window.Aura = (() => {
5891
5921
  {
5892
5922
  ref,
5893
5923
  "aria-label": props.label,
5894
- className: cx("aura-tabs aura-tabs--links", props.className)
5924
+ className: cx(rootCls, "aura-tabs--links", props.className)
5895
5925
  },
5896
- /* @__PURE__ */ React32.createElement("div", { className: "aura-tabs__list" }, items2.map(function(t) {
5926
+ /* @__PURE__ */ React32.createElement("div", { className: listCls }, items2.map(function(t) {
5897
5927
  const on = t.id === cur;
5898
5928
  const inner = [
5899
5929
  t.icon ? /* @__PURE__ */ React32.createElement(Icon, { key: "i", name: t.icon }) : null,
@@ -5917,7 +5947,7 @@ window.Aura = (() => {
5917
5947
  {
5918
5948
  ...omit(own, ["onClick"]),
5919
5949
  key: t.id,
5920
- className: cx("aura-tab is-disabled", own.className),
5950
+ className: cx(tabCls(false), "is-disabled", own.className),
5921
5951
  "aria-disabled": true
5922
5952
  },
5923
5953
  inner
@@ -5927,7 +5957,7 @@ window.Aura = (() => {
5927
5957
  ...own,
5928
5958
  key: t.id,
5929
5959
  href: t.href,
5930
- className: cx("aura-tab", on && "is-active", own.className),
5960
+ className: tabCls(on, own.className),
5931
5961
  "aria-current": on ? "page" : void 0,
5932
5962
  onClick: function(e) {
5933
5963
  if (own.onClick) own.onClick(e);
@@ -5951,12 +5981,12 @@ window.Aura = (() => {
5951
5981
  const stop = manual && focusId && items2.filter(function(t) {
5952
5982
  return t.id === focusId && !t.disabled;
5953
5983
  })[0] || current2;
5954
- return /* @__PURE__ */ React32.createElement("div", { ref, className: cx("aura-tabs", props.className) }, /* @__PURE__ */ React32.createElement(
5984
+ return /* @__PURE__ */ React32.createElement("div", { ref, className: cx(rootCls, props.className) }, /* @__PURE__ */ React32.createElement(
5955
5985
  "div",
5956
5986
  {
5957
5987
  role: "tablist",
5958
5988
  "aria-label": props.label,
5959
- className: "aura-tabs__list",
5989
+ className: listCls,
5960
5990
  onBlur: manual ? function(e) {
5961
5991
  if (!e.currentTarget.contains(e.relatedTarget)) setFocusId(null);
5962
5992
  } : void 0,
@@ -5999,7 +6029,7 @@ window.Aura = (() => {
5999
6029
  ref: function(el) {
6000
6030
  refs.current[t.id] = el;
6001
6031
  },
6002
- className: cx("aura-tab", on && "is-active", own.className),
6032
+ className: tabCls(on, own.className),
6003
6033
  onFocus: manual ? function(e) {
6004
6034
  if (own.onFocus) own.onFocus(e);
6005
6035
  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; }
@@ -1203,6 +1209,16 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1203
1209
  .aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
1204
1210
  .aura-segmented--sm .aura-segmented__option.is-icon { width: 28px; padding: 0; }
1205
1211
  .aura-segmented.is-disabled { opacity: var(--aura-disabled-opacity); }
1212
+ /* 5.10 (Chamber-OS 72): Tabs variant="segmented" — the tablist wears SegmentedControl's track and pills. The focus
1213
+ * ring is drawn inside the pill, so neither the scrolling list (many tabs on a phone) nor the next pill can cover it;
1214
+ * full-width tabs share the track but never shrink below their label (the track scrolls instead). The phone rule's
1215
+ * wider tab gap doesn't apply. */
1216
+ .aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; }
1217
+ .aura-tabs--segmented .aura-tabs__list.aura-segmented.is-full .aura-tab { min-width: max-content; }
1218
+ .aura-tabs--segmented .aura-tab.aura-segmented__option { margin-bottom: 0; }
1219
+ .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)); }
1220
+ .aura-tabs--segmented .aura-tab.aura-segmented__option:disabled,
1221
+ .aura-tabs--segmented .aura-tab.aura-segmented__option.is-disabled { opacity: var(--aura-disabled-opacity); cursor: not-allowed; }
1206
1222
 
1207
1223
  /* A field inside a row Stack takes the remaining width (a search box beside its button). */
1208
1224
  .aura-stack > .aura-field { flex: 1 1 auto; min-width: 0; }
@@ -1590,4 +1606,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1590
1606
  .aura-segmented__option.is-selected { outline: 2px solid Highlight; outline-offset: -2px; }
1591
1607
  .aura-tab { border-bottom-color: Canvas; }
1592
1608
  .aura-tab.is-active { border-bottom-color: Highlight; }
1609
+ /* 5.10: segmented tabs mark the selection with a border and focus with a dashed ring inside it, so a focused tab
1610
+ * and the selected one never look alike (manual activation moves focus without selecting). */
1611
+ .aura-tabs--segmented .aura-tab.aura-segmented__option.is-selected { outline: none; border: 2px solid Highlight; }
1612
+ .aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { outline: 2px dashed CanvasText; outline-offset: -5px; }
1593
1613
  }
@@ -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.0",
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.0",
3
3
  "meta": {
4
4
  "name": "AURA Design System",
5
5
  "tier": "3-tier",