@bitforgehq/ui 0.3.0 → 0.3.1

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 (144) hide show
  1. package/CHANGELOG.md +482 -0
  2. package/README.md +17 -11
  3. package/ai/MANIFEST.index.json +288 -0
  4. package/ai/MANIFEST.json +8716 -3926
  5. package/ai/REFERENCE.index.json +413 -0
  6. package/ai/REFERENCE.json +4710 -2047
  7. package/ai/UI_GUIDE.md +185 -57
  8. package/ai/components/back-button.md +42 -2
  9. package/ai/components/badge.md +17 -7
  10. package/ai/components/bar-chart.md +188 -0
  11. package/ai/components/breadcrumb-item.md +121 -0
  12. package/ai/components/breadcrumb.md +277 -0
  13. package/ai/components/button.md +82 -15
  14. package/ai/components/calendar.md +300 -0
  15. package/ai/components/card.md +63 -2
  16. package/ai/components/chart-card.md +283 -0
  17. package/ai/components/combobox.md +411 -0
  18. package/ai/components/date-input.md +396 -0
  19. package/ai/components/datetime-input.md +215 -0
  20. package/ai/components/description-item.md +111 -0
  21. package/ai/components/description-list.md +160 -0
  22. package/ai/components/donut-chart.md +169 -0
  23. package/ai/components/header.md +67 -2
  24. package/ai/components/icon-button.md +74 -16
  25. package/ai/components/icon.md +63 -7
  26. package/ai/components/line-chart.md +234 -0
  27. package/ai/components/list.md +70 -6
  28. package/ai/components/menu.md +5 -0
  29. package/ai/components/multi-combobox.md +257 -0
  30. package/ai/components/nav-group.md +178 -0
  31. package/ai/components/nav-item.md +210 -0
  32. package/ai/components/page-header.md +237 -0
  33. package/ai/components/pagination.md +173 -0
  34. package/ai/components/popover.md +250 -0
  35. package/ai/components/side-panel.md +391 -0
  36. package/ai/components/sidebar.md +363 -0
  37. package/ai/components/skeleton.md +215 -0
  38. package/ai/components/sparkline.md +188 -0
  39. package/ai/components/stat.md +233 -0
  40. package/ai/components/tab-group.md +226 -0
  41. package/ai/components/tab-panel.md +163 -0
  42. package/ai/components/table.md +568 -0
  43. package/ai/components/tabs.md +42 -2
  44. package/ai/components/theme-menu-button.md +182 -0
  45. package/ai/components/theme-switch.md +170 -0
  46. package/ai/components/time-input.md +240 -0
  47. package/ai/components/toolbar.md +289 -0
  48. package/ai/components/tooltip.md +230 -0
  49. package/ai/components/top-nav.md +159 -0
  50. package/ai/reference/bf-badge-appearance.md +36 -0
  51. package/ai/reference/bf-badge-variant.md +1 -1
  52. package/ai/reference/bf-bar-chart-orientation.md +27 -0
  53. package/ai/reference/bf-button-appearance.md +36 -0
  54. package/ai/reference/bf-button-variant.md +1 -1
  55. package/ai/reference/bf-calendar-disabled-date.md +23 -0
  56. package/ai/reference/bf-calendar-harness.md +51 -0
  57. package/ai/reference/bf-calendar-weekday.md +31 -0
  58. package/ai/reference/bf-chart-card-heading-level.md +26 -0
  59. package/ai/reference/bf-chart-curve.md +28 -0
  60. package/ai/reference/bf-chart-harness.md +51 -0
  61. package/ai/reference/bf-chart-legend-position.md +30 -0
  62. package/ai/reference/bf-chart-series.md +23 -0
  63. package/ai/reference/bf-chart-value-format.md +23 -0
  64. package/ai/reference/bf-color-scheme-service.md +30 -0
  65. package/ai/reference/bf-color-scheme.md +31 -0
  66. package/ai/reference/bf-combobox-filter.md +27 -0
  67. package/ai/reference/bf-combobox-harness.md +89 -0
  68. package/ai/reference/bf-combobox-option.md +33 -0
  69. package/ai/reference/bf-compare-with.md +2 -0
  70. package/ai/reference/bf-date-input-harness.md +67 -0
  71. package/ai/reference/bf-date-input-picker.md +27 -0
  72. package/ai/reference/bf-date-time-input-harness.md +76 -0
  73. package/ai/reference/bf-date-time-input-picker.md +27 -0
  74. package/ai/reference/bf-description-list-layout.md +26 -0
  75. package/ai/reference/bf-donut-slice.md +22 -0
  76. package/ai/reference/bf-labels.md +67 -20
  77. package/ai/reference/bf-menu-button-variant.md +1 -0
  78. package/ai/reference/bf-menu-placement.md +1 -0
  79. package/ai/reference/bf-overlay-options.md +28 -27
  80. package/ai/reference/bf-overlay-placement.md +34 -0
  81. package/ai/reference/bf-overlay-position.md +1 -1
  82. package/ai/reference/bf-overlay-service.md +5 -3
  83. package/ai/reference/bf-page-header-heading-level.md +26 -0
  84. package/ai/reference/bf-pagination-harness.md +61 -0
  85. package/ai/reference/bf-platform-service.md +17 -15
  86. package/ai/reference/bf-popover-harness.md +42 -0
  87. package/ai/reference/bf-popover-placement.md +28 -0
  88. package/ai/reference/bf-popover-trigger-directive.md +56 -0
  89. package/ai/reference/bf-side-panel-config.md +35 -0
  90. package/ai/reference/bf-side-panel-harness.md +34 -0
  91. package/ai/reference/bf-side-panel-ref.md +44 -0
  92. package/ai/reference/bf-side-panel-service.md +43 -0
  93. package/ai/reference/bf-side-panel-template-context.md +32 -0
  94. package/ai/reference/bf-side-panel-width.md +28 -0
  95. package/ai/reference/bf-size.md +12 -0
  96. package/ai/reference/bf-skeleton-shape.md +28 -0
  97. package/ai/reference/bf-stat-trend-intent.md +27 -0
  98. package/ai/reference/bf-stat-trend.md +27 -0
  99. package/ai/reference/bf-stat-variant.md +26 -0
  100. package/ai/reference/bf-tab-group-harness.md +44 -0
  101. package/ai/reference/bf-table-align.md +26 -0
  102. package/ai/reference/bf-table-cell-context.md +24 -0
  103. package/ai/reference/bf-table-cell-directive.md +22 -0
  104. package/ai/reference/bf-table-column.md +28 -0
  105. package/ai/reference/bf-table-harness.md +77 -0
  106. package/ai/reference/bf-table-row-key.md +21 -0
  107. package/ai/reference/bf-table-sort-direction.md +26 -0
  108. package/ai/reference/bf-table-sort.md +27 -0
  109. package/ai/reference/bf-theme.md +5 -4
  110. package/ai/reference/bf-time-input-harness.md +67 -0
  111. package/ai/reference/bf-time-input-hour-cycle.md +27 -0
  112. package/ai/reference/bf-time-input-picker.md +27 -0
  113. package/ai/reference/bf-tooltip-directive.md +43 -0
  114. package/ai/reference/bf-tooltip-harness.md +43 -0
  115. package/ai/reference/bf-tooltip-placement.md +28 -0
  116. package/ai/reference/bf_color_scheme_storage.md +20 -0
  117. package/ai/reference/bf_side_panel_data.md +17 -0
  118. package/ai/reference/bf_theme_storage.md +3 -1
  119. package/ai/reference/provide-bf-color-scheme.md +20 -0
  120. package/ai/reference/provide-bf-labels.md +12 -4
  121. package/ai/reference/slide-in-from-end.md +24 -0
  122. package/ai/reference/slide-out-to-end.md +24 -0
  123. package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
  124. package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
  125. package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
  126. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
  127. package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
  128. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  129. package/fesm2022/bitforgehq-ui-overlay.mjs +6436 -33
  130. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  131. package/fesm2022/bitforgehq-ui-testing.mjs +618 -1
  132. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  133. package/fesm2022/bitforgehq-ui.mjs +5092 -153
  134. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  135. package/llms-full.txt +25177 -14398
  136. package/llms.txt +95 -1
  137. package/package.json +7 -3
  138. package/theme.css +101 -0
  139. package/types/bitforgehq-ui-charts.d.ts +967 -0
  140. package/types/bitforgehq-ui-icons.d.ts +13 -1
  141. package/types/bitforgehq-ui-mobile.d.ts +25 -18
  142. package/types/bitforgehq-ui-overlay.d.ts +1844 -7
  143. package/types/bitforgehq-ui-testing.d.ts +748 -1
  144. package/types/bitforgehq-ui.d.ts +2136 -44
package/CHANGELOG.md CHANGED
@@ -4,6 +4,488 @@ All notable changes to `@bitforgehq/ui`. One `##` section per version; one `###`
4
4
 
5
5
  The heading for the version being worked on carries `(unreleased)` in place of a date. At release time, that placeholder is replaced with the release date (`## 0.3.1 (unreleased)` becomes `## 0.3.1 (YYYY-MM-DD)`) in the same commit that bumps `projects/ui/package.json`. See "Releasing" in the root `README.md`. 0.3.0 is the first version published to npm; the 0.2.0 and 0.1.0 sections below are the history of versions that were never published.
6
6
 
7
+ ## 0.3.1 (2026-10-29)
8
+
9
+ ### calendar
10
+
11
+ New `bf-calendar` in `@bitforgehq/ui`: an inline month calendar for picking one date. The value
12
+ is a plain ISO date string (`'YYYY-MM-DD'`) or `null`, never a `Date`, so no time zone shifts
13
+ the day; `(changed)` emits a new pick and `(picked)` every pick (for a popover to close).
14
+ `[(month)]` (`'YYYY-MM'`) is the month on show. `min`, `max` and `disabledDate`
15
+ (`BfCalendarDisabledDate`) disable dates; `firstDayOfWeek` (`BfCalendarWeekday`) and `locale`
16
+ (default the document's `lang`) set the week and the `Intl` month and weekday names; `today`
17
+ overrides the device's date; `size` sets the cells (32/40/48 px, 44 px under a coarse pointer).
18
+ The header's title opens a month view and then a year view for quick jumps. An ARIA grid with
19
+ roving focus: arrows by day and week (mirrored in RTL), Page Up/Down by month (Shift by year),
20
+ Home/End to the week's ends, Enter/Space picks, Escape leaves the jump views; disabled days stay
21
+ focusable with `aria-disabled`. Signal Forms value contract like `bf-segmented`. New label keys
22
+ `calendarPreviousMonth`, `calendarNextMonth`, `calendarChooseMonth`, `calendarChooseYear`, and
23
+ a `BfCalendarHarness` in `@bitforgehq/ui/testing` (`getMonthLabel()`, `selectDate(iso)`,
24
+ `getSelected()`, `nextMonth()`, `previousMonth()`).
25
+
26
+ ### toolbar
27
+
28
+ New `bf-toolbar` in `@bitforgehq/ui`: a wrapping `role="group"` row for the search field and
29
+ filters (default slot), a view switch (`bfSlot="view"`) and the trailing actions
30
+ (`bfSlot="actions"`, kept together at the inline end). `label` names the group; `status` renders
31
+ a polite `role="status"` live region for the result count. Its providers set
32
+ `provideBfDefaults({ size: 'sm' })`, so every projected control and field is `sm` unless it sets
33
+ `size` itself. Desktop only; in the Capacitor shell use `bf-header` actions and a `bf-sheet` of
34
+ filters.
35
+
36
+ ### theme name
37
+
38
+ The README, the package description, `theme.css`, the agent guide and the docs site's Theming
39
+ page now say what the shipped colours are: the Meadow Sage palette with a neutral indigo default
40
+ primary (`#2b26ba`) that is meant to be overridden, so an indigo primary after install is
41
+ expected. The agent guide no longer calls the primary sage green. No token changes.
42
+
43
+ ### tabs
44
+
45
+ The `## Desktop` docs name the desktop replacement: a `bf-top-nav` or `bf-sidebar` of
46
+ `a[bfNavItem]` links with `routerLinkActive` and `ariaCurrentWhenActive="page"`, driven by the
47
+ same destinations array as the shell's `bf-tab-bar`, with an example. No code change.
48
+
49
+ ### back-button
50
+
51
+ The `## Desktop` docs and When to use name the desktop replacement, `bf-breadcrumb`, with an
52
+ example. No code change.
53
+
54
+ ### header
55
+
56
+ The `## Desktop` docs and When to use name the desktop replacement, `bf-page-header` (with
57
+ `bf-breadcrumb` and `bf-toolbar`), with an example. No code change.
58
+
59
+ ### color-scheme
60
+
61
+ New `provideBfColorScheme()` and `BfColorSchemeService` in `@bitforgehq/ui`, with no Capacitor:
62
+ the System / Light / Dark choice (`BfColorScheme`), toggling `dark` on `<html>`, following
63
+ `prefers-color-scheme` under `system` and storing the choice in `localStorage` under `bf-theme`
64
+ (`BF_COLOR_SCHEME_STORAGE`). Storage and the media query are read in the browser only, so a server
65
+ render starts light and writes nothing. A desktop web app no longer needs its own theme service.
66
+
67
+ ### platform
68
+
69
+ `BfPlatformService` builds on `BfColorSchemeService`: `theme` and `dark` are its `scheme` and
70
+ `dark` signals, and the shell claims `<html>` through it, so the system bars still follow the
71
+ theme. Its public API, the `bf-theme` key and the in-memory fallback are unchanged;
72
+ `BF_THEME_STORAGE` is now the same token as `BF_COLOR_SCHEME_STORAGE`, so providing either one
73
+ moves the choice for both.
74
+
75
+ ### badge
76
+
77
+ New `info` tone for informational labels such as a category, on the new `info` / `info-soft`
78
+ tokens (7.7:1 in light, 7.2:1 in dark; not derived from the primary). `variant` is now typed
79
+ `BfBadgeAppearance` (the five `BfBadgeVariant` tones plus `info`); `BfBadgeVariant` keeps its five
80
+ members, so code that maps over it keeps compiling.
81
+
82
+ ### theme
83
+
84
+ New `--color-info` and `--color-info-soft` tokens in both modes (`text-info`, `bg-info-soft`),
85
+ used by the badge's `info` tone.
86
+
87
+ New `--animate-skeleton-shimmer` and `--animate-skeleton-shimmer-rtl` animations
88
+ (`animate-skeleton-shimmer`, 1.6 s, endless), the sweep of `bf-skeleton`'s shimmer from the
89
+ inline start.
90
+ New `--color-tooltip` and `--color-on-tooltip` tokens in both modes (`bg-tooltip`,
91
+ `text-on-tooltip`; 14.9:1 in light, 13.8:1 in dark), used by `bf-tooltip`.
92
+ New chart tokens in both modes for `@bitforgehq/ui/charts`: `--color-chart-1` … `--color-chart-8`
93
+ (a categorical palette in a fixed order: sage green, indigo, terracotta, teal, ochre, berry, violet,
94
+ olive; each ≥ 3:1 on `surface` and `bg`, adjacent slots distinct under colour vision deficiency),
95
+ `--color-chart-grid` (grid lines) and `--color-chart-axis` (the axis line, ≥ 3:1). New
96
+ `--animate-chart-draw`, `--animate-chart-grow-y`, `--animate-chart-grow-x` and
97
+ `--animate-chart-fade` animations, the charts' enter motion.
98
+
99
+ ### icon
100
+
101
+ New `filled` input: fills the shape with `currentColor` and keeps the stroke, for a favourite
102
+ heart or a starred item. Only icons whose every shape is closed are filled (`Heart`, `Star`,
103
+ `Bookmark`, …); a stroke icon or an outline holding inner strokes renders as before, so `filled`
104
+ never paints the area a check mark encloses or hides a stroke. Put the state in the control's name
105
+ or `aria-pressed` too.
106
+
107
+ ### list
108
+
109
+ New opt-in `divided` input: a `border` line between rows instead of a gap, with square rows whose
110
+ hover fill reaches the list's edges. On a card, `<bf-list [inset]="false" divided class="-mx-card">`
111
+ runs the rows edge to edge, and the rows take the card's inline padding so their text stays aligned
112
+ with the card's content. A divided inset list is one rounded `surface`. Lists without `divided`
113
+ look as before. When to use links `bf-table` for data compared across columns.
114
+
115
+ ### icon-button
116
+
117
+ New `a[bfIconButton]` selector: put `bfIconButton` on your own `<a>` (with `href` or `routerLink`)
118
+ for an icon-only link such as back, open or edit. The `<a>` takes the variant, size, focus ring
119
+ and touch hit area, `label` becomes its `aria-label` (still required), and it behaves as a link
120
+ (Enter, Ctrl/Cmd-click and middle click open it; `clicked` emits on every click). `type` and
121
+ `disabled` do not apply, as on `a[bfButton]`. The `bf-icon-button` element is unchanged.
122
+
123
+ ### button
124
+
125
+ New `ghost` variant: quiet and neutral (`text-muted` label, no fill at rest, `surface-muted` on
126
+ hover, `surface-pressed` on press), with the `text` variant's sizes, padding and disabled state,
127
+ for secondary toolbar actions and navigation rows. `a[bfButton]` with `aria-current="page"` shows
128
+ the current page: `ghost` and `text` links take the tonal fill, `outline` a primary border, so a
129
+ top nav built with `routerLinkActive` and `ariaCurrentWhenActive="page"` needs no `[variant]`
130
+ switch. `variant` is now typed `BfButtonAppearance` (the five `BfButtonVariant` styles plus
131
+ `ghost`); `BfButtonVariant` keeps its five members, so code that maps over it keeps compiling.
132
+
133
+ ### card
134
+
135
+ The footer (`bfSlot="footer"`) sits on the card's bottom edge (`mt-auto`), so in a grid of cards
136
+ whose descriptions wrap differently every footer lines up. A card whose footer already followed
137
+ the body directly looks the same unless the card is taller than its content.
138
+
139
+ ### packaging
140
+
141
+ The `bf-ui` bin entry points at `bin/bf-ui.js` without a leading `./`, the form npm stores, so
142
+ `npm publish` no longer warns that it cleaned the path. The command itself is unchanged.
143
+
144
+ ### page-header
145
+
146
+ New `bf-page-header` in `@bitforgehq/ui`, the desktop page header: a `breadcrumb` slot above,
147
+ the `title` as an `h1` (`headingLevel` 2 for an `h2`, `BfPageHeaderHeadingLevel`) with an
148
+ optional `subtitle`, a `meta` slot for badges beside the title, a `title` slot for rich title
149
+ content, and an `actions` slot at the inline end that wraps under the title in a narrow window.
150
+ Empty slots take no space. It is a sibling of `bf-header` (`/mobile`), not a shell-aware mode of
151
+ it: no large title, no scroll-under surface, no safe areas.
152
+
153
+ ### table
154
+
155
+ New `bf-table` and `ng-template[bfTableCell]` (`BfTableCellDirective`) in `@bitforgehq/ui`, a
156
+ data table for desktop pages: `rows`, `columns` (`BfTableColumn`: `key`, `label`, `sortable`,
157
+ `align` of `BfTableAlign`), `rowKey` (`BfTableRowKey`, the row's `id` by default) and a
158
+ visually hidden caption from `label`. Sortable headers set the `sort` model (`BfTableSort`,
159
+ `BfTableSortDirection`) and `aria-sort`; the consumer sorts the rows. `selectable` adds a
160
+ checkbox column bound to the `selection` model of row keys, with a mixed header checkbox named
161
+ by the new `tableSelectAll` label ("Select all rows") or `selectAllLabel`. A `bfTableCell`
162
+ template replaces one column's cells (`BfTableCellContext`; `bfTableCellOf` types the row), the
163
+ `empty` slot shows while there are no rows, `size` follows `controlSize`, and ArrowUp / ArrowDown
164
+ move focus between the controls of a column. New `BfTableHarness` in `@bitforgehq/ui/testing`.
165
+
166
+ The docs gain a Paging section and a `table/paged` example: one page of rows with a
167
+ `bf-pagination` under the table, back to page 1 on a sort.
168
+
169
+ ### theme-switch
170
+
171
+ New `bf-theme-switch` in `@bitforgehq/ui`: System, Light and Dark as a three-option
172
+ `bf-segmented`, holding a `BfColorScheme` in `value` (`[(value)]`, `changed`), with `label`,
173
+ `size` and `fullWidth`. It injects no service; bind it to `BfColorSchemeService.scheme`
174
+ (`BfPlatformService.theme` in the shell). New `BfLabels` keys `theme`, `themeSystem`, `themeLight`
175
+ and `themeDark`.
176
+
177
+ ### theme-menu-button
178
+
179
+ New `bf-theme-menu-button` in `@bitforgehq/ui/overlay`: a `bf-menu-button` whose menu holds
180
+ System, Light and Dark as radio items, for a crowded top bar. Same `value` / `changed` / `label`
181
+ as `bf-theme-switch`, plus `iconOnly`, `variant`, `size`, `placement` (default `'below-end'`) and
182
+ an `icon` slot for the consumer's sun or moon.
183
+
184
+ ### description-list
185
+
186
+ New: `bf-description-list`, a native `dl` of label-value pairs for the details of one record, in
187
+ both shells. `layout="rows"` (default) divides the pairs with a `border` line and puts the label
188
+ beside the value once the list is 28rem wide (a container query, so a narrow pane stacks them);
189
+ `layout="grid"` lays them out as wrapping tiles. New type `BfDescriptionListLayout`.
190
+
191
+ ### description-item
192
+
193
+ New: `div[bfDescriptionItem]`, one pair of a `bf-description-list`: its required `label` is the
194
+ `dt`, its content (text, a badge, a link) the `dd`.
195
+
196
+ ### stat
197
+
198
+ New: `bf-stat`, one labelled figure in its own `dl`, with the value at the `title` size in
199
+ tabular figures, an `icon` slot for a tile at the start and a supporting line in the default slot.
200
+ `variant="card"` gives it the outlined `flat` card surface; `plain` (default) draws none. An
201
+ opt-in trend: `delta` (the formatted change, such as `+12%`; empty shows nothing) with `trend`
202
+ (`up`, `down`, `flat`) renders a pill beside the value with a CSS arrow mark and the direction as a
203
+ hidden word (new labels `statTrendUp`, `statTrendDown`, `statTrendFlat`). `trendIntent`
204
+ (`positive` default, up is good; `negative` when down is good, such as costs) picks `success` or
205
+ `danger`; flat is neutral. New types `BfStatVariant`, `BfStatTrend` and `BfStatTrendIntent`.
206
+
207
+ ### breadcrumb
208
+
209
+ New `bf-breadcrumb` in `@bitforgehq/ui`, the desktop breadcrumb trail: a `nav` (named by
210
+ `label`, default `provideBfLabels()`'s new `breadcrumb` key, "Breadcrumb") around an `ol` of
211
+ `li[bfBreadcrumbItem]` levels, with CSS chevrons between them that are never announced.
212
+ `maxItems` collapses a longer trail to the first level and the last `maxItems - 1`, behind an
213
+ ellipsis button (the new `breadcrumbExpand` label, "Show 2 more levels") that shows the hidden
214
+ levels in place and focuses the first one; `bfSlot="overflow"` replaces that button with the app's
215
+ own control, such as a `bf-menu-button` of the hidden levels. It replaces the hand-built
216
+ breadcrumb the back-button docs showed.
217
+
218
+ ### breadcrumb-item
219
+
220
+ New `li[bfBreadcrumbItem]` in `@bitforgehq/ui`: one level of a `bf-breadcrumb` on the app's own
221
+ `<li>`. A projected link takes the muted link look, the hover underline and the focus ring (at
222
+ least 24 px tall, 44 px under a coarse pointer); the last level is the current page, wrapped in
223
+ `aria-current="page"` with no chevron.
224
+
225
+ ### sidebar
226
+
227
+ New `bf-sidebar`: the vertical navigation of a desktop app, a `nav` landmark with `header`,
228
+ default and `footer` slots, named by `label` (else the new `navigation` label, "Main").
229
+ `collapsed` (a model the app binds to its own toggle button) turns it into an icon rail. Below a
230
+ breakpoint, hide it with `hidden md:flex` and drive a `bf-menu-button` from the same routes.
231
+
232
+ ### top-nav
233
+
234
+ New `bf-top-nav`: the horizontal navigation of a desktop app with few sections, a `nav` row of
235
+ `a[bfNavItem]` links that wraps in a narrow container, named by `label` (else `navigation`).
236
+
237
+ ### nav-group
238
+
239
+ New `bf-nav-group`: a titled group of sidebar links, `role="group"` named by its small heading,
240
+ which stays the group's name (hidden visually) while the sidebar is collapsed.
241
+
242
+ ### nav-item
243
+
244
+ New `a[bfNavItem]`: one destination of a `bf-sidebar` or `bf-top-nav` on the app's own
245
+ `<a routerLink routerLinkActive ariaCurrentWhenActive="page">`. Neutral at rest, the tonal fill
246
+ for the current page from `aria-current="page"` (as an `a[bfButton]` ghost link), an `icon` slot,
247
+ `size` from `controlSize` (44 px at least under a coarse pointer) and a `badge` count announced
248
+ with the new `navBadge(count)` label ("3 new"). In a collapsed sidebar it shows its icon only,
249
+ keeps its label as screen-reader text and as its `title`, and turns the badge into a dot.
250
+
251
+ ### tab-group
252
+
253
+ New `bf-tab-group`: in-page tabs over the content they switch, in both shells. A `tablist` of
254
+ `button role="tab"` from its `bf-tab-panel` children, the `selected` model of the shown panel's
255
+ `value` with `changed` after a user change (unset or unknown shows the first enabled panel),
256
+ named by `label` (else the new `tabGroup` label, "Tabs"), `size` from `controlSize` (44 px at
257
+ least under a coarse pointer) and `fitted` to share the width. A roving tab stop and automatic
258
+ activation: the arrow keys (mirrored under `dir="rtl"`), Home and End select and focus a tab,
259
+ skipping disabled ones. The selected tab is underlined in `primary`; the row scrolls sideways
260
+ when the tabs do not fit. New `BfTabGroupHarness` in `@bitforgehq/ui/testing` (`getTabLabels()`,
261
+ `getSelectedLabel()`, `selectTab(label)`, `getPanelText()`).
262
+
263
+ ### tab-panel
264
+
265
+ New `bf-tab-panel`: one tab of a `bf-tab-group` and its content (`role="tabpanel"`,
266
+ `aria-labelledby` its tab, `tabindex="0"`): `value`, `label`, `disabled`, a `badge` count
267
+ announced with `tabBadge(count)` and an `icon` slot shown in the tab. Content in an `ng-template`
268
+ is created only while the panel shows; `keepAlive` keeps it, hidden, once visited.
269
+
270
+ ### docs site
271
+
272
+ The docs site's initial bundle is 587 kB and the embedded mobile demo's 653 kB, over their 580 kB
273
+ and 650 kB budget warnings: the example registry (one entry per example, in the initial bundle)
274
+ gained about 40 examples for the desktop widgets, and the mobile demo's project page and Me tab now
275
+ render `bf-stat`, `bf-description-list` and `bf-theme-switch`. The `initial` warnings
276
+ (`angular.json`) are now 600 kB (docs) and 670 kB (mobile demo). The web demo's initial bundle (511 kB:
277
+ its stylesheet holds the classes of every library widget, and the desktop frame now uses the side
278
+ panel and tooltips) warns at 540 kB instead of 500 kB. The mobile demo's (677 kB) warns at 690 kB: `BF_DEFAULT_LABELS`
279
+ is one object every app ships, and it gained about 60 keys for the new widgets.
280
+
281
+ ### labels
282
+
283
+ New keys for the desktop widgets: `navigation` ("Main") and `navBadge(count)` ("{count} new")
284
+ for `bf-sidebar`, `bf-top-nav` and `a[bfNavItem]`; `breadcrumb` ("Breadcrumb") and
285
+ `breadcrumbExpand(count)` ("Show 2 more levels") for `bf-breadcrumb`; `tableSelectAll` ("Select
286
+ all rows") for `bf-table`; `theme`, `themeSystem`, `themeLight` and `themeDark` for
287
+ `bf-theme-switch` and `bf-theme-menu-button`.
288
+
289
+ New keys for `bf-pagination`: `pagination` ("Pagination"), `previousPage` ("Previous page"),
290
+ `nextPage` ("Next page"), `pageOf(page, count)` ("Page 3 of 12") and `goToPage(page)` ("Page 3").
291
+
292
+ New keys for the `bf-stat` trend: `statTrendUp` ("Up"), `statTrendDown` ("Down") and
293
+ `statTrendFlat` ("No change"), the hidden direction word before a delta.
294
+ New keys for `@bitforgehq/ui/charts`: `chart` ("Chart"), `chartLegend` ("Legend"), `chartValues`
295
+ ("Data points, arrow keys move between them"), `chartShowData` ("Show data"), `chartHideData`
296
+ ("Hide data"), `chartCategory` ("Category"), `chartValue` ("Value"), `chartEmpty` ("No data to
297
+ show") and `chartError` ("The data could not be loaded").
298
+
299
+ ### skeleton
300
+
301
+ New `bf-skeleton` in `@bitforgehq/ui`, a loading placeholder for both shells: `shape` (the new
302
+ `BfSkeletonShape`: `text` lines, a `rect` or a `circle`), `lines` for several text lines with a
303
+ shorter last one, and `width` / `height` as CSS lengths. It draws the `surface-sunken` fill with a
304
+ `surface` shimmer that sweeps from the inline start (hidden under reduced motion) and carries
305
+ `aria-hidden="true"`; the docs mark the loading region with `aria-busy="true"` and show a card and
306
+ a table of skeleton rows.
307
+
308
+ ### pagination
309
+
310
+ New `bf-pagination` in `@bitforgehq/ui`, page navigation for both shells: a `nav` (named by
311
+ `label`, else the new `pagination` label) with Previous and Next buttons around the page buttons.
312
+ `[(page)]` is 1-based; the count comes from `pageCount`, else `total` over `pageSize` (default 10).
313
+ The row keeps the first and the last page and `siblings` (default 1) on each side of the current
314
+ one, which carries `aria-current="page"`, and turns hidden runs into an `aria-hidden` ellipsis, at
315
+ a constant number of slots. `compact` shows "Page 3 of 12" (`pageOf`) between the arrows as a
316
+ polite live region. At the ends the arrows are `aria-disabled` and stay focusable. `size` follows
317
+ `controlSize`; the chevrons are CSS and mirror under `dir="rtl"`. New `BfPaginationHarness` in
318
+ `@bitforgehq/ui/testing` (`getCurrentPage()`, `getPageCount()`, `getVisiblePages()`, `goTo()`,
319
+ `next()`, `previous()`).
320
+
321
+ ### side-panel
322
+
323
+ New `BfSidePanelService` and `bf-side-panel` in `@bitforgehq/ui/overlay`: a full-height panel
324
+ that slides in from the inline end edge (the left under `dir="rtl"`; a cross-fade under reduced
325
+ motion). `open(component | template, { data, width, modal, ariaLabel, injector })` returns a
326
+ `BfSidePanelRef<R, C>` (`close(result)`, `afterClosed`, `closed()`, `componentInstance`); the
327
+ content injects it and `BF_SIDE_PANEL_DATA`, and a template gets `{ $implicit: ref, data }`
328
+ (`BfSidePanelTemplateContext`). `width` is a `BfSidePanelWidth`: `sm` 360 px, `md` 480 px
329
+ (default), `lg` 640 px, capped at the viewport. Modal by default (a scrim that closes it, a focus
330
+ trap, an inert page, no page scroll); `modal: false` keeps the page usable beside it, and
331
+ `Escape` closes it only while focus is inside it. `bf-side-panel` renders the `title` as the
332
+ `h2` the panel is labelled by, a close button named by the `close` label (`closeLabel`), a
333
+ scrolling body and a `footer` slot kept at the bottom. New `BfSidePanelHarness` in
334
+ `@bitforgehq/ui/testing` (`getTitle()`, `close()`, `isOpen()`). Desktop only; in the Capacitor
335
+ shell use `bf-sheet` or `bf-modal`.
336
+
337
+ ### overlay core
338
+
339
+ New `modal` option of `BfOverlayService.open()` (default true): `modal: false` opens an overlay
340
+ with no backdrop, no focus trap and no `aria-modal`, outside `BfOverlayStack` (the page stays
341
+ usable and scrollable); focus moves in, `Escape` closes it only while focus is inside it, and on
342
+ close focus returns to the opener only from inside it. New `position: 'end'` (full height
343
+ against the inline end edge) through the new `BfOverlayPlacement` type
344
+ (`BfOverlayPosition | 'end'`), the type of `BfOverlayOptions.position` now. New motion helpers
345
+ `slideInFromEnd()` and `slideOutToEnd()`, mirrored under `dir="rtl"`.
346
+
347
+ ### tooltip
348
+
349
+ New `[bfTooltip]` (`BfTooltipDirective`) and its `bf-tooltip` pill (`BfTooltipComponent`) in
350
+ `@bitforgehq/ui/overlay`: a short hint beside a control after a 500 ms hover or at once on keyboard
351
+ focus, hidden on leave, blur and `Escape`, and kept while the pointer moves onto it (WCAG 1.4.13).
352
+ `bfTooltipPlacement` (`BfTooltipPlacement`: `top` default, `bottom`, `start`, `end`, logical)
353
+ flips when it would overflow. While it shows, the control is described by it
354
+ (`aria-describedby`). Nothing shows under a coarse pointer. Fades in over 120 ms, not under
355
+ reduced motion. New `BfTooltipHarness` in `@bitforgehq/ui/testing`.
356
+
357
+ ### popover
358
+
359
+ New `[bfPopoverTriggerFor]` (`BfPopoverTriggerDirective`, exported as `bfPopoverTrigger`) and the
360
+ `bf-popover` panel (`BfPopoverComponent`) in `@bitforgehq/ui/overlay`: a non-modal `role="dialog"`
361
+ panel anchored to its trigger, for filters, details or a small form. `bfPopoverPlacement`
362
+ (`BfPopoverPlacement`, the menu's four placements), `bfPopoverLabel`, `(bfPopoverOpened)` and
363
+ `(bfPopoverClosed)`, and `open()`, `close()`, `toggle()`, `isOpen()`. The trigger's control gets
364
+ `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls`; focus moves in on open, `Tab` is not
365
+ trapped (it closes the panel past either end), and `Escape`, a click outside or a navigation close
366
+ it and return focus. New `BfPopoverHarness` in `@bitforgehq/ui/testing`.
367
+
368
+ ### menu
369
+
370
+ The menu's positions now come from the anchored-panel positioning the popover and tooltip share.
371
+ No behaviour change.
372
+
373
+ ### combobox
374
+
375
+ New `bf-combobox` (`BfComboboxComponent<T>`) in `@bitforgehq/ui/overlay`: a text field with a
376
+ list of options that filters as you type, to pick one value (the ARIA 1.2 combobox pattern with a
377
+ list autocomplete). `[(value)]` is `T | null`; `options` are `BfComboboxOption<T>` (`value`,
378
+ `label`, `description`, `disabled`, `group`); `compareWith`, `label`, `ariaLabel`,
379
+ `ariaLabelledby`, `placeholder`, `helpText`, `errorMessage`, `size` (`fieldSize`) and the Signal
380
+ Forms value contract like `bf-select`. Filters by label, ignoring case and accents; `filter`
381
+ (`BfComboboxFilter<T>`) replaces the match, `null` turns local filtering off for server results,
382
+ `(queried)` emits the typed text and `loading` shows a spinner row. Arrow keys open and move
383
+ (wrapping), `Home`/`End`, `Enter` picks, `Escape` closes then drops the typed text, `Tab` picks
384
+ nothing; leaving the field reverts unmatched text to the picked label (emptied text clears the
385
+ value, `(changed)` emits `null`). Grouped options render in named `role="group"` sections. New
386
+ label key `comboboxEmpty` ("No results", or `emptyLabel`) and a `BfComboboxHarness` in
387
+ `@bitforgehq/ui/testing` (`open()`, `type(query)`, `getOptionLabels()`, `selectOption(label)`,
388
+ `getValueText()`, `getChipLabels()`, `removeChip(label)`) for both comboboxes.
389
+
390
+ ### multi-combobox
391
+
392
+ New `bf-multi-combobox` (`BfMultiComboboxComponent<T>`) in `@bitforgehq/ui/overlay`: the
393
+ combobox for several values, `[(value)]` a `readonly T[]`. Picked values are tags inside the
394
+ field, each with a remove button named by the new `comboboxRemove(label)` label ("Remove
395
+ Design"); `Backspace` in the empty field removes the last tag, the list stays open after a pick
396
+ and picked options carry a check mark (`aria-multiselectable`, `aria-selected`). Same options,
397
+ filtering, server search and Signal Forms contract as `bf-combobox`.
398
+
399
+ ### date-input
400
+
401
+ New `bf-date-input` in `@bitforgehq/ui/overlay`: a date field typed part by part, the day, month
402
+ and year as spinbuttons in the locale's order and separators (`Intl.DateTimeFormat`
403
+ `formatToParts`), with muted placeholders (dd/mm/yyyy). Digits type in and move on by themselves,
404
+ Arrow Up/Down step with wrapping, Backspace clears, Arrow Left/Right move between the parts. The
405
+ value is an ISO date string (`'YYYY-MM-DD'`), `null` while empty or incomplete, and `(changed)`
406
+ emits each user change. `picker` (`BfDateInputPicker`: `'calendar'`, the default, or `'none'`)
407
+ adds a button that opens a `bf-calendar` in an anchored popover; a pick closes it and returns
408
+ focus. `min`, `max` and `disabledDate` bound the calendar and show the field's own error for a
409
+ typed date outside them (as for an incomplete or impossible one). `native` renders
410
+ `input type="date"` under a coarse pointer, for the Capacitor shell. `locale`, `size`
411
+ (`fieldSize`), `openPicker()`, `closePicker()`, `focus()`, and the Signal Forms value contract
412
+ like `bf-input`. New label keys `dateInputDay`, `dateInputMonth`, `dateInputYear`,
413
+ `dateInputOpenCalendar`, `dateInputInvalid` and `dateInputUnavailable`, and a
414
+ `BfDateInputHarness` in `@bitforgehq/ui/testing` (`getValue()`, `setValue(text)`, `clear()`,
415
+ `openPicker()`, `pickDate(iso)`).
416
+
417
+ ### time-input
418
+
419
+ New `bf-time-input` in `@bitforgehq/ui/overlay`: a time-of-day field typed as hour and minute
420
+ (and second with `seconds`), with an AM/PM part under a 12-hour clock. The clock follows the
421
+ locale; `hourCycle` (`BfTimeInputHourCycle`: `'h12'` or `'h23'`) forces one. The value is an ISO
422
+ time on a 24-hour clock (`'HH:mm'`, `'HH:mm:ss'` with seconds). `picker` (`BfTimeInputPicker`:
423
+ `'list'`, the default, or `'none'`) adds a button that opens a listbox of times every `step`
424
+ minutes (30 by default), scrolled to the value; `min` and `max` disable the times outside them
425
+ and show an error for a typed one. `native` renders `input type="time"` under a coarse pointer.
426
+ New label keys `timeInputHour`, `timeInputMinute`, `timeInputSecond`, `timeInputDayPeriod`,
427
+ `timeInputOpenList`, `timeInputInvalid`, `timeInputMin` and `timeInputMax`, and a
428
+ `BfTimeInputHarness` in `@bitforgehq/ui/testing` (`setValue(text)`, `pickTime(time)`).
429
+
430
+ ### datetime-input
431
+
432
+ New `bf-datetime-input` in `@bitforgehq/ui/overlay`: the date and the time parts in one field,
433
+ valued as a local ISO date and time without a zone (`'YYYY-MM-DDTHH:mm'`). `picker`
434
+ (`BfDateTimeInputPicker`: `'popover'`, the default, or `'none'`) opens one popover with the
435
+ calendar and a list of times side by side: a day moves focus to the list, a time closes it.
436
+ `min` and `max` bound the calendar and, on their own day, the list. `step`, `hourCycle`,
437
+ `disabledDate`, `native` (`input type="datetime-local"`) and the field contract as on the date
438
+ and time inputs. New `BfDateTimeInputHarness` in `@bitforgehq/ui/testing` (`pickDate(iso)`,
439
+ `pickTime(time)`, `setValue(text)`).
440
+
441
+ ### line-chart
442
+
443
+ New `bf-line-chart` in the new `@bitforgehq/ui/charts` entry (which imports only Angular core and
444
+ common and the root entry): one or more `series` (`BfChartSeries`: `name`, `values` with `null`
445
+ for a gap) over `categories`, drawn in SVG with the `--color-chart-*` tokens. A value axis of nice
446
+ ticks (1, 2 or 5 × 10ⁿ), a legend that shows and hides series (`aria-pressed`; `legend`:
447
+ `BfChartLegendPosition` `top`, `bottom` or `none`), a crosshair tooltip with markers on hover and
448
+ on keyboard focus (the plot is focusable; the arrow keys, `Home`, `End` and `Escape` move between
449
+ the categories and a polite live region reads each one), and a Show data toggle that shows the data
450
+ as a table. `area` fills under the lines, `curve` (`BfChartCurve`: `linear` or `smooth`, a monotone
451
+ cubic) shapes them, `valueFormat` formats every value (default `Intl.NumberFormat` in the document's
452
+ language), `height` sets the plot's height and the width follows the container (`ResizeObserver`;
453
+ 600 px on the server). Lines draw in on the first render and on every data change, not under
454
+ reduced motion; `dir="rtl"` mirrors the category axis.
455
+ New `BfChartHarness` in `@bitforgehq/ui/testing` for the line, bar and donut charts:
456
+ `getLabel()`, `getSeriesNames()`, `isSeriesShown()`, `toggleSeries()` and `showData()` (the data
457
+ table's rows).
458
+
459
+ ### bar-chart
460
+
461
+ New `bf-bar-chart` in `@bitforgehq/ui/charts`: series over categories as bars from a zero
462
+ baseline, side by side or `stacked`, vertical or `orientation="horizontal"`
463
+ (`BfBarChartOrientation`), with the line chart's value axis, legend, tooltip, keyboard reading and
464
+ data table. Bars grow from the baseline on the first render and on every data change, not under
465
+ reduced motion; `dir="rtl"` mirrors the horizontal axis.
466
+
467
+ ### donut-chart
468
+
469
+ New `bf-donut-chart` in `@bitforgehq/ui/charts`: `slices` (`BfDonutSlice`: `label`, `value`) as
470
+ the parts of a whole, with a `thickness`, a `center` slot for a total, a legend that hides slices
471
+ (the shares follow), a tooltip with each slice's value and share, keyboard reading and a data
472
+ table. Slices draw in along the ring, not under reduced motion, and run counter-clockwise under
473
+ `dir="rtl"`.
474
+
475
+ ### sparkline
476
+
477
+ New `bf-sparkline` in `@bitforgehq/ui/charts`: a tiny line of one series with no axes, legend or
478
+ tooltip, for a `bf-stat`'s supporting line or a table cell; `area`, `curve` and `height`, a dot on
479
+ the latest value. With a `label` it is a named image, without one it is decorative.
480
+
481
+ ### chart-card
482
+
483
+ New `bf-chart-card` in `@bitforgehq/ui/charts`: the `flat` card surface around one chart, with a
484
+ `title` (the chart's name when it has no `label`; `headingLevel`, `BfChartCardHeadingLevel` 2 or
485
+ 3), a `period` slot for the range control, the `legend` placement for the chart inside, and
486
+ `loading` (a `bf-skeleton`, `aria-busy`), `empty` and `error` states with slots of the same names
487
+ and the `chartEmpty` and `chartError` labels as fallbacks.
488
+
7
489
  ## 0.3.0 (2026-09-28)
8
490
 
9
491
  ### Migration
package/README.md CHANGED
@@ -2,12 +2,16 @@
2
2
 
3
3
  Bitforge's shared Angular component library: Meadow Sage design, Tailwind CSS v4, signals-first, Angular 22.
4
4
 
5
+ The theme is the Meadow Sage palette (warm off-white canvas, white surfaces, sage-tinted neutrals and status colours, pill controls). Its default primary is a neutral indigo (`#2b26ba`), not the design's sage green: it is meant to be overridden with your brand colour (`--color-primary` and its set; the docs site's theme configurator derives them), so an indigo primary after install is expected, not a broken install.
6
+
5
7
  - Widgets: `@bitforgehq/ui`
6
8
  - Number field: `@bitforgehq/ui/number-input` (`bf-number-input`; loads Angular Forms, peer
7
9
  dependency `@angular/forms`)
8
10
  - Icons: `@bitforgehq/ui/icons` (Lucide data, peer dependency `lucide`)
9
11
  - Overlay core: `@bitforgehq/ui/overlay` (no Capacitor; peer dependencies `@angular/cdk` and
10
12
  `rxjs`)
13
+ - Charts: `@bitforgehq/ui/charts` (line, bar and donut charts, sparklines and the chart card, drawn
14
+ in SVG with the `--color-chart-*` tokens; no peer dependency beyond the widgets entry)
11
15
  - Mobile shell: `@bitforgehq/ui/mobile` (from v0.2)
12
16
  - Test support: `@bitforgehq/ui/testing` (jsdom shims, `provideBfTesting()`, CDK component
13
17
  harnesses; needs `@angular/cdk` as a dev dependency)
@@ -79,7 +83,7 @@ In `src/index.html`, give `<body>` the theme background and text colour, so the
79
83
 
80
84
  ### 7. Switch to dark mode
81
85
 
82
- 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.
86
+ 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.
83
87
 
84
88
  ```html
85
89
  <html lang="en" class="dark">
@@ -136,15 +140,16 @@ Render library components only in the browser, for example in a `@defer` block o
136
140
  route. Support will be decided before 1.0.
137
141
 
138
142
  The one server-side check is a render smoke, not a support test: the library repository's
139
- `tools/ssr-smoke.ts` renders 28 fixtures with `renderApplication()` (every root widget, `bf-icon`,
140
- `bf-number-input`, a `/mobile` page with `provideBfShell()`, `bf-page`, `bf-header` and
141
- `bf-content`, `bf-tabs` with `bf-stack-outlet` under the router, and an `/overlay` dialog), and all
142
- of them render without an error. 19 of them render identically twice. The other 9 number their ids
143
- differently on every render, from a module-level counter, so they could not hydrate: `bf-checkbox`,
144
- `bf-input`, `bf-radio-group`, `bf-segmented`, `bf-select`, `bf-textarea`, `bf-toggle`,
145
- `bf-number-input` and the `/overlay` dialog. Hydration itself is still not supported or tested, and
146
- more stands in its way: `/mobile` writes to `<html>` and adds a keyboard probe to `<body>`, and an
147
- `/overlay` dialog renders outside the app root and marks it inert.
143
+ `tools/ssr-smoke.ts` renders 42 fixtures with `renderApplication()` (every root widget, `bf-icon`,
144
+ `bf-number-input`, a `/charts` line chart, a `/mobile` page with `provideBfShell()`, `bf-page`,
145
+ `bf-header` and `bf-content`, `bf-tabs` with `bf-stack-outlet` under the router, and an `/overlay`
146
+ dialog), and all of them render without an error. 27 of them render identically twice. The other 15
147
+ number their ids differently on every render, from a module-level counter, so they could not
148
+ hydrate: `bf-calendar`, `bf-checkbox`, `bf-input`, `bf-radio-group`, `bf-segmented`, `bf-select`,
149
+ `bf-sidebar`, `bf-tab-group`, `bf-table`, `bf-textarea`, `bf-theme-switch`, `bf-toggle`,
150
+ `bf-line-chart`, `bf-number-input` and the `/overlay` dialog. Hydration itself is still not
151
+ supported or tested, and more stands in its way: `/mobile` writes to `<html>` and adds a keyboard
152
+ probe to `<body>`, and an `/overlay` dialog renders outside the app root and marks it inert.
148
153
 
149
154
  ## Testing
150
155
 
@@ -156,7 +161,8 @@ jsdom lacks them. `provideBfTesting()` passes defaults and labels to the widgets
156
161
  overlays and themes leave on the document after each test (when TestBed destroys the test
157
162
  environment, so keep its default `teardown: { destroyAfterEach: true }`), and the CDK harnesses
158
163
  (`BfSelectHarness`, `BfRadioGroupHarness`, `BfSegmentedHarness`, `BfDialogHarness`,
159
- `BfMenuButtonHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfDesktopToastHarness`) drive the
164
+ `BfMenuButtonHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfDesktopToastHarness`,
165
+ `BfTableHarness`, `BfPaginationHarness`, `BfTabGroupHarness`, `BfCalendarHarness`, `BfChartHarness`) drive the
160
166
  widgets whose DOM a spec should not query by hand; overlays come from
161
167
  `TestbedHarnessEnvironment.documentRootLoader(fixture)`. An app on the mobile shell uses
162
168
  `provideBfShellTesting()` from `@bitforgehq/ui/mobile` instead (router, shell, an in-memory theme