@xferops/design-guide 0.2.1
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 +30 -0
- package/dist/chunk-R4AEJOIP.js +161 -0
- package/dist/chunk-R4AEJOIP.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +246 -0
- package/dist/server.js.map +1 -0
- package/guides/feedback-status-states.md +107 -0
- package/guides/forms-field-input.md +124 -0
- package/guides/foundations-app-setup.md +83 -0
- package/guides/foundations-token-theming.md +125 -0
- package/guides/navigation-shell.md +124 -0
- package/guides/overlays-actions.md +134 -0
- package/guides/primitives-icons-actions.md +77 -0
- package/guides/primitives-layout-content.md +96 -0
- package/guides/table-sorting.md +156 -0
- package/package.json +42 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: primitives/layout-content
|
|
3
|
+
title: Layout and Content Primitives
|
|
4
|
+
summary: Compose Box, Stack, Text, Card, List, and Avatar into token-driven page sections.
|
|
5
|
+
category: primitives
|
|
6
|
+
slug: layout-content
|
|
7
|
+
tags:
|
|
8
|
+
- primitives
|
|
9
|
+
- layout
|
|
10
|
+
- content
|
|
11
|
+
- box
|
|
12
|
+
- stack
|
|
13
|
+
- text
|
|
14
|
+
- card
|
|
15
|
+
sourcePath: apps/docs/src/App.tsx
|
|
16
|
+
relatedPaths:
|
|
17
|
+
- packages/ui/src/components/Box
|
|
18
|
+
- packages/ui/src/components/Stack
|
|
19
|
+
- packages/ui/src/components/Text
|
|
20
|
+
- packages/ui/src/components/Card
|
|
21
|
+
- packages/ui/src/components/List
|
|
22
|
+
- packages/ui/src/components/Avatar
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Layout and Content Primitives
|
|
26
|
+
|
|
27
|
+
Use the low-level primitives in `@xferops/ui` to build structure before introducing more specialized components.
|
|
28
|
+
|
|
29
|
+
## Import the Core Primitives
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { Avatar, Box, Card, List, Stack, Text } from "@xferops/ui";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Use `Box` for Token-Aligned Containers
|
|
36
|
+
|
|
37
|
+
`Box` is the base surface primitive for padding, border, radius, background, and shadow.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<Box padding="lg" background="surface" border="subtle" radius="lg">
|
|
41
|
+
<Stack gap="sm">
|
|
42
|
+
<Text as="span" weight="semibold">
|
|
43
|
+
Tenant summary
|
|
44
|
+
</Text>
|
|
45
|
+
<Text as="span" size="sm" tone="muted">
|
|
46
|
+
Shared shell container with semantic surface styling.
|
|
47
|
+
</Text>
|
|
48
|
+
</Stack>
|
|
49
|
+
</Box>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Use `Stack` for Spacing and Alignment
|
|
53
|
+
|
|
54
|
+
Keep layout logic in `Stack` rather than custom wrappers for every screen.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Stack gap="lg">
|
|
58
|
+
<Stack direction="row" gap="sm" align="center" wrap>
|
|
59
|
+
<Avatar name="Ada Lovelace" />
|
|
60
|
+
<Avatar name="Grace Hopper" />
|
|
61
|
+
<Avatar fallbackText="+4" />
|
|
62
|
+
</Stack>
|
|
63
|
+
|
|
64
|
+
<Card padding="md" tone="elevated">
|
|
65
|
+
<Stack gap="xs">
|
|
66
|
+
<Text as="span" size="sm" weight="semibold">
|
|
67
|
+
Elevated card
|
|
68
|
+
</Text>
|
|
69
|
+
<Text as="span" size="sm" tone="muted">
|
|
70
|
+
Use cards for grouped content and secondary emphasis.
|
|
71
|
+
</Text>
|
|
72
|
+
</Stack>
|
|
73
|
+
</Card>
|
|
74
|
+
</Stack>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Use `Text` and `List` for Readable Content
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<Stack gap="xs">
|
|
81
|
+
<Text as="span" size="sm" weight="medium">
|
|
82
|
+
Feature checklist
|
|
83
|
+
</Text>
|
|
84
|
+
<List size="sm" spacing="sm">
|
|
85
|
+
<li>Export invoices as CSV</li>
|
|
86
|
+
<li>Apply partner theme overrides</li>
|
|
87
|
+
<li>Invite finance teammates</li>
|
|
88
|
+
</List>
|
|
89
|
+
</Stack>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Notes
|
|
93
|
+
|
|
94
|
+
- `Box` is polymorphic, so switch the `as` prop when the container should be a `section`, `article`, or `aside`.
|
|
95
|
+
- `Text` should carry semantic meaning through `as`, while size, weight, and tone stay visual.
|
|
96
|
+
- `Stack` is the default spacing primitive for rows, columns, and wrapped action groups.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: table/sorting
|
|
3
|
+
title: Sortable Table Setup
|
|
4
|
+
summary: Set up a sortable table using TableSortButton and useTableSort.
|
|
5
|
+
category: table
|
|
6
|
+
slug: sorting
|
|
7
|
+
tags:
|
|
8
|
+
- table
|
|
9
|
+
- sorting
|
|
10
|
+
- accessibility
|
|
11
|
+
- react
|
|
12
|
+
- docs
|
|
13
|
+
sourcePath: apps/docs/src/App.tsx
|
|
14
|
+
relatedPaths:
|
|
15
|
+
- packages/ui/src/components/Table
|
|
16
|
+
- packages/ui/src/hooks/useTableSort.ts
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Sortable Table Setup
|
|
20
|
+
|
|
21
|
+
This guide shows how to build a sortable table using the same pattern used in `apps/docs/src/App.tsx`.
|
|
22
|
+
|
|
23
|
+
## Imports
|
|
24
|
+
|
|
25
|
+
Import the table primitives and the sorting hook from `@xferops/ui`.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import {
|
|
29
|
+
Table,
|
|
30
|
+
TableBody,
|
|
31
|
+
TableCaption,
|
|
32
|
+
TableCell,
|
|
33
|
+
TableHead,
|
|
34
|
+
TableHeaderCell,
|
|
35
|
+
TableRow,
|
|
36
|
+
TableSortButton,
|
|
37
|
+
useTableSort
|
|
38
|
+
} from "@xferops/ui";
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 1. Define Your Row Type
|
|
42
|
+
|
|
43
|
+
Your row type should describe the raw data in the table. Define a separate union for the columns you want to sort.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
type InvoiceRow = {
|
|
47
|
+
customer: string;
|
|
48
|
+
status: string;
|
|
49
|
+
totalCents: number;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type SortColumn = "customer" | "status" | "totalCents";
|
|
53
|
+
|
|
54
|
+
const rows: InvoiceRow[] = [
|
|
55
|
+
{ customer: "Northwind", status: "Paid", totalCents: 120000 },
|
|
56
|
+
{ customer: "Stark Industries", status: "Overdue", totalCents: 489000 },
|
|
57
|
+
{ customer: "Wayne Enterprises", status: "Draft", totalCents: 53000 }
|
|
58
|
+
];
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 2. Initialize `useTableSort`
|
|
62
|
+
|
|
63
|
+
The hook stores the current sort state and returns a sorted copy of your rows.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
const {
|
|
67
|
+
sort,
|
|
68
|
+
sortedRows,
|
|
69
|
+
requestSort,
|
|
70
|
+
getAriaSort
|
|
71
|
+
} = useTableSort<InvoiceRow, SortColumn>({
|
|
72
|
+
rows,
|
|
73
|
+
initialSort: {
|
|
74
|
+
column: "customer",
|
|
75
|
+
direction: "asc"
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
What it gives you:
|
|
81
|
+
|
|
82
|
+
- `sort`: the active sort state, including `column` and `direction`
|
|
83
|
+
- `sortedRows`: a sorted copy of the input rows
|
|
84
|
+
- `requestSort(column)`: toggles sort direction when clicking the active column, or switches to a new column in ascending order
|
|
85
|
+
- `getAriaSort(column)`: returns `"ascending"`, `"descending"`, or `"none"` for accessibility
|
|
86
|
+
|
|
87
|
+
## 3. Build the Table Header
|
|
88
|
+
|
|
89
|
+
Put `aria-sort` on each `TableHeaderCell`. Inside each header cell, render a `TableSortButton`.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<Table striped>
|
|
93
|
+
<TableCaption>Recent invoices</TableCaption>
|
|
94
|
+
<TableHead>
|
|
95
|
+
<TableRow>
|
|
96
|
+
<TableHeaderCell aria-sort={getAriaSort("customer")}>
|
|
97
|
+
<TableSortButton
|
|
98
|
+
active={sort.column === "customer"}
|
|
99
|
+
direction={sort.direction}
|
|
100
|
+
onClick={() => requestSort("customer")}
|
|
101
|
+
>
|
|
102
|
+
Customer
|
|
103
|
+
</TableSortButton>
|
|
104
|
+
</TableHeaderCell>
|
|
105
|
+
|
|
106
|
+
<TableHeaderCell aria-sort={getAriaSort("status")}>
|
|
107
|
+
<TableSortButton
|
|
108
|
+
active={sort.column === "status"}
|
|
109
|
+
direction={sort.direction}
|
|
110
|
+
onClick={() => requestSort("status")}
|
|
111
|
+
>
|
|
112
|
+
Status
|
|
113
|
+
</TableSortButton>
|
|
114
|
+
</TableHeaderCell>
|
|
115
|
+
|
|
116
|
+
<TableHeaderCell aria-sort={getAriaSort("totalCents")}>
|
|
117
|
+
<TableSortButton
|
|
118
|
+
active={sort.column === "totalCents"}
|
|
119
|
+
direction={sort.direction}
|
|
120
|
+
onClick={() => requestSort("totalCents")}
|
|
121
|
+
>
|
|
122
|
+
Total
|
|
123
|
+
</TableSortButton>
|
|
124
|
+
</TableHeaderCell>
|
|
125
|
+
</TableRow>
|
|
126
|
+
</TableHead>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`TableSortButton` displays:
|
|
130
|
+
|
|
131
|
+
- `↕` when the column is not active
|
|
132
|
+
- `↑` when the active column is ascending
|
|
133
|
+
- `↓` when the active column is descending
|
|
134
|
+
|
|
135
|
+
## 4. Render `sortedRows`
|
|
136
|
+
|
|
137
|
+
Render the rows returned by the hook instead of the original array.
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<TableBody>
|
|
141
|
+
{sortedRows.map((invoice) => (
|
|
142
|
+
<TableRow key={invoice.customer}>
|
|
143
|
+
<TableCell>{invoice.customer}</TableCell>
|
|
144
|
+
<TableCell>{invoice.status}</TableCell>
|
|
145
|
+
<TableCell>{formatCurrency(invoice.totalCents)}</TableCell>
|
|
146
|
+
</TableRow>
|
|
147
|
+
))}
|
|
148
|
+
</TableBody>
|
|
149
|
+
</Table>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Notes
|
|
153
|
+
|
|
154
|
+
- `useTableSort` supports `string`, `number`, `boolean`, `Date`, `null`, and `undefined`.
|
|
155
|
+
- When values are `null` or `undefined`, they sort to the bottom in ascending order.
|
|
156
|
+
- If your displayed value differs from the value you want to sort on, use the hook's `getSortValue` option.
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xferops/design-guide",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"xferops-design-guide-mcp": "dist/server.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"guides"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./server": {
|
|
25
|
+
"types": "./dist/server.d.ts",
|
|
26
|
+
"default": "./dist/server.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
|
+
"test": "vitest run"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
36
|
+
"zod": "^4.1.11"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^24.5.2",
|
|
40
|
+
"tsx": "^4.20.6"
|
|
41
|
+
}
|
|
42
|
+
}
|