@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
@@ -0,0 +1,111 @@
1
+ # div[bfDescriptionItem]
2
+
3
+ One label and value pair of a description list, label as term, content as value
4
+
5
+ `BfDescriptionItemComponent` from `@bitforgehq/ui` · Data display · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import { BfDescriptionItemComponent, BfDescriptionListComponent } from '@bitforgehq/ui';
12
+
13
+ /**
14
+ * The bf-description-item overview.
15
+ *
16
+ * Live demo of `div[bfDescriptionItem]`: three pairs of a recipe, each a label and its value, in a
17
+ * `bf-description-list`.
18
+ */
19
+ @Component({
20
+ selector: 'app-example-description-item-overview',
21
+ imports: [BfDescriptionItemComponent, BfDescriptionListComponent],
22
+ template: `
23
+ <bf-description-list>
24
+ <div bfDescriptionItem label="Oven">200 °C, fan 180 °C</div>
25
+ <div bfDescriptionItem label="Keeps">2 days in the fridge</div>
26
+ <div bfDescriptionItem label="Freezes">Yes, up to 3 months</div>
27
+ </bf-description-list>
28
+ `,
29
+ })
30
+ export class DescriptionItemOverviewExampleComponent {}
31
+ ```
32
+
33
+ ## Overview
34
+
35
+ One label-value pair of a `bf-description-list`: the `label` becomes the `dt`, the content the
36
+ `dd`. Put it on a `div` inside the list.
37
+
38
+ One label-value pair of a [`bf-description-list`](https://bitforge-ui.web.app/components/description-list): put `bfDescriptionItem` on a `div` inside the list, set its `label`, and put the value in its content. The `label` becomes the pair's `dt` and the content its `dd`; the item takes its look (a divided row or a tile) from the list's `layout`. It works the same in both shells.
39
+
40
+ ## When to use
41
+
42
+ Use `div[bfDescriptionItem]` for each fact inside a `bf-description-list`. Use [bf-list-item](https://bitforge-ui.web.app/components/list-item) instead for a row the user can open or act on, and [bf-stat](https://bitforge-ui.web.app/components/stat) instead for a single headline figure.
43
+
44
+ ## Basic usage
45
+
46
+ `label` is required; the value is the content, so it can be text, a badge or a link. A class on the item (such as `col-span-full` in the grid layout) is kept alongside the item's own classes.
47
+
48
+ ```ts
49
+ import { Component } from '@angular/core';
50
+ import { BfDescriptionItemComponent, BfDescriptionListComponent } from '@bitforgehq/ui';
51
+
52
+ /**
53
+ * The bf-description-item overview.
54
+ *
55
+ * Live demo of `div[bfDescriptionItem]`: three pairs of a recipe, each a label and its value, in a
56
+ * `bf-description-list`.
57
+ */
58
+ @Component({
59
+ selector: 'app-example-description-item-overview',
60
+ imports: [BfDescriptionItemComponent, BfDescriptionListComponent],
61
+ template: `
62
+ <bf-description-list>
63
+ <div bfDescriptionItem label="Oven">200 °C, fan 180 °C</div>
64
+ <div bfDescriptionItem label="Keeps">2 days in the fridge</div>
65
+ <div bfDescriptionItem label="Freezes">Yes, up to 3 months</div>
66
+ </bf-description-list>
67
+ `,
68
+ })
69
+ export class DescriptionItemOverviewExampleComponent {}
70
+ ```
71
+
72
+ ## Desktop
73
+
74
+ The item is not interactive: no hover, no pointer cursor, no focus stop; only a link or button in its value takes focus. It renders the same inside `provideBfShell()`.
75
+
76
+ ## Theming
77
+
78
+ - Label (`dt`): `text-caption` in `text-muted`. Value (`dd`): `text-body` in `text`; a long value wraps anywhere rather than overflow.
79
+ - In the `rows` layout the item has `py-3` and, from a 28rem wide list, a one-third label column; in the `grid` layout it is a tile with the label over the value.
80
+
81
+ ## Accessibility
82
+
83
+ The item is the `div` that groups one `dt` with its `dd` inside the list's `dl`, which HTML allows, so the pair is announced as a term and its description. It adds no ARIA.
84
+
85
+ ## Inputs
86
+
87
+ | Name | Type | Default | Description |
88
+ | ------- | -------- | -------- | ------------------------------------ |
89
+ | `label` | `string` | required | The term, shown as the pair's label. |
90
+
91
+ ## Slots
92
+
93
+ - `default`: The value: text or rich content such as a badge or a link.
94
+
95
+ ## Notes
96
+
97
+ ### Do
98
+
99
+ - Use one item per fact, with a short noun as the `label`.
100
+
101
+ ### Don't
102
+
103
+ - Don't use `bfDescriptionItem` outside a `bf-description-list`: without the `dl` around it, its `dt` and `dd` are invalid HTML.
104
+ - Don't put a second term in one item; give each its own.
105
+
106
+ ## Migration notes
107
+
108
+ ### 0.3.1
109
+
110
+ New: `div[bfDescriptionItem]`, one pair of a `bf-description-list`: its required `label` is the
111
+ `dt`, its content (text, a badge, a link) the `dd`.
@@ -0,0 +1,160 @@
1
+ # bf-description-list
2
+
3
+ Native description list of label-value pairs, as divided rows or a tile grid
4
+
5
+ `BfDescriptionListComponent` from `@bitforgehq/ui` · Data display · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import {
12
+ BfBadgeComponent,
13
+ BfDescriptionItemComponent,
14
+ BfDescriptionListComponent,
15
+ } from '@bitforgehq/ui';
16
+
17
+ /**
18
+ * The bf-description-list overview.
19
+ *
20
+ * Live demo of `bf-description-list`: a recipe's details as divided rows, with a badge and a link
21
+ * as values; the labels move beside the values once the list is wide enough.
22
+ */
23
+ @Component({
24
+ selector: 'app-example-description-list-overview',
25
+ imports: [BfBadgeComponent, BfDescriptionItemComponent, BfDescriptionListComponent],
26
+ template: `
27
+ <bf-description-list>
28
+ <div bfDescriptionItem label="Recipe">Lemon risotto with peas</div>
29
+ <div bfDescriptionItem label="Cuisine">Italian</div>
30
+ <div bfDescriptionItem label="Difficulty"><bf-badge variant="success">Easy</bf-badge></div>
31
+ <div bfDescriptionItem label="Diet">Vegetarian, gluten-free</div>
32
+ <div bfDescriptionItem label="Source">
33
+ <a href="#source" class="text-primary underline">Nonna's notebook, page 42</a>
34
+ </div>
35
+ <div bfDescriptionItem label="Added">12 October 2026</div>
36
+ </bf-description-list>
37
+ `,
38
+ })
39
+ export class DescriptionListOverviewExampleComponent {}
40
+ ```
41
+
42
+ ## Overview
43
+
44
+ A list of label-value pairs, such as a record's details: a native `dl` around
45
+ `div[bfDescriptionItem]`s, as divided rows (label beside value) or a grid of tiles.
46
+
47
+ A description list shows the details of one record as label-value pairs: a recipe's cuisine and difficulty, an order's status and date, a person's role and email. `bf-description-list` renders the native `dl`; each pair is a [`div[bfDescriptionItem]`](https://bitforge-ui.web.app/components/description-item) whose `label` becomes the `dt` and whose content becomes the `dd`. It works the same in both shells: in the Capacitor shell and in a desktop web app.
48
+
49
+ ## When to use
50
+
51
+ Use `bf-description-list` for read-only facts about one thing, where the reader looks up a value by its label. Use [bf-list](https://bitforge-ui.web.app/components/list) instead when the rows are actions or links, and [bf-stat](https://bitforge-ui.web.app/components/stat) instead for a few headline figures (a duration, a count, a total) that should read at a glance. A table compares many records across columns; a description list describes one.
52
+
53
+ ## Rows
54
+
55
+ `layout="rows"` (the default) separates the pairs with a `border` line, as a divided `bf-list`. Once the list is 28rem wide or more, the label sits beside its value in a one-third column; in a narrower list (a phone, a side pane) the label stacks over the value. The width that counts is the list's own (the host is an inline-size container), not the viewport's, so the same list stacks in a narrow pane and sits side by side on a wide page. The value can be rich content: a badge, a link, a short sentence.
56
+
57
+ ```ts
58
+ import { Component } from '@angular/core';
59
+ import {
60
+ BfBadgeComponent,
61
+ BfDescriptionItemComponent,
62
+ BfDescriptionListComponent,
63
+ } from '@bitforgehq/ui';
64
+
65
+ /**
66
+ * The bf-description-list overview.
67
+ *
68
+ * Live demo of `bf-description-list`: a recipe's details as divided rows, with a badge and a link
69
+ * as values; the labels move beside the values once the list is wide enough.
70
+ */
71
+ @Component({
72
+ selector: 'app-example-description-list-overview',
73
+ imports: [BfBadgeComponent, BfDescriptionItemComponent, BfDescriptionListComponent],
74
+ template: `
75
+ <bf-description-list>
76
+ <div bfDescriptionItem label="Recipe">Lemon risotto with peas</div>
77
+ <div bfDescriptionItem label="Cuisine">Italian</div>
78
+ <div bfDescriptionItem label="Difficulty"><bf-badge variant="success">Easy</bf-badge></div>
79
+ <div bfDescriptionItem label="Diet">Vegetarian, gluten-free</div>
80
+ <div bfDescriptionItem label="Source">
81
+ <a href="#source" class="text-primary underline">Nonna's notebook, page 42</a>
82
+ </div>
83
+ <div bfDescriptionItem label="Added">12 October 2026</div>
84
+ </bf-description-list>
85
+ `,
86
+ })
87
+ export class DescriptionListOverviewExampleComponent {}
88
+ ```
89
+
90
+ ## Grid
91
+
92
+ `layout="grid"` lays the pairs out as tiles with the label over the value, in as many columns of at least 10rem as fit, so the tiles wrap on their own without a breakpoint. Use it for short values that read well side by side; a long value belongs in the rows layout.
93
+
94
+ ```ts
95
+ import { Component } from '@angular/core';
96
+ import { BfDescriptionItemComponent, BfDescriptionListComponent } from '@bitforgehq/ui';
97
+
98
+ /** A recipe's nutrition per serving as a grid of tiles that wraps by itself. */
99
+ @Component({
100
+ selector: 'app-example-description-list-grid',
101
+ imports: [BfDescriptionItemComponent, BfDescriptionListComponent],
102
+ template: `
103
+ <bf-description-list layout="grid">
104
+ <div bfDescriptionItem label="Energy">520 kcal</div>
105
+ <div bfDescriptionItem label="Protein">16 g</div>
106
+ <div bfDescriptionItem label="Carbohydrates">78 g</div>
107
+ <div bfDescriptionItem label="Fat">14 g</div>
108
+ <div bfDescriptionItem label="Note" class="col-span-full">
109
+ Values per serving, with parmesan.
110
+ </div>
111
+ </bf-description-list>
112
+ `,
113
+ })
114
+ export class DescriptionListGridExampleComponent {}
115
+ ```
116
+
117
+ ## Desktop
118
+
119
+ The list is not interactive: it has no hover, no pointer cursor and no focus stop of its own, and only the links or buttons you put in a value take focus. Its spacing does not follow `provideBfDefaults()`; the rows keep one rhythm on every density. Inside `provideBfShell()` (the Capacitor shell) it renders and behaves exactly the same. Because the host is an inline-size container, give it a width when you place it in a flex row (`flex-1` or a `basis-*`), or it shrinks to nothing.
120
+
121
+ ## Theming
122
+
123
+ - Label (`dt`): `text-caption` in `text-muted`. Value (`dd`): `text-body` in `text`.
124
+ - `rows`: the lines between the pairs are `border`; each pair has `py-3` (12 px) of block padding.
125
+ - `grid`: `gap-x-6` / `gap-y-4` between the tiles, columns of at least 10rem.
126
+
127
+ ## Accessibility
128
+
129
+ `bf-description-list` renders a native `dl`, and each `div[bfDescriptionItem]` is a `div` holding one `dt` and one `dd`, which is valid HTML, so screen readers announce a list of terms with their descriptions. The list adds no ARIA. Content in a value carries its own semantics: a badge's text is read with the value, a link stays a link.
130
+
131
+ ## Inputs
132
+
133
+ | Name | Type | Default | Description |
134
+ | -------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
135
+ | `layout` | `BfDescriptionListLayout` (`'rows' \| 'grid'`) | `'rows'` | `rows` (default): label beside value, divided, stacked in a narrow list; `grid`: tiles with the label over the value. |
136
+
137
+ ## Slots
138
+
139
+ - `default`: The pairs, each a `div[bfDescriptionItem]`.
140
+
141
+ ## Notes
142
+
143
+ ### Do
144
+
145
+ - Keep labels short nouns ("Cuisine", "Servings") and put the unit in the value ("45 min").
146
+ - Put a badge or a link in a value when the value is a status or points elsewhere.
147
+
148
+ ### Don't
149
+
150
+ - Don't put anything but `div[bfDescriptionItem]` pairs in a `bf-description-list`; a `dl` expects term and description groups.
151
+ - Don't use it for rows the user acts on; that is a `bf-list` of `bf-list-item` rows.
152
+
153
+ ## Migration notes
154
+
155
+ ### 0.3.1
156
+
157
+ New: `bf-description-list`, a native `dl` of label-value pairs for the details of one record, in
158
+ both shells. `layout="rows"` (default) divides the pairs with a `border` line and puts the label
159
+ beside the value once the list is 28rem wide (a container query, so a narrow pane stacks them);
160
+ `layout="grid"` lays them out as wrapping tiles. New type `BfDescriptionListLayout`.
@@ -0,0 +1,169 @@
1
+ # bf-donut-chart
2
+
3
+ Donut chart of the parts of a whole, with a legend, tooltip and centre slot
4
+
5
+ `BfDonutChartComponent` from `@bitforgehq/ui/charts` · Data display · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, computed } from '@angular/core';
11
+ import { BfDonutChartComponent, type BfDonutSlice } from '@bitforgehq/ui/charts';
12
+
13
+ /**
14
+ * The bf-donut-chart overview.
15
+ *
16
+ * Live demo of `bf-donut-chart`: a month's spending by category with the total in the centre.
17
+ * Hover a slice, or focus the chart and use the arrow keys; hide a slice in the legend and the
18
+ * others share the rest.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-donut-chart-overview',
22
+ imports: [BfDonutChartComponent],
23
+ template: `
24
+ <bf-donut-chart label="Spending in October" [slices]="slices" [valueFormat]="francs">
25
+ <span bfSlot="center" class="text-heading text-text">{{ total() }}</span>
26
+ <span bfSlot="center" class="text-caption text-text-muted">October</span>
27
+ </bf-donut-chart>
28
+ `,
29
+ })
30
+ export class DonutChartOverviewExampleComponent {
31
+ readonly slices: readonly BfDonutSlice[] = [
32
+ { label: 'Rent', value: 1850 },
33
+ { label: 'Groceries', value: 640 },
34
+ { label: 'Transport', value: 310 },
35
+ { label: 'Leisure', value: 280 },
36
+ { label: 'Other', value: 170 },
37
+ ];
38
+ private readonly format = new Intl.NumberFormat('en', {
39
+ style: 'currency',
40
+ currency: 'CHF',
41
+ maximumFractionDigits: 0,
42
+ });
43
+ readonly francs = (value: number): string => this.format.format(value);
44
+ readonly total = computed(() => this.francs(this.slices.reduce((sum, s) => sum + s.value, 0)));
45
+ }
46
+ ```
47
+
48
+ ## Overview
49
+
50
+ A donut chart of one set of slices, the parts of a whole (a budget by category, traffic by
51
+ source), drawn in SVG with the chart palette: each slice's angle is its share of the total.
52
+ A legend shows and hides slices (the total follows), a tooltip shows a slice's value and share
53
+ on hover and on keyboard focus (Arrow keys move between the slices and a polite live region
54
+ reads each one), and a Show data table lists them. The `center` slot holds a total or a label
55
+ in the ring's hole. Slices draw in on the first render and on every data change, still under
56
+ reduced motion; under `dir="rtl"` the slices run counter-clockwise.
57
+
58
+ A donut chart shows the parts of one whole: a budget by category, traffic by source, tasks by status. `bf-donut-chart` draws it in SVG from `slices` (each a `label` and a `value`); each slice's angle is its share of the total. A legend shows and hides slices, a tooltip shows a slice's value and share on hover and on keyboard focus, a Show data table lists them, and the `center` slot holds a total in the ring's hole. It lives in `@bitforgehq/ui/charts` (`import { BfDonutChartComponent } from '@bitforgehq/ui/charts'`) and works the same in a desktop web app and in the Capacitor shell.
59
+
60
+ ## When to use
61
+
62
+ Use `bf-donut-chart` for the shares of one whole with a few parts (two to six read well). Our charts are themed with the `--color-chart-*` tokens and bring their own legend, tooltip, keyboard reading and data table; bring a chart library (themed with the same tokens) only for a form they do not draw. Use [bf-bar-chart](https://bitforge-ui.web.app/components/bar-chart) instead to compare the parts precisely or when there are many, and [bf-stat](https://bitforge-ui.web.app/components/stat) for a single share ("42 % done").
63
+
64
+ ## Centre and thickness
65
+
66
+ Content with `bfSlot="center"` sits in the ring's hole: the total and a caption, such as `1,800 CHF` over `this month`. Keep it short; it does not take pointer events, so hovering the hole never hides the tooltip of a slice. `thickness` sets the ring's width in px (28 by default); a thickness as large as the radius draws a full pie.
67
+
68
+ ```ts
69
+ import { Component } from '@angular/core';
70
+ import { BfDonutChartComponent, type BfDonutSlice } from '@bitforgehq/ui/charts';
71
+
72
+ /**
73
+ * A thin ring and a pie.
74
+ *
75
+ * `thickness` sets the ring's width: a thin ring leaves room for the centre, a thickness as large
76
+ * as the radius draws a full pie. Tasks by status, the legend above the ring.
77
+ */
78
+ @Component({
79
+ selector: 'app-example-donut-chart-thickness',
80
+ imports: [BfDonutChartComponent],
81
+ template: `
82
+ <div class="grid w-full grid-cols-[repeat(auto-fit,minmax(min(14rem,100%),1fr))] gap-4">
83
+ <bf-donut-chart label="Tasks by status" legend="top" [thickness]="12" [slices]="tasks">
84
+ <span bfSlot="center" class="text-heading text-text">48</span>
85
+ <span bfSlot="center" class="text-caption text-text-muted">tasks</span>
86
+ </bf-donut-chart>
87
+ <bf-donut-chart
88
+ label="Tasks by status, as a pie"
89
+ legend="top"
90
+ [thickness]="200"
91
+ [slices]="tasks"
92
+ />
93
+ </div>
94
+ `,
95
+ })
96
+ export class DonutChartThicknessExampleComponent {
97
+ readonly tasks: readonly BfDonutSlice[] = [
98
+ { label: 'Done', value: 27 },
99
+ { label: 'In progress', value: 13 },
100
+ { label: 'Blocked', value: 8 },
101
+ ];
102
+ }
103
+ ```
104
+
105
+ ## Data and formatting
106
+
107
+ Slices run clockwise from the top in their order (counter-clockwise under `dir="rtl"`), with a 2 px gap between them; a slice of zero or less draws nothing, but stays in the legend and the table. Hiding a slice in the legend removes it from the total, so the other shares grow. `valueFormat` formats the values in the tooltip, the announcement and the table; unset, values use `Intl.NumberFormat` in the document's language, and shares are whole percentages. The slices draw in along the ring on the first render and whenever `slices` change. `height` sets the plot's height in px (220 by default); the ring fits the smaller of it and the container's width.
108
+
109
+ ## Desktop
110
+
111
+ Under a mouse, hovering a slice thickens it and shows its tooltip; the hole and the space around the ring show none. The plot is one stop in the tab order, with the focus ring on keyboard focus: focusing it shows the first slice. Under a coarse pointer (touch) a tap shows a slice's tooltip, and the legend buttons and the Show data button are at least 44 px tall. Inside `provideBfShell()` it renders and behaves the same.
112
+
113
+ ## Theming
114
+
115
+ - Slices: `--color-chart-1` … `--color-chart-8` in order (`stroke-chart-n`); the ring's track: `chart-grid`.
116
+ - Tooltip, legend and data table: as in [bf-line-chart](https://bitforge-ui.web.app/components/line-chart).
117
+ - Motion: `--animate-chart-draw` along each slice; the active slice thickens over 150 ms; none under `prefers-reduced-motion`.
118
+
119
+ ## Accessibility
120
+
121
+ The chart is a native `figure` named by `label` (else the `bf-chart-card` title, else the `chart` label); the SVG is hidden from assistive technology, and the centre slot's text is read as part of the figure. The plot is a focusable `role="group"` named by the `chartValues` label: the arrow keys move between the slices and a polite live region reads each one with its share ("Rent: 900 CHF (50%)"). The legend is a group of toggle buttons (`aria-pressed`); the Show data button (`aria-expanded`) shows the slices as a native `table` (`chartCategory` and `chartValue` headers). Every slice colour is ≥ 3:1 against `surface` and `bg`, and the legend names each one.
122
+
123
+ ### Keyboard
124
+
125
+ | Key | Action |
126
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------ |
127
+ | <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves through the plot, the legend buttons and the Show data button |
128
+ | <kbd>ArrowRight</kbd> | Moves to the next slice and reads it (the previous one under `dir="rtl"`) |
129
+ | <kbd>ArrowLeft</kbd> | Moves to the previous slice and reads it (the next one under `dir="rtl"`) |
130
+ | <kbd>Home</kbd> / <kbd>End</kbd> | Moves to the first / last slice |
131
+ | <kbd>Escape</kbd> | Hides the tooltip |
132
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | On a legend button, shows or hides its slice; on Show data, shows or hides the table |
133
+
134
+ ## Inputs
135
+
136
+ | Name | Type | Default | Description |
137
+ | ------------- | --------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
138
+ | `slices` | `readonly BfDonutSlice[]` | required | The slices, in order clockwise from the top. |
139
+ | `label` | `string` | `''` | The chart's accessible name; empty falls back to the `bf-chart-card` title, then to `provideBfLabels()`'s `chart` ("Chart"). |
140
+ | `height` | `number` | `220` | The plot's height in px; the ring fits the smaller of it and the container's width. |
141
+ | `thickness` | `number` | `28` | The ring's thickness in px (at most the radius, a full pie). |
142
+ | `valueFormat` | `BfChartValueFormat \| undefined` | `undefined` | Formats the values in the tooltip, the announcements and the table; unset uses `Intl.NumberFormat` in the document's language. |
143
+ | `legend` | `BfChartLegendPosition \| undefined` (`'top' \| 'bottom' \| 'none' \| undefined`) | `undefined` | Where the legend goes; unset follows the `bf-chart-card`, else `bottom`. A single slice draws no legend. |
144
+
145
+ ## Slots
146
+
147
+ - `center`: Content in the ring's hole, such as the total and its label.
148
+
149
+ ## Notes
150
+
151
+ ### Do
152
+
153
+ - Order the slices from largest to smallest, starting at the top.
154
+ - Put the total in the `center` slot, so the shares have a whole to refer to.
155
+
156
+ ### Don't
157
+
158
+ - Don't use a donut for more than about six slices or for slices of nearly equal size; use a bar chart.
159
+ - Don't use it for values that do not add up to a whole.
160
+
161
+ ## Migration notes
162
+
163
+ ### 0.3.1
164
+
165
+ New `bf-donut-chart` in `@bitforgehq/ui/charts`: `slices` (`BfDonutSlice`: `label`, `value`) as
166
+ the parts of a whole, with a `thickness`, a `center` slot for a total, a legend that hides slices
167
+ (the shares follow), a tooltip with each slice's value and share, keyboard reading and a data
168
+ table. Slices draw in along the ring, not under reduced motion, and run counter-clockwise under
169
+ `dir="rtl"`.
@@ -98,6 +98,89 @@ export class EmptyStateActionExampleComponent {
98
98
 
99
99
  Set `headingLevel` to `3` when the empty state sits inside a section that already opened with an `h2`, so the page outline stays in order.
100
100
 
101
+ ## As the page
102
+
103
+ When the empty state is the whole page (a not-found page, a no-access page, an app's first run), its title is the page's heading: set `headingLevel` to `1` so it renders the `h1`, and give the page no other `h1`. Centre it in the content area: a column with a minimum height (`min-h-[60vh]`) and `justify-center`. Put the primary action first in the `actions` slot (a `bfButton` link home) and an optional secondary link after it (a `text` `bfButton`, such as "Contact support").
104
+
105
+ The preview below sits inside this docs page, which already has its `h1`, so it keeps `headingLevel` 2 in a shorter area; a real page uses `1`, as in the code-only example after it.
106
+
107
+ ```ts
108
+ import { Component } from '@angular/core';
109
+ import { RouterLink } from '@angular/router';
110
+ import { BfButtonComponent, BfEmptyStateComponent } from '@bitforgehq/ui';
111
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
112
+ import { FileQuestionMark } from 'lucide';
113
+
114
+ /**
115
+ * An empty state as a whole page.
116
+ *
117
+ * Centred in a tall content area, with a primary action and a secondary link. This preview sits
118
+ * inside a docs page that has its own `h1`, so it uses `headingLevel` 2; on a real not-found page
119
+ * the empty state is the page, so set it to 1 (see the next example).
120
+ */
121
+ @Component({
122
+ selector: 'app-example-empty-state-page',
123
+ imports: [RouterLink, BfButtonComponent, BfEmptyStateComponent, BfIconComponent],
124
+ template: `
125
+ <div class="flex min-h-96 flex-col justify-center">
126
+ <bf-empty-state
127
+ title="Page not found"
128
+ description="The page may have moved, or the link has a typo."
129
+ [headingLevel]="2"
130
+ >
131
+ <bf-icon bfSlot="icon" [icon]="fileQuestion" size="lg" />
132
+ <a bfSlot="actions" bfButton routerLink="/">Go to the home page</a>
133
+ <a bfSlot="actions" bfButton variant="text" href="mailto:support@example.com">
134
+ Contact support
135
+ </a>
136
+ </bf-empty-state>
137
+ </div>
138
+ `,
139
+ })
140
+ export class EmptyStatePageExampleComponent {
141
+ protected readonly fileQuestion = FileQuestionMark;
142
+ }
143
+ ```
144
+
145
+ ```ts
146
+ import { Component } from '@angular/core';
147
+ import { RouterLink } from '@angular/router';
148
+ import { BfButtonComponent, BfEmptyStateComponent } from '@bitforgehq/ui';
149
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
150
+ import { FileQuestionMark } from 'lucide';
151
+
152
+ /**
153
+ * A not-found page whose empty state is the page heading.
154
+ *
155
+ * The route's whole content: `headingLevel` 1 renders the title as the page's `h1`, and the
156
+ * `min-h-[60vh]` area centres the empty state vertically in the content column.
157
+ *
158
+ * @preview none
159
+ */
160
+ @Component({
161
+ selector: 'app-example-empty-state-not-found-page',
162
+ imports: [RouterLink, BfButtonComponent, BfEmptyStateComponent, BfIconComponent],
163
+ template: `
164
+ <div class="flex min-h-[60vh] flex-col justify-center">
165
+ <bf-empty-state
166
+ title="Page not found"
167
+ description="The page may have moved, or the link has a typo."
168
+ [headingLevel]="1"
169
+ >
170
+ <bf-icon bfSlot="icon" [icon]="fileQuestion" size="lg" />
171
+ <a bfSlot="actions" bfButton routerLink="/">Go to the home page</a>
172
+ <a bfSlot="actions" bfButton variant="text" href="mailto:support@example.com">
173
+ Contact support
174
+ </a>
175
+ </bf-empty-state>
176
+ </div>
177
+ `,
178
+ })
179
+ export class EmptyStateNotFoundPageExampleComponent {
180
+ protected readonly fileQuestion = FileQuestionMark;
181
+ }
182
+ ```
183
+
101
184
  ## Theming
102
185
 
103
186
  - Icon tile: `surface-sunken` background, `text-muted` icon color, `tile` radius.
@@ -105,15 +188,15 @@ Set `headingLevel` to `3` when the empty state sits inside a section that alread
105
188
 
106
189
  ## Accessibility
107
190
 
108
- The title renders as an `<h2>` by default and an `<h3>` when `headingLevel` is `3`, so it slots into the surrounding page outline instead of always claiming the top level. The icon and action slots carry their own accessibility — an icon should stay decorative, and the action needs its own accessible name.
191
+ The title renders as an `<h2>` by default, an `<h3>` when `headingLevel` is `3` and an `<h1>` when it is `1` (only when the empty state is the page and the page has no other `h1`), so it slots into the surrounding page outline instead of always claiming the top level. The icon and action slots carry their own accessibility — an icon should stay decorative, and the action needs its own accessible name.
109
192
 
110
193
  ## Inputs
111
194
 
112
- | Name | Type | Default | Description |
113
- | -------------- | ------------------------------------- | -------- | -------------------------------------------------------------------- |
114
- | `title` | `string` | required | What is missing, as a short title. |
115
- | `description` | `string` | `''` | One sentence on how to fill it. |
116
- | `headingLevel` | `BfEmptyStateHeadingLevel` (`2 \| 3`) | `2` | Heading level of the title so the empty state fits the page outline. |
195
+ | Name | Type | Default | Description |
196
+ | -------------- | ------------------------------------- | -------- | ------------------------------------------------------------------------------------------- |
197
+ | `title` | `string` | required | What is missing, as a short title. |
198
+ | `description` | `string` | `''` | One sentence on how to fill it. |
199
+ | `headingLevel` | `BfEmptyStateHeading` (`2 \| 3 \| 1`) | `2` | Heading level of the title so the empty state fits the page outline; 1 when it is the page. |
117
200
 
118
201
  ## Slots
119
202
 
@@ -129,10 +212,20 @@ The title renders as an `<h2>` by default and an `<h3>` when `headingLevel` is `
129
212
 
130
213
  ### Don't
131
214
 
215
+ - Don't set `headingLevel` to `1` on an empty state inside a page that already has its own `h1`.
132
216
  - Don't use `bf-empty-state` for a loading screen; that is `bf-spinner`.
133
217
 
134
218
  ## Migration notes
135
219
 
220
+ ### 0.3.2
221
+
222
+ `headingLevel` now takes the new named type `BfEmptyStateHeading` (`BfEmptyStateHeadingLevel |
223
+ 1`): `1` renders the title as the page's `h1`, for an empty state that is the page (a not-found
224
+ or no-access page). The released `BfEmptyStateHeadingLevel` keeps its two members, so `Record`s
225
+ over it keep compiling. New "As the page" docs section with the `empty-state/page` example
226
+ (centred in a tall area, a primary action and a secondary link) and the code-only
227
+ `empty-state/not-found-page`.
228
+
136
229
  ### 0.3.0
137
230
 
138
231
  New exported `BfEmptyStateHeadingLevel` (`2 | 3`), the type of `headingLevel` (was an inline union