@bitforgehq/ui 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +4 -4
  3. package/ai/MANIFEST.index.json +36 -0
  4. package/ai/MANIFEST.json +436 -24
  5. package/ai/REFERENCE.index.json +35 -0
  6. package/ai/REFERENCE.json +202 -10
  7. package/ai/UI_GUIDE.md +25 -4
  8. package/ai/components/app-frame.md +14 -7
  9. package/ai/components/badge.md +39 -2
  10. package/ai/components/header.md +9 -0
  11. package/ai/components/list-item.md +105 -9
  12. package/ai/components/step.md +112 -0
  13. package/ai/components/steps.md +124 -0
  14. package/ai/components/table.md +119 -5
  15. package/ai/components/timeline-event.md +144 -0
  16. package/ai/components/timeline.md +201 -0
  17. package/ai/components/toggle.md +11 -1
  18. package/ai/reference/bf-action-directive.md +3 -1
  19. package/ai/reference/bf-badge-emphasis.md +27 -0
  20. package/ai/reference/bf-labels.md +2 -0
  21. package/ai/reference/bf-select-harness.md +4 -0
  22. package/ai/reference/bf-step-heading-level.md +28 -0
  23. package/ai/reference/bf-table-align.md +1 -1
  24. package/ai/reference/bf-table-column-align.md +33 -0
  25. package/ai/reference/bf-table-column.md +7 -7
  26. package/ai/reference/bf-table-harness.md +18 -0
  27. package/ai/reference/bf-timeline-tone.md +31 -0
  28. package/ai/reference/bf-toggle-harness.md +55 -0
  29. package/ai/reference/provide-bf-labels.md +2 -2
  30. package/dark-ink.css +124 -0
  31. package/fesm2022/bitforgehq-ui-mobile.mjs +13 -3
  32. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  33. package/fesm2022/bitforgehq-ui-testing.mjs +55 -2
  34. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  35. package/fesm2022/bitforgehq-ui.mjs +714 -46
  36. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  37. package/llms-full.txt +1128 -26
  38. package/llms.txt +9 -0
  39. package/package.json +2 -1
  40. package/theme.css +37 -0
  41. package/types/bitforgehq-ui-testing.d.ts +90 -1
  42. package/types/bitforgehq-ui.d.ts +306 -18
package/CHANGELOG.md CHANGED
@@ -4,6 +4,138 @@ 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.4.0 (2026-10-29)
8
+
9
+ ### timeline
10
+
11
+ New: `bf-timeline`, a dated history of events (who did what, and when) for a record's history
12
+ or an audit trail, in both shells. A `role="list"` named by `label`, in the order the consumer
13
+ gives (it never sorts); each event's time is formatted with `Intl.DateTimeFormat` in the app's
14
+ `LOCALE_ID` (`timeFormat`, default a medium date and a short time; `locale` overrides it).
15
+
16
+ ### timeline-event
17
+
18
+ New: `bf-timeline-event`, one event of a `bf-timeline`: a 10 px marker coloured by `tone`
19
+ (`neutral`, `primary`, `success`, `warning`, `danger`, `info`; new type `BfTimelineTone`) on a
20
+ `border` line that ends at the last event, the event text in the default slot (links in it take
21
+ the link style), then `actor` and `time` (a `Date` or an ISO 8601 string, in a `time` element;
22
+ `timeText` shows a relative text while `datetime` keeps the exact value; an invalid time shows
23
+ nothing). A `bfSlot="end"` action sits at the inline end, or under the text once the event is
24
+ narrow.
25
+
26
+ ### table
27
+
28
+ New: toggle cells for a settings matrix (rows of alert kinds, columns of channels, a `bf-toggle`
29
+ in each cell), documented with the `table/toggle-matrix` example. `BfTableColumn.align` takes
30
+ `'center'`: the header and each cell's control are centred, and while stacked the control sits at
31
+ the tile's end, centred on its label line. New type `BfTableColumnAlign` (`BfTableAlign` plus
32
+ `'center'`; `BfTableAlign` keeps its two members). A stacked cell of a start or end column keeps
33
+ its baseline alignment. `@bitforgehq/ui/testing`: new `BfTableHarness.getCellHarness(row, column,
34
+ query)` returns the harness of a control in one cell, and `selectRow()`, `getSelectedCount()`
35
+ read only the selection column's checkboxes, never a checkbox or toggle in a custom cell.
36
+
37
+ ### toggle
38
+
39
+ A label-less `sm` toggle (a table cell) takes a 44 px min-width under a coarse pointer, its track
40
+ centred, so its touch target is 44 × 44 px (it was 40 px wide). Nothing changes under a fine
41
+ pointer or for `md` and `lg`. `@bitforgehq/ui/testing`: new `BfToggleHarness` (`with({ label })`
42
+ by the switch's computed name, `getLabel()`, `isChecked()`, `isDisabled()`, `isReadonly()`,
43
+ `toggle()`, `check()`, `uncheck()`, `getErrorText()`).
44
+
45
+ ### badge
46
+
47
+ New `emphasis` input (`BfBadgeEmphasis`: `soft`, the default, or `strong`). `strong` is a solid
48
+ fill with contrasting text in every variant, for the one state that must stand out ("Critical");
49
+ `primary` uses the brand fill. A strong count pill takes its fill from `variant` (a soft count pill
50
+ stays primary, as before). In forced colours a strong badge keeps a `CanvasText` outline. Opt-in:
51
+ without `emphasis` nothing changes.
52
+
53
+ ### theme
54
+
55
+ New strong fill tokens in both modes, for the strong badge: `--color-neutral-strong`,
56
+ `--color-success-strong`, `--color-warning-strong`, `--color-danger-strong` and
57
+ `--color-info-strong`, each with its text token `--color-on-<tone>-strong` (`bg-danger-strong`,
58
+ `text-on-danger-strong`, …). Every pair reaches 4.5:1 and every fill 3:1 on `bg`, `surface` and
59
+ `surface-sunken` in both modes; danger is `#b3261e` / `#ffffff` in light and `#c9372c` /
60
+ `#ffffff` in dark.
61
+
62
+ New: Ink, an opt-in dark preset, `@bitforgehq/ui/dark-ink.css`. Import it after `theme.css`; it
63
+ redefines tokens under `.dark` only (light mode is untouched): near-black surfaces one small step
64
+ apart (`bg` `#0b0c0e`, `surface` `#121316`), see-through status tints (the tone at 12 %, so they
65
+ sit on any surface; `danger-soft` stays opaque because the danger button writes it as text on
66
+ the danger fill), off-white text, a neutral focus ring and a 1 px edge inside `shadow-raised` and
67
+ `shadow-floating`. It sets every colour and shadow token of the default `.dark` block except the
68
+ brand set, so the app's primary stays; the Theming guide has an optional monochrome primary block.
69
+ Every pair is contrast-checked on `bg`, `surface`, `surface-muted` and `surface-sunken`. Four
70
+ mockup values were lifted to pass: `border-strong`, `control-off` and `chart-axis` `#64676e`
71
+ (3:1 on `surface-muted`), `text-muted` `#90949b` (4.5:1 on `primary-soft`) and `text-subtle`
72
+ `#63676f` (3:1 on `surface-muted`). The default dark palette does not change.
73
+
74
+ ### list-item
75
+
76
+ New `unread` input (boolean, default false) for a row not yet opened: a `primary` dot at the
77
+ row's inline start on the title line, a bolder title (`font-extrabold`) and a visually hidden
78
+ state read before the title, from the new label key `listItemUnread` ("Unread"). A row's
79
+ `bfAction` link or button then names itself `aria-labelledby="<state> <itself>"` ("Unread,
80
+ Report"; an id is added if it has none, and its own `aria-labelledby` is kept after the state).
81
+ The dot sits in the row's own inline-start padding, so read and unread rows stay aligned. Opt-in: without `unread` nothing
82
+ changes.
83
+
84
+ ### steps
85
+
86
+ New `bf-steps`: numbered instructions a user follows in order ("Set up the app on your phone"),
87
+ a `role="list"` of `bf-step` items whose markers are joined by a line. `start` (a number, 1 by
88
+ default) continues a list; a continued list sets `aria-posinset` and `aria-setsize` on its steps
89
+ so the announced position matches the visible number.
90
+
91
+ ### step
92
+
93
+ New `bf-step`: one step of a `bf-steps` list, with `title` (the short instruction, text by
94
+ default; `headingLevel` 2, 3 or 4, a `BfStepHeadingLevel`, makes it a heading) and the details
95
+ in the default slot. Its number is drawn in a `primary-soft` circle and hidden from assistive
96
+ technology, which reads the list position instead.
97
+
98
+ ## 0.3.3 (2026-10-29)
99
+
100
+ ### header
101
+
102
+ Fix: `bf-header` measures its title and slots in the next animation frame after a resize, not
103
+ inside the `ResizeObserver` callback. Its measurement writes the title inset and height, which
104
+ resize the observed elements, and WebKit then reported "ResizeObserver loop completed with
105
+ undelivered notifications": an error event that an app's global error listener
106
+ (`provideBrowserGlobalErrorListeners()`) logs as an error. Several resizes in one frame now
107
+ measure once.
108
+
109
+ ### app-frame
110
+
111
+ Fix: the narrow header bar puts the `menu` slot at the inline start, before the `brand` (0.3.2 put
112
+ it after the brand), with the actions at the end as before: the top app bar convention. The menu
113
+ also comes first in the tab order. No API change; an app needs no change unless it relied on the
114
+ brand coming first.
115
+
116
+ ### table
117
+
118
+ Fix: stacked rows (`stack="narrow"` and `stack="coarse"`) show their column labels again when the
119
+ package is installed from npm. 0.3.2 wrote the label as `content-[attr(data-label)_/_""]`; the
120
+ library build escaped its quotes in the FESM (`\"\"`), so Tailwind built the rule for a class no
121
+ cell carries and every label `::before` computed to `none`. The cells now carry
122
+ `before:content-data-label`, a utility `theme.css` defines (below). Apps that added the
123
+ workaround `@source inline('@max-xl:before:content-[attr(data-label)_/_""] pointer-coarse:before:content-[attr(data-label)_/_""]')`
124
+ can remove it. consumer-smoke now opens a stacked table built from the packed package in
125
+ Chromium, under a fine and a coarse pointer, and fails if any FESM class string holds a
126
+ backslash escape.
127
+
128
+ The stacked Sort by select's first option is Not sorted (new label key `tableSortNone`, "Not
129
+ sorted"): it shows while `sort` is null, so the select is no longer an empty box, and choosing it
130
+ sets `sort` back to null. New `BfTableHarness.getSortSelect()` returns that select's
131
+ `BfSelectHarness` (`null` when the table cannot stack or has no sortable column).
132
+
133
+ ### theme
134
+
135
+ New utility `content-data-label` in `theme.css`: `content: attr(data-label) / ''` (the element's
136
+ `data-label`, with empty alternative text). The stacked `bf-table` uses it behind its stack
137
+ variants; apps can use it with `before:` the same way.
138
+
7
139
  ## 0.3.2 (2026-10-29)
8
140
 
9
141
  ### docs site
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. `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).
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). For a near-black dark mode, `@bitforgehq/ui/dark-ink.css` (the opt-in Ink preset, imported right after the theme) redefines the dark tokens and leaves light mode and your brand as they are.
6
6
 
7
7
  - Widgets: `@bitforgehq/ui`
8
8
  - Number field: `@bitforgehq/ui/number-input` (`bf-number-input`; loads Angular Forms, peer
@@ -83,7 +83,7 @@ In `src/index.html`, give `<body>` the theme background and text colour, so the
83
83
 
84
84
  ### 7. Switch to dark mode
85
85
 
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.
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. For a near-black dark mode, add `@import '@bitforgehq/ui/dark-ink.css';` right after the theme import: the Ink preset, which changes dark mode only.
87
87
 
88
88
  ```html
89
89
  <html lang="en" class="dark">
@@ -140,10 +140,10 @@ 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 43 fixtures with `renderApplication()` (every root widget, `bf-icon`,
143
+ `tools/ssr-smoke.ts` renders 45 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 16
146
+ dialog), and all of them render without an error. 29 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
148
  hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-input`, `bf-radio-group`, `bf-segmented`,
149
149
  `bf-select`, `bf-sidebar`, `bf-tab-group`, `bf-table`, `bf-textarea`, `bf-theme-switch`,
@@ -550,6 +550,24 @@
550
550
  "summary": "One labelled figure with an optional icon tile and a supporting line",
551
551
  "since": "0.3.1"
552
552
  },
553
+ {
554
+ "name": "step",
555
+ "selector": "bf-step",
556
+ "className": "BfStepComponent",
557
+ "category": "Data display",
558
+ "entryPoint": "@bitforgehq/ui",
559
+ "summary": "One numbered step of an instruction list, with a title and its details",
560
+ "since": "0.4.0"
561
+ },
562
+ {
563
+ "name": "steps",
564
+ "selector": "bf-steps",
565
+ "className": "BfStepsComponent",
566
+ "category": "Data display",
567
+ "entryPoint": "@bitforgehq/ui",
568
+ "summary": "Numbered instruction steps a user follows in order, with joined markers",
569
+ "since": "0.4.0"
570
+ },
553
571
  {
554
572
  "name": "tab",
555
573
  "selector": "bf-tab",
@@ -640,6 +658,24 @@
640
658
  "summary": "Typed time-of-day field with a popover list of times, valued as an ISO string",
641
659
  "since": "0.3.1"
642
660
  },
661
+ {
662
+ "name": "timeline",
663
+ "selector": "bf-timeline",
664
+ "className": "BfTimelineComponent",
665
+ "category": "Data display",
666
+ "entryPoint": "@bitforgehq/ui",
667
+ "summary": "Dated list of events with who did what and when, on a vertical line",
668
+ "since": "0.4.0"
669
+ },
670
+ {
671
+ "name": "timeline-event",
672
+ "selector": "bf-timeline-event",
673
+ "className": "BfTimelineEventComponent",
674
+ "category": "Data display",
675
+ "entryPoint": "@bitforgehq/ui",
676
+ "summary": "One timeline event with a coloured marker, its text, actor and time",
677
+ "since": "0.4.0"
678
+ },
643
679
  {
644
680
  "name": "toast",
645
681
  "selector": "bf-toast",