@lotics/ui 45.9.0 → 45.10.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/AGENTS.md +70 -137
- package/docs/ai_patterns.md +88 -150
- package/docs/catalog.md +243 -285
- package/docs/composition.md +455 -465
- package/docs/data_entry.md +109 -155
- package/docs/reviewing.md +47 -55
- package/docs/templates.md +480 -371
- package/docs/testing.md +3 -7
- package/package.json +3 -1
- package/src/bar_chart.tsx +20 -2
- package/src/chip_group.tsx +5 -3
- package/src/finding.tsx +18 -3
- package/src/line_chart.tsx +5 -14
- package/src/line_chart_labels.ts +32 -0
- package/src/page_header.tsx +13 -1
- package/src/pressable_row.tsx +7 -5
- package/src/stacked_bar_chart.tsx +197 -0
- package/src/table.tsx +15 -14
- package/src/waterfall_chart.tsx +398 -0
package/AGENTS.md
CHANGED
|
@@ -15,189 +15,122 @@ CURRENT major only — upgrading an app across majors is `MIGRATION.md`.
|
|
|
15
15
|
| Doc | Read it for |
|
|
16
16
|
|---|---|
|
|
17
17
|
| [docs/catalog.md](./docs/catalog.md) | **The complete inventory** — Reach-by-role (each data role → the ONE canonical component) + every `@lotics/ui/<module>` entry point (incl. `@lotics/ui/vite`'s `loticsOptimizeDeps` + `loticsResolve()` — the pre-bundle list and the whole `resolve` block a custom-code app's `vite.config.ts` imports rather than hand-carries, dev-link included). Read before building any screen; reuse first. |
|
|
18
|
-
| [docs/data_entry.md](./docs/data_entry.md) |
|
|
19
|
-
| [docs/ai_patterns.md](./docs/ai_patterns.md) | AI acts, the human stays in charge — composer, live run feed (`AgentRun`), the one law's split — it turns on WHO supplied the values
|
|
20
|
-
| [docs/composition.md](./docs/composition.md) | The design-language contract — **the form comes before the treatment** (name what the subject IS
|
|
21
|
-
| [docs/reviewing.md](./docs/reviewing.md) | **Reviewing a screen you built** — the other docs say what good looks like; this one says how to find out whether you achieved it
|
|
18
|
+
| [docs/data_entry.md](./docs/data_entry.md) | Which editing pattern for which job — inline edit, fieldset forms, choosing a CHOICE control by option count, find-or-create (`Combobox`), line items, handoffs, phased records, billing, tags, dispositions, attachments (`InlineFiles` for a record ROW, the `FilesEditor` COMPOUND for a whole section, plus the three-way file INTAKE), stage gates, and the commit-on-blur vs action-press ordering law. |
|
|
19
|
+
| [docs/ai_patterns.md](./docs/ai_patterns.md) | AI acts, the human stays in charge — composer, live run feed (`AgentRun`), the one law's split — it turns on WHO supplied the values; findings, provenance, confidence; **after the run** — a stored record that fills up from several writers; the whole run in a dialog, **stopping**; **review surfaces compose from atoms** — `DiffValue`, `DiffMark`, `useChangeSet`. |
|
|
20
|
+
| [docs/composition.md](./docs/composition.md) | The design-language contract — **the form comes before the treatment** (name what the subject IS before reaching for parts), canvas + content column, heading altitude, register vs inset rows, the button ladder, master-detail `Drawer`, the register's rhythm, craft and TRIAGE bands, where the accent goes, theming as an app-level ESCAPE HATCH, color discipline, typography, and where a commit sits. |
|
|
21
|
+
| [docs/reviewing.md](./docs/reviewing.md) | **Reviewing a screen you built** — the other docs say what good looks like; this one says how to find out whether you achieved it. GATES that outrank every treatment question, then 11 probes each naming what to COLLECT and the SIGNATURE in the numbers. Plus the extraction snippets, what measurement cannot see, and how to act on a finding. |
|
|
22
22
|
| [docs/testing.md](./docs/testing.md) | Driving the kit in a browser — the three anatomies where the a11y tree says one thing and a driver must do another: a `PressDoor` row whose named button always intercepts pointer events (by design), portalled overlays that render at the top of the DOM, and custom pointer drag that `dragTo` cannot move. |
|
|
23
23
|
| [docs/templates.md](./docs/templates.md) | The map of `examples/tpl_*.tsx` — what shape each template solves and which to start from (copy + adapt, never import) — plus the record-surface composition rules (pipeline order, static shape, decision budget) and the ACTIVITY shape — a communications feed where the row's label is the GIST and the body varies by medium, one anatomy rather than a row type per kind. |
|
|
24
24
|
|
|
25
25
|
## Iron rules
|
|
26
26
|
|
|
27
27
|
- **Reuse first; the catalog can lag `src/`.** Before hand-rolling ANY capability, `ls src/` and
|
|
28
|
-
grep
|
|
29
|
-
|
|
28
|
+
grep it as well as reading the catalog.
|
|
29
|
+
→ [catalog.md §"How to read a component's API"](./docs/catalog.md).
|
|
30
30
|
- **One canonical component per data role** (member → `MemberChip`, SEVERAL of them →
|
|
31
31
|
`AvatarGroup`, who-is-this → `MemberPeek`/`MemberProfileCard`, select → `OptionBadge`,
|
|
32
|
-
files → `FilePreview` family, …) —
|
|
33
|
-
|
|
32
|
+
files → `FilePreview` family, …) — the catalog's Reach-by-role outranks neighboring code.
|
|
33
|
+
→ [catalog.md §"Reach by role"](./docs/catalog.md).
|
|
34
34
|
- **Responsive layout measures the CONTAINER, not the screen.** `useContainerSize()` reports the
|
|
35
|
-
nearest `SizeBoundary`; wrap one around any region whose width stops tracking its parent's
|
|
36
|
-
`
|
|
37
|
-
|
|
38
|
-
these questions and is not yours to answer: `PopoverContent` reads the screen itself and takes
|
|
39
|
-
no prop, because it fills the viewport whatever box opened it — handing it a container width is
|
|
40
|
-
how a narrow column on a wide display opened a full-bleed phone sheet.
|
|
41
|
-
`useScreenSize()` answers a question about the DEVICE — is there a keyboard
|
|
42
|
-
worth a shortcut hint, are these touch targets — and reaching for it to decide SPACE is how a
|
|
43
|
-
panel a few hundred pixels wide lays its contents out for the whole display. (A component that
|
|
44
|
-
needs a number rather than a bucket — how many columns fit — measures its own box; `Table`
|
|
45
|
-
does exactly that.) → [catalog.md §Utility hooks](./docs/catalog.md).
|
|
35
|
+
nearest `SizeBoundary`; wrap one around any region whose width stops tracking its parent's.
|
|
36
|
+
`useScreenSize()` answers a question about the DEVICE.
|
|
37
|
+
→ [catalog.md §"Utility hooks & plumbing"](./docs/catalog.md).
|
|
46
38
|
- **No user-facing string is a literal in `src/` when a locale slice already names it.** Every
|
|
47
|
-
string a component renders resolves prop → `useLoticsLocale()` slice → the pack
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
unannotated column sorts above the whole hand-written range, so one annotation makes every
|
|
54
|
-
other column drop first, and one unannotated column among annotated ones goes first however
|
|
55
|
-
important it is. **A conditionally hidden column is the same family**: the `columns` entry and
|
|
56
|
-
the row's `TableCell` are two edits, dropping only the first leaves the body one cell longer
|
|
57
|
-
than the header and shifts every column after it under its neighbour's title — derive both from
|
|
58
|
-
one value so they cannot be edited apart. →
|
|
59
|
-
[composition.md §The register's own craft](./docs/composition.md).
|
|
39
|
+
string a component renders resolves prop → `useLoticsLocale()` slice → the English pack.
|
|
40
|
+
→ [composition.md §"Localization — a rendered string is WIRED"](./docs/composition.md).
|
|
41
|
+
- **Annotate every droppable table column, or none.** A partial `priority` is the trap.
|
|
42
|
+
**A conditionally hidden column is the same family**: the `columns` entry and the row's
|
|
43
|
+
`TableCell` are two edits — derive both from one value so they cannot be edited apart.
|
|
44
|
+
→ [composition.md §"The register's own craft"](./docs/composition.md).
|
|
60
45
|
- **A section's ADD rides its heading row, right edge** — Add files, Add fee, New line: a
|
|
61
|
-
`primary` `Button` beside `SectionHeadingTitle`, rendered empty or full.
|
|
62
|
-
|
|
63
|
-
the one place it doesn't. → [composition.md §The add-placement law](./docs/composition.md).
|
|
46
|
+
`primary` `Button` beside `SectionHeadingTitle`, rendered empty or full.
|
|
47
|
+
→ [composition.md §"The add-placement law"](./docs/composition.md).
|
|
64
48
|
- **A section heading's RUNG is the surface's answer, never the call site's.** There is ONE
|
|
65
49
|
`SectionHeadingTitle`; it renders `##` xl on a page and `####` md inside a pane, because the
|
|
66
|
-
container publishes the altitude
|
|
67
|
-
|
|
68
|
-
`HeadingAltitudeContext`. Do not look for a per-surface heading variant and do not hand-pick a
|
|
69
|
-
size: a `##` inside a drawer is 24px over the drawer's own 20px title.
|
|
70
|
-
→ [composition.md §The heading ramp](./docs/composition.md).
|
|
50
|
+
container publishes the altitude.
|
|
51
|
+
→ [composition.md §"The heading ramp"](./docs/composition.md).
|
|
71
52
|
- **`EmptyState` carries NO verb, a FAILED read is not an empty one, and a BAND is not a region.**
|
|
72
53
|
Four region states, picked by what the region can ASSERT: `Skeleton`/`Loading` in flight →
|
|
73
54
|
**`ErrorState`** (`message`/`detail`/`onRetry`) on failure → `EmptyState` (succeeded, found
|
|
74
|
-
nothing) → `CompletionState` on done.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
cannot be both legible and cap-aligned in this icon set), the default for a whole region.
|
|
78
|
-
→ [catalog.md §Status / feedback](./docs/catalog.md),
|
|
79
|
-
[composition.md §Empty states](./docs/composition.md).
|
|
55
|
+
nothing) → `CompletionState` on done.
|
|
56
|
+
→ [catalog.md §"Status / feedback"](./docs/catalog.md),
|
|
57
|
+
[composition.md §"Empty states"](./docs/composition.md).
|
|
80
58
|
- **Progress: ONE compound, TWO axes — and both are about meaning.** A run of markable rows is
|
|
81
59
|
`checklist` (`Checklist` + `ChecklistGroup` + `ChecklistItem`, over `stepper`). **`connected`**
|
|
82
|
-
asks *is this a sequence
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
ring means "done" reads a ticked pick-list as work already carried out. A GROUP is a phase — a name, optionally a
|
|
87
|
-
Show/Hide disclosure, and no body: it owns no COMPLETION, and a ring that never responds invites
|
|
88
|
-
a press it refuses. Rows tick ONE at a time; derive position from the highest stamp and one click marks
|
|
89
|
-
everything below it without inventing dates. A checklist REPORTS — a field owned by a section is
|
|
90
|
-
named as a gap with a `ChecklistNote action` that jumps there, never edited or previewed in the
|
|
91
|
-
row. A per-ROW third axis, `ChecklistItem.verdict` (`"pass"`/`"fail"`), is a DECIDED outcome — a
|
|
92
|
-
QC gate, a reconciliation match — never toggled; see `docs/catalog.md` for why it withholds
|
|
93
|
-
`onToggle`. Worked in `examples/tpl_record.tsx` § Progress. See `docs/catalog.md`.
|
|
60
|
+
asks *is this a sequence*. **`mark`** asks *does ticking mean DONE or CHOSEN*.
|
|
61
|
+
Worked in `examples/tpl_record.tsx` § Progress.
|
|
62
|
+
→ [catalog.md §"Tasks, to-dos, phased work"](./docs/catalog.md),
|
|
63
|
+
[catalog.md §"Tasks & checklists"](./docs/catalog.md).
|
|
94
64
|
- **`Badge` = STATUS only; supporting detail is the muted second line.** A type / category /
|
|
95
65
|
attribute / count is not a status — it belongs under its identity as `size="xs" color="muted"`,
|
|
96
|
-
never a second chip.
|
|
97
|
-
|
|
98
|
-
[composition.md §`Badge` is for STATUS only](./docs/composition.md).
|
|
66
|
+
never a second chip. →
|
|
67
|
+
[composition.md §"`Badge` is for STATUS only"](./docs/composition.md).
|
|
99
68
|
- **A countdown ANNOTATES its date field — never a trailing badge, and never hand-rolled.**
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- **A third-party channel takes `BrandMark`, our own glyphs take `Icon`.** `@lotics/ui/brand_mark`
|
|
107
|
-
draws Facebook / Zalo / LinkedIn / X / Google Meet as solid single-colour silhouettes — a reader
|
|
108
|
-
scanning a feed recognises the logo before the word. The names are an allowlist, so a channel with
|
|
109
|
-
no mark (a phone call, an event, a website) fails to typecheck and falls back to `Icon` instead of
|
|
110
|
-
borrowing another company's logo. A product's official full-colour logo is NOT one of these: the
|
|
111
|
-
kit ships no vendor artwork, so a row that names a product passes its own image into
|
|
112
|
-
`SwitchButton`'s `leading` slot. → [catalog.md §Text & formatting](./docs/catalog.md).
|
|
69
|
+
Reach for `@lotics/ui/deadline` and spread `deadlineAnnotation` onto the
|
|
70
|
+
`DetailRow`. → [catalog.md §"Dates & times"](./docs/catalog.md).
|
|
71
|
+
- **A third-party channel takes `BrandMark`, our own glyphs take `Icon`.** The names are an
|
|
72
|
+
allowlist, so a channel with no mark (a phone call, an event, a website) fails to typecheck
|
|
73
|
+
and falls back to `Icon` instead of borrowing another company's logo.
|
|
74
|
+
→ [catalog.md §"Text & formatting"](./docs/catalog.md).
|
|
113
75
|
- **The kit's fonts/colors/icons ARE the design system** — never a custom font, icon set, or
|
|
114
76
|
hand-picked palette shade; color is `solid`/`tint`/`ramp` with ONE accent per screen.
|
|
77
|
+
→ [composition.md §"Color discipline"](./docs/composition.md).
|
|
115
78
|
- **Hand-typed type is off-system — and it always lands too small.** Every run of language is
|
|
116
|
-
`<Text size= weight= color=>` on the fixed rungs
|
|
117
|
-
`
|
|
118
|
-
`xs` (12) and `sm` (14) — which is what "looks small and cheap" literally is. `Text` is also
|
|
119
|
-
the only way to get the letter-spacing curve and Inter's `cv11 ss01 ss03` alternates, the
|
|
120
|
-
"designed" tells you cannot type by hand. **Grep the diff: `fontSize:` / `lineHeight:` /
|
|
121
|
-
`letterSpacing:` outside `src/` is a bug.** → [composition.md §Typography](./docs/composition.md).
|
|
79
|
+
`<Text size= weight= color=>` on the fixed rungs. **Grep the diff: `fontSize:` / `lineHeight:` /
|
|
80
|
+
`letterSpacing:` outside `src/` is a bug.** → [composition.md §"Typography"](./docs/composition.md).
|
|
122
81
|
- **A label is an `Eyebrow` or a `GroupLead`, and the difference is WHAT IT LABELS.** A value
|
|
123
82
|
gets `<Eyebrow>` (`xs` muted medium, dense surfaces, never uppercase); a BLOCK someone acts on
|
|
124
83
|
gets `<GroupLead>` (`sm` medium, page ink). Neither takes `size`/`weight`/`transform` — the
|
|
125
|
-
omitted props are the rule.
|
|
126
|
-
|
|
127
|
-
[composition.md §Eyebrow vs group lead](./docs/composition.md).
|
|
84
|
+
omitted props are the rule. →
|
|
85
|
+
[composition.md §"The heading ramp"](./docs/composition.md).
|
|
128
86
|
- **Pure primitives only** — no i18n, analytics, or domain types in `src/` (pass `labels`,
|
|
129
87
|
callbacks); Lotics-coupled UI belongs in `@lotics/ui-internal`.
|
|
130
88
|
- **A button never contains a control** — a row that presses open AND carries its own controls
|
|
131
89
|
(a CTA, ⋯, a checkbox, a `Link`) is a role-less `PressableRow` + a **`PressDoor`** sibling
|
|
132
90
|
(the tab stop, name, focus ring), never `PressableHighlight`, which wraps its children in the
|
|
133
|
-
button. `Table`/`TableRow` already does this.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
91
|
+
button. `Table`/`TableRow` already does this.
|
|
92
|
+
→ [composition.md §"Row actions are always-visible siblings"](./docs/composition.md).
|
|
93
|
+
- **A fact every row needs is a COLUMN; a fact one row needs expands in place.** `TableRow`'s
|
|
94
|
+
`detail` + `expanded` reveal the detail beneath the row; keep the drawer for a heavy form.
|
|
95
|
+
→ [composition.md §"No dead rows"](./docs/composition.md),
|
|
96
|
+
[catalog.md §"Lists, tables & registers"](./docs/catalog.md).
|
|
139
97
|
- **ONE LEFT EDGE — a row that paints a wash BLEEDS it, and never insets its content.**
|
|
140
98
|
`marginHorizontal: -ROW_WASH_BLEED` absorbed by an equal `paddingHorizontal` is net zero, so
|
|
141
|
-
a row's content sits on its container's own edge
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
amount to catch up, and that compensation spreads to each new surface built beside it. **An indent is legible only when something VISIBLE
|
|
147
|
-
occupies it** — a checkbox, an ordinal, a diff mark. An empty indent is a defect.
|
|
148
|
-
- **The one exception is a self-contained OVERLAY.** A menu or a selector's popover
|
|
149
|
-
(`MenuListItem`, `OptionList`) has nothing outside itself to align with, so its rows keep
|
|
150
|
-
plain padding. Bleeding there would push rows past the popover's own edge.
|
|
151
|
-
- **A slot holding another component's part takes THAT component's constant**, never a
|
|
152
|
-
hand-picked number that happens to look close. Import the owner's constant.
|
|
153
|
-
- Inside a `Dialog`, every band reads `dialogGutter(small)` — header, scroll area, footer and
|
|
154
|
-
any pane a caller drops in. A hand-copied `small ? 16 : 24` is a fifth copy of a number
|
|
155
|
-
that has to agree with four others.
|
|
156
|
-
→ [composition.md](./docs/composition.md).
|
|
99
|
+
a row's content sits on its container's own edge. **An indent is legible only when something
|
|
100
|
+
VISIBLE occupies it** — a checkbox, an ordinal, a diff mark. An empty indent is a defect.
|
|
101
|
+
- Inside a `Dialog` that edge is `useDialogGutter()` — no argument, read and never re-derived.
|
|
102
|
+
→ [composition.md §"Canvas & content column"](./docs/composition.md),
|
|
103
|
+
[composition.md §"No dead rows"](./docs/composition.md).
|
|
157
104
|
- **Documents get a LIST; images get a GRID.** The discriminator is what identifies the file to
|
|
158
|
-
the person looking for it.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
CONTENT — the filename is `IMG_4471.jpg` and tells nobody anything — so use
|
|
164
|
-
`FileThumbnailGrid`, where the picture is the identity and many fit on one screen. **A mixed
|
|
165
|
-
set follows its MAJORITY, and that one surface holds the whole set — never split a pile by
|
|
166
|
-
file type** — the cost lands on the minority by design, not on the reader, who would otherwise
|
|
167
|
-
get two places to look for one thing.
|
|
168
|
-
→ [catalog.md](./docs/catalog.md).
|
|
105
|
+
the person looking for it. Use `FileRow` / `FileRows`, where the full name is the row. Photos,
|
|
106
|
+
scans and drawings are identified by their CONTENT — so use `FileThumbnailGrid`, where the
|
|
107
|
+
picture is the identity. **A mixed set follows its MAJORITY, and that one surface holds the
|
|
108
|
+
whole set — never split a pile by file type.**
|
|
109
|
+
→ [catalog.md §"Files"](./docs/catalog.md).
|
|
169
110
|
- **A reference to another record is a FIELD** — it wears the inline editor's own resting
|
|
170
111
|
surface and PEEKS its facts on press; EVERY verb lives in the peek's footer as a `Button`,
|
|
171
|
-
none on the field
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Section, never a card, never the other record's fields as rows on this one. **A peek can
|
|
175
|
-
hold editors only as a DRAFT** — `Edit` → inputs → `Save` (which sends only what CHANGED),
|
|
176
|
-
with the open draft pinning the popover so a stray click cannot discard typing; a
|
|
177
|
-
self-persisting inline editor may never go in one, because there blur and dismiss are the
|
|
178
|
-
same gesture.
|
|
179
|
-
→ [composition.md](./docs/composition.md), [catalog.md](./docs/catalog.md).
|
|
112
|
+
none on the field. Never its own Section, never a card, never the other record's fields as
|
|
113
|
+
rows on this one.
|
|
114
|
+
→ [composition.md §"Field annotations & linked records"](./docs/composition.md), [catalog.md](./docs/catalog.md).
|
|
180
115
|
- **A verb about a VALUE goes on the value** — an `InlineButton` in the field's `actions`
|
|
181
|
-
(Copy, Open) or a **`CopyButton`** in a register cell, never the row's `trailing
|
|
182
|
-
|
|
183
|
-
makes EVERY row reserve the column. → [composition.md](./docs/composition.md).
|
|
116
|
+
(Copy, Open) or a **`CopyButton`** in a register cell, never the row's `trailing`.
|
|
117
|
+
→ [composition.md §"Field annotations & linked records"](./docs/composition.md).
|
|
184
118
|
- **A register row's text is SELECTABLE and a drag is not a press** — the row is a button wrapped
|
|
185
119
|
around DATA, so dragging across a name and copying it works, and the release that ended the
|
|
186
120
|
selection does not also open the record. Reach for `CopyButton` only where ONE value is copied
|
|
187
|
-
often enough to earn a control.
|
|
121
|
+
often enough to earn a control.
|
|
122
|
+
→ [composition.md §"Row actions are always-visible siblings"](./docs/composition.md).
|
|
188
123
|
- **Files land THREE ways on any surface that holds them** — the section's Add CTA
|
|
189
124
|
(`pickFiles`), and a drag OR paste, both from ONE **`<FileDropTarget … paste>`** wrapping the
|
|
190
|
-
region
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Paste routes to the top-most FOCUSED region, else the top of the stack — two peer file sections
|
|
194
|
-
each win when focus is in them. Nobody should hunt for a dropzone; a `FileDropzone` well is the
|
|
195
|
-
empty state, not the only way in. Since a wrapped region shows no zone, name the paths with a
|
|
196
|
-
muted heading description ("Drag, paste, or click to add files"). → [data_entry.md](./docs/data_entry.md).
|
|
125
|
+
region. Nobody should hunt for a dropzone; a `FileDropzone` well is the empty state, not the
|
|
126
|
+
only way in.
|
|
127
|
+
→ [data_entry.md §"Intake — files land THREE ways"](./docs/data_entry.md).
|
|
197
128
|
- **Every state designed** — skeleton (mirroring layout), empty, error; no layout shift.
|
|
129
|
+
→ [catalog.md §"Badges, status & feedback"](./docs/catalog.md).
|
|
198
130
|
- **Copy states content, not mechanics** — a `description` names the section's content or one
|
|
199
131
|
domain rule; widget behavior ("edits in place, saves on its own") and selling points never
|
|
200
|
-
appear. Instructions live in empty states / form-field hints.
|
|
132
|
+
appear. Instructions live in empty states / form-field hints.
|
|
133
|
+
→ [composition.md §"Microcopy"](./docs/composition.md).
|
|
201
134
|
|
|
202
135
|
## Keeping this reference current
|
|
203
136
|
|