@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,215 @@
1
+ # bf-skeleton
2
+
3
+ Loading placeholder shaped as text lines, a rectangle or a circle, with a shimmer
4
+
5
+ `BfSkeletonComponent` from `@bitforgehq/ui` · Feedback · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import { BfSkeletonComponent } from '@bitforgehq/ui';
12
+
13
+ /**
14
+ * The bf-skeleton overview.
15
+ *
16
+ * Live demo of `bf-skeleton`: the three shapes (a circle for an avatar, text lines with a shorter
17
+ * last line, a rect for an image) inside a region marked `aria-busy="true"` while it loads.
18
+ */
19
+ @Component({
20
+ selector: 'app-example-skeleton-overview',
21
+ imports: [BfSkeletonComponent],
22
+ template: `
23
+ <div aria-busy="true" class="flex max-w-md flex-col gap-4">
24
+ <div class="flex items-center gap-3">
25
+ <bf-skeleton shape="circle" />
26
+ <div class="flex min-w-0 flex-1 flex-col gap-2">
27
+ <bf-skeleton width="40%" height="0.875rem" />
28
+ <bf-skeleton width="25%" />
29
+ </div>
30
+ </div>
31
+ <bf-skeleton [lines]="3" height="0.875rem" />
32
+ <bf-skeleton shape="rect" height="8rem" />
33
+ </div>
34
+ `,
35
+ })
36
+ export class SkeletonOverviewExampleComponent {}
37
+ ```
38
+
39
+ ## Overview
40
+
41
+ Loading placeholder in the shape of the content to come: text lines, a rectangle or a circle,
42
+ on the sunken surface with a shimmer. It is hidden from assistive technology; mark the region it
43
+ stands in for with `aria-busy="true"` while it loads.
44
+
45
+ A skeleton is a placeholder in the shape of the content that is still loading: a few text lines, a rectangle for an image or a chart, a circle for an avatar. It sits on the `surface-sunken` fill with a soft shimmer, so the page keeps its layout while the data arrives and nothing jumps when it does. `bf-skeleton` is hidden from assistive technology; the region it stands in for says that it is busy. It works the same in both shells: in a desktop web app and in the Capacitor shell.
46
+
47
+ ## When to use
48
+
49
+ Use `bf-skeleton` while a screen or a section loads content whose shape you know: a card, a list, the rows of a table. Use [bf-spinner](https://bitforge-ui.web.app/components/spinner) instead for a short wait with no known shape, or inside a button, and [bf-progress](https://bitforge-ui.web.app/components/progress) when you can say how far the work is. Once loading has failed, replace the skeletons with a [bf-alert](https://bitforge-ui.web.app/components/alert); when it returned nothing, with a [bf-empty-state](https://bitforge-ui.web.app/components/empty-state).
50
+
51
+ ## Shapes and sizes
52
+
53
+ `shape` picks the placeholder. `text` (the default) draws one line, fully rounded; `lines` draws several, the last one shorter, as a paragraph's last line is. `rect` is one bar with the field radius, for an image, a chart or a field. `circle` is a disc for an avatar.
54
+
55
+ `width` and `height` take CSS lengths (`12rem`, `60%`, `48px`). Without a `width` a text or rect skeleton fills its container and a circle is 40 px; a circle's height always follows its width. `height` is the height of each text line (default `0.75rem`) or of the rect (default `6rem`); a circle ignores it. Match the size of the content to come, such as `0.875rem` lines for `text-body`, so the page does not move when it arrives.
56
+
57
+ ## A card while it loads
58
+
59
+ Build the placeholder from the same layout as the loaded card: the same `bf-card`, the same gaps, a skeleton where each piece of content goes. Mark the region the skeletons stand in for with `aria-busy="true"` while it loads, and drop the attribute (or set it to `false`) when the content is in.
60
+
61
+ ```ts
62
+ import { Component } from '@angular/core';
63
+ import { BfAvatarComponent, BfCardComponent, BfSkeletonComponent } from '@bitforgehq/ui';
64
+
65
+ /**
66
+ * A card while it loads, beside the loaded card.
67
+ *
68
+ * The loading card has the loaded one's layout, with a skeleton where each piece of content goes:
69
+ * a 40 px circle for the avatar, two short lines for the name and the role, three lines of text.
70
+ * Only the loading card carries `aria-busy="true"`.
71
+ */
72
+ @Component({
73
+ selector: 'app-example-skeleton-card',
74
+ imports: [BfCardComponent, BfAvatarComponent, BfSkeletonComponent],
75
+ template: `
76
+ <div class="grid grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] gap-4">
77
+ <bf-card aria-busy="true">
78
+ <div class="flex flex-col gap-4">
79
+ <div class="flex items-center gap-3">
80
+ <bf-skeleton shape="circle" />
81
+ <div class="flex min-w-0 flex-1 flex-col gap-2">
82
+ <bf-skeleton width="55%" height="1rem" />
83
+ <bf-skeleton width="35%" height="0.75rem" />
84
+ </div>
85
+ </div>
86
+ <bf-skeleton [lines]="3" height="0.875rem" />
87
+ </div>
88
+ </bf-card>
89
+ <bf-card>
90
+ <div class="flex flex-col gap-4">
91
+ <div class="flex items-center gap-3">
92
+ <bf-avatar initials="MK" />
93
+ <div class="flex min-w-0 flex-col">
94
+ <p class="text-label text-text">Mara Keller</p>
95
+ <p class="text-caption text-text-muted">Design lead</p>
96
+ </div>
97
+ </div>
98
+ <p class="text-body text-text-muted">
99
+ Runs the Atlas rebrand and the design reviews on Thursdays. Ask her about the type
100
+ scale.
101
+ </p>
102
+ </div>
103
+ </bf-card>
104
+ </div>
105
+ `,
106
+ })
107
+ export class SkeletonCardExampleComponent {}
108
+ ```
109
+
110
+ ## Table rows
111
+
112
+ A loading `bf-table` keeps its real header and shows skeleton rows: pass a few placeholder rows and render a `bf-skeleton` in each column through `bfTableCell` templates, with widths that differ from row to row. The header tells the user what is coming; the table's wrapper carries `aria-busy="true"`. In a list, render a few `bf-list-item`s with a circle and two lines the same way.
113
+
114
+ ```ts
115
+ import { Component } from '@angular/core';
116
+ import {
117
+ BfSkeletonComponent,
118
+ BfTableCellDirective,
119
+ BfTableComponent,
120
+ type BfTableColumn,
121
+ } from '@bitforgehq/ui';
122
+
123
+ interface PlaceholderRow {
124
+ id: string;
125
+ /** The width of each column's skeleton in this row, so the rows do not look stamped. */
126
+ widths: Readonly<Record<'title' | 'client' | 'amount', string>>;
127
+ }
128
+
129
+ /**
130
+ * Skeleton rows in a bf-table.
131
+ *
132
+ * The table keeps its real header while the rows load: four placeholder rows render a
133
+ * `bf-skeleton` in every column through `bfTableCell` templates, at widths that differ from row to
134
+ * row, and the wrapper carries `aria-busy="true"`.
135
+ */
136
+ @Component({
137
+ selector: 'app-example-skeleton-table',
138
+ imports: [BfTableComponent, BfTableCellDirective, BfSkeletonComponent],
139
+ template: `
140
+ <div aria-busy="true">
141
+ <bf-table label="Invoices" [rows]="rows" [columns]="columns">
142
+ <ng-template bfTableCell="title" [bfTableCellOf]="rows" let-row>
143
+ <bf-skeleton [width]="row.widths.title" height="0.875rem" />
144
+ </ng-template>
145
+ <ng-template bfTableCell="client" [bfTableCellOf]="rows" let-row>
146
+ <bf-skeleton [width]="row.widths.client" height="0.875rem" />
147
+ </ng-template>
148
+ <ng-template bfTableCell="amount" [bfTableCellOf]="rows" let-row>
149
+ <bf-skeleton class="ms-auto" [width]="row.widths.amount" height="0.875rem" />
150
+ </ng-template>
151
+ </bf-table>
152
+ </div>
153
+ `,
154
+ })
155
+ export class SkeletonTableExampleComponent {
156
+ readonly columns: readonly BfTableColumn[] = [
157
+ { key: 'title', label: 'Invoice' },
158
+ { key: 'client', label: 'Client' },
159
+ { key: 'amount', label: 'Amount', align: 'end' },
160
+ ];
161
+ readonly rows: readonly PlaceholderRow[] = [
162
+ { id: 'a', widths: { title: '70%', client: '55%', amount: '4rem' } },
163
+ { id: 'b', widths: { title: '55%', client: '70%', amount: '3rem' } },
164
+ { id: 'c', widths: { title: '80%', client: '45%', amount: '4.5rem' } },
165
+ { id: 'd', widths: { title: '60%', client: '60%', amount: '3.5rem' } },
166
+ ];
167
+ }
168
+ ```
169
+
170
+ ## Desktop
171
+
172
+ A skeleton is not interactive: no hover, no pointer cursor, no focus stop, and its size does not follow `provideBfDefaults()`. On desktop and in the Capacitor shell it looks and behaves the same. Show skeletons only for a wait that lasts: when a load usually finishes in a few hundred milliseconds, start showing them after a short delay (a timer in the state service), so a fast connection never sees a flash of placeholders.
173
+
174
+ ## Theming
175
+
176
+ - Fill: `surface-sunken`. Shimmer: a `surface` highlight at 70 % opacity that sweeps across from the inline start, with `--animate-skeleton-shimmer` (1.6 s, ease-in-out, endless) and `--animate-skeleton-shimmer-rtl` under `dir="rtl"`.
177
+ - Radius: text lines and the circle `rounded-full`; the rect `rounded-field`.
178
+ - Under `prefers-reduced-motion: reduce` the shimmer is hidden and the placeholder stays still. Under forced colours a `GrayText` border draws the shape, as backgrounds are dropped there.
179
+
180
+ ## Accessibility
181
+
182
+ Every `bf-skeleton` carries `aria-hidden="true"`: a placeholder has nothing to say, and a screen reader that read out a row of shapes would only make noise. Say that the content is loading on the region it stands in for instead: `aria-busy="true"` on that element tells assistive technology the region is being updated, and dropping it lets the finished content be read. For a load the user started and waits for (a search), also put a short polite status in the page, such as "Loading results", from a `role="status"` element or a `bf-spinner`, which announces its `label` (default "Loading"). The skeleton takes no focus, so keyboard focus stays where the user left it.
183
+
184
+ ## Inputs
185
+
186
+ | Name | Type | Default | Description |
187
+ | -------- | -------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
188
+ | `shape` | `BfSkeletonShape` (`'text' \| 'rect' \| 'circle'`) | `'text'` | What it stands in for: `text` lines (default), a `rect` (an image, a chart) or a `circle` (an avatar). |
189
+ | `lines` | `number` | `1` | How many lines a `text` skeleton draws, the last one shorter when there are several; ignored by the other shapes. |
190
+ | `width` | `string` | `''` | Width as a CSS length (`12rem`, `60%`); empty fills the container, a circle is 40 px. A circle's height follows its width. |
191
+ | `height` | `string` | `''` | Height as a CSS length: of each line of `text` (default `0.75rem`) or of the `rect` (default `6rem`); a circle ignores it. |
192
+
193
+ ## Notes
194
+
195
+ ### Do
196
+
197
+ - Shape the skeleton like the content to come, at its size, so nothing jumps when it loads.
198
+ - Put `aria-busy="true"` on the loading region, and remove it once the content is there.
199
+ - Show a few rows or cards, not a whole page of them.
200
+
201
+ ### Don't
202
+
203
+ - Don't show a skeleton for a wait that has no known shape; use a `bf-spinner`.
204
+ - Don't leave skeletons up after a load fails or returns nothing; show the error or the empty state.
205
+
206
+ ## Migration notes
207
+
208
+ ### 0.3.1
209
+
210
+ New `bf-skeleton` in `@bitforgehq/ui`, a loading placeholder for both shells: `shape` (the new
211
+ `BfSkeletonShape`: `text` lines, a `rect` or a `circle`), `lines` for several text lines with a
212
+ shorter last one, and `width` / `height` as CSS lengths. It draws the `surface-sunken` fill with a
213
+ `surface` shimmer that sweeps from the inline start (hidden under reduced motion) and carries
214
+ `aria-hidden="true"`; the docs mark the loading region with `aria-busy="true"` and show a card and
215
+ a table of skeleton rows.
@@ -0,0 +1,188 @@
1
+ # bf-sparkline
2
+
3
+ Tiny trend line with no axes or legend, for stats and table cells
4
+
5
+ `BfSparklineComponent` from `@bitforgehq/ui/charts` · Data display · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import { BfStatComponent } from '@bitforgehq/ui';
12
+ import { BfSparklineComponent } from '@bitforgehq/ui/charts';
13
+
14
+ /**
15
+ * The bf-sparkline overview.
16
+ *
17
+ * Live demo of `bf-sparkline`: three stats with a 30-day trend in their supporting line, the
18
+ * trend also stated in words; the middle one fills its area.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-sparkline-overview',
22
+ imports: [BfStatComponent, BfSparklineComponent],
23
+ template: `
24
+ <div class="grid w-full grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-3">
25
+ <bf-stat label="Revenue" value="CHF 48.2k" variant="card">
26
+ <span class="flex flex-col gap-1">
27
+ <bf-sparkline [values]="revenue" />
28
+ Up 12% in 30 days
29
+ </span>
30
+ </bf-stat>
31
+ <bf-stat label="Sign-ups" value="1,204" variant="card">
32
+ <span class="flex flex-col gap-1">
33
+ <bf-sparkline area curve="smooth" [values]="signups" />
34
+ Up 4% in 30 days
35
+ </span>
36
+ </bf-stat>
37
+ <bf-stat label="Churn" value="2.1%" variant="card">
38
+ <span class="flex flex-col gap-1">
39
+ <bf-sparkline [values]="churn" />
40
+ Down 0.4 points in 30 days
41
+ </span>
42
+ </bf-stat>
43
+ </div>
44
+ `,
45
+ })
46
+ export class SparklineOverviewExampleComponent {
47
+ readonly revenue = [31, 34, 33, 36, 35, 39, 42, 41, 45, 48];
48
+ readonly signups = [980, 1010, 1060, 1040, 1100, 1120, 1150, 1170, 1190, 1204];
49
+ readonly churn = [2.6, 2.5, 2.6, 2.4, 2.4, 2.3, 2.3, 2.2, 2.2, 2.1];
50
+ }
51
+ ```
52
+
53
+ ## Overview
54
+
55
+ A tiny line of one series with no axes, legend or tooltip, for a trend beside a figure (in a
56
+ `bf-stat`'s supporting line, a table cell). Its scale spans the values' own range, so it shows
57
+ the shape of the trend, not its size; a dot marks the latest value. With a `label` it is an
58
+ image with that name (say what the trend means, such as "Revenue up 12% over 30 days"); without
59
+ one it is decorative and hidden from assistive technology. It draws in on the first render and
60
+ on every data change, still under reduced motion; under `dir="rtl"` it runs from the right.
61
+
62
+ A sparkline is a word-sized line of one series with no axes, legend or tooltip: the shape of a trend beside a figure. `bf-sparkline` draws it in SVG from `values`, oldest first, scaled to their own range, with a dot on the latest value. It lives in `@bitforgehq/ui/charts` (`import { BfSparklineComponent } from '@bitforgehq/ui/charts'`) and works the same in a desktop web app and in the Capacitor shell.
63
+
64
+ ## When to use
65
+
66
+ Use `bf-sparkline` in a [bf-stat](https://bitforge-ui.web.app/components/stat)'s supporting line, a table cell or a list row, where the figure carries the number and the line only its direction. Use [bf-line-chart](https://bitforge-ui.web.app/components/line-chart) instead when readers need the values, the axis or more than one series.
67
+
68
+ ## Area and size
69
+
70
+ `area` fills under the line; `curve="smooth"` draws a monotone curve. `height` sets the height in px (32 by default); the width follows the container, so give the element a width (`class="w-24"`) where it sits in a row. A flat series draws a level line in the middle, and a `null` value leaves a gap. The line draws in on the first render and whenever `values` change.
71
+
72
+ ```ts
73
+ import { Component } from '@angular/core';
74
+ import { BfSparklineComponent } from '@bitforgehq/ui/charts';
75
+
76
+ interface Region {
77
+ readonly name: string;
78
+ readonly total: string;
79
+ readonly trend: readonly (number | null)[];
80
+ readonly summary: string;
81
+ }
82
+
83
+ /**
84
+ * Sparklines in a table.
85
+ *
86
+ * A fixed-width sparkline per row, each named for screen readers by what its trend means; a
87
+ * `null` leaves a gap where a week was not reported.
88
+ */
89
+ @Component({
90
+ selector: 'app-example-sparkline-table',
91
+ imports: [BfSparklineComponent],
92
+ template: `
93
+ <table class="text-label-sm w-full text-start">
94
+ <caption class="sr-only">
95
+ Orders by region
96
+ </caption>
97
+ <thead>
98
+ <tr class="border-border border-b">
99
+ <th scope="col" class="text-label-xs text-text-muted h-8 px-3 text-start">Region</th>
100
+ <th scope="col" class="text-label-xs text-text-muted h-8 px-3 text-end">Orders</th>
101
+ <th scope="col" class="text-label-xs text-text-muted h-8 px-3 text-start">8 weeks</th>
102
+ </tr>
103
+ </thead>
104
+ <tbody>
105
+ @for (region of regions; track region.name) {
106
+ <tr class="border-divider border-b">
107
+ <th scope="row" class="text-text h-10 px-3 text-start font-bold">{{ region.name }}</th>
108
+ <td class="text-text h-10 px-3 text-end tabular-nums">{{ region.total }}</td>
109
+ <td class="h-10 px-3">
110
+ <bf-sparkline
111
+ class="w-24"
112
+ [height]="24"
113
+ [values]="region.trend"
114
+ [label]="region.summary"
115
+ />
116
+ </td>
117
+ </tr>
118
+ }
119
+ </tbody>
120
+ </table>
121
+ `,
122
+ })
123
+ export class SparklineTableExampleComponent {
124
+ readonly regions: readonly Region[] = [
125
+ {
126
+ name: 'North',
127
+ total: '1,284',
128
+ trend: [120, 132, 128, 141, 150, 149, 162, 171],
129
+ summary: 'Rising',
130
+ },
131
+ {
132
+ name: 'Central',
133
+ total: '962',
134
+ trend: [131, 127, 119, null, 112, 118, 109, 104],
135
+ summary: 'Falling',
136
+ },
137
+ {
138
+ name: 'South',
139
+ total: '1,047',
140
+ trend: [128, 131, 126, 133, 129, 132, 130, 131],
141
+ summary: 'Steady',
142
+ },
143
+ ];
144
+ }
145
+ ```
146
+
147
+ ## Desktop
148
+
149
+ A sparkline is not interactive: no hover, no tooltip, no focus stop. Its size does not follow `provideBfDefaults()`. Inside `provideBfShell()` it renders the same.
150
+
151
+ ## Theming
152
+
153
+ - Line: `--color-chart-1` (`stroke-chart-1`), 2 px; area the same at 16 % (`fill-chart-1/16`); the end dot `fill-chart-1`.
154
+ - Motion: `--animate-chart-draw` and `--animate-chart-fade`; none under `prefers-reduced-motion`.
155
+
156
+ ## Accessibility
157
+
158
+ With a `label` the SVG is a `role="img"` named by it: say what the trend means ("Revenue up 12% over 30 days"), not that it is a chart. Without one it is decorative and hidden from assistive technology, which is right beside a figure whose supporting text already states the trend. It has no focus stop and no data table; put the values in a table or a `bf-line-chart` when readers need them.
159
+
160
+ ## Inputs
161
+
162
+ | Name | Type | Default | Description |
163
+ | -------- | --------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
164
+ | `values` | `readonly (number \| null)[]` | required | The values in order, oldest first; `null` leaves a gap. |
165
+ | `label` | `string` | `''` | What the trend means, its accessible name ("Revenue up 12% this month"); empty makes the sparkline decorative. |
166
+ | `height` | `number` | `32` | Height in px; the width follows the container. |
167
+ | `area` | `boolean` | `false` | Fills the area under the line. |
168
+ | `curve` | `BfChartCurve` (`'linear' \| 'smooth'`) | `'linear'` | The line between points: `linear` (default) or `smooth`, a monotone curve. |
169
+
170
+ ## Notes
171
+
172
+ ### Do
173
+
174
+ - State the trend in words next to it (the stat's supporting line, a `+12%`), so it is never colour or shape alone.
175
+ - Use the same time window for every sparkline in a row.
176
+
177
+ ### Don't
178
+
179
+ - Don't compare sparklines' heights: each one is scaled to its own range.
180
+ - Don't put a tooltip or a link on a sparkline; link the stat or the row instead.
181
+
182
+ ## Migration notes
183
+
184
+ ### 0.3.1
185
+
186
+ New `bf-sparkline` in `@bitforgehq/ui/charts`: a tiny line of one series with no axes, legend or
187
+ tooltip, for a `bf-stat`'s supporting line or a table cell; `area`, `curve` and `height`, a dot on
188
+ the latest value. With a `label` it is a named image, without one it is decorative.