@tailor-platform/app-shell 1.15.0 → 1.17.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 (109) hide show
  1. package/dist/app-shell-core.css +1 -1
  2. package/dist/app-shell.d.ts +811 -131
  3. package/dist/app-shell.js +7155 -5797
  4. package/dist/{appshell-DLVBFo5J.js → appshell-CahhZy7v.js} +656 -524
  5. package/dist/semantic-contrast.test.ts +154 -0
  6. package/dist/style-entry.d.ts +147 -92
  7. package/dist/testing.d.ts +152 -95
  8. package/dist/testing.js +1 -1
  9. package/dist/theme-bridge.test.ts +101 -1
  10. package/dist/theme.bridge.css +91 -0
  11. package/dist/theme.css +6 -1
  12. package/dist/themes/_template.css +7 -1
  13. package/dist/themes/bloom.css +0 -6
  14. package/dist/themes/cream.css +0 -6
  15. package/dist/themes/default.css +389 -69
  16. package/dist/vite-plugin.d.ts +147 -92
  17. package/package.json +8 -8
  18. package/skills/app-shell-patterns/SKILL.md +108 -19
  19. package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
  20. package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
  21. package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
  22. package/skills/app-shell-patterns/references/api/define-module.md +247 -0
  23. package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
  24. package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
  25. package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
  26. package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
  27. package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
  28. package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
  29. package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
  30. package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
  31. package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
  32. package/skills/app-shell-patterns/references/api/use-app-shell-config.md +132 -0
  33. package/skills/app-shell-patterns/references/api/use-app-shell-data.md +150 -0
  34. package/skills/app-shell-patterns/references/api/use-app-shell.md +150 -0
  35. package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
  36. package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
  37. package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
  38. package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
  39. package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
  40. package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
  41. package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
  42. package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
  43. package/skills/app-shell-patterns/references/api/use-toast.md +104 -0
  44. package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
  45. package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
  46. package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
  47. package/skills/app-shell-patterns/references/components/alert.md +202 -0
  48. package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
  49. package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
  50. package/skills/app-shell-patterns/references/components/attachment.md +138 -0
  51. package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
  52. package/skills/app-shell-patterns/references/components/avatar.md +80 -0
  53. package/skills/app-shell-patterns/references/components/badge.md +309 -0
  54. package/skills/app-shell-patterns/references/components/button.md +164 -0
  55. package/skills/app-shell-patterns/references/components/card.md +181 -0
  56. package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
  57. package/skills/app-shell-patterns/references/components/combobox.md +318 -0
  58. package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
  59. package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
  60. package/skills/app-shell-patterns/references/components/data-table.md +1168 -0
  61. package/skills/app-shell-patterns/references/components/date-picker.md +259 -0
  62. package/skills/app-shell-patterns/references/components/default-header.md +75 -0
  63. package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
  64. package/skills/app-shell-patterns/references/components/description-card.md +569 -0
  65. package/skills/app-shell-patterns/references/components/dialog.md +164 -0
  66. package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
  67. package/skills/app-shell-patterns/references/components/filter-rail.md +165 -0
  68. package/skills/app-shell-patterns/references/components/form.md +378 -0
  69. package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
  70. package/skills/app-shell-patterns/references/components/grid.md +184 -0
  71. package/skills/app-shell-patterns/references/components/input.md +83 -0
  72. package/skills/app-shell-patterns/references/components/layout.md +539 -0
  73. package/skills/app-shell-patterns/references/components/menu.md +218 -0
  74. package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
  75. package/skills/app-shell-patterns/references/components/select.md +283 -0
  76. package/skills/app-shell-patterns/references/components/sheet.md +237 -0
  77. package/skills/app-shell-patterns/references/components/sidebar-group.md +472 -0
  78. package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
  79. package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
  80. package/skills/app-shell-patterns/references/components/sidebar-section.md +114 -0
  81. package/skills/app-shell-patterns/references/components/spinner.md +81 -0
  82. package/skills/app-shell-patterns/references/components/table.md +188 -0
  83. package/skills/app-shell-patterns/references/components/tabs.md +158 -0
  84. package/skills/app-shell-patterns/references/components/textarea.md +122 -0
  85. package/skills/app-shell-patterns/references/components/timeline.md +317 -0
  86. package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
  87. package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
  88. package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
  89. package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
  90. package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
  91. package/skills/app-shell-patterns/references/concepts/file-based-routing.md +356 -0
  92. package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +31 -18
  93. package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
  94. package/skills/app-shell-patterns/references/concepts/routing-navigation.md +425 -0
  95. package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +420 -0
  96. package/skills/app-shell-patterns/references/concepts/styling-theming.md +785 -0
  97. package/skills/app-shell-patterns/references/migrations.md +55 -0
  98. package/skills/app-shell-patterns/references/pages/{detail.md → document-detail.md} +10 -33
  99. package/skills/app-shell-patterns/references/patterns/form-composer.md +43 -45
  100. package/skills/app-shell-patterns/references/patterns/form-modal.md +37 -92
  101. package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
  102. package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
  103. package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
  104. package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
  105. package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +136 -139
  106. package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
  107. package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +13 -88
  108. package/skills/app-shell-patterns/references/fundamental/components.md +0 -1009
  109. package/skills/app-shell-patterns/references/fundamental/design-system.md +0 -440
@@ -0,0 +1,378 @@
1
+ ---
2
+ title: Form, Field, and Fieldset
3
+ description: Components for building validated forms with automatic accessibility wiring, validation state management, and optional React Hook Form integration
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/form.docs.outline.md — do not edit by hand. -->
7
+
8
+ # Form, Field, and Fieldset
9
+
10
+ `Form`, `Field`, and `Fieldset` are compound components for building accessible, validated forms. They handle label association, `aria-describedby`, validation state, and external error routing automatically.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import { Form, Field, Fieldset } from "@tailor-platform/app-shell";
16
+ ```
17
+
18
+ ## Basic Usage
19
+
20
+ ```tsx
21
+ <Form onFormSubmit={(values) => save(values)}>
22
+ <Field.Root name="email">
23
+ <Field.Label>Email</Field.Label>
24
+ <Field.Control type="email" required />
25
+ <Field.Description>We'll never share your email.</Field.Description>
26
+ <Field.Error match="valueMissing">Email is required.</Field.Error>
27
+ <Field.Error match="typeMismatch">Enter a valid email address.</Field.Error>
28
+ </Field.Root>
29
+ <button type="submit">Save</button>
30
+ </Form>
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Form
36
+
37
+ A form element with consolidated error handling and validation. Wraps every child `Field.Root` in a shared validation context.
38
+
39
+ ### Form Props
40
+
41
+ | Prop | Type | Default | Description |
42
+ | ---------------- | --------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
43
+ | `children` | `React.ReactNode` | **Required** | Form contents |
44
+ | `onFormSubmit` | `(values: FormValues, details: FormSubmitEventDetails) => void` | - | Called after successful validation with parsed form values. Prefer this for simple forms. Use a type argument for type-safe values. |
45
+ | `onSubmit` | `React.FormEventHandler` | - | Low-level native submit handler. Use when integrating with React Hook Form's `handleSubmit`. |
46
+ | `errors` | `Record<string, string>` | - | External errors keyed by field `name` (e.g. from an API response). Automatically routed to matching `Field.Error` components. |
47
+ | `validationMode` | `"onSubmit" \| "onBlur" \| "onChange"` | `"onSubmit"` | Controls when field validation fires. |
48
+ | `noValidate` | `boolean` | - | Disables native browser validation UI (recommended — AppShell renders its own). |
49
+ | `actionsRef` | `React.Ref<{ validate: () => void }>` | - | Ref to imperatively trigger validation from outside the submit flow. |
50
+ | `id` | `string` | - | Applied to the `<form>` element. Lets a submit button outside the form target it via the native `form` attribute. |
51
+ | `className` | `string` | - | Additional CSS classes for the `<form>` element. |
52
+
53
+ ### External Errors
54
+
55
+ Feed API validation errors back into the form via the `errors` prop. Errors are keyed by field `name` and cleared automatically when the user edits the corresponding field.
56
+
57
+ ```tsx
58
+ const [errors, setErrors] = React.useState({});
59
+
60
+ async function handleSubmit(values) {
61
+ const res = await api.save(values);
62
+ if (res.errors) setErrors(res.errors);
63
+ }
64
+
65
+ <Form errors={errors} onFormSubmit={handleSubmit}>
66
+ <Field.Root name="url">
67
+ <Field.Label>Homepage</Field.Label>
68
+ <Field.Control type="url" required />
69
+ <Field.Error />
70
+ </Field.Root>
71
+ <button type="submit">Submit</button>
72
+ </Form>;
73
+ ```
74
+
75
+ ### Programmatic Validation
76
+
77
+ Use `actionsRef` to imperatively trigger validation (e.g. in a multi-step wizard).
78
+
79
+ ```tsx
80
+ const actions = React.useRef(null);
81
+
82
+ <Form actionsRef={actions}>
83
+ <Field.Root name="name">
84
+ <Field.Label>Name</Field.Label>
85
+ <Field.Control required />
86
+ <Field.Error>Name is required.</Field.Error>
87
+ </Field.Root>
88
+ </Form>;
89
+
90
+ <button onClick={() => actions.current?.validate()}>Check</button>;
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Field
96
+
97
+ A compound component that groups all parts of a form field and manages its validation state.
98
+
99
+ `Field.Root` creates a context boundary. All child sub-components and any Base UI-backed AppShell component (e.g. `Select`, `Combobox`, `Autocomplete`) placed inside `Field.Root` automatically connect to this context — inheriting label association (`htmlFor`), `aria-describedby`, `disabled` state, and validation state.
100
+
101
+ ### Sub-components
102
+
103
+ | Sub-component | Description |
104
+ | ------------------- | --------------------------------------------------------------------------------------------- |
105
+ | `Field.Root` | Context boundary for a single field. Manages validation state. |
106
+ | `Field.Label` | Accessible label; `htmlFor` is resolved automatically via context. |
107
+ | `Field.Control` | Styled `<input>`. Can be omitted when using a Base UI-backed AppShell component as the input. |
108
+ | `Field.Description` | Supplementary help text; automatically linked via `aria-describedby`. |
109
+ | `Field.Error` | Validation error message. Use `match` to target specific validity states. |
110
+ | `Field.Validity` | Render-prop access to the field's `ValidityState` for fully custom validation UI. |
111
+
112
+ ### Field.Root Props
113
+
114
+ | Prop | Type | Default | Description |
115
+ | ------------------------ | ---------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
116
+ | `name` | `string` | - | Field name; used for form value extraction and error routing. |
117
+ | `children` | `React.ReactNode` | - | Field sub-components and input controls. |
118
+ | `disabled` | `boolean` | - | Disables all controls within the field. |
119
+ | `isTouched` | `boolean` | - | Whether the field has been blurred. Maps to React Hook Form's `fieldState.isTouched`. |
120
+ | `isDirty` | `boolean` | - | Whether the field value differs from its default. Maps to React Hook Form's `fieldState.isDirty`. |
121
+ | `invalid` | `boolean` | - | Marks the field as invalid (shows error styling). |
122
+ | `error` | `{ message?: string }` | - | Error object from React Hook Form's `fieldState.error`. Sets `invalid` automatically when provided. |
123
+ | `validate` | `(value: string) => string \| undefined` | - | Custom validation function; return an error message string to mark the field invalid. |
124
+ | `validationMode` | `"onSubmit" \| "onBlur" \| "onChange"` | - | Overrides the parent Form's `validationMode` for this field. |
125
+ | `validationDebounceTime` | `number` | - | Debounce delay (ms) for `"onChange"` validation mode. |
126
+ | `className` | `string` | - | Additional CSS classes for the field wrapper. |
127
+
128
+ ### Field.Error Props
129
+
130
+ | Prop | Type | Default | Description |
131
+ | ---------- | ---------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
132
+ | `match` | `keyof ValidityState \| boolean \| ((value: string) => boolean)` | - | Validity state key to match (e.g. `"valueMissing"`, `"typeMismatch"`). Omit for a catch-all error message. |
133
+ | `children` | `React.ReactNode` | - | Error message content. If omitted, the browser's native validation message is shown. |
134
+
135
+ ### Using Another AppShell Component as the Control
136
+
137
+ `Field.Control` can be omitted when using a Base UI-backed AppShell component (e.g. `Select`, `Combobox`). The component registers itself with the `Field` context automatically, inheriting label association and validation state.
138
+
139
+ ```tsx
140
+ const [country, setCountry] = React.useState<string | null>(null);
141
+
142
+ <Field.Root name="country">
143
+ <Field.Label>Country</Field.Label>
144
+ <Select
145
+ items={["Japan", "United States"]}
146
+ value={country}
147
+ onValueChange={setCountry}
148
+ placeholder="Select a country"
149
+ />
150
+ <Field.Error>Please select a country.</Field.Error>
151
+ </Field.Root>;
152
+ ```
153
+
154
+ ### How values reach `onFormSubmit`
155
+
156
+ `onFormSubmit` does **not** read `FormData`. It collects values from the `Field.Root`s registered
157
+ inside the `Form`, keyed by each field's `name`. Every AppShell control works this way once wrapped
158
+ in a `Field.Root` — `Select`, `Combobox`, and `Autocomplete` included. They need no `name` of their
159
+ own and usually no React state. Mirroring a field into `useState` just to submit it is normally an
160
+ anti-pattern; the exception is a value the component must read during render, such as a composer body
161
+ that gates the submit button or swaps its placeholder.
162
+
163
+ Non-string items are serialised into the submitted value:
164
+
165
+ | Item shape | Value in `onFormSubmit` |
166
+ | -------------------------------------- | ------------------------------------ |
167
+ | `string` | the string |
168
+ | `{ value, label }` | `value`, automatically |
169
+ | any other object | **JSON string** — usually not wanted |
170
+ | any other object + `itemToStringValue` | whatever that function returns |
171
+
172
+ Multi-select submits an array. For arbitrary objects, pass `itemToStringValue` to choose what gets
173
+ submitted — it is distinct from `mapItem`, which controls what the user sees:
174
+
175
+ ```tsx
176
+ <Field.Root name="warehouse">
177
+ <Field.Label>Warehouse</Field.Label>
178
+ <Combobox
179
+ items={warehouses}
180
+ mapItem={(w) => ({ label: w.name, key: String(w.id) })}
181
+ itemToStringValue={(w) => String(w.id)}
182
+ />
183
+ </Field.Root>
184
+ ```
185
+
186
+ ### Composer-style textareas
187
+
188
+ Most forms should let `Field.Root` own the submitted value. A composer is the notable exception:
189
+ the current draft is often read during render to disable Send on whitespace-only input, swap the
190
+ placeholder, or show live UI around the text. In that case, keep the `Textarea` controlled, but
191
+ still make it a real field inside `Form` so validation and server errors land beside the draft.
192
+
193
+ Use a visually hidden `Field.Label` rather than only `aria-label` so the field keeps the same
194
+ label, description, and error wiring as any other form control. If the submit can fail, route the
195
+ server rejection through `Form`'s `errors` prop and clear the draft only after success.
196
+
197
+ ```tsx
198
+ function ReplyComposer() {
199
+ const [body, setBody] = React.useState("");
200
+ const [internal, setInternal] = React.useState(false);
201
+ const [errors, setErrors] = React.useState<Record<string, string>>({});
202
+
203
+ async function handleSubmit() {
204
+ const result = await saveReply({ body, internal });
205
+ if (result?.error) {
206
+ setErrors({ body: result.error });
207
+ return;
208
+ }
209
+
210
+ setErrors({});
211
+ setBody("");
212
+ }
213
+
214
+ return (
215
+ <Form noValidate errors={errors} onFormSubmit={handleSubmit}>
216
+ <Field.Root name="body">
217
+ <Field.Label className="sr-only">Reply</Field.Label>
218
+ <Textarea
219
+ required
220
+ rows={4}
221
+ value={body}
222
+ onChange={(event) => setBody(event.target.value)}
223
+ placeholder={internal ? "Internal context for teammates..." : "Reply to customer..."}
224
+ />
225
+ <Field.Error />
226
+ </Field.Root>
227
+
228
+ <Checkbox
229
+ label="Internal note (not sent to customer)"
230
+ checked={internal}
231
+ onCheckedChange={setInternal}
232
+ />
233
+
234
+ <Button type="submit" disabled={body.trim().length === 0}>
235
+ Send
236
+ </Button>
237
+ </Form>
238
+ );
239
+ }
240
+ ```
241
+
242
+ ### Native form submission
243
+
244
+ Separately from `onFormSubmit`, controls can participate in **native** submission — a plain
245
+ `<form>`, `new FormData(form)`, or a server action. That path reads the DOM, so each control needs
246
+ its own `name`. `Select`, `Combobox`, and `Autocomplete` accept `name` (plus `form`, `required`,
247
+ `inputRef`) from 1.13.0; before that they contributed nothing to a native payload.
248
+
249
+ ### Submitting from outside the form
250
+
251
+ Give the `Form` an `id` and point a detached submit button at it with the native `form` attribute —
252
+ this is how a page-header Save reaches a form in the page body (1.13.0+).
253
+
254
+ ```tsx
255
+ <Layout.Header
256
+ title="Create product"
257
+ actions={[
258
+ <Button key="save" type="submit" form="product-form">
259
+ Save
260
+ </Button>,
261
+ ]}
262
+ />
263
+ <Form id="product-form" onFormSubmit={(values) => save(values)}>
264
+ {/* … */}
265
+ </Form>
266
+ ```
267
+
268
+ ### Custom Validation UI with Field.Validity
269
+
270
+ `Field.Validity` exposes the field's `ValidityState` via a render callback, enabling fully custom validation UI.
271
+
272
+ ```tsx
273
+ <Field.Root name="password">
274
+ <Field.Label>Password</Field.Label>
275
+ <Field.Control type="password" required minLength={8} />
276
+ <Field.Validity>
277
+ {(state) => (
278
+ <ul>
279
+ <li>{state.validity.valueMissing ? "❌" : "✅"} Required</li>
280
+ <li>{state.validity.tooShort ? "❌" : "✅"} At least 8 characters</li>
281
+ </ul>
282
+ )}
283
+ </Field.Validity>
284
+ </Field.Root>
285
+ ```
286
+
287
+ ---
288
+
289
+ ## Fieldset
290
+
291
+ A compound component (`Fieldset.Root`, `Fieldset.Legend`) for grouping related fields with a shared legend for accessible form sectioning.
292
+
293
+ ### Sub-components
294
+
295
+ | Sub-component | Description |
296
+ | ----------------- | -------------------------------------------------------------- |
297
+ | `Fieldset.Root` | Renders a `<fieldset>`. Propagates `disabled` to all children. |
298
+ | `Fieldset.Legend` | Accessible `<legend>` for the group. |
299
+
300
+ ### Fieldset.Root Props
301
+
302
+ | Prop | Type | Default | Description |
303
+ | ----------- | ----------------- | ------- | ------------------------------------------ |
304
+ | `children` | `React.ReactNode` | - | Field components to group. |
305
+ | `disabled` | `boolean` | - | Disables all controls within the fieldset. |
306
+ | `className` | `string` | - | Additional CSS classes. |
307
+
308
+ ### Example
309
+
310
+ ```tsx
311
+ <Fieldset.Root>
312
+ <Fieldset.Legend>Billing details</Fieldset.Legend>
313
+ <Field.Root name="company">
314
+ <Field.Label>Company</Field.Label>
315
+ <Field.Control />
316
+ </Field.Root>
317
+ <Field.Root name="taxId">
318
+ <Field.Label>Tax ID</Field.Label>
319
+ <Field.Control />
320
+ </Field.Root>
321
+ </Fieldset.Root>
322
+ ```
323
+
324
+ ---
325
+
326
+ ## React Hook Form Integration
327
+
328
+ React Hook Form is **optional and not an AppShell dependency** — install it in your own app. `Form` +
329
+ `Field` is the default stack and covers most forms via `onFormSubmit`. Reach for RHF only when a form
330
+ genuinely needs cross-field validation, field arrays, or a schema resolver (Zod). The two compose
331
+ rather than compete: `Field` handles accessibility wiring and visual state, RHF handles values and
332
+ the validation lifecycle.
333
+
334
+ `Field.Root` accepts `isTouched`, `isDirty`, `invalid`, and `error` props that align with React Hook Form's `fieldState` shape, so you can spread `fieldState` directly. Use `Form`'s `onSubmit` prop to connect RHF's `handleSubmit`.
335
+
336
+ ```tsx
337
+ import { useForm, Controller } from "react-hook-form";
338
+ import { zodResolver } from "@hookform/resolvers/zod";
339
+ import { z } from "zod";
340
+
341
+ const schema = z.object({ email: z.string().email() });
342
+
343
+ function MyForm() {
344
+ const { control, handleSubmit } = useForm({
345
+ resolver: zodResolver(schema),
346
+ });
347
+
348
+ return (
349
+ <Form onSubmit={handleSubmit((data) => save(data))}>
350
+ <Controller
351
+ name="email"
352
+ control={control}
353
+ render={({ field, fieldState }) => (
354
+ <Field.Root {...fieldState}>
355
+ <Field.Label>Email</Field.Label>
356
+ <Field.Control {...field} type="email" />
357
+ <Field.Error>{fieldState.error?.message}</Field.Error>
358
+ </Field.Root>
359
+ )}
360
+ />
361
+ <button type="submit">Save</button>
362
+ </Form>
363
+ );
364
+ }
365
+ ```
366
+
367
+ The `Field` context handles only accessibility wiring (`htmlFor`, `aria-describedby`) and visual state (`data-invalid`, `data-dirty`, `data-touched`) — it does not interfere with RHF's value management or validation lifecycle.
368
+
369
+ ---
370
+
371
+ ## Related
372
+
373
+ - [Input](./input.md) — Standalone styled input, useful outside of forms.
374
+ - [Textarea](./textarea.md) — Multi-line text control that integrates automatically with `Field`.
375
+ - [Checkbox](./checkbox.md) — Boolean control that integrates automatically with `Field`.
376
+ - [Select](./select.md) — Dropdown that integrates automatically with `Field`.
377
+ - [Combobox](./combobox.md) — Searchable combobox that integrates automatically with `Field`.
378
+ - [Autocomplete](./autocomplete.md) — Free-text input with suggestions that integrates automatically with `Field`.
@@ -0,0 +1,132 @@
1
+ ---
2
+ title: GlobalHeaderLayout
3
+ description: Opinionated layout with an app-wide header above the whole shell and a sidebar that collapses to a persistent icon rail
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/global-header-layout.docs.outline.md — do not edit by hand. -->
7
+
8
+ # GlobalHeaderLayout
9
+
10
+ `GlobalHeaderLayout` is AppShell's opinionated "global top bar" layout. It is a thin wrapper over [`SidebarLayout`](./sidebar-layout.md) that wires together:
11
+
12
+ - an app-wide header above both the sidebar and the content area
13
+ - a default sidebar with its own header removed
14
+ - icon-rail collapse behavior
15
+ - the stock content container and outlet
16
+
17
+ Use it when you want the whole app to share one top bar. Reach for `SidebarLayout` directly when you need the more flexible primitive.
18
+
19
+ ## Import
20
+
21
+ ```tsx
22
+ import { GlobalHeaderLayout } from "@tailor-platform/app-shell";
23
+ ```
24
+
25
+ ## Basic usage
26
+
27
+ ```tsx
28
+ import {
29
+ AppShell,
30
+ AppearanceSwitcher,
31
+ GlobalHeaderLayout,
32
+ SidebarGroup,
33
+ SidebarItem,
34
+ } from "@tailor-platform/app-shell";
35
+ import { LayersIcon } from "lucide-react";
36
+
37
+ function App() {
38
+ return (
39
+ <AppShell title="Operations" modules={modules}>
40
+ <GlobalHeaderLayout
41
+ header={
42
+ <GlobalHeaderLayout.DefaultHeader actions={[<AppearanceSwitcher key="appearance" />]} />
43
+ }
44
+ sidebar={
45
+ <GlobalHeaderLayout.DefaultSidebar>
46
+ <SidebarItem to="/" />
47
+ <SidebarGroup title="Main" icon={<LayersIcon className="size-4" />}>
48
+ <SidebarItem to="/dashboard" />
49
+ <SidebarItem to="/orders" />
50
+ </SidebarGroup>
51
+ </GlobalHeaderLayout.DefaultSidebar>
52
+ }
53
+ />
54
+ </AppShell>
55
+ );
56
+ }
57
+ ```
58
+
59
+ ## Props
60
+
61
+ | Prop | Type | Default | Description |
62
+ | ------------- | --------------------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------- |
63
+ | `header` | `React.ReactNode` | `<GlobalHeaderLayout.DefaultHeader />` | Replaces the full-width app header. |
64
+ | `sidebar` | `React.ReactNode` | `<GlobalHeaderLayout.DefaultSidebar />` | Replaces the primary sidebar. |
65
+ | `defaultOpen` | `boolean` | `true` | Whether the sidebar starts expanded on desktop. |
66
+ | `collapsible` | `boolean` | `true` | Whether the sidebar can be collapsed. |
67
+ | `children` | `(props: { Outlet: () => React.ReactNode }) => React.ReactNode` | current route outlet | Custom content renderer for the stock content container. |
68
+ | `body` | `React.ReactNode` | - | Escape hatch that replaces the entire region beside the sidebar. |
69
+
70
+ `children` and `body` are mutually exclusive. Use `body` when you need to add your own side columns around the main content.
71
+
72
+ ## Built-in parts
73
+
74
+ `GlobalHeaderLayout` exposes the same composition pieces as `SidebarLayout`, plus the opinionated header and sidebar defaults:
75
+
76
+ | Export | Description |
77
+ | ------------------------------------- | -------------------------------------------------------------------------------------------------------- |
78
+ | `GlobalHeaderLayout.DefaultHeader` | The app-wide header (`GlobalHeader`) with app title + breadcrumb on the left and `actions` on the right. |
79
+ | `GlobalHeaderLayout.DefaultSidebar` | `DefaultSidebar` with `hideHeader` and `iconRail` forced on. |
80
+ | `GlobalHeaderLayout.ContentContainer` | The stock main content column. |
81
+ | `GlobalHeaderLayout.Outlet` | The current route outlet. |
82
+ | `GlobalHeaderLayout.Trigger` | The built-in sidebar toggle button. |
83
+ | `GlobalHeaderLayout.Breadcrumb` | The route-driven breadcrumb component (`DynamicBreadcrumb`). |
84
+
85
+ ## `GlobalHeader`
86
+
87
+ The layout's default header is also exported as a top-level `GlobalHeader` component. It renders:
88
+
89
+ - the AppShell `title` and optional `icon`
90
+ - the route-driven breadcrumb
91
+ - a right-aligned `actions` cluster
92
+
93
+ `actions` behaves the same way as on `SidebarLayout.DefaultHeader`: omitting it renders the built-in [`AppearanceSwitcher`](./appearance-switcher.md), and passing it replaces the entire right-hand cluster.
94
+
95
+ ```tsx
96
+ import { AppearanceSwitcher, GlobalHeader } from "@tailor-platform/app-shell";
97
+
98
+ <GlobalHeader actions={[<AppearanceSwitcher key="appearance" />]} />;
99
+ ```
100
+
101
+ ## Owning the body region
102
+
103
+ Use `body` when you want custom columns beside the content while still keeping the global header and icon-rail sidebar.
104
+
105
+ ```tsx
106
+ <GlobalHeaderLayout
107
+ body={
108
+ <>
109
+ <GlobalHeaderLayout.ContentContainer>
110
+ <GlobalHeaderLayout.Outlet />
111
+ </GlobalHeaderLayout.ContentContainer>
112
+ <aside className="w-96 shrink-0 overflow-y-auto border-l">
113
+ <AssistantPanel />
114
+ </aside>
115
+ </>
116
+ }
117
+ />
118
+ ```
119
+
120
+ ## When to use which layout
121
+
122
+ | Goal | Use |
123
+ | ------------------------------------------------------ | ------------------------------------------------------------- |
124
+ | Built-in sidebar + content header | [`SidebarLayout`](./sidebar-layout.md) |
125
+ | Full-width app header above the whole shell | `GlobalHeaderLayout` |
126
+ | Completely custom page structure with AppShell routing | `SidebarLayout body={...}` or `GlobalHeaderLayout body={...}` |
127
+
128
+ ## Related
129
+
130
+ - [SidebarLayout](./sidebar-layout.md) - lower-level primitive that GlobalHeaderLayout wraps
131
+ - [DefaultSidebar](./default-sidebar.md) - underlying sidebar component used by `GlobalHeaderLayout.DefaultSidebar`
132
+ - [DefaultHeader](./default-header.md) - content-column header for `SidebarLayout`