@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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -2
  3. package/dist/bin.mjs +831 -0
  4. package/dist/client-DsTLQ2lT.mjs +286 -0
  5. package/dist/in-app.json +10 -0
  6. package/dist/index.d.mts +160 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/libraries/aria.lib.par +14516 -0
  9. package/dist/libraries/baseui.lib.par +8298 -0
  10. package/dist/libraries/buildkit-baseui.edits.json +3 -0
  11. package/dist/libraries/chakra.lib.par +539225 -0
  12. package/dist/libraries/fluent-icons.lib.par +2674 -0
  13. package/dist/libraries/fluent.lib.par +33703 -0
  14. package/dist/libraries/heroicons.lib.par +33 -0
  15. package/dist/libraries/hugeicons.lib.par +708 -0
  16. package/dist/libraries/lucide.lib.par +2123 -0
  17. package/dist/libraries/luma.lib.par +16362 -0
  18. package/dist/libraries/lyra.lib.par +16362 -0
  19. package/dist/libraries/maia.lib.par +16362 -0
  20. package/dist/libraries/mantine.lib.par +32935 -0
  21. package/dist/libraries/mira.lib.par +16362 -0
  22. package/dist/libraries/mui.lib.par +9817 -0
  23. package/dist/libraries/nova.lib.par +16363 -0
  24. package/dist/libraries/phosphor.lib.par +3060 -0
  25. package/dist/libraries/radix.lib.par +6890 -0
  26. package/dist/libraries/recharts.lib.par +5445 -0
  27. package/dist/libraries/rhea.lib.par +16362 -0
  28. package/dist/libraries/sera.lib.par +16362 -0
  29. package/dist/libraries/tabler.lib.par +5325 -0
  30. package/dist/libraries/vega.lib.par +16362 -0
  31. package/dist/published-BtL8RM6W.mjs +1 -0
  32. package/dist/published-CdXoUgfD.mjs +200 -0
  33. package/dist/src-8MhDV_sA.mjs +4780 -0
  34. package/dist/src-D9EMN48r.mjs +1306 -0
  35. package/dist/src-DXZqtm6z.mjs +53 -0
  36. package/dist/src-DYi23Ypb.mjs +1 -0
  37. package/dist/src-FF8eY6EI.mjs +2 -0
  38. package/package.json +50 -3
  39. package/skills/SKILL.md +96 -0
  40. package/skills/document.md +170 -0
  41. package/skills/execute.md +298 -0
  42. package/skills/guide/checking.md +56 -0
  43. package/skills/guide/components.md +48 -0
  44. package/skills/guide/design-system.md +52 -0
  45. package/skills/guide/mobile.md +32 -0
  46. package/skills/guide/states-and-modes.md +55 -0
  47. 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.