@bitforgehq/ui 0.3.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 (281) hide show
  1. package/CHANGELOG.md +3665 -0
  2. package/LICENSE +21 -0
  3. package/README.md +175 -0
  4. package/ai/MANIFEST.index.json +383 -0
  5. package/ai/MANIFEST.json +7528 -0
  6. package/ai/REFERENCE.index.json +1461 -0
  7. package/ai/REFERENCE.json +7213 -0
  8. package/ai/UI_GUIDE.md +1159 -0
  9. package/ai/components/action-bar.md +209 -0
  10. package/ai/components/action-sheet.md +526 -0
  11. package/ai/components/alert-dialog.md +509 -0
  12. package/ai/components/alert.md +376 -0
  13. package/ai/components/avatar.md +146 -0
  14. package/ai/components/back-button.md +286 -0
  15. package/ai/components/badge.md +171 -0
  16. package/ai/components/button.md +431 -0
  17. package/ai/components/card.md +303 -0
  18. package/ai/components/checkbox.md +418 -0
  19. package/ai/components/chip-group.md +205 -0
  20. package/ai/components/chip.md +300 -0
  21. package/ai/components/content.md +474 -0
  22. package/ai/components/desktop-toast.md +420 -0
  23. package/ai/components/dialog.md +962 -0
  24. package/ai/components/empty-state.md +146 -0
  25. package/ai/components/field.md +319 -0
  26. package/ai/components/header.md +809 -0
  27. package/ai/components/icon-button.md +221 -0
  28. package/ai/components/icon.md +176 -0
  29. package/ai/components/input.md +455 -0
  30. package/ai/components/list-item.md +326 -0
  31. package/ai/components/list.md +178 -0
  32. package/ai/components/menu-button.md +377 -0
  33. package/ai/components/menu.md +661 -0
  34. package/ai/components/modal.md +613 -0
  35. package/ai/components/number-input.md +423 -0
  36. package/ai/components/page.md +299 -0
  37. package/ai/components/progress.md +215 -0
  38. package/ai/components/radio-group.md +432 -0
  39. package/ai/components/refresher.md +377 -0
  40. package/ai/components/segmented.md +517 -0
  41. package/ai/components/select.md +510 -0
  42. package/ai/components/sheet.md +845 -0
  43. package/ai/components/spinner.md +171 -0
  44. package/ai/components/stack-outlet.md +661 -0
  45. package/ai/components/tab-bar.md +427 -0
  46. package/ai/components/tab.md +295 -0
  47. package/ai/components/tabs.md +408 -0
  48. package/ai/components/textarea.md +371 -0
  49. package/ai/components/toast.md +522 -0
  50. package/ai/components/toggle.md +387 -0
  51. package/ai/reference/bf-action-directive.md +21 -0
  52. package/ai/reference/bf-action-sheet-action-role.md +27 -0
  53. package/ai/reference/bf-action-sheet-action.md +28 -0
  54. package/ai/reference/bf-action-sheet-options.md +28 -0
  55. package/ai/reference/bf-action-sheet-service.md +33 -0
  56. package/ai/reference/bf-alert-dialog-button-role.md +27 -0
  57. package/ai/reference/bf-alert-dialog-button.md +27 -0
  58. package/ai/reference/bf-alert-dialog-options.md +29 -0
  59. package/ai/reference/bf-alert-dialog-service.md +34 -0
  60. package/ai/reference/bf-alert-variant.md +28 -0
  61. package/ai/reference/bf-autocapitalize.md +30 -0
  62. package/ai/reference/bf-avatar-size.md +27 -0
  63. package/ai/reference/bf-avatar-variant.md +28 -0
  64. package/ai/reference/bf-back-at-root.md +28 -0
  65. package/ai/reference/bf-back-button-service.md +40 -0
  66. package/ai/reference/bf-badge-variant.md +29 -0
  67. package/ai/reference/bf-button-size.md +29 -0
  68. package/ai/reference/bf-button-type.md +28 -0
  69. package/ai/reference/bf-button-variant.md +29 -0
  70. package/ai/reference/bf-card-padding.md +26 -0
  71. package/ai/reference/bf-card-variant.md +28 -0
  72. package/ai/reference/bf-chip-group-mode.md +27 -0
  73. package/ai/reference/bf-chip-variant.md +26 -0
  74. package/ai/reference/bf-compare-with.md +24 -0
  75. package/ai/reference/bf-component-overlay-ref.md +33 -0
  76. package/ai/reference/bf-confirm-button-role.md +26 -0
  77. package/ai/reference/bf-confirm-button.md +27 -0
  78. package/ai/reference/bf-confirm-options.md +31 -0
  79. package/ai/reference/bf-confirm-size.md +26 -0
  80. package/ai/reference/bf-content-scroll-event.md +21 -0
  81. package/ai/reference/bf-defaults-options.md +32 -0
  82. package/ai/reference/bf-defaults.md +28 -0
  83. package/ai/reference/bf-desktop-toast-config.md +28 -0
  84. package/ai/reference/bf-desktop-toast-harness.md +33 -0
  85. package/ai/reference/bf-desktop-toast-options.md +28 -0
  86. package/ai/reference/bf-desktop-toast-position.md +28 -0
  87. package/ai/reference/bf-desktop-toast-service.md +42 -0
  88. package/ai/reference/bf-dialog-config.md +44 -0
  89. package/ai/reference/bf-dialog-harness.md +52 -0
  90. package/ai/reference/bf-dialog-ref.md +44 -0
  91. package/ai/reference/bf-dialog-role.md +26 -0
  92. package/ai/reference/bf-dialog-service.md +55 -0
  93. package/ai/reference/bf-dialog-size.md +31 -0
  94. package/ai/reference/bf-dialog-template-context.md +32 -0
  95. package/ai/reference/bf-empty-state-heading-level.md +26 -0
  96. package/ai/reference/bf-enter-key-hint.md +31 -0
  97. package/ai/reference/bf-field-control-class.md +32 -0
  98. package/ai/reference/bf-field-control-state.md +28 -0
  99. package/ai/reference/bf-field-size.md +29 -0
  100. package/ai/reference/bf-haptics-impact-style.md +27 -0
  101. package/ai/reference/bf-haptics-notification-type.md +27 -0
  102. package/ai/reference/bf-haptics-service.md +41 -0
  103. package/ai/reference/bf-harness-filters.md +29 -0
  104. package/ai/reference/bf-horizontal-scroll-directive.md +129 -0
  105. package/ai/reference/bf-icon-button-variant.md +28 -0
  106. package/ai/reference/bf-icon-size.md +27 -0
  107. package/ai/reference/bf-input-autocapitalize.md +36 -0
  108. package/ai/reference/bf-input-enter-key-hint.md +38 -0
  109. package/ai/reference/bf-input-input-mode.md +38 -0
  110. package/ai/reference/bf-input-mode.md +32 -0
  111. package/ai/reference/bf-input-type.md +35 -0
  112. package/ai/reference/bf-jsdom-shim-options.md +22 -0
  113. package/ai/reference/bf-keyboard-assist-behavior.md +26 -0
  114. package/ai/reference/bf-keyboard-assist-directive.md +185 -0
  115. package/ai/reference/bf-keyboard-phase.md +30 -0
  116. package/ai/reference/bf-keyboard-service.md +50 -0
  117. package/ai/reference/bf-labels-overrides.md +26 -0
  118. package/ai/reference/bf-labels.md +44 -0
  119. package/ai/reference/bf-menu-button-harness.md +40 -0
  120. package/ai/reference/bf-menu-button-variant.md +27 -0
  121. package/ai/reference/bf-menu-divider-directive.md +13 -0
  122. package/ai/reference/bf-menu-group-directive.md +14 -0
  123. package/ai/reference/bf-menu-harness.md +55 -0
  124. package/ai/reference/bf-menu-item-checkbox-directive.md +31 -0
  125. package/ai/reference/bf-menu-item-directive.md +31 -0
  126. package/ai/reference/bf-menu-item-harness.md +52 -0
  127. package/ai/reference/bf-menu-item-radio-directive.md +29 -0
  128. package/ai/reference/bf-menu-placement.md +29 -0
  129. package/ai/reference/bf-menu-trigger-directive.md +46 -0
  130. package/ai/reference/bf-modal-options.md +30 -0
  131. package/ai/reference/bf-modal-presentation.md +26 -0
  132. package/ai/reference/bf-modal-service.md +38 -0
  133. package/ai/reference/bf-nav-controller.md +59 -0
  134. package/ai/reference/bf-nav-direction.md +23 -0
  135. package/ai/reference/bf-nav-target.md +21 -0
  136. package/ai/reference/bf-number-input-mode.md +28 -0
  137. package/ai/reference/bf-number-input-step.md +25 -0
  138. package/ai/reference/bf-overlay-auto-focus.md +29 -0
  139. package/ai/reference/bf-overlay-controller.md +22 -0
  140. package/ai/reference/bf-overlay-host.md +36 -0
  141. package/ai/reference/bf-overlay-options.md +53 -0
  142. package/ai/reference/bf-overlay-position.md +30 -0
  143. package/ai/reference/bf-overlay-ref.md +53 -0
  144. package/ai/reference/bf-overlay-role.md +27 -0
  145. package/ai/reference/bf-overlay-service.md +53 -0
  146. package/ai/reference/bf-overlay-stack-options.md +24 -0
  147. package/ai/reference/bf-overlay-stack.md +81 -0
  148. package/ai/reference/bf-overlay-template-context.md +24 -0
  149. package/ai/reference/bf-overscroll-stretch-directive.md +163 -0
  150. package/ai/reference/bf-page-context.md +24 -0
  151. package/ai/reference/bf-platform-service.md +53 -0
  152. package/ai/reference/bf-platform.md +28 -0
  153. package/ai/reference/bf-progress-size.md +26 -0
  154. package/ai/reference/bf-progress-tone.md +28 -0
  155. package/ai/reference/bf-radio-group-harness.md +49 -0
  156. package/ai/reference/bf-radio-group-orientation.md +26 -0
  157. package/ai/reference/bf-radio-option.md +30 -0
  158. package/ai/reference/bf-refresher-refresh-event.md +22 -0
  159. package/ai/reference/bf-segment-option.md +29 -0
  160. package/ai/reference/bf-segmented-harness.md +48 -0
  161. package/ai/reference/bf-segmented-size.md +29 -0
  162. package/ai/reference/bf-select-harness.md +70 -0
  163. package/ai/reference/bf-select-option.md +29 -0
  164. package/ai/reference/bf-sheet-auto-focus.md +29 -0
  165. package/ai/reference/bf-sheet-options.md +32 -0
  166. package/ai/reference/bf-sheet-ref.md +45 -0
  167. package/ai/reference/bf-sheet-service.md +34 -0
  168. package/ai/reference/bf-shell-config-mode.md +28 -0
  169. package/ai/reference/bf-shell-config.md +38 -0
  170. package/ai/reference/bf-shell-dynamic-type-limit.md +21 -0
  171. package/ai/reference/bf-shell-dynamic-type.md +25 -0
  172. package/ai/reference/bf-shell-mode.md +28 -0
  173. package/ai/reference/bf-shell-testing-options.md +26 -0
  174. package/ai/reference/bf-size.md +45 -0
  175. package/ai/reference/bf-spinner-size.md +27 -0
  176. package/ai/reference/bf-stack-change.md +22 -0
  177. package/ai/reference/bf-stack-reuse-strategy.md +32 -0
  178. package/ai/reference/bf-stack-transition-event.md +26 -0
  179. package/ai/reference/bf-stack-transition.md +26 -0
  180. package/ai/reference/bf-stacked-overlay.md +23 -0
  181. package/ai/reference/bf-system-bars-style.md +38 -0
  182. package/ai/reference/bf-tab-bar-controller.md +51 -0
  183. package/ai/reference/bf-tab-bar-host.md +52 -0
  184. package/ai/reference/bf-tab-context.md +32 -0
  185. package/ai/reference/bf-tab-links.md +37 -0
  186. package/ai/reference/bf-testing-options.md +27 -0
  187. package/ai/reference/bf-text-scale-service.md +35 -0
  188. package/ai/reference/bf-theme.md +27 -0
  189. package/ai/reference/bf-toast-action.md +25 -0
  190. package/ai/reference/bf-toast-controller.md +17 -0
  191. package/ai/reference/bf-toast-dismiss-reason.md +29 -0
  192. package/ai/reference/bf-toast-options.md +29 -0
  193. package/ai/reference/bf-toast-position.md +26 -0
  194. package/ai/reference/bf-toast-ref.md +41 -0
  195. package/ai/reference/bf-toast-service.md +39 -0
  196. package/ai/reference/bf-toggle-label-position.md +26 -0
  197. package/ai/reference/bf-transition-kind.md +28 -0
  198. package/ai/reference/bf-validation-message.md +31 -0
  199. package/ai/reference/bf-validation-messages.md +33 -0
  200. package/ai/reference/bf-view-event.md +28 -0
  201. package/ai/reference/bf-view-lifecycle.md +41 -0
  202. package/ai/reference/bf-view-state.md +28 -0
  203. package/ai/reference/bf_default_labels.md +21 -0
  204. package/ai/reference/bf_default_values.md +21 -0
  205. package/ai/reference/bf_defaults.md +21 -0
  206. package/ai/reference/bf_desktop_toast_defaults.md +21 -0
  207. package/ai/reference/bf_dialog_data.md +17 -0
  208. package/ai/reference/bf_labels.md +21 -0
  209. package/ai/reference/bf_overlay_data.md +19 -0
  210. package/ai/reference/bf_overlay_host.md +21 -0
  211. package/ai/reference/bf_sheet_default_snap_points.md +17 -0
  212. package/ai/reference/bf_shell_config.md +21 -0
  213. package/ai/reference/bf_shell_defaults.md +21 -0
  214. package/ai/reference/bf_tab_bar.md +21 -0
  215. package/ai/reference/bf_tab_bar_controller.md +21 -0
  216. package/ai/reference/bf_tab_context.md +21 -0
  217. package/ai/reference/bf_tab_links.md +21 -0
  218. package/ai/reference/bf_theme_storage.md +19 -0
  219. package/ai/reference/create-transition.md +36 -0
  220. package/ai/reference/duration_fast.md +17 -0
  221. package/ai/reference/duration_md.md +17 -0
  222. package/ai/reference/duration_overlay.md +17 -0
  223. package/ai/reference/duration_push.md +17 -0
  224. package/ai/reference/fade-down.md +26 -0
  225. package/ai/reference/fade-in.md +25 -0
  226. package/ai/reference/fade-out.md +25 -0
  227. package/ai/reference/fade-up.md +26 -0
  228. package/ai/reference/inject-shell-mode.md +23 -0
  229. package/ai/reference/install-bf-jsdom-shims.md +31 -0
  230. package/ai/reference/ios_duration.md +17 -0
  231. package/ai/reference/ios_easing.md +17 -0
  232. package/ai/reference/is-text-entry.md +25 -0
  233. package/ai/reference/max_complete_ms.md +17 -0
  234. package/ai/reference/md-transition.md +31 -0
  235. package/ai/reference/md_duration.md +17 -0
  236. package/ai/reference/md_easing.md +17 -0
  237. package/ai/reference/overlay_duration.md +21 -0
  238. package/ai/reference/overlay_easing.md +19 -0
  239. package/ai/reference/overlay_reduced_duration.md +17 -0
  240. package/ai/reference/pop-frames-at.md +25 -0
  241. package/ai/reference/prefers-reduced-motion.md +25 -0
  242. package/ai/reference/provide-bf-defaults.md +33 -0
  243. package/ai/reference/provide-bf-desktop-toast.md +28 -0
  244. package/ai/reference/provide-bf-labels.md +38 -0
  245. package/ai/reference/provide-bf-shell-testing.md +34 -0
  246. package/ai/reference/provide-bf-shell.md +41 -0
  247. package/ai/reference/provide-bf-testing.md +33 -0
  248. package/ai/reference/reduced-transition.md +28 -0
  249. package/ai/reference/reduced_duration.md +17 -0
  250. package/ai/reference/resolve-shell-mode.md +33 -0
  251. package/ai/reference/scale-in.md +25 -0
  252. package/ai/reference/settle-duration.md +25 -0
  253. package/ai/reference/settle-pop.md +33 -0
  254. package/ai/reference/settle_easing.md +17 -0
  255. package/ai/reference/should-complete-pop.md +26 -0
  256. package/ai/reference/slide-down.md +26 -0
  257. package/ai/reference/slide-up.md +26 -0
  258. package/ai/reference/with-bf-shell-routing.md +19 -0
  259. package/bin/bf-ui.js +81 -0
  260. package/fesm2022/bitforgehq-ui-icons.mjs +230 -0
  261. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -0
  262. package/fesm2022/bitforgehq-ui-mobile.mjs +5530 -0
  263. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -0
  264. package/fesm2022/bitforgehq-ui-number-input.mjs +680 -0
  265. package/fesm2022/bitforgehq-ui-number-input.mjs.map +1 -0
  266. package/fesm2022/bitforgehq-ui-overlay.mjs +2675 -0
  267. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -0
  268. package/fesm2022/bitforgehq-ui-testing.mjs +402 -0
  269. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -0
  270. package/fesm2022/bitforgehq-ui.mjs +7765 -0
  271. package/fesm2022/bitforgehq-ui.mjs.map +1 -0
  272. package/llms-full.txt +25394 -0
  273. package/llms.txt +329 -0
  274. package/package.json +94 -0
  275. package/theme.css +253 -0
  276. package/types/bitforgehq-ui-icons.d.ts +50 -0
  277. package/types/bitforgehq-ui-mobile.d.ts +2914 -0
  278. package/types/bitforgehq-ui-number-input.d.ts +282 -0
  279. package/types/bitforgehq-ui-overlay.d.ts +1514 -0
  280. package/types/bitforgehq-ui-testing.d.ts +495 -0
  281. package/types/bitforgehq-ui.d.ts +3499 -0
@@ -0,0 +1,146 @@
1
+ # bf-empty-state
2
+
3
+ Placeholder for an empty screen or list with icon, title, message and action
4
+
5
+ `BfEmptyStateComponent` from `@bitforgehq/ui` · Feedback · since 0.1.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { BfButtonComponent, BfEmptyStateComponent } from '@bitforgehq/ui';
12
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
13
+ import { ListTodo } from 'lucide';
14
+
15
+ /**
16
+ * The bf-empty-state overview.
17
+ *
18
+ * Live demo of `bf-empty-state`: an icon tile, a title, a description and a primary action that counts the tasks it creates.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-empty-state-overview',
22
+ imports: [BfEmptyStateComponent, BfIconComponent, BfButtonComponent],
23
+ template: `
24
+ <div class="flex flex-col gap-3">
25
+ <bf-empty-state title="No tasks yet" description="Add your first task to get started.">
26
+ <bf-icon bfSlot="icon" [icon]="listTodo" size="lg" />
27
+ <bf-button bfSlot="actions" (clicked)="created.set(created() + 1)">New task</bf-button>
28
+ </bf-empty-state>
29
+ <p data-status aria-live="polite" class="text-caption text-text-muted text-center">
30
+ {{ created() === 0 ? 'No task created yet.' : 'Created ' + created() + ' task(s).' }}
31
+ </p>
32
+ </div>
33
+ `,
34
+ })
35
+ export class EmptyStateOverviewExampleComponent {
36
+ readonly listTodo = ListTodo;
37
+ readonly created = signal(0);
38
+ }
39
+ ```
40
+
41
+ ## Overview
42
+
43
+ Placeholder for a screen or list with nothing in it yet: an icon tile, a title, one sentence,
44
+ and the action that fills it.
45
+
46
+ An empty state fills the space where a list or screen would otherwise show nothing: an icon tile, a title, one sentence of explanation, and the action that gets the user unstuck.
47
+
48
+ ## When to use
49
+
50
+ Use `bf-empty-state` where a list, a search result or a screen has nothing to show yet, with the one action that fills it. Use [bf-spinner](https://bitforge-ui.web.app/components/spinner) instead while the content is still loading, and [bf-alert](https://bitforge-ui.web.app/components/alert) when loading failed.
51
+
52
+ ## Basic usage
53
+
54
+ ```ts
55
+ import { Component } from '@angular/core';
56
+ import { BfEmptyStateComponent } from '@bitforgehq/ui';
57
+
58
+ /** An empty state with a title and a description. */
59
+ @Component({
60
+ selector: 'app-example-empty-state-basic',
61
+ imports: [BfEmptyStateComponent],
62
+ template: `
63
+ <bf-empty-state title="No tasks yet" description="Add your first task to get started." />
64
+ `,
65
+ })
66
+ export class EmptyStateBasicExampleComponent {}
67
+ ```
68
+
69
+ ## With an action
70
+
71
+ Put the icon in the `icon` slot (a `bf-icon` or any glyph) and the primary action in the `actions` slot; the empty state centers both for you.
72
+
73
+ ```ts
74
+ import { Component, signal } from '@angular/core';
75
+ import { BfButtonComponent, BfEmptyStateComponent } from '@bitforgehq/ui';
76
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
77
+ import { ListTodo } from 'lucide';
78
+
79
+ /** An empty state with an icon and an action. */
80
+ @Component({
81
+ selector: 'app-example-empty-state-action',
82
+ imports: [BfButtonComponent, BfEmptyStateComponent, BfIconComponent],
83
+ template: `
84
+ <bf-empty-state
85
+ title="No tasks yet"
86
+ [description]="created() ? 'Task created.' : 'Add your first task to get started.'"
87
+ >
88
+ <bf-icon bfSlot="icon" [icon]="listTodo" size="lg" />
89
+ <bf-button bfSlot="actions" (clicked)="created.set(true)">New task</bf-button>
90
+ </bf-empty-state>
91
+ `,
92
+ })
93
+ export class EmptyStateActionExampleComponent {
94
+ protected readonly listTodo = ListTodo;
95
+ protected readonly created = signal(false);
96
+ }
97
+ ```
98
+
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
+
101
+ ## Theming
102
+
103
+ - Icon tile: `surface-sunken` background, `text-muted` icon color, `tile` radius.
104
+ - Title: `text`. Description: `text-muted`.
105
+
106
+ ## Accessibility
107
+
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.
109
+
110
+ ## Inputs
111
+
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. |
117
+
118
+ ## Slots
119
+
120
+ - `icon`: The icon in the tile above the title.
121
+ - `actions`: Buttons under the description, such as Create.
122
+
123
+ ## Notes
124
+
125
+ ### Do
126
+
127
+ - Keep `description` to one sentence that says what to do next.
128
+ - Offer the action that resolves the empty state, not a link elsewhere.
129
+
130
+ ### Don't
131
+
132
+ - Don't use `bf-empty-state` for a loading screen; that is `bf-spinner`.
133
+
134
+ ## Migration notes
135
+
136
+ ### 0.3.0
137
+
138
+ New exported `BfEmptyStateHeadingLevel` (`2 | 3`), the type of `headingLevel` (was an inline union
139
+ with the same values).
140
+
141
+ JSDoc of `empty-state.component.ts` for the API reference: `@slot` docs for `icon`, `actions`. No
142
+ behaviour change.
143
+
144
+ ### 0.1.0
145
+
146
+ New `bf-empty-state`: a centered placeholder with a 64 px `icon` tile, a required `title`, an optional `description`, and an `actions` slot for the action that fills it. Added `headingLevel` (`2` default or `3`) so the title renders as `h2` or `h3` to fit the page outline.
@@ -0,0 +1,319 @@
1
+ # bf-field
2
+
3
+ Label, help text and error text wrapper that wires ids for assistive tech
4
+
5
+ `BfFieldComponent` from `@bitforgehq/ui` · Forms · since 0.1.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { BfFieldComponent, bfFieldControlClass } from '@bitforgehq/ui';
12
+
13
+ /**
14
+ * The bf-field overview.
15
+ *
16
+ * Live demo of `bf-field`: wrapping a native `<input>` with a label and help text, styled by
17
+ * `bfFieldControlClass()` like `bf-input` and described by the field's help text; the typed value
18
+ * is echoed.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-field-overview',
22
+ imports: [BfFieldComponent],
23
+ template: `
24
+ <div class="flex flex-col gap-3">
25
+ <bf-field
26
+ #field
27
+ label="Project name"
28
+ helpText="Visible to your team"
29
+ controlId="field-demo-project"
30
+ >
31
+ <input
32
+ #project
33
+ id="field-demo-project"
34
+ placeholder="Atlas rebrand"
35
+ [class]="controlClass"
36
+ [attr.aria-describedby]="field.describedBy()"
37
+ (input)="name.set(project.value)"
38
+ />
39
+ </bf-field>
40
+ <p data-status aria-live="polite" class="text-caption text-text-muted">
41
+ {{ name() ? 'Name: ' + name() : 'Type a project name.' }}
42
+ </p>
43
+ </div>
44
+ `,
45
+ })
46
+ export class FieldOverviewExampleComponent {
47
+ readonly name = signal('');
48
+ protected readonly controlClass = bfFieldControlClass({
49
+ size: 'md',
50
+ invalid: false,
51
+ disabled: false,
52
+ readonly: false,
53
+ });
54
+ }
55
+ ```
56
+
57
+ ## Overview
58
+
59
+ Label, help text and error text around a form control, with the id wiring assistive tech needs.
60
+ The form widgets use it internally; use it directly only to dress a custom control the same way.
61
+
62
+ A field wraps a control with its label, helper text and error text, and wires the ids that screen readers use. Every form widget in the library renders one internally; reach for it directly when you build a custom control and want it to look and behave like the others.
63
+
64
+ ## When to use
65
+
66
+ Use `bf-field` around a control you build yourself, so it gets the library's label, help and error text and their ids. Use [bf-input](https://bitforge-ui.web.app/components/input), [bf-select](https://bitforge-ui.web.app/components/select) or [bf-textarea](https://bitforge-ui.web.app/components/textarea) instead for a standard field: each renders its own `bf-field`.
67
+
68
+ ## Basic usage
69
+
70
+ Give the control an id and pass the same id as `controlId`. Style a native control with `bfFieldControlClass({ size, invalid, disabled, readonly })` from `@bitforgehq/ui` (`@since 0.3.0`): it returns the class list `bf-input` renders on its own `<input>` in the same state, so the custom control has the library's box, height per `BfSize` step, focus ring and danger border. Set the native `disabled` and `readonly` attributes on the control as well; their styles come from `:disabled` and `:read-only`. Take the field's template reference (`#field`) and bind the control's `aria-describedby` to `field.describedBy()`, and its `aria-invalid` to your own error state, so help and error text are announced.
71
+
72
+ ```ts
73
+ import { Component, computed, inject, signal } from '@angular/core';
74
+ import { BF_DEFAULTS, BfFieldComponent, bfFieldControlClass } from '@bitforgehq/ui';
75
+
76
+ /** Keeps the control id unique when the example renders more than once. */
77
+ let nextId = 0;
78
+
79
+ /**
80
+ * A custom control inside bf-field.
81
+ *
82
+ * A native `<input>` for a project code, styled by `bfFieldControlClass()` exactly like
83
+ * `bf-input` (size from `provideBfDefaults()`, the danger border once the code is invalid). The
84
+ * field's template reference hands `describedBy()` to the control's `aria-describedby`, so the
85
+ * help text, and after a wrong entry the error text, is announced. The code shows in capitals
86
+ * as it is typed (CSS `uppercase`, so the caret stays where it is) and is stored in capitals when
87
+ * the field is left.
88
+ */
89
+ @Component({
90
+ selector: 'app-example-field-custom-control',
91
+ imports: [BfFieldComponent],
92
+ template: `
93
+ <bf-field
94
+ #field
95
+ label="Project code"
96
+ helpText="Three letters, a dash and three digits, e.g. ATL-204"
97
+ [error]="error()"
98
+ [controlId]="id"
99
+ >
100
+ <input
101
+ #control
102
+ [id]="id"
103
+ autocomplete="off"
104
+ spellcheck="false"
105
+ placeholder="ATL-204"
106
+ [value]="code()"
107
+ [class]="controlClass() + ' uppercase'"
108
+ [attr.aria-invalid]="error() ? 'true' : null"
109
+ [attr.aria-describedby]="field.describedBy()"
110
+ (input)="code.set(control.value)"
111
+ (blur)="leave(control.value)"
112
+ />
113
+ </bf-field>
114
+ `,
115
+ })
116
+ export class FieldCustomControlExampleComponent {
117
+ private readonly size = inject(BF_DEFAULTS).fieldSize;
118
+
119
+ protected readonly id = `project-code-${nextId++}`;
120
+ protected readonly code = signal('');
121
+ protected readonly touched = signal(false);
122
+ protected readonly error = computed(() =>
123
+ this.touched() && !/^[A-Z]{3}-\d{3}$/i.test(this.code()) ? 'Enter a code like ATL-204' : '',
124
+ );
125
+ protected readonly controlClass = computed(() =>
126
+ bfFieldControlClass({
127
+ size: this.size,
128
+ invalid: this.error() !== '',
129
+ disabled: false,
130
+ readonly: false,
131
+ }),
132
+ );
133
+
134
+ /** Marks the field touched and stores the code in capitals, as it is shown. */
135
+ protected leave(value: string): void {
136
+ this.code.set(value.toUpperCase());
137
+ this.touched.set(true);
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Error state
143
+
144
+ Setting `error` renders the error text in `danger` under the control; the control itself decides how it renders invalid (`bfFieldControlClass({ …, invalid: true })` gives it the danger border).
145
+
146
+ ```ts
147
+ import { Component, signal } from '@angular/core';
148
+ import { BfFieldComponent, bfFieldControlClass } from '@bitforgehq/ui';
149
+
150
+ /**
151
+ * A field in its error state.
152
+ *
153
+ * `error` shows under the control in the danger colour; the control shows the danger border
154
+ * itself, here through `bfFieldControlClass({ invalid: true, … })`.
155
+ */
156
+ @Component({
157
+ selector: 'app-example-field-error',
158
+ imports: [BfFieldComponent],
159
+ template: `
160
+ <bf-field #field label="Email" [error]="emailError()" controlId="field-error-email">
161
+ <input
162
+ id="field-error-email"
163
+ type="email"
164
+ value="lena@studio"
165
+ [class]="controlClass"
166
+ aria-invalid="true"
167
+ [attr.aria-describedby]="field.describedBy()"
168
+ />
169
+ </bf-field>
170
+ `,
171
+ })
172
+ export class FieldErrorExampleComponent {
173
+ protected readonly emailError = signal('Enter a valid email address');
174
+ protected readonly controlClass = bfFieldControlClass({
175
+ size: 'md',
176
+ invalid: true,
177
+ disabled: false,
178
+ readonly: false,
179
+ });
180
+ }
181
+ ```
182
+
183
+ ## Help and error
184
+
185
+ The help text stays while an error shows: the error sits under it, so the hint the user needs to fix the value ("Letters and numbers only") never disappears at the moment it matters. `describedBy()` then lists both ids, help first (`"<controlId>-help <controlId>-error"`), so a screen reader reads the hint and the error.
186
+
187
+ An error that appears while the primary mouse button, a finger or a pen is held down is shown only once the press ends. A field is marked touched on blur, and a press on the form's Save button blurs it on mousedown: showing the message right then would push Save down by a line before the mouseup, and the click would be lost. During the press the message is already in the page, visually hidden, so the ids in `describedBy()` resolve and a screen reader reads it as soon as focus returns to the field; the same paragraph becomes visible a moment after the release, once the click has gone through. A key press or a pointer move with no button down shows it at once. An error that goes away during a press stays on screen until the release, for the same reason.
188
+
189
+ ```ts
190
+ import { Component, signal } from '@angular/core';
191
+ import { FormField, FormRoot, form, pattern, required } from '@angular/forms/signals';
192
+ import { BfButtonComponent, BfInputComponent } from '@bitforgehq/ui';
193
+
194
+ /**
195
+ * Help text that stays when the error shows.
196
+ *
197
+ * Press Save: the error appears under the help text, and the field is described by both.
198
+ */
199
+ @Component({
200
+ selector: 'app-example-field-help-and-error',
201
+ imports: [FormField, FormRoot, BfButtonComponent, BfInputComponent],
202
+ template: `
203
+ <form [formRoot]="account" class="flex w-full flex-col gap-4 sm:w-80">
204
+ <bf-input
205
+ label="Username"
206
+ helpText="Letters and numbers only"
207
+ [formField]="account.username"
208
+ />
209
+ <bf-button type="submit" class="self-start">Save</bf-button>
210
+ </form>
211
+ `,
212
+ })
213
+ export class FieldHelpAndErrorExampleComponent {
214
+ private readonly model = signal({ username: 'lena moser' });
215
+ protected readonly account = form(
216
+ this.model,
217
+ (p) => {
218
+ required(p.username);
219
+ pattern(p.username, /^[a-z0-9]+$/i, { message: 'Use letters and numbers only, no spaces' });
220
+ },
221
+ {
222
+ submission: {
223
+ action: () => Promise.resolve(undefined),
224
+ onInvalid: (f) => f().errorSummary()[0]?.fieldTree().focusBoundControl(),
225
+ },
226
+ },
227
+ );
228
+ }
229
+ ```
230
+
231
+ ## Theming
232
+
233
+ - Label: `text-muted`, switching to `primary-text` (primary as text, ≥ 4.5:1 on `bg` and `surface`) while the control inside is focused (`group-focus-within/bf-field`) and to `text-disabled` when `disabled`.
234
+ - Required marker: `danger`. Error text: `danger`. Help text: `text-muted`.
235
+
236
+ Label, control, help text and error text sit 6 px apart, the same label-to-message spacing as `bf-checkbox`, `bf-radio-group` and `bf-toggle`.
237
+
238
+ ## Accessibility
239
+
240
+ The field renders the label as a native `<label for>` pointing at `controlId`, and gives the help and error paragraphs stable ids (`<controlId>-help` / `<controlId>-error`); both render together, the error under the help text. Label, help and error text break long words anywhere (`overflow-wrap: anywhere`), so a German or Finnish compound never runs past the field at 320 px or 400 % zoom. It sets no ARIA on the control itself — bind the projected control's `aria-describedby` to `describedBy()` (and its own `aria-invalid`, plus `aria-errormessage` pointing at `<controlId>-error` while it is invalid, as the library's controls do) so the wiring actually reaches assistive tech.
241
+
242
+ ## Inputs
243
+
244
+ | Name | Type | Default | Description |
245
+ | ----------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
246
+ | `label` | `string` | `''` | Visible label; rendered as a `<label for>` pointing at `controlId`. |
247
+ | `helpText` | `string` | `''` | Helper text under the control; stays while `error` is shown. |
248
+ | `error` | `string` | `''` | Error text under the help text. One that appears while a pointer button is held down (the press on a Save button that blurred the field) renders visually hidden until the press ends, so assistive tech has it at once and the layout never moves under the pointer between mousedown and mouseup. |
249
+ | `required` | `boolean` | `false` | Adds the required marker to the label. |
250
+ | `disabled` | `boolean` | `false` | Dims the label; the control carries its own disabled styling. |
251
+ | `controlId` | `string` | required | The id of the control inside the default slot. |
252
+
253
+ ## Slots
254
+
255
+ - `default`: The control the field labels, such as a native input styled with `bfFieldControlClass`.
256
+
257
+ ## Notes
258
+
259
+ ### Do
260
+
261
+ - Always give a control a visible label; use `placeholder` only as an example value.
262
+ - Keep help text to one short sentence.
263
+
264
+ ### Don't
265
+
266
+ - Don't nest one `bf-field` inside another.
267
+ - Don't use it for buttons or read-only text.
268
+
269
+ ## Migration notes
270
+
271
+ ### 0.3.0
272
+
273
+ **Behaviour:** the label of a focused field is `primary-text` (was `primary`).
274
+
275
+ New export `bfFieldControlClass(state: { size: BfSize; invalid: boolean; disabled: boolean;
276
+ readonly: boolean }): string` (and its `BfFieldControlState` type) from `@bitforgehq/ui`: the class
277
+ list `bf-input` renders on its native `<input>` in the same state (box, height and type per size,
278
+ padding, focus ring, `border-strong` or, while `invalid`, `danger-border`), so a custom control
279
+ inside `bf-field` looks like the library's own fields without copying its classes. The disabled
280
+ and read-only styles come from the control's native `disabled`/`readonly` attributes. `bf-input`
281
+ builds its own list from the same pieces, and a spec asserts the two agree for every size and
282
+ state. The field docs and the `@example` bind the control's `aria-describedby` to the field's
283
+ `describedBy()` through a template reference (`#field`); the new `field/custom-control` example
284
+ does so for a native input styled by `bfFieldControlClass()`.
285
+
286
+ JSDoc of `field-classes.ts` for the API reference: `@summary`/`@since` on the exported symbols;
287
+ `@param` docs. No behaviour change.
288
+
289
+ JSDoc of `field.component.ts` for the API reference: `@slot` docs for `default`. No behaviour
290
+ change.
291
+
292
+ **Behaviour:** the help paragraph renders always (when set) and the error paragraph under it;
293
+ help text no longer disappears on error (F-020). `describedBy()` lists both ids,
294
+ `"<controlId>-help <controlId>-error"`, `"<controlId>-help"`, `"<controlId>-error"` or `null`. A new
295
+ `error` that arrives while a pointer button is held down anywhere in the page shows one task after
296
+ the release (or at once on a key press, or a pointer move with no button down): the press on a
297
+ Save button blurs the field and marks it touched on mousedown, and the message pushed Save about
298
+ 22 px down before the mouseup, so the click was lost in every browser. The field holds its layout
299
+ through the press instead of reserving an empty line under every field. See Migration 25.
300
+
301
+ `fieldDescribedBy(id, errorText, helpText)` (internal, `ɵfieldDescribedBy`) returns
302
+ `"<id>-help <id>-error"`, `"<id>-help"`, `"<id>-error"` or `null`; the new internal
303
+ `ɵfieldErrorMessageId` gives the controls' `aria-errormessage`.
304
+
305
+ While a press holds a new error back, its paragraph is already rendered, visually hidden
306
+ (`sr-only`), so the controls' `aria-describedby` and `aria-errormessage` always point at an element
307
+ that exists and assistive tech reads the error when focus returns to the field; the same paragraph
308
+ turns visible on release. Only the primary mouse button, a touch or a pen starts a hold (a
309
+ right-click never does). The self-rendering controls (checkbox, toggle, radio group, segmented)
310
+ behave the same way.
311
+
312
+ ### 0.2.0
313
+
314
+ Fixed: the label, help and error text break long words anywhere (`wrap-anywhere`), so a German or
315
+ Finnish compound no longer runs past the field at 320 px.
316
+
317
+ ### 0.1.0
318
+
319
+ `disabled` no longer dims the whole field; it dims only the label. Controls already carry their own disabled styling.