@bitforgehq/ui 0.3.0 → 0.3.2

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 (158) hide show
  1. package/CHANGELOG.md +598 -0
  2. package/README.md +18 -11
  3. package/ai/MANIFEST.index.json +306 -0
  4. package/ai/MANIFEST.json +8915 -3788
  5. package/ai/REFERENCE.index.json +476 -0
  6. package/ai/REFERENCE.json +5810 -2905
  7. package/ai/UI_GUIDE.md +212 -58
  8. package/ai/components/app-frame.md +328 -0
  9. package/ai/components/back-button.md +42 -2
  10. package/ai/components/badge.md +17 -7
  11. package/ai/components/bar-chart.md +188 -0
  12. package/ai/components/breadcrumb-item.md +121 -0
  13. package/ai/components/breadcrumb.md +277 -0
  14. package/ai/components/button.md +82 -15
  15. package/ai/components/calendar.md +300 -0
  16. package/ai/components/card.md +119 -4
  17. package/ai/components/chart-card.md +283 -0
  18. package/ai/components/combobox.md +411 -0
  19. package/ai/components/date-input.md +396 -0
  20. package/ai/components/datetime-input.md +215 -0
  21. package/ai/components/description-item.md +111 -0
  22. package/ai/components/description-list.md +160 -0
  23. package/ai/components/donut-chart.md +169 -0
  24. package/ai/components/empty-state.md +99 -6
  25. package/ai/components/header.md +71 -2
  26. package/ai/components/icon-button.md +74 -16
  27. package/ai/components/icon.md +63 -7
  28. package/ai/components/line-chart.md +234 -0
  29. package/ai/components/list.md +70 -6
  30. package/ai/components/menu.md +5 -0
  31. package/ai/components/multi-combobox.md +257 -0
  32. package/ai/components/nav-group.md +178 -0
  33. package/ai/components/nav-item.md +210 -0
  34. package/ai/components/page-header.md +237 -0
  35. package/ai/components/pagination.md +173 -0
  36. package/ai/components/popover.md +250 -0
  37. package/ai/components/side-panel.md +391 -0
  38. package/ai/components/sidebar-toggle.md +287 -0
  39. package/ai/components/sidebar.md +541 -0
  40. package/ai/components/skeleton.md +215 -0
  41. package/ai/components/sparkline.md +188 -0
  42. package/ai/components/stat.md +297 -0
  43. package/ai/components/tab-group.md +226 -0
  44. package/ai/components/tab-panel.md +163 -0
  45. package/ai/components/table.md +697 -0
  46. package/ai/components/tabs.md +46 -2
  47. package/ai/components/theme-menu-button.md +182 -0
  48. package/ai/components/theme-switch.md +170 -0
  49. package/ai/components/time-input.md +240 -0
  50. package/ai/components/toolbar.md +354 -0
  51. package/ai/components/tooltip.md +230 -0
  52. package/ai/components/top-nav.md +159 -0
  53. package/ai/reference/bf-app-frame-breakpoint.md +27 -0
  54. package/ai/reference/bf-app-frame-content-width.md +29 -0
  55. package/ai/reference/bf-badge-appearance.md +36 -0
  56. package/ai/reference/bf-badge-variant.md +1 -1
  57. package/ai/reference/bf-bar-chart-orientation.md +27 -0
  58. package/ai/reference/bf-button-appearance.md +36 -0
  59. package/ai/reference/bf-button-variant.md +1 -1
  60. package/ai/reference/bf-calendar-disabled-date.md +23 -0
  61. package/ai/reference/bf-calendar-harness.md +51 -0
  62. package/ai/reference/bf-calendar-weekday.md +31 -0
  63. package/ai/reference/bf-chart-card-heading-level.md +26 -0
  64. package/ai/reference/bf-chart-curve.md +28 -0
  65. package/ai/reference/bf-chart-harness.md +51 -0
  66. package/ai/reference/bf-chart-legend-position.md +30 -0
  67. package/ai/reference/bf-chart-series.md +23 -0
  68. package/ai/reference/bf-chart-value-format.md +23 -0
  69. package/ai/reference/bf-color-scheme-service.md +30 -0
  70. package/ai/reference/bf-color-scheme.md +31 -0
  71. package/ai/reference/bf-combobox-filter.md +27 -0
  72. package/ai/reference/bf-combobox-harness.md +89 -0
  73. package/ai/reference/bf-combobox-option.md +33 -0
  74. package/ai/reference/bf-compare-with.md +2 -0
  75. package/ai/reference/bf-date-input-harness.md +67 -0
  76. package/ai/reference/bf-date-input-picker.md +27 -0
  77. package/ai/reference/bf-date-time-input-harness.md +76 -0
  78. package/ai/reference/bf-date-time-input-picker.md +27 -0
  79. package/ai/reference/bf-description-list-layout.md +26 -0
  80. package/ai/reference/bf-donut-slice.md +22 -0
  81. package/ai/reference/bf-empty-state-heading-level.md +1 -1
  82. package/ai/reference/bf-empty-state-heading.md +33 -0
  83. package/ai/reference/bf-labels.md +73 -20
  84. package/ai/reference/bf-menu-button-variant.md +1 -0
  85. package/ai/reference/bf-menu-placement.md +1 -0
  86. package/ai/reference/bf-overlay-options.md +28 -27
  87. package/ai/reference/bf-overlay-placement.md +34 -0
  88. package/ai/reference/bf-overlay-position.md +1 -1
  89. package/ai/reference/bf-overlay-service.md +5 -3
  90. package/ai/reference/bf-page-header-heading-level.md +26 -0
  91. package/ai/reference/bf-pagination-harness.md +61 -0
  92. package/ai/reference/bf-platform-service.md +17 -15
  93. package/ai/reference/bf-popover-harness.md +42 -0
  94. package/ai/reference/bf-popover-placement.md +28 -0
  95. package/ai/reference/bf-popover-trigger-directive.md +56 -0
  96. package/ai/reference/bf-side-panel-config.md +35 -0
  97. package/ai/reference/bf-side-panel-harness.md +34 -0
  98. package/ai/reference/bf-side-panel-ref.md +44 -0
  99. package/ai/reference/bf-side-panel-service.md +43 -0
  100. package/ai/reference/bf-side-panel-template-context.md +32 -0
  101. package/ai/reference/bf-side-panel-width.md +28 -0
  102. package/ai/reference/bf-sidebar-state-service.md +21 -0
  103. package/ai/reference/bf-sidebar-toggle-harness.md +32 -0
  104. package/ai/reference/bf-size.md +13 -0
  105. package/ai/reference/bf-skeleton-shape.md +28 -0
  106. package/ai/reference/bf-stat-appearance.md +33 -0
  107. package/ai/reference/bf-stat-trend-intent.md +27 -0
  108. package/ai/reference/bf-stat-trend.md +27 -0
  109. package/ai/reference/bf-stat-variant.md +26 -0
  110. package/ai/reference/bf-tab-group-harness.md +44 -0
  111. package/ai/reference/bf-table-align.md +26 -0
  112. package/ai/reference/bf-table-cell-context.md +24 -0
  113. package/ai/reference/bf-table-cell-directive.md +22 -0
  114. package/ai/reference/bf-table-column.md +28 -0
  115. package/ai/reference/bf-table-harness.md +77 -0
  116. package/ai/reference/bf-table-row-key.md +21 -0
  117. package/ai/reference/bf-table-sort-direction.md +26 -0
  118. package/ai/reference/bf-table-sort.md +27 -0
  119. package/ai/reference/bf-table-stack.md +29 -0
  120. package/ai/reference/bf-theme.md +5 -4
  121. package/ai/reference/bf-time-input-harness.md +67 -0
  122. package/ai/reference/bf-time-input-hour-cycle.md +27 -0
  123. package/ai/reference/bf-time-input-picker.md +27 -0
  124. package/ai/reference/bf-tooltip-directive.md +43 -0
  125. package/ai/reference/bf-tooltip-harness.md +43 -0
  126. package/ai/reference/bf-tooltip-placement.md +28 -0
  127. package/ai/reference/bf_color_scheme_storage.md +20 -0
  128. package/ai/reference/bf_side_panel_data.md +17 -0
  129. package/ai/reference/bf_sidebar_state_storage.md +20 -0
  130. package/ai/reference/bf_theme_storage.md +3 -1
  131. package/ai/reference/provide-bf-color-scheme.md +20 -0
  132. package/ai/reference/provide-bf-labels.md +13 -4
  133. package/ai/reference/provide-bf-sidebar-state.md +20 -0
  134. package/ai/reference/slide-in-from-end.md +24 -0
  135. package/ai/reference/slide-out-to-end.md +24 -0
  136. package/bin/bf-ui.js +433 -3
  137. package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
  138. package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
  139. package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
  140. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
  141. package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
  142. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  143. package/fesm2022/bitforgehq-ui-overlay.mjs +6566 -38
  144. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  145. package/fesm2022/bitforgehq-ui-testing.mjs +634 -1
  146. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  147. package/fesm2022/bitforgehq-ui.mjs +5708 -137
  148. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  149. package/llms-full.txt +26766 -14470
  150. package/llms.txt +106 -1
  151. package/package.json +7 -3
  152. package/theme.css +101 -0
  153. package/types/bitforgehq-ui-charts.d.ts +967 -0
  154. package/types/bitforgehq-ui-icons.d.ts +13 -1
  155. package/types/bitforgehq-ui-mobile.d.ts +25 -18
  156. package/types/bitforgehq-ui-overlay.d.ts +1895 -7
  157. package/types/bitforgehq-ui-testing.d.ts +782 -1
  158. package/types/bitforgehq-ui.d.ts +2393 -45
package/ai/UI_GUIDE.md CHANGED
@@ -72,7 +72,7 @@ In `src/index.html`, give `<body>` the theme background and text colour, so the
72
72
 
73
73
  ### 7. Switch to dark mode
74
74
 
75
- Add the `dark` class to `<html>` (and remove it for light mode). Every token is redefined under `.dark`, and the theme sets `color-scheme`, so scrollbars and native controls follow too. Components need no `dark:` classes.
75
+ Add the `dark` class to `<html>` (and remove it for light mode). Every token is redefined under `.dark`, and the theme sets `color-scheme`, so scrollbars and native controls follow too. Components need no `dark:` classes. To let users choose System, Light or Dark, add `provideBfColorScheme()` to the app providers: it sets the class, follows the OS and remembers the choice.
76
76
 
77
77
  ```html
78
78
  <html lang="en" class="dark">
@@ -117,7 +117,12 @@ the widgets style their native controls themselves.
117
117
  Optional per-app overrides go after the import. A brand colour is a set: override
118
118
  `--color-primary` together with `--color-primary-text` (primary as text, ≥ 4.5:1 on `bg` and
119
119
  `surface`) and the rest of the `--color-primary-*` tokens, in `@theme` and in `.dark`; the docs
120
- site's theme configurator derives the whole contrast-checked set from one colour.
120
+ site's theme configurator derives the whole contrast-checked set from one colour. From a
121
+ terminal, `bunx bf-ui theme '<hex>' [--dark '<hex>']` prints the set (`primary`, its hover,
122
+ pressed, soft and strong steps, `on-primary`, `primary-text`, `selected`, `ring`) as a `@theme`
123
+ and a `.dark` block with each contrast pair's ratio, and exits 1 naming the pairs below their
124
+ minimum and a shade that passes; paste its output, never hand-pick the steps. Example (the
125
+ Meadow Sage green): `bunx bf-ui theme '#238652' --dark '#5cc48a'`.
121
126
  `--radius-control` (`9999px`, pills) reshapes buttons, chips, badges, segmented, the tab bar, the
122
127
  action bar and toggles at once:
123
128
 
@@ -138,15 +143,17 @@ Render library components only in the browser, for example in a `@defer` block o
138
143
  route. Support will be decided before 1.0.
139
144
 
140
145
  The one server-side check is a render smoke, not a support test: the library repository's
141
- `tools/ssr-smoke.ts` renders 28 fixtures with `renderApplication()` (every root widget, `bf-icon`,
142
- `bf-number-input`, a `/mobile` page with `provideBfShell()`, `bf-page`, `bf-header` and
143
- `bf-content`, `bf-tabs` with `bf-stack-outlet` under the router, and an `/overlay` dialog), and all
144
- of them render without an error. 19 of them render identically twice. The other 9 number their ids
145
- differently on every render, from a module-level counter, so they could not hydrate: `bf-checkbox`,
146
- `bf-input`, `bf-radio-group`, `bf-segmented`, `bf-select`, `bf-textarea`, `bf-toggle`,
147
- `bf-number-input` and the `/overlay` dialog. Hydration itself is still not supported or tested, and
148
- more stands in its way: `/mobile` writes to `<html>` and adds a keyboard probe to `<body>`, and an
149
- `/overlay` dialog renders outside the app root and marks it inert.
146
+ `tools/ssr-smoke.ts` renders 43 fixtures with `renderApplication()` (every root widget, `bf-icon`,
147
+ `bf-number-input`, a `/charts` line chart, a `/mobile` page with `provideBfShell()`, `bf-page`,
148
+ `bf-header` and `bf-content`, `bf-tabs` with `bf-stack-outlet` under the router, and an `/overlay`
149
+ dialog), and all of them render without an error. 27 of them render identically twice. The other 16
150
+ number their ids differently on every render, from a module-level counter, so they could not
151
+ hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-input`, `bf-radio-group`, `bf-segmented`,
152
+ `bf-select`, `bf-sidebar`, `bf-tab-group`, `bf-table`, `bf-textarea`, `bf-theme-switch`,
153
+ `bf-toggle`, `bf-line-chart`, `bf-number-input` and the `/overlay` dialog. Hydration itself is still
154
+ not supported or tested, and more stands in its way: `/mobile` writes to `<html>` and adds a
155
+ keyboard probe to `<body>`, and an `/overlay` dialog renders outside the app root and marks it
156
+ inert.
150
157
 
151
158
  ## Keeping this guide in sync
152
159
 
@@ -165,6 +172,11 @@ or out of date — wire it into CI so agent guidance never drifts from the insta
165
172
  - Number field: `import { BfNumberInputComponent } from '@bitforgehq/ui/number-input';`. It is
166
173
  the one widget in its own entry point, because it loads Angular Forms (`@angular/forms`, a
167
174
  required peer); `@bitforgehq/ui` itself loads no forms code, so an app without forms ships none.
175
+ - Charts: `import { BfLineChartComponent } from '@bitforgehq/ui/charts';` (also `BfBarChartComponent`,
176
+ `BfDonutChartComponent`, `BfSparklineComponent`, `BfChartCardComponent`). Our own SVG charts,
177
+ themed with the `--color-chart-1` … `--color-chart-8` tokens; pass `series` (`{ name, values }`) and
178
+ `categories`, and a `label` that names the figure. Prefer them to a chart library unless you need a
179
+ chart form they do not draw.
168
180
  - Layout classes go on the element (`<bf-button class="mt-4">`); never restyle a widget's internals.
169
181
  - Colors: use only the semantic tokens (`bg-primary`, `text-text-muted`, `bg-surface`,
170
182
  `border-border`, `rounded-field`, `shadow-raised`, `text-body`). Never raw palette classes and
@@ -260,7 +272,7 @@ toolbar or a German settings page); a key you leave out keeps the parent's value
260
272
  (hence `fieldSize: 'lg'` for touch). Keep `gap-2` or more between wrapping rows of `sm` controls.
261
273
  - `provideBfLabels({ ... })` replaces the built-in English strings (`loading`, `dismiss`, `tabBar`,
262
274
  `tabBadge(count)`, `back`, `ok`, `cancel`, `actions`, `sheet`, `modal`, `refreshing`, `close`,
263
- `dialog`) and the
275
+ `dialog`, `theme`, `themeSystem`, `themeLight`, `themeDark`) and the
264
276
  default error text per validator kind (`validation`: `required`, `email`, `pattern`, `parse`,
265
277
  `min(n)`, `max(n)`, `minLength(n)`, `maxLength(n)`, `minDate(d)`, `maxDate(d)`, `fallback`),
266
278
  merged key by key; a key set to `undefined` also keeps the parent's string. Use it for any
@@ -272,7 +284,7 @@ toolbar or a German settings page); a key you leave out keeps the parent's value
272
284
  fall back to `sheet`/`modal`.
273
285
  - What a component-level `provideBfLabels()` covers: the widgets, form controls and
274
286
  `bf-back-button`/`bf-refresher` rendered inside that component (`loading`, `dismiss`, `tabBar`,
275
- `tabBadge`, `back`, `refreshing`, `validation`). The overlay services (`BfAlertDialogService`
287
+ `tabBadge`, `back`, `refreshing`, the `theme…` keys, `validation`). The overlay services (`BfAlertDialogService`
276
288
  `ok`, `BfActionSheetService` `cancel`/`actions`, `BfSheetService` `sheet`, `BfModalService`
277
289
  `modal`, `BfDialogService` `close`/`dialog`/`ok`/`cancel`, `BfDesktopToastService`
278
290
  `notifications`) are root singletons: they read the app-level labels unless the call passes the
@@ -302,6 +314,101 @@ export const appConfig: ApplicationConfig = {
302
314
  };
303
315
  ```
304
316
 
317
+ ### Colour scheme
318
+
319
+ A System / Light / Dark choice needs no code of your own. In a desktop web app add
320
+ `provideBfColorScheme()` to `ApplicationConfig.providers`; `BfColorSchemeService` (both from
321
+ `@bitforgehq/ui`, no Capacitor) then toggles `dark` on `<html>`, follows `prefers-color-scheme`
322
+ while the choice is `system`, and stores the choice in `localStorage` under `bf-theme`
323
+ (`BF_COLOR_SCHEME_STORAGE` moves it elsewhere). Read `dark()`, and switch with
324
+ `scheme.set('dark')`. For the control, bind `bf-theme-switch` (three segments, for a settings page
325
+ or a header row with room) or `bf-theme-menu-button` (`@bitforgehq/ui/overlay`, a menu for a
326
+ crowded top bar, icon in `bfSlot="icon"`) two-way: `[(value)]="colorScheme.scheme"`. Storage and
327
+ the media query are read in the browser only, so a server render starts light and writes nothing.
328
+ A Capacitor app gets the same service from `provideBfShell()`: `BfPlatformService.theme` and
329
+ `dark` are the same signals, and the system bars follow them. Never write your own `matchMedia`
330
+ listener, storage key or `<html>` class toggle.
331
+
332
+ ```ts
333
+ export const appConfig: ApplicationConfig = { providers: [provideBfColorScheme()] };
334
+ // In a settings component:
335
+ protected readonly colorScheme = inject(BfColorSchemeService);
336
+ // <bf-theme-switch [(value)]="colorScheme.scheme" />
337
+ ```
338
+
339
+ ## Desktop layout
340
+
341
+ `bf-tabs`, `bf-back-button` and `bf-header` are Capacitor shell parts. A desktop web app (no
342
+ `provideBfShell()`) uses their desktop counterparts from `@bitforgehq/ui`:
343
+
344
+ - **App frame:** wrap the whole app in `<bf-app-frame>` (never build the frame by hand): the
345
+ `bf-sidebar` in `bfSlot="sidebar"` (sticky, full height, from `md` up), and below the
346
+ breakpoint a header bar with `bfSlot="brand"`, `bfSlot="menu"` (a `bf-menu-button` of
347
+ `a[bfMenuItem]` links from the same destinations array) and `bfSlot="actions"` (the theme
348
+ menu); the router outlet is the content. It brings the skip link and the focusable main
349
+ landmark; `breakpoint` and `contentWidth` adjust it. Desktop only: never inside
350
+ `provideBfShell()`.
351
+ - **Sidebar collapse:** `<bf-sidebar-toggle>` (`/overlay`, icons in `bfSlot="collapse-icon"` and
352
+ `bfSlot="expand-icon"`) in the sidebar footer beside the icon-only theme menu; with
353
+ `provideBfSidebarState()` bind `[(collapsed)]="sidebarState.collapsed"`
354
+ (`BfSidebarStateService`) so the choice is remembered.
355
+ - **Navigation:** `bf-sidebar` (groups in `bf-nav-group`, `[(collapsed)]` for an icon rail) or
356
+ `bf-top-nav` for a few sections, holding
357
+ `<a bfNavItem routerLink="…" routerLinkActive ariaCurrentWhenActive="page">` links with an icon
358
+ in `bfSlot="icon"` and an optional `[badge]` count. The router sets `aria-current="page"` and
359
+ the item shows it. Drive the sidebar, a below-`md` `bf-menu-button` of `a[bfMenuItem]` links and,
360
+ in the shell, the `bf-tab`s of a `bf-tab-bar` from one array of destinations.
361
+ - **Breadcrumb:** `<bf-breadcrumb>` of `<li bfBreadcrumbItem><a routerLink>…</a></li>`, the
362
+ current page last as plain text (marked `aria-current="page"`); `maxItems` collapses a long
363
+ trail, and a `bfSlot="overflow"` `bf-menu-button` turns the collapse into a menu. On mobile it is
364
+ `bf-back-button` in `bf-header`.
365
+ - **Page header:** `<bf-page-header title="…" subtitle="…">` with the breadcrumb in
366
+ `bfSlot="breadcrumb"`, badges in `bfSlot="meta"` and the actions in `bfSlot="actions"` (the main
367
+ action a primary `bf-button`, secondary ones `ghost`, the rest in an icon-only `bf-menu-button`).
368
+ An icon-only link (back, open, edit) is `a[bfIconButton]`.
369
+ - **Toolbar:** `<bf-toolbar label="…" [status]="count">` under the page header: search and filters
370
+ in the default slot, a view switch in `bfSlot="view"`, actions in `bfSlot="actions"`; its
371
+ controls default to `sm`, and `status` is a polite live region for the result count.
372
+ - **Table:** `<bf-table [rows] [columns] [(sort)] selectable [(selection)]>` with
373
+ `ng-template[bfTableCell]` for rich cells and `bfSlot="empty"` for the empty state; the app
374
+ sorts the rows in a `computed()`, and a selection shows a `bf-action-bar`. `stack="narrow"`
375
+ (below 36rem of its own width) or `stack="coarse"` turns each row into a label/value tile with
376
+ a sort and select bar; in the Capacitor shell use `bf-list-item` rows instead.
377
+ - **Details:** `bf-description-list` of `div[bfDescriptionItem]` label/value pairs, and `bf-stat`
378
+ figure tiles in a grid; both work in both shells.
379
+ - **Cards in a grid:** the grid stretches each `bf-card` and its `bfSlot="footer"` sits on the
380
+ bottom edge, so footers line up; put a footer's parts in one element.
381
+ - **Rows on a card:** `<bf-list [inset]="false" divided class="-mx-card">`, flush to the card's
382
+ edges with lines between the rows.
383
+ - **Theme switch:** `<bf-theme-switch [(value)]="colorScheme.scheme" />` (or
384
+ `bf-theme-menu-button` from `/overlay` in a crowded top bar) with `provideBfColorScheme()` (see
385
+ "Colour scheme").
386
+ - **Tabs in a page:** `<bf-tab-group [(selected)]="tab">` of `<bf-tab-panel value="…" label="…">`
387
+ (content in an `ng-template` inside renders only while shown). Not `bf-tabs`/`bf-tab-bar` (the
388
+ shell's bottom tabs), and not for routes (use `bf-top-nav` links).
389
+ - **Paging and loading:** `<bf-pagination [(page)]="page" [total]="count" [pageSize]="20">` under a
390
+ `bf-table` (the app slices its rows); `bf-skeleton` (`shape` text/rect/circle, `lines`) as the
391
+ placeholder while data loads, inside a region with `aria-busy="true"`.
392
+ - **Dates and times:** `<bf-calendar [(value)]="date">` for an inline month view;
393
+ `bf-date-input`, `bf-time-input` and `bf-datetime-input` (`/overlay`) for a field: typed parts
394
+ in locale order plus a calendar or time-list popover (`picker="none"` for typed only, `native`
395
+ for the platform picker on touch). Values are ISO strings (`'2026-10-28'`, `'14:30'`,
396
+ `'2026-10-28T14:30'`); month and day names come from `Intl` in the document's language.
397
+ - **Picking from many options:** `bf-combobox` (`/overlay`) for one value with search, groups and
398
+ server search (`queried`, `loading`); `bf-multi-combobox` for tags. `bf-select` stays the choice
399
+ for a short fixed list.
400
+ - **Dashboards:** `bf-stat` with `delta`, `trend` and `trendIntent` for a figure and its change;
401
+ `@bitforgehq/ui/charts` for `bf-line-chart`, `bf-bar-chart`, `bf-donut-chart` and
402
+ `bf-sparkline` inside a `bf-chart-card` (title, period slot, loading, empty and error states),
403
+ themed by the `chart-1`…`chart-8` tokens. Every chart takes a `label` and offers its data as a
404
+ table.
405
+ - **Surfaces:** one surface style per level. A raised `bf-card` holds sunken tiles
406
+ (`bf-stat variant="tile"`), never an outlined surface (`variant="card"` or a `flat` card) inside
407
+ a raised one; next to raised cards on a dashboard, use raised cards.
408
+ - **Empty pages:** a 404, "no access" or "nothing here yet" page is a `bf-empty-state` with
409
+ `headingLevel` 1, centred in the content area, with the main action and an optional link.
410
+ - **Toolbar on a phone:** `bf-toolbar` keeps its status and actions together on the last row.
411
+
305
412
  ## Overlays (`@bitforgehq/ui/overlay`)
306
413
 
307
414
  A desktop or plain web app (no Capacitor, no router required) installs `@angular/cdk` and `rxjs`
@@ -331,6 +438,13 @@ injector? })` stacks up to three cards bottom-right (configurable with
331
438
  keys, arrow navigation with wrap, Home/End, type-ahead, `Escape`/`Tab` close with focus back on
332
439
  the trigger, `ArrowRight`/`ArrowLeft` for submenus. A menu opened from inside a `bf-dialog`:
333
440
  `Escape` closes only the menu, never the dialog underneath.
441
+ - **Tooltip and popover:** `[bfTooltip]="'Text'"` on a focusable element shows a short hint on
442
+ hover and keyboard focus (never on touch, never the only place the information lives).
443
+ `[bfPopoverTriggerFor]="panel"` opens a non-modal `bf-popover` panel anchored to its button
444
+ (filters, details); focus moves in, `Escape` and an outside click close it.
445
+ - **Side panel:** `BfSidePanelService.open(component | template, { width, modal, data })` slides a
446
+ full-height `bf-side-panel` in from the inline end: a record's details or edit form beside the
447
+ list. `modal: false` keeps the page usable. In the Capacitor shell use `bf-sheet`/`bf-modal`.
334
448
  - **`closeOnNavigation`:** every overlay (`BfDialogConfig`, `confirm()`, and the `/mobile` sheet,
335
449
  modal, action sheet and alert options) closes when the URL path changes by default (`true`);
336
450
  pass `closeOnNavigation: false` to keep one open across a navigation (e.g. a draft that must
@@ -862,7 +976,7 @@ describe('bf-button in a consumer app (Vitest)', () => {
862
976
 
863
977
  ### 6. Drive a form widget through its harness
864
978
 
865
- The entry ships CDK harnesses for the widgets whose markup is not obvious to drive: `BfSelectHarness`, `BfRadioGroupHarness`, `BfSegmentedHarness`, `BfDialogHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfMenuButtonHarness` and `BfDesktopToastHarness`. `with({ label })` finds a widget by its label. Load an in-page widget with `TestbedHarnessEnvironment.loader(fixture)`.
979
+ The entry ships CDK harnesses for the widgets whose markup is not obvious to drive: `BfSelectHarness`, `BfRadioGroupHarness`, `BfSegmentedHarness`, `BfDialogHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfMenuButtonHarness`, `BfDesktopToastHarness`, `BfTableHarness`, `BfPaginationHarness` `BfTabGroupHarness`, `BfCalendarHarness` and `BfChartHarness`. `with({ label })` finds a widget by its label. Load an in-page widget with `TestbedHarnessEnvironment.loader(fixture)`.
866
980
 
867
981
  `select-harness.spec.ts`:
868
982
 
@@ -1048,10 +1162,16 @@ Don't assert on the library's CSS classes or internal markup. They are styling d
1048
1162
 
1049
1163
  ## Design foundations (Meadow Sage)
1050
1164
 
1051
- Warm off-white canvas (`bg-bg`), white surfaces, sage green primary, pill-shaped controls,
1165
+ Warm off-white canvas (`bg-bg`), white surfaces, a neutral indigo default primary
1166
+ (`#2b26ba`), pill-shaped controls,
1052
1167
  radii 12/16/24 px (`rounded-tile/field/card`), 1.5 px control borders, soft layered shadows
1053
1168
  (`shadow-raised`, `shadow-floating`), Manrope type (`text-display/title/heading/body/caption`).
1054
1169
 
1170
+ The palette is Meadow Sage, but the primary is not its sage green: the indigo default is neutral
1171
+ on purpose and meant to be overridden with the app's brand colour (`--color-primary` and the rest
1172
+ of its set, in `@theme` and `.dark`). An indigo primary right after install is expected; it does
1173
+ not mean the theme failed to load.
1174
+
1055
1175
  ### Type and spacing scale
1056
1176
 
1057
1177
  - Type (rem tokens; px at a 16 px root): `text-display` 40, `text-title` 28, `text-heading` 18,
@@ -1076,33 +1196,50 @@ notes.
1076
1196
  | `bf-action-bar` | `@bitforgehq/ui` | Floating pill of contextual actions that appears while items are selected |
1077
1197
  | `bf-button` | `@bitforgehq/ui` | Pill-shaped action button with five variants, three sizes and a loading state |
1078
1198
  | `bf-icon-button` | `@bitforgehq/ui` | Round icon-only button for toolbars and rows, with a required accessible label |
1199
+ | `bf-toolbar` | `@bitforgehq/ui` | Dense row of search, filters, view switch, result count and actions |
1079
1200
 
1080
1201
  ### Forms
1081
1202
 
1082
- | Component | Entry point | Purpose |
1083
- | ----------------- | ----------------------------- | ------------------------------------------------------------------------------------------- |
1084
- | `bf-checkbox` | `@bitforgehq/ui` | Rounded checkbox with an optional label that binds to Signal Forms |
1085
- | `bf-chip` | `@bitforgehq/ui` | Toggleable filter chip with a check mark when selected, in tonal or ink style |
1086
- | `bf-chip-group` | `@bitforgehq/ui` | Named row of chips that picks one like radios, or groups independent toggles |
1087
- | `bf-field` | `@bitforgehq/ui` | Label, help text and error text wrapper that wires ids for assistive tech |
1088
- | `bf-input` | `@bitforgehq/ui` | Single-line text field with label, help and error text, icon slots and Signal Forms binding |
1089
- | `bf-number-input` | `@bitforgehq/ui/number-input` | Number field that binds number or null, with native min, max and step |
1090
- | `bf-radio-group` | `@bitforgehq/ui` | Group of radio buttons for choosing one option, with optional per-option descriptions |
1091
- | `bf-segmented` | `@bitforgehq/ui` | Segmented control for switching between two to four views, keyboard-navigable like radios |
1092
- | `bf-select` | `@bitforgehq/ui` | Native single-choice dropdown styled like the text field, with Signal Forms binding |
1093
- | `bf-textarea` | `@bitforgehq/ui` | Multi-line text field with label, help and error text, and Signal Forms binding |
1094
- | `bf-toggle` | `@bitforgehq/ui` | On/off switch for settings that apply immediately, with Signal Forms binding |
1203
+ | Component | Entry point | Purpose |
1204
+ | ------------------- | ----------------------------- | ------------------------------------------------------------------------------------------- |
1205
+ | `bf-calendar` | `@bitforgehq/ui` | Inline month calendar for picking one date, with month and year jumps |
1206
+ | `bf-checkbox` | `@bitforgehq/ui` | Rounded checkbox with an optional label that binds to Signal Forms |
1207
+ | `bf-chip` | `@bitforgehq/ui` | Toggleable filter chip with a check mark when selected, in tonal or ink style |
1208
+ | `bf-chip-group` | `@bitforgehq/ui` | Named row of chips that picks one like radios, or groups independent toggles |
1209
+ | `bf-combobox` | `@bitforgehq/ui/overlay` | Text field that filters a list of options as you type and picks one |
1210
+ | `bf-date-input` | `@bitforgehq/ui/overlay` | Typed date field with a calendar popover, valued as an ISO date string |
1211
+ | `bf-datetime-input` | `@bitforgehq/ui/overlay` | Typed date and time field with a calendar and time list in one popover |
1212
+ | `bf-field` | `@bitforgehq/ui` | Label, help text and error text wrapper that wires ids for assistive tech |
1213
+ | `bf-input` | `@bitforgehq/ui` | Single-line text field with label, help and error text, icon slots and Signal Forms binding |
1214
+ | `bf-multi-combobox` | `@bitforgehq/ui/overlay` | Text field that filters options as you type and picks several as tags |
1215
+ | `bf-number-input` | `@bitforgehq/ui/number-input` | Number field that binds number or null, with native min, max and step |
1216
+ | `bf-radio-group` | `@bitforgehq/ui` | Group of radio buttons for choosing one option, with optional per-option descriptions |
1217
+ | `bf-segmented` | `@bitforgehq/ui` | Segmented control for switching between two to four views, keyboard-navigable like radios |
1218
+ | `bf-select` | `@bitforgehq/ui` | Native single-choice dropdown styled like the text field, with Signal Forms binding |
1219
+ | `bf-textarea` | `@bitforgehq/ui` | Multi-line text field with label, help and error text, and Signal Forms binding |
1220
+ | `bf-theme-switch` | `@bitforgehq/ui` | Segmented System, Light and Dark switch bound two-way to the app colour scheme |
1221
+ | `bf-time-input` | `@bitforgehq/ui/overlay` | Typed time-of-day field with a popover list of times, valued as an ISO string |
1222
+ | `bf-toggle` | `@bitforgehq/ui` | On/off switch for settings that apply immediately, with Signal Forms binding |
1095
1223
 
1096
1224
  ### Data display
1097
1225
 
1098
- | Component | Entry point | Purpose |
1099
- | -------------- | ---------------- | --------------------------------------------------------------------------------------- |
1100
- | `bf-avatar` | `@bitforgehq/ui` | Round avatar showing a photo or initials, in three sizes and four color variants |
1101
- | `bf-badge` | `@bitforgehq/ui` | Small status label in semantic tones, or a compact numeric pill for counts |
1102
- | `bf-card` | `@bitforgehq/ui` | Content surface with an optional header slot, in four visual styles from raised to flat |
1103
- | `bf-list` | `@bitforgehq/ui` | Container for list rows, inset by default with rounded corners on a warm background |
1104
- | `bf-list-item` | `@bitforgehq/ui` | List row with leading slot, title, subtitle, trailing slot and an optional chevron |
1105
- | `bf-progress` | `@bitforgehq/ui` | Progress bar in two sizes and four tones for completion, quotas and unknown waits |
1226
+ | Component | Entry point | Purpose |
1227
+ | ------------------------ | ----------------------- | --------------------------------------------------------------------------------------- |
1228
+ | `bf-avatar` | `@bitforgehq/ui` | Round avatar showing a photo or initials, in three sizes and four color variants |
1229
+ | `bf-badge` | `@bitforgehq/ui` | Small status label in semantic tones, or a compact numeric pill for counts |
1230
+ | `bf-bar-chart` | `@bitforgehq/ui/charts` | Bar chart of series over categories, grouped or stacked, vertical or horizontal |
1231
+ | `bf-card` | `@bitforgehq/ui` | Content surface with an optional header slot, in four visual styles from raised to flat |
1232
+ | `bf-chart-card` | `@bitforgehq/ui/charts` | Card around one chart with a title, period control and loading, empty and error states |
1233
+ | `div[bfDescriptionItem]` | `@bitforgehq/ui` | One label and value pair of a description list, label as term, content as value |
1234
+ | `bf-description-list` | `@bitforgehq/ui` | Native description list of label-value pairs, as divided rows or a tile grid |
1235
+ | `bf-donut-chart` | `@bitforgehq/ui/charts` | Donut chart of the parts of a whole, with a legend, tooltip and centre slot |
1236
+ | `bf-line-chart` | `@bitforgehq/ui/charts` | Line chart of series over categories, with tooltip, legend and data table |
1237
+ | `bf-list` | `@bitforgehq/ui` | Container for list rows, inset by default with rounded corners on a warm background |
1238
+ | `bf-list-item` | `@bitforgehq/ui` | List row with leading slot, title, subtitle, trailing slot and an optional chevron |
1239
+ | `bf-progress` | `@bitforgehq/ui` | Progress bar in two sizes and four tones for completion, quotas and unknown waits |
1240
+ | `bf-sparkline` | `@bitforgehq/ui/charts` | Tiny trend line with no axes or legend, for stats and table cells |
1241
+ | `bf-stat` | `@bitforgehq/ui` | One labelled figure with an optional icon tile and a supporting line |
1242
+ | `bf-table` | `@bitforgehq/ui` | Desktop data table with sortable headers, row selection, custom cells and an empty slot |
1106
1243
 
1107
1244
  ### Feedback
1108
1245
 
@@ -1110,35 +1247,52 @@ notes.
1110
1247
  | ---------------- | ---------------- | ---------------------------------------------------------------------------------- |
1111
1248
  | `bf-alert` | `@bitforgehq/ui` | Inline status message in four tones with a title and an optional dismiss button |
1112
1249
  | `bf-empty-state` | `@bitforgehq/ui` | Placeholder for an empty screen or list with icon, title, message and action |
1250
+ | `bf-skeleton` | `@bitforgehq/ui` | Loading placeholder shaped as text lines, a rectangle or a circle, with a shimmer |
1113
1251
  | `bf-spinner` | `@bitforgehq/ui` | Indeterminate loading spinner in three sizes that inherits its parent's text color |
1114
1252
 
1115
1253
  ### Navigation
1116
1254
 
1117
- | Component | Entry point | Purpose |
1118
- | ----------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
1119
- | `bf-back-button` | `@bitforgehq/ui/mobile` | Header back button that pops the page stack or falls back to a default route |
1120
- | `bf-content` | `@bitforgehq/ui/mobile` | Page's single scroll container that keeps focused fields clear of the soft keyboard |
1121
- | `bf-header` | `@bitforgehq/ui/mobile` | Page header toolbar with a large title that collapses into the toolbar on scroll |
1122
- | `bf-page` | `@bitforgehq/ui/mobile` | Frame for one stacked mobile screen that supplies lifecycle events and shared page state |
1123
- | `bf-refresher` | `@bitforgehq/ui/mobile` | Pull-to-refresh indicator with haptic feedback, native iOS bounce and an Android-style pull |
1124
- | `bf-stack-outlet` | `@bitforgehq/ui/mobile` | Router outlet keeping a live page stack with slide transitions and iOS swipe back |
1125
- | `bf-tab` | `@bitforgehq/ui` | One tab bar destination with an icon, a label and an optional count badge |
1126
- | `bf-tab-bar` | `@bitforgehq/ui` | Floating pill tab bar for mobile apps with arrow-key selection and count badges |
1127
- | `bf-tabs` | `@bitforgehq/ui/mobile` | Tabs page with one navigation stack per tab, wiring the tab bar to the router |
1255
+ | Component | Entry point | Purpose |
1256
+ | ---------------------- | ------------------------ | ------------------------------------------------------------------------------------------- |
1257
+ | `bf-app-frame` | `@bitforgehq/ui` | Desktop app frame with skip link, sticky sidebar, narrow header and main content |
1258
+ | `bf-back-button` | `@bitforgehq/ui/mobile` | Header back button that pops the page stack or falls back to a default route |
1259
+ | `bf-breadcrumb` | `@bitforgehq/ui` | Desktop breadcrumb trail of links to the current page, with optional collapsing |
1260
+ | `li[bfBreadcrumbItem]` | `@bitforgehq/ui` | One level of a breadcrumb trail: a link, or the current page as text |
1261
+ | `bf-content` | `@bitforgehq/ui/mobile` | Page's single scroll container that keeps focused fields clear of the soft keyboard |
1262
+ | `bf-header` | `@bitforgehq/ui/mobile` | Page header toolbar with a large title that collapses into the toolbar on scroll |
1263
+ | `bf-nav-group` | `@bitforgehq/ui` | A titled group of sidebar links with a small heading that names it |
1264
+ | `a[bfNavItem]` | `@bitforgehq/ui` | Navigation link for a sidebar or top nav, with icon, label and count |
1265
+ | `bf-page` | `@bitforgehq/ui/mobile` | Frame for one stacked mobile screen that supplies lifecycle events and shared page state |
1266
+ | `bf-page-header` | `@bitforgehq/ui` | Desktop page header with breadcrumb, title, subtitle, badges and a row of actions |
1267
+ | `bf-pagination` | `@bitforgehq/ui` | Previous, next and numbered page buttons for a long list or table |
1268
+ | `bf-refresher` | `@bitforgehq/ui/mobile` | Pull-to-refresh indicator with haptic feedback, native iOS bounce and an Android-style pull |
1269
+ | `bf-sidebar` | `@bitforgehq/ui` | Vertical app navigation with brand, grouped links, footer and an icon rail mode |
1270
+ | `bf-sidebar-toggle` | `@bitforgehq/ui/overlay` | Icon button inside a sidebar that collapses it to an icon rail and back |
1271
+ | `bf-stack-outlet` | `@bitforgehq/ui/mobile` | Router outlet keeping a live page stack with slide transitions and iOS swipe back |
1272
+ | `bf-tab` | `@bitforgehq/ui` | One tab bar destination with an icon, a label and an optional count badge |
1273
+ | `bf-tab-bar` | `@bitforgehq/ui` | Floating pill tab bar for mobile apps with arrow-key selection and count badges |
1274
+ | `bf-tab-group` | `@bitforgehq/ui` | In-page tabs that switch between content panels, with keyboard navigation and badges |
1275
+ | `bf-tab-panel` | `@bitforgehq/ui` | One tab of a tab group with its label, badge, icon and lazy content |
1276
+ | `bf-tabs` | `@bitforgehq/ui/mobile` | Tabs page with one navigation stack per tab, wiring the tab bar to the router |
1277
+ | `bf-top-nav` | `@bitforgehq/ui` | Horizontal app navigation, a wrapping row of links for apps with few sections |
1128
1278
 
1129
1279
  ### Overlays
1130
1280
 
1131
- | Component | Entry point | Purpose |
1132
- | ------------------ | ------------------------ | --------------------------------------------------------------------------------------------- |
1133
- | `bf-action-sheet` | `@bitforgehq/ui/mobile` | Bottom panel of labelled actions with a separate cancel button, destructive ones shown in red |
1134
- | `bf-alert-dialog` | `@bitforgehq/ui/mobile` | Centred confirmation dialog with a title, message and a row of role-styled buttons |
1135
- | `bf-desktop-toast` | `@bitforgehq/ui/overlay` | Desktop notification card in a bottom corner stack of up to three |
1136
- | `bf-dialog` | `@bitforgehq/ui/overlay` | Desktop modal dialog with a title, scrolling body, footer actions and focus trap |
1137
- | `bf-menu` | `@bitforgehq/ui/overlay` | Keyboard-operable menu panel with items, checkbox and radio items, groups and submenus |
1138
- | `bf-menu-button` | `@bitforgehq/ui/overlay` | Styled button that opens a menu, with outline, text, tonal and icon-only looks |
1139
- | `bf-modal` | `@bitforgehq/ui/mobile` | Modal overlay shown as an inset card over the scaled-down page, or full screen |
1140
- | `bf-sheet` | `@bitforgehq/ui/mobile` | Bottom sheet with snap points, drag to dismiss and a backdrop |
1141
- | `bf-toast` | `@bitforgehq/ui/mobile` | Passive status pill with an optional action that never steals focus |
1281
+ | Component | Entry point | Purpose |
1282
+ | ---------------------- | ------------------------ | --------------------------------------------------------------------------------------------- |
1283
+ | `bf-action-sheet` | `@bitforgehq/ui/mobile` | Bottom panel of labelled actions with a separate cancel button, destructive ones shown in red |
1284
+ | `bf-alert-dialog` | `@bitforgehq/ui/mobile` | Centred confirmation dialog with a title, message and a row of role-styled buttons |
1285
+ | `bf-desktop-toast` | `@bitforgehq/ui/overlay` | Desktop notification card in a bottom corner stack of up to three |
1286
+ | `bf-dialog` | `@bitforgehq/ui/overlay` | Desktop modal dialog with a title, scrolling body, footer actions and focus trap |
1287
+ | `bf-menu` | `@bitforgehq/ui/overlay` | Keyboard-operable menu panel with items, checkbox and radio items, groups and submenus |
1288
+ | `bf-menu-button` | `@bitforgehq/ui/overlay` | Styled button that opens a menu, with outline, text, tonal and icon-only looks |
1289
+ | `bf-modal` | `@bitforgehq/ui/mobile` | Modal overlay shown as an inset card over the scaled-down page, or full screen |
1290
+ | `bf-popover` | `@bitforgehq/ui/overlay` | Anchored, non-modal panel for filters, details or a small form |
1291
+ | `bf-sheet` | `@bitforgehq/ui/mobile` | Bottom sheet with snap points, drag to dismiss and a backdrop |
1292
+ | `bf-side-panel` | `@bitforgehq/ui/overlay` | Desktop panel sliding in from the end edge, with a title, body and footer actions |
1293
+ | `bf-theme-menu-button` | `@bitforgehq/ui/overlay` | Menu button with System, Light and Dark radio items bound to the colour scheme |
1294
+ | `bf-toast` | `@bitforgehq/ui/mobile` | Passive status pill with an optional action that never steals focus |
1295
+ | `bf-tooltip` | `@bitforgehq/ui/overlay` | Short hint beside a control on hover or keyboard focus, for desktop |
1142
1296
 
1143
1297
  ### Icons
1144
1298