@zeyos/zx 4.4.0 → 5.2.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 (252) hide show
  1. package/CHANGELOG.md +447 -0
  2. package/README.md +44 -11
  3. package/dist/types/components/account-menu/account-menu.d.ts +8 -0
  4. package/dist/types/components/activity-list/activity-list.d.ts +31 -1
  5. package/dist/types/components/app-icon/app-icon.d.ts +14 -2
  6. package/dist/types/components/app-sidebar/app-sidebar.d.ts +293 -17
  7. package/dist/types/components/avatar/avatar.d.ts +46 -7
  8. package/dist/types/components/badge/badge.d.ts +21 -1
  9. package/dist/types/components/button/button.d.ts +15 -0
  10. package/dist/types/components/calendar/calendar.d.ts +67 -5
  11. package/dist/types/components/card-view/card-view.d.ts +7 -4
  12. package/dist/types/components/card-view/record-card.d.ts +11 -0
  13. package/dist/types/components/chart/chart.d.ts +10 -0
  14. package/dist/types/components/checklist/checklist.d.ts +9 -0
  15. package/dist/types/components/code-editor/code-editor.d.ts +6 -17
  16. package/dist/types/components/context-menu/context-menu.d.ts +6 -2
  17. package/dist/types/components/data-filter/data-filter.d.ts +4 -4
  18. package/dist/types/components/datebox/date-range-box.d.ts +5 -1
  19. package/dist/types/components/datebox/datebox.d.ts +7 -2
  20. package/dist/types/components/dialog/dialog.d.ts +46 -6
  21. package/dist/types/components/dropdown/dropdown.d.ts +24 -9
  22. package/dist/types/components/entity-ref/entity-ref.d.ts +2 -2
  23. package/dist/types/components/field/field.d.ts +54 -2
  24. package/dist/types/components/fieldset/external-adapter.d.ts +83 -0
  25. package/dist/types/components/fieldset/fieldset.d.ts +26 -1
  26. package/dist/types/components/file-item/file-item.d.ts +34 -6
  27. package/dist/types/components/file-list/file-list.d.ts +35 -3
  28. package/dist/types/components/filter/filter.d.ts +127 -3
  29. package/dist/types/components/filter-panel/filter-panel.d.ts +36 -7
  30. package/dist/types/components/form/form.d.ts +12 -1
  31. package/dist/types/components/kanban-view/kanban-card.d.ts +8 -0
  32. package/dist/types/components/launcher/launcher.d.ts +268 -15
  33. package/dist/types/components/list-toolbar/list-toolbar.d.ts +16 -7
  34. package/dist/types/components/menu-button/menu-button.d.ts +39 -5
  35. package/dist/types/components/message/message.d.ts +132 -17
  36. package/dist/types/components/modal/modal.d.ts +43 -2
  37. package/dist/types/components/module-bar/module-bar.d.ts +395 -0
  38. package/dist/types/components/pagination/pagination.d.ts +17 -3
  39. package/dist/types/components/select/color.d.ts +12 -0
  40. package/dist/types/components/select/glyphs.d.ts +124 -0
  41. package/dist/types/components/select/select.d.ts +17 -3
  42. package/dist/types/components/sheet/sheet.d.ts +15 -0
  43. package/dist/types/components/stat-tile/stat-tile.d.ts +16 -5
  44. package/dist/types/components/tabbox/tabbox.d.ts +9 -0
  45. package/dist/types/components/table/table.d.ts +311 -18
  46. package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
  47. package/dist/types/components/tree/tree.d.ts +13 -0
  48. package/dist/types/components/view/record-view.d.ts +59 -2
  49. package/dist/types/components/view-settings/view-settings.d.ts +879 -0
  50. package/dist/types/components/workspace-switcher/workspace-switcher.d.ts +327 -0
  51. package/dist/types/core/breakpoint.d.ts +1 -1
  52. package/dist/types/core/component.d.ts +12 -2
  53. package/dist/types/core/i18n.d.ts +27 -3
  54. package/dist/types/core/icons.d.ts +23 -25
  55. package/dist/types/core/keyboard.d.ts +98 -1
  56. package/dist/types/core/messages.d.ts +382 -0
  57. package/dist/types/core/motion.d.ts +59 -0
  58. package/dist/types/core/overlay-host.d.ts +42 -1
  59. package/dist/types/core/overlay-stack.d.ts +260 -0
  60. package/dist/types/index.d.ts +10 -5
  61. package/dist/types/internal/app-rail.d.ts +38 -0
  62. package/dist/types/internal/menu-items.d.ts +71 -3
  63. package/dist/types/zeyos/connect.d.ts +6 -0
  64. package/dist/types/zeyos/form.d.ts +50 -9
  65. package/dist/types/zeyos/index.d.ts +2 -1
  66. package/dist/types/zeyos/launcher.d.ts +9 -0
  67. package/dist/types/zeyos/module-bar.d.ts +176 -0
  68. package/dist/types/zeyos/query.d.ts +110 -5
  69. package/dist/types/zeyos/schema.d.ts +24 -1
  70. package/dist/types/zeyos/table.d.ts +345 -22
  71. package/dist/types/zeyos/view.d.ts +26 -2
  72. package/dist/zx-zeyos.esm.js +6178 -1008
  73. package/dist/zx-zeyos.esm.min.js +53 -5
  74. package/dist/zx.css +2861 -827
  75. package/dist/zx.esm.js +8909 -1480
  76. package/dist/zx.esm.min.js +55 -7
  77. package/dist/zx.global.js +8911 -1482
  78. package/dist/zx.global.min.js +55 -7
  79. package/dist/zx.min.css +1 -1
  80. package/docs/api.json +1079 -117
  81. package/docs/llms.md +956 -154
  82. package/docs/llms.txt +9 -5
  83. package/package.json +1 -1
  84. package/src/components/account-menu/account-menu.css +35 -10
  85. package/src/components/account-menu/account-menu.js +12 -0
  86. package/src/components/activity-item/activity-item.css +1 -1
  87. package/src/components/activity-item/activity-item.js +3 -0
  88. package/src/components/activity-list/activity-list.css +38 -3
  89. package/src/components/activity-list/activity-list.js +61 -1
  90. package/src/components/app-icon/app-icon.css +14 -3
  91. package/src/components/app-icon/app-icon.js +11 -1
  92. package/src/components/app-rail/app-rail.css +59 -79
  93. package/src/components/app-sidebar/app-sidebar.css +304 -168
  94. package/src/components/app-sidebar/app-sidebar.js +667 -46
  95. package/src/components/aurora/aurora.css +5 -3
  96. package/src/components/avatar/avatar.css +32 -7
  97. package/src/components/avatar/avatar.js +72 -8
  98. package/src/components/badge/badge.css +54 -2
  99. package/src/components/badge/badge.js +29 -2
  100. package/src/components/breadcrumb/breadcrumb.css +1 -1
  101. package/src/components/button/button.css +67 -9
  102. package/src/components/button/button.js +38 -0
  103. package/src/components/calendar/calendar.css +47 -47
  104. package/src/components/calendar/calendar.js +26 -52
  105. package/src/components/card/card.css +4 -4
  106. package/src/components/card-view/card-view.css +14 -12
  107. package/src/components/card-view/card-view.js +13 -9
  108. package/src/components/card-view/record-card.js +14 -3
  109. package/src/components/chart/chart.css +4 -1
  110. package/src/components/chart/chart.js +12 -0
  111. package/src/components/check-button/check-button.css +10 -2
  112. package/src/components/checklist/checklist.css +9 -2
  113. package/src/components/checklist/checklist.js +11 -5
  114. package/src/components/checklist/field-adapter.js +3 -1
  115. package/src/components/code-editor/code-editor.css +7 -6
  116. package/src/components/code-editor/code-editor.js +19 -1
  117. package/src/components/code-editor/field-adapter.js +9 -2
  118. package/src/components/context-menu/context-menu.css +9 -4
  119. package/src/components/context-menu/context-menu.js +8 -2
  120. package/src/components/copy/copy-button.css +6 -0
  121. package/src/components/copy/copy-input.css +9 -4
  122. package/src/components/data-filter/data-filter.css +5 -4
  123. package/src/components/data-filter/data-filter.js +2 -2
  124. package/src/components/date-picker/date-picker.css +6 -6
  125. package/src/components/date-picker/date-range-picker.css +3 -3
  126. package/src/components/date-picker/time-picker.css +3 -3
  127. package/src/components/datebox/date-range-box.css +79 -2
  128. package/src/components/datebox/date-range-box.js +13 -2
  129. package/src/components/datebox/datebox.css +79 -2
  130. package/src/components/datebox/datebox.js +13 -2
  131. package/src/components/dialog/dialog.css +26 -0
  132. package/src/components/dialog/dialog.js +99 -17
  133. package/src/components/dock/dock.css +5 -4
  134. package/src/components/dropdown/dropdown.css +1 -1
  135. package/src/components/dropdown/dropdown.js +159 -72
  136. package/src/components/empty-state/empty-state.css +2 -2
  137. package/src/components/entity-ref/entity-ref.css +1 -1
  138. package/src/components/entity-ref/entity-ref.js +1 -1
  139. package/src/components/field/field.css +28 -9
  140. package/src/components/field/field.js +101 -29
  141. package/src/components/field-upload/field-upload.css +1 -1
  142. package/src/components/fieldset/external-adapter.js +363 -0
  143. package/src/components/fieldset/fieldset.css +5 -3
  144. package/src/components/fieldset/fieldset.js +38 -64
  145. package/src/components/file-item/file-item.js +144 -10
  146. package/src/components/file-list/file-list.js +144 -21
  147. package/src/components/filter/filter.css +86 -2
  148. package/src/components/filter/filter.js +307 -11
  149. package/src/components/filter-panel/filter-panel.css +1 -1
  150. package/src/components/filter-panel/filter-panel.js +149 -32
  151. package/src/components/finder/finder.css +16 -14
  152. package/src/components/form/form.css +5 -4
  153. package/src/components/form/form.js +11 -2
  154. package/src/components/grid/grid.css +1 -1
  155. package/src/components/groupbox/groupbox.css +1 -1
  156. package/src/components/kanban-view/kanban-card.js +11 -3
  157. package/src/components/kanban-view/kanban-view.css +42 -18
  158. package/src/components/kanban-view/kanban-view.js +9 -6
  159. package/src/components/launcher/launcher.css +108 -26
  160. package/src/components/launcher/launcher.js +530 -42
  161. package/src/components/list-toolbar/list-toolbar.css +13 -8
  162. package/src/components/list-toolbar/list-toolbar.js +40 -14
  163. package/src/components/master-panel/master-panel.css +2 -0
  164. package/src/components/master-panel/master-panel.js +0 -1
  165. package/src/components/menu-button/menu-button.css +72 -4
  166. package/src/components/menu-button/menu-button.js +160 -18
  167. package/src/components/message/message.css +33 -3
  168. package/src/components/message/message.js +264 -22
  169. package/src/components/modal/modal.css +1 -1
  170. package/src/components/modal/modal.js +83 -15
  171. package/src/components/module-bar/module-bar.css +486 -0
  172. package/src/components/module-bar/module-bar.js +1074 -0
  173. package/src/components/multi-value-editor/multi-value-editor.css +1 -1
  174. package/src/components/navigation-bar/navigation-bar.css +18 -9
  175. package/src/components/number-field/field-adapter.js +3 -1
  176. package/src/components/number-field/number-field.css +70 -0
  177. package/src/components/number-field/number-field.js +2 -0
  178. package/src/components/pagination/pagination.css +13 -11
  179. package/src/components/pagination/pagination.js +20 -4
  180. package/src/components/panel/panel.css +4 -3
  181. package/src/components/questionnaire/questionnaire.css +4 -4
  182. package/src/components/rating/field-adapter.js +2 -1
  183. package/src/components/rating/rating.css +8 -1
  184. package/src/components/search/search.css +4 -1
  185. package/src/components/select/color.js +22 -0
  186. package/src/components/select/field-adapter.js +2 -0
  187. package/src/components/select/glyphs.js +165 -0
  188. package/src/components/select/select.css +138 -45
  189. package/src/components/select/select.js +92 -47
  190. package/src/components/sheet/sheet.css +12 -9
  191. package/src/components/sheet/sheet.js +74 -32
  192. package/src/components/slider/field-adapter.js +6 -1
  193. package/src/components/slider/slider.css +18 -4
  194. package/src/components/slider/slider.js +1 -1
  195. package/src/components/sort-control/sort-control.css +4 -1
  196. package/src/components/split-view/split-view.css +1 -1
  197. package/src/components/stat-tile/stat-tile.css +42 -47
  198. package/src/components/stat-tile/stat-tile.js +17 -6
  199. package/src/components/stepper/stepper.css +1 -1
  200. package/src/components/tabbox/tabbox.css +41 -32
  201. package/src/components/tabbox/tabbox.js +5 -1
  202. package/src/components/table/table.css +187 -37
  203. package/src/components/table/table.js +718 -73
  204. package/src/components/tag-picker/field-adapter.js +2 -1
  205. package/src/components/tag-picker/tag-picker.css +83 -5
  206. package/src/components/tag-picker/tag-picker.js +25 -8
  207. package/src/components/timebox/timebox.css +22 -2
  208. package/src/components/toggle/toggle.css +12 -1
  209. package/src/components/toolbar/toolbar.js +2 -2
  210. package/src/components/tooltip/tooltip.css +4 -0
  211. package/src/components/tooltip/tooltip.js +44 -19
  212. package/src/components/tree/tree.css +26 -17
  213. package/src/components/tree/tree.js +31 -0
  214. package/src/components/value-list/value-list.css +3 -3
  215. package/src/components/view/record-view.css +1 -1
  216. package/src/components/view/record-view.js +178 -18
  217. package/src/components/view-settings/view-settings.css +285 -0
  218. package/src/components/view-settings/view-settings.js +1830 -0
  219. package/src/components/workspace-switcher/workspace-switcher.css +188 -0
  220. package/src/components/workspace-switcher/workspace-switcher.js +463 -0
  221. package/src/core/breakpoint.js +1 -1
  222. package/src/core/component.js +88 -17
  223. package/src/core/fontawesome.js +5 -1
  224. package/src/core/i18n.js +47 -4
  225. package/src/core/icons.js +204 -61
  226. package/src/core/keyboard.js +206 -11
  227. package/src/core/messages.js +427 -0
  228. package/src/core/motion.js +147 -0
  229. package/src/core/overlay-host.js +77 -3
  230. package/src/core/overlay-stack.js +487 -0
  231. package/src/core/position.js +22 -3
  232. package/src/index.js +10 -5
  233. package/src/internal/app-rail.js +135 -55
  234. package/src/internal/menu-items.js +61 -3
  235. package/src/zeyos/connect.js +6 -2
  236. package/src/zeyos/form.js +69 -27
  237. package/src/zeyos/index.js +4 -1
  238. package/src/zeyos/launcher.js +8 -3
  239. package/src/zeyos/module-bar.js +181 -0
  240. package/src/zeyos/query.js +197 -13
  241. package/src/zeyos/schema.js +144 -27
  242. package/src/zeyos/table.js +287 -84
  243. package/src/zeyos/view.js +49 -93
  244. package/styles/base.css +72 -1
  245. package/styles/icons.css +11 -1
  246. package/styles/print.css +1 -1
  247. package/styles/tokens/dark.css +117 -37
  248. package/styles/tokens/global.css +121 -27
  249. package/styles/tokens/semantic.css +140 -43
  250. package/styles/tokens/themes.css +7 -6
  251. package/styles/zx.css +3 -1
  252. package/styles/tokens/density.css +0 -9
@@ -2,15 +2,18 @@
2
2
  * Four tab appearances, all square-cornered by default:
3
3
  *
4
4
  * divided (default) flat blocks on a muted track, split by hairline dividers, the active
5
- * block lifted onto the panel surface under an accent rule — the workhorse for a
6
- * record page or a panel that owns its content box.
5
+ * block lifted onto the panel surface it opens — the workhorse for a record page or
6
+ * a panel that owns its content box.
7
7
  * bracket folder tabs that outline and fuse into the panel below, the active tab bridging
8
8
  * the seam — for document-like views where the tabs belong to the sheet.
9
- * segmented a compact group on a muted fill, the active tab raised onto a control surface —
10
- * for switching a view inside a toolbar or card header, where the row reads as one
11
- * control rather than as navigation.
12
- * line a full-width rule with the active tab underlined — for page- and section-level
13
- * navigation, where the row spans the content it switches.
9
+ * segmented a compact framed group, the active tab on the neutral current fill — for switching
10
+ * a view inside a toolbar or card header, where the row reads as one control rather
11
+ * than as navigation.
12
+ * line a full-width rule with the active tab underlined in the text colour — for page- and
13
+ * section-level navigation, where the row spans the content it switches.
14
+ *
15
+ * The active tab is where you are, not something you picked, so no variant marks it with the
16
+ * accent: it takes the text colour and a heavier label, and its own variant's neutral ground.
14
17
  *
15
18
  * `divided` and `bracket` draw the panel box themselves; `line` and `segmented` leave the panel
16
19
  * bare so the surrounding layout supplies the chrome.
@@ -57,7 +60,7 @@
57
60
  color: var(--zx-color-text-muted);
58
61
  font: inherit;
59
62
  font-size: var(--zx-control-font-size);
60
- font-weight: 500;
63
+ font-weight: var(--zx-weight-medium);
61
64
  white-space: nowrap;
62
65
  cursor: pointer;
63
66
  }
@@ -68,6 +71,7 @@
68
71
 
69
72
  .zx-tabbox__tab[aria-selected="true"] {
70
73
  color: var(--zx-color-text);
74
+ font-weight: var(--zx-weight-semibold);
71
75
  }
72
76
 
73
77
  .zx-tabbox__tab[aria-disabled="true"] {
@@ -90,8 +94,6 @@
90
94
 
91
95
  .zx-tabbox[data-variant="divided"] .zx-tabbox__tab {
92
96
  block-size: calc(var(--zx-control-height) + 8px);
93
- /* The top rule is the selected marker; transparent here keeps every block the same height. */
94
- border-block-start: 2px solid transparent;
95
97
  border-inline-end: 1px solid var(--zx-color-border);
96
98
  padding-inline: var(--zx-space-4);
97
99
  }
@@ -104,15 +106,15 @@
104
106
  background: var(--zx-color-bg-hover);
105
107
  }
106
108
 
109
+ /* The active block is lifted onto the panel surface it opens; nothing is docked to its edge. */
107
110
  .zx-tabbox[data-variant="divided"] .zx-tabbox__tab[aria-selected="true"] {
108
- border-block-start-color: var(--zx-color-accent);
109
111
  background: var(--zx-color-bg-surface);
110
- font-weight: 650;
111
112
  }
112
113
 
113
114
  .zx-tabbox[data-variant="divided"] .zx-tabbox__tab:focus-visible {
114
115
  z-index: 1;
115
- outline: 0;
116
+ outline-color: transparent;
117
+ outline-offset: -2px;
116
118
  box-shadow: inset var(--zx-focus-ring);
117
119
  }
118
120
 
@@ -141,7 +143,6 @@
141
143
  .zx-tabbox[data-variant="bracket"] .zx-tabbox__tab[aria-selected="true"] {
142
144
  border-color: var(--zx-color-border);
143
145
  background: var(--zx-color-bg-surface);
144
- font-weight: 650;
145
146
  }
146
147
 
147
148
  .zx-tabbox[data-variant="bracket"] .zx-tabbox__tab[aria-selected="true"]::after {
@@ -155,7 +156,8 @@
155
156
 
156
157
  .zx-tabbox[data-variant="bracket"] .zx-tabbox__tab:focus-visible {
157
158
  z-index: 1;
158
- outline: 0;
159
+ outline-color: transparent;
160
+ outline-offset: -2px;
159
161
  box-shadow: inset var(--zx-focus-ring);
160
162
  }
161
163
 
@@ -175,35 +177,37 @@
175
177
  border-block-end-color: var(--zx-color-border-strong);
176
178
  }
177
179
 
180
+ /* The underline is the text colour, not the accent: a tab row is navigation, and the screen's
181
+ one solid accent belongs to its primary action. */
178
182
  .zx-tabbox[data-variant="line"] .zx-tabbox__tab[aria-selected="true"] {
179
- border-block-end-color: var(--zx-color-accent);
180
- font-weight: 600;
183
+ border-block-end-color: var(--zx-color-text);
181
184
  }
182
185
 
183
186
  .zx-tabbox[data-variant="line"] .zx-tabbox__tab:focus-visible {
184
187
  z-index: 1;
185
- outline: 0;
188
+ outline-color: transparent;
189
+ outline-offset: -2px;
186
190
  box-shadow: inset var(--zx-focus-ring);
187
191
  }
188
192
 
189
193
  /* ------------------------------------------------------------- segmented variant -- */
190
194
 
195
+ /* One framed control: a hairline round the group, the segments flat inside it, and the active one
196
+ on the neutral current fill — the same recipe as the view switch in ListToolbar and Calendar. */
191
197
  .zx-tabbox[data-variant="segmented"] .zx-tabbox__tablist {
192
198
  inline-size: fit-content;
193
199
  max-inline-size: 100%;
194
200
  align-items: center;
195
201
  gap: 2px;
196
202
  block-size: var(--zx-control-height);
203
+ border: 1px solid var(--zx-color-border-control);
197
204
  border-radius: var(--zx-tabbox-radius);
198
- background: var(--zx-color-bg-muted);
199
- padding: 3px;
205
+ padding: 2px;
200
206
  }
201
207
 
202
208
  .zx-tabbox[data-variant="segmented"] .zx-tabbox__tab {
203
209
  block-size: 100%;
204
- /* A transparent border keeps the active and inactive boxes the same size. */
205
- border: 1px solid transparent;
206
- /* Nested inside the track's 3px padding, so the inner corner is one step tighter. */
210
+ /* Nested inside the frame's 2px padding, so the inner corner is one step tighter. */
207
211
  border-radius: max(0px, calc(var(--zx-tabbox-radius) - 2px));
208
212
  padding-inline: var(--zx-space-3);
209
213
  font-size: var(--zx-text-sm);
@@ -214,15 +218,12 @@
214
218
  }
215
219
 
216
220
  .zx-tabbox[data-variant="segmented"] .zx-tabbox__tab[aria-selected="true"] {
217
- border-color: var(--zx-color-border-control);
218
- background: var(--zx-color-bg-control);
219
- box-shadow: var(--zx-shadow-2);
221
+ background: var(--zx-color-bg-current);
220
222
  }
221
223
 
222
224
  .zx-tabbox[data-variant="segmented"] .zx-tabbox__tab:focus-visible {
223
225
  z-index: 1;
224
- outline: 0;
225
- border-color: var(--zx-color-ring);
226
+ outline-color: transparent;
226
227
  box-shadow: var(--zx-focus-ring);
227
228
  }
228
229
 
@@ -237,18 +238,25 @@
237
238
 
238
239
  /* ---------------------------------------------------------------- badge & close -- */
239
240
 
241
+ /* A count, not a call to action: a neutral overlay that reads on every variant's ground, in the
242
+ muted text colour, taking the full text colour with its tab. */
240
243
  .zx-tabbox__badge {
241
244
  min-inline-size: 1.25rem;
242
245
  border-radius: var(--zx-radius-full);
243
- background: var(--zx-color-accent);
246
+ background: var(--zx-color-bg-current);
244
247
  padding-inline: var(--zx-space-1);
245
- color: var(--zx-color-on-accent);
248
+ color: var(--zx-color-text-muted);
246
249
  font-size: var(--zx-text-xs);
247
- font-weight: 700;
250
+ font-variant-numeric: tabular-nums;
251
+ font-weight: var(--zx-weight-medium);
248
252
  line-height: 1.25rem;
249
253
  text-align: center;
250
254
  }
251
255
 
256
+ .zx-tabbox__tab[aria-selected="true"] .zx-tabbox__badge {
257
+ color: var(--zx-color-text);
258
+ }
259
+
252
260
  /* Chrome comes from the shared .zx-icon-btn ghost utility; only the box is reset here, because
253
261
  a full control-height square would widen every closable tab. The extra .zx-tabbox raises the
254
262
  specificity to match `.zx-scope .zx-icon-btn` in base.css, which sets the size. */
@@ -286,6 +294,7 @@
286
294
  }
287
295
 
288
296
  .zx-tabbox__panel:focus-visible {
289
- outline: 0;
297
+ outline-color: transparent;
298
+ outline-offset: -2px;
290
299
  box-shadow: inset var(--zx-focus-ring);
291
300
  }
@@ -37,6 +37,9 @@ const VARIANTS = new Set(['divided', 'bracket', 'line', 'segmented']);
37
37
  * the active tab across a full-width rule; `segmented` renders a compact group that reads as one
38
38
  * control, for a toolbar or card header.
39
39
  * @property {boolean} [keepAlive=true] Keep inactive panel elements mounted.
40
+ * @property {string} [label] Accessible name of the tab list — what the tabs switch between
41
+ * ("Record sections"). A tab list needs one whenever the page has more than one, and screen
42
+ * readers announce it on entry.
40
43
  * @property {(event: CustomEvent<{name: string, previous: string|null}>) => void} [onchange] Change callback.
41
44
  * @property {(event: CustomEvent<{name: string}>) => void} [onclose] Close callback, fired when the
42
45
  * user dismisses a closable tab.
@@ -101,7 +104,8 @@ export class Tabbox extends Component {
101
104
  class: 'zx-tabbox__tablist',
102
105
  ref: 'tablist',
103
106
  role: 'tablist',
104
- ariaOrientation: 'horizontal'
107
+ ariaOrientation: 'horizontal',
108
+ ariaLabel: this.options.label || null
105
109
  });
106
110
  const panels = h('div', {
107
111
  class: 'zx-tabbox__panels',
@@ -64,9 +64,9 @@
64
64
  grid-template-columns: minmax(0, 1fr) auto;
65
65
  align-items: center;
66
66
  gap: var(--zx-space-2);
67
- min-block-size: var(--zx-control-height);
67
+ min-block-size: calc(var(--zx-control-height) - 2px);
68
68
  border-radius: var(--zx-radius-sm);
69
- padding-inline: var(--zx-space-2);
69
+ padding: var(--zx-space-1) var(--zx-space-2);
70
70
  }
71
71
 
72
72
  .zx-table__column-option:hover { background: var(--zx-color-overlay-hover); }
@@ -108,7 +108,7 @@
108
108
  }
109
109
 
110
110
  .zx-table__column-move:focus-visible {
111
- outline: none;
111
+ outline-color: transparent;
112
112
  box-shadow: var(--zx-focus-ring);
113
113
  }
114
114
 
@@ -132,16 +132,28 @@
132
132
  }
133
133
  }
134
134
 
135
+ /*
136
+ * The reorder and selection columns are one control height wide, the control centred in the slot.
137
+ * Their cells drop the inline padding: a fixed layout holds the column to exactly that width, and
138
+ * the padding would push the control off its middle. The controls are blocks centred by their
139
+ * margins: inline, they would sit on the text baseline with the descender room left below them, a
140
+ * few pixels above the middle of the row.
141
+ */
135
142
  .zx-table__reorder-column,
136
- .zx-table__reorder-header,
137
- .zx-table__reorder-cell {
143
+ .zx-table__selection-column {
138
144
  inline-size: var(--zx-control-height);
139
- text-align: center;
145
+ }
146
+
147
+ .zx-table :is(.zx-table__reorder-header, .zx-table__reorder-cell,
148
+ .zx-table__selection-header, .zx-table__selection-cell) {
149
+ inline-size: var(--zx-control-height);
150
+ padding-inline: 0;
140
151
  }
141
152
 
142
153
  .zx-table__row-handle {
143
- display: inline-grid;
154
+ display: grid;
144
155
  place-items: center;
156
+ margin-inline: auto;
145
157
  inline-size: 26px;
146
158
  block-size: 26px;
147
159
  min-block-size: 0;
@@ -171,7 +183,9 @@
171
183
  border: 1px solid var(--zx-color-border);
172
184
  border-radius: var(--zx-radius-md);
173
185
  background: var(--zx-color-bg-surface);
174
- scrollbar-gutter: stable;
186
+ /* No reserved gutter: a table that does not scroll would carry a blank strip beside its last
187
+ column wherever scrollbars take up room. One that scrolls gets its scrollbar when it does. */
188
+ scrollbar-gutter: auto;
175
189
  }
176
190
 
177
191
  /* Loading state (Table.setLoading): dim rows and show an indeterminate top progress bar. */
@@ -214,6 +228,8 @@
214
228
  border-spacing: 0;
215
229
  background: var(--zx-color-bg-surface);
216
230
  color: var(--zx-color-text);
231
+ /* Figures line up down a column whatever the cell's alignment. Only digits change width. */
232
+ font-variant-numeric: tabular-nums;
217
233
  }
218
234
 
219
235
  /* Auto layout (intrinsic/px columns): let content set the width and scroll horizontally. */
@@ -227,23 +243,32 @@
227
243
  table-layout: fixed;
228
244
  }
229
245
 
230
- .zx-table__selection-column {
231
- inline-size: var(--zx-control-height);
232
- }
233
-
246
+ /*
247
+ * Rows are separated by the subtle divider, the header from the body by the ordinary one. The
248
+ * header has no band of its own: it sits on the table's surface in the muted text colour at a
249
+ * medium weight, so it labels the columns without competing with the values in them — and stays
250
+ * opaque, which a sticky header has to be.
251
+ */
234
252
  .zx-table th,
235
253
  .zx-table td {
236
- border-block-end: 1px solid var(--zx-table-border-color, var(--zx-color-border));
254
+ border-block-end: 1px solid var(--zx-table-border-color, var(--zx-color-border-subtle));
237
255
  padding: var(--zx-space-2) var(--zx-space-3);
238
256
  text-align: start;
239
257
  vertical-align: middle;
240
258
  }
241
259
 
242
260
  .zx-table th {
243
- background: var(--zx-table-header-bg, var(--zx-color-bg-raised));
261
+ border-block-end-color: var(--zx-table-border-color, var(--zx-color-border));
262
+ background: var(--zx-table-header-bg, var(--zx-color-bg-surface));
244
263
  color: var(--zx-color-text-muted);
245
264
  font-size: var(--zx-text-sm);
246
- font-weight: 650;
265
+ font-weight: var(--zx-weight-medium);
266
+ }
267
+
268
+ /* The column the rows are ordered by says so in its label as well as its arrow. */
269
+ .zx-table th[aria-sort="ascending"],
270
+ .zx-table th[aria-sort="descending"] {
271
+ color: var(--zx-color-text);
247
272
  }
248
273
 
249
274
  .zx-table__scroll[data-sticky-header="true"] th {
@@ -273,20 +298,40 @@
273
298
  }
274
299
 
275
300
  .zx-table__sort-button {
301
+ /* At least 24px tall (WCAG 2.5.8 target size) without making the header any taller: the height
302
+ the label's line lacks is padding, handed back by a negative margin of the same size. */
303
+ --zx-table-sort-slack: max(0px, (24px - 1lh) / 2);
276
304
  display: flex;
277
305
  align-items: center;
278
306
  justify-content: space-between;
279
307
  gap: var(--zx-space-2);
280
308
  inline-size: 100%;
281
- min-block-size: auto;
309
+ min-block-size: 24px;
282
310
  border: 0;
283
311
  border-radius: var(--zx-radius-sm);
284
312
  background: inherit;
285
313
  color: inherit;
286
- padding: 0;
314
+ margin-block: calc(-1 * var(--zx-table-sort-slack));
315
+ padding: var(--zx-table-sort-slack) 0;
316
+ /* The header's type, not a control's: base.css sets buttons in the control size and weight, and
317
+ the browser resets their tracking, so a sortable label would not match its unsortable
318
+ neighbours. */
319
+ font: inherit;
320
+ letter-spacing: inherit;
287
321
  text-align: inherit;
288
322
  }
289
323
 
324
+ /* A sortable figure column keeps its label over the figures: label at the end edge, the direction
325
+ arrow before it. */
326
+ .zx-table th:is([data-align="end"], [data-align="right"]) .zx-table__sort-button {
327
+ flex-direction: row-reverse;
328
+ justify-content: flex-start;
329
+ }
330
+
331
+ .zx-table th[data-align="center"] .zx-table__sort-button {
332
+ justify-content: center;
333
+ }
334
+
290
335
  .zx-table__sort-button:hover {
291
336
  background: var(--zx-table-row-hover-bg, var(--zx-color-bg-hover));
292
337
  color: var(--zx-color-text);
@@ -298,12 +343,12 @@
298
343
 
299
344
  .zx-table th[aria-sort="ascending"] .zx-table__sort-button::after {
300
345
  content: "↑";
301
- color: var(--zx-color-accent);
346
+ color: var(--zx-color-text-muted);
302
347
  }
303
348
 
304
349
  .zx-table th[aria-sort="descending"] .zx-table__sort-button::after {
305
350
  content: "↓";
306
- color: var(--zx-color-accent);
351
+ color: var(--zx-color-text-muted);
307
352
  }
308
353
 
309
354
  .zx-table__body tr:last-child td {
@@ -314,26 +359,127 @@
314
359
  background: var(--zx-color-bg-page);
315
360
  }
316
361
 
362
+ /* Checked rows of a multi-selection are picks and keep the accent tint. The selected row of a
363
+ single-selection table — no checkbox column — is the record you are on, the current row of a
364
+ master–detail list, and takes the neutral current fill instead. */
317
365
  .zx-table__body tr[aria-selected="true"] {
318
366
  background: var(--zx-table-row-selected-bg, var(--zx-color-bg-selected));
319
367
  }
320
368
 
369
+ .zx-table__body tr[aria-selected="true"]:not(:has(> .zx-table__selection-cell)) {
370
+ background: var(--zx-table-row-selected-bg, var(--zx-color-bg-current));
371
+ }
372
+
321
373
  .zx-table__body tr[data-row]:hover {
322
374
  background: var(--zx-color-bg-hover);
323
375
  }
324
376
 
377
+ /* ------------------------------------------------------------------ plain -- */
378
+
379
+ /*
380
+ * `variant: 'plain'` — for a table inside a card or panel that already draws the box: no outer
381
+ * frame, no header band, no zebra, hairline rows. The cells keep their padding, so the hover and
382
+ * selection fills never run up against the text.
383
+ */
384
+ .zx-table[data-variant="plain"] .zx-table__scroll {
385
+ border: 0;
386
+ border-radius: 0;
387
+ background: transparent;
388
+ }
389
+
390
+ .zx-table[data-variant="plain"] .zx-table__table {
391
+ background: transparent;
392
+ }
393
+
394
+ .zx-table[data-variant="plain"] th {
395
+ background: var(--zx-table-header-bg, var(--zx-color-bg-surface));
396
+ border-block-end-color: var(--zx-table-border-color, var(--zx-color-border-subtle));
397
+ }
398
+
399
+ .zx-table[data-variant="plain"] .zx-table__scroll[data-sticky-header="false"] th {
400
+ background: transparent;
401
+ }
402
+
403
+ .zx-table[data-variant="plain"] .zx-table__scroll[data-zebra="true"] .zx-table__body tr:nth-child(even):not([aria-selected="true"]) {
404
+ background: transparent;
405
+ }
406
+
407
+ .zx-table[data-variant="plain"] .zx-table__body tr:last-child td {
408
+ border-block-end: 0;
409
+ }
410
+
411
+
412
+ /*
413
+ * The selection checkboxes are drawn rather than native. A native checkbox paints its glyph across
414
+ * its whole box, so the only way to give it a 24px target (WCAG 2.5.8) would be a 24px glyph. Here
415
+ * the box stays the --zx-space-4 square it always was and a transparent border around it makes up
416
+ * the rest of the target — the inset shadow that draws the box's edge sits inside that border.
417
+ * Forced colours get the native control back, which the system paints in its own colours.
418
+ */
325
419
  .zx-table__checkbox {
420
+ --zx-table-checkbox-slack: max(0px, (24px - var(--zx-space-4)) / 2);
421
+ appearance: none;
422
+ display: grid;
423
+ place-content: center;
424
+ box-sizing: content-box;
326
425
  inline-size: var(--zx-space-4);
327
- min-block-size: auto;
328
426
  block-size: var(--zx-space-4);
329
- margin: 0;
330
- accent-color: var(--zx-color-accent);
427
+ min-block-size: auto;
428
+ margin: 0 auto;
429
+ border: var(--zx-table-checkbox-slack) solid transparent;
430
+ border-radius: calc(var(--zx-radius-sm) + var(--zx-table-checkbox-slack));
431
+ padding: 0;
432
+ background: var(--zx-color-bg-control) padding-box;
433
+ box-shadow: inset 0 0 0 1px var(--zx-color-text-subtle);
434
+ color: var(--zx-color-on-accent);
435
+ cursor: pointer;
331
436
  }
332
437
 
333
- .zx-table__selection-header,
334
- .zx-table__selection-cell {
335
- inline-size: var(--zx-control-height);
336
- text-align: center;
438
+ .zx-table__checkbox::before {
439
+ content: "";
440
+ inline-size: calc(var(--zx-space-4) * .625);
441
+ block-size: calc(var(--zx-space-4) * .625);
442
+ background: currentColor;
443
+ clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%);
444
+ visibility: hidden;
445
+ }
446
+
447
+ .zx-table__checkbox:hover:not(:disabled) {
448
+ box-shadow: inset 0 0 0 1px var(--zx-color-text-muted);
449
+ }
450
+
451
+ .zx-table__checkbox:is(:checked, :indeterminate) {
452
+ background-color: var(--zx-color-accent);
453
+ box-shadow: inset 0 0 0 1px var(--zx-color-accent);
454
+ }
455
+
456
+ .zx-table__checkbox:is(:checked, :indeterminate)::before {
457
+ visibility: visible;
458
+ }
459
+
460
+ .zx-table__checkbox:indeterminate::before {
461
+ clip-path: inset(40% 5%);
462
+ }
463
+
464
+ .zx-table__checkbox:focus-visible {
465
+ outline-color: transparent;
466
+ outline-offset: calc(-1 * var(--zx-table-checkbox-slack));
467
+ box-shadow: inset 0 0 0 1px var(--zx-color-accent), var(--zx-focus-ring);
468
+ }
469
+
470
+ .zx-table__checkbox:disabled {
471
+ opacity: .5;
472
+ cursor: not-allowed;
473
+ }
474
+
475
+ @media (forced-colors: active) {
476
+ .zx-table__checkbox {
477
+ appearance: auto;
478
+ }
479
+
480
+ .zx-table__checkbox::before {
481
+ content: none;
482
+ }
337
483
  }
338
484
 
339
485
  .zx-table__empty {
@@ -377,7 +523,7 @@
377
523
  }
378
524
 
379
525
  .zx-table__tree-toggle:focus-visible {
380
- outline: 0;
526
+ outline-color: transparent;
381
527
  box-shadow: var(--zx-focus-ring);
382
528
  }
383
529
 
@@ -441,13 +587,20 @@
441
587
  cursor: cell;
442
588
  }
443
589
 
444
- /* Cells carry a roving tabindex, so their ring must sit inside the cell box — an outer ring would
445
- be painted over by the neighbouring cell. */
446
- .zx-table__body td[data-editable="true"]:focus-visible {
447
- outline: 0;
590
+ /* Rows and cells carry the grid's roving tabindex, so their ring sits inside the box — an outer
591
+ ring would be painted over by the neighbouring row or cell, or clipped by the scroll region. The
592
+ outline stays, transparent, for forced colours to paint. */
593
+ .zx-table__body :is(tr[data-row], td, th)[tabindex]:focus-visible {
594
+ outline-color: transparent;
595
+ outline-offset: -2px;
448
596
  box-shadow: inset var(--zx-focus-ring);
449
597
  }
450
598
 
599
+ /* A row moved to by keyboard scrolls clear of the sticky header rather than behind it. */
600
+ .zx-table__body > tr[data-row] {
601
+ scroll-margin-block: calc(var(--zx-control-height) + var(--zx-space-2)) var(--zx-space-2);
602
+ }
603
+
451
604
  .zx-table__body td[data-editing="true"] {
452
605
  position: relative;
453
606
  }
@@ -583,12 +736,9 @@
583
736
  content: none;
584
737
  }
585
738
 
586
- .zx-table[data-stacked] .zx-table__selection-cell {
587
- justify-content: flex-start;
588
- }
589
-
590
- .zx-table[data-stacked] .zx-table__reorder-cell {
591
- justify-content: flex-start;
739
+ /* A card's checkbox and handle lead their line, where the auto margins would centre them. */
740
+ .zx-table[data-stacked] :is(.zx-table__checkbox, .zx-table__row-handle) {
741
+ margin-inline: 0;
592
742
  }
593
743
 
594
744