@egose/shadcn-theme-ng-tw 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 (165) 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/fesm2022/alert.mjs +1 -1
  5. package/alert-dialog/README.md +471 -5
  6. package/aspect-ratio/README.md +272 -5
  7. package/autocomplete/README.md +502 -2
  8. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  9. package/avatar/README.md +357 -5
  10. package/badge/README.md +318 -2
  11. package/basic-alert/README.md +353 -2
  12. package/breadcrumb/README.md +406 -5
  13. package/button/README.md +482 -2
  14. package/button/fesm2022/button.mjs +85 -107
  15. package/button/types/button.d.ts +5 -8
  16. package/button-group/README.md +318 -5
  17. package/button-group/fesm2022/button-group.mjs +1 -1
  18. package/calendar/README.md +357 -2
  19. package/card/README.md +331 -5
  20. package/carousel/README.md +333 -5
  21. package/carousel/fesm2022/carousel.mjs +4 -1
  22. package/checkbox/README.md +320 -2
  23. package/checkbox/fesm2022/checkbox.mjs +6 -7
  24. package/checkbox/types/checkbox.d.ts +1 -1
  25. package/collapsible/README.md +332 -5
  26. package/combobox/README.md +507 -5
  27. package/combobox/fesm2022/combobox.mjs +5 -2
  28. package/command/README.md +435 -5
  29. package/confirmation-dialog/README.md +301 -2
  30. package/context-menu/README.md +366 -5
  31. package/date-picker/README.md +469 -2
  32. package/date-picker/fesm2022/date-picker.mjs +150 -32
  33. package/date-picker/types/date-picker.d.ts +102 -9
  34. package/dialog/README.md +448 -2
  35. package/drawer/README.md +395 -5
  36. package/dropdown-menu/README.md +417 -5
  37. package/empty/README.md +329 -5
  38. package/field/README.md +385 -5
  39. package/form-autocomplete/README.md +177 -0
  40. package/form-autocomplete/fesm2022/form-autocomplete.mjs +211 -0
  41. package/form-autocomplete/package.json +24 -0
  42. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  43. package/form-checkbox/README.md +322 -2
  44. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  45. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  46. package/form-combobox/README.md +202 -0
  47. package/form-combobox/fesm2022/form-combobox.mjs +273 -0
  48. package/form-combobox/package.json +24 -0
  49. package/form-combobox/types/form-combobox.d.ts +73 -0
  50. package/form-date-picker/README.md +348 -2
  51. package/form-date-picker/fesm2022/form-date-picker.mjs +60 -16
  52. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  53. package/form-date-picker-multi/README.md +191 -0
  54. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +239 -0
  55. package/form-date-picker-multi/package.json +24 -0
  56. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  57. package/form-date-range-picker/README.md +253 -0
  58. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +236 -0
  59. package/form-date-range-picker/package.json +24 -0
  60. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  61. package/form-field/README.md +356 -2
  62. package/form-field-simple/README.md +340 -2
  63. package/form-input-otp/README.md +194 -0
  64. package/form-input-otp/fesm2022/form-input-otp.mjs +194 -0
  65. package/form-input-otp/package.json +24 -0
  66. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  67. package/form-month-year-picker/README.md +188 -0
  68. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +229 -0
  69. package/form-month-year-picker/package.json +24 -0
  70. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  71. package/form-native-select/README.md +205 -0
  72. package/form-native-select/fesm2022/form-native-select.mjs +187 -0
  73. package/form-native-select/package.json +24 -0
  74. package/form-native-select/types/form-native-select.d.ts +57 -0
  75. package/form-phone-input/README.md +188 -0
  76. package/form-phone-input/fesm2022/form-phone-input.mjs +203 -0
  77. package/form-phone-input/package.json +24 -0
  78. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  79. package/form-radio-group/README.md +198 -0
  80. package/form-radio-group/fesm2022/form-radio-group.mjs +211 -0
  81. package/form-radio-group/package.json +24 -0
  82. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  83. package/form-searchable-multiselect/README.md +371 -2
  84. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  85. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  86. package/form-select/README.md +360 -2
  87. package/form-select/fesm2022/form-select.mjs +24 -15
  88. package/form-select/types/form-select.d.ts +18 -1
  89. package/form-slider/README.md +182 -0
  90. package/form-slider/fesm2022/form-slider.mjs +186 -0
  91. package/form-slider/package.json +24 -0
  92. package/form-slider/types/form-slider.d.ts +60 -0
  93. package/form-switch/README.md +173 -0
  94. package/form-switch/fesm2022/form-switch.mjs +178 -0
  95. package/form-switch/package.json +24 -0
  96. package/form-switch/types/form-switch.d.ts +50 -0
  97. package/form-text-input/README.md +381 -2
  98. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  99. package/form-text-input/types/form-text-input.d.ts +18 -1
  100. package/form-textarea/README.md +357 -2
  101. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  102. package/form-textarea/types/form-textarea.d.ts +18 -1
  103. package/form-toggle/README.md +186 -0
  104. package/form-toggle/fesm2022/form-toggle.mjs +241 -0
  105. package/form-toggle/package.json +24 -0
  106. package/form-toggle/types/form-toggle.d.ts +82 -0
  107. package/form-toggle-group/README.md +176 -0
  108. package/form-toggle-group/fesm2022/form-toggle-group.mjs +216 -0
  109. package/form-toggle-group/package.json +24 -0
  110. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  111. package/hover-card/README.md +256 -5
  112. package/icon/README.md +239 -2
  113. package/input/README.md +269 -2
  114. package/input-group/README.md +335 -5
  115. package/input-group/fesm2022/input-group.mjs +23 -13
  116. package/input-group/types/input-group.d.ts +4 -1
  117. package/input-otp/README.md +375 -5
  118. package/item/README.md +385 -5
  119. package/item/fesm2022/item.mjs +3 -3
  120. package/kbd/README.md +291 -5
  121. package/label/README.md +272 -2
  122. package/layout-simple/README.md +193 -2
  123. package/layout-simple/fesm2022/layout-simple.mjs +877 -409
  124. package/layout-simple/types/layout-simple.d.ts +174 -137
  125. package/menu/README.md +417 -2
  126. package/menubar/README.md +343 -5
  127. package/native-select/README.md +323 -5
  128. package/native-select/fesm2022/native-select.mjs +18 -6
  129. package/native-select/types/native-select.d.ts +7 -2
  130. package/navigation-menu/README.md +369 -5
  131. package/package.json +57 -1
  132. package/pagination/README.md +388 -5
  133. package/phone-input/README.md +114 -0
  134. package/phone-input/fesm2022/phone-input.mjs +191 -0
  135. package/phone-input/package.json +24 -0
  136. package/phone-input/types/phone-input.d.ts +67 -0
  137. package/popover/README.md +331 -2
  138. package/progress/README.md +311 -5
  139. package/radio-group/README.md +364 -2
  140. package/radio-group/fesm2022/radio-group.mjs +5 -1
  141. package/resizable/README.md +269 -5
  142. package/scroll-area/README.md +233 -5
  143. package/searchable-multiselect/README.md +323 -2
  144. package/select/README.md +437 -2
  145. package/separator/README.md +222 -2
  146. package/sheet/README.md +311 -2
  147. package/sheet/fesm2022/sheet.mjs +1 -1
  148. package/sidebar/README.md +457 -5
  149. package/skeleton/README.md +217 -5
  150. package/slider/README.md +273 -5
  151. package/slider/fesm2022/slider.mjs +17 -13
  152. package/sonner/README.md +346 -2
  153. package/spinner/README.md +284 -2
  154. package/switch/README.md +310 -2
  155. package/switch/fesm2022/switch.mjs +7 -5
  156. package/switch/types/switch.d.ts +2 -1
  157. package/table/README.md +423 -5
  158. package/tabs/README.md +411 -2
  159. package/tabs/fesm2022/tabs.mjs +12 -2
  160. package/textarea/README.md +282 -5
  161. package/toggle/README.md +270 -5
  162. package/toggle-group/README.md +340 -5
  163. package/tooltip/README.md +269 -2
  164. package/typography/README.md +271 -5
  165. package/utils/README.md +303 -2
package/button/README.md CHANGED
@@ -1,3 +1,483 @@
1
- # Button Subpath
1
+ # Button (`@egose/shadcn-theme-ng/button`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/button` or `@egose/shadcn-theme-ng-tw/button`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ The shadcn/ui Button: a consistently styled clickable control for actions, links-that-look-like
4
+ buttons, and form submits — with tones, sizes, outline appearances, icon slots, and a built-in
5
+ loading spinner.
6
+
7
+ This subpath exports **two** controls over the headless `BrnButton` from
8
+ `@spartan-ng/brain/button`:
9
+
10
+ - `HlmButton` (`button[hlmButton], a[hlmButton]`) — the full-featured component: `variant`,
11
+ `size`, `appearance`, `loading` (overlay spinner via `HlmSpinner`), `icon` template + position,
12
+ `disabled`, `type`, and `class` merging through `buttonVariants` (`cva`) + `hlm()`.
13
+ - `HlmBtn` (`button[hlmBtn], a[hlmBtn]`, `exportAs: hlmBtn`) — a thin `BrnButton` wrapper with only
14
+ `variant` / `size` / `type` (defaults injectable via `provideBrnButtonConfig`). Used internally
15
+ by footer-style consumers (e.g. `alert-dialog`) and handy when you need the tones without the
16
+ spinner/icon machinery.
17
+
18
+ > **Ships as:** `@egose/shadcn-theme-ng/button` and `@egose/shadcn-theme-ng-tw/button`
19
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
20
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
21
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ # Plain Tailwind (no prefix)
27
+ npm install @egose/shadcn-theme-ng
28
+
29
+ # tw:-prefixed Tailwind variant
30
+ npm install @egose/shadcn-theme-ng-tw
31
+ ```
32
+
33
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
34
+ [package README](../../README.md#peer-dependencies). This subpath additionally relies at runtime on
35
+ `@egose/shadcn-theme-ng/utils` (`hlm()`) and `@egose/shadcn-theme-ng/spinner` (`HlmSpinner`,
36
+ used by the `HlmButton` loading state).
37
+
38
+ ## Imports
39
+
40
+ All symbols are exported from the subpath root (`projects/button/src/public-api.ts`):
41
+
42
+ ```ts
43
+ import {
44
+ HlmButton,
45
+ HlmBtn,
46
+ buttonVariants,
47
+ provideBrnButtonConfig,
48
+ injectBrnButtonConfig,
49
+ HlmButtonImports, // [HlmButton, HlmBtn]
50
+ HlmButtonModule,
51
+ type ButtonVariants,
52
+ type VariantType,
53
+ type SizeType,
54
+ type AppearanceType,
55
+ type BrnButtonConfig,
56
+ } from '@egose/shadcn-theme-ng/button';
57
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/button'
58
+ ```
59
+
60
+ Standalone component — spread the `*Imports` array (both controls):
61
+
62
+ ```ts
63
+ import { Component } from '@angular/core';
64
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
65
+
66
+ @Component({
67
+ selector: 'app-demo',
68
+ standalone: true,
69
+ imports: [...HlmButtonImports],
70
+ template: `...`,
71
+ })
72
+ export class DemoComponent {}
73
+ ```
74
+
75
+ NgModule-based consumer — import the module:
76
+
77
+ ```ts
78
+ import { NgModule } from '@angular/core';
79
+ import { HlmButtonModule } from '@egose/shadcn-theme-ng/button';
80
+
81
+ @NgModule({ imports: [HlmButtonModule] })
82
+ export class DemoModule {}
83
+ ```
84
+
85
+ Global defaults — provide a `BrnButtonConfig` once (consumed by `HlmBtn`):
86
+
87
+ ```ts
88
+ import { Component } from '@angular/core';
89
+ import { HlmButtonImports, provideBrnButtonConfig } from '@egose/shadcn-theme-ng/button';
90
+
91
+ @Component({
92
+ selector: 'app-shell',
93
+ standalone: true,
94
+ imports: [...HlmButtonImports],
95
+ providers: [provideBrnButtonConfig({ variant: 'secondary', size: 'sm' })],
96
+ template: `...`,
97
+ })
98
+ export class ShellComponent {}
99
+ ```
100
+
101
+ ## Anatomy / Structure
102
+
103
+ ```html
104
+ <!-- full-featured -->
105
+ <button hlmButton variant="primary" size="default" appearance="solid">Save</button>
106
+ <a hlmButton variant="outline" href="/docs">Docs</a>
107
+
108
+ <!-- thin wrapper -->
109
+ <button hlmBtn variant="ghost" size="sm">Cancel</button>
110
+ ```
111
+
112
+ Real selectors (from source):
113
+
114
+ | Class | Selector(s) | Kind |
115
+ | ----------- | ------------------------------------------------ | --------- |
116
+ | `HlmButton` | `button[hlmButton], a[hlmButton]` | Component |
117
+ | `HlmBtn` | `button[hlmBtn], a[hlmBtn]` (`exportAs: hlmBtn`) | Directive |
118
+
119
+ `HlmButton` loading template (from source): when `loading()` is true, the projected content is
120
+ kept invisible for sizing while an absolutely centered `<hlm-spinner>` overlays it; otherwise the
121
+ content renders in a flex row with the optional `icon()` template on the `left`/`right`.
122
+
123
+ ## API reference
124
+
125
+ ### `HlmButton` — `button[hlmButton], a[hlmButton]`
126
+
127
+ | Input | Type | Default | Description |
128
+ | ------------------------------ | ----------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- |
129
+ | `variant` | `VariantType` | `'primary'` | Semantic color tone, plus legacy `default`, `outline`, `link`, and `ghost` variants (15 values). |
130
+ | `size` | `SizeType` | `'default'` | Height/padding scale (13 values, see below). |
131
+ | `appearance` | `AppearanceType` | `'solid'` | `'solid'`, `'outline'` (theme background + tone border/text), `'outline-filled'` (fills on hover), `'ghost'`, or `'link'`. |
132
+ | `loading` | `boolean` | `false` | Shows the spinner overlay; sets `aria-busy`, forces `disabled`, adds `pointer-events-none`. |
133
+ | `icon` | `TemplateRef<unknown> \| undefined` | `undefined` | Icon template rendered beside the label. |
134
+ | `iconPosition` | `'left' \| 'right'` | `'left'` | Which side the `icon()` renders on. |
135
+ | `disabled` | `boolean` | `false` | Disables the control (also forwarded to `BrnButton`). |
136
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type (reflected as `type` attr). |
137
+ | `class` (alias of `userClass`) | `ClassValue` | `''` | Extra classes merged via `hlm()`. |
138
+ | `spinnerUserClass` | `ClassValue` | `''` | Extra classes for the loading `<hlm-spinner>` (defaults to the button's current text color). |
139
+
140
+ | Method | Signature | Description |
141
+ | ---------- | --------------------------------- | ----------------------------------- |
142
+ | `setClass` | `setClass(classes: string): void` | Imperatively appends extra classes. |
143
+
144
+ `variant` values: `default` · `primary` · `secondary` · `success` · `warning` · `danger` ·
145
+ `info` · `light` · `dark` · `accent` · `destructive` · `muted` · `outline` · `link` · `ghost`.
146
+
147
+ `size` values: `xs` · `sm` · `default` · `lg` · `icon` · `icon-xs` · `icon-sm` · `icon-lg` ·
148
+ `compact-xs` · `compact-sm` · `compact-default` · `compact-lg` · `compact-icon`.
149
+
150
+ `appearance` values: `solid` · `outline` · `outline-filled` · `ghost` · `link`.
151
+
152
+ Prefer a semantic `variant` with an independent `appearance`, for example
153
+ `variant="success" appearance="ghost"` or `variant="danger" appearance="link"`.
154
+ `ghost` is transparent with a subtle tone-colored hover background; `link` is transparent
155
+ with an underline on hover. Both have no border or shadow.
156
+ Legacy `variant="outline"`, `variant="link"`, and `variant="ghost"` remain supported;
157
+ `default` remains an alias for the primary color. The legacy ghost variant retains its
158
+ light-colored hover treatment.
159
+
160
+ ### `HlmBtn` — `button[hlmBtn], a[hlmBtn]`
161
+
162
+ Thin wrapper: only `variant` / `size` / `type` (+ `class`), no `appearance`, `loading`, or icon
163
+ support. Its `variant`/`size` defaults come from `injectBrnButtonConfig()` (global default
164
+ `{ variant: 'default', size: 'default' }`, overridable per subtree with
165
+ `provideBrnButtonConfig()`).
166
+
167
+ | Input | Type | Default | Description |
168
+ | ------------------------------ | --------------------------------- | ----------------------------- | ------------------- |
169
+ | `variant` | `ButtonVariants['variant']` | injected config (`'default'`) | Tone. |
170
+ | `size` | `ButtonVariants['size']` | injected config (`'default'`) | Size. |
171
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type. |
172
+ | `class` (alias of `userClass`) | `ClassValue` | `''` | Extra classes. |
173
+
174
+ | Method | Signature | Description |
175
+ | ---------- | --------------------------------- | ----------------------------------- |
176
+ | `setClass` | `setClass(classes: string): void` | Imperatively appends extra classes. |
177
+
178
+ ### Config helpers (`button.token.ts`)
179
+
180
+ | Symbol | Signature | Description |
181
+ | ------------------------ | ----------------------------------------------------- | ------------------------------------------------------------ |
182
+ | `BrnButtonConfig` | `{ variant; size }` | Shape of the button defaults object. |
183
+ | `provideBrnButtonConfig` | `(config: Partial<BrnButtonConfig>) => ValueProvider` | Provide subtree defaults for `HlmBtn`. |
184
+ | `injectBrnButtonConfig` | `() => BrnButtonConfig` | Read the effective config (falls back to built-in defaults). |
185
+
186
+ Exported styling/types: `buttonVariants` (`cva` table — reuse for custom hosts), `ButtonVariants`,
187
+ `VariantType`, `SizeType`, `AppearanceType`.
188
+
189
+ ## Examples
190
+
191
+ ### 1. Basic usage
192
+
193
+ ```ts
194
+ import { Component } from '@angular/core';
195
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
196
+
197
+ @Component({
198
+ selector: 'app-button-basic',
199
+ standalone: true,
200
+ imports: [...HlmButtonImports],
201
+ template: `
202
+ <div class="tw:flex tw:gap-2">
203
+ <button hlmButton>Primary</button>
204
+ <button hlmBtn variant="secondary">Thin wrapper</button>
205
+ <a hlmButton variant="outline" href="/docs">Docs link</a>
206
+ </div>
207
+ `,
208
+ })
209
+ export class ButtonBasicComponent {}
210
+ ```
211
+
212
+ ```html
213
+ <app-button-basic />
214
+ ```
215
+
216
+ ### 2. All variants and sizes
217
+
218
+ ```ts
219
+ import { Component } from '@angular/core';
220
+ import { HlmButton, type VariantType, type SizeType } from '@egose/shadcn-theme-ng/button';
221
+
222
+ @Component({
223
+ selector: 'app-button-matrix',
224
+ standalone: true,
225
+ imports: [HlmButton],
226
+ template: `
227
+ <div class="tw:flex tw:flex-wrap tw:gap-2">
228
+ @for (v of variants; track v) {
229
+ <button hlmButton [variant]="v">{{ v }}</button>
230
+ }
231
+ </div>
232
+ <div class="tw:mt-4 tw:flex tw:flex-wrap tw:items-center tw:gap-2">
233
+ @for (s of sizes; track s) {
234
+ <button hlmButton variant="secondary" [size]="s">{{ s }}</button>
235
+ }
236
+ </div>
237
+ `,
238
+ })
239
+ export class ButtonMatrixComponent {
240
+ readonly variants: VariantType[] = [
241
+ 'default',
242
+ 'primary',
243
+ 'secondary',
244
+ 'success',
245
+ 'warning',
246
+ 'danger',
247
+ 'info',
248
+ 'light',
249
+ 'dark',
250
+ 'accent',
251
+ 'destructive',
252
+ 'muted',
253
+ 'outline',
254
+ 'link',
255
+ 'ghost',
256
+ ];
257
+ readonly sizes: SizeType[] = [
258
+ 'xs',
259
+ 'sm',
260
+ 'default',
261
+ 'lg',
262
+ 'icon',
263
+ 'compact-xs',
264
+ 'compact-sm',
265
+ 'compact-default',
266
+ 'compact-lg',
267
+ ];
268
+ }
269
+ ```
270
+
271
+ ### 3. Outline appearances + icon buttons
272
+
273
+ ```ts
274
+ import { Component } from '@angular/core';
275
+ import { NgIcon, provideIcons } from '@ng-icons/core';
276
+ import { lucidePlus, lucideTrash } from '@ng-icons/lucide';
277
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
278
+ import { HlmIcon } from '@egose/shadcn-theme-ng/icon';
279
+
280
+ @Component({
281
+ selector: 'app-button-appearance',
282
+ standalone: true,
283
+ imports: [HlmButton, NgIcon, HlmIcon],
284
+ providers: [provideIcons({ lucidePlus, lucideTrash })],
285
+ template: `
286
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-2">
287
+ <button hlmButton variant="success" appearance="outline">Outline</button>
288
+ <button hlmButton variant="destructive" appearance="outline-filled">Outline-filled (hover me)</button>
289
+ <button hlmButton variant="success" appearance="ghost">Ghost</button>
290
+ <button hlmButton variant="danger" appearance="link">Link appearance</button>
291
+ <button hlmButton size="icon" aria-label="Create">
292
+ <ng-icon hlm name="lucidePlus" />
293
+ </button>
294
+ <button hlmButton variant="destructive" size="icon-sm" aria-label="Delete">
295
+ <ng-icon hlm name="lucideTrash" size="sm" />
296
+ </button>
297
+ </div>
298
+ `,
299
+ })
300
+ export class ButtonAppearanceComponent {}
301
+ ```
302
+
303
+ ### 4. Loading state with `HlmButton` (async submit)
304
+
305
+ `loading` overlays a tone-matched spinner, keeps the button width stable, sets `aria-busy`, and
306
+ blocks interaction until done:
307
+
308
+ ```ts
309
+ import { Component, signal } from '@angular/core';
310
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
311
+
312
+ @Component({
313
+ selector: 'app-button-loading',
314
+ standalone: true,
315
+ imports: [HlmButton],
316
+ template: `
317
+ <form (ngSubmit)="submit()">
318
+ <button hlmButton type="submit" [loading]="saving()">Save changes</button>
319
+ <button hlmButton variant="ghost" type="button" [disabled]="saving()">Cancel</button>
320
+ </form>
321
+ `,
322
+ })
323
+ export class ButtonLoadingComponent {
324
+ readonly saving = signal(false);
325
+
326
+ async submit() {
327
+ this.saving.set(true);
328
+ try {
329
+ await fetch('/api/save', { method: 'POST' });
330
+ } finally {
331
+ this.saving.set(false);
332
+ }
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### 5. Icon templates with `icon` / `iconPosition`
338
+
339
+ ```ts
340
+ import { Component } from '@angular/core';
341
+ import { NgIcon, provideIcons } from '@ng-icons/core';
342
+ import { lucideArrowLeft, lucideArrowRight } from '@ng-icons/lucide';
343
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
344
+
345
+ @Component({
346
+ selector: 'app-button-icons',
347
+ standalone: true,
348
+ imports: [HlmButton, NgIcon],
349
+ providers: [provideIcons({ lucideArrowLeft, lucideArrowRight })],
350
+ template: `
351
+ <ng-template #backIcon><ng-icon name="lucideArrowLeft" /></ng-template>
352
+ <ng-template #nextIcon><ng-icon name="lucideArrowRight" /></ng-template>
353
+
354
+ <div class="tw:flex tw:gap-2">
355
+ <button hlmButton variant="outline" [icon]="backIcon" iconPosition="left">Back</button>
356
+ <button hlmButton [icon]="nextIcon" iconPosition="right">Next</button>
357
+ </div>
358
+ `,
359
+ })
360
+ export class ButtonIconsComponent {}
361
+ ```
362
+
363
+ ### 6. Reactive form submit + global `HlmBtn` defaults
364
+
365
+ ```ts
366
+ import { Component } from '@angular/core';
367
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
368
+ import { HlmButtonImports, provideBrnButtonConfig } from '@egose/shadcn-theme-ng/button';
369
+
370
+ @Component({
371
+ selector: 'app-button-form',
372
+ standalone: true,
373
+ imports: [...HlmButtonImports, ReactiveFormsModule],
374
+ // Every hlmBtn in this subtree defaults to compact secondary unless overridden.
375
+ providers: [provideBrnButtonConfig({ variant: 'secondary', size: 'compact-default' })],
376
+ template: `
377
+ <form [formGroup]="form" (ngSubmit)="submit()" class="tw:flex tw:flex-col tw:gap-3">
378
+ <input formControlName="name" placeholder="Project name" />
379
+ <div class="tw:flex tw:gap-2">
380
+ <button hlmButton [disabled]="form.invalid || saving">Create project</button>
381
+ <button hlmBtn type="button" (click)="form.reset()">Reset</button>
382
+ </div>
383
+ </form>
384
+ `,
385
+ })
386
+ export class ButtonFormComponent {
387
+ readonly form = new FormGroup({ name: new FormControl('', { validators: Validators.required, nonNullable: true }) });
388
+ saving = false;
389
+
390
+ submit() {
391
+ if (this.form.invalid) return;
392
+ this.saving = true;
393
+ setTimeout(() => (this.saving = false), 800);
394
+ }
395
+ }
396
+ ```
397
+
398
+ ## Accessibility notes
399
+
400
+ - These are native `<button>` / `<a>` elements enhanced by `BrnButton`: keyboard focus,
401
+ `Enter`/`Space` activation, and `disabled` semantics work out of the box. Never render a
402
+ `div` with `hlmButton` — the selector only matches `button`/`a` for exactly this reason.
403
+ - `loading` sets `aria-busy="true"` and disables the control; announce completion with adjacent
404
+ text or a toast (see `.../sonner`) since the spinner itself is silent.
405
+ - Icon-only buttons (`size="icon*"`) **must** have an `aria-label`. Keep visible labels verb-led
406
+ and unique per context ("Delete project", not "Delete" × 5).
407
+ - `disabled` uses `pointer-events-none` + reduced opacity — pair disabled states with a visible
408
+ explanation (e.g. "Complete the required fields") rather than leaving users guessing.
409
+
410
+ ## Theming / CSS variables
411
+
412
+ Buttons use shared semantic Tailwind color tokens, so the consumer controls their palette in
413
+ global CSS. Define each tone and its `-foreground` partner: `primary`, `secondary`, `success`,
414
+ `warning`, `danger`, `info`, `light`, `dark`, `accent`, `destructive`, and `muted`. Also provide
415
+ `background` for outline surfaces and `ring` for keyboard focus.
416
+
417
+ For example, add these mappings to your Tailwind v4 stylesheet (alongside the package source
418
+ scan described in the package README):
419
+
420
+ ```css
421
+ @theme inline {
422
+ --color-primary: var(--primary);
423
+ --color-primary-foreground: var(--primary-foreground);
424
+ --color-success: var(--success);
425
+ --color-success-foreground: var(--success-foreground);
426
+ --color-background: hsl(var(--background));
427
+ --color-ring: hsl(var(--ring));
428
+ /* Map the remaining semantic tones in the same way. */
429
+ }
430
+
431
+ :root {
432
+ --primary: #228be6;
433
+ --primary-foreground: #ffffff;
434
+ --success: #28a745;
435
+ --success-foreground: #ffffff;
436
+ --background: 0 0% 100%;
437
+ --ring: 0 0% 3.9%;
438
+ }
439
+
440
+ .dark {
441
+ --primary: #74c0fc;
442
+ --primary-foreground: #102a43;
443
+ --success: #75b798;
444
+ --success-foreground: #0a3622;
445
+ --background: 0 0% 3.9%;
446
+ --ring: 0 0% 83.1%;
447
+ }
448
+
449
+ .brand-theme {
450
+ --primary: #7950f2;
451
+ --primary-foreground: #ffffff;
452
+ }
453
+ ```
454
+
455
+ The semantic palette variables above contain complete CSS colors; `background` and `ring`
456
+ use HSL channels with `hsl(...)` mappings. `@theme inline` ensures a `.dark` or `.brand-theme`
457
+ ancestor can override colors for just its subtree. The example app's `src/styles.css` supplies
458
+ the full light/dark palette. Keep the existing `prefix(tw)` import when using the `-tw` package;
459
+ the `@theme` token names stay unprefixed.
460
+
461
+ ```html
462
+ <section class="brand-theme">
463
+ <button hlmButton variant="primary" appearance="outline">Branded outline</button>
464
+ <button hlmButton variant="success" appearance="ghost">Save changes</button>
465
+ </section>
466
+ ```
467
+
468
+ Adding a CSS token such as `--color-brand` does **not** register `variant="brand"`;
469
+ variant names remain a fixed typed API. Customize an existing semantic token or supply
470
+ utility classes through `class` instead.
471
+
472
+ `class` overrides are merged after the variant/appearance styles; `setClass()` appends
473
+ imperative overrides. The loading spinner inherits the button's resolved text color, and
474
+ `spinnerUserClass` can override it. For custom hosts, use
475
+ `hlm(buttonVariants({ variant: 'success', appearance: 'outline' }), customClasses)`;
476
+ the shared variant function includes all appearance styles.
477
+
478
+ ## Related subpaths
479
+
480
+ - `@egose/shadcn-theme-ng/spinner` — the loader rendered in the `loading` state
481
+ - `@egose/shadcn-theme-ng/badge` — counts/status chips composed inside buttons
482
+ - `@egose/shadcn-theme-ng/button-group` — joined button rows
483
+ - `@egose/shadcn-theme-ng/alert-dialog` — footer `hlmAlertDialogAction` / `hlmAlertDialogCancel` (built on `HlmBtn`)