ionbase-ui 0.2.0 → 0.4.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.
Files changed (85) hide show
  1. package/dist/components/Avatar.d.ts +8 -5
  2. package/dist/components/Avatar.d.ts.map +1 -1
  3. package/dist/components/Avatar.js +31 -11
  4. package/dist/components/Avatar.js.map +1 -1
  5. package/dist/components/Button.d.ts +4 -0
  6. package/dist/components/Button.d.ts.map +1 -1
  7. package/dist/components/Button.js +6 -2
  8. package/dist/components/Button.js.map +1 -1
  9. package/dist/components/Checkbox.d.ts +6 -0
  10. package/dist/components/Checkbox.d.ts.map +1 -1
  11. package/dist/components/Checkbox.js +6 -3
  12. package/dist/components/Checkbox.js.map +1 -1
  13. package/dist/components/Input.d.ts +30 -2
  14. package/dist/components/Input.d.ts.map +1 -1
  15. package/dist/components/Input.js +23 -7
  16. package/dist/components/Input.js.map +1 -1
  17. package/dist/components/Menu.d.ts +6 -0
  18. package/dist/components/Menu.d.ts.map +1 -1
  19. package/dist/components/Menu.js +11 -7
  20. package/dist/components/Menu.js.map +1 -1
  21. package/dist/components/NavItem.d.ts +4 -0
  22. package/dist/components/NavItem.d.ts.map +1 -1
  23. package/dist/components/NavItem.js +22 -2
  24. package/dist/components/NavItem.js.map +1 -1
  25. package/dist/components/Radio.d.ts +11 -0
  26. package/dist/components/Radio.d.ts.map +1 -1
  27. package/dist/components/Radio.js +25 -17
  28. package/dist/components/Radio.js.map +1 -1
  29. package/dist/components/ScrollProgress.d.ts.map +1 -1
  30. package/dist/components/ScrollProgress.js +1 -0
  31. package/dist/components/ScrollProgress.js.map +1 -1
  32. package/dist/components/Select.d.ts +11 -2
  33. package/dist/components/Select.d.ts.map +1 -1
  34. package/dist/components/Select.js +6 -3
  35. package/dist/components/Select.js.map +1 -1
  36. package/dist/components/Table.d.ts +26 -1
  37. package/dist/components/Table.d.ts.map +1 -1
  38. package/dist/components/Table.js +30 -7
  39. package/dist/components/Table.js.map +1 -1
  40. package/dist/components/Tabs.d.ts +7 -3
  41. package/dist/components/Tabs.d.ts.map +1 -1
  42. package/dist/components/Tabs.js +13 -2
  43. package/dist/components/Tabs.js.map +1 -1
  44. package/dist/components/Toggle.d.ts +6 -0
  45. package/dist/components/Toggle.d.ts.map +1 -1
  46. package/dist/components/Toggle.js +6 -3
  47. package/dist/components/Toggle.js.map +1 -1
  48. package/dist/components/dom-props.d.ts +59 -0
  49. package/dist/components/dom-props.d.ts.map +1 -1
  50. package/dist/components/dom-props.js +86 -0
  51. package/dist/components/dom-props.js.map +1 -1
  52. package/dist/components/index.d.ts +1 -1
  53. package/dist/components/index.d.ts.map +1 -1
  54. package/dist/components/index.js.map +1 -1
  55. package/dist/components/resolve-disabled.d.ts +9 -0
  56. package/dist/components/resolve-disabled.d.ts.map +1 -0
  57. package/dist/components/resolve-disabled.js +15 -0
  58. package/dist/components/resolve-disabled.js.map +1 -0
  59. package/dist/index.d.ts +5 -6
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +5 -6
  62. package/dist/index.js.map +1 -1
  63. package/dist/styles/button.css +36 -7
  64. package/dist/styles/checkbox.css +2 -2
  65. package/dist/styles/index.css +133 -0
  66. package/dist/styles/input.css +2 -2
  67. package/dist/styles/menu.css +1 -1
  68. package/dist/styles/nav-item.css +2 -2
  69. package/dist/styles/radio.css +2 -2
  70. package/dist/styles/scroll-progress.css +11 -9
  71. package/dist/styles/select.css +2 -2
  72. package/dist/styles/table.css +11 -7
  73. package/dist/styles/tabs.css +15 -6
  74. package/dist/styles/toggle.css +10 -3
  75. package/dist/styles/tokens/base.css +1 -1
  76. package/dist/styles/tokens/elevation.css +8 -8
  77. package/dist/styles/tokens/index.css +1 -0
  78. package/dist/styles/tokens/motion.css +51 -0
  79. package/dist/tokens/index.d.ts +1 -1
  80. package/dist/tokens/index.js +1 -1
  81. package/dist/tokens-js.d.ts +12 -0
  82. package/dist/tokens-js.d.ts.map +1 -0
  83. package/dist/tokens-js.js +12 -0
  84. package/dist/tokens-js.js.map +1 -0
  85. package/package.json +7 -3
@@ -1 +1 @@
1
- {"version":3,"file":"dom-props.d.ts","sourceRoot":"","sources":["../../src/components/dom-props.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,yBAAyB,yLAa2B,CAAC;AAElE;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,SAAS,WAAW,EAAE,EAC1E,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,CAAC,GACN,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAIpB"}
1
+ {"version":3,"file":"dom-props.d.ts","sourceRoot":"","sources":["../../src/components/dom-props.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,yBAAyB,yLAa2B,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,6BAA6B,0PAiBgB,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,2BAA2B,6JAUwB,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,SAAS,WAAW,EAAE,EAC1E,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,CAAC,GACN,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAIpB"}
@@ -53,6 +53,92 @@ export const ARIA_BUTTON_NON_DOM_PROPS = [
53
53
  'target',
54
54
  'rel',
55
55
  ];
56
+ /**
57
+ * The same, for `AriaTextFieldProps` — Input's prop type.
58
+ *
59
+ * Input had the opposite defect to Button's: it destructured ten named props
60
+ * and never built a rest object at all, so nothing outside `AriaTextFieldProps`
61
+ * ever reached the `<input>`. `data-testid` and every other custom attribute
62
+ * vanished with no warning. Spreading the rest fixes that, but only if these
63
+ * come out first.
64
+ *
65
+ * Sources, as of react-aria 3.50:
66
+ * LabelableProps label
67
+ * HelpTextProps description, errorMessage
68
+ * InputBase isDisabled, isReadOnly
69
+ * Validation isRequired, isInvalid, validationState, validationBehavior,
70
+ * validate
71
+ * ValueBase onChange — takes a string, not an event; `useTextField`
72
+ * returns a real DOM handler in its place
73
+ * FocusEvents onFocusChange
74
+ * FocusableDOMProps excludeFromTabOrder
75
+ *
76
+ * Several of these Input already destructures by name; they are listed anyway
77
+ * so this reads as a complete statement about the type rather than a diff
78
+ * against one component's parameter list.
79
+ *
80
+ * `spellCheck` is the odd one out: a real input attribute, but React Aria types
81
+ * it as `string` where React wants `Booleanish`, so forwarding it from the rest
82
+ * spread does not typecheck. It is dropped rather than cast because
83
+ * `useTextField` already returns it — verified: `inputProps` carries the whole
84
+ * of `TextInputDOMProps`, `spellCheck` and `autoCorrect` included.
85
+ *
86
+ * `value` and `defaultValue` are here for a subtler reason than the rest.
87
+ * "`inputProps` is spread afterwards and wins" only holds for the SAME key —
88
+ * and `useTextField` always returns `value`, even when the caller passed
89
+ * `defaultValue`. Forwarding `defaultValue` therefore puts both on the element
90
+ * and React warns that the input is neither controlled nor uncontrolled. This
91
+ * was a real regression, caught by the Input stories, not a precaution.
92
+ *
93
+ * `placeholder`, `name`, `autoComplete` and `id` stay out of the list: they are
94
+ * single-key collisions, so `inputProps` genuinely does win.
95
+ */
96
+ export const ARIA_TEXT_FIELD_NON_DOM_PROPS = [
97
+ 'label',
98
+ 'description',
99
+ 'errorMessage',
100
+ 'isDisabled',
101
+ 'isReadOnly',
102
+ 'isRequired',
103
+ 'isInvalid',
104
+ 'validationState',
105
+ 'validationBehavior',
106
+ 'validate',
107
+ 'onChange',
108
+ 'onFocusChange',
109
+ 'excludeFromTabOrder',
110
+ 'spellCheck',
111
+ 'value',
112
+ 'defaultValue',
113
+ ];
114
+ /**
115
+ * The same, for `AriaTabListProps` — Tabs' prop type.
116
+ *
117
+ * Tabs previously forwarded its leftover props only into the Aria/Stately
118
+ * hooks, so HTML attributes like `data-testid` never reached the root. The
119
+ * list below is what must stay off the DOM when the rest is spread onto the
120
+ * outer element; the hooks still see the full object.
121
+ *
122
+ * Sources, as of react-aria / react-stately 3.50:
123
+ * CollectionBase children, items, disabledKeys
124
+ * TabListProps isDisabled, selectedKey, defaultSelectedKey,
125
+ * onSelectionChange
126
+ * AriaTabListProps keyboardActivation, orientation
127
+ *
128
+ * `id`, `aria-*` and other `DOMProps` / `AriaLabelingProps` members are valid
129
+ * on the root and are deliberately absent.
130
+ */
131
+ export const ARIA_TAB_LIST_NON_DOM_PROPS = [
132
+ 'children',
133
+ 'items',
134
+ 'disabledKeys',
135
+ 'isDisabled',
136
+ 'selectedKey',
137
+ 'defaultSelectedKey',
138
+ 'onSelectionChange',
139
+ 'keyboardActivation',
140
+ 'orientation',
141
+ ];
56
142
  /**
57
143
  * Returns `props` without `keys`, leaving `props` untouched.
58
144
  *
@@ -1 +1 @@
1
- {"version":3,"file":"dom-props.js","sourceRoot":"","sources":["../../src/components/dom-props.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,SAAS;IACT,cAAc;IACd,YAAY;IACZ,eAAe;IACf,WAAW;IACX,eAAe;IACf,qBAAqB;IACrB,qBAAqB;IACrB,aAAa;IACb,MAAM;IACN,QAAQ;IACR,KAAK;CAC0D,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CACvB,KAAQ,EACR,IAAO;IAEP,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,EAAkC,CAAC;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3C,OAAO,MAA4B,CAAC;AACtC,CAAC"}
1
+ {"version":3,"file":"dom-props.js","sourceRoot":"","sources":["../../src/components/dom-props.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,SAAS;IACT,cAAc;IACd,YAAY;IACZ,eAAe;IACf,WAAW;IACX,eAAe;IACf,qBAAqB;IACrB,qBAAqB;IACrB,aAAa;IACb,MAAM;IACN,QAAQ;IACR,KAAK;CAC0D,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,OAAO;IACP,aAAa;IACb,cAAc;IACd,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,WAAW;IACX,iBAAiB;IACjB,oBAAoB;IACpB,UAAU;IACV,UAAU;IACV,eAAe;IACf,qBAAqB;IACrB,YAAY;IACZ,OAAO;IACP,cAAc;CAC0C,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,UAAU;IACV,OAAO;IACP,cAAc;IACd,YAAY;IACZ,aAAa;IACb,oBAAoB;IACpB,mBAAmB;IACnB,oBAAoB;IACpB,aAAa;CACiD,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CACvB,KAAQ,EACR,IAAO;IAEP,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,EAAkC,CAAC;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3C,OAAO,MAA4B,CAAC;AACtC,CAAC"}
@@ -27,7 +27,7 @@ export type { DividerProps, DividerOrientation } from './Divider.js';
27
27
  export { NavItem } from './NavItem.js';
28
28
  export type { NavItemProps } from './NavItem.js';
29
29
  export { Table, TableHead, TableBody, TableRow, TableCell } from './Table.js';
30
- export type { TableProps, TableDensity, TableRowProps, TableCellProps, TableCellAlign, } from './Table.js';
30
+ export type { TableProps, TableDensity, TableRowProps, TableRowSelection, TableCellProps, TableCellAlign, TableCellScope, } from './Table.js';
31
31
  export { ScrollProgress } from './ScrollProgress.js';
32
32
  export type { ScrollProgressProps, ScrollProgressSection, } from './ScrollProgress.js';
33
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EACV,UAAU,EACV,eAAe,EACf,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EACV,SAAS,EACT,aAAa,EACb,QAAQ,EACR,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EACV,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EACV,UAAU,EACV,eAAe,EACf,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EACV,SAAS,EACT,aAAa,EACb,QAAQ,EACR,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EACV,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAMzC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAO/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAO3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ9E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAMzC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAO/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAO3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAU9E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Disabled-prop resolution for the library-wide `isDisabled` convention.
3
+ *
4
+ * Canonical name is React Aria's `isDisabled`. Native `disabled` remains
5
+ * accepted as a deprecated alias for one minor so callers are not broken on
6
+ * the rename. When both are passed, `isDisabled` wins.
7
+ */
8
+ export declare function resolveDisabled(isDisabled?: boolean, disabled?: boolean): boolean | undefined;
9
+ //# sourceMappingURL=resolve-disabled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-disabled.d.ts","sourceRoot":"","sources":["../../src/components/resolve-disabled.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,UAAU,CAAC,EAAE,OAAO,EACpB,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,GAAG,SAAS,CAIrB"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Disabled-prop resolution for the library-wide `isDisabled` convention.
3
+ *
4
+ * Canonical name is React Aria's `isDisabled`. Native `disabled` remains
5
+ * accepted as a deprecated alias for one minor so callers are not broken on
6
+ * the rename. When both are passed, `isDisabled` wins.
7
+ */
8
+ export function resolveDisabled(isDisabled, disabled) {
9
+ if (isDisabled !== undefined)
10
+ return isDisabled;
11
+ if (disabled !== undefined)
12
+ return disabled;
13
+ return undefined;
14
+ }
15
+ //# sourceMappingURL=resolve-disabled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-disabled.js","sourceRoot":"","sources":["../../src/components/resolve-disabled.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAoB,EACpB,QAAkB;IAElB,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAChD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -9,17 +9,16 @@
9
9
  *
10
10
  * import 'ionbase-ui/styles'; // tokens + components, one import
11
11
  * import { Button, Icon } from 'ionbase-ui';
12
+ * import { semantic } from 'ionbase-ui/tokens-js'; // JS token values
12
13
  *
13
14
  * Icons are not bundled. `Icon` takes the icon as a prop, so any SVG component
14
15
  * works — lucide-react, heroicons, react-icons, your own. See ./Icon.tsx.
16
+ *
17
+ * JS token values used to re-export from this barrel as `tokens`. That pulled
18
+ * ~48KB into any consumer whose bundler does not tree-shake a namespace
19
+ * re-export. They live at `ionbase-ui/tokens-js` now.
15
20
  */
16
21
  export * from './components/index.js';
17
22
  export { Icon } from './Icon.js';
18
23
  export type { IconProps, IconSize } from './Icon.js';
19
- /**
20
- * Generated token values, as `var(--…)` references. Copied in from the token
21
- * pipeline at build time by scripts/sync-tokens.mjs — never hand-edited, and
22
- * git-ignored, because Figma owns these names and values.
23
- */
24
- export * as tokens from './tokens/index.js';
25
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErD;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -9,16 +9,15 @@
9
9
  *
10
10
  * import 'ionbase-ui/styles'; // tokens + components, one import
11
11
  * import { Button, Icon } from 'ionbase-ui';
12
+ * import { semantic } from 'ionbase-ui/tokens-js'; // JS token values
12
13
  *
13
14
  * Icons are not bundled. `Icon` takes the icon as a prop, so any SVG component
14
15
  * works — lucide-react, heroicons, react-icons, your own. See ./Icon.tsx.
16
+ *
17
+ * JS token values used to re-export from this barrel as `tokens`. That pulled
18
+ * ~48KB into any consumer whose bundler does not tree-shake a namespace
19
+ * re-export. They live at `ionbase-ui/tokens-js` now.
15
20
  */
16
21
  export * from './components/index.js';
17
22
  export { Icon } from './Icon.js';
18
- /**
19
- * Generated token values, as `var(--…)` references. Copied in from the token
20
- * pipeline at build time by scripts/sync-tokens.mjs — never hand-edited, and
21
- * git-ignored, because Figma owns these names and values.
22
- */
23
- export * as tokens from './tokens/index.js';
24
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
@@ -89,10 +89,34 @@
89
89
  border-style: solid;
90
90
  border-width: var(--border-width-default);
91
91
  border-radius: var(--radius-md);
92
+
93
+ /*
94
+ * `base` + `ease-out`, from the shared motion ladder in motion.css.
95
+ *
96
+ * This was `150ms cubic-bezier(0.4, 0, 0.2, 1)`, hardcoded here and in eleven
97
+ * other stylesheets. Both halves were wrong for a state change: 150ms is
98
+ * short enough to read as a jump rather than a change, and that curve is
99
+ * symmetric — its slow tail means a colour appears to arrive late and then
100
+ * snap. `ease-out` leaves immediately and decelerates in, so the same change
101
+ * reads as settling.
102
+ */
92
103
  transition:
93
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
94
- border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
95
- box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1);
104
+ background-color var(--ion-duration-base) var(--ion-ease-out),
105
+ border-color var(--ion-duration-base) var(--ion-ease-out),
106
+ box-shadow var(--ion-duration-base) var(--ion-ease-out);
107
+ }
108
+
109
+ /*
110
+ * Press drops to `fast`.
111
+ *
112
+ * A press must resolve while the pointer is still down or the feedback reads as
113
+ * lag rather than as a press — it is the one interaction the user is actively
114
+ * waiting on. Release still returns at `base`, because the rule only overrides
115
+ * the duration while the pressed state holds.
116
+ */
117
+ .ion-button:active,
118
+ .ion-button[data-pressed='true'] {
119
+ transition-duration: var(--ion-duration-fast);
96
120
  }
97
121
 
98
122
  /* Size modifiers — radius, border-width and font-weight are size-invariant. */
@@ -204,12 +228,12 @@
204
228
 
205
229
  @media (hover: hover) {
206
230
  .ion-button--primary-neutral:hover {
207
- background-color: var(--surface-inverse);
231
+ background-color: var(--surface-inverse-subtle);
208
232
  }
209
233
  }
210
234
 
211
235
  .ion-button--primary-neutral[data-hovered='true'] {
212
- background-color: var(--surface-inverse);
236
+ background-color: var(--surface-inverse-subtle);
213
237
  }
214
238
 
215
239
  .ion-button--primary-neutral:active,
@@ -229,16 +253,20 @@
229
253
  border-color: var(--border-strong);
230
254
  }
231
255
 
256
+ /*
257
+ * The border does NOT move on hover — Figma binds `border/strong` on Default,
258
+ * Hover and Focus alike, and only steps to `border/stronger` on Pressed. The
259
+ * hover step was previously reproduced here and is a difference the current
260
+ * design does not draw.
261
+ */
232
262
  @media (hover: hover) {
233
263
  .ion-button--secondary:hover {
234
264
  background-color: var(--surface-page);
235
- border-color: var(--border-stronger);
236
265
  }
237
266
  }
238
267
 
239
268
  .ion-button--secondary[data-hovered='true'] {
240
269
  background-color: var(--surface-page);
241
- border-color: var(--border-stronger);
242
270
  }
243
271
 
244
272
  .ion-button--secondary:active,
@@ -246,6 +274,7 @@
246
274
  --ion-button-elevation: var(--ion-button-pressed);
247
275
 
248
276
  background-color: var(--surface-muted);
277
+ border-color: var(--border-stronger);
249
278
  }
250
279
 
251
280
  /* Tertiary Variant — intentionally flat; a ghost button has no surface to raise. */
@@ -124,8 +124,8 @@
124
124
  (var(--ion-checkbox-line-height) - var(--ion-checkbox-size)) / 2
125
125
  );
126
126
  transition:
127
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
128
- border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
127
+ background-color var(--ion-duration-base) var(--ion-ease-out),
128
+ border-color var(--ion-duration-base) var(--ion-ease-out);
129
129
  }
130
130
 
131
131
  .ion-checkbox__mark {
@@ -62,3 +62,136 @@ body {
62
62
  -webkit-font-smoothing: antialiased;
63
63
  -moz-osx-font-smoothing: grayscale;
64
64
  }
65
+
66
+ /*
67
+ * Reduced motion — WCAG 2.3.3.
68
+ *
69
+ * Ten component stylesheets carry transitions; none of them individually
70
+ * gated on the query. One global block covers every `ion-*` rule rather than
71
+ * duplicating the media query ten times. `0.01ms` rather than `0` so the
72
+ * browser still fires `transitionend` / `animationend` for anything that
73
+ * listens.
74
+ */
75
+ @media (prefers-reduced-motion: reduce) {
76
+ [class^='ion-'],
77
+ [class*=' ion-'],
78
+ [class^='ion-'] *,
79
+ [class*=' ion-'] * {
80
+ animation-duration: 0.01ms !important;
81
+ animation-iteration-count: 1 !important;
82
+ transition-duration: 0.01ms !important;
83
+ scroll-behavior: auto !important;
84
+ }
85
+ }
86
+
87
+ /*
88
+ * Forced colours — Windows High Contrast / WCAG 1.4.11.
89
+ *
90
+ * Components that convey state with background or border tokens flatten to
91
+ * unreadable blocks when the OS remaps colour. Re-express those states with
92
+ * system colour keywords so selected, checked, focused and disabled remain
93
+ * distinguishable. `forced-color-adjust: none` only where the system would
94
+ * otherwise erase a fill the mark depends on (checked boxes, selected pills).
95
+ */
96
+ @media (forced-colors: active) {
97
+ .ion-button:focus-visible,
98
+ .ion-button[data-focused='true'],
99
+ .ion-nav-item:focus-visible,
100
+ .ion-nav-item[data-focused='true'],
101
+ .ion-tabs__item:focus-visible,
102
+ .ion-tabs__item[data-focused='true'],
103
+ .ion-tabs__panel:focus-visible,
104
+ .ion-menu__item:focus-visible,
105
+ .ion-input:focus-within,
106
+ .ion-select:focus-within,
107
+ .ion-scroll-progress__trigger:focus-visible,
108
+ .ion-scroll-progress__heading:focus-visible,
109
+ .ion-table-container:focus-visible {
110
+ outline: var(--border-width-thick) solid Highlight;
111
+ outline-offset: 2px;
112
+ }
113
+
114
+ .ion-checkbox__input:focus-visible + .ion-checkbox__indicator,
115
+ .ion-checkbox[data-focused='true'] .ion-checkbox__indicator,
116
+ .ion-radio__input:focus-visible + .ion-radio__indicator,
117
+ .ion-radio[data-focused='true'] .ion-radio__indicator,
118
+ .ion-toggle__input:focus-visible + .ion-toggle__track,
119
+ .ion-toggle[data-focused='true'] .ion-toggle__track {
120
+ outline: var(--border-width-thick) solid Highlight;
121
+ outline-offset: 2px;
122
+ }
123
+
124
+ /* Checked / selected before disabled so specificity reads non-decreasing. */
125
+ .ion-checkbox__input:checked + .ion-checkbox__indicator,
126
+ .ion-checkbox__input:indeterminate + .ion-checkbox__indicator,
127
+ .ion-radio__input:checked + .ion-radio__indicator,
128
+ .ion-toggle__input:checked + .ion-toggle__track {
129
+ forced-color-adjust: none;
130
+ background-color: Highlight;
131
+ border-color: Highlight;
132
+ }
133
+
134
+ .ion-checkbox__input:checked + .ion-checkbox__indicator .ion-checkbox__mark,
135
+ .ion-checkbox__input:indeterminate
136
+ + .ion-checkbox__indicator
137
+ .ion-checkbox__mark {
138
+ forced-color-adjust: none;
139
+ color: HighlightText;
140
+ }
141
+
142
+ .ion-radio__input:checked + .ion-radio__indicator .ion-radio__dot {
143
+ forced-color-adjust: none;
144
+ background-color: HighlightText;
145
+ }
146
+
147
+ .ion-toggle__input:checked + .ion-toggle__track .ion-toggle__thumb {
148
+ forced-color-adjust: none;
149
+ background-color: HighlightText;
150
+ }
151
+
152
+ .ion-table tbody tr[data-selected='true'] {
153
+ forced-color-adjust: none;
154
+ background-color: Highlight;
155
+ color: HighlightText;
156
+ }
157
+
158
+ .ion-tabs__item--underline[data-selected='true'] {
159
+ border-bottom-color: Highlight;
160
+ }
161
+
162
+ .ion-tabs__item--pill[data-selected='true'] {
163
+ forced-color-adjust: none;
164
+ background-color: Highlight;
165
+ color: HighlightText;
166
+ }
167
+
168
+ .ion-button:disabled,
169
+ .ion-button[data-disabled='true'],
170
+ .ion-nav-item--disabled,
171
+ .ion-nav-item[data-disabled='true'],
172
+ .ion-tabs__item[data-disabled='true'],
173
+ .ion-menu__item:disabled,
174
+ .ion-menu__item[data-disabled='true'],
175
+ .ion-input[data-disabled='true'],
176
+ .ion-select[data-disabled='true'],
177
+ .ion-checkbox--disabled,
178
+ .ion-checkbox[data-disabled='true'],
179
+ .ion-radio--disabled,
180
+ .ion-radio[data-disabled='true'],
181
+ .ion-toggle--disabled,
182
+ .ion-toggle[data-disabled='true'] {
183
+ color: GrayText;
184
+ border-color: GrayText;
185
+ }
186
+
187
+ .ion-checkbox__input:disabled + .ion-checkbox__indicator,
188
+ .ion-checkbox__input:checked:disabled + .ion-checkbox__indicator,
189
+ .ion-checkbox__input:indeterminate:disabled + .ion-checkbox__indicator,
190
+ .ion-radio__input:disabled + .ion-radio__indicator,
191
+ .ion-radio__input:checked:disabled + .ion-radio__indicator,
192
+ .ion-toggle__input:disabled + .ion-toggle__track,
193
+ .ion-toggle__input:checked:disabled + .ion-toggle__track {
194
+ border-color: GrayText;
195
+ background-color: Canvas;
196
+ }
197
+ }
@@ -63,8 +63,8 @@
63
63
  font-size: var(--ion-input-font-size);
64
64
  line-height: var(--ion-input-line-height);
65
65
  transition:
66
- border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
67
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
66
+ border-color var(--ion-duration-base) var(--ion-ease-out),
67
+ background-color var(--ion-duration-base) var(--ion-ease-out);
68
68
  }
69
69
 
70
70
  /*
@@ -54,7 +54,7 @@
54
54
  width: 100%;
55
55
  cursor: pointer;
56
56
  user-select: none;
57
- transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
57
+ transition: background-color var(--ion-duration-base) var(--ion-ease-out);
58
58
  }
59
59
 
60
60
  .ion-menu__icon,
@@ -37,7 +37,7 @@
37
37
  text-decoration: none;
38
38
  cursor: pointer;
39
39
  user-select: none;
40
- transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
40
+ transition: color var(--ion-duration-base) var(--ion-ease-out);
41
41
  }
42
42
 
43
43
  .ion-nav-item__icon,
@@ -47,7 +47,7 @@
47
47
  justify-content: center;
48
48
  flex-shrink: 0;
49
49
  color: var(--icon-tertiary);
50
- transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
50
+ transition: color var(--ion-duration-base) var(--ion-ease-out);
51
51
  }
52
52
 
53
53
  .ion-nav-item__icon svg,
@@ -96,8 +96,8 @@
96
96
  /* Centred on the label's first line — see checkbox.css. */
97
97
  margin-top: calc((var(--ion-radio-line-height) - var(--ion-radio-size)) / 2);
98
98
  transition:
99
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
100
- border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
99
+ background-color var(--ion-duration-base) var(--ion-ease-out),
100
+ border-color var(--ion-duration-base) var(--ion-ease-out);
101
101
  }
102
102
 
103
103
  .ion-radio__dot {
@@ -29,8 +29,10 @@
29
29
  *
30
30
  * REVEAL TRANSITION IS DELIBERATELY PLAIN
31
31
  *
32
- * Opacity and an 8px slide, the same 150ms/cubic-bezier(0.4,0,0.2,1) language
33
- * every hover state in the system already uses. The deeper choreography this
32
+ * Opacity and an 8px slide on the shared motion ladder — `--ion-duration-base`
33
+ * and `--ion-ease-out`, the same language every hover state in the system
34
+ * already uses. It reads as a reveal rather than travel, so it stays on `base`
35
+ * rather than taking the `slow` rung. The deeper choreography this
34
36
  * component could eventually justify — a staggered reveal per row, easing
35
37
  * that matches the rail's own scale — is future work, tracked in
36
38
  * docs/motion-system.md, not invented here ahead of that decision.
@@ -62,7 +64,7 @@
62
64
  font-family: var(--font-family-sans);
63
65
  font-size: var(--type-body-sm);
64
66
  line-height: var(--type-body-sm-line-height);
65
- transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
67
+ transition: color var(--ion-duration-base) var(--ion-ease-out);
66
68
  }
67
69
 
68
70
  .ion-scroll-progress__rail {
@@ -76,7 +78,7 @@
76
78
  width: var(--spacing-32);
77
79
  height: var(--border-width-thick);
78
80
  background-color: var(--border-strong);
79
- transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
81
+ transition: background-color var(--ion-duration-base) var(--ion-ease-out);
80
82
  }
81
83
 
82
84
  .ion-scroll-progress__tick--active {
@@ -118,9 +120,9 @@
118
120
  pointer-events: none;
119
121
  transform: translateY(-50%) translateX(-4px);
120
122
  transition:
121
- opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
122
- transform 150ms cubic-bezier(0.4, 0, 0.2, 1),
123
- visibility 150ms;
123
+ opacity var(--ion-duration-base) var(--ion-ease-out),
124
+ transform var(--ion-duration-base) var(--ion-ease-out),
125
+ visibility var(--ion-duration-base);
124
126
  }
125
127
 
126
128
  .ion-scroll-progress__panel[data-open='true'] {
@@ -145,8 +147,8 @@
145
147
  text-align: left;
146
148
  cursor: pointer;
147
149
  transition:
148
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
149
- border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
150
+ background-color var(--ion-duration-base) var(--ion-ease-out),
151
+ border-color var(--ion-duration-base) var(--ion-ease-out);
150
152
  }
151
153
 
152
154
  @media (hover: hover) {
@@ -47,8 +47,8 @@
47
47
  font-size: var(--ion-select-font-size);
48
48
  line-height: var(--ion-select-line-height);
49
49
  transition:
50
- border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
51
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
50
+ border-color var(--ion-duration-base) var(--ion-ease-out),
51
+ background-color var(--ion-duration-base) var(--ion-ease-out);
52
52
  }
53
53
 
54
54
  /* Small shares Medium's padding — see input.css. */
@@ -37,6 +37,12 @@
37
37
  border-radius: var(--radius-md);
38
38
  }
39
39
 
40
+ /* Keyboard focus ring for the scroll region — see Table.tsx. */
41
+ .ion-table-container:focus-visible {
42
+ outline: var(--border-width-thick) solid var(--border-focus);
43
+ outline-offset: 2px;
44
+ }
45
+
40
46
  .ion-table {
41
47
  width: 100%;
42
48
  border-collapse: collapse;
@@ -83,12 +89,10 @@
83
89
  * inside `<thead>`.
84
90
  *
85
91
  * The `<th>` half is the Figma binding (see the header). The `thead` half
86
- * exists because a header row can legitimately contain a cell that is not a
87
- * column header — a selection-checkbox column has nothing to label, and an
88
- * empty `<th>` is an accessibility failure rather than a neutral one: axe's
89
- * `empty-table-header` flags it, and `aria-label` does not rescue it, since the
90
- * rule is specifically about text a sighted screen-reader user can also see.
91
- * Such a cell should be a `<td>`, which then still needs to look like a header.
92
+ * exists for cells that sit in the header row but are not column headers —
93
+ * rare, and mostly historical. Selection's select-all cell is a real `<th
94
+ * scope="col">` with a labelled checkbox, so it gets the fill from the `<th>`
95
+ * rule rather than this fallback.
92
96
  *
93
97
  * Declared after `.ion-table td` so specificity reads non-decreasing; the two
94
98
  * `td` selectors would otherwise trip `no-descending-specificity`.
@@ -103,7 +107,7 @@
103
107
 
104
108
  .ion-table tr {
105
109
  background-color: var(--surface-default);
106
- transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
110
+ transition: background-color var(--ion-duration-base) var(--ion-ease-out);
107
111
  }
108
112
 
109
113
  .ion-table tbody tr {