@egose/shadcn-theme-ng 0.1.0 → 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 (162) hide show
  1. package/README.md +2 -2
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  8. package/avatar/README.md +357 -5
  9. package/badge/README.md +318 -2
  10. package/basic-alert/README.md +353 -2
  11. package/breadcrumb/README.md +406 -5
  12. package/button/README.md +482 -2
  13. package/button/fesm2022/button.mjs +85 -107
  14. package/button/types/button.d.ts +5 -8
  15. package/button-group/README.md +318 -5
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/checkbox/fesm2022/checkbox.mjs +6 -7
  22. package/checkbox/types/checkbox.d.ts +1 -1
  23. package/collapsible/README.md +332 -5
  24. package/combobox/README.md +507 -5
  25. package/combobox/fesm2022/combobox.mjs +5 -2
  26. package/command/README.md +435 -5
  27. package/confirmation-dialog/README.md +301 -2
  28. package/context-menu/README.md +366 -5
  29. package/date-picker/README.md +469 -2
  30. package/date-picker/fesm2022/date-picker.mjs +150 -32
  31. package/date-picker/types/date-picker.d.ts +102 -9
  32. package/dialog/README.md +448 -2
  33. package/drawer/README.md +395 -5
  34. package/dropdown-menu/README.md +417 -5
  35. package/empty/README.md +329 -5
  36. package/field/README.md +385 -5
  37. package/form-autocomplete/README.md +177 -0
  38. package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
  39. package/form-autocomplete/package.json +24 -0
  40. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  41. package/form-checkbox/README.md +322 -2
  42. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  43. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  44. package/form-combobox/README.md +202 -0
  45. package/form-combobox/fesm2022/form-combobox.mjs +147 -0
  46. package/form-combobox/package.json +24 -0
  47. package/form-combobox/types/form-combobox.d.ts +73 -0
  48. package/form-date-picker/README.md +348 -2
  49. package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
  50. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  51. package/form-date-picker-multi/README.md +191 -0
  52. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
  53. package/form-date-picker-multi/package.json +24 -0
  54. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  55. package/form-date-range-picker/README.md +253 -0
  56. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
  57. package/form-date-range-picker/package.json +24 -0
  58. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  59. package/form-field/README.md +356 -2
  60. package/form-field-simple/README.md +340 -2
  61. package/form-input-otp/README.md +194 -0
  62. package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
  63. package/form-input-otp/package.json +24 -0
  64. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  65. package/form-month-year-picker/README.md +188 -0
  66. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
  67. package/form-month-year-picker/package.json +24 -0
  68. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  69. package/form-native-select/README.md +205 -0
  70. package/form-native-select/fesm2022/form-native-select.mjs +103 -0
  71. package/form-native-select/package.json +24 -0
  72. package/form-native-select/types/form-native-select.d.ts +57 -0
  73. package/form-phone-input/README.md +188 -0
  74. package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
  75. package/form-phone-input/package.json +24 -0
  76. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  77. package/form-radio-group/README.md +198 -0
  78. package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
  79. package/form-radio-group/package.json +24 -0
  80. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  81. package/form-searchable-multiselect/README.md +371 -2
  82. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  83. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  84. package/form-select/README.md +360 -2
  85. package/form-select/fesm2022/form-select.mjs +20 -11
  86. package/form-select/types/form-select.d.ts +18 -1
  87. package/form-slider/README.md +182 -0
  88. package/form-slider/fesm2022/form-slider.mjs +106 -0
  89. package/form-slider/package.json +24 -0
  90. package/form-slider/types/form-slider.d.ts +60 -0
  91. package/form-switch/README.md +173 -0
  92. package/form-switch/fesm2022/form-switch.mjs +100 -0
  93. package/form-switch/package.json +24 -0
  94. package/form-switch/types/form-switch.d.ts +50 -0
  95. package/form-text-input/README.md +381 -2
  96. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  97. package/form-text-input/types/form-text-input.d.ts +18 -1
  98. package/form-textarea/README.md +357 -2
  99. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  100. package/form-textarea/types/form-textarea.d.ts +18 -1
  101. package/form-toggle/README.md +186 -0
  102. package/form-toggle/fesm2022/form-toggle.mjs +159 -0
  103. package/form-toggle/package.json +24 -0
  104. package/form-toggle/types/form-toggle.d.ts +82 -0
  105. package/form-toggle-group/README.md +176 -0
  106. package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
  107. package/form-toggle-group/package.json +24 -0
  108. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  109. package/hover-card/README.md +256 -5
  110. package/icon/README.md +239 -2
  111. package/input/README.md +269 -2
  112. package/input-group/README.md +335 -5
  113. package/input-group/fesm2022/input-group.mjs +22 -12
  114. package/input-group/types/input-group.d.ts +4 -1
  115. package/input-otp/README.md +375 -5
  116. package/item/README.md +385 -5
  117. package/item/fesm2022/item.mjs +3 -3
  118. package/kbd/README.md +291 -5
  119. package/label/README.md +272 -2
  120. package/layout-simple/README.md +193 -2
  121. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  122. package/layout-simple/types/layout-simple.d.ts +174 -137
  123. package/menu/README.md +417 -2
  124. package/menubar/README.md +343 -5
  125. package/native-select/README.md +323 -5
  126. package/native-select/fesm2022/native-select.mjs +18 -6
  127. package/native-select/types/native-select.d.ts +7 -2
  128. package/navigation-menu/README.md +369 -5
  129. package/package.json +57 -1
  130. package/pagination/README.md +388 -5
  131. package/phone-input/README.md +114 -0
  132. package/phone-input/fesm2022/phone-input.mjs +191 -0
  133. package/phone-input/package.json +24 -0
  134. package/phone-input/types/phone-input.d.ts +67 -0
  135. package/popover/README.md +331 -2
  136. package/progress/README.md +311 -5
  137. package/radio-group/README.md +364 -2
  138. package/radio-group/fesm2022/radio-group.mjs +5 -1
  139. package/resizable/README.md +269 -5
  140. package/scroll-area/README.md +233 -5
  141. package/searchable-multiselect/README.md +323 -2
  142. package/select/README.md +437 -2
  143. package/separator/README.md +222 -2
  144. package/sheet/README.md +311 -2
  145. package/sidebar/README.md +457 -5
  146. package/skeleton/README.md +217 -5
  147. package/slider/README.md +273 -5
  148. package/slider/fesm2022/slider.mjs +3 -3
  149. package/sonner/README.md +346 -2
  150. package/spinner/README.md +284 -2
  151. package/switch/README.md +310 -2
  152. package/switch/fesm2022/switch.mjs +7 -5
  153. package/switch/types/switch.d.ts +2 -1
  154. package/table/README.md +423 -5
  155. package/tabs/README.md +411 -2
  156. package/tabs/fesm2022/tabs.mjs +2 -2
  157. package/textarea/README.md +282 -5
  158. package/toggle/README.md +270 -5
  159. package/toggle-group/README.md +340 -5
  160. package/tooltip/README.md +269 -2
  161. package/typography/README.md +271 -5
  162. package/utils/README.md +303 -2
@@ -1,3 +1,357 @@
1
- # Form Field Subpath
1
+ # Form Field (`@egose/shadcn-theme-ng/form-field`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/form-field` or `@egose/shadcn-theme-ng-tw/form-field`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ The classic labelled-field wrapper: `hlm-form-field` groups a brain field control with a label, shows `hlm-hint` by default, and swaps to `hlm-error` automatically when the control reports validation errors. Equivalent to shadcn/ui `FormField` + `FormMessage` composition.
4
+
5
+ The Angular implementation is a small composition layer over
6
+ [`BrnField` from `@spartan-ng/brain/field`](https://www.spartan-ng.com/): `HlmFormField` hosts
7
+ `BrnField`, reads the projected `BrnFieldControl` (`contentChild`) and `HlmError` children
8
+ (`contentChildren`), and projects either `<ng-content select="hlm-error">` or
9
+ `<ng-content select="hlm-hint">`. `HlmError` / `HlmHint` are pure styling directives;
10
+ `HlmFormIdGenerator` mints unique ids. Must live inside a `FormGroupDirective` (`[formGroup]`
11
+ parent) so `formControlName` and `ControlContainer` resolve.
12
+
13
+ > **Ships as:** `@egose/shadcn-theme-ng/form-field` and `@egose/shadcn-theme-ng-tw/form-field`
14
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
15
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
16
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # Plain Tailwind (no prefix)
22
+ npm install @egose/shadcn-theme-ng
23
+
24
+ # tw:-prefixed Tailwind variant
25
+ npm install @egose/shadcn-theme-ng-tw
26
+ ```
27
+
28
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
29
+ [package README](../../README.md#peer-dependencies). This subpath has no extra runtime component
30
+ dependencies beyond `@egose/shadcn-theme-ng/utils` and `@spartan-ng/brain/field`.
31
+
32
+ ## Imports
33
+
34
+ All symbols are exported from the subpath root (`projects/form-field/src/public-api.ts`):
35
+
36
+ ```ts
37
+ import {
38
+ HlmFormField,
39
+ HlmError,
40
+ HlmHint,
41
+ HlmFormIdGenerator,
42
+ HlmFormFieldImports,
43
+ HlmFormFieldModule,
44
+ } from '@egose/shadcn-theme-ng/form-field';
45
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/form-field'
46
+ ```
47
+
48
+ Standalone component — spread the `*Imports` array:
49
+
50
+ ```ts
51
+ import { Component } from '@angular/core';
52
+ import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
53
+
54
+ @Component({
55
+ selector: 'app-demo',
56
+ standalone: true,
57
+ imports: [...HlmFormFieldImports],
58
+ template: `...`,
59
+ })
60
+ export class DemoComponent {}
61
+ ```
62
+
63
+ NgModule-based consumer — import the module:
64
+
65
+ ```ts
66
+ import { NgModule } from '@angular/core';
67
+ import { HlmFormFieldModule } from '@egose/shadcn-theme-ng/form-field';
68
+
69
+ @NgModule({ imports: [HlmFormFieldModule] })
70
+ export class DemoModule {}
71
+ ```
72
+
73
+ `ControlValueAccessor` behavior: `hlm-form-field` is not itself a form control — the projected
74
+ control (any `BrnFieldControl`: `hlm-input`, `hlm-checkbox`, `hlm-date-picker`, …) carries the
75
+ `formControlName` binding. The wrapper only observes its `errors()` signal to switch hint/error.
76
+
77
+ ## Anatomy / Structure
78
+
79
+ ```html
80
+ <form [formGroup]="form">
81
+ <hlm-form-field>
82
+ <label hlmLabel for="email">Email</label>
83
+ <input hlmInput id="email" formControlName="email" placeholder="you@example.com" />
84
+ <hlm-hint>We'll never share your email.</hlm-hint>
85
+ <hlm-error>Email is required.</hlm-error>
86
+ </hlm-form-field>
87
+ </form>
88
+ ```
89
+
90
+ Real selectors (from source):
91
+
92
+ | Class | Selector | Kind |
93
+ | -------------------- | ------------------------------- | ---------------------------- |
94
+ | `HlmFormField` | `hlm-form-field` | Component (hosts `BrnField`) |
95
+ | `HlmError` | `hlm-error` | Directive |
96
+ | `HlmHint` | `hlm-hint` | Directive |
97
+ | `HlmFormIdGenerator` | (service, `providedIn: 'root'`) | Injectable |
98
+
99
+ ## API reference
100
+
101
+ ### `HlmFormField` — `hlm-form-field`
102
+
103
+ | Input | Type | Default | Description |
104
+ | ------- | ------------ | ------- | -------------------------------------------------- |
105
+ | `class` | `ClassValue` | `''` | Extra classes merged over `tw:block tw:space-y-2`. |
106
+
107
+ | Member | Description |
108
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
109
+ | `control` | `contentChild(BrnFieldControl)` — the projected field control; **throws `hlm-form-field must contain a BrnFieldControl` if absent.** |
110
+ | `errorChildren` | `contentChildren(HlmError)` — drives the hint/error switch. |
111
+ | `_hasDisplayedMessage` | `'error'` when an `hlm-error` child exists and `control.errors()` is non-empty, else `'hint'` (protected, governs projection). |
112
+
113
+ Template projects default content always, then either `hlm-error` (error case) or `hlm-hint`.
114
+
115
+ ### `HlmError` — `hlm-error` / `HlmHint` — `hlm-hint`
116
+
117
+ Style-only directives (`text-destructive` / `text-muted-foreground`, block, `text-sm`).
118
+
119
+ | Input | Type | Default | Description |
120
+ | ------- | ------------ | ------- | -------------- |
121
+ | `class` | `ClassValue` | `''` | Extra classes. |
122
+
123
+ ### `HlmFormIdGenerator`
124
+
125
+ ```ts
126
+ @Injectable({ providedIn: 'root' })
127
+ export class HlmFormIdGenerator {
128
+ generate(prefix: string): string; // `${prefix}-${appId}-${nextId++}`
129
+ }
130
+ ```
131
+
132
+ Inject and call `generate('my-field')` for stable label/control ids (used by the `eg-form-*` wrappers).
133
+
134
+ ## Examples
135
+
136
+ ### 1. Text input with hint → error swap
137
+
138
+ ```ts
139
+ import { Component, inject } from '@angular/core';
140
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
141
+ import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
142
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
143
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
144
+
145
+ @Component({
146
+ selector: 'app-text-field',
147
+ standalone: true,
148
+ imports: [ReactiveFormsModule, HlmLabel, HlmInput, ...HlmFormFieldImports],
149
+ template: `
150
+ <form [formGroup]="form" (ngSubmit)="save()">
151
+ <hlm-form-field>
152
+ <label hlmLabel for="username">Username</label>
153
+ <input hlmInput id="username" formControlName="username" placeholder="ada" />
154
+ <hlm-hint>Letters and numbers only.</hlm-hint>
155
+ <hlm-error>Username is required.</hlm-error>
156
+ </hlm-form-field>
157
+ <button type="submit" [disabled]="form.invalid">Save</button>
158
+ </form>
159
+ `,
160
+ })
161
+ export class TextFieldComponent {
162
+ private readonly fb = inject(FormBuilder);
163
+ readonly form = this.fb.group({ username: ['', Validators.required] });
164
+ save() {
165
+ console.log(this.form.value);
166
+ }
167
+ }
168
+ ```
169
+
170
+ ### 2. Textarea with generated ids
171
+
172
+ ```ts
173
+ import { Component, inject } from '@angular/core';
174
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
175
+ import { HlmFormFieldImports, HlmFormIdGenerator } from '@egose/shadcn-theme-ng/form-field';
176
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
177
+ import { HlmTextarea } from '@egose/shadcn-theme-ng/textarea';
178
+
179
+ @Component({
180
+ selector: 'app-bio-field',
181
+ standalone: true,
182
+ imports: [ReactiveFormsModule, HlmLabel, HlmTextarea, ...HlmFormFieldImports],
183
+ template: `
184
+ <form [formGroup]="form">
185
+ <hlm-form-field>
186
+ <label hlmLabel [for]="bioId">Bio</label>
187
+ <textarea hlmTextarea [id]="bioId" formControlName="bio" rows="3"></textarea>
188
+ <hlm-hint>Tell us about yourself in 160 characters.</hlm-hint>
189
+ <hlm-error>Bio is required.</hlm-error>
190
+ </hlm-form-field>
191
+ </form>
192
+ `,
193
+ })
194
+ export class BioFieldComponent {
195
+ private readonly fb = inject(FormBuilder);
196
+ private readonly ids = inject(HlmFormIdGenerator);
197
+ readonly bioId = this.ids.generate('bio');
198
+ readonly form = this.fb.group({ bio: ['', Validators.required] });
199
+ }
200
+ ```
201
+
202
+ ### 3. Select field
203
+
204
+ ```ts
205
+ import { Component, inject } from '@angular/core';
206
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
207
+ import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
208
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
209
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
210
+
211
+ @Component({
212
+ selector: 'app-role-field',
213
+ standalone: true,
214
+ imports: [ReactiveFormsModule, HlmLabel, ...HlmFormFieldImports, ...HlmSelectImports],
215
+ template: `
216
+ <form [formGroup]="form">
217
+ <hlm-form-field>
218
+ <label hlmLabel for="role">Role</label>
219
+ <hlm-select formControlName="role" id="role" placeholder="Select a role">
220
+ <hlm-select-content>
221
+ <hlm-select-item value="admin">Admin</hlm-select-item>
222
+ <hlm-select-item value="editor">Editor</hlm-select-item>
223
+ </hlm-select-content>
224
+ </hlm-select>
225
+ <hlm-hint>Controls what they can access.</hlm-hint>
226
+ <hlm-error>Pick a role.</hlm-error>
227
+ </hlm-form-field>
228
+ </form>
229
+ `,
230
+ })
231
+ export class RoleFieldComponent {
232
+ private readonly fb = inject(FormBuilder);
233
+ readonly form = this.fb.group({ role: ['', Validators.required] });
234
+ }
235
+ ```
236
+
237
+ ### 4. Date picker field
238
+
239
+ ```ts
240
+ import { Component, inject } from '@angular/core';
241
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
242
+ import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
243
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
244
+ import { HlmDatePickerImports } from '@egose/shadcn-theme-ng/date-picker';
245
+
246
+ @Component({
247
+ selector: 'app-date-field',
248
+ standalone: true,
249
+ imports: [ReactiveFormsModule, HlmLabel, ...HlmFormFieldImports, ...HlmDatePickerImports],
250
+ template: `
251
+ <form [formGroup]="form">
252
+ <hlm-form-field>
253
+ <label hlmLabel for="start">Start date</label>
254
+ <hlm-date-picker formControlName="start">
255
+ <hlm-date-picker-input inputId="start" placeholder="Pick a date" />
256
+ </hlm-date-picker>
257
+ <hlm-hint>First day of the subscription.</hlm-hint>
258
+ <hlm-error>Start date is required.</hlm-error>
259
+ </hlm-form-field>
260
+ </form>
261
+ `,
262
+ })
263
+ export class DateFieldComponent {
264
+ private readonly fb = inject(FormBuilder);
265
+ readonly form = this.fb.group({ start: [null as Date | null, Validators.required] });
266
+ }
267
+ ```
268
+
269
+ ### 5. Multiple errors (first-match display)
270
+
271
+ ```ts
272
+ import { Component, inject } from '@angular/core';
273
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
274
+ import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
275
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
276
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
277
+
278
+ @Component({
279
+ selector: 'app-multi-error',
280
+ standalone: true,
281
+ imports: [ReactiveFormsModule, HlmLabel, HlmInput, ...HlmFormFieldImports],
282
+ template: `
283
+ <form [formGroup]="form" (ngSubmit)="submit()">
284
+ <hlm-form-field>
285
+ <label hlmLabel for="email">Email</label>
286
+ <input hlmInput id="email" type="email" formControlName="email" />
287
+ <hlm-hint>We'll send the receipt here.</hlm-hint>
288
+ <!-- All hlm-error children project together while any error exists. -->
289
+ <hlm-error>Email is required.</hlm-error>
290
+ <hlm-error>Enter a valid email address.</hlm-error>
291
+ </hlm-form-field>
292
+ <button type="submit" (click)="form.markAllAsTouched()">Submit</button>
293
+ </form>
294
+ `,
295
+ })
296
+ export class MultiErrorComponent {
297
+ private readonly fb = inject(FormBuilder);
298
+ readonly form = this.fb.group({ email: ['', [Validators.required, Validators.email]] });
299
+ submit() {
300
+ this.form.markAllAsTouched();
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### 6. Checkbox row + submit-gated validation
306
+
307
+ ```ts
308
+ import { Component, inject } from '@angular/core';
309
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
310
+ import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
311
+ import { HlmCheckbox } from '@egose/shadcn-theme-ng/checkbox';
312
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
313
+
314
+ @Component({
315
+ selector: 'app-check-field',
316
+ standalone: true,
317
+ imports: [ReactiveFormsModule, HlmCheckbox, HlmLabel, ...HlmFormFieldImports],
318
+ template: `
319
+ <form [formGroup]="form" (ngSubmit)="submit()">
320
+ <hlm-form-field class="tw:space-y-1">
321
+ <div class="tw:flex tw:items-center tw:gap-2">
322
+ <hlm-checkbox id="consent" formControlName="consent" />
323
+ <label hlmLabel for="consent">I agree to the terms</label>
324
+ </div>
325
+ <hlm-error>You must agree before continuing.</hlm-error>
326
+ </hlm-form-field>
327
+ <button type="submit">Continue</button>
328
+ </form>
329
+ `,
330
+ })
331
+ export class CheckFieldComponent {
332
+ private readonly fb = inject(FormBuilder);
333
+ readonly form = this.fb.group({ consent: [false, Validators.requiredTrue] });
334
+ submit() {
335
+ this.form.markAllAsTouched();
336
+ if (this.form.valid) console.log('ok');
337
+ }
338
+ }
339
+ ```
340
+
341
+ ## Accessibility notes
342
+
343
+ - The wrapper itself adds no label — always include a `<label hlmLabel>` (or `aria-label` on the control) so the field has an accessible name.
344
+ - Hint and error are mutually exclusive in the DOM (only one projects at a time), keeping `aria-describedby` targets unambiguous.
345
+ - For custom controls, implement `BrnFieldControl` so `errors()` and described-by wiring flow; otherwise the wrapper throws at runtime.
346
+ - Mark forms for submit-gated validation with `markAllAsTouched()` so errors appear on submit, not just on blur.
347
+
348
+ ## Theming / CSS variables
349
+
350
+ No component-specific CSS variables. The wrapper is `tw:block tw:space-y-2`; errors are `text-destructive`, hints `text-muted-foreground`. Extend via `class` on each element.
351
+
352
+ ## Related subpaths
353
+
354
+ - `@egose/shadcn-theme-ng/form-field-simple` — lighter `eg-form-field` alternative with touched/dirty-gated errors.
355
+ - `@egose/shadcn-theme-ng/field` — full shadcn Field layout system (groups, legends, separators).
356
+ - `@egose/shadcn-theme-ng/label` — `HlmLabel` used for field labels.
357
+ - `@egose/shadcn-theme-ng/form-checkbox`, `.../form-date-picker`, `.../form-text-input` — ready-made wrappers built on these primitives.