@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,455 @@
1
+ # bf-input
2
+
3
+ Single-line text field with label, help and error text, icon slots and Signal Forms binding
4
+
5
+ `BfInputComponent` from `@bitforgehq/ui` · Forms · since 0.1.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { BfInputComponent } from '@bitforgehq/ui';
12
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
13
+ import { Search } from 'lucide';
14
+
15
+ /**
16
+ * The bf-input overview.
17
+ *
18
+ * Live demo of `bf-input`: a bound default field with an echo, an icon-start search field, an error state and a readonly field; then the sm, md and lg sizes in a row.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-input-overview',
22
+ imports: [BfInputComponent, BfIconComponent],
23
+ template: `
24
+ <div class="flex flex-col gap-3">
25
+ <bf-input label="Project name" placeholder="Atlas rebrand" [(value)]="projectName" />
26
+ <p data-status aria-live="polite" class="text-caption text-text-muted">
27
+ {{ projectName() ? 'Project: ' + projectName() : 'Type a project name.' }}
28
+ </p>
29
+ <bf-input label="Search" type="search" [iconStart]="true">
30
+ <bf-icon bfSlot="icon-start" [icon]="search" />
31
+ </bf-input>
32
+ <bf-input label="Email" errorMessage="Enter a valid email address" value="lena@studio" />
33
+ <bf-input label="Owner" value="Lena Moser" [readonly]="true" />
34
+ <div data-sizes class="flex flex-wrap items-center gap-3">
35
+ <bf-input size="sm" ariaLabel="Small field" placeholder="sm · 32 px" class="w-40" />
36
+ <bf-input ariaLabel="Medium field" placeholder="md · 40 px" class="w-40" />
37
+ <bf-input size="lg" ariaLabel="Large field" placeholder="lg · 48 px" class="w-40" />
38
+ </div>
39
+ </div>
40
+ `,
41
+ })
42
+ export class InputOverviewExampleComponent {
43
+ readonly search = Search;
44
+ readonly projectName = signal('');
45
+ }
46
+ ```
47
+
48
+ ## Overview
49
+
50
+ Single-line text field with label, help and error text, icon slots and Signal Forms binding.
51
+ Bind `[(value)]` directly or `[formField]` from a signal form; the field state (touched,
52
+ invalid, errors, disabled, readonly, required) flows in through the matching inputs.
53
+
54
+ The text field for one line of input: email, password, search, numbers. It renders its own label, helper and error text through `bf-field`, so a form is a list of `bf-input`s with no extra markup.
55
+
56
+ ## When to use
57
+
58
+ Use `bf-input` for one line of text: a name, an email, a password, a search term, a date. Use [bf-textarea](https://bitforge-ui.web.app/components/textarea) instead for text that runs over several lines, [bf-number-input](https://bitforge-ui.web.app/components/number-input) for a value bound as a `number`, and [bf-select](https://bitforge-ui.web.app/components/select) when the answer is one of a known list.
59
+
60
+ ## Basic usage
61
+
62
+ ```ts
63
+ import { Component, signal } from '@angular/core';
64
+ import { BfInputComponent } from '@bitforgehq/ui';
65
+
66
+ /** A labelled text field bound to a signal. */
67
+ @Component({
68
+ selector: 'app-example-input-basic',
69
+ imports: [BfInputComponent],
70
+ template: `
71
+ <div class="flex flex-col gap-2">
72
+ <bf-input label="Project name" placeholder="Atlas rebrand" [(value)]="name" />
73
+ <p class="text-caption text-text-muted">Name: {{ name() || '(empty)' }}</p>
74
+ </div>
75
+ `,
76
+ })
77
+ export class InputBasicExampleComponent {
78
+ protected readonly name = signal('');
79
+ }
80
+ ```
81
+
82
+ `(changed)` emits the new text right after each user edit (a keystroke or a paste), the moment
83
+ `value` changes; a programmatic write to `value` does not emit.
84
+
85
+ ## With Signal Forms
86
+
87
+ Bind the field instead of the value. Touched, invalid, errors, disabled, readonly and required flow in automatically; the first error shows once the user leaves the field. A validator's `message` becomes the text; one without a message shows its kind's default text (see Validation). `[formRoot]` turns off the browser's validation bubbles and runs the form's `submission` on submit. The [Forms and validation guide](https://bitforge-ui.web.app/forms) has a complete, compiled example.
88
+
89
+ ```ts
90
+ import { Component, signal } from '@angular/core';
91
+ import { FormField, FormRoot, email, form, required } from '@angular/forms/signals';
92
+ import { BfButtonComponent, BfInputComponent } from '@bitforgehq/ui';
93
+
94
+ /**
95
+ * An email field on Signal Forms.
96
+ *
97
+ * `[formField]` carries touched, invalid, errors, disabled, readonly and required; the
98
+ * validators' messages become the error text. `[formRoot]` runs the submission on submit.
99
+ */
100
+ @Component({
101
+ selector: 'app-example-input-signal-forms',
102
+ imports: [FormField, FormRoot, BfButtonComponent, BfInputComponent],
103
+ template: `
104
+ <form [formRoot]="signup" class="flex flex-col items-start gap-3">
105
+ <bf-input label="Email" type="email" class="self-stretch" [formField]="signup.email" />
106
+ <bf-button type="submit">Sign up</bf-button>
107
+ </form>
108
+ `,
109
+ })
110
+ export class InputSignalFormsExampleComponent {
111
+ private readonly model = signal({ email: '' });
112
+ protected readonly signup = form(
113
+ this.model,
114
+ (p) => {
115
+ required(p.email, { message: 'Enter your email' });
116
+ email(p.email, { message: 'Enter a valid email address' });
117
+ },
118
+ { submission: { action: () => Promise.resolve(undefined) } },
119
+ );
120
+ }
121
+ ```
122
+
123
+ ## Icons
124
+
125
+ Set `iconStart` / `iconEnd` to reserve the space, then project the icon into the slot.
126
+
127
+ ```ts
128
+ import { Component, signal } from '@angular/core';
129
+ import { BfInputComponent } from '@bitforgehq/ui';
130
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
131
+ import { Search } from 'lucide';
132
+
133
+ /** A search field with an icon in its icon-start slot. */
134
+ @Component({
135
+ selector: 'app-example-input-icons',
136
+ imports: [BfInputComponent, BfIconComponent],
137
+ template: `
138
+ <bf-input label="Search" type="search" [iconStart]="true" [(value)]="query">
139
+ <bf-icon bfSlot="icon-start" [icon]="search" />
140
+ </bf-input>
141
+ `,
142
+ })
143
+ export class InputIconsExampleComponent {
144
+ protected readonly search = Search;
145
+ protected readonly query = signal('');
146
+ }
147
+ ```
148
+
149
+ ## Search fields and other unlabelled inputs
150
+
151
+ Skip the visible `label` when the field itself makes its purpose clear (a toolbar search box) and
152
+ give it `ariaLabel` instead so assistive tech still gets a name; help and error wiring keep working
153
+ the same way. When a visible heading already names the field, point `ariaLabelledby` at its id.
154
+ A field without a visible label needs one of the two.
155
+
156
+ ```ts
157
+ import { Component, signal } from '@angular/core';
158
+ import { BfInputComponent } from '@bitforgehq/ui';
159
+
160
+ /**
161
+ * Fields without a visible label.
162
+ *
163
+ * A toolbar search named by `ariaLabel`, and a budget field named by the heading it points at.
164
+ */
165
+ @Component({
166
+ selector: 'app-example-input-unlabelled',
167
+ imports: [BfInputComponent],
168
+ template: `
169
+ <div class="flex flex-col gap-3">
170
+ <bf-input type="search" ariaLabel="Search projects" [(value)]="query" />
171
+ <h3 id="input-budget-heading" class="text-heading">Budget</h3>
172
+ <bf-input ariaLabelledby="input-budget-heading" [(value)]="budget" />
173
+ </div>
174
+ `,
175
+ })
176
+ export class InputUnlabelledExampleComponent {
177
+ protected readonly query = signal('');
178
+ protected readonly budget = signal('');
179
+ }
180
+ ```
181
+
182
+ ## Native constraints and keyboards
183
+
184
+ `maxLength`, `minLength`, `min`, `max`, `step` and `pattern` reach the native `<input>`, so the
185
+ browser enforces length limits and date pickers respect the range. Signal Forms binds them from
186
+ its rules (`maxLength(p.code, 20)` renders `maxlength="20"`; exactly one `pattern()` renders the
187
+ native `pattern` when its flags are empty, `u` or `v`; a pattern with other flags such as `i`, or
188
+ several patterns, is left to the form, because HTML compiles the attribute without them).
189
+ `inputMode`, `enterKeyHint`, `spellcheck` and `autocapitalize` pick the right
190
+ phone keyboard (`BfInputInputMode`, `BfInputEnterKeyHint` and `BfInputAutocapitalize`: a
191
+ `BfInputMode`, `BfEnterKeyHint` or `BfAutocapitalize` keyword, or `''`, the default, which leaves
192
+ the attribute off). The `date`, `time`, `datetime-local` and `month` types open the platform's own
193
+ picker.
194
+
195
+ ```ts
196
+ import { Component, signal } from '@angular/core';
197
+ import { BfInputComponent } from '@bitforgehq/ui';
198
+
199
+ /**
200
+ * Native constraints and keyboard hints.
201
+ *
202
+ * A date range, a six-digit code with the numeric keyboard, and a city field that capitalises
203
+ * words and skips spell checking.
204
+ */
205
+ @Component({
206
+ selector: 'app-example-input-native',
207
+ imports: [BfInputComponent],
208
+ template: `
209
+ <div class="flex flex-col gap-3">
210
+ <bf-input label="Start" type="date" min="2026-01-01" max="2026-12-31" [(value)]="start" />
211
+ <bf-input
212
+ label="Code"
213
+ [maxLength]="6"
214
+ inputMode="numeric"
215
+ enterKeyHint="next"
216
+ [(value)]="code"
217
+ />
218
+ <bf-input label="City" autocapitalize="words" [spellcheck]="false" [(value)]="city" />
219
+ </div>
220
+ `,
221
+ })
222
+ export class InputNativeExampleComponent {
223
+ protected readonly start = signal('');
224
+ protected readonly code = signal('');
225
+ protected readonly city = signal('');
226
+ }
227
+ ```
228
+
229
+ ## Sizes and states
230
+
231
+ One `BfSize` scale for every field and control: `sm` 32 px, `md` 40 px (default), `lg` 48 px, so an input lines up with the buttons, selects, chips and segmented controls of the same step. Unset, `size` follows `provideBfDefaults()`'s `fieldSize`, `md` by default; a Capacitor app sets `provideBfDefaults({ fieldSize: 'lg' })` once for the 48 px phone look, and a dense sub-tree (a filter bar, a table) can set `{ size: 'sm' }` in a component's `providers`. Fields get no touch extension: `sm`/`md` fields stay 32/40 px under a coarse pointer, so touch apps use `provideBfDefaults({ fieldSize: 'lg' })`. Icon insets and padding use logical properties, so `icon-start` sits at the inline start under `dir="rtl"` too. `disabled`, `readonly` and `errorMessage` render the design's states.
232
+
233
+ ```ts
234
+ import { Component, signal } from '@angular/core';
235
+ import { BfInputComponent } from '@bitforgehq/ui';
236
+
237
+ /**
238
+ * Field sizes and states.
239
+ *
240
+ * The sm, md and lg steps, a read-only field and one with an explicit error message.
241
+ */
242
+ @Component({
243
+ selector: 'app-example-input-sizes',
244
+ imports: [BfInputComponent],
245
+ template: `
246
+ <div class="flex flex-col gap-3">
247
+ <bf-input size="sm" ariaLabel="Filter" [(value)]="filter" />
248
+ <bf-input label="Project name" [(value)]="name" />
249
+ <bf-input size="lg" label="Title" [(value)]="title" />
250
+ <bf-input label="Owner" value="Lena Moser" [readonly]="true" />
251
+ <bf-input label="Email" errorMessage="Enter a valid email address" />
252
+ </div>
253
+ `,
254
+ })
255
+ export class InputSizesExampleComponent {
256
+ protected readonly filter = signal('');
257
+ protected readonly name = signal('Atlas rebrand');
258
+ protected readonly title = signal('');
259
+ }
260
+ ```
261
+
262
+ ## Validation
263
+
264
+ Once the control is touched it shows the first error as text: the error's own `message`, else the default for its kind from `provideBfLabels()`'s `validation` labels (`required` "Fill in this field", `email`, `pattern`, `parse`, `min(n)`, `max(n)`, `minLength(n)` "Use at least n characters", `maxLength(n)`, `minDate(d)`, `maxDate(d)`, and `fallback` "Check this value" for any other kind), so it never turns red without text. Reactive Forms `Validators.*` errors carry no message and get the same defaults (`minlength`/`maxlength` read their limit from the error's `context`). An explicit `errorMessage` wins over both and shows at once, e.g. for a server error.
265
+
266
+ ```ts
267
+ import { Component, signal } from '@angular/core';
268
+ import { FormField, FormRoot, email, form, required } from '@angular/forms/signals';
269
+ import { BfButtonComponent, BfInputComponent } from '@bitforgehq/ui';
270
+
271
+ /**
272
+ * Default and explicit error text.
273
+ *
274
+ * The first field's validators carry no message, so a submit shows the defaults; the second
275
+ * field's `errorMessage` (a server error) shows at once.
276
+ */
277
+ @Component({
278
+ selector: 'app-example-input-validation',
279
+ imports: [FormField, FormRoot, BfButtonComponent, BfInputComponent],
280
+ template: `
281
+ <form [formRoot]="signup" class="flex flex-col items-start gap-3">
282
+ <bf-input label="Email" type="email" class="self-stretch" [formField]="signup.email" />
283
+ <bf-input
284
+ label="Backup email"
285
+ class="self-stretch"
286
+ errorMessage="This address is already registered"
287
+ />
288
+ <bf-button type="submit">Sign up</bf-button>
289
+ </form>
290
+ `,
291
+ })
292
+ export class InputValidationExampleComponent {
293
+ private readonly model = signal({ email: '' });
294
+ protected readonly signup = form(
295
+ this.model,
296
+ (p) => {
297
+ required(p.email);
298
+ email(p.email);
299
+ },
300
+ { submission: { action: () => Promise.resolve(undefined) } },
301
+ );
302
+ }
303
+ ```
304
+
305
+ ## Desktop
306
+
307
+ The field has no hover state; the text cursor shows over it. The `primary` border and the focus ring show on every focus, by mouse or keyboard, because a focused field takes typing. Fields default to `md` (40 px); `provideBfDefaults({ fieldSize: 'sm' })`, or `{ size: 'sm' }` in a component's `providers`, gives 32 px fields for a dense filter bar or table ([density guide](https://bitforge-ui.web.app/density)).
308
+
309
+ ## Theming
310
+
311
+ - Border: `border-strong` (the control boundary, ≥ 3:1), switching to `danger-border` on error; focus: `primary` border with a `ring` ring.
312
+ - Disabled: `surface-sunken` border, `surface-muted` background, `text-disabled` text. Read-only: `surface-muted` background. Placeholder: `text-muted` (≥ 4.5:1). Icon slots: `text-subtle`.
313
+ - Label, help and error text come from the `bf-field` wrapper (see its Theming section).
314
+
315
+ ## Accessibility
316
+
317
+ Native `<input>`, with `type` reflecting the `type` input so mobile keyboards match. `bf-field` renders the `<label for>`; the input itself wires `aria-describedby` to the help paragraph and the error paragraph under it (both while shown, help first) and, while it is invalid, `aria-errormessage` to the error paragraph; `aria-invalid` is set once the field is touched and invalid (an explicit `errorMessage` shows immediately). `required` reflects the native attribute as well as the label's required marker. `aria-label` and `aria-labelledby` carry `ariaLabel` and `ariaLabelledby` only while there is no visible `label`, so a `label` and an `ariaLabel` given together never fight over the accessible name — the visible label always wins and the other two are ignored. A field without a visible label needs `ariaLabel` or `ariaLabelledby`. `readonly` uses the native attribute: the value can be selected and copied, the input stays in the tab order, and assistive tech announces it as read-only. `focus()` focuses the native input; Signal Forms calls it from `focusBoundControl()`, so "focus the first invalid field" works.
318
+
319
+ ### Keyboard
320
+
321
+ The input is a native `<input>`: typing, selection and the date and time pickers are the browser's own.
322
+
323
+ | Key | Action |
324
+ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
325
+ | <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves focus to and from the field; a `readonly` field stays in the tab order, a `disabled` one is skipped |
326
+ | <kbd>Enter</kbd> | Inside a `<form>` with a submit button, submits the form (the browser's implicit submission) |
327
+
328
+ ## Inputs
329
+
330
+ | Name | Type | Default | Description |
331
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
332
+ | `type` | `BfInputType` (`'text' \| 'email' \| 'password' \| 'number' \| 'tel' \| 'url' \| 'search' \| 'date' \| 'time' \| 'datetime-local' \| 'month'`) | `'text'` | Native input type; the value stays a string. For numbers use `bf-number-input`, which binds `number \| null`. |
333
+ | `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`fieldSize`, `md` by default). |
334
+ | `label` | `string` | `''` | Visible label above the control. |
335
+ | `ariaLabel` | `string` | `''` | Accessible name when no visible label is rendered (e.g. a search field). Ignored when `label` is set. |
336
+ | `ariaLabelledby` | `string` | `''` | Id(s) of the element(s) that name the field when no visible label is rendered. Ignored when `label` is set. |
337
+ | `placeholder` | `string` | `''` | Example value shown while empty. |
338
+ | `helpText` | `string` | `''` | Helper text under the control. |
339
+ | `errorMessage` | `string` | `''` | Explicit error text; takes precedence over Signal Forms errors. |
340
+ | `disabled` | `boolean` | `false` | Disables the control. Set by `[formField]`; do not bind it yourself. |
341
+ | `readonly` | `boolean` | `false` | Makes the control read-only. Set by `[formField]`; do not bind it yourself. |
342
+ | `required` | `boolean` | `false` | Marks the control required (label marker and native attribute). Set by `[formField]`; do not bind it yourself. |
343
+ | `invalid` | `boolean` | `false` | Field validity from Signal Forms. Set by `[formField]`; do not bind it yourself. |
344
+ | `touched` | `boolean` | `false` | Whether the user has left the field; errors show only once touched. Set by `[formField]`; do not bind it yourself. |
345
+ | `errors` | `readonly ValidationError.WithOptionalFieldTree[]` | `[]` | Validation errors from Signal Forms; the first message is shown once touched. Set by `[formField]`; do not bind it yourself. |
346
+ | `name` | `string` | `''` | Native name attribute. Set by `[formField]`; do not bind it yourself. |
347
+ | `id` | `string` | `generated` | Control id; generated when not set. |
348
+ | `iconStart` | `boolean` | `false` | Reserve space for an icon in the `icon-start` slot. |
349
+ | `iconEnd` | `boolean` | `false` | Reserve space for an icon in the `icon-end` slot. |
350
+ | `autocomplete` | `string` | `''` | Native autocomplete hint. |
351
+ | `maxLength` | `number \| undefined` | `undefined` | Maximum number of characters (native `maxlength`); bound by Signal Forms `maxLength()`. Set by `[formField]`; do not bind it yourself. |
352
+ | `minLength` | `number \| undefined` | `undefined` | Minimum number of characters (native `minlength`); bound by Signal Forms `minLength()`. Set by `[formField]`; do not bind it yourself. |
353
+ | `min` | `string \| undefined` | `undefined` | Lowest value (native `min`), e.g. `2026-01-01` for a date or `0` for a number. Set by `[formField]`; do not bind it yourself. |
354
+ | `max` | `string \| undefined` | `undefined` | Highest value (native `max`), e.g. `2026-12-31` for a date or `100` for a number. Set by `[formField]`; do not bind it yourself. |
355
+ | `step` | `string \| number \| undefined` | `undefined` | Step between valid values (native `step`), e.g. `0.5` or `any`. |
356
+ | `pattern` | `readonly RegExp[]` | `[]` | Patterns from Signal Forms `pattern()`; exactly one, with no flags or only `u`/`v`, is rendered as the native `pattern`. Set by `[formField]`; do not bind it yourself. |
357
+ | `inputMode` | `BfInputInputMode` (`'none' \| 'text' \| 'decimal' \| 'numeric' \| 'tel' \| 'search' \| 'email' \| 'url' \| ''`) | `''` | Virtual keyboard to show (native `inputmode`). |
358
+ | `enterKeyHint` | `BfInputEnterKeyHint` (`'enter' \| 'done' \| 'go' \| 'next' \| 'previous' \| 'search' \| 'send' \| ''`) | `''` | Label of the virtual keyboard's Enter key (native `enterkeyhint`). |
359
+ | `spellcheck` | `boolean \| undefined` | `undefined` | Spell checking on or off (native `spellcheck`); the browser decides while unset. |
360
+ | `autocapitalize` | `BfInputAutocapitalize` (`'off' \| 'none' \| 'on' \| 'sentences' \| 'words' \| 'characters' \| ''`) | `''` | Capitalisation of typed text on virtual keyboards (native `autocapitalize`). |
361
+ | `value` | `string` | `''` | The text value; two-way bindable and bound by `[formField]`. Two-way: `[(value)]`. |
362
+
363
+ ## Outputs
364
+
365
+ | Name | Payload | Description |
366
+ | --------- | ------------ | ------------------------------------------------------------------------------------------------------- |
367
+ | `changed` | `string` | Emitted with the new text right after the user changes it (each keystroke or paste). |
368
+ | `focused` | `FocusEvent` | Emitted when the control gains focus. |
369
+ | `blurred` | `FocusEvent` | Emitted when the control loses focus. |
370
+ | `touch` | `void` | Emitted on blur so Signal Forms marks the field touched. Set by `[formField]`; do not bind it yourself. |
371
+
372
+ ## Slots
373
+
374
+ - `icon-start`: An icon inside the field, before the text.
375
+ - `icon-end`: An icon inside the field, after the text.
376
+
377
+ ## Methods
378
+
379
+ ### `focus(options?: FocusOptions): void`
380
+
381
+ Focuses the native input; Signal Forms calls it from `focusBoundControl()`.
382
+
383
+ | Parameter | Type | Description |
384
+ | ---------- | -------------- | --------------------------------------------------------------------- |
385
+ | `options?` | `FocusOptions` | Passed on to the native `focus()`, such as `{ preventScroll: true }`. |
386
+
387
+ ## Notes
388
+
389
+ ### Do
390
+
391
+ - Always set `label`, or `ariaLabel` / `ariaLabelledby` when the field shows no visible label (e.g. a search box).
392
+ - Use the right `type` so mobile keyboards match (email, tel, url); for a number bound as a `number`, use `bf-number-input`.
393
+ - Let Signal Forms drive validation; use `errorMessage` only for server-side errors.
394
+
395
+ ### Don't
396
+
397
+ - Don't use `placeholder` as the label.
398
+ - Don't bind both `[(value)]` and `[formField]`.
399
+
400
+ ## Migration notes
401
+
402
+ ### 0.3.0
403
+
404
+ New `changed` output: the new text right after each user edit (not for a programmatic write), so
405
+ every value control has one `changed`. New exported `BfInputInputMode` (`BfInputMode | ''`),
406
+ `BfInputEnterKeyHint` (`BfEnterKeyHint | ''`) and `BfInputAutocapitalize`
407
+ (`BfAutocapitalize | ''`), the types of `inputMode`, `enterKeyHint` and `autocapitalize` (were
408
+ the same unions written inline; `''`, the default, leaves the attribute off). `BfInputMode`,
409
+ `BfEnterKeyHint` and `BfAutocapitalize` keep their 0.2.0 members (their `@since` now says 0.2.0).
410
+
411
+ **Breaking:** the default `size` is `md` (40 px) instead of `lg` (48 px); `size` is a `BfSize`
412
+ (`sm` 32, `md` 40, `lg` 48 px — `sm` is new) and, unset, follows `provideBfDefaults()`'s
413
+ `fieldSize`. See Migration 2. Icon insets and padding are logical (`start-*`/`end-*`, `ps-*`/`pe-*`),
414
+ so `icon-start` and `icon-end` sit at the inline start and end under `dir="rtl"`.
415
+
416
+ **Behaviour:** an error without a message shows its kind's default text (was a red border only):
417
+ once touched, the field shows the first error's own `message`, else `provideBfLabels()`'s
418
+ `validation` text for its kind (Signal Forms and Reactive interop kinds), else
419
+ `validation.fallback`. `errorMessage` still wins. See Migration 1.
420
+
421
+ JSDoc of `input.component.ts` for the API reference: `@summary`/`@since` on the exported symbols;
422
+ `@slot` docs for `icon-start`, `icon-end`; `@param` docs; template handlers and framework hooks
423
+ marked `@internal`, so the reference leaves them out. No behaviour change.
424
+
425
+ **Behaviour:** the help text stays while an error shows, with the error under it (F-020):
426
+ `aria-describedby` lists both (`"<id>-help <id>-error"`), and `aria-errormessage` points at the
427
+ error paragraph while `aria-invalid="true"`. See Migration 25.
428
+
429
+ ### 0.2.0
430
+
431
+ The rest border is `border-strong` (was `border`, 1.32:1) and the placeholder `text-muted` (was
432
+ `text-subtle`, 2.73:1); the icon slots stay `text-subtle`.
433
+
434
+ Changed: the border narrows to 1 px (was 1.5 px); contrast stays ≥ 3:1.
435
+
436
+ Changed: the focus ring narrows to 2 px (was 4 px); it still shows on mouse focus, not just
437
+ keyboard focus.
438
+
439
+ Added: `ariaLabel` input (mirrors `bf-toggle`) sets `aria-label` on the native input for a field
440
+ with no visible `label` (e.g. a search box); ignored while `label` is set, so the visible label
441
+ stays the sole accessible name.
442
+
443
+ Added: `maxLength`, `minLength`, `min`, `max`, `step`, `pattern` (exactly one pattern with no flags,
444
+ `u` or `v` renders the native attribute; other flags such as `i` cannot be expressed in HTML, so the
445
+ form alone validates them), `inputMode`, `enterKeyHint`, `spellcheck` and `autocapitalize` inputs,
446
+ each rendered on the native `<input>` only when set; Signal Forms binds the constraint rules to them
447
+ (`maxLength(p.code, 20)` renders `maxlength="20"`). New exported types `BfInputMode`,
448
+ `BfEnterKeyHint` and `BfAutocapitalize`. `BfInputType` adds `date`, `time`, `datetime-local` and
449
+ `month`, which open the native picker. `ariaLabelledby` sets `aria-labelledby` while there is no
450
+ visible `label`. `focus(options?)` focuses the native control, so Signal Forms'
451
+ `focusBoundControl()` and "focus the first invalid field" on submit now work (they were no-ops).
452
+
453
+ ### 0.1.0
454
+
455
+ Field sizes renamed: `sm`/`md` (40/48 px) is now `md`/`lg` (40/48 px), default stays 48 px but the default token changed to `lg`. The host no longer reflects a static `id` attribute onto `<bf-input>`; the id lands only on the native `<input>`.