@zeyos/zx 4.3.4 → 5.0.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 (231) hide show
  1. package/CHANGELOG.md +438 -0
  2. package/README.md +49 -10
  3. package/dist/types/components/activity-item/activity-item.d.ts +351 -0
  4. package/dist/types/components/activity-list/activity-list.d.ts +217 -0
  5. package/dist/types/components/app-sidebar/app-sidebar.d.ts +33 -3
  6. package/dist/types/components/avatar/avatar.d.ts +46 -7
  7. package/dist/types/components/badge/badge.d.ts +21 -1
  8. package/dist/types/components/button/button.d.ts +23 -0
  9. package/dist/types/components/calendar/calendar.d.ts +19 -0
  10. package/dist/types/components/card/card.d.ts +7 -0
  11. package/dist/types/components/card-view/card-view.d.ts +11 -3
  12. package/dist/types/components/card-view/record-card.d.ts +7 -1
  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 +149 -0
  16. package/dist/types/components/code-editor/field-adapter.d.ts +5 -0
  17. package/dist/types/components/context-menu/context-menu.d.ts +18 -2
  18. package/dist/types/components/date-picker/date-picker.d.ts +8 -0
  19. package/dist/types/components/date-picker/time-picker.d.ts +7 -0
  20. package/dist/types/components/datebox/date-range-box.d.ts +5 -1
  21. package/dist/types/components/datebox/datebox.d.ts +7 -2
  22. package/dist/types/components/dialog/dialog.d.ts +75 -6
  23. package/dist/types/components/dropdown/dropdown.d.ts +2 -2
  24. package/dist/types/components/entity-ref/entity-ref.d.ts +331 -0
  25. package/dist/types/components/field/field.d.ts +24 -2
  26. package/dist/types/components/fieldset/fieldset.d.ts +19 -1
  27. package/dist/types/components/file-item/file-item.d.ts +262 -0
  28. package/dist/types/components/file-list/file-list.d.ts +171 -0
  29. package/dist/types/components/filter/filter.d.ts +91 -36
  30. package/dist/types/components/filter-panel/filter-panel.d.ts +449 -0
  31. package/dist/types/components/form/form.d.ts +12 -1
  32. package/dist/types/components/kanban-view/kanban-card.d.ts +146 -0
  33. package/dist/types/components/kanban-view/kanban-view.d.ts +170 -4
  34. package/dist/types/components/launcher/launcher.d.ts +278 -26
  35. package/dist/types/components/list-toolbar/list-toolbar.d.ts +599 -0
  36. package/dist/types/components/menu-button/menu-button.d.ts +37 -42
  37. package/dist/types/components/message/message.d.ts +126 -17
  38. package/dist/types/components/modal/modal.d.ts +61 -2
  39. package/dist/types/components/navigation-bar/navigation-bar.d.ts +105 -21
  40. package/dist/types/components/panel/panel.d.ts +7 -0
  41. package/dist/types/components/search/search.d.ts +7 -0
  42. package/dist/types/components/select/color.d.ts +12 -0
  43. package/dist/types/components/select/glyphs.d.ts +124 -0
  44. package/dist/types/components/select/select.d.ts +15 -3
  45. package/dist/types/components/sort-control/sort-control.d.ts +105 -0
  46. package/dist/types/components/stat-tile/stat-tile.d.ts +288 -0
  47. package/dist/types/components/tabbox/tabbox.d.ts +16 -0
  48. package/dist/types/components/table/table.d.ts +190 -1
  49. package/dist/types/components/table-view/table-view.d.ts +11 -5
  50. package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
  51. package/dist/types/components/timebox/timebox.d.ts +7 -0
  52. package/dist/types/components/tree/tree.d.ts +13 -0
  53. package/dist/types/components/value-list/value-list.d.ts +8 -0
  54. package/dist/types/components/view/record-view.d.ts +96 -5
  55. package/dist/types/core/breakpoint.d.ts +1 -1
  56. package/dist/types/core/icons.d.ts +37 -22
  57. package/dist/types/core/motion.d.ts +59 -0
  58. package/dist/types/core/overlay-host.d.ts +15 -1
  59. package/dist/types/index.d.ts +16 -3
  60. package/dist/types/internal/app-rail.d.ts +10 -0
  61. package/dist/types/internal/menu-items.d.ts +134 -0
  62. package/dist/types/zeyos/connect.d.ts +6 -0
  63. package/dist/types/zeyos/form.d.ts +50 -9
  64. package/dist/types/zeyos/index.d.ts +1 -1
  65. package/dist/types/zeyos/query.d.ts +79 -0
  66. package/dist/types/zeyos/schema.d.ts +24 -1
  67. package/dist/types/zeyos/table.d.ts +100 -14
  68. package/dist/types/zeyos/view.d.ts +10 -0
  69. package/dist/zx-zeyos.esm.js +2206 -489
  70. package/dist/zx-zeyos.esm.min.js +56 -1
  71. package/dist/zx.css +3322 -724
  72. package/dist/zx.esm.js +12244 -5809
  73. package/dist/zx.esm.min.js +58 -3
  74. package/dist/zx.global.js +12246 -5811
  75. package/dist/zx.global.min.js +58 -3
  76. package/dist/zx.min.css +1 -1
  77. package/docs/api.json +1683 -146
  78. package/docs/llms.md +777 -110
  79. package/docs/llms.txt +14 -8
  80. package/package.json +1 -1
  81. package/src/components/account-menu/account-menu.css +35 -10
  82. package/src/components/activity-item/activity-item.css +137 -0
  83. package/src/components/activity-item/activity-item.js +602 -0
  84. package/src/components/activity-list/activity-list.css +99 -0
  85. package/src/components/activity-list/activity-list.js +409 -0
  86. package/src/components/app-icon/app-icon.css +1 -1
  87. package/src/components/app-rail/app-rail.css +191 -22
  88. package/src/components/app-sidebar/app-sidebar.css +349 -24
  89. package/src/components/app-sidebar/app-sidebar.js +86 -11
  90. package/src/components/aurora/aurora.css +5 -3
  91. package/src/components/avatar/avatar.css +32 -7
  92. package/src/components/avatar/avatar.js +72 -8
  93. package/src/components/badge/badge.css +54 -2
  94. package/src/components/badge/badge.js +29 -2
  95. package/src/components/button/button.css +74 -8
  96. package/src/components/button/button.js +44 -1
  97. package/src/components/calendar/calendar.css +46 -46
  98. package/src/components/calendar/calendar.js +33 -3
  99. package/src/components/card/card.css +2 -2
  100. package/src/components/card/card.js +12 -1
  101. package/src/components/card-view/card-view.css +10 -7
  102. package/src/components/card-view/card-view.js +25 -9
  103. package/src/components/card-view/record-card.js +7 -3
  104. package/src/components/chart/chart.css +4 -1
  105. package/src/components/chart/chart.js +12 -0
  106. package/src/components/check-button/check-button.css +10 -2
  107. package/src/components/checklist/checklist.css +9 -2
  108. package/src/components/checklist/checklist.js +11 -5
  109. package/src/components/checklist/field-adapter.js +3 -1
  110. package/src/components/code-editor/code-editor.css +68 -0
  111. package/src/components/code-editor/code-editor.js +306 -0
  112. package/src/components/code-editor/field-adapter.js +34 -0
  113. package/src/components/context-menu/context-menu.css +87 -5
  114. package/src/components/context-menu/context-menu.js +22 -30
  115. package/src/components/copy/copy-button.css +6 -0
  116. package/src/components/copy/copy-input.css +8 -3
  117. package/src/components/data-filter/data-filter.css +5 -4
  118. package/src/components/date-picker/date-picker.css +6 -6
  119. package/src/components/date-picker/date-picker.js +22 -8
  120. package/src/components/date-picker/date-range-picker.css +3 -3
  121. package/src/components/date-picker/month-picker.js +3 -3
  122. package/src/components/date-picker/time-picker.css +1 -1
  123. package/src/components/date-picker/time-picker.js +20 -8
  124. package/src/components/datebox/date-range-box.css +79 -2
  125. package/src/components/datebox/date-range-box.js +13 -2
  126. package/src/components/datebox/datebox.css +79 -2
  127. package/src/components/datebox/datebox.js +13 -2
  128. package/src/components/dialog/dialog.css +26 -0
  129. package/src/components/dialog/dialog.js +174 -70
  130. package/src/components/dropdown/dropdown.css +1 -1
  131. package/src/components/dropdown/dropdown.js +7 -5
  132. package/src/components/empty-state/empty-state.css +2 -2
  133. package/src/components/entity-ref/entity-ref.css +182 -0
  134. package/src/components/entity-ref/entity-ref.js +496 -0
  135. package/src/components/field/field.css +28 -9
  136. package/src/components/field/field.js +68 -26
  137. package/src/components/field-upload/field-upload.css +1 -1
  138. package/src/components/fieldset/fieldset.css +5 -3
  139. package/src/components/fieldset/fieldset.js +28 -5
  140. package/src/components/file-item/file-item.css +88 -0
  141. package/src/components/file-item/file-item.js +505 -0
  142. package/src/components/file-list/file-list.css +51 -0
  143. package/src/components/file-list/file-list.js +320 -0
  144. package/src/components/filter/filter.css +27 -1
  145. package/src/components/filter/filter.js +205 -47
  146. package/src/components/filter-panel/filter-panel.css +104 -0
  147. package/src/components/filter-panel/filter-panel.js +964 -0
  148. package/src/components/finder/finder.css +15 -13
  149. package/src/components/form/form.css +5 -4
  150. package/src/components/form/form.js +11 -2
  151. package/src/components/grid/grid.css +1 -1
  152. package/src/components/groupbox/groupbox.css +1 -1
  153. package/src/components/kanban-view/kanban-card.js +247 -0
  154. package/src/components/kanban-view/kanban-view.css +226 -8
  155. package/src/components/kanban-view/kanban-view.js +255 -12
  156. package/src/components/launcher/launcher.css +107 -25
  157. package/src/components/launcher/launcher.js +557 -63
  158. package/src/components/list-toolbar/list-toolbar.css +194 -0
  159. package/src/components/list-toolbar/list-toolbar.js +851 -0
  160. package/src/components/master-panel/master-panel.css +2 -0
  161. package/src/components/menu-button/menu-button.css +87 -5
  162. package/src/components/menu-button/menu-button.js +86 -46
  163. package/src/components/message/message.css +38 -5
  164. package/src/components/message/message.js +230 -17
  165. package/src/components/modal/modal.css +15 -0
  166. package/src/components/modal/modal.js +159 -3
  167. package/src/components/navigation-bar/navigation-bar.css +95 -11
  168. package/src/components/navigation-bar/navigation-bar.js +171 -4
  169. package/src/components/number-field/field-adapter.js +1 -0
  170. package/src/components/number-field/number-field.css +70 -0
  171. package/src/components/number-field/number-field.js +2 -0
  172. package/src/components/pagination/pagination.css +8 -6
  173. package/src/components/panel/panel.css +1 -1
  174. package/src/components/panel/panel.js +13 -2
  175. package/src/components/rating/rating.css +7 -0
  176. package/src/components/search/search.js +17 -5
  177. package/src/components/select/color.js +22 -0
  178. package/src/components/select/field-adapter.js +6 -1
  179. package/src/components/select/glyphs.js +165 -0
  180. package/src/components/select/select.css +138 -45
  181. package/src/components/select/select.js +86 -47
  182. package/src/components/sheet/sheet.css +11 -8
  183. package/src/components/slider/slider.css +16 -2
  184. package/src/components/slider/slider.js +1 -1
  185. package/src/components/sort-control/sort-control.css +20 -0
  186. package/src/components/sort-control/sort-control.js +140 -0
  187. package/src/components/stat-tile/stat-tile.css +208 -0
  188. package/src/components/stat-tile/stat-tile.js +601 -0
  189. package/src/components/tabbox/tabbox.css +32 -27
  190. package/src/components/tabbox/tabbox.js +17 -2
  191. package/src/components/table/table.css +155 -31
  192. package/src/components/table/table.js +386 -33
  193. package/src/components/table-view/table-view.css +8 -2
  194. package/src/components/table-view/table-view.js +21 -6
  195. package/src/components/tag-picker/tag-picker.css +86 -6
  196. package/src/components/tag-picker/tag-picker.js +25 -8
  197. package/src/components/timebox/timebox.css +22 -2
  198. package/src/components/timebox/timebox.js +18 -5
  199. package/src/components/toggle/toggle.css +12 -1
  200. package/src/components/toolbar/toolbar.js +2 -2
  201. package/src/components/tooltip/tooltip.css +4 -0
  202. package/src/components/tooltip/tooltip.js +10 -3
  203. package/src/components/tree/tree.css +25 -16
  204. package/src/components/tree/tree.js +31 -0
  205. package/src/components/value-list/value-list.css +3 -3
  206. package/src/components/value-list/value-list.js +22 -6
  207. package/src/components/view/record-view.css +5 -0
  208. package/src/components/view/record-view.js +100 -7
  209. package/src/core/breakpoint.js +1 -1
  210. package/src/core/icons.js +259 -57
  211. package/src/core/motion.js +147 -0
  212. package/src/core/overlay-host.js +21 -2
  213. package/src/index.js +30 -3
  214. package/src/internal/app-rail.js +18 -1
  215. package/src/internal/menu-items.js +142 -0
  216. package/src/zeyos/connect.js +6 -2
  217. package/src/zeyos/form.js +67 -27
  218. package/src/zeyos/index.js +1 -1
  219. package/src/zeyos/query.js +153 -0
  220. package/src/zeyos/schema.js +144 -27
  221. package/src/zeyos/table.js +68 -5
  222. package/src/zeyos/view.js +6 -0
  223. package/styles/base.css +56 -0
  224. package/styles/icons.css +11 -1
  225. package/styles/print.css +192 -0
  226. package/styles/tokens/dark.css +113 -37
  227. package/styles/tokens/global.css +121 -27
  228. package/styles/tokens/semantic.css +100 -31
  229. package/styles/tokens/themes.css +7 -6
  230. package/styles/zx.css +11 -1
  231. package/styles/tokens/density.css +0 -9
package/CHANGELOG.md CHANGED
@@ -8,6 +8,444 @@ and friends remain the complete record.
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 5.0.0 — 2026-10-09
12
+
13
+ Xenon now has one geometry and a quieter finish. The cozy and compact view modes are gone; in their
14
+ place is a single, slightly tighter default whose spacing, corners, and control height are tokens a
15
+ product sets once. On top of it the visual language was refined against how well-made business
16
+ applications look today: "where you are" is a neutral fill rather than a block of accent colour,
17
+ status colours no longer borrow the brand green, chrome and canvas separate by tone instead of by
18
+ lines, labels are sentence case at medium weights, and icons are outlines.
19
+
20
+ The other half is what building twenty complete screens on the library asked of it. A layout
21
+ gallery of application templates and screen patterns — mail, an issue tracker, a knowledge base, AI
22
+ chat, an agent workspace, invoicing, inventory, workflows, infrastructure, CRM, and the screens
23
+ inside them — every one usable from a phone to a wide screen and clean under an accessibility
24
+ audit. Fixing what those screens found: some forty component defects, status colours that read as
25
+ text, nested dark themes, and focus that never falls to the page. And what they needed: motion as
26
+ a reader's setting, a command palette with pages, toasts that carry their action, busy buttons,
27
+ type-to-confirm, and public status and priority glyphs.
28
+
29
+ ### Breaking
30
+
31
+ - **Form field keys are no longer element ids.** Control ids are generated per Form and Fieldset
32
+ instance (`<idPrefix>-<key>`; `idPrefix` makes them predictable), so a form mounted twice repeats
33
+ no id and a key such as `stage` never collides with `#stage` elsewhere. The key stays the field's
34
+ `name`, the native control's `name`, and its `data-field-id`; radio groups are named after the
35
+ control id. Code that looked a control up by `#<key>` reads `form.getField(key).controlId` (or
36
+ queries `[data-field-id="<key>"]`) instead. An explicit `id` that differs from the key is used as
37
+ given, and the legacy compatibility layer keeps key ids.
38
+ - **Density modes are removed.** `data-zx-density` (`cozy` | `compact`) no longer has any effect,
39
+ `styles/tokens/density.css` no longer ships, and no component carries density-specific rules.
40
+ Migration: drop the attribute. To tighten an application — or one region of it — set
41
+ `--zx-space-unit` and `--zx-control-height` instead, for example
42
+ `:root { --zx-space-unit: 3.5px; --zx-control-height: 28px; }`, which is close to what compact
43
+ used to do without shrinking the type.
44
+ - **`FileItem`'s `density` option is now `identitySize`** (`'sm'|'md'|'lg'`, the size of the
45
+ file's icon and name), and `FileList` items take the same name. The word now means one thing
46
+ nowhere in the library.
47
+
48
+ ### Changed
49
+
50
+ - Every toast pauses while it holds focus as well as hover, resumes when neither applies, and
51
+ returns focus to where it came from if it closes while focused.
52
+ - Sheet slides on `--zx-ease-drawer`; KanbanView's drag preview and grabbed card take
53
+ `--zx-drag-shadow`. A solid primary button's pressed step is darker than its hover step.
54
+ - Launcher `select` and `error` details carry `page`; closing the launcher returns it to its root.
55
+ The status and priority glyph styles are `.zx-status-icon` and `.zx-priority-icon` (Select's
56
+ glyphs keep their old classes as well).
57
+ - **`Select.priority()` reads by height, not hue**: levels are four grey signal bars filled to the
58
+ level in muted ink, and only the highest is coloured — a danger square with "!". It offers "No
59
+ priority" (value null, message `priority.none`) at the head of the list; `clearable: false`
60
+ restores the old list, a required priority field leaves it out, and the legacy compatibility
61
+ select keeps its old list unless it allows empty values. Values and message keys are unchanged.
62
+ - **Quiet NumberField** start-aligns its value with the quiet values around it; both steppers sit
63
+ at the trailing end, shown on hover or focus.
64
+ - **Field `type: 'html'`** draws no input frame; display content keeps a control's height.
65
+ - **Toggle labels** start-align and wrap like text, the track level with the first line; a field
66
+ beside a taller one keeps its label and control at the top of its cell.
67
+ - **FileItem** shows a readable type ("PDF", "Image") instead of the MIME type and picks its icon
68
+ from the MIME type when the icon set has one.
69
+ - **AppSidebar** creates its own root as a `<div>` (the `<nav>` inside is the landmark), so it no
70
+ longer adds an unnamed complementary landmark; `getItems()` leaves out empty `children`.
71
+ - **Table** puts `data-column` on every header and body cell in every mode; **KanbanView**
72
+ assignee faces take Avatar's tint from the name (or a descriptor `tone`); the neutral soft
73
+ **Badge** is a wash of the text colour, visible on raised dark surfaces, and a small dotted
74
+ Badge has 12px text and a 4px gap.
75
+ - **ZeyOS enums read as labels** ("Invoiced", "Partly paid") in generated tables and forms; a
76
+ schema `{value, label}` is used as given. **ZeyOS date-only fields are calendar days** — indexed
77
+ `date`, `duedate`, `birthdate`, `datefrom`/`dateto` outside appointments and events, `datecancel`,
78
+ and `lastbilling` show no time and are stored as midnight in the instance's zone (`timeZone`,
79
+ default the browser's); `lastmodified` and the appointment, event, comment, record, and
80
+ document-version timestamps are date-times.
81
+ - **Spacing derives from one unit.** `--zx-space-1` … `--zx-space-8` are multiples of the new
82
+ `--zx-space-unit` (4px by default, so the steps are unchanged at 4–40px).
83
+ - **Corners are smaller and proportional.** `--zx-radius` is now `4px`, and `-sm`, `-md`, `-lg`,
84
+ and `-xl` are ×0.5, ×0.75, ×1, and ×1.5 of it — 2 / 3 / 4 / 6px, down from 3 / 5 / 7 / 11px — so a
85
+ theme that scales the base keeps every step in proportion. `--zx-control-radius` still follows
86
+ `-lg`. Panels that used `-xl` (Launcher, Sheet, the AppSidebar and AppRail items) use `-lg` or
87
+ `-md`; ValueList chips and record-card metadata chips are no longer pills, and the SortControl
88
+ direction button is a control rather than a circle.
89
+ - **Controls are 30px tall** (`--zx-control-height`, from 32px). `--zx-control-pad-x` stays 10px
90
+ and text sizes are unchanged.
91
+ - **Structured menus are tighter.** AppSidebar rows are one control tall (from control + 8px) with
92
+ 2px between rows, 12px between groups, and a 3px corner. A text-only child starts under its
93
+ parent's label; a child with its own icon steps in 16px, just clear of the guide line, instead
94
+ of a whole icon column. The rail flyouts follow. TreeView indents 12px per level (from
95
+ 18px) with a 4px block padding per row. MenuButton, ContextMenu, Select, TagPicker, Launcher,
96
+ Finder, Checklist, and the Table column menu share one row: 4px × 8px padding, the smallest
97
+ radius, panels at `--zx-radius-lg`, and section labels no taller than an item.
98
+ - **"Current" is neutral, and nothing is docked to an edge.** Where you are — the active
99
+ navigation item (AppSidebar, its rails and flyouts), the open TreeView node, the current Finder
100
+ column entry, the active tab in every Tabbox variant, the pressed ListToolbar view, the current
101
+ Calendar view, Launcher app and Pagination page — is a neutral `--zx-color-bg-current` fill with
102
+ the text colour at weight 600; the edge marker beside the active item is gone. What you have
103
+ *picked* (checked rows, chosen options, a multi-selection) keeps the accent tint. A rail item
104
+ whose flyout holds the active route takes the accent colour. Forced colours still use the
105
+ system's selected pair.
106
+ - **One solid accent per screen.** Solid accent is for the primary button; tab underlines, pressed
107
+ toggles, and decorative icons no longer use it. In dark, the primary button sits on the accent
108
+ 600 step with white text (`--zx-color-accent-solid` / `--zx-color-on-accent-solid`) instead of
109
+ the bright 400 step.
110
+ - **Status green is its own colour.** `--zx-green-*` is a yellow-green status ramp that no longer
111
+ matches the default ZeyOS accent, so a "success" badge or chart segment never reads as branding.
112
+ The brand values moved to `--zx-emerald-*`, which the default accent points at.
113
+ - **Dark theme is layered.** Page, chrome, surface, raised, and overlay steps sit off near-black
114
+ (#1b1b1e → #313136), primary text is about 90% white, and borders are translucent white.
115
+ - **Quieter type.** Decorative upper-case and letter-spaced labels are gone from every component
116
+ (menu and Select groups, Launcher groups, StatTile labels, Calendar weekdays, Table headers, …);
117
+ row and label weights moved from 600 to 500, titles to 600–650, and UI text is set with
118
+ `--zx-tracking-tight` and Inter's `cv11`. Field labels and Fieldset legends are small, medium, and
119
+ muted, so values lead.
120
+ - **Tables** drop the header band (muted medium labels on the surface), divide rows with
121
+ `--zx-color-border-subtle`, and set numbers in tabular figures.
122
+ - **Avatars** fall back to a colour picked from the name (one of eight tones) with no ring, at
123
+ 24 / 32 / 40px, and show one initial below 24px.
124
+ - **StatTile** labels are sentence case, values 24px tabular, the icon is a muted glyph without a
125
+ coloured chip, and tiles in a row line up whether or not they carry a sparkline.
126
+ - **EmptyState** draws its icon in `--zx-color-text-subtle` and its title semibold.
127
+ - **Danger is legible as text.** Light `--zx-color-danger` moves to the 600 step and the dark
128
+ danger tint is lighter, so danger ink keeps 4.5:1 on every surface and on its own tint, as the
129
+ other status colours do (now tested for all four in both themes and every preset).
130
+ - **The dark selected tint is lighter** (13% of the accent instead of 18%), so muted text on a
131
+ checked row or chosen option keeps 4.5:1 under every preset (now tested, light and dark).
132
+ - **Toasts lose their coloured leading-edge bar**; the kind shows in the icon and a tinted border,
133
+ as everywhere else nothing is docked to an edge.
134
+ - **Button key hints** are quieter by colour and weight instead of opacity, which left them below
135
+ 4.5:1 on a solid primary button.
136
+ - **Select.entity** shows an entity without a colour as a neutral tile with a muted glyph; before,
137
+ the glyph was white on the pale accent tint and could not be seen.
138
+ - **Icons are Lucide outlines.** The bundled set renders as strokes in `currentColor` (ISC; a
139
+ 1.75-unit stroke per 24 that never thins below 1px), with every existing name kept. Bundles keep
140
+ the licence notice. A chosen Rating star is drawn solid.
141
+ - **The theme studio's Density section is now Spacing**: a slider for `--zx-space-unit` (3–6px).
142
+ The radius slider sets `--zx-radius` in pixels (0–10px), the standard recipes carry a spacing
143
+ unit instead of a density, Copy CSS exports the unit, and a theme saved by an earlier studio
144
+ still loads.
145
+
146
+ ### Added
147
+
148
+ - **Motion as a setting.** `data-zx-motion="reduced"` on any element stops motion inside it: the
149
+ duration tokens become `0ms` and a base rule stops CSS animations and transitions, while state
150
+ changes still apply; the system preference zeroes the tokens too. `prefersReducedMotion(element)`,
151
+ `watchMotion(element, fn, { signal, immediate })`, and `MOTION_ATTRIBUTE` let script follow it;
152
+ Chart passes `reducedMotion` to its adapter and `ChartJsAdapter` turns animation off under it.
153
+ - `--zx-ease-spring` (a small overshoot) and `--zx-ease-drawer` (a decisive ease-out) beside
154
+ `--zx-ease`, and `--zx-drag-shadow` for an item being dragged.
155
+ - **Launcher pages.** An item with `items` (an array, or a loader `(query, { signal })`) opens a
156
+ nested page: a scope chip in the input, Backspace, ArrowLeft, or × to go back, "⌫ Back" in the
157
+ footer, `open({ page })`, `back()`, `getPage()`, and a `page` event. Items take a `hint` (muted
158
+ text at the end of the row), `shortcut` key caps, and `keys` for `aria-keyshortcuts`.
159
+ - **Message `action`** (`{ label, onclick, kind }`, a small button that runs and closes the toast;
160
+ a toast with an action stays at least 8 seconds) and **`leading`** (a node such as an Avatar in
161
+ place of the kind icon).
162
+ - **`Dialog.confirm({ confirmText })`** — type-to-confirm: a labelled input that has focus, and a
163
+ confirm button and Enter that work only on an exact match.
164
+ - **Busy buttons.** `button({ busy })` and `setButtonBusy(button, busy)` show a spinner in place of
165
+ the icon, set `aria-busy`, and ignore activation while the action runs — but keep the button
166
+ focusable, so focus does not fall to the page as it does from a disabled button.
167
+ - **`statusIcon(status)` and `priorityIcon(level)`**, the glyphs `Select.status()` and
168
+ `Select.priority()` draw, for lists, boards, and filters; decorative unless given a `label`.
169
+ - `idPrefix` on Form and Fieldset; Field `name`, `field.name`, and `field.controlId`.
170
+ - Checklist `hideLabel`; TagPicker `toneKey` (default `'tone'`), colouring a tag the way a Badge
171
+ `tone` does; ActivityList `variant: 'compact'` for cards narrower than 30rem; Table column
172
+ `ariaLabel` for a blank or terse header; FileItem `typeLabel`; `Dialog.prompt` `okLabel` and
173
+ `cancelLabel`.
174
+ - `MenuButton.open({ returnFocus })` (also on `focusFirst()` and `focusLast()`) and
175
+ `Message.disposeRegion(scope)`.
176
+ - ZeyOS: `zeyosTable` passes Table options through (`variant`, `zebra`, `stickyHeader`,
177
+ `responsive`, `growing`, `emptyText`, `rowClass`, `rowKind(s)`, the column chooser, and the Table
178
+ listeners; editing, row reordering, and hierarchy stay out because the binding never persists
179
+ them); `timeZone` and per-field `formats` on `zeyosTable`, `zeyosForm`, `zeyosView`, and
180
+ `resolveFields`; `unixSecondsToCalendarDay()` and `calendarDayToUnixSeconds()`; `zeyosForm`
181
+ `scope` and `notify`.
182
+ - **Colour roles:** `--zx-color-bg-current`, `--zx-color-bg-chrome`, `--zx-color-border-subtle`,
183
+ `--zx-color-text-subtle`, the categorical tones `--zx-color-tone-{blue,teal,green,amber,orange,
184
+ red,pink,violet,slate}-{bg,fg}`, and the chart series `--zx-color-chart-1` … `-8`, whose first
185
+ series is deliberately not the accent. Contrast is tested in both themes and all six presets.
186
+ - **Type tokens:** `--zx-text-3xl` (30px), `--zx-weight-medium`, `--zx-weight-semibold`,
187
+ `--zx-tracking-tight`, `--zx-tracking-title`.
188
+ - **Quiet fields:** inside any `[data-zx-fields="quiet"]` container, native fields and Select,
189
+ TagPicker, Datebox, DateRangeBox, NumberField, and Timebox show their frame only on hover and
190
+ focus — property panels read as data, not as a wall of boxes.
191
+ - `Table` / `TableView` `variant: 'plain'` (no frame, no header band) for tables inside cards;
192
+ `badge({ tone })` with the categorical tones (and `BADGE_TONES`); `Avatar` `tone`, `avatarTone()`
193
+ and `AVATAR_TONES`; `StatTile` `caption`, read with the value.
194
+ - `registerIcons()` accepts outline glyphs as `[viewBox, path, 'stroke']`; `[viewBox, path]` pairs
195
+ still render filled.
196
+ - `Tabbox` `label`, the tab list's accessible name.
197
+
198
+ ### Fixed
199
+
200
+ - A destructive `Dialog.confirm` started focus on the destructive button; it starts on Cancel, the
201
+ least destructive choice (type-to-confirm starts in its input).
202
+ - On a phone, toasts covered the header's controls; they rise from the foot of the screen there.
203
+ - A ghost Button showed no focus ring: its `box-shadow: none` outranked `:focus-visible`.
204
+ - Select's loading spinner never turned (it read an undefined duration token).
205
+ - The Launcher's search row could push its close button off a narrow screen.
206
+ - A `[data-zx-theme="dark"]` region on a light page inherited light glass, overlays, hover and
207
+ current fills, tone tints, and focus ring: colours derived from other theme colours were resolved
208
+ on `:root` only. They are restated on every theme element now; geometry overrides on `:root`
209
+ still reach nested regions.
210
+ - Overlays (Dropdown, Tooltip, MenuButton, ContextMenu, Modal, Dialog, Sheet, Launcher) created before their
211
+ anchor or scope was in the document parked their panel on `<body>`, outside the theme scope; they
212
+ find their host on first open.
213
+ - Choosing a MenuButton item dropped focus to `<body>`; it returns to the trigger. CodeEditor
214
+ trapped the keyboard; Escape then Tab leaves it, as documented.
215
+ - Floating Message regions kept sending toasts into an emptied host and were never removed.
216
+ - Table and Grid number and date editors select their content on entry (typing "6" over "1.00"
217
+ gives 6); sortable headers use the same font as the others; no scrollbar gutter is reserved on a
218
+ table that does not scroll; a blank header has an accessible name. TreeView type-ahead calls
219
+ `preventDefault` and leaves text controls in rows alone.
220
+ - Table's selection checkboxes and row-reorder handles sit in the middle of their cell. They rode a
221
+ few pixels high on the text baseline, and the cell padding pushed them off the centre of their
222
+ control-height column, in a fixed layout past its edge.
223
+ - NumberField, Datebox, and DateRangeBox fill a Field's control column; clear-button padding is
224
+ reserved only when `clearable`, and a disabled Datebox or DateRangeBox shows no clear button.
225
+ Slider end labels stay inside the component. A Checklist inside a Field no longer repeats its
226
+ heading.
227
+ - `colorKey` on `Select.entity()` and TagPicker accepts token references (`var(--zx-…)`). Clicking a
228
+ read-only Select no longer draws the keyboard focus ring; a disabled Select fades like a disabled
229
+ Field; a read-only TagPicker shows only its tags.
230
+ - `collapsible: false` still showed AppSidebar's minimise button. copyButton's status line and
231
+ CopyInput's hidden label were positioned outside their control.
232
+ - A Field whose adapter returns a wrapper (a `custom` field holding a component) named the
233
+ wrapper, which cannot carry a name; its label, description, and validity now reach the first
234
+ unnamed form control inside it, or the adapter's new `labelTarget`.
235
+ - Launcher ran an item's command before closing, so closing put focus back on an element the
236
+ command had just redrawn or moved focus away from; it closes first now.
237
+ - A MenuButton's menu had no accessible name; it is labelled by its trigger, as the APG pattern
238
+ has it. NumberField no longer swallows Mod+Enter (and other chords), so a form or dialog's submit
239
+ shortcut works from a number field. Datebox and DateRangeBox were 2px taller than the text input
240
+ beside them; every control is one control height.
241
+ - A Tooltip placed beside an anchor near the edge of the screen squeezed to a word per line
242
+ instead of moving: it now takes the other side, or goes above or below when neither side fits.
243
+ - A Chart in a narrow card overflowed it (its 220px minimum height became a 587px minimum width
244
+ through the aspect ratio); it grows taller instead. A StatTile delta keeps its figure on one line
245
+ in a narrow tile.
246
+ - `zeyosTable` showed a due date as a date-time whose hour moved with daylight saving, and
247
+ `zeyosForm` edited it as one; `zeyosForm.save()` raised its toast wherever focus was.
248
+ - The AccountMenu trigger rendered as a centred, boxed card because `menu-button.css` loaded after
249
+ `account-menu.css`; it is a flat row again.
250
+ - CodeEditor referenced `--zx-color-bg-subtle`, which never existed.
251
+ - The Marque Gold preset's accent text now reaches 4.5:1 on the light chrome tone.
252
+ - A sortable end-aligned Table column put its header label at the start, away from its figures;
253
+ the label now sits at the end with the sort arrow before it.
254
+ - ActivityItem's heading read the actor and title as one word ("Jonas Weberset…") to assistive
255
+ technology; a space now separates them.
256
+ - Table's Columns chooser, RecordView's Fields chooser, KanbanView's undo and redo, and Filter's
257
+ actions used a `zx-button` class no stylesheet defines, so they rendered as bare browser buttons;
258
+ they are small Zx buttons now (ghost in toolbars, primary for Apply).
259
+
260
+ ### Website
261
+
262
+ - The application layouts moved from the documentation sidebar to a gallery of their own
263
+ (`layouts.html`), with a live thumbnail per layout, a near-full-screen view with its source, and a
264
+ standalone page (`layout.html#<id>`) for opening one in its own tab. Nine complete application
265
+ templates join the eight screen patterns, which are rebuilt on the same application shell.
266
+ - The documentation, gallery, and theme studio share the library's look: navigation on the chrome
267
+ tone, neutral current entries, sentence-case labels, hairline example cards, and code and API
268
+ tables drawn like the refined Table.
269
+ - New in the gallery: an **agent workspace** template — agents with live presence, editor tabs, a
270
+ plan and execution log with hand-offs, approvals as toasts that carry their action, a command
271
+ palette with pages, a bottom panel, and a status bar — and two screen patterns, **agent chat**
272
+ (tool calls, an approval that blocks the run, sources, and the run beside the thread) and
273
+ **system states** (every empty, loading, error, validation, and confirmation state, with a
274
+ switch to go offline). The AI chat template shows its work: tool steps, sources, artifacts with
275
+ versions, regenerate and branch, and `@agent` mentions.
276
+ - Every layout works from a phone to a wide screen: below 1180px the inspector and below 860px
277
+ the navigation become drawers, and on a phone a layout shows its list or its record with a way
278
+ back. All seventeen pass an accessibility audit — named controls and landmarks, text contrast,
279
+ visible focus, heading order — in both themes, wide and narrow.
280
+ - The theme studio has a Motion setting (System or Reduced), kept across the whole site as a
281
+ reader's preference, and a Button states card showing every kind in every state.
282
+ - The site self-hosts Inter as a variable font (SIL OFL), so every weight renders as designed and no
283
+ font request leaves the site.
284
+ - The theme studio offers more to build from. Eleven neutral tints — Neutral, Zinc, Stone, Cool,
285
+ Mist, Sage, Olive, Warm, Taupe, Mauve, and Accent — are picked from swatches of the ramp each
286
+ would produce, and a strength control scales any of them from grey to twice its colour; every
287
+ tint keeps the text roles legible at full strength. The typeface list grows from six system
288
+ stacks to eleven, drawn in their own type, plus a custom stack for a brand font, and the code
289
+ font (`--zx-font-mono`) is a choice of its own.
290
+ - Themes can be saved and loaded as JSON. Export writes a `zx-theme` file — the settings, plus the
291
+ custom properties they produce — to copy or download; Import takes one pasted, dropped, or chosen,
292
+ loads it even when a value is out of range or unknown, and says what it adjusted.
293
+
294
+ ## 4.4.0 — 2026-09-22
295
+
296
+ A large release in two halves. The first adds capability: a dependency-free code editor, the
297
+ record primitives (`EntityRef`, `FileItem`/`FileList`, `ActivityItem`/`ActivityList`), kanban
298
+ movement and workflow policy, and richer action menus. The second is what building a real
299
+ application on the library found missing — group rows, list controls, a filter panel driven by
300
+ server metadata, a metric tile, and the long tail of strings a translated product could not
301
+ reach.
302
+
303
+ Nothing here is a breaking change. Every new option defaults to the previous behaviour, and every
304
+ English string a translator can now reach keeps its exact previous wording when no translator is
305
+ installed.
306
+
307
+ ### Added
308
+
309
+ - **Structured text no longer requires an editor dependency.** `CodeEditor` keeps source in a
310
+ native textarea with browser undo and IME behavior, adds Tab/Shift+Tab indentation, language
311
+ metadata, line/column feedback, read-only and disabled states, and a `code` Form field adapter.
312
+ It never executes or injects the value; rich HTML authoring remains a separate policy surface.
313
+ - **Reusable record primitives cover dense identity, files, and activity.** `EntityRef` presents
314
+ linked or static identity with labelled metadata and injected actions; `FileItem`/`FileList`
315
+ cover durable, temporary, waiting, progress, MIME, size, download, loading, and empty states;
316
+ `ActivityItem`/`ActivityList` cover actor, time, content, attachments, actions, stable grouping,
317
+ and incremental feed updates. Zx owns safe presentation and cancelable interaction boundaries;
318
+ applications retain routing, authorization, transport, posting, reactions, and persistence.
319
+ - **Action menus now carry business metadata without becoming free-form popovers.** MenuButton and
320
+ ContextMenu share headings, object separators, safe native links, descriptions, badges,
321
+ shortcuts, text/Node adornments, and checked/radio menu-item roles. Existing flat items and
322
+ `'-'` separators are unchanged.
323
+ - **Kanban cards carry the anatomy an operational board needs.** New read-only projections put an
324
+ `entityIcon` and `identifier` on a card head beside `status` and `priority` indicators, and a
325
+ `progress` meter and `assignees` avatars on a card footer. `priorityTones` and `statusTones` map
326
+ a value to a semantic tone as data rather than through a renderer, `progressMax` sets the
327
+ completion scale explicitly, and `maxAvatars` bounds the avatar stack. A board that sets none of
328
+ them renders exactly the card it rendered in 4.3.4.
329
+
330
+ - **`FilterPanel` — typed filters from server metadata.** Takes the `{key: {type, label,
331
+ options, min, max}}` map a list endpoint already publishes and returns a query object. Ten
332
+ field types including both range forms, multi-valued selects on `Checklist`, draft-then-apply
333
+ by default, and `serialize` for the Unix-seconds / ISO / epoch-ms question. `DataFilter`
334
+ filters a client-side row set and `Filter` builds an AST; this is the third shape, and the one
335
+ a data-driven application needs.
336
+ - **`ListToolbar` — the bar above a record view.** Search, a live result count, a view switch and
337
+ badged tools. A tool's badge is part of its accessible name rather than a second thing beside
338
+ it, `setCount(null)` clears the live region during a load, and `bind()` feature-detects a
339
+ `TableView`/`CardView` instead of importing it.
340
+ - **`StatTile` / `statTile()` — the labelled metric.** Formats through the core formatters, never
341
+ encodes "up is good" (`deltaKind` decides the colour, not the sign), marks its sparkline
342
+ `aria-hidden`, and picks `<a>` / `<button>` / `<div role="group">` from the behaviour it was
343
+ given rather than wrapping a click handler round a div.
344
+ - **`Table` group and section rows.** `rowKind` and `rowKinds` render a `<th scope="colgroup">`
345
+ spanning every rendered cell, including the selection and reorder columns. `CardView` has had
346
+ `groupBy`; `Table` had nothing, while `Grid.BillingItems()` already modelled the row kind for
347
+ editability and simply never drew it. Sort controls are withheld while a spanning row is
348
+ present, because sorting a grouped table scatters its positions out of their sections.
349
+ - **`ActivityItem.state`** — `pending` and `failed`, for an entry rendered optimistically before
350
+ its request resolves and for one whose send failed. An entry that disappears on failure tells
351
+ the reader their message was sent when nobody was told.
352
+
353
+ ### Changed
354
+
355
+ - **The move handle is no longer pointer furniture.** Since 4.3.4 the card itself is the drag
356
+ source, so the knob is decoration that competes with the card's content. The new
357
+ `moveHandle: 'keyboard' | 'always'` defaults to `keyboard`: the button stays in the DOM, in tab
358
+ order, and fully announced, but is clipped out of sight and out of the layout until it takes
359
+ focus or holds a grabbed card. `always` restores the 4.3.4 presentation, and `dragFrom: 'handle'`
360
+ keeps the handle visible regardless, since it is then the only thing a pointer user can grab.
361
+ - **An empty column now reads as a drop target.** `labels.columnEmpty` defaults to
362
+ "Drop a card here" in a dashed zone instead of an empty gap; `labels.columnEmpty: ''` restores
363
+ the previous blank column, and `renderColumnEmpty` still replaces it.
364
+ - **A field the card head or footer projects no longer repeats itself as a metadata row.** Set
365
+ `duplicate: true` on the field descriptor to show it in both places.
366
+
367
+ - **`fieldControls` accepts `{label, target}`** on `TableView`, `CardView` and `KanbanView`.
368
+ `true`/`false` are unchanged. The chooser's trigger can now be named, and mounted into a host
369
+ toolbar instead of the view's own — which is what made it usable in a translated product at
370
+ all.
371
+ - **`DialogButton.disabled`**, honoured by the constructor, `setButtons()` and per-view buttons,
372
+ and skipped by `autofocus`. `Sheet` inherits it. A form a client cannot render completely must
373
+ not be submittable, and until now the only way to show that was to reach into the rendered
374
+ footer.
375
+ - **Roughly ninety-six user-visible strings across sixteen components now route through
376
+ `_message()`**, which falls back to `translate()` — so installing a host translator is enough.
377
+ Most are `aria-label` and `title`, which is the half that does not show up in a screenshot: a
378
+ German application previously announced "Clear search", "Close message" and "Previous month" in
379
+ the middle of its own chrome, with no option to prevent it. Every English fallback is
380
+ byte-identical to the previous literal.
381
+ - **`Launcher` and `Filter` label options default to `null`** and resolve through the translator,
382
+ so an application that installs one and passes no labels gets a translated component. The
383
+ rendered words are unchanged without a translator.
384
+ - **`AppSidebar` visual pass.** A filled active pill with an edge marker and a weight change, so
385
+ nothing rests on colour alone; one spacing scale; a fixed icon gutter that collapses when no
386
+ item has an icon; labels that wrap and hyphenate rather than truncate; badges that survive
387
+ minimization as a dot with the number still in the accessible name. Options, events, methods,
388
+ DOM and the keyboard map are unchanged.
389
+ - **`NavigationBar` overflow is configurable.** `overflow: false` keeps every item at every
390
+ width; `overflowBelow` moves the collapse threshold; `minVisible: n` pins the first `n` items.
391
+ Defaults are unchanged — every item still collapses below 44 rem through the same container
392
+ query, with no observer installed. Collapsing all of a navigation into one button is right for
393
+ a top app bar and wrong for a phone bottom bar, and there was no way to say so.
394
+ - **A print stylesheet ships.** `styles/print.css`, imported last, releases sticky headers and
395
+ scroll containers, drops fills, shadows, zebra striping and focus rings, hides overlays and
396
+ navigation chrome, repeats table headers across pages, keeps cards and rows from breaking, and
397
+ prints link URLs in prose but never inside a table cell or on a button. Status badges keep
398
+ their colour, because there it carries meaning beside the word. An application that sets
399
+ `data-zx-theme="dark"` explicitly still prints dark text tokens; `auto` is unaffected.
400
+
401
+ ### Fixed
402
+
403
+ - **Context-menu selection could not be cancelled.** Preventing its `select` event now suppresses
404
+ the item callback and native link navigation and leaves the menu open, matching MenuButton.
405
+ - **The keyboard move handle could take focus while staying invisible.** It revealed itself on
406
+ `:focus-visible`, which does not match after pointer input — so the focus a committed pointer
407
+ drop moves onto the handle landed on a one-pixel control. It now reveals on `:focus`, and
408
+ `dragFrom: 'handle'` keeps the handle visible regardless of `moveHandle`.
409
+ - **A literal indicator descriptor rendered nothing.** `priority: {label: 'Blocked'}` was read as
410
+ a record property name and resolved `undefined`.
411
+ - **A blank progress label produced an unnamed progressbar.** An empty or whitespace label now
412
+ falls back to the localized default instead of becoming `aria-label=""`.
413
+ - **A fractional `maxAvatars` hid every face** while the overflow chip still counted from the raw
414
+ option. The faces and the chip now come from one normalization.
415
+ - **The card identifier named itself with `aria-label` on a `<span>`**, which the generic role
416
+ prohibits. Identifier and indicator prefixes are visually hidden text instead.
417
+ - **An assignee with an image but no name rendered an unidentifiable face.** Entries without a
418
+ name are now dropped rather than announced as "Assigned to ".
419
+ - **Board rendering rebuilt the whole field-descriptor array for every axis read.** A string
420
+ column or lane accessor was resolved through `getFields()` — which clones every descriptor —
421
+ once per record per column per lane. Descriptors are now indexed once per refresh.
422
+
423
+ - **`Modal` and `Dialog` place and restore focus.** Both have always built a native `<dialog>`
424
+ and opened it with `showModal()`, so focus containment, page inertness, Escape and the top
425
+ layer were the platform's — a reading of the source that greps for a focus trap and finds
426
+ none draws the wrong conclusion, and one did. What was genuinely missing is now there:
427
+ initial focus lands on the first focusable control inside the panel rather than on the panel,
428
+ focus returns to the opener on every close path including the non-native presentation, and
429
+ `aria-modal` is set while modal. `Sheet` keeps its own trap for the non-modal case and is not
430
+ double-trapped. Candidates inside a `hidden`, `aria-hidden` or `inert` subtree are now skipped,
431
+ so a dialog whose close button is suppressed focuses its first real control instead of nothing.
432
+ - **`icon()` no longer throws on a name it cannot resolve.** Icon names routinely arrive from a
433
+ server, and one unconfigured name should not take down the screen it appears on. An
434
+ unresolvable inline glyph renders an empty `<svg>` of the requested size — so layout does not
435
+ shift — carrying `data-zx-icon-missing="<name>"`, and reports itself once per distinct name.
436
+ `icon(name, { strict: true })` keeps the old `RangeError` for callers that can afford to fail.
437
+ Font Awesome paths are unaffected: a kit resolves names in the browser, so nothing under one
438
+ is "unknown" here and nothing warns.
439
+ - **The minimized rail's flyout matches the expanded sidebar.** The visual pass in this release
440
+ could not reach it — the panel is portaled into a `Dropdown`, so the sidebar's descendant
441
+ selectors do not apply to it. It now carries the same spacing scale, icon gutter, wrapping
442
+ labels and active-item treatment, derived from the same tokens. Fixing it surfaced a cascade
443
+ bug: `app-rail.css` is imported before `dropdown.css`, so the popover's own padding had never
444
+ won and the panel had been rendering with the generic overlay's.
445
+ - **One physical-direction declaration** remained in the component tree (`table.css`), plus four
446
+ asymmetric box shorthands that pin an edge physically. All are logical now, and a test scans
447
+ for both so the state is enforced rather than accidental.
448
+
11
449
  ## 4.3.4 — 2026-08-31
12
450
 
13
451
  ### Added
package/README.md CHANGED
@@ -80,6 +80,10 @@ namespaced `storage()` view that degrades to memory when the browser refuses wri
80
80
  `toCsv`/`downloadBlob`/`copyToClipboard` for exporting what a table is showing.
81
81
 
82
82
  `icon()` renders the inline SVG glyphs Zx bundles — no webfont, stylesheet, or network request.
83
+ They are outline icons from [Lucide](https://lucide.dev) (ISC licence; the notice ships in
84
+ `src/core/icons.js`): `currentColor` strokes on a 24-unit grid, about 1.2px at the default 16px.
85
+ `registerIcons()` adds more, either filled (`[viewBox, path]`, e.g. Font Awesome paths) or outlined
86
+ (`[viewBox, path, 'stroke']`, e.g. further Lucide icons).
83
87
  Applications that want Font Awesome call `loadFontAwesome({ kit })` once, and every icon in the
84
88
  library follows, including names Zx maps to their Font Awesome counterparts. Names can also pick a
85
89
  renderer themselves: `'fa:user'`, `'duotone:user'`, `'kit:zeyos-notes'`, `'builtin:check'`, or a
@@ -127,6 +131,9 @@ ZeyOS binding — see `src/zeyos/modules.js`.
127
131
 
128
132
  | API | Description |
129
133
  | --- | --- |
134
+ | `EntityRef` | Dense linked/static entity identity with labelled metadata and injected actions. |
135
+ | `FileItem`, `FileList` | Durable and transient file presentation with MIME/size metadata, status, progress, downloads, and collection states. |
136
+ | `ActivityItem`, `ActivityList` | Chronological activity entries and grouped collections with actor, time, attachment, action, loading, and empty slots. |
130
137
  | `Table` | Sortable/selectable table with typed transaction columns, flat-data hierarchy/treegrid projection, and opt-in cell/row editing. |
131
138
  | `TableView` | Record-oriented Table composition with shared fields, portable state, column visibility/order, sorting, and selection. |
132
139
  | `CardView` | Responsive record cards with configurable fields, safe preview media, grouping, actions, sorting, and selection. |
@@ -135,6 +142,8 @@ ZeyOS binding — see `src/zeyos/modules.js`.
135
142
  | `Chart`, `ChartJsAdapter` | Accessible engine-neutral chart host and explicitly injected Chart.js adapter. |
136
143
  | `Calendar` | Agenda/day/week/month/year scheduling with timed collisions, spanning events, range selection, and configurable optimistic drag/resize editing. |
137
144
  | `DataFilter` | Declarative select, text, and custom filters for client-side row sets. |
145
+ | `FilterPanel` | Typed filter form built from a server's own field metadata, drafted then applied. |
146
+ | `ListToolbar` | Search, result count, view switch, and badged tools above a record view. |
138
147
  | `TreeView` | APG tree with lazy children, filtering, and tri-state checkboxes. |
139
148
  | `Finder` | Miller-columns hierarchy browser with an optional preview pane. |
140
149
  | `Pagination` | Page, page-size, and load-more pager, with the pure `paginationRange()` helper. |
@@ -146,6 +155,7 @@ ZeyOS binding — see `src/zeyos/modules.js`.
146
155
  | `Field` | Label, description, validation state, and adapter-backed form control. |
147
156
  | `Fieldset` | Native fieldset that owns and coordinates fields. |
148
157
  | `Form` | Form composition, values, validation, highlighting, and actions. |
158
+ | `CodeEditor` | Text-safe structured-text/source editor with native undo, Tab indentation, and position feedback. |
149
159
  | `Questionnaire` | Guided one-question-at-a-time flow: branching, path-aware progress, review. |
150
160
  | `ValueList` | Validated, sortable tag/chip editor. |
151
161
  | `MultiValueEditor` | Ordered editable rows with add, remove, and move controls. |
@@ -160,6 +170,7 @@ ZeyOS binding — see `src/zeyos/modules.js`.
160
170
  | `Avatar` | Fixed-size user image with initials fallback and optional presence. |
161
171
  | `Groupbox` | Collapsible section backed by native `<details>`. |
162
172
  | `Card` | Semantic content/record surface with media, native title link, actions, and footer. |
173
+ | `StatTile`, `statTile()` | Labelled metric with optional delta, sparkline, and loading state. |
163
174
  | `Aurora` | One- to four-colour ambient canvas light; chrome, raised content, and overlays respond through shared material tokens. |
164
175
  | `Launcher` | Cmd/Ctrl+K application and record launcher with ranked local items and abortable grouped sources. |
165
176
  | `Panel` | Framed, optionally collapsible section with a footer. |
@@ -226,13 +237,13 @@ moduleColor('invoices'); // '#535494'
226
237
  name, identity colour, label, and a stock Font Awesome fallback. The `--zx-module-*` CSS custom
227
238
  properties are generated from that file, so CSS and JavaScript cannot disagree about a colour.
228
239
 
229
- ## Themes and density
240
+ ## Themes
230
241
 
231
242
  Load `zx.css` once. Zx components consume semantic `--zx-*` tokens and inherit their values from
232
- the document. Select a built-in theme and density on any ancestor (normally `<html>`):
243
+ the document. Select a built-in theme on any ancestor (normally `<html>`):
233
244
 
234
245
  ```html
235
- <html data-zx-theme="dark" data-zx-density="compact">
246
+ <html data-zx-theme="dark">
236
247
  ```
237
248
 
238
249
  Top-layer menus, dialogs, tooltips, launchers, and toast regions stay under the nearest Zx theme
@@ -240,10 +251,22 @@ scope in the DOM, so a theme or product-token override on a nested application s
240
251
  to its overlays. Pass the optional `scope` context to Modal, Dialog, Sheet, Launcher, or static
241
252
  Message calls when the opener is not inside the intended scope.
242
253
 
243
- Theme values are `light`, `dark`, and `auto`; density values are `cozy` and `compact`. `auto`
244
- follows the operating-system color preference.
254
+ Theme values are `light`, `dark`, and `auto`. `auto` follows the operating-system color
255
+ preference.
245
256
 
246
- A third attribute picks the **accent**, orthogonally to the other two:
257
+ There is one geometry. Spacing steps are multiples of `--zx-space-unit` (4px:
258
+ 4, 8, 12, 16, 20, 24, 32, 40), radius steps are fractions of `--zx-radius` (4px: 2, 3, 4, 6), and
259
+ controls are `--zx-control-height` (30px) tall. To tighten or loosen an application — or one
260
+ region of it — override those tokens rather than switching a mode:
261
+
262
+ ```css
263
+ :root {
264
+ --zx-space-unit: 3.5px;
265
+ --zx-control-height: 28px;
266
+ }
267
+ ```
268
+
269
+ A second attribute picks the **accent**, orthogonally to the theme:
247
270
 
248
271
  ```html
249
272
  <html data-zx-theme="dark" data-zx-preset="ocean">
@@ -265,6 +288,8 @@ through, so a preset rethemes light, dark, and auto at once:
265
288
 
266
289
  Light surfaces take the 600 and 700 stops, dark ones take 300, 400, and 500 — so keep 600 legible
267
290
  against white and 400 against near-black, and `--zx-color-on-accent` stays correct in both. The
291
+ primary button is the exception on purpose: `--zx-color-accent-solid` is the 600 stop with white
292
+ ink in both themes, so the one solid accent on a screen never turns into a bright slab in dark. The
268
293
  [theme studio](https://zx.zeyos.com/theme.html) renders every component family under a live theme and
269
294
  exports the result.
270
295
 
@@ -285,7 +310,20 @@ Anything beyond the accent is a semantic token override, without reaching into c
285
310
 
286
311
  Prefer semantic tokens such as `--zx-color-*`, `--zx-control-*`, `--zx-space-*`,
287
312
  `--zx-radius-*`, and `--zx-text-*`. Palette tokens are implementation details and should not be
288
- used by application or component CSS.
313
+ used by application or component CSS. A few roles carry most of the visual language:
314
+
315
+ - `--zx-color-bg-chrome` for navigation and inspector columns beside the `--zx-color-bg-surface`
316
+ canvas, and `--zx-color-border-subtle` for dividers inside them — tone separates, lines only
317
+ where tone cannot.
318
+ - `--zx-color-bg-current` (neutral) for where you are, `--zx-color-bg-selected` (accent tint) for
319
+ what you picked.
320
+ - `--zx-color-text-muted` for secondary text and `--zx-color-text-subtle` for icons and meta.
321
+ - `--zx-color-tone-<hue>-bg` / `-fg` (blue, teal, green, amber, orange, red, pink, violet, slate)
322
+ for categorical colour — tags, avatars, icon tiles — and `--zx-color-chart-1` … `-8` for chart
323
+ series. Neither is the accent or a status colour; `success` is a yellow-green of its own.
324
+
325
+ Add `data-zx-fields="quiet"` to a property panel and its native fields show no border or fill until
326
+ hovered or focused.
289
327
 
290
328
  ## Browser support
291
329
 
@@ -304,9 +342,10 @@ npm install
304
342
  npm run serve
305
343
  ```
306
344
 
307
- Open <http://127.0.0.1:8321/website/docs.html> for the documentation — getting started, every
308
- component with a live demo and its JavaScript source, and the application layouts. Also available: <http://127.0.0.1:8321/tests/smoke/smoke.html> for source-module smoke
309
- tests, or build first and open <http://127.0.0.1:8321/tests/smoke/smoke-dist.html> for
345
+ Open <http://127.0.0.1:8321/website/docs.html> for the documentation — getting started, and every
346
+ component with a live demo and its JavaScript source — and
347
+ <http://127.0.0.1:8321/website/layouts.html> for the gallery of application layouts. Also
348
+ available: <http://127.0.0.1:8321/tests/smoke/smoke.html> for source-module smoke tests, or build first and open <http://127.0.0.1:8321/tests/smoke/smoke-dist.html> for
310
349
  classic-script distribution tests.
311
350
 
312
351
  ```sh