@loykin/designkit 0.0.3 → 0.0.5

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.
@@ -4,6 +4,8 @@ This is the normative implementation contract for a tabbed administrative table
4
4
 
5
5
  The Playground's **Guides / Resource Management / Managed Table** screen is the executable end-to-end reference. Its records are sample data; the pattern is defined by the table responsibilities and template composition below, not by that data domain. If an implementation differs from this contract, revise the implementation rather than inventing a local layout.
6
6
 
7
+ Read [AI UI Implementation Contract](./ai-ui-implementation-contract.md) first.
8
+
7
9
  ## Pattern identity and reference registry
8
10
 
9
11
  - Pattern ID: `managed-table`
@@ -16,10 +18,10 @@ The Playground's **Guides / Resource Management / Managed Table** screen is the
16
18
 
17
19
  | Status | Playground reference | Role in this pattern |
18
20
  | -------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- |
19
- | Executable pattern | `Guides / Resource Management / Managed Table` | Complete list, tabs, queries, create route, form, pagination, and concise detail Sheet |
21
+ | Executable pattern | `Guides / Resource Management / Managed Table` | Complete list, tabs, queries, create route, form, pagination, and concise detail SidePanel |
20
22
  | 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 |
22
- | Supporting reference | `DetailBodyTemplate / Detail / Record` | Full-page destination when detail outgrows a Sheet |
23
+ | Supporting contract | `Guides / Forms / Stacked Form` | Create/edit form spacing, modular Groups, and action alignment |
24
+ | Supporting reference | `DetailBodyTemplate / Detail / Record` | Full-page destination when detail outgrows a SidePanel |
23
25
  | Supporting reference | `FormWizardBodyTemplate / Wizard` | Multi-step destination when create/edit outgrows a stacked form |
24
26
  | Counterexample | `DashboardBodyTemplate / Dashboard` | Monitoring panels are not an administrative table |
25
27
  | Counterexample | `BrowseBodyTemplate / Browse` | Consumer discovery is not an administrative resource list |
@@ -32,8 +34,8 @@ Only the entry marked **Executable pattern** implements this pattern end to end.
32
34
  | -------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
33
35
  | Collection list, tabs, search, filters, pagination | `DataBodyTemplate` + `DataBodyTemplate.Resource` | `Guides / Resource Management / Managed Table` |
34
36
  | Base table behavior | GridKit `DataGrid` | `DataBodyTemplate / Table / Standard` |
35
- | Simple create/edit route | stacked `DataBodyTemplate.Group` | `DataBodyTemplate / Form / Stacked` |
36
- | Concise read-only inspection | `Sheet` | `Managed Table` row detail |
37
+ | Simple create/edit route | stacked `DataBodyTemplate.Group` | `Guides / Forms / Stacked Form` |
38
+ | Concise read-only inspection | BaseKit `SidePanel` (`@loykin/side-panel`) | `Managed Table` row detail |
37
39
  | Complex full-page detail route | `DetailBodyTemplate` | `DetailBodyTemplate / Detail / Record` |
38
40
  | Multi-step create/edit route | `FormWizardBodyTemplate` | `FormWizardBodyTemplate / Wizard` |
39
41
  | Destructive confirmation | `AlertDialog` | UI primitive contract |
@@ -72,7 +74,7 @@ This guide covers three destinations for any managed resource:
72
74
 
73
75
  ```text
74
76
  /<resources>
75
- ├─ select row → concise read-only detail Sheet
77
+ ├─ select row → concise read-only detail SidePanel
76
78
  ├─ create action → /<resources>/new create page
77
79
  └─ edit action → /<resources>/:resourceId/edit page
78
80
  ```
@@ -84,12 +86,12 @@ It does not define every `DataBodyTemplate` use case. Settings, dashboards, long
84
86
  | User intent | Default destination | Reason |
85
87
  | ------------------------------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------- |
86
88
  | Browse, search, filter, paginate | Page | Stable, linkable working context |
87
- | Inspect concise read-only row information | Sheet | Preserves list context |
89
+ | Inspect concise read-only row information | SidePanel | Preserves list context |
88
90
  | Create or edit an entity | Page with its own URL | Space for validation, permissions, responsive layout, and future fields |
89
- | Inspect long, editable, multi-section, permission-sensitive, or linkable detail | Page with its own URL | Sheet constraints are no longer appropriate |
91
+ | Inspect long, editable, multi-section, permission-sensitive, or linkable detail | Page with its own URL | SidePanel constraints are no longer appropriate |
90
92
  | Confirm a destructive action or collect one narrowly scoped value | Modal | Short, blocking decision |
91
93
 
92
- A Sheet is not the default form container. Do not put general create or edit forms in a Sheet merely because the trigger originates in a table.
94
+ A SidePanel is not the default form container. Do not put general create or edit forms in a SidePanel merely because the trigger originates in a table.
93
95
 
94
96
  ## Route and page hierarchy
95
97
 
@@ -156,7 +158,7 @@ Each tab must be a separate React component and own all state that affects its r
156
158
  - TanStack Query and query key
157
159
  - Search and filters
158
160
  - Pagination
159
- - Row selection and concise detail Sheet
161
+ - Row selection and concise detail SidePanel
160
162
  - Mutations that belong to the list itself
161
163
 
162
164
  Query keys include every server-state input:
@@ -302,7 +304,7 @@ Grid rules:
302
304
 
303
305
  ## Create and edit form page
304
306
 
305
- Create and edit forms use the established Form / Stacked composition. Preserve the template-owned page width and padding.
307
+ 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
308
 
307
309
  ```tsx
308
310
  function UserCreatePage() {
@@ -332,43 +334,21 @@ function UserCreatePage() {
332
334
  description="Identity and access settings for the new account."
333
335
  >
334
336
  <form className="space-y-3" onSubmit={handleSubmit}>
335
- <div className="space-y-1.5">
336
- <Label htmlFor="user-name" className="text-xs">
337
- Name
338
- </Label>
337
+ <FormField label="Name" htmlFor="user-name">
339
338
  <Input id="user-name" className="h-8 text-sm" required />
340
- </div>
341
- <div className="space-y-1.5">
342
- <Label htmlFor="user-email" className="text-xs">
343
- Email
344
- </Label>
339
+ </FormField>
340
+ <FormField label="Email" htmlFor="user-email">
345
341
  <Input id="user-email" type="email" className="h-8 text-sm" required />
346
- </div>
347
- <div className="space-y-1.5">
348
- <Label htmlFor="user-role" className="text-xs">
349
- Role
350
- </Label>
342
+ </FormField>
343
+ <FormField label="Role" htmlFor="user-role">
351
344
  <Select defaultValue="Viewer">
352
345
  <SelectTrigger id="user-role" className="h-8 text-sm">
353
346
  <SelectValue />
354
347
  </SelectTrigger>
355
348
  <SelectContent>{/* roles */}</SelectContent>
356
349
  </Select>
357
- </div>
358
- <div className="flex justify-end gap-2">
359
- <Button
360
- type="button"
361
- variant="outline"
362
- size="sm"
363
- className="h-8 text-xs"
364
- onClick={() => navigate('/users')}
365
- >
366
- Cancel
367
- </Button>
368
- <Button type="submit" size="sm" className="h-8 text-xs">
369
- Create user
370
- </Button>
371
- </div>
350
+ </FormField>
351
+ <FormActions onCancel={() => navigate('/users')} submitLabel="Create user" />
372
352
  </form>
373
353
  </DataBodyTemplate.Group>
374
354
  </DataBodyTemplate>
@@ -379,28 +359,57 @@ function UserCreatePage() {
379
359
  Form rules:
380
360
 
381
361
  - Form controls and buttons are `32px` (`h-8`); this differs from the table toolbar's `28px` controls.
382
- - Use `space-y-3` for the form and `space-y-1.5` within each field.
362
+ - Wrap each field in `FormField`; use `space-y-3` between fields inside the form.
363
+ - End the form in `FormActions`; it already renders the divider and Cancel-before-submit order — do not add a second divider around it.
383
364
  - Use the full content width supplied by the stacked group.
384
- - Do not add `mx-auto`, an arbitrary `max-w-*`, extra horizontal padding, a nested card, or a second action divider.
365
+ - Do not add `mx-auto`, an arbitrary `max-w-*`, extra horizontal padding, or a nested card.
385
366
  - The form page owns its mutation. After success, invalidate the narrowest affected queries and navigate to the list or created entity.
386
367
 
387
- ## Concise detail Sheet
368
+ ## Concise detail SidePanel
388
369
 
389
- Keep the list mounted while showing concise, mostly read-only row information:
370
+ Keep the list mounted while showing concise, mostly read-only row information. Use BaseKit `SidePanelProvider`/`useSidePanel` (`@loykin/side-panel`) — the same package the Kubernetes Workspace guide uses — with DesignKit `PanelTemplate` for the panel body: `title`/`eyebrow` for the header, `PanelTemplate.Section` + `PanelTemplate.Row` for read-only fields, and a `Close` button in `footer` calling `close()`. Do not use DesignKit's own `Sheet` primitive for this, and do not hand-roll a label/value grid with raw `div`/`p` markup.
390
371
 
391
372
  ```tsx
392
- <Sheet open={Boolean(selectedUser)} onOpenChange={(open) => !open && clearSelection()}>
393
- <SheetContent>
394
- <SheetHeader>
395
- <SheetTitle>{selectedUser?.name}</SheetTitle>
396
- <SheetDescription>User details</SheetDescription>
397
- </SheetHeader>
398
- {/* concise read-only fields */}
399
- </SheetContent>
400
- </Sheet>
373
+ function UserPanel({ user }: { user: SelectedUser }) {
374
+ const { close } = useSidePanel()
375
+ return (
376
+ <PanelTemplate
377
+ eyebrow="User"
378
+ title={user.name}
379
+ footer={
380
+ <div className="flex justify-end">
381
+ <Button variant="outline" size="sm" onClick={() => void close()}>
382
+ Close
383
+ </Button>
384
+ </div>
385
+ }
386
+ >
387
+ <PanelTemplate.Section title="Details">
388
+ <dl className="space-y-2">
389
+ <PanelTemplate.Row label="Email">{user.email}</PanelTemplate.Row>
390
+ <PanelTemplate.Row label="Role">{user.role}</PanelTemplate.Row>
391
+ </dl>
392
+ </PanelTemplate.Section>
393
+ </PanelTemplate>
394
+ )
395
+ }
396
+
397
+ function UsersListPage() {
398
+ const { open } = useSidePanel()
399
+ return (
400
+ <SidePanelProvider className="h-full min-h-0">
401
+ <DataBodyTemplate>
402
+ <DataGrid
403
+ /* … */
404
+ onRowClick={(user) => open(<UserPanel user={user} />, { side: 'right', size: 420, resizable: true })}
405
+ />
406
+ </DataBodyTemplate>
407
+ </SidePanelProvider>
408
+ )
409
+ }
401
410
  ```
402
411
 
403
- If the detail gains editing, multiple sections, complex permissions, a long history, or a need for a shareable URL, replace the Sheet with a detail page. Do not gradually turn the Sheet into a full page inside an overlay.
412
+ If the detail gains editing, multiple sections, complex permissions, a long history, or a need for a shareable URL, replace the SidePanel with a detail page. Do not gradually turn the SidePanel into a full page inside an overlay.
404
413
 
405
414
  ## Public API boundary
406
415
 
@@ -429,7 +438,9 @@ Do not import package-internal source paths. Do not add TanStack Query, React Ro
429
438
  - [ ] Table toolbar and pagination controls are `28px` high.
430
439
  - [ ] Form controls and buttons are `32px` high.
431
440
  - [ ] The stacked form has no arbitrary max-width wrapper or extra action divider.
432
- - [ ] Create and edit use pages; concise read-only detail uses a Sheet.
441
+ - [ ] The create/edit form uses `FormField` and `FormActions` (see the Stacked Form guide), not hand-rolled label/input or action-row markup.
442
+ - [ ] Create and edit use pages; concise read-only detail uses a SidePanel.
443
+ - [ ] The SidePanel body uses `PanelTemplate` (`title`/`eyebrow` + `PanelTemplate.Section`/`PanelTemplate.Row`), not hand-rolled label/value markup, and DesignKit's own `Sheet` primitive is not used for this.
433
444
  - [ ] Complex detail uses a page.
434
445
  - [ ] Only public package entry points are imported.
435
446
  - [ ] The experience is visually checked in both Sidebar and Header shells.
@@ -14,6 +14,32 @@
14
14
  ],
15
15
  "contract": "managed-table.md"
16
16
  },
17
+ {
18
+ "id": "kubernetes-workspace",
19
+ "title": "Operations / Kubernetes Workspace",
20
+ "summary": "Operational resource collection with quick SidePanel inspection and persistent logs, shell, and events tools in a bottom dock.",
21
+ "useWhen": [
22
+ "Kubernetes operations",
23
+ "resource inspection",
24
+ "logs and shell",
25
+ "persistent tool dock"
26
+ ],
27
+ "templates": ["DataBodyTemplate", "PanelTemplate"],
28
+ "playgroundPaths": [
29
+ "/sidebar/kubernetes-workspace-guide",
30
+ "/header/kubernetes-workspace-guide"
31
+ ],
32
+ "contract": "kubernetes-workspace.md"
33
+ },
34
+ {
35
+ "id": "form-workflow",
36
+ "title": "Forms / Stacked Form",
37
+ "summary": "Canonical stacked page structure and modular section boundaries for create, edit, and settings forms.",
38
+ "useWhen": ["create form", "edit form", "settings form", "multiple form sections"],
39
+ "templates": ["DataBodyTemplate"],
40
+ "playgroundPaths": ["/sidebar/form-workflow-guide", "/header/form-workflow-guide"],
41
+ "contract": "form-workflow.md"
42
+ },
17
43
  {
18
44
  "id": "publishing-workflow",
19
45
  "title": "Publishing / Blog → Article",
@@ -12,6 +12,8 @@ Use this guide to build a publishing experience where a content collection leads
12
12
 
13
13
  The existing **Blog Feed** and **Article** template demos are visual references only. This Guide is the supported example of how those page shapes connect.
14
14
 
15
+ Read [AI UI Implementation Contract](./ai-ui-implementation-contract.md) first. It governs how this Guide's card grid and article content are built.
16
+
15
17
  ## Route contract
16
18
 
17
19
  ```text
@@ -37,6 +39,18 @@ Card selection uses React Router navigation. Browser Back returns to the collect
37
39
  - Article actions such as Save or Share belong in the article header.
38
40
  - Render one page-level template per route. Never put `DetailBodyTemplate` inside `DataBodyTemplate.Body`.
39
41
 
42
+ ## Card and article presentation
43
+
44
+ Do not hand-roll the card grid or article body with raw `<div>`/Tailwind. Use these DesignKit components:
45
+
46
+ - `ArticleCardPreview` — the whole grid card: cover, category, title, excerpt, byline, read time. Pass `coverContent` for per-listing decoration (an icon, an overlay); do not reassemble the card shape from `InteractiveCard` + `ArticleCover` + `ArticleByline` by hand — that duplication is exactly why this component exists (it was copy-pasted near-verbatim between this guide and the Blog Feed demo before being promoted).
47
+ - `ArticleCover` / `ArticleByline` — the cover and byline building blocks `ArticleCardPreview` uses internally. Reach for them directly only outside the card shape, e.g. the article page's hero (`ArticleCover`) and header (`ArticleByline`). `tone` is a semantic value (`'violet' | 'emerald' | 'amber' | 'rose' | 'sky' | 'slate'`), not a raw gradient class string — sample data stores `accent: ArticleTone`, never a Tailwind string.
48
+ - `ArticleBody` — reading-width container with article typography for the article's prose.
49
+ - `ArticleBodySkeleton` — loading placeholder matching `ArticleBody`'s width.
50
+ - `ArticleToc` — the article aside's table of contents.
51
+
52
+ Article title, excerpt, and category stay as plain content passed into these components; only the reusable shell moves into DesignKit.
53
+
40
54
  ## Deterministic ordering
41
55
 
42
56
  The mock API returns articles newest first and the GridKit collection declares the same initial published-date sort. In a real API, send sort parameters to the server and include them in the query key. Never sort humanized labels such as “2 hours ago”.
@@ -51,3 +65,6 @@ The mock API returns articles newest first and the GridKit collection declares t
51
65
  - [ ] Initial loading is local to the destination content
52
66
  - [ ] Existing content remains visible during background refetch
53
67
  - [ ] No nested page templates and no article Sheet
68
+ - [ ] The grid card uses `ArticleCardPreview`, not a hand-assembled `InteractiveCard` + `ArticleCover` + `ArticleByline` composition
69
+ - [ ] Article page presentation uses `ArticleBody`/`ArticleBodySkeleton` and `ArticleToc`, not hand-rolled `className` blocks
70
+ - [ ] Sample data stores a semantic `accent`/tone value, never a Tailwind gradient string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loykin/designkit",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A React UI component library with theming support.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -49,6 +49,7 @@
49
49
  "devDependencies": {
50
50
  "@eslint/js": "^10.0.1",
51
51
  "@loykin/gridkit": "^0.2.2",
52
+ "@tanstack/react-table": "^8.21.3",
52
53
  "@typescript/native": "npm:typescript@~7.0.2",
53
54
  "@types/node": "^26.0.0",
54
55
  "@types/react": "^19.2.14",