@parity-app/cli 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +64 -2
- package/dist/bin.mjs +831 -0
- package/dist/client-DsTLQ2lT.mjs +286 -0
- package/dist/in-app.json +10 -0
- package/dist/index.d.mts +160 -0
- package/dist/index.mjs +1 -0
- package/dist/libraries/aria.lib.par +14516 -0
- package/dist/libraries/baseui.lib.par +8298 -0
- package/dist/libraries/buildkit-baseui.edits.json +3 -0
- package/dist/libraries/chakra.lib.par +539225 -0
- package/dist/libraries/fluent-icons.lib.par +2674 -0
- package/dist/libraries/fluent.lib.par +33703 -0
- package/dist/libraries/heroicons.lib.par +33 -0
- package/dist/libraries/hugeicons.lib.par +708 -0
- package/dist/libraries/lucide.lib.par +2123 -0
- package/dist/libraries/luma.lib.par +16362 -0
- package/dist/libraries/lyra.lib.par +16362 -0
- package/dist/libraries/maia.lib.par +16362 -0
- package/dist/libraries/mantine.lib.par +32935 -0
- package/dist/libraries/mira.lib.par +16362 -0
- package/dist/libraries/mui.lib.par +9817 -0
- package/dist/libraries/nova.lib.par +16363 -0
- package/dist/libraries/phosphor.lib.par +3060 -0
- package/dist/libraries/radix.lib.par +6890 -0
- package/dist/libraries/recharts.lib.par +5445 -0
- package/dist/libraries/rhea.lib.par +16362 -0
- package/dist/libraries/sera.lib.par +16362 -0
- package/dist/libraries/tabler.lib.par +5325 -0
- package/dist/libraries/vega.lib.par +16362 -0
- package/dist/published-BtL8RM6W.mjs +1 -0
- package/dist/published-CdXoUgfD.mjs +200 -0
- package/dist/src-8MhDV_sA.mjs +4780 -0
- package/dist/src-D9EMN48r.mjs +1306 -0
- package/dist/src-DXZqtm6z.mjs +53 -0
- package/dist/src-DYi23Ypb.mjs +1 -0
- package/dist/src-FF8eY6EI.mjs +2 -0
- package/package.json +50 -3
- package/skills/SKILL.md +96 -0
- package/skills/document.md +170 -0
- package/skills/execute.md +298 -0
- package/skills/guide/checking.md +56 -0
- package/skills/guide/components.md +48 -0
- package/skills/guide/design-system.md +52 -0
- package/skills/guide/mobile.md +32 -0
- package/skills/guide/states-and-modes.md +55 -0
- package/skills/guide/web-app.md +43 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# The `execute` tool
|
|
2
|
+
|
|
3
|
+
`execute` runs a JavaScript snippet against a working copy of one tab's designs. The functions below are globals.
|
|
4
|
+
`await` works at the top level. When the snippet finishes, every change lands on the canvas as one step the person can
|
|
5
|
+
undo; when it throws, nothing changes and you get an `editId` to fix it with.
|
|
6
|
+
|
|
7
|
+
- Pass `description`: 3 to 8 words the person sees while it runs, such as "Add a pricing section".
|
|
8
|
+
- `Print(...)` (or `console.log`) is how you see values. Objects print as JSON.
|
|
9
|
+
- Values assigned without `const` or `let` carry over to your next snippet in the same tab.
|
|
10
|
+
- A snippet has 5 seconds of computing time. Rendering (screenshots and checks) doesn't count against it.
|
|
11
|
+
- To retry a failed snippet, send `editId` with `edits`, each an exact `{ find, replace }` on the snippet text (`all` to
|
|
12
|
+
replace every match).
|
|
13
|
+
|
|
14
|
+
## API
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// Reading
|
|
18
|
+
Designs(): DesignSummary[] // every design: id, name, kind, states, what it shows, size
|
|
19
|
+
GetDesign(design): Design // a whole design, as in document.md
|
|
20
|
+
Get(path, { depth?, state? }?): Node // a design's root or a node; depth cuts children off with "…"
|
|
21
|
+
Get(path?, visit): any[] // walks nodes, collecting what visit returns
|
|
22
|
+
Components(): string[] // the components designs can use
|
|
23
|
+
Props(component, { all? }?): PropsInfo // a component's props, from its source
|
|
24
|
+
Icons(): IconSet[] // the project's icon sets, with their styles
|
|
25
|
+
Icons(set, { search?, style?, limit? }): Icon[] // async: a set's icons, as component nodes name them
|
|
26
|
+
GetTokens({ category?, source?, search? }?): Token[]
|
|
27
|
+
GetModes(): { settings, modes } // the project's modes, and each resolved
|
|
28
|
+
Selection(): { design?, node?, mode? } // what the person has selected
|
|
29
|
+
|
|
30
|
+
// Designs
|
|
31
|
+
CreateDesign({ name, kind?, root?, x?, y?, file?, baseState? }): string // returns the new design's id
|
|
32
|
+
UpdateDesign(design, { name?, kind?, x?, y?, mode?, showModes?, state?, allStates? }): void
|
|
33
|
+
DeleteDesign(design): void
|
|
34
|
+
|
|
35
|
+
// Nodes
|
|
36
|
+
Insert(parent, node, { index?, slot? }?): string // returns the new node's id
|
|
37
|
+
Update(path, change, { state? }?): void // change: name, text, tag, hidden, props, args, style, component
|
|
38
|
+
Replace(path, node): string
|
|
39
|
+
Move(path, parent, { index?, slot? }?): void // within one design
|
|
40
|
+
Copy(path, parent, { index?, slot?, name? }?): string
|
|
41
|
+
Delete(path): void
|
|
42
|
+
Responsive(path, viewport, patch | null): void // a patch for one viewport, or null to remove it
|
|
43
|
+
|
|
44
|
+
// States
|
|
45
|
+
AddState(design, { name, changes? }): string // changes keyed by node id or name
|
|
46
|
+
UpdateState(design, state, { name?, changes? }): void
|
|
47
|
+
DeleteState(design, state): void
|
|
48
|
+
|
|
49
|
+
// Components
|
|
50
|
+
FindRepeats(design): Repeat[] // parts that repeat with the same structure
|
|
51
|
+
ExtractComponent(design, nodes, { name, params?, file? }): string
|
|
52
|
+
|
|
53
|
+
// Modes and tokens
|
|
54
|
+
SetModes(settings): void // replaces the project's modes
|
|
55
|
+
SetTokens({ name: value | { theme: value } }): void
|
|
56
|
+
|
|
57
|
+
// Checking
|
|
58
|
+
Screenshot(design, { state?, mode?, scale? }?): number
|
|
59
|
+
Measure(design, { state?, mode? }?): Layout
|
|
60
|
+
Problems(design, { state?, mode? }?): Problem[]
|
|
61
|
+
Accessibility(design, { state?, mode? }?): Finding[]
|
|
62
|
+
|
|
63
|
+
// The canvas
|
|
64
|
+
FindEmptySpace({ width, height, direction?, near?, padding? }): { x, y }
|
|
65
|
+
Overlaps(design?): Overlap[]
|
|
66
|
+
Print(...values): void
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Paths
|
|
70
|
+
|
|
71
|
+
A design is named by its id or its name. A node is named by `design/node`, where `node` is the node's id or its name, or
|
|
72
|
+
by its id alone when only one design has that id. Names aren't unique, so prefer ids once you have them; `Insert`,
|
|
73
|
+
`Copy` and `CreateDesign` return them, and nodes you create with a `name` are listed under "Created" in the result.
|
|
74
|
+
|
|
75
|
+
## Reading
|
|
76
|
+
|
|
77
|
+
### Designs
|
|
78
|
+
|
|
79
|
+
Lists every design with its `id`, `name`, `kind` (`screen` or `component`), `states`, and `showing`: the state on the
|
|
80
|
+
canvas, the main mode and the modes beside it. Start here to find ids.
|
|
81
|
+
|
|
82
|
+
### Get
|
|
83
|
+
|
|
84
|
+
`Get("dashboard", { depth: 2 })` returns the design's root with two levels of children; deeper ones show as `"…"`. Read
|
|
85
|
+
a branch with a node path, as in `Get("dashboard/Stats", { depth: 2 })`. With `state`, the result shows the nodes as
|
|
86
|
+
that state changes them.
|
|
87
|
+
|
|
88
|
+
`Get(path, visit)` walks the nodes under `path`, or every design without one, and returns what `visit` returns for each.
|
|
89
|
+
`visit(node, { design, path, depth, index, parent, skipChildren })`. Return `undefined` to collect nothing.
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
// Every Button in the project, with where it is.
|
|
93
|
+
Print(Get((node, at) => (node.type === "component" && node.component === "Button" ? at.path : undefined)))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Components and Props
|
|
97
|
+
|
|
98
|
+
`Components()` is everything a `component` node can name. `Props("Button")` returns the props found in the component's
|
|
99
|
+
source: variants with their options (`variant`, `size`), its own props, and with `{ all: true }` the attributes it
|
|
100
|
+
passes to its element. A prop that isn't listed, or a variant value outside its options, comes back as a warning.
|
|
101
|
+
|
|
102
|
+
### Icons
|
|
103
|
+
|
|
104
|
+
`Icons()` lists the project's icon sets: each one's `id`, `name`, `package`, `styles` (such as `outline` and `solid`,
|
|
105
|
+
empty when it has one) and the `style` used when none is asked for. An icon is a component node named `<set>/<Icon>`,
|
|
106
|
+
with no children. `await Icons("lucide", { search: "settings" })` finds icons by name, by the other names the set
|
|
107
|
+
exports them under and by the tags it publishes, best first; each comes back with its `label` and the `component` (and
|
|
108
|
+
`props`, for sets that take the style as a prop) to insert it with. Without `search` it lists the set's icons in order.
|
|
109
|
+
It returns 50 unless `limit` says otherwise, in the set's default style unless `style` names another.
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
// A settings button with the project's own settings icon.
|
|
113
|
+
const [icon] = await Icons(Icons()[0].id, { search: "settings", limit: 1 })
|
|
114
|
+
Insert("dashboard/Toolbar", {
|
|
115
|
+
type: "component",
|
|
116
|
+
component: "Button",
|
|
117
|
+
props: { variant: "ghost", "aria-label": "Settings" },
|
|
118
|
+
slots: { children: [{ type: "component", component: icon.component, props: icon.props }] }
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### GetTokens
|
|
123
|
+
|
|
124
|
+
Each token has a `name`, a `category` (`color`, `font-size`, `radius`, `shadow`, `spacing`, `font`, `font-weight`,
|
|
125
|
+
`leading`, `tracking` and others), a `source` (`semantic` for the project's own variables, `theme` for its styling
|
|
126
|
+
framework's scale) and its `values` per theme. Filter with `GetTokens({ category: "color", source: "semantic" })` or
|
|
127
|
+
`GetTokens({ search: "radius" })`.
|
|
128
|
+
|
|
129
|
+
### Selection
|
|
130
|
+
|
|
131
|
+
What the person has selected: the design's id, and the node and mode when they picked one. "This", "here" and "the
|
|
132
|
+
selected one" usually mean it.
|
|
133
|
+
|
|
134
|
+
## Designs
|
|
135
|
+
|
|
136
|
+
### CreateDesign
|
|
137
|
+
|
|
138
|
+
Makes a design and returns its id. Without `root` it starts as a page layout with padding, a gap and the background and
|
|
139
|
+
foreground colours. Without `x` and `y` it goes into empty space. `kind: "component"` makes a component design, which
|
|
140
|
+
screens place as instances; most component designs come from `ExtractComponent` instead, because that also gives them
|
|
141
|
+
params.
|
|
142
|
+
|
|
143
|
+
### UpdateDesign
|
|
144
|
+
|
|
145
|
+
Renames or moves a design, or changes how it's shown: `mode` sets the main frame's mode, `showModes` the modes beside
|
|
146
|
+
it, `state` the state on the canvas, and `allStates: true` shows every state in its own row. None of these change the
|
|
147
|
+
design itself.
|
|
148
|
+
|
|
149
|
+
## Nodes
|
|
150
|
+
|
|
151
|
+
### Insert
|
|
152
|
+
|
|
153
|
+
Adds a node, with everything inside it, as the last child of `parent` or at `index`. Into a component node, it goes into
|
|
154
|
+
the `children` slot unless `slot` names another. Every node gets a fresh id; don't pass ids. `parent` can be a design,
|
|
155
|
+
meaning its root.
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
const form = Insert("settings/Content", {
|
|
159
|
+
type: "layout",
|
|
160
|
+
name: "Profile form",
|
|
161
|
+
style: { gap: 4, maxWidth: 120 },
|
|
162
|
+
children: [
|
|
163
|
+
{ type: "component", component: "Label", props: { htmlFor: "name", children: "Name" } },
|
|
164
|
+
{ type: "component", component: "Input", props: { id: "name", defaultValue: "Avery Chen" } },
|
|
165
|
+
{ type: "component", component: "Button", props: { children: "Save changes" }, style: { self: "start" } }
|
|
166
|
+
]
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Update
|
|
171
|
+
|
|
172
|
+
Changes a node. `change` takes `name`, `text`, `tag`, `hidden`, `props`, `args` (an instance's values), `style` and
|
|
173
|
+
`component`. `props`, `args` and `style` merge with what's there; set a key to `null` to remove it.
|
|
174
|
+
|
|
175
|
+
With `{ state: "loading" }` the change belongs to that state instead of the base design. A state can change only
|
|
176
|
+
`hidden`, `text`, `props` and `style`.
|
|
177
|
+
|
|
178
|
+
### Replace, Move, Copy and Delete
|
|
179
|
+
|
|
180
|
+
`Replace` swaps a node for a new one in the same place and returns its id. `Move` reorders or reparents a node within
|
|
181
|
+
its design; to move it to another design, `Copy` it there and `Delete` the original. `Copy` gives the copy fresh ids and
|
|
182
|
+
returns the new id. A design's root can't be deleted; use `Replace`.
|
|
183
|
+
|
|
184
|
+
### Responsive
|
|
185
|
+
|
|
186
|
+
Patches a node in one viewport: `Responsive("dashboard/Stats", "mobile", { style: { columns: 1 } })`. The viewport is a
|
|
187
|
+
viewport option's id from the modes (such as `desktop`, `tablet` and `mobile`), not a mode's id. Patches take `hidden`,
|
|
188
|
+
`text`, `props` and `style`, and merge with the viewport's earlier patch. `null` removes it.
|
|
189
|
+
|
|
190
|
+
## States
|
|
191
|
+
|
|
192
|
+
A state is a situation the whole design is in, like a story: loading, empty, error, signed out. It stores only what
|
|
193
|
+
differs from the base. `changes` maps node ids or names to patches (`hidden`, `text`, `props`, `style`); `null` clears a
|
|
194
|
+
node's patch. The first state is the base.
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
AddState("team-members", {
|
|
198
|
+
name: "Empty",
|
|
199
|
+
changes: {
|
|
200
|
+
"Members table": { hidden: true },
|
|
201
|
+
"Empty state": { hidden: false },
|
|
202
|
+
seats: { props: { children: "1 of 10 seats used" } }
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Put the parts only some states show into the base design with `hidden: true`, then show them in those states. A text
|
|
208
|
+
node's words are its `text`; a component's are usually its `children` prop.
|
|
209
|
+
|
|
210
|
+
## Components
|
|
211
|
+
|
|
212
|
+
### FindRepeats
|
|
213
|
+
|
|
214
|
+
Returns parts of a design that repeat with the same structure: each has the root ids of its occurrences (`nodes`), a
|
|
215
|
+
starting `name`, and the `params`, which are what differs between occurrences (texts, props, whether a node shows).
|
|
216
|
+
|
|
217
|
+
### ExtractComponent
|
|
218
|
+
|
|
219
|
+
Turns occurrences into a component design and replaces each with an instance that keeps its own content as args. Returns
|
|
220
|
+
the component's id. `params` renames the params found, by the name they were given. It refuses when the result wouldn't
|
|
221
|
+
render the same in every state and viewport.
|
|
222
|
+
|
|
223
|
+
Look at what `FindRepeats` found before extracting, because the params' names are only a start:
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
repeats = FindRepeats("team-members")
|
|
227
|
+
Print(repeats)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Then, in the next snippet, extract with names that say what each param is:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
const [rows] = repeats
|
|
234
|
+
ExtractComponent("team-members", rows.nodes, { name: "Member row", params: { [rows.params[0].name]: "name" } })
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Modes and tokens
|
|
238
|
+
|
|
239
|
+
`SetModes(settings)` replaces the project's modes: take `GetModes().settings`, change it, and pass it back. Modes are
|
|
240
|
+
shared by every design.
|
|
241
|
+
|
|
242
|
+
`SetTokens` changes token values in the project's stylesheets: `SetTokens({ primary: "oklch(0.55 0.2 260)" })` for every
|
|
243
|
+
theme, or `SetTokens({ primary: { default: "…", dark: "…" } })` per theme. Every frame shows the new values at once, and
|
|
244
|
+
saving writes them into the project's code, so do it only when the person asks to change the design system, not to style
|
|
245
|
+
one design. They're undone with the call's other changes.
|
|
246
|
+
|
|
247
|
+
## Checking
|
|
248
|
+
|
|
249
|
+
`Screenshot` renders frames offscreen at their real widths and returns how many it took; the images come back with the
|
|
250
|
+
result. `state` and `mode` take an id, a list, or `"all"`; `mode` also takes `"shown"`, the modes the design shows. The
|
|
251
|
+
defaults are the state on the canvas and the main mode.
|
|
252
|
+
|
|
253
|
+
`Measure` lays out one frame and returns its size, whether it overflows sideways, and every node's box (`clipped` when a
|
|
254
|
+
parent cuts it off). `Problems` checks frames for render errors, overflow, clipping and unknown components.
|
|
255
|
+
`Accessibility` returns axe-core's findings per node and frame, with the rule, its impact and what's wrong.
|
|
256
|
+
|
|
257
|
+
## The canvas
|
|
258
|
+
|
|
259
|
+
`FindEmptySpace({ width, height })` finds room for a new board, to the right of the others unless `direction: "below"`,
|
|
260
|
+
or next to the design named in `near`. `Overlaps()` lists boards that collide, titles included; move one with
|
|
261
|
+
`UpdateDesign(design, { x, y })`.
|
|
262
|
+
|
|
263
|
+
## Examples
|
|
264
|
+
|
|
265
|
+
A new screen, checked in every state and mode:
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
const id = CreateDesign({ name: "Billing" })
|
|
269
|
+
Insert(id, { type: "text", tag: "h1", text: "Billing", style: { size: "text-2xl", weight: 600 } })
|
|
270
|
+
Insert(id, {
|
|
271
|
+
type: "component",
|
|
272
|
+
component: "Card",
|
|
273
|
+
name: "Plan",
|
|
274
|
+
slots: {
|
|
275
|
+
children: [
|
|
276
|
+
{
|
|
277
|
+
type: "component",
|
|
278
|
+
component: "CardHeader",
|
|
279
|
+
slots: {
|
|
280
|
+
children: [
|
|
281
|
+
{ type: "component", component: "CardTitle", props: { children: "Pro plan" } },
|
|
282
|
+
{ type: "component", component: "CardDescription", props: { children: "$24 per seat, billed monthly" } }
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
await Screenshot(id, { state: "all", mode: "shown" })
|
|
290
|
+
Print(await Problems(id, { state: "all", mode: "shown" }))
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
A loading state that swaps content for skeletons:
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
Update("dashboard/Stats loading", { hidden: true })
|
|
297
|
+
AddState("dashboard", { name: "Loading", changes: { Stats: { hidden: true }, "Stats loading": { hidden: false } } })
|
|
298
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Checking a design
|
|
2
|
+
|
|
3
|
+
Parity renders every frame offscreen at its real width, with the project's real components, so checks see what the app
|
|
4
|
+
would show. Run them after every change you make, and before you tell the person you're done.
|
|
5
|
+
|
|
6
|
+
## Screenshots
|
|
7
|
+
|
|
8
|
+
`screenshot`, or `Screenshot` in a snippet, returns the images. Look at them: text that wraps badly, a button pushed off
|
|
9
|
+
the edge, a skeleton that doesn't match what it stands for, colours that disappear in dark mode. `scale: 2` helps with
|
|
10
|
+
small details.
|
|
11
|
+
|
|
12
|
+
## Layout problems
|
|
13
|
+
|
|
14
|
+
`find_problems`, or `Problems` in a snippet, lists what's wrong in each frame without looking at it:
|
|
15
|
+
|
|
16
|
+
- **Render errors.** A component threw. Usually a prop with the wrong type, or a part used outside its parent (a
|
|
17
|
+
`TabsTrigger` without `Tabs`). `parity components` lists the components failing anywhere on the canvas, with why.
|
|
18
|
+
- **Overflow.** Content wider than the frame, usually a row that needs to wrap or stack on mobile.
|
|
19
|
+
- **Clipping.** A node cut off by a parent with `overflow: hidden` or a fixed size.
|
|
20
|
+
- **Unknown components.** A name that isn't in `Components()`; it draws as a placeholder.
|
|
21
|
+
|
|
22
|
+
`Measure` gives every node's box in one frame, for checking alignment or sizes by numbers.
|
|
23
|
+
|
|
24
|
+
## Accessibility
|
|
25
|
+
|
|
26
|
+
`check_accessibility`, or `Accessibility` in a snippet, runs axe-core against WCAG 2.2 AA in each frame and reports each
|
|
27
|
+
rule broken on the layers that break it, worst first. Fix what it finds within the design system:
|
|
28
|
+
|
|
29
|
+
| Finding | Fix in the design |
|
|
30
|
+
| -------------------------- | --------------------------------------------------------------------------------- |
|
|
31
|
+
| Text contrast too low | A stronger token: `foreground` instead of `muted-foreground`, or a darker variant |
|
|
32
|
+
| Button or link has no name | Visible text, or an `aria-label` prop on an icon-only button |
|
|
33
|
+
| Form field has no label | A `Label` with `htmlFor` matching the field's `id`, or an `aria-label` |
|
|
34
|
+
| Image has no alt text | `alt` that says what it shows, or `alt: ""` when it's decoration |
|
|
35
|
+
| ARIA reference to nothing | Add what it points at, such as the `TabsContent` for each `TabsTrigger` |
|
|
36
|
+
| Heading level skipped | Text tags that go down one level at a time: `h1`, then `h2` |
|
|
37
|
+
| Target too small | A bigger size variant, or more room around small controls |
|
|
38
|
+
|
|
39
|
+
Automated checks find some problems, not all. A clean result doesn't make a design accessible; say so when you report.
|
|
40
|
+
|
|
41
|
+
## The console
|
|
42
|
+
|
|
43
|
+
`get_console` returns what the project's own components wrote to the console: errors, warnings and logs, from the
|
|
44
|
+
person's canvas and from your offscreen renders. React warnings about props, keys and nesting show up here, often before
|
|
45
|
+
they show on screen. `clear: true` empties it, so the next read shows only what's new.
|
|
46
|
+
|
|
47
|
+
React's warnings keep their `%s` placeholders, with the values that fill them listed after the message.
|
|
48
|
+
|
|
49
|
+
## Trying it
|
|
50
|
+
|
|
51
|
+
Checks can't click. The person can: the Interact tool (I, or the pointing hand in the toolbar) hands their mouse and
|
|
52
|
+
keyboard to the real components, so tabs switch, menus open and fields take typing. Escape steps out of a component, and
|
|
53
|
+
a second Escape stops interacting; the design goes back to how it was. When behaviour matters (a menu, a dialog,
|
|
54
|
+
keyboard order), ask them to try it, and read `get_console` afterwards for errors it caused.
|
|
55
|
+
|
|
56
|
+
The Accessibility tab also shows the tab order on the canvas, numbered in the order the Tab key visits.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
Two kinds of component show on the canvas, and they work differently.
|
|
4
|
+
|
|
5
|
+
**The project's components** are its code: `Button`, `Card`, `Tabs`, and library ones such as `nova/Accordion`. A
|
|
6
|
+
`component` node places one. You can't change what they look like from Parity, only choose their props and what goes in
|
|
7
|
+
their slots. `Components()` lists them.
|
|
8
|
+
|
|
9
|
+
**Component designs** are designs with `kind: "component"`: a stat card, a member row, a sidebar. They're made of the
|
|
10
|
+
project's components and layouts, and screens place them as `instance` nodes. Change the component design and every
|
|
11
|
+
instance follows.
|
|
12
|
+
|
|
13
|
+
## Choosing a component
|
|
14
|
+
|
|
15
|
+
- Look before you build. `Components()` usually has what a screen needs; shadcn-style projects split a card into `Card`,
|
|
16
|
+
`CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` and `CardAction`.
|
|
17
|
+
- Read its props with `Props("Badge")`. Variants and sizes come with their options; use those values exactly. A prop the
|
|
18
|
+
component doesn't declare comes back as a warning.
|
|
19
|
+
- Compose the parts the way the library intends: `Tabs` holds a `TabsList` of `TabsTrigger`s and one `TabsContent` per
|
|
20
|
+
trigger; `Table` holds `TableHeader` and `TableBody`, each with `TableRow`s of cells.
|
|
21
|
+
- Put children where they go. `slots.children` is what goes between the component's tags. Other slots, when a component
|
|
22
|
+
takes nodes as props, are named after the prop.
|
|
23
|
+
|
|
24
|
+
## Icons
|
|
25
|
+
|
|
26
|
+
Icons are components named after their library, as in `lucide/Search` or `lucide/ChevronRight`. Size them with the
|
|
27
|
+
`size` prop (in pixels, 16 in most controls). Put an icon inside a button's `children` slot next to its text, or alone
|
|
28
|
+
with an `aria-label` prop when the button has no text.
|
|
29
|
+
|
|
30
|
+
## Component designs and instances
|
|
31
|
+
|
|
32
|
+
Make a component design when something repeats with the same structure: rows in a list, cards in a grid, items in a
|
|
33
|
+
feed. Let `FindRepeats` find the candidates and `ExtractComponent` do the work: it makes the component design, works out
|
|
34
|
+
its params from what differs, and replaces each occurrence with an instance carrying its own values.
|
|
35
|
+
|
|
36
|
+
- Give params names that say what they are (`name`, `email`, `role`), not where they are.
|
|
37
|
+
- An instance's values are its `args`. Change them with `Update(instance, { args: { role: "Admin" } })`.
|
|
38
|
+
- A component design can have states too, such as a row that's loading. An instance's `state` picks which one it shows.
|
|
39
|
+
- A component design can have variants: choices each instance makes, like a size or a tone. Each option patches the
|
|
40
|
+
design the way a state does, and an instance picks one in its `args`: `Update(row, { args: { density: "compact" } })`.
|
|
41
|
+
- An instance can override what's inside it, such as one copy's title or a hidden badge. `Get` shows these as the
|
|
42
|
+
instance's `overrides`. People make them on the canvas; `execute` can't edit overrides or add variants yet, so to
|
|
43
|
+
change one copy, give the component design a param for it.
|
|
44
|
+
- A component design that needs a parent to render properly, such as a table row, is drawn inside that parent on its own
|
|
45
|
+
board. Rows extracted from a table get this automatically.
|
|
46
|
+
- Place a new instance with `Insert(parent, { type: "instance", design: "member-row", args: { … } })`.
|
|
47
|
+
|
|
48
|
+
Keep component designs in their own area of the canvas, away from screens, as `ExtractComponent` does.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Designing with the project's system
|
|
2
|
+
|
|
3
|
+
The project already decided its colours, type, spacing and components. Your job is to compose them well, not to style
|
|
4
|
+
around them. A design that only uses the system's pieces is one the code can build without new CSS.
|
|
5
|
+
|
|
6
|
+
## Spacing
|
|
7
|
+
|
|
8
|
+
Spacing is a scale of steps (`4` is 1rem in a default Tailwind project). Use a few steps consistently:
|
|
9
|
+
|
|
10
|
+
| Between | Steps |
|
|
11
|
+
| -------------------------------------------- | ------- |
|
|
12
|
+
| An icon and its label, a label and its field | 1 to 2 |
|
|
13
|
+
| Items in a list or fields in a form | 3 to 4 |
|
|
14
|
+
| Groups within a section | 6 |
|
|
15
|
+
| Sections of a page | 8 to 12 |
|
|
16
|
+
| A page's edge, desktop | 8 |
|
|
17
|
+
| A page's edge, mobile | 4 to 5 |
|
|
18
|
+
|
|
19
|
+
Space shows what belongs together: less inside a group than between groups. Let cards and other components keep their
|
|
20
|
+
own padding; add gaps between them, not padding around them.
|
|
21
|
+
|
|
22
|
+
## Type
|
|
23
|
+
|
|
24
|
+
Use the type scale tokens (`text-sm`, `text-base`, `text-lg`, `text-2xl`) and weights 400 to 700. A page needs about
|
|
25
|
+
three sizes: its title, section titles and body. Secondary text uses `muted-foreground`, not a smaller size alone. Pick
|
|
26
|
+
the `tag` for meaning: one `h1` per screen, then `h2` for sections.
|
|
27
|
+
|
|
28
|
+
## Colour
|
|
29
|
+
|
|
30
|
+
Semantic tokens carry meaning and follow the theme:
|
|
31
|
+
|
|
32
|
+
- `background` and `foreground` for the page; `card` and `card-foreground` for raised surfaces.
|
|
33
|
+
- `muted` and `muted-foreground` for quiet surfaces and secondary text.
|
|
34
|
+
- `primary` for the one main action on a screen, through a component's default variant rather than a raw background.
|
|
35
|
+
- `destructive` for actions that delete or can't be undone.
|
|
36
|
+
- `border` for dividers and outlines.
|
|
37
|
+
|
|
38
|
+
Palette colours (`color-emerald-500`) don't change with the theme, so keep them for data, such as chart series and
|
|
39
|
+
status dots, and check them in dark mode.
|
|
40
|
+
|
|
41
|
+
## Composing
|
|
42
|
+
|
|
43
|
+
- Start from the page's structure: a header with the title and its actions, then the content, then anything secondary.
|
|
44
|
+
- Group with the components meant for it. A `Card` frames one subject; a `Table` shows records with the same fields; a
|
|
45
|
+
list of rows suits items people scan and act on; `Tabs` split one subject into views.
|
|
46
|
+
- One primary button per view. Other actions use `outline`, `secondary` or `ghost` variants, and destructive ones the
|
|
47
|
+
`destructive` variant, usually behind a confirmation.
|
|
48
|
+
- Align to one edge. Text and controls in a column share a left edge; actions sit together, at the end of a row or the
|
|
49
|
+
bottom of a form.
|
|
50
|
+
- Show status with the system's pieces: `Badge` for a status or count, `Skeleton` for loading, `Empty` for nothing yet.
|
|
51
|
+
- Fill designs with believable data: real names, dates, amounts and lengths. Include a long one; it finds layout
|
|
52
|
+
problems a short one hides.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Phones and small viewports
|
|
2
|
+
|
|
3
|
+
In Parity a phone layout is the same design in a mobile viewport, patched where it needs to differ. Show a mobile mode
|
|
4
|
+
beside the main frame (`UpdateDesign(design, { showModes: ["mobile-light"] })`) and fix what breaks there.
|
|
5
|
+
|
|
6
|
+
## What usually changes
|
|
7
|
+
|
|
8
|
+
| On desktop | On a phone |
|
|
9
|
+
| ------------------------------------ | -------------------------------------------------- |
|
|
10
|
+
| A sidebar | Hidden, with navigation in a top bar or a menu |
|
|
11
|
+
| Rows of cards or columns of a grid | One column: `columns: 1`, or `direction: "column"` |
|
|
12
|
+
| A header row with actions at the end | Title above, actions below or in a menu |
|
|
13
|
+
| Page padding of 8 steps | 4 to 5 steps |
|
|
14
|
+
| A wide table | Fewer columns, or each row as a stacked card |
|
|
15
|
+
| Side-by-side form fields | Stacked, full width |
|
|
16
|
+
|
|
17
|
+
Patch these with `Responsive(node, "mobile", patch)`. Hiding a column in a table means hiding its header cell and the
|
|
18
|
+
matching cell in the row component design.
|
|
19
|
+
|
|
20
|
+
## Touch
|
|
21
|
+
|
|
22
|
+
- Controls need room for a finger. WCAG asks for targets of at least 24 by 24 pixels; phones are easier at about 44. Use
|
|
23
|
+
the components' default or large sizes, and keep small controls apart.
|
|
24
|
+
- Put the main action where a thumb reaches it: full width at the bottom of a form, or at the bottom of the screen.
|
|
25
|
+
- Nothing can depend on hover. Anything a hover shows on desktop needs another way in.
|
|
26
|
+
|
|
27
|
+
## Content
|
|
28
|
+
|
|
29
|
+
- Text wraps more. Check long names, emails and numbers in every state; `lines: 1` keeps a single line with an ellipsis
|
|
30
|
+
where wrapping would break a row.
|
|
31
|
+
- Lead with less. A phone shows the first few items; put the most important ones first.
|
|
32
|
+
- Check the mobile frame with `find_problems`: overflow there usually means a row that needs to stack.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# States and modes
|
|
2
|
+
|
|
3
|
+
A design shows on the canvas as a grid of frames: its states down, and the modes it shows across. Both multiply what you
|
|
4
|
+
have to check, and both are cheap to add, so use them instead of copying designs.
|
|
5
|
+
|
|
6
|
+
## States
|
|
7
|
+
|
|
8
|
+
A state is one situation the design can be in. Most screens need some of these:
|
|
9
|
+
|
|
10
|
+
| State | What changes |
|
|
11
|
+
| -------------- | ---------------------------------------------------------------------------- |
|
|
12
|
+
| Loading | Content swaps for skeletons of the same size, so nothing jumps when it loads |
|
|
13
|
+
| Empty | The list or table hides; an empty state says why and offers the next step |
|
|
14
|
+
| Error | A message near what failed, saying what happened and what to do |
|
|
15
|
+
| Partly filled | One item, a long name, a missing photo: the cases real data brings |
|
|
16
|
+
| Disabled, busy | A submit button that's disabled or shows progress while saving |
|
|
17
|
+
| Permissions | What a viewer sees compared with an admin |
|
|
18
|
+
|
|
19
|
+
Build states from the base design. Put what only some states need (skeletons, an empty state, an error message) into the
|
|
20
|
+
base with `hidden: true`, then show and hide things per state with `AddState(design, { name, changes })`. A state stores
|
|
21
|
+
only its changes, so editing the base updates every state.
|
|
22
|
+
|
|
23
|
+
Name states for the situation ("No members yet", "Invite failed"), not the change ("Table hidden").
|
|
24
|
+
|
|
25
|
+
## Modes
|
|
26
|
+
|
|
27
|
+
Modes are the project's, shared by every design. Each mode picks one option of each variable:
|
|
28
|
+
|
|
29
|
+
- **Viewport:** a width, and a device. Desktop, tablet and mobile are typical.
|
|
30
|
+
- **Theme:** a class on the frame, such as `dark`, that switches the project's tokens.
|
|
31
|
+
- **Locale:** `lang` and `dir` attributes, for languages and right-to-left scripts.
|
|
32
|
+
|
|
33
|
+
`GetModes()` lists them. A design shows its main mode and, beside it, any others you choose with
|
|
34
|
+
`UpdateDesign(design, { showModes: ["desktop-dark", "mobile-light"] })`. Show the modes that matter for the design: a
|
|
35
|
+
dashboard in desktop and mobile, a component in light and dark.
|
|
36
|
+
|
|
37
|
+
## Viewports and responsive patches
|
|
38
|
+
|
|
39
|
+
One design covers every viewport. Patch the differences with `Responsive(node, viewport, patch)` on the nodes that
|
|
40
|
+
change:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
Responsive("dashboard/Sidebar", "mobile", { hidden: true })
|
|
44
|
+
Responsive("dashboard/Stats", "tablet", { style: { columns: 2 } })
|
|
45
|
+
Responsive("dashboard/Stats", "mobile", { style: { columns: 1 } })
|
|
46
|
+
Responsive("dashboard/Content", "mobile", { style: { padding: [5, 4] } })
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The viewport is the viewport option's id, not the mode's. A frame is as wide as its viewport and as tall as its content,
|
|
50
|
+
unless the design sets a size for that viewport.
|
|
51
|
+
|
|
52
|
+
## Checking them all
|
|
53
|
+
|
|
54
|
+
After a change, screenshot every frame the design shows: `Screenshot(design, { state: "all", mode: "shown" })`. Check
|
|
55
|
+
dark mode for colours that only worked on white, mobile for rows that don't fit, and each state for a layout that jumps.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Web app screens
|
|
2
|
+
|
|
3
|
+
A web app is used, not read. People come to a screen to do something: check a number, find a record, change a setting.
|
|
4
|
+
Design each screen around that job.
|
|
5
|
+
|
|
6
|
+
## Before you design
|
|
7
|
+
|
|
8
|
+
Answer three questions, from the request or by asking:
|
|
9
|
+
|
|
10
|
+
1. **Who uses this screen, and what do they come to do?** The main task gets the most space and the first look.
|
|
11
|
+
2. **What data does it show?** Real fields, typical amounts, and the extremes: none, one, thousands, a very long name.
|
|
12
|
+
3. **What can go wrong?** Loading, empty, errors, no permission. Each is a state of the design.
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
|
|
16
|
+
- **App shell.** Navigation in a sidebar or top bar, then the page. Make the shell a component design (`app-sidebar`,
|
|
17
|
+
`app-top-bar`) and place it as an instance on every screen, so it stays the same everywhere.
|
|
18
|
+
- **Page header.** The page's title, a line saying what it's for when that isn't obvious, and its main actions at the
|
|
19
|
+
end of the same row.
|
|
20
|
+
- **Content.** Lead with what the person came for. On a dashboard that's a few key numbers, then what changed, then
|
|
21
|
+
detail. On a list, the list, with search and filters above it.
|
|
22
|
+
- **Width.** Forms and reading text work best narrow (a `maxWidth` around 160 steps); tables and dashboards use the
|
|
23
|
+
room.
|
|
24
|
+
|
|
25
|
+
## Common screens
|
|
26
|
+
|
|
27
|
+
- **Dashboard.** A row of stat cards (a component design), then a chart or a table of what needs attention. Every number
|
|
28
|
+
says what it is and, where it helps, how it changed.
|
|
29
|
+
- **List and table.** A header with the count and the main action, search and filters, the table, and pagination or a
|
|
30
|
+
"load more". Row actions sit at the end of the row. Plan the empty state and the filtered-to-nothing state, which are
|
|
31
|
+
different.
|
|
32
|
+
- **Detail.** The record's name and status at the top, its main facts in a card, then related lists in tabs.
|
|
33
|
+
- **Settings.** Sections with a title and a line of explanation on one side and the fields on the other, or stacked on
|
|
34
|
+
mobile. Save per section, and show when it saved.
|
|
35
|
+
- **Forms.** One column. Labels above fields, help text under them, errors under the field they're about, in
|
|
36
|
+
`destructive`. The primary button at the end; disable it or show progress while it saves.
|
|
37
|
+
- **Sign in and onboarding.** One task per screen, centred, narrow, with the product's name or logo above it.
|
|
38
|
+
|
|
39
|
+
## Behaviour
|
|
40
|
+
|
|
41
|
+
Designs are static, but they should show behaviour as states: a menu open, a dialog over the page, a toast after saving,
|
|
42
|
+
a row being edited. When you're unsure whether something works, ask the person to try it with the Interact tool; it uses
|
|
43
|
+
the real components.
|