@jirawatpyk/aura-tokens 5.5.0 → 5.7.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.5.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand.
1
+ /* AURA v5.7.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.5.0 — Tailwind v4 theme with aura- prefixed names. GENERATED by scripts/build-tokens.mts. Do not edit by hand.
1
+ /* AURA v5.7.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.5.0 — GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand. */
1
+ /* AURA v5.7.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"] {
@@ -828,6 +828,8 @@ window.Aura = (() => {
828
828
  const pos = posState[0], setPos = posState[1];
829
829
  const items2 = props.items || [];
830
830
  const mounted = useMounted();
831
+ const headerId = uid();
832
+ const hasHeader = props.header != null && props.header !== false && props.header !== "";
831
833
  const Link = useLinkComponent(props.linkComponent);
832
834
  useIsoLayoutEffect(
833
835
  function() {
@@ -845,7 +847,20 @@ window.Aura = (() => {
845
847
  const left = Math.max(8, Math.min(r.right - mw, window.innerWidth - mw - 8));
846
848
  setPos({ top, left, maxHeight });
847
849
  },
848
- [props.anchor, mounted]
850
+ [props.anchor, mounted, hasHeader]
851
+ );
852
+ const hadHeader = React6.useRef(hasHeader);
853
+ React6.useEffect(
854
+ function() {
855
+ if (hadHeader.current === hasHeader) return;
856
+ hadHeader.current = hasHeader;
857
+ const a = document.activeElement;
858
+ if (own.current && (!a || a === document.body)) {
859
+ const first = own.current.querySelector('[role^="menuitem"]:not([disabled])');
860
+ if (first) first.focus();
861
+ }
862
+ },
863
+ [hasHeader]
849
864
  );
850
865
  React6.useEffect(
851
866
  function() {
@@ -871,22 +886,22 @@ window.Aura = (() => {
871
886
  [mounted]
872
887
  );
873
888
  function onKeyDown(e) {
874
- const list = Array.prototype.slice.call(
889
+ const list2 = Array.prototype.slice.call(
875
890
  own.current.querySelectorAll('[role^="menuitem"]:not([disabled])')
876
891
  );
877
- const i = list.indexOf(document.activeElement);
892
+ const i = list2.indexOf(document.activeElement);
878
893
  if (e.key === "ArrowDown") {
879
894
  e.preventDefault();
880
- list[(i + 1) % list.length].focus();
895
+ list2[(i + 1) % list2.length].focus();
881
896
  } else if (e.key === "ArrowUp") {
882
897
  e.preventDefault();
883
- list[(i - 1 + list.length) % list.length].focus();
898
+ list2[(i - 1 + list2.length) % list2.length].focus();
884
899
  } else if (e.key === "Home") {
885
900
  e.preventDefault();
886
- list[0].focus();
901
+ list2[0].focus();
887
902
  } else if (e.key === "End") {
888
903
  e.preventDefault();
889
- list[list.length - 1].focus();
904
+ list2[list2.length - 1].focus();
890
905
  } else if (e.key === " " && document.activeElement && document.activeElement.tagName === "A") {
891
906
  e.preventDefault();
892
907
  document.activeElement.click();
@@ -898,9 +913,9 @@ window.Aura = (() => {
898
913
  }
899
914
  e.stopPropagation();
900
915
  }
901
- function groupItems(list) {
916
+ function groupItems(list2) {
902
917
  const out = [];
903
- list.forEach(function(it, i) {
918
+ list2.forEach(function(it, i) {
904
919
  const g = it.type === "radio" && it.group ? it.group : null;
905
920
  const last = out[out.length - 1];
906
921
  if (last && last.group === g && g !== null) last.items.push({ it, i });
@@ -955,27 +970,28 @@ window.Aura = (() => {
955
970
  body
956
971
  );
957
972
  }
958
- const el = /* @__PURE__ */ React6.createElement(
973
+ const style = {
974
+ top: pos ? pos.top : -9999,
975
+ left: pos ? pos.left : -9999,
976
+ maxHeight: pos && pos.maxHeight ? pos.maxHeight : void 0
977
+ };
978
+ const list = groupItems(items2).map(function(block, bi) {
979
+ const rendered = block.items.map(function(x) {
980
+ return renderItem(x.it, x.i);
981
+ });
982
+ return block.group ? /* @__PURE__ */ React6.createElement("div", { key: "g" + bi, role: "group", "aria-label": block.group, className: "aura-menu__group" }, rendered) : /* @__PURE__ */ React6.createElement(React6.Fragment, { key: "f" + bi }, rendered);
983
+ });
984
+ const el = hasHeader ? /* @__PURE__ */ React6.createElement("div", { ref: merged, className: "aura-menu has-header", onKeyDown, style }, /* @__PURE__ */ React6.createElement(
959
985
  "div",
960
986
  {
961
- ref: merged,
962
- role: "menu",
963
- "aria-label": props.label,
964
- className: "aura-menu",
965
- onKeyDown,
966
- style: {
967
- top: pos ? pos.top : -9999,
968
- left: pos ? pos.left : -9999,
969
- maxHeight: pos && pos.maxHeight ? pos.maxHeight : void 0
987
+ className: "aura-menu__header",
988
+ id: headerId,
989
+ onMouseDown: function(e) {
990
+ e.preventDefault();
970
991
  }
971
992
  },
972
- groupItems(items2).map(function(block, bi) {
973
- const rendered = block.items.map(function(x) {
974
- return renderItem(x.it, x.i);
975
- });
976
- return block.group ? /* @__PURE__ */ React6.createElement("div", { key: "g" + bi, role: "group", "aria-label": block.group, className: "aura-menu__group" }, rendered) : /* @__PURE__ */ React6.createElement(React6.Fragment, { key: "f" + bi }, rendered);
977
- })
978
- );
993
+ props.header
994
+ ), /* @__PURE__ */ React6.createElement("div", { role: "menu", "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React6.createElement("div", { ref: merged, role: "menu", "aria-label": props.label, className: "aura-menu", onKeyDown, style }, list);
979
995
  return mounted ? (0, import_react_dom.createPortal)(el, document.body) : null;
980
996
  });
981
997
 
@@ -1015,6 +1031,7 @@ window.Aura = (() => {
1015
1031
  label: props.label,
1016
1032
  items: props.items,
1017
1033
  linkComponent: props.linkComponent,
1034
+ header: props.header,
1018
1035
  onClose: function(restore) {
1019
1036
  setAnchor(null);
1020
1037
  if (restore && typeof anchor.focus === "function") anchor.focus();
@@ -3079,10 +3096,35 @@ window.Aura = (() => {
3079
3096
  promote();
3080
3097
  emitToasts();
3081
3098
  };
3099
+ var hotkeyOrigin = /* @__PURE__ */ new WeakMap();
3100
+ function outsideToaster(el) {
3101
+ return el && el !== document.body && !el.closest(".aura-toaster") ? el : null;
3102
+ }
3082
3103
  function ToastItem(props) {
3083
3104
  const str = useStrings();
3105
+ const Link = useLinkComponent();
3106
+ const node = React19.useRef(null);
3107
+ const origin = React19.useRef(null);
3108
+ useIsoLayoutEffect(function() {
3109
+ origin.current = outsideToaster(document.activeElement);
3110
+ const el = node.current;
3111
+ return function() {
3112
+ if (!el || !el.contains(document.activeElement)) return;
3113
+ const back = [hotkeyOrigin.get(el), origin.current].filter(function(b) {
3114
+ return !!b && b.isConnected;
3115
+ })[0];
3116
+ hotkeyOrigin.delete(el);
3117
+ setTimeout(function() {
3118
+ if (back && back.isConnected && (!document.activeElement || document.activeElement === document.body))
3119
+ back.focus();
3120
+ }, 0);
3121
+ };
3122
+ }, []);
3084
3123
  const t = props.toast, timer = React19.useRef(null), left = React19.useRef(t.duration || 5e3), since = React19.useRef(0);
3085
- const hover = React19.useRef(false);
3124
+ const hover = React19.useRef(false), focused = React19.useRef(false);
3125
+ function syncFocus() {
3126
+ if (focused.current && !(node.current && node.current.contains(document.activeElement))) focused.current = false;
3127
+ }
3086
3128
  function start() {
3087
3129
  if (left.current === Infinity || timer.current) return;
3088
3130
  since.current = Date.now();
@@ -3091,11 +3133,10 @@ window.Aura = (() => {
3091
3133
  }, left.current);
3092
3134
  }
3093
3135
  function resume() {
3094
- hover.current = false;
3095
- start();
3136
+ syncFocus();
3137
+ if (!hover.current && !focused.current) start();
3096
3138
  }
3097
3139
  function pause() {
3098
- hover.current = true;
3099
3140
  if (timer.current) {
3100
3141
  clearTimeout(timer.current);
3101
3142
  timer.current = null;
@@ -3107,23 +3148,56 @@ window.Aura = (() => {
3107
3148
  if (timer.current) clearTimeout(timer.current);
3108
3149
  timer.current = null;
3109
3150
  left.current = t.duration || 5e3;
3110
- if (!hover.current) start();
3151
+ syncFocus();
3152
+ if (!hover.current && !focused.current) start();
3111
3153
  return function() {
3112
3154
  if (timer.current) clearTimeout(timer.current);
3113
3155
  };
3114
3156
  },
3115
3157
  [t.rev]
3116
3158
  );
3159
+ const rich = t.description != null && typeof t.description !== "boolean" && t.description !== "";
3160
+ const actions = (t.actions && t.actions.length ? t.actions : t.action ? [t.action] : []).slice(0, 2);
3161
+ function run(a) {
3162
+ if (a.onClick) a.onClick();
3163
+ if (a.dismiss !== false) toast.dismiss(t.id);
3164
+ }
3117
3165
  return /* @__PURE__ */ React19.createElement(
3118
3166
  "div",
3119
3167
  {
3120
- className: cx("aura-toast", "aura-toast--" + t.tone, t.loading && "is-loading"),
3168
+ ref: node,
3169
+ onKeyDown: function(e) {
3170
+ if (e.key === "Escape") {
3171
+ e.stopPropagation();
3172
+ toast.dismiss(t.id);
3173
+ }
3174
+ },
3175
+ className: cx(
3176
+ "aura-toast",
3177
+ "aura-toast--" + t.tone,
3178
+ t.loading && "is-loading",
3179
+ /* Two actions, or one beside a description, go on their own row under the text so it keeps its width. */
3180
+ (actions.length > 1 || actions.length && rich) && "has-action-row"
3181
+ ),
3121
3182
  role: t.tone === "danger" ? "alert" : "status",
3122
3183
  "aria-busy": t.loading || void 0,
3123
- onMouseEnter: pause,
3124
- onMouseLeave: resume,
3125
- onFocus: pause,
3126
- onBlur: resume
3184
+ onMouseEnter: function() {
3185
+ hover.current = true;
3186
+ pause();
3187
+ },
3188
+ onMouseLeave: function() {
3189
+ hover.current = false;
3190
+ resume();
3191
+ },
3192
+ onFocus: function() {
3193
+ focused.current = true;
3194
+ pause();
3195
+ },
3196
+ onBlur: function(e) {
3197
+ if (node.current && e.relatedTarget && node.current.contains(e.relatedTarget)) return;
3198
+ focused.current = false;
3199
+ resume();
3200
+ }
3127
3201
  },
3128
3202
  /* @__PURE__ */ React19.createElement(
3129
3203
  Icon,
@@ -3132,19 +3206,33 @@ window.Aura = (() => {
3132
3206
  className: cx("aura-toast__icon", t.loading && "aura-spin")
3133
3207
  }
3134
3208
  ),
3135
- /* @__PURE__ */ React19.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React19.createElement("p", { className: "aura-toast__title" }, t.title), t.description ? /* @__PURE__ */ React19.createElement("p", { className: "aura-toast__text" }, t.description) : null),
3136
- t.action ? /* @__PURE__ */ React19.createElement(
3137
- "button",
3138
- {
3139
- type: "button",
3140
- className: "aura-toast__action",
3141
- onClick: function() {
3142
- t.action.onClick && t.action.onClick();
3143
- toast.dismiss(t.id);
3144
- }
3145
- },
3146
- t.action.label
3147
- ) : null,
3209
+ /* @__PURE__ */ React19.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React19.createElement("p", { className: "aura-toast__title" }, t.title), rich ? /* @__PURE__ */ React19.createElement("div", { className: "aura-toast__text" }, t.description) : null),
3210
+ actions.length ? /* @__PURE__ */ React19.createElement("div", { className: "aura-toast__actions" }, actions.map(function(a, i) {
3211
+ return a.href ? /* @__PURE__ */ React19.createElement(
3212
+ Link,
3213
+ {
3214
+ key: i,
3215
+ href: a.href,
3216
+ className: "aura-toast__action",
3217
+ onClick: function(e) {
3218
+ if (plainClick(e)) run(a);
3219
+ else if (a.onClick) a.onClick();
3220
+ }
3221
+ },
3222
+ a.label
3223
+ ) : /* @__PURE__ */ React19.createElement(
3224
+ "button",
3225
+ {
3226
+ key: i,
3227
+ type: "button",
3228
+ className: "aura-toast__action",
3229
+ onClick: function() {
3230
+ run(a);
3231
+ }
3232
+ },
3233
+ a.label
3234
+ );
3235
+ })) : null,
3148
3236
  /* @__PURE__ */ React19.createElement(
3149
3237
  IconButton,
3150
3238
  {
@@ -3158,7 +3246,22 @@ window.Aura = (() => {
3158
3246
  )
3159
3247
  );
3160
3248
  }
3161
- function Toaster(props) {
3249
+ function hotkeyMatcher(spec) {
3250
+ const parts = spec.split("+").map(function(p) {
3251
+ return p.trim().toLowerCase();
3252
+ });
3253
+ const key = parts[parts.length - 1] || "";
3254
+ const code = /^[a-z]$/.test(key) ? "Key" + key.toUpperCase() : /^[0-9]$/.test(key) ? "Digit" + key : null;
3255
+ const want = function(m) {
3256
+ return parts.indexOf(m) >= 0 && parts.indexOf(m) < parts.length - 1;
3257
+ };
3258
+ return function(e) {
3259
+ if (e.altKey !== want("alt") || e.ctrlKey !== want("ctrl") || e.shiftKey !== want("shift") || e.metaKey !== (want("meta") || want("cmd")))
3260
+ return false;
3261
+ return code ? e.code === code : e.key.toLowerCase() === key;
3262
+ };
3263
+ }
3264
+ function Toaster(props = {}) {
3162
3265
  const t = useStrings();
3163
3266
  const mounted = useMounted();
3164
3267
  const s = React19.useState(toastState.list);
@@ -3171,15 +3274,49 @@ window.Aura = (() => {
3171
3274
  });
3172
3275
  };
3173
3276
  }, []);
3277
+ const region = React19.useRef(null);
3278
+ const hotkey = props.hotkey === void 0 ? "Alt+T" : props.hotkey;
3279
+ React19.useEffect(
3280
+ function() {
3281
+ if (!hotkey) return;
3282
+ const match = hotkeyMatcher(hotkey);
3283
+ function onKey(e) {
3284
+ if (!e.key || e.repeat || e.isComposing || !match(e) || !region.current) return;
3285
+ const el = e.target;
3286
+ const editable = !!el && (el.isContentEditable || /^(INPUT|TEXTAREA)$/.test(el.tagName || ""));
3287
+ const mac = /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent || "");
3288
+ if (editable && mac && e.altKey && !e.ctrlKey && !e.metaKey && e.key.length === 1) return;
3289
+ const items2 = region.current.querySelectorAll(".aura-toast");
3290
+ const last = items2[items2.length - 1];
3291
+ if (!last) return;
3292
+ const target = last.querySelector(".aura-toast__action") || last.querySelector("button");
3293
+ if (!target) return;
3294
+ e.preventDefault();
3295
+ const from = outsideToaster(document.activeElement);
3296
+ if (from) hotkeyOrigin.set(last, from);
3297
+ target.focus();
3298
+ }
3299
+ document.addEventListener("keydown", onKey);
3300
+ return function() {
3301
+ document.removeEventListener("keydown", onKey);
3302
+ };
3303
+ },
3304
+ [hotkey]
3305
+ );
3174
3306
  if (!mounted) return null;
3307
+ const pos = props.position || "bottom";
3308
+ const offset = props.offset;
3175
3309
  return (0, import_react_dom5.createPortal)(
3176
3310
  /* @__PURE__ */ React19.createElement(
3177
3311
  "div",
3178
3312
  {
3179
- className: cx("aura-toaster", props && props.position === "top" && "is-top"),
3313
+ ref: region,
3314
+ className: cx("aura-toaster", pos.indexOf("top") === 0 && "is-top", /center$/.test(pos) && "is-center"),
3315
+ style: offset != null ? { "--aura-toaster-offset": typeof offset === "number" ? offset + "px" : offset } : void 0,
3180
3316
  role: "region",
3181
3317
  "aria-live": "polite",
3182
- "aria-label": t.notifications
3318
+ "aria-label": hotkey ? t.notifications + " (" + hotkey + ")" : t.notifications,
3319
+ "aria-keyshortcuts": hotkey || void 0
3183
3320
  },
3184
3321
  s[0].map(function(t2) {
3185
3322
  return /* @__PURE__ */ React19.createElement(ToastItem, { key: t2.id, toast: t2 });
@@ -3894,6 +4031,7 @@ window.Aura = (() => {
3894
4031
  function close() {
3895
4032
  if (props.dismissible !== false && props.onClose) props.onClose();
3896
4033
  }
4034
+ const scrimCloses = props.dismissible !== false && (props.dismissOnScrim !== void 0 ? props.dismissOnScrim : props.role !== "alertdialog");
3897
4035
  const modal = useModal(props.open, own, {
3898
4036
  autoFocus: props.autoFocus,
3899
4037
  onEscape: close,
@@ -3902,7 +4040,17 @@ window.Aura = (() => {
3902
4040
  });
3903
4041
  if (!modal.ready) return null;
3904
4042
  return (0, import_react_dom8.createPortal)(
3905
- /* @__PURE__ */ React27.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React27.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React27.createElement(
4043
+ /* @__PURE__ */ React27.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React27.createElement(
4044
+ "div",
4045
+ {
4046
+ className: "aura-scrim",
4047
+ onClick: scrimCloses ? close : void 0,
4048
+ onMouseDown: scrimCloses ? void 0 : function(e) {
4049
+ e.preventDefault();
4050
+ },
4051
+ "aria-hidden": true
4052
+ }
4053
+ ), /* @__PURE__ */ React27.createElement(
3906
4054
  "div",
3907
4055
  {
3908
4056
  ref: merged,
@@ -3936,8 +4084,19 @@ window.Aura = (() => {
3936
4084
  });
3937
4085
  if (!modal.ready) return null;
3938
4086
  const side = props.side === "left" ? "left" : "right";
4087
+ const drawerScrimCloses = props.dismissible !== false && props.dismissOnScrim !== false;
3939
4088
  return (0, import_react_dom8.createPortal)(
3940
- /* @__PURE__ */ React27.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React27.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React27.createElement(
4089
+ /* @__PURE__ */ React27.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React27.createElement(
4090
+ "div",
4091
+ {
4092
+ className: "aura-scrim",
4093
+ onClick: drawerScrimCloses ? close : void 0,
4094
+ onMouseDown: !drawerScrimCloses ? function(e) {
4095
+ e.preventDefault();
4096
+ } : void 0,
4097
+ "aria-hidden": true
4098
+ }
4099
+ ), /* @__PURE__ */ React27.createElement(
3941
4100
  "div",
3942
4101
  {
3943
4102
  ref: merged,
@@ -4007,7 +4166,27 @@ window.Aura = (() => {
4007
4166
  );
4008
4167
  const sort = sortState[0], setSort = sortState[1];
4009
4168
  const selState = useMaybeControlled(props.selected, props.defaultSelected || [], props.onSelectionChange);
4010
- const selected = selState[0], setSelected = selState[1];
4169
+ const canSelect = function(r) {
4170
+ return !!r && (!props.isRowSelectable || !!props.isRowSelectable(r));
4171
+ };
4172
+ const rejected = {};
4173
+ if (props.isRowSelectable)
4174
+ rows.forEach(function(r) {
4175
+ if (!canSelect(r)) rejected[String(r[rowKey])] = true;
4176
+ });
4177
+ const selected = selState[0].filter(function(k) {
4178
+ return !rejected[String(k)];
4179
+ }), setSelected = selState[1];
4180
+ const droppedSig = selState[0].length === selected.length ? "" : "#" + selState[0].filter(function(k) {
4181
+ return rejected[String(k)];
4182
+ }).join("\0") + "#" + selected.join("\0");
4183
+ React28.useEffect(
4184
+ function() {
4185
+ if (droppedSig !== "") setSelected(selected);
4186
+ },
4187
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4188
+ [droppedSig]
4189
+ );
4011
4190
  const pageState = useMaybeControlled(
4012
4191
  props.page,
4013
4192
  props.defaultPage || 1,
@@ -4418,7 +4597,7 @@ window.Aura = (() => {
4418
4597
  el.focus();
4419
4598
  }
4420
4599
  }
4421
- const pageKeys = pageRows.map(function(r) {
4600
+ const pageKeys = pageRows.filter(canSelect).map(function(r) {
4422
4601
  return r[rowKey];
4423
4602
  });
4424
4603
  const selSet = {};
@@ -4682,10 +4861,12 @@ window.Aura = (() => {
4682
4861
  if (inner) inner.focus();
4683
4862
  else handled = false;
4684
4863
  } else if (k === " " || k === "Enter") {
4685
- if (r === 0 && !col && selectable && nRows) toggleAll();
4686
- else if (r === 0 && col && col.sortable && canSortAny) sortBy(col.key);
4687
- else if (r > 0 && k === " " && selectable) toggle(row[rowKey], !selSet[row[rowKey]]);
4688
- else if (r > 0 && k === "Enter" && rowHref && !target.querySelector("button, a[href]:not(.aura-table__row-link), input, select, textarea"))
4864
+ if (r === 0 && !col && selectable && nRows) {
4865
+ if (pageKeys.length) toggleAll();
4866
+ } else if (r === 0 && col && col.sortable && canSortAny) sortBy(col.key);
4867
+ else if (r > 0 && k === " " && selectable) {
4868
+ if (canSelect(row)) toggle(row[rowKey], !selSet[row[rowKey]]);
4869
+ } else if (r > 0 && k === "Enter" && rowHref && !target.querySelector("button, a[href]:not(.aura-table__row-link), input, select, textarea"))
4689
4870
  followRow(target.closest('[role="row"]'), e);
4690
4871
  else if (r > 0 && k === "Enter" && target.querySelector("button, a[href], input, select, textarea"))
4691
4872
  target.querySelector("button, a[href], input, select, textarea").focus();
@@ -4714,7 +4895,7 @@ window.Aura = (() => {
4714
4895
  activeState[1]({ r: 0, c: 0 });
4715
4896
  }
4716
4897
  },
4717
- nRows ? /* @__PURE__ */ React28.createElement(
4898
+ pageKeys.length ? /* @__PURE__ */ React28.createElement(
4718
4899
  Checkbox,
4719
4900
  {
4720
4901
  hideLabel: true,
@@ -4725,7 +4906,7 @@ window.Aura = (() => {
4725
4906
  onChange: toggleAll
4726
4907
  }
4727
4908
  ) : null,
4728
- nRows ? /* @__PURE__ */ React28.createElement("span", { className: "aura-table__sel-text" }, nSel ? t.selectedCount(nSel) : t.selectAll) : /* @__PURE__ */ React28.createElement("span", { className: "aura-sr-only" }, t.selectRows)
4909
+ pageKeys.length ? /* @__PURE__ */ React28.createElement("span", { className: "aura-table__sel-text" }, nSel ? t.selectedCount(nSel) : t.selectAll) : /* @__PURE__ */ React28.createElement("span", { className: "aura-sr-only" }, t.selectRows)
4729
4910
  )
4730
4911
  );
4731
4912
  vis.forEach(function(c, i) {
@@ -4863,12 +5044,12 @@ window.Aura = (() => {
4863
5044
  className: cx("aura-table__sel", nPinned && "is-pinned"),
4864
5045
  tabIndex: tabFor(ri, 0),
4865
5046
  "data-rc": ri + ":0",
4866
- "aria-label": t.selectRow(k),
5047
+ "aria-label": canSelect(r) ? t.selectRow(k) : props.rowSelectDisabledLabel ? props.rowSelectDisabledLabel(r) : void 0,
4867
5048
  onFocus: function(e) {
4868
5049
  if (e.target === e.currentTarget) activeState[1]({ r: ri, c: 0 });
4869
5050
  }
4870
5051
  },
4871
- /* @__PURE__ */ React28.createElement(
5052
+ canSelect(r) ? /* @__PURE__ */ React28.createElement(
4872
5053
  Checkbox,
4873
5054
  {
4874
5055
  hideLabel: true,
@@ -4879,7 +5060,7 @@ window.Aura = (() => {
4879
5060
  toggle(k, on);
4880
5061
  }
4881
5062
  }
4882
- )
5063
+ ) : null
4883
5064
  )
4884
5065
  );
4885
5066
  vis.forEach(function(c, j) {
@@ -4982,7 +5163,7 @@ window.Aura = (() => {
4982
5163
  key: k,
4983
5164
  role: "row",
4984
5165
  "aria-rowindex": first + i + 2,
4985
- "aria-selected": selectable ? isSel : void 0,
5166
+ "aria-selected": selectable && canSelect(r) ? isSel : void 0,
4986
5167
  className: cx(
4987
5168
  "aura-table__row",
4988
5169
  isSel && "is-selected",
@@ -5584,7 +5765,10 @@ window.Aura = (() => {
5584
5765
  const items2 = props.items || [];
5585
5766
  return /* @__PURE__ */ React32.createElement("nav", { ref, "aria-label": props.label || t.breadcrumb, className: cx("aura-crumbs", props.className) }, /* @__PURE__ */ React32.createElement("ol", null, items2.map(function(it, i) {
5586
5767
  const last = i === items2.length - 1;
5587
- return /* @__PURE__ */ React32.createElement("li", { key: i }, last ? /* @__PURE__ */ React32.createElement("span", { "aria-current": "page", className: "aura-crumbs__current" }, it.label) : it.href ? /* @__PURE__ */ React32.createElement(Link, { href: it.href, onClick: it.onClick }, it.label) : /* @__PURE__ */ React32.createElement("button", { type: "button", onClick: it.onClick }, it.label), last ? null : /* @__PURE__ */ React32.createElement(Icon, { name: "chevron-right", size: 12, className: "aura-crumbs__sep" }));
5768
+ return /* @__PURE__ */ React32.createElement("li", { key: i }, last ? /* @__PURE__ */ React32.createElement("span", { "aria-current": "page", className: "aura-crumbs__current" }, it.label) : it.href ? /* @__PURE__ */ React32.createElement(Link, { href: it.href, onClick: it.onClick }, it.label) : it.onClick ? /* @__PURE__ */ React32.createElement("button", { type: "button", onClick: it.onClick }, it.label) : (
5769
+ /* 5.7: a segment with no page and no action is text, not a button that does nothing. */
5770
+ /* @__PURE__ */ React32.createElement("span", { className: "aura-crumbs__text" }, it.label)
5771
+ ), last ? null : /* @__PURE__ */ React32.createElement(Icon, { name: "chevron-right", size: 12, className: "aura-crumbs__sep" }));
5588
5772
  })));
5589
5773
  });
5590
5774
 
@@ -5779,7 +5963,7 @@ window.Aura = (() => {
5779
5963
  },
5780
5964
  "aria-expanded": open
5781
5965
  }
5782
- ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__bar-content" }, props.header)) : null, /* @__PURE__ */ React38.createElement("main", { className: "aura-shell__content", id: props.mainId || "main" }, props.children), props.bottomNav || null));
5966
+ ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__bar-content" }, props.header)) : null, /* @__PURE__ */ React38.createElement("main", { className: "aura-shell__content", id: props.mainId || "main", tabIndex: -1 }, props.children), props.bottomNav || null));
5783
5967
  });
5784
5968
 
5785
5969
  // src/ActionBar.tsx
@@ -5934,8 +6118,11 @@ window.Aura = (() => {
5934
6118
  /* @__PURE__ */ React42.createElement("ul", { className: "aura-bottomnav__list" }, props.items.map(function(it) {
5935
6119
  const on = active === it.id;
5936
6120
  const count = it.count != null && it.count > 0 ? it.count > 99 ? "99+" : String(it.count) : null;
6121
+ const suffix = count || it.badge && it.badgeLabel ? " (" + (count || it.badgeLabel) + ")" : "";
5937
6122
  const common = {
5938
6123
  className: cx("aura-bottomnav__item", on && "is-active"),
6124
+ /* 5.7: a full name when the label is shortened; the count / badge words still follow. */
6125
+ "aria-label": it.ariaLabel ? it.ariaLabel + suffix : void 0,
5939
6126
  "aria-current": on ? "page" : void 0,
5940
6127
  onClick: function(e) {
5941
6128
  if (!it.href) e.preventDefault();
@@ -5946,7 +6133,7 @@ window.Aura = (() => {
5946
6133
  const inner = [
5947
6134
  /* @__PURE__ */ React42.createElement("span", { key: "i", className: "aura-bottomnav__icon" }, /* @__PURE__ */ React42.createElement(Icon, { name: it.icon, size: "md" }), count ? /* @__PURE__ */ React42.createElement("span", { className: "aura-bottomnav__count", "aria-hidden": "true" }, count) : it.badge ? /* @__PURE__ */ React42.createElement("span", { className: "aura-bottomnav__dot", "aria-hidden": "true" }) : null),
5948
6135
  /* @__PURE__ */ React42.createElement("span", { key: "l", className: "aura-bottomnav__label" }, it.label),
5949
- count || it.badge && it.badgeLabel ? /* @__PURE__ */ React42.createElement("span", { key: "s", className: "aura-sr-only" }, " (" + (count || it.badgeLabel) + ")") : null
6136
+ suffix && !it.ariaLabel ? /* @__PURE__ */ React42.createElement("span", { key: "s", className: "aura-sr-only" }, suffix) : null
5950
6137
  ];
5951
6138
  return /* @__PURE__ */ React42.createElement("li", { key: it.id, className: "aura-bottomnav__cell" }, it.href ? /* @__PURE__ */ React42.createElement(Link, { href: it.href, ...common }, inner) : /* @__PURE__ */ React42.createElement("button", { type: "button", ...common }, inner));
5952
6139
  }))
@@ -247,6 +247,12 @@ a.aura-menu__item { text-decoration: none; box-sizing: border-box; }
247
247
  }
248
248
  .aura-menu__radio.is-on { border-color: var(--aura-button-primary-bg); }
249
249
  .aura-menu__radio.is-on::after { content: ''; width: 8px; height: 8px; border-radius: var(--aura-radius-full); background: var(--aura-button-primary-bg); }
250
+ /* 5.7: a header above the items (who is signed in), set off by a hairline like a separator. */
251
+ .aura-menu__header { margin: calc(-1 * var(--aura-space-1)) calc(-1 * var(--aura-space-1)) var(--aura-space-1); padding: var(--aura-space-3); border-bottom: 1px solid var(--aura-border-default); color: var(--aura-fg-secondary); font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
252
+ .aura-menu__header > * { margin: 0; }
253
+ /* Stays in view when a long menu scrolls. */
254
+ .aura-menu.has-header .aura-menu__header { position: sticky; top: calc(-1 * var(--aura-space-1)); z-index: 1; background: var(--aura-bg-surface); }
255
+ .aura-menu__header strong, .aura-menu__header b { color: var(--aura-fg-primary); font-weight: 500; font-size: 13px; }
250
256
  .aura-menu__sep { height: 1px; margin: var(--aura-space-1) calc(-1 * var(--aura-space-1)); background: var(--aura-border-default); }
251
257
 
252
258
  /* Footer + pager */
@@ -496,7 +502,9 @@ a.aura-icon-btn { text-decoration: none; }
496
502
  display: flex; flex-direction: column; gap: var(--aura-space-2); width: min(380px, calc(100vw - 2 * var(--aura-space-4)));
497
503
  font-family: var(--font-sans);
498
504
  }
499
- .aura-toaster.is-top { bottom: auto; top: var(--aura-space-6); }
505
+ .aura-toaster.is-top { bottom: auto; top: calc(var(--aura-toaster-offset, var(--aura-space-6)) + env(safe-area-inset-top, 0px)); }
506
+ /* 5.6: top-center / bottom-center; `offset` (or --aura-toaster-offset) clears an app's top bar. */
507
+ .aura-toaster.is-center { right: auto; left: 50%; transform: translateX(-50%); }
500
508
  .aura-toast {
501
509
  display: flex; align-items: flex-start; gap: var(--aura-space-3); padding: var(--aura-space-3) var(--aura-space-2) var(--aura-space-3) var(--aura-space-4);
502
510
  background: var(--aura-bg-surface); color: var(--aura-fg-primary);
@@ -511,10 +519,27 @@ a.aura-icon-btn { text-decoration: none; }
511
519
  .aura-toast__body { flex: 1; min-width: 0; padding-top: 1px; }
512
520
  .aura-toast__title { margin: 0; font-size: 14px; font-weight: 500; line-height: 20px; }
513
521
  .aura-toast__text { margin: 2px 0 0; font-size: 13px; line-height: 1.5; color: var(--aura-fg-secondary); }
522
+ .aura-toast__text > * { margin: 0; }
523
+ .aura-toast__text a { color: var(--aura-fg-accent); text-underline-offset: 2px; }
524
+ .aura-toast__text a:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-radius: var(--aura-radius-xs); }
525
+ .aura-toast__actions { flex: none; align-self: center; display: flex; gap: var(--aura-space-1); }
526
+ /* Two actions, or one under a description (5.6): the actions take a row of their own below the text, lined up with it
527
+ * (the action's own padding sits left of the text column), so the text keeps the toast's width. */
528
+ .aura-toast.has-action-row { flex-wrap: wrap; row-gap: var(--aura-space-1); }
529
+ .aura-toast.has-action-row .aura-toast__body { flex-basis: 0; }
530
+ .aura-toast.has-action-row .aura-toast__actions { order: 3; flex-basis: 100%; flex-wrap: wrap; margin-left: calc(16px + var(--aura-space-3) - var(--aura-space-2)); margin-bottom: calc(-1 * var(--aura-space-1)); }
514
531
  .aura-toast__action {
515
532
  flex: none; align-self: center; height: var(--aura-control-sm); padding: 0 var(--aura-space-2);
516
533
  border: 0; border-radius: var(--aura-radius-sm); background: transparent; color: var(--aura-fg-accent);
517
534
  font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
535
+ display: inline-flex; align-items: center; text-decoration: none; white-space: nowrap;
536
+ }
537
+ /* Touch (5.6): the 32px action gets a 44px hit area, as IconButton does. Links in the text only get a little padding —
538
+ * lines sit 20px apart, so a 44px box would overlap the next line's link. */
539
+ @media (pointer: coarse) {
540
+ .aura-toast__action { position: relative; }
541
+ .aura-toast__action::after { content: ''; position: absolute; top: 50%; left: 50%; width: max(100%, var(--aura-touch-target)); height: max(100%, var(--aura-touch-target)); transform: translate(-50%, -50%); }
542
+ .aura-toast__text a { padding-block: 2px; }
518
543
  }
519
544
  .aura-toast__action:hover { background: var(--aura-bg-surface-hover); }
520
545
  .aura-toast__action:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 0; }
@@ -625,6 +650,8 @@ a.aura-tab { text-decoration: none; }
625
650
  .aura-nav__item.is-active { background: var(--aura-bg-selected); color: var(--aura-fg-primary); }
626
651
  .aura-nav__item.is-active .aura-icon { color: var(--aura-fg-accent); }
627
652
  .aura-nav__item:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: -2px; }
653
+ /* Touch (5.7): rows are 44px on coarse pointers (AppShell's phone drawer); desktop keeps 36px. */
654
+ @media (pointer: coarse) { .aura-nav__item { height: auto; min-height: var(--aura-touch-target); } }
628
655
  .aura-nav__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
629
656
  .aura-nav__count { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--aura-fg-secondary); }
630
657
  .aura-nav__badge { display: inline-flex; align-items: center; flex: none; }
@@ -670,6 +697,8 @@ a.aura-tab { text-decoration: none; }
670
697
  .aura-crumbs a:hover, .aura-crumbs button:hover { color: var(--aura-fg-primary); text-decoration: underline; text-underline-offset: 3px; }
671
698
  .aura-crumbs a:focus-visible, .aura-crumbs button:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; }
672
699
  .aura-crumbs__current { color: var(--aura-fg-primary); font-weight: 500; }
700
+ /* 5.7: a segment with no page of its own — the links' colour, not interactive. */
701
+ .aura-crumbs__text { color: var(--aura-fg-secondary); }
673
702
  .aura-crumbs__sep { color: var(--aura-fg-tertiary); }
674
703
 
675
704
  /* ---------- Avatar ---------- */
@@ -865,6 +894,8 @@ a.aura-tab { text-decoration: none; }
865
894
  }
866
895
  .aura-shell__bar-content { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--aura-space-3); }
867
896
  .aura-shell__content { flex: 1; min-width: 0; padding: var(--aura-space-6) var(--aura-space-4); }
897
+ /* 5.7: <main> takes focus (tabIndex -1) for skip links and "the row you acted on is gone"; a landmark gets no ring. */
898
+ .aura-shell__content:focus { outline: none; }
868
899
  @media (min-width: 640px) { .aura-shell__content { padding: var(--aura-space-6); } }
869
900
  @media (min-width: 1024px) { .aura-shell__content { padding: var(--aura-space-8); } }
870
901
  /* The shell already pads its content: a Container directly inside only caps the width. */
@@ -1203,6 +1234,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1203
1234
  .aura-dialog__foot > * { width: 100%; }
1204
1235
  .aura-drawer:not(.aura-drawer--nav) { max-width: none; }
1205
1236
  .aura-toaster { left: var(--aura-space-4); right: var(--aura-space-4); bottom: var(--aura-space-4); width: auto; }
1237
+ .aura-toaster.is-center { left: var(--aura-space-4); right: var(--aura-space-4); transform: none; }
1238
+ .aura-toaster.is-top { top: calc(var(--aura-toaster-offset, var(--aura-space-4)) + env(safe-area-inset-top, 0px)); }
1206
1239
  .aura-card { padding: var(--aura-space-4); }
1207
1240
  .aura-stat { padding: var(--aura-space-4); }
1208
1241
  .aura-stat__value { font-size: 24px; }
@@ -1385,9 +1418,9 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1385
1418
  @media (max-width: 767.98px) { :root:has(.aura-bottomnav--below-md) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1386
1419
  @media (max-width: 1023.98px) { :root:has(.aura-bottomnav--below-lg) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1387
1420
  @media (max-width: 1279.98px) { :root:has(.aura-bottomnav--below-xl) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1388
- /* Toasts clear a BottomNav wherever it shows (phones and tablets). */
1389
- .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-6) + var(--aura-bottomnav-offset, 0px)); }
1390
- @media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-4) + var(--aura-bottomnav-offset, 0px)); } }
1421
+ /* Toasts clear a BottomNav wherever it shows (phones and tablets), else the bottom safe-area inset (5.6). */
1422
+ .aura-toaster:not(.is-top) { bottom: calc(var(--aura-toaster-offset, var(--aura-space-6)) + var(--aura-bottomnav-offset, env(safe-area-inset-bottom, 0px))); }
1423
+ @media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-toaster-offset, var(--aura-space-4)) + var(--aura-bottomnav-offset, env(safe-area-inset-bottom, 0px))); } }
1391
1424
 
1392
1425
  /* ---------- 4.19: ActionBar ---------- */
1393
1426
  /* Sticky, not fixed: it keeps its place at the end of its parent, so it never covers the last field. */
@@ -124,6 +124,11 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
124
124
  onSortChange?: ((sort: DataTableSort | null) => void) | undefined;
125
125
  /** Adds a checkbox column. */
126
126
  selectable?: boolean | undefined;
127
+ /** Which rows can be selected (5.6). A row it rejects shows no checkbox (the cell stays, so columns line up), is
128
+ * skipped by select-all, Space and the count, and never appears in `onSelectionChange` — even if passed in. */
129
+ isRowSelectable?: RowCallback<boolean, Row> | undefined;
130
+ /** Accessible name for a rejected row's empty cell, e.g. `(r) => 'Not awaiting review'` (5.6). */
131
+ rowSelectDisabledLabel?: RowCallback<string, Row> | undefined;
127
132
  /** Controlled selection (row keys); pair with onSelectionChange. */
128
133
  selected?: Array<string | number> | undefined;
129
134
  /** Initial selection when uncontrolled. */
@@ -305,6 +310,9 @@ export interface MenuProps {
305
310
  autoFocus?: boolean | undefined;
306
311
  /** Router link for items with `href`; defaults to AuraProvider's `linkComponent`, then `<a>`. (4.19) */
307
312
  linkComponent?: React$1.ElementType | undefined;
313
+ /** Content above the items (5.7), e.g. who is signed in. Not an item: arrow keys skip it; it is the menu's
314
+ * accessible description, read once when the menu opens. */
315
+ header?: React$1.ReactNode | undefined;
308
316
  }
309
317
  export interface FieldProps {
310
318
  /** Visible label; also the accessible name. */
@@ -487,14 +495,23 @@ export interface AlertProps {
487
495
  onDismiss?: (() => void) | undefined;
488
496
  className?: string | undefined;
489
497
  }
498
+ /** A button (or, with `href`, a link through AuraProvider's `linkComponent`) on a toast (5.6: `href`, `dismiss`). */
499
+ export interface ToastAction {
500
+ label: string;
501
+ onClick?: (() => void) | undefined;
502
+ /** Makes the action a link (routes through `linkComponent`, so `next/link` navigates client-side). */
503
+ href?: string | undefined;
504
+ /** Default true: the toast closes when the action runs. `false` keeps it (a list of items to work through). */
505
+ dismiss?: boolean | undefined;
506
+ }
490
507
  export interface ToastOptions {
491
508
  title: string;
492
- description?: string | undefined;
509
+ /** Text, or (5.6) rich content — lines with their own links, read once inside the toast's live region. */
510
+ description?: React$1.ReactNode | undefined;
493
511
  tone?: FeedbackTone | undefined;
494
- action?: {
495
- label: string;
496
- onClick?: (() => void) | undefined;
497
- } | undefined;
512
+ action?: ToastAction | undefined;
513
+ /** Up to two actions in a row (5.6); used instead of `action` when given. */
514
+ actions?: ToastAction[] | undefined;
498
515
  /** ms before it closes itself. Default 5000; Infinity keeps it until dismissed. */
499
516
  duration?: number | undefined;
500
517
  /** Reuse an id to replace a toast in place (same position, timer restarts) — never a second toast. */
@@ -533,6 +550,9 @@ export interface DialogProps {
533
550
  size?: "sm" | "md" | "lg" | undefined;
534
551
  /** Default true. False hides the close button and ignores Escape and scrim clicks. */
535
552
  dismissible?: boolean | undefined;
553
+ /** Whether a click on the scrim closes it (5.7). Default true, false for `role="alertdialog"`: a stray click
554
+ * outside shouldn't throw away a typed reason. Escape and the close button still close it. */
555
+ dismissOnScrim?: boolean | undefined;
536
556
  /** Use `alertdialog` for destructive confirmations. */
537
557
  role?: "dialog" | "alertdialog" | undefined;
538
558
  /** Default true. Turn off only for static demos. */
@@ -624,6 +644,7 @@ export interface SideNavProps {
624
644
  }
625
645
  export interface BreadcrumbProps {
626
646
  /** Root first; the last item is the current page. */
647
+ /** An item with neither `href` nor `onClick` is plain text (5.7), for a segment with no page of its own. */
627
648
  items: Array<{
628
649
  label: string;
629
650
  href?: string | undefined;
@@ -651,6 +672,9 @@ export interface DropdownMenuProps {
651
672
  label?: string | undefined;
652
673
  /** Router link for items with `href`. (4.19) */
653
674
  linkComponent?: React$1.ElementType | undefined;
675
+ /** Content above the items (5.7), e.g. name, email and role in an account menu. Not an item: arrow keys skip
676
+ * it; it is the menu's accessible description (`aria-describedby`). */
677
+ header?: React$1.ReactNode | undefined;
654
678
  }
655
679
  export interface ComboboxOption {
656
680
  value: string;
@@ -851,6 +875,8 @@ export interface DrawerProps {
851
875
  footer?: React$1.ReactNode | undefined;
852
876
  /** Scrim click and Escape close it. Default true. */
853
877
  dismissible?: boolean | undefined;
878
+ /** Whether a click on the scrim closes it (5.7). Default true; Escape and the close button still close it. */
879
+ dismissOnScrim?: boolean | undefined;
854
880
  /** Default true: focus moves to the element with `data-autofocus`, else the first control in the body. False only for static demos. */
855
881
  autoFocus?: boolean | undefined;
856
882
  /** Needed when there is no title. */
@@ -891,7 +917,8 @@ export interface AppShellProps {
891
917
  children?: React$1.ReactNode | undefined;
892
918
  navLabel?: string | undefined;
893
919
  menuLabel?: string | undefined;
894
- /** id of `<main>`, for a skip link. Default `main`. */
920
+ /** id of `<main>`, for a skip link. Default `main`. `<main>` has `tabIndex={-1}` (5.7), so a skip link or
921
+ * `document.getElementById(mainId).focus()` moves focus there (no ring is drawn on it). */
895
922
  mainId?: string | undefined;
896
923
  /** A BottomNav for phones (4.19). Shown below its `hideFrom` breakpoint; the content keeps room for it, and a
897
924
  * viewport ActionBar sits on top of it. With a `bottomNav` and no `nav`, there is no menu button. */
@@ -965,6 +992,9 @@ export interface BottomNavItem {
965
992
  badge?: boolean | undefined;
966
993
  /** Read after the label when `badge` is set, e.g. "new". */
967
994
  badgeLabel?: string | undefined;
995
+ /** The full name for screen readers when `label` is shortened to fit (5.7), e.g. label "Konto", ariaLabel
996
+ * "Mitt konto". Include the visible label in it (WCAG 2.5.3). The count or badge is still appended. */
997
+ ariaLabel?: string | undefined;
968
998
  }
969
999
  export interface BottomNavProps {
970
1000
  /** 2–5 items. */
@@ -1285,10 +1315,18 @@ export declare namespace toast {
1285
1315
  var dismiss: (id: string) => void;
1286
1316
  }
1287
1317
  export interface ToasterProps {
1288
- /** Default `bottom`. */
1289
- position?: "bottom" | "top" | undefined;
1290
- }
1291
- export declare function Toaster(props: ToasterProps): React$1.ReactElement | null;
1318
+ /** Default `bottom` (bottom right). `top` is top right; `top-center` / `bottom-center` centre the stack (5.6).
1319
+ * Below 640px toasts span the width either way. */
1320
+ position?: "bottom" | "bottom-right" | "bottom-center" | "top" | "top-right" | "top-center" | undefined;
1321
+ /** Distance from the top or bottom edge (5.6), e.g. `64` to clear a 56px top bar; px or any CSS length. Sets
1322
+ * `--aura-toaster-offset`; the safe-area inset is added on top. Default 24px (16px below 640px). */
1323
+ offset?: number | string | undefined;
1324
+ /** Keyboard shortcut to the newest toast (5.6): focuses its action, else its close button, and pauses its timer;
1325
+ * Escape closes it and returns focus. Default `"Alt+T"` (matched on the physical key, so macOS Option+T works);
1326
+ * `false` turns it off. */
1327
+ hotkey?: string | false | undefined;
1328
+ }
1329
+ export declare function Toaster(props?: ToasterProps): React$1.ReactElement | null;
1292
1330
  export declare const PasswordField: React$1.ForwardRefExoticComponent<PasswordFieldProps & React$1.RefAttributes<HTMLInputElement>>;
1293
1331
  export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErrorSummaryProps & React$1.RefAttributes<HTMLDivElement>>;
1294
1332
  export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-tokens",
3
- "version": "5.5.0",
3
+ "version": "5.7.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.5.0",
2
+ "version": "5.7.0",
3
3
  "meta": {
4
4
  "name": "AURA Design System",
5
5
  "tier": "3-tier",