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