@bitforgehq/ui 0.3.1 → 0.3.3

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 (49) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +9 -8
  3. package/ai/MANIFEST.index.json +18 -0
  4. package/ai/MANIFEST.json +380 -31
  5. package/ai/REFERENCE.index.json +63 -0
  6. package/ai/REFERENCE.json +265 -7
  7. package/ai/UI_GUIDE.md +59 -33
  8. package/ai/components/app-frame.md +335 -0
  9. package/ai/components/card.md +56 -2
  10. package/ai/components/empty-state.md +99 -6
  11. package/ai/components/header.md +14 -1
  12. package/ai/components/sidebar-toggle.md +287 -0
  13. package/ai/components/sidebar.md +188 -10
  14. package/ai/components/stat.md +74 -10
  15. package/ai/components/table.md +152 -5
  16. package/ai/components/tabs.md +5 -1
  17. package/ai/components/toolbar.md +69 -4
  18. package/ai/reference/bf-app-frame-breakpoint.md +27 -0
  19. package/ai/reference/bf-app-frame-content-width.md +29 -0
  20. package/ai/reference/bf-empty-state-heading-level.md +1 -1
  21. package/ai/reference/bf-empty-state-heading.md +33 -0
  22. package/ai/reference/bf-labels.md +7 -0
  23. package/ai/reference/bf-select-harness.md +4 -0
  24. package/ai/reference/bf-sidebar-state-service.md +21 -0
  25. package/ai/reference/bf-sidebar-toggle-harness.md +32 -0
  26. package/ai/reference/bf-size.md +1 -0
  27. package/ai/reference/bf-stat-appearance.md +33 -0
  28. package/ai/reference/bf-stat-variant.md +1 -1
  29. package/ai/reference/bf-table-harness.md +7 -1
  30. package/ai/reference/bf-table-stack.md +29 -0
  31. package/ai/reference/bf_sidebar_state_storage.md +20 -0
  32. package/ai/reference/provide-bf-labels.md +4 -3
  33. package/ai/reference/provide-bf-sidebar-state.md +20 -0
  34. package/bin/bf-ui.js +433 -3
  35. package/fesm2022/bitforgehq-ui-mobile.mjs +11 -2
  36. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  37. package/fesm2022/bitforgehq-ui-overlay.mjs +571 -446
  38. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  39. package/fesm2022/bitforgehq-ui-testing.mjs +22 -2
  40. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  41. package/fesm2022/bitforgehq-ui.mjs +777 -127
  42. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  43. package/llms-full.txt +1605 -43
  44. package/llms.txt +11 -0
  45. package/package.json +1 -1
  46. package/theme.css +13 -0
  47. package/types/bitforgehq-ui-overlay.d.ts +51 -0
  48. package/types/bitforgehq-ui-testing.d.ts +49 -1
  49. package/types/bitforgehq-ui.d.ts +284 -20
package/CHANGELOG.md CHANGED
@@ -4,6 +4,163 @@ 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.3 (2026-10-29)
8
+
9
+ ### header
10
+
11
+ Fix: `bf-header` measures its title and slots in the next animation frame after a resize, not
12
+ inside the `ResizeObserver` callback. Its measurement writes the title inset and height, which
13
+ resize the observed elements, and WebKit then reported "ResizeObserver loop completed with
14
+ undelivered notifications": an error event that an app's global error listener
15
+ (`provideBrowserGlobalErrorListeners()`) logs as an error. Several resizes in one frame now
16
+ measure once.
17
+
18
+ ### app-frame
19
+
20
+ Fix: the narrow header bar puts the `menu` slot at the inline start, before the `brand` (0.3.2 put
21
+ it after the brand), with the actions at the end as before: the top app bar convention. The menu
22
+ also comes first in the tab order. No API change; an app needs no change unless it relied on the
23
+ brand coming first.
24
+
25
+ ### table
26
+
27
+ Fix: stacked rows (`stack="narrow"` and `stack="coarse"`) show their column labels again when the
28
+ package is installed from npm. 0.3.2 wrote the label as `content-[attr(data-label)_/_""]`; the
29
+ library build escaped its quotes in the FESM (`\"\"`), so Tailwind built the rule for a class no
30
+ cell carries and every label `::before` computed to `none`. The cells now carry
31
+ `before:content-data-label`, a utility `theme.css` defines (below). Apps that added the
32
+ workaround `@source inline('@max-xl:before:content-[attr(data-label)_/_""] pointer-coarse:before:content-[attr(data-label)_/_""]')`
33
+ can remove it. consumer-smoke now opens a stacked table built from the packed package in
34
+ Chromium, under a fine and a coarse pointer, and fails if any FESM class string holds a
35
+ backslash escape.
36
+
37
+ The stacked Sort by select's first option is Not sorted (new label key `tableSortNone`, "Not
38
+ sorted"): it shows while `sort` is null, so the select is no longer an empty box, and choosing it
39
+ sets `sort` back to null. New `BfTableHarness.getSortSelect()` returns that select's
40
+ `BfSelectHarness` (`null` when the table cannot stack or has no sortable column).
41
+
42
+ ### theme
43
+
44
+ New utility `content-data-label` in `theme.css`: `content: attr(data-label) / ''` (the element's
45
+ `data-label`, with empty alternative text). The stacked `bf-table` uses it behind its stack
46
+ variants; apps can use it with `before:` the same way.
47
+
48
+ ## 0.3.2 (2026-10-29)
49
+
50
+ ### docs site
51
+
52
+ The docs initial bundle (620 kB) warns at 640 kB instead of 600 kB: the example registry ships in
53
+ the initial bundle and gained this release's examples (moving it into a lazy chunk is the lasting
54
+ fix). The web demo loads its app frame lazily, like its pages, and its initial bundle (542 kB, the
55
+ stylesheet with every widget's classes) warns at 560 kB instead of 540 kB.
56
+
57
+ ### tabs
58
+
59
+ The `## Desktop` docs point at `bf-app-frame` for the frame around the desktop navigation.
60
+
61
+ ### header
62
+
63
+ The `## Desktop` docs say `bf-app-frame` supplies the app-level header bar below its breakpoint.
64
+
65
+ ### app-frame
66
+
67
+ New `bf-app-frame` in `@bitforgehq/ui`: the frame of a desktop web app. Slots `sidebar` (a
68
+ `bf-sidebar`, in a column that sticks to the viewport at full height from the breakpoint up),
69
+ `brand`, `menu` (the narrow-screen `bf-menu-button`) and `actions` (the header bar shown below
70
+ the breakpoint) and the default slot (the page, in the `main` landmark). `breakpoint`
71
+ (`BfAppFrameBreakpoint`: `'md'` default, `'lg'`), `contentWidth` (`BfAppFrameContentWidth`:
72
+ `'md'` 48rem, `'lg'` 64rem, `'xl'` 80rem default, `'full'`) and `mainId`. A skip link, first in
73
+ the tab order and hidden until focused, moves focus to the main landmark by script, so it works
74
+ under `<base href="/">`; new label key `skipToContent` ("Skip to main content"). Inside another
75
+ `main` landmark the content drops its `main` role. Desktop only: the Capacitor shell keeps
76
+ `bf-tabs`.
77
+
78
+ ### toolbar
79
+
80
+ On a narrow row the actions no longer end up alone on a row of their own: the status and the
81
+ actions are now one group that takes the rest of the last row and wraps as a whole, with the
82
+ status at the start and the actions at the inline end. The wide layout is unchanged, and a
83
+ toolbar without actions keeps the status as a plain item of the row. New `toolbar/narrow`
84
+ example at phone width.
85
+
86
+ ### stat
87
+
88
+ New `variant="tile"`: a sunken, borderless tile (`surface-sunken`, the `card` radius and
89
+ padding) for a row of stats inside a raised `bf-card`; on it the icon tile turns `surface`.
90
+ `variant` now takes the new named type `BfStatAppearance` (`BfStatVariant | 'tile'`); the
91
+ released `BfStatVariant` keeps its two members, so `Record`s over it keep compiling. New
92
+ `stat/dashboard` example with one surface per level.
93
+
94
+ ### card
95
+
96
+ New "Nesting surfaces" docs section: a raised card holds sunken tiles, never an outlined
97
+ surface (`flat`, a `card` stat), and each level of a dashboard keeps one surface style. New
98
+ `card/nesting` example. No code change.
99
+
100
+ ### empty-state
101
+
102
+ `headingLevel` now takes the new named type `BfEmptyStateHeading` (`BfEmptyStateHeadingLevel |
103
+ 1`): `1` renders the title as the page's `h1`, for an empty state that is the page (a not-found
104
+ or no-access page). The released `BfEmptyStateHeadingLevel` keeps its two members, so `Record`s
105
+ over it keep compiling. New "As the page" docs section with the `empty-state/page` example
106
+ (centred in a tall area, a primary action and a secondary link) and the code-only
107
+ `empty-state/not-found-page`.
108
+
109
+ ### table
110
+
111
+ New opt-in `stack` input (`BfTableStack`: `'never'` by default, so existing tables do not
112
+ change, `'narrow'` or `'coarse'`): while the table is narrower than 36rem (a container query on
113
+ the host, which then gets `@container`) or, for `'coarse'`, under a coarse pointer at any width,
114
+ each body row renders as a sunken tile of label/value pairs. The markup stays a `table` with
115
+ explicit `table`, `rowgroup`, `row`, `columnheader` and `cell` roles (some browsers drop a
116
+ table's semantics once its display changes), the header row is visually hidden but still read,
117
+ the selection checkbox and the first cell head each tile, and every other cell shows its column
118
+ label (`data-label`, a `::before` with empty alternative text, so it is not read twice). A bar
119
+ above the tiles holds a labelled Select all checkbox and a Sort by `bf-select`, since the hidden
120
+ header's controls are not shown. New label keys `tableSortBy` ("Sort by"), `tableSortAscending`
121
+ and `tableSortDescending` (`"Hours, ascending"`). `BfTableHarness` reads and drives a stacked
122
+ table the same way. New `table/stacked` example and a Stacked rows docs section.
123
+
124
+ ### sidebar-toggle
125
+
126
+ New `bf-sidebar-toggle` in `@bitforgehq/ui/overlay`: a text icon button placed inside a
127
+ `bf-sidebar` (header or footer) that flips the sidebar between full width and an icon rail by
128
+ writing its `collapsed` model, so `[(collapsed)]` sees every press. Its name and `bfTooltip` say
129
+ what the next press does (new label keys `sidebarCollapse` "Collapse sidebar" and `sidebarExpand`
130
+ "Expand sidebar"); `aria-expanded` follows the sidebar and `aria-controls` points at its `nav`.
131
+ Icons are the `collapse-icon` and `expand-icon` slots, only the matching one shown; `size` as
132
+ `bf-icon-button`. Outside a sidebar (in a `bf-top-nav`, or alone) it renders nothing. A
133
+ `BfSidebarToggleHarness` in `@bitforgehq/ui/testing` (`getLabel()`, `isExpanded()`, `toggle()`).
134
+
135
+ ### sidebar
136
+
137
+ New `BfSidebarStateService` (a `collapsed` signal) and `provideBfSidebarState()` in
138
+ `@bitforgehq/ui`: bind the sidebar's `[(collapsed)]` to `sidebarState.collapsed` and the provider
139
+ stores every change under `bf-sidebar-collapsed` in `BF_SIDEBAR_STATE_STORAGE` (`localStorage` in
140
+ the browser, memory elsewhere), read back at the next start; without the provider nothing is
141
+ written, and on the server nothing is read. The sidebar's `nav` now has an id, for a toggle's
142
+ `aria-controls`. The docs gain "Remember the collapsed sidebar" and a "Footer" composition
143
+ (Settings link, theme menu, toggle, version line) that works expanded and as an icon rail.
144
+
145
+ ### theme
146
+
147
+ Decision: the default primary stays indigo (`#2b26ba`). Making the Meadow Sage green the default
148
+ would recolour every app that relies on the indigo, a breaking change; a brand override recipe
149
+ gives a new app the green (or any brand) on day one instead. No token changes.
150
+
151
+ New `bf-ui theme <hex> [--dark <hex>]` command in the package's CLI (no new dependencies): it
152
+ derives the brand set (`--color-primary`, `-hover`, `-pressed`, `-soft`, `-soft-hover`,
153
+ `-soft-pressed`, `-strong`, `--color-on-primary`, `--color-primary-text`, `--color-selected`,
154
+ `--color-ring`) for light and dark mode with the docs configurator's OKLCH steps, keeping white
155
+ text on the brand in light mode; `--dark` sets the dark-mode primary (else the brand is lifted to
156
+ OKLCH lightness 0.76). It prints a `@theme` and a `.dark` block to paste after the theme import,
157
+ with every brand contrast pair's ratio in a comment (the pairs of the theme's contrast test, plus
158
+ `primary` at 3:1 on `bg` and `surface`), and exits 1 naming each pair below its minimum and the
159
+ nearest darker (light) or lighter (dark) shade that passes. The docs Theming page gains a "Brand
160
+ colour: the recipe" section (each token, how it is derived in light and dark, and why) with a
161
+ Meadow Sage green worked example (`bf-ui theme '#238652' --dark '#5cc48a'`; the design's
162
+ `#2f8f5b` gives white text only 4.04:1), and the agent guide and README point to the command.
163
+
7
164
  ## 0.3.1 (2026-10-29)
8
165
 
9
166
  ### calendar
package/README.md CHANGED
@@ -2,7 +2,7 @@
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.
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. `bunx bf-ui theme '#238652' --dark '#5cc48a'` prints the whole set for a brand colour, as a `@theme` and a `.dark` block to paste after the theme import, with every contrast pair's ratio, and exits 1 naming the failing pairs and a shade that passes (the docs site's Theming page has the recipe and a Meadow Sage green worked example).
6
6
 
7
7
  - Widgets: `@bitforgehq/ui`
8
8
  - Number field: `@bitforgehq/ui/number-input` (`bf-number-input`; loads Angular Forms, peer
@@ -140,16 +140,17 @@ Render library components only in the browser, for example in a `@defer` block o
140
140
  route. Support will be decided before 1.0.
141
141
 
142
142
  The one server-side check is a render smoke, not a support test: the library repository's
143
- `tools/ssr-smoke.ts` renders 42 fixtures with `renderApplication()` (every root widget, `bf-icon`,
143
+ `tools/ssr-smoke.ts` renders 43 fixtures with `renderApplication()` (every root widget, `bf-icon`,
144
144
  `bf-number-input`, a `/charts` line chart, a `/mobile` page with `provideBfShell()`, `bf-page`,
145
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
146
+ dialog), and all of them render without an error. 27 of them render identically twice. The other 16
147
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
+ hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-input`, `bf-radio-group`, `bf-segmented`,
149
+ `bf-select`, `bf-sidebar`, `bf-tab-group`, `bf-table`, `bf-textarea`, `bf-theme-switch`,
150
+ `bf-toggle`, `bf-line-chart`, `bf-number-input` and the `/overlay` dialog. Hydration itself is still
151
+ not supported or tested, and more stands in its way: `/mobile` writes to `<html>` and adds a
152
+ keyboard probe to `<body>`, and an `/overlay` dialog renders outside the app root and marks it
153
+ inert.
153
154
 
154
155
  ## Testing
155
156
 
@@ -37,6 +37,15 @@
37
37
  "summary": "Centred confirmation dialog with a title, message and a row of role-styled buttons",
38
38
  "since": "0.2.0"
39
39
  },
40
+ {
41
+ "name": "app-frame",
42
+ "selector": "bf-app-frame",
43
+ "className": "BfAppFrameComponent",
44
+ "category": "Navigation",
45
+ "entryPoint": "@bitforgehq/ui",
46
+ "summary": "Desktop app frame with skip link, sticky sidebar, narrow header and main content",
47
+ "since": "0.3.2"
48
+ },
40
49
  {
41
50
  "name": "avatar",
42
51
  "selector": "bf-avatar",
@@ -487,6 +496,15 @@
487
496
  "summary": "Vertical app navigation with brand, grouped links, footer and an icon rail mode",
488
497
  "since": "0.3.1"
489
498
  },
499
+ {
500
+ "name": "sidebar-toggle",
501
+ "selector": "bf-sidebar-toggle",
502
+ "className": "BfSidebarToggleComponent",
503
+ "category": "Navigation",
504
+ "entryPoint": "@bitforgehq/ui/overlay",
505
+ "summary": "Icon button inside a sidebar that collapses it to an icon rail and back",
506
+ "since": "0.3.2"
507
+ },
490
508
  {
491
509
  "name": "skeleton",
492
510
  "selector": "bf-skeleton",