@loykin/designkit 0.0.3 → 0.0.4

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.
package/README.md CHANGED
@@ -11,9 +11,16 @@ loading behavior, action placement, and the correct page templates.
11
11
  | Product workflow | Guide ID | Connected destinations |
12
12
  | ------------------- | --------------------- | -------------------------------------- |
13
13
  | Administrative CRUD | `managed-table` | table → detail Sheet; create/edit page |
14
+ | Forms | `form-workflow` | create, edit, and settings pages |
14
15
  | Publishing | `publishing-workflow` | blog collection → article route |
15
16
  | Commerce | `commerce-workflow` | filtered catalog → product route |
16
17
 
18
+ The executable **Forms / Stacked Form** Playground guide uses React Hook Form
19
+ to demonstrate shared state, controlled DesignKit inputs, validation, and
20
+ modular section components. React Hook Form is installed in the Playground
21
+ only; it is not a dependency or peer dependency of `@loykin/designkit` and is
22
+ not required to preserve the form design contract.
23
+
17
24
  List the guides included with the installed package:
18
25
 
19
26
  ```bash
@@ -24,6 +31,7 @@ Print a complete contract for a developer or coding agent:
24
31
 
25
32
  ```bash
26
33
  npx @loykin/designkit guide managed-table
34
+ npx @loykin/designkit guide form-workflow --prompt
27
35
  npx @loykin/designkit guide publishing-workflow --prompt
28
36
  npx @loykin/designkit guide commerce-workflow
29
37
  ```
@@ -5,6 +5,7 @@ The Playground's **Guides** group contains complete product flows. Existing temp
5
5
  | Workflow | Pattern ID | Connected destinations | Contract |
6
6
  | ------------------- | --------------------- | -------------------------------------- | ----------------------------------------------- |
7
7
  | Resource Management | `managed-table` | table → detail Sheet; create/edit page | [Managed Table](./managed-table.md) |
8
+ | Forms | `form-workflow` | create, edit, and settings pages | [Stacked Form](./form-workflow.md) |
8
9
  | Publishing | `publishing-workflow` | blog collection → article route | [Publishing Workflow](./publishing-workflow.md) |
9
10
  | Commerce | `commerce-workflow` | filtered catalog → product route | [Commerce Workflow](./commerce-workflow.md) |
10
11
 
@@ -0,0 +1,212 @@
1
+ # Stacked Form Workflow Contract for AI
2
+
3
+ Use this contract for create, edit, and settings forms. It standardizes the visual structure and component boundaries. It does not prescribe a form-state, validation, routing, or data-fetching library.
4
+
5
+ ## Canonical reference
6
+
7
+ - Pattern ID: `form-workflow`
8
+ - Playground: **Guides / Forms / Stacked Form**
9
+ - Page template: `DataBodyTemplate`
10
+ - Section primitive: `DataBodyTemplate.Group layout="stacked"`
11
+
12
+ The existing **DataBodyTemplate / Form / Horizontal**, **Stacked**, and **Inline** entries are visual API references. When generating a product form, follow this guide instead of selecting among those demos.
13
+
14
+ The executable Playground example uses React Hook Form to demonstrate shared state across modular section components, controlled DesignKit inputs, and field-level errors. React Hook Form is a Playground implementation choice, not a DesignKit dependency or part of this visual contract.
15
+
16
+ ## Non-negotiable visual contract
17
+
18
+ 1. Render the form as a full route page by default. Do not use a Sheet for ordinary create, edit, or settings work.
19
+ 2. Keep the route hierarchy in `PageTopBar`: for example, `Resources / Members / Add member`.
20
+ 3. Use one `DataBodyTemplate` page root.
21
+ 4. Use the stacked form shape for create, edit, and settings pages. Do not switch to `horizontal` or `inline` because the domain changed.
22
+ 5. Divide the form into one or more `DataBodyTemplate.Group layout="stacked"` sections according to meaning, not visual preference.
23
+ 6. Keep field spacing at `space-y-3`; keep each label/control/help block at `space-y-1.5`.
24
+ 7. Use DesignKit controls and the established compact control sizing: `h-8 text-sm` for inputs and selects, `size="sm"` with `h-8 text-xs` for actions.
25
+ 8. Put Cancel before the primary submit action. Keep the action cluster right-aligned at the bottom of its save boundary.
26
+ 9. Show validation text directly below its field. Show form-level failure above the bottom actions, inside the same form boundary.
27
+ 10. A submitting, refreshing, or validation state must not replace or flash the page header or unrelated groups.
28
+
29
+ ## Module boundary contract
30
+
31
+ Treat each semantic group like tab-scoped content: implement it as a named component instead of one large page function.
32
+
33
+ - The route component owns only `DataBodyTemplate`, breadcrumb, title, and route-level description.
34
+ - A form component owns the submit boundary and bottom actions.
35
+ - Each section component returns one stacked `DataBodyTemplate.Group`.
36
+ - Section-only state and asynchronous work stay in that section component when possible.
37
+ - Shared form state may be provided by the form component through props, context, React Hook Form, or another application-selected mechanism.
38
+ - Do not lift section-only pending, error, or refresh state into the page header.
39
+ - Do not nest another page-level template inside the form or a Group.
40
+
41
+ Separating components is a behavior boundary, not permission to change their layout. Every section still uses the same stacked Group contract.
42
+
43
+ ## Save boundaries
44
+
45
+ The visual system remains the same; only form ownership changes.
46
+
47
+ | Workflow | Form ownership | Actions |
48
+ | -------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------- |
49
+ | Create or edit one record | One form owns all stacked sections | One bottom action row after the final section |
50
+ | Settings saved as one document | One form owns all stacked sections | One bottom action row after the final section |
51
+ | Settings with independently saved categories | Each section component owns its own form | Each stacked section ends with its own right-aligned action row |
52
+
53
+ Do not create separate forms merely because the screen contains multiple Groups. Split forms only when the server-side save boundaries are genuinely independent.
54
+
55
+ ## Canonical composition
56
+
57
+ The following is the concrete React Hook Form composition used by the Playground. Applications may replace React Hook Form, but must preserve the same component and visual boundaries.
58
+
59
+ ```tsx
60
+ import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form'
61
+
62
+ interface MemberFormValues {
63
+ name: string
64
+ email: string
65
+ role: string
66
+ active: boolean
67
+ }
68
+
69
+ function IdentitySection() {
70
+ const {
71
+ register,
72
+ formState: { errors },
73
+ } = useFormContext<MemberFormValues>()
74
+
75
+ return (
76
+ <DataBodyTemplate.Group
77
+ layout="stacked"
78
+ title="Identity"
79
+ description="Basic account information."
80
+ >
81
+ <div className="space-y-3">
82
+ <div className="space-y-1.5">
83
+ <Label htmlFor="member-name" className="text-xs">
84
+ Name
85
+ </Label>
86
+ <Input
87
+ id="member-name"
88
+ {...register('name', { required: 'Enter a member name.' })}
89
+ aria-invalid={Boolean(errors.name)}
90
+ className="h-8 text-sm"
91
+ />
92
+ {errors.name && <p className="text-xs text-destructive">{errors.name.message}</p>}
93
+ </div>
94
+ </div>
95
+ </DataBodyTemplate.Group>
96
+ )
97
+ }
98
+
99
+ function AccessSection() {
100
+ const { control } = useFormContext<MemberFormValues>()
101
+
102
+ return (
103
+ <DataBodyTemplate.Group
104
+ layout="stacked"
105
+ title="Role & access"
106
+ description="Default workspace permissions."
107
+ >
108
+ <div className="space-y-3">
109
+ <div className="space-y-1.5">
110
+ <Label htmlFor="member-role" className="text-xs">
111
+ Role
112
+ </Label>
113
+ <Controller
114
+ control={control}
115
+ name="role"
116
+ render={({ field }) => (
117
+ <Select value={field.value} onValueChange={field.onChange}>
118
+ <SelectTrigger id="member-role" className="h-8 text-sm">
119
+ <SelectValue />
120
+ </SelectTrigger>
121
+ <SelectContent>{/* roles */}</SelectContent>
122
+ </Select>
123
+ )}
124
+ />
125
+ </div>
126
+ <Controller
127
+ control={control}
128
+ name="active"
129
+ render={({ field }) => <Switch checked={field.value} onCheckedChange={field.onChange} />}
130
+ />
131
+ </div>
132
+ </DataBodyTemplate.Group>
133
+ )
134
+ }
135
+
136
+ function MemberForm() {
137
+ const form = useForm<MemberFormValues>({
138
+ defaultValues: {
139
+ name: '',
140
+ email: '',
141
+ role: 'viewer',
142
+ active: true,
143
+ },
144
+ })
145
+
146
+ const submitMember = (values: MemberFormValues) => {
147
+ // Application-owned mutation or submit behavior.
148
+ }
149
+
150
+ return (
151
+ <FormProvider {...form}>
152
+ <form className="contents" onSubmit={form.handleSubmit(submitMember)}>
153
+ <IdentitySection />
154
+ <AccessSection />
155
+ <div className="flex justify-end gap-2 border-t border-border pt-(--designkit-panel-gap)">
156
+ <Button type="button" variant="outline" size="sm" className="h-8 text-xs">
157
+ Cancel
158
+ </Button>
159
+ <Button type="submit" size="sm" className="h-8 text-xs">
160
+ Save
161
+ </Button>
162
+ </div>
163
+ </form>
164
+ </FormProvider>
165
+ )
166
+ }
167
+
168
+ export function MemberCreatePage() {
169
+ return (
170
+ <DataBodyTemplate
171
+ topBar={<PageTopBar left="Resources / Members / Add member" />}
172
+ title="Add member"
173
+ description="Create a workspace member."
174
+ >
175
+ <MemberForm />
176
+ </DataBodyTemplate>
177
+ )
178
+ }
179
+ ```
180
+
181
+ React Hook Form is used here to prove that independently implemented Group components can share one form boundary. It remains a Playground-only dependency. Replacing it with ordinary React state, Formik, or another form tool must not change the rendered structure.
182
+
183
+ ## Settings with independent saves
184
+
185
+ When settings categories save independently, retain the same stacked appearance and move each form boundary into its section component.
186
+
187
+ ```tsx
188
+ function ProfileSettingsSection() {
189
+ return (
190
+ <DataBodyTemplate.Group layout="stacked" title="Profile">
191
+ <form className="space-y-3" onSubmit={saveProfile}>
192
+ {/* profile fields */}
193
+ <div className="flex justify-end">
194
+ <Button type="submit" size="sm" className="h-8 text-xs">
195
+ Save profile
196
+ </Button>
197
+ </div>
198
+ </form>
199
+ </DataBodyTemplate.Group>
200
+ )
201
+ }
202
+ ```
203
+
204
+ ## Review checklist
205
+
206
+ - Create, edit, and settings screens all use the stacked shape.
207
+ - The page is a route rather than a Sheet unless the task explicitly requires a constrained secondary edit.
208
+ - Breadcrumb, title, Groups, fields, and actions follow the same vertical order.
209
+ - Groups are named components and unrelated section state is not owned by the route header.
210
+ - Group count follows the information model; it does not select a different layout.
211
+ - Form and validation libraries remain application choices.
212
+ - Bottom actions belong to the form that they submit.
@@ -18,7 +18,7 @@ The Playground's **Guides / Resource Management / Managed Table** screen is the
18
18
  | -------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- |
19
19
  | Executable pattern | `Guides / Resource Management / Managed Table` | Complete list, tabs, queries, create route, form, pagination, and concise detail Sheet |
20
20
  | Supporting reference | `DataBodyTemplate / Table / Standard` | Base GridKit table composition and table sizing |
21
- | Supporting reference | `DataBodyTemplate / Form / Stacked` | Create/edit form spacing, padding, and action alignment |
21
+ | Supporting contract | `Guides / Forms / Stacked Form` | Create/edit form spacing, modular Groups, and action alignment |
22
22
  | Supporting reference | `DetailBodyTemplate / Detail / Record` | Full-page destination when detail outgrows a Sheet |
23
23
  | Supporting reference | `FormWizardBodyTemplate / Wizard` | Multi-step destination when create/edit outgrows a stacked form |
24
24
  | Counterexample | `DashboardBodyTemplate / Dashboard` | Monitoring panels are not an administrative table |
@@ -32,7 +32,7 @@ Only the entry marked **Executable pattern** implements this pattern end to end.
32
32
  | -------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
33
33
  | Collection list, tabs, search, filters, pagination | `DataBodyTemplate` + `DataBodyTemplate.Resource` | `Guides / Resource Management / Managed Table` |
34
34
  | Base table behavior | GridKit `DataGrid` | `DataBodyTemplate / Table / Standard` |
35
- | Simple create/edit route | stacked `DataBodyTemplate.Group` | `DataBodyTemplate / Form / Stacked` |
35
+ | Simple create/edit route | stacked `DataBodyTemplate.Group` | `Guides / Forms / Stacked Form` |
36
36
  | Concise read-only inspection | `Sheet` | `Managed Table` row detail |
37
37
  | Complex full-page detail route | `DetailBodyTemplate` | `DetailBodyTemplate / Detail / Record` |
38
38
  | Multi-step create/edit route | `FormWizardBodyTemplate` | `FormWizardBodyTemplate / Wizard` |
@@ -302,7 +302,7 @@ Grid rules:
302
302
 
303
303
  ## Create and edit form page
304
304
 
305
- Create and edit forms use the established Form / Stacked composition. Preserve the template-owned page width and padding.
305
+ Create and edit forms follow the canonical `form-workflow` contract in **Guides / Forms / Stacked Form**. Preserve the template-owned page width and padding, and split semantic Groups into named section components.
306
306
 
307
307
  ```tsx
308
308
  function UserCreatePage() {
@@ -14,6 +14,15 @@
14
14
  ],
15
15
  "contract": "managed-table.md"
16
16
  },
17
+ {
18
+ "id": "form-workflow",
19
+ "title": "Forms / Stacked Form",
20
+ "summary": "Canonical stacked page structure and modular section boundaries for create, edit, and settings forms.",
21
+ "useWhen": ["create form", "edit form", "settings form", "multiple form sections"],
22
+ "templates": ["DataBodyTemplate"],
23
+ "playgroundPaths": ["/sidebar/form-workflow-guide", "/header/form-workflow-guide"],
24
+ "contract": "form-workflow.md"
25
+ },
17
26
  {
18
27
  "id": "publishing-workflow",
19
28
  "title": "Publishing / Blog → Article",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loykin/designkit",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "A React UI component library with theming support.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",