@slotted/angular 0.1.0-beta.1 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/docs/AGENTS.snippet.md +10 -0
- package/docs/checkbox.json +7 -0
- package/docs/checkbox.md +1 -0
- package/docs/event-calendar.json +7 -0
- package/docs/event-calendar.md +1 -0
- package/docs/guidelines/README.md +27 -0
- package/docs/guidelines/async-feedback.md +108 -0
- package/docs/guidelines/browsing.md +74 -0
- package/docs/guidelines/builders.md +97 -0
- package/docs/guidelines/composition.md +129 -0
- package/docs/guidelines/conversations.md +59 -0
- package/docs/guidelines/errors-and-conflicts.md +71 -0
- package/docs/guidelines/forms.md +192 -0
- package/docs/guidelines/index.md +121 -0
- package/docs/guidelines/layout-alignment.md +95 -0
- package/docs/guidelines/ledgers.md +45 -0
- package/docs/guidelines/list-pages.md +165 -0
- package/docs/guidelines/overviews.md +66 -0
- package/docs/guidelines/own-elements.md +115 -0
- package/docs/guidelines/reading-permissions.md +55 -0
- package/docs/guidelines/record-pages.md +178 -0
- package/docs/guidelines/reports.md +123 -0
- package/docs/guidelines/shell-composition.md +94 -0
- package/docs/guidelines/workspaces.md +46 -0
- package/docs/index.md +12 -0
- package/docs/progress-indicator.json +1 -2
- package/docs/progress-indicator.md +2 -2
- package/docs/skills/build-own-element/SKILL.md +90 -0
- package/docs/skills/compose-screen/SKILL.md +76 -0
- package/docs/skills/index.md +9 -0
- package/docs/skills/review-screen/SKILL.md +70 -0
- package/docs/skills/theme-app/SKILL.md +74 -0
- package/docs/skills/waiting-states/SKILL.md +139 -0
- package/docs/stat-card.json +1 -0
- package/docs/stat-card.md +1 -1
- package/docs/tabs.json +1 -4
- package/docs/tabs.md +1 -1
- package/docs/tokens.md +424 -0
- package/fesm2022/slotted-angular-app-shell.mjs +2 -2
- package/fesm2022/slotted-angular-app-shell.mjs.map +1 -1
- package/fesm2022/slotted-angular-checkbox.mjs +11 -2
- package/fesm2022/slotted-angular-checkbox.mjs.map +1 -1
- package/fesm2022/slotted-angular-column-manager.mjs +1 -1
- package/fesm2022/slotted-angular-column-manager.mjs.map +1 -1
- package/fesm2022/slotted-angular-combobox.mjs +12 -6
- package/fesm2022/slotted-angular-combobox.mjs.map +1 -1
- package/fesm2022/slotted-angular-dashboard-grid.mjs +18 -22
- package/fesm2022/slotted-angular-dashboard-grid.mjs.map +1 -1
- package/fesm2022/slotted-angular-data-grid.mjs +6 -2
- package/fesm2022/slotted-angular-data-grid.mjs.map +1 -1
- package/fesm2022/slotted-angular-data-list.mjs +1 -1
- package/fesm2022/slotted-angular-data-list.mjs.map +1 -1
- package/fesm2022/slotted-angular-drawer.mjs +5 -1
- package/fesm2022/slotted-angular-drawer.mjs.map +1 -1
- package/fesm2022/slotted-angular-event-calendar.mjs +17 -7
- package/fesm2022/slotted-angular-event-calendar.mjs.map +1 -1
- package/fesm2022/slotted-angular-loading-overlay.mjs +2 -2
- package/fesm2022/slotted-angular-markdown.mjs +1 -1
- package/fesm2022/slotted-angular-markdown.mjs.map +1 -1
- package/fesm2022/slotted-angular-multi-select.mjs +32 -27
- package/fesm2022/slotted-angular-multi-select.mjs.map +1 -1
- package/fesm2022/slotted-angular-number-field.mjs +2 -2
- package/fesm2022/slotted-angular-number-field.mjs.map +1 -1
- package/fesm2022/slotted-angular-progress-indicator.mjs +6 -6
- package/fesm2022/slotted-angular-select.mjs +1 -25
- package/fesm2022/slotted-angular-select.mjs.map +1 -1
- package/fesm2022/slotted-angular-stat-card.mjs +2 -2
- package/fesm2022/slotted-angular-stat-card.mjs.map +1 -1
- package/fesm2022/slotted-angular-table.mjs +3 -3
- package/fesm2022/slotted-angular-table.mjs.map +1 -1
- package/fesm2022/slotted-angular-tabs.mjs +2 -2
- package/fesm2022/slotted-angular-tabs.mjs.map +1 -1
- package/fesm2022/slotted-angular-transfer.mjs +1 -1
- package/fesm2022/slotted-angular-transfer.mjs.map +1 -1
- package/fesm2022/slotted-angular-upload.mjs +2 -2
- package/package.json +3 -3
- package/types/slotted-angular-checkbox.d.ts +7 -1
- package/types/slotted-angular-checkbox.d.ts.map +1 -1
- package/types/slotted-angular-combobox.d.ts.map +1 -1
- package/types/slotted-angular-dashboard-grid.d.ts.map +1 -1
- package/types/slotted-angular-data-grid.d.ts.map +1 -1
- package/types/slotted-angular-drawer.d.ts.map +1 -1
- package/types/slotted-angular-event-calendar.d.ts +9 -2
- package/types/slotted-angular-event-calendar.d.ts.map +1 -1
- package/types/slotted-angular-multi-select.d.ts +10 -0
- package/types/slotted-angular-multi-select.d.ts.map +1 -1
- package/types/slotted-angular-select.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,10 @@ export const appConfig = {
|
|
|
47
47
|
};
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
## Agents
|
|
51
|
+
|
|
52
|
+
`docs/AGENTS.snippet.md` is the section an application adds to the instructions its coding agent reads: `AGENTS.md`, or `CLAUDE.md` for Claude Code, directly or through `@AGENTS.md`. It points the agent at the family pages, the guidelines and the skills of the installed version.
|
|
53
|
+
|
|
50
54
|
## License
|
|
51
55
|
|
|
52
56
|
MIT
|
package/docs/AGENTS.snippet.md
CHANGED
|
@@ -8,3 +8,13 @@ Before writing or changing UI, read `node_modules/@slotted/angular/docs/index.md
|
|
|
8
8
|
- Change a family's appearance through its inputs (`size`, `variant`, `tone` where it has them), not with CSS that targets its parts.
|
|
9
9
|
- Application CSS uses `var(--slotted-*)` tokens for color, spacing and radius, inside a layer declared after the library's: `@layer slotted, app;`.
|
|
10
10
|
- In CSS and tests, select a family by `[data-ui="<family>"]` and its parts by `[data-part="<part>"]`.
|
|
11
|
+
|
|
12
|
+
### Tasks
|
|
13
|
+
|
|
14
|
+
Read the skill before the task:
|
|
15
|
+
|
|
16
|
+
- Compose or change a screen — a list, a record, a form, an overview: `node_modules/@slotted/angular/docs/skills/compose-screen/SKILL.md`
|
|
17
|
+
- Show what a screen draws while it waits, and its empty and error states: `node_modules/@slotted/angular/docs/skills/waiting-states/SKILL.md`
|
|
18
|
+
- Review a screen against the guidelines, or fix what a review found: `node_modules/@slotted/angular/docs/skills/review-screen/SKILL.md`
|
|
19
|
+
- Write a component or element that no family draws: `node_modules/@slotted/angular/docs/skills/build-own-element/SKILL.md`
|
|
20
|
+
- Change the brand colour, the scheme, the density or a control's shape: `node_modules/@slotted/angular/docs/skills/theme-app/SKILL.md`
|
package/docs/checkbox.json
CHANGED
|
@@ -172,6 +172,13 @@
|
|
|
172
172
|
"appliesTo": "SlCheckbox",
|
|
173
173
|
"description": "Unset rather than false. Writes aria-invalid on the input and data-invalid on the root, which draws a thicker border in the danger tone."
|
|
174
174
|
},
|
|
175
|
+
{
|
|
176
|
+
"name": "tabIndex",
|
|
177
|
+
"type": "number",
|
|
178
|
+
"defaultValue": "—",
|
|
179
|
+
"appliesTo": "SlCheckbox",
|
|
180
|
+
"description": "Written to the input as tabindex. A composite widget that owns the tab sequence, such as a data grid, passes -1 until the box’s cell is current. Unset leaves the native order."
|
|
181
|
+
},
|
|
175
182
|
{
|
|
176
183
|
"name": "required",
|
|
177
184
|
"type": "boolean",
|
package/docs/checkbox.md
CHANGED
|
@@ -62,6 +62,7 @@ export class CheckboxBasic {}
|
|
|
62
62
|
| `disabled` | `boolean` | from the form control, group or field, or false | SlCheckbox | Unset rather than false, so the box follows a disabled form control, then its checkbox group or field. Written to the input as the native attribute, which removes the box from the tab order and from form submission. |
|
|
63
63
|
| `readOnly` | `boolean` | from the group or field, or false | SlCheckbox | Unset rather than false. A native checkbox has no readonly, so SlCheckbox refuses every change and writes aria-readonly. The box keeps its tab stop, its focus ring and its contrast, and its value is still submitted. |
|
|
64
64
|
| `invalid` | `boolean` | from the group or field, or false | SlCheckbox | Unset rather than false. Writes aria-invalid on the input and data-invalid on the root, which draws a thicker border in the danger tone. |
|
|
65
|
+
| `tabIndex` | `number` | — | SlCheckbox | Written to the input as tabindex. A composite widget that owns the tab sequence, such as a data grid, passes -1 until the box’s cell is current. Unset leaves the native order. |
|
|
65
66
|
| `required` | `boolean` | — | the host element | The native attribute, read once as a static attribute and passed to the input unchanged; SlCheckbox never writes it. A field marks necessity with aria-required instead, because the native attribute turns on browser constraint validation. |
|
|
66
67
|
| `name / value` | `string` | — / on | SlCheckbox | The native submission pair: a checked box submits value under name. Inside a checkbox group, a box without a name takes the group’s, and value is the entry the group’s selection holds. |
|
|
67
68
|
| `exportAs="slCheckbox"` | `SlCheckbox` | — | a template | Reaches the component instance. Its inputElement is the input, whose own focus() and click() methods move focus and toggle the box. |
|
package/docs/event-calendar.json
CHANGED
|
@@ -559,6 +559,13 @@
|
|
|
559
559
|
"appliesTo": "slEventCalendar",
|
|
560
560
|
"description": "Read once; pass it when rendering on a server"
|
|
561
561
|
},
|
|
562
|
+
{
|
|
563
|
+
"name": "now",
|
|
564
|
+
"type": "PlainDateTime",
|
|
565
|
+
"defaultValue": "the clock in timeZone, ticked each minute",
|
|
566
|
+
"appliesTo": "slEventCalendar",
|
|
567
|
+
"description": "Present means the consumer owns it: the clock is no longer read, and today and the now line follow it. A page dated by its content passes it."
|
|
568
|
+
},
|
|
562
569
|
{
|
|
563
570
|
"name": "view",
|
|
564
571
|
"type": "model<EventCalendarView | undefined>",
|
package/docs/event-calendar.md
CHANGED
|
@@ -112,6 +112,7 @@ Excerpt — not a complete module
|
|
|
112
112
|
| `calendars` | `readonly EventCalendarCalendar[]` | [] | slEventCalendar | Key, label and an optional colour slot 1–8 |
|
|
113
113
|
| `date` | `model<PlainDate \| undefined>` | — | slEventCalendar | The day the calendar is on; [(date)] two-way |
|
|
114
114
|
| `defaultDate` | `PlainDate` | today in timeZone | slEventCalendar | Read once; pass it when rendering on a server |
|
|
115
|
+
| `now` | `PlainDateTime` | the clock in timeZone, ticked each minute | slEventCalendar | Present means the consumer owns it: the clock is no longer read, and today and the now line follow it. A page dated by its content passes it. |
|
|
115
116
|
| `view` | `model<EventCalendarView \| undefined>` | month | slEventCalendar | [(view)] two-way; unbound opens on the first of views |
|
|
116
117
|
| `defaultView` | `EventCalendarView` | views[0] | slEventCalendar | Read once |
|
|
117
118
|
| `views` | `readonly EventCalendarView[]` | month,week,day,list | slEventCalendar | The switcher, in order |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Guidelines
|
|
2
|
+
|
|
3
|
+
Rules for composing screens from the Slotted families: what the reference
|
|
4
|
+
pages do not state. A family's page states what it is — its parts, states,
|
|
5
|
+
tokens and keyboard. It does not state where a pager stands, what a region
|
|
6
|
+
shows before its first answer, or which family a bar's search slot is for.
|
|
7
|
+
|
|
8
|
+
## Reading protocol
|
|
9
|
+
|
|
10
|
+
For a reader with a task — a person or a model about to write or review a
|
|
11
|
+
screen:
|
|
12
|
+
|
|
13
|
+
1. Read `index.md`: every active rule in one line, with its level and its file.
|
|
14
|
+
2. Match the task against the `triggers` in each topic file's front matter.
|
|
15
|
+
Read the topic files that match, and `composition.md` always.
|
|
16
|
+
3. Before writing an element beside a family, read that family's page in
|
|
17
|
+
`node_modules/@slotted/angular/docs`. Before writing an element no family draws, read
|
|
18
|
+
`own-elements.md` and `node_modules/@slotted/angular/docs/tokens.md`.
|
|
19
|
+
4. Apply the rules by level: a `MUST` broken is a defect, a `SHOULD` broken is
|
|
20
|
+
stated with its reason, a `MAY` is a permission.
|
|
21
|
+
5. A screen is done when each `Check` of the rules read passes.
|
|
22
|
+
|
|
23
|
+
## Rule identity
|
|
24
|
+
|
|
25
|
+
An id (`AF-03`) never changes meaning. A rule whose meaning or level changes
|
|
26
|
+
is retired, keeps its heading with `Status: retired`, and is rewritten under a
|
|
27
|
+
new id. The release notes of each version list the ids added and retired.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
topic: async feedback
|
|
3
|
+
prefix: AF
|
|
4
|
+
triggers:
|
|
5
|
+
[
|
|
6
|
+
loading,
|
|
7
|
+
pending,
|
|
8
|
+
busy,
|
|
9
|
+
empty,
|
|
10
|
+
skeleton,
|
|
11
|
+
placeholder,
|
|
12
|
+
request,
|
|
13
|
+
fetch,
|
|
14
|
+
mutation,
|
|
15
|
+
tab,
|
|
16
|
+
view,
|
|
17
|
+
page,
|
|
18
|
+
pagination,
|
|
19
|
+
popover,
|
|
20
|
+
refresh,
|
|
21
|
+
latency,
|
|
22
|
+
spinner,
|
|
23
|
+
submit,
|
|
24
|
+
action,
|
|
25
|
+
]
|
|
26
|
+
families:
|
|
27
|
+
[skeleton, button, popover, list, list-item, notification-center, tabs, pagination, result-state]
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Async feedback
|
|
31
|
+
|
|
32
|
+
What a screen shows between a request starting and its answer arriving. The
|
|
33
|
+
library gives the states — `skeleton` for a region, `loading` on a control,
|
|
34
|
+
`data-loading` and `aria-busy` on the families that fill themselves — and does
|
|
35
|
+
not say which one a screen owes at which moment. A request answers within a
|
|
36
|
+
second on a fast network and takes several on a slow one; a screen that shows
|
|
37
|
+
nothing for that long reads as a stall, and a screen that shows its empty
|
|
38
|
+
state reads as an answer.
|
|
39
|
+
|
|
40
|
+
### AF-01 · Pending is not empty
|
|
41
|
+
|
|
42
|
+
Level: MUST
|
|
43
|
+
Applies to: any region filled by a request — a `popover` body, a `list`, a `data-grid`, a `notification-center` feed, a page section
|
|
44
|
+
Rule: A region whose first answer has not arrived shows its pending state; the empty state appears only after an answer holding zero items.
|
|
45
|
+
Why: An empty state is a statement about the data, and before the answer there is no data to state anything about.
|
|
46
|
+
Do: Keep a loading fact beside the items, set before the request and cleared after it. Render placeholders while the fact is true and the items are none; render the empty state while the fact is false and the items are none.
|
|
47
|
+
Don't: Branch on the items alone. A region that opens on "Nothing waiting for you" while the request is in flight tells the reader the queue is clear, for as long as the request takes.
|
|
48
|
+
Check: Throttle the network to a slow profile and open the region cold; the empty words do not appear before the answer.
|
|
49
|
+
|
|
50
|
+
### AF-02 · First answer draws placeholders, later answers keep the rows
|
|
51
|
+
|
|
52
|
+
Level: SHOULD
|
|
53
|
+
Applies to: every region under AF-01 that is filled more than once — a panel opened again, a list read back after a mutation
|
|
54
|
+
Rule: A region with no items yet draws placeholders; a region that already shows items keeps them on screen and marks itself busy while a newer answer is on its way.
|
|
55
|
+
Why: Placeholders in place of rows the reader was looking at erase their place; rows silently replaced give no sign anything happened.
|
|
56
|
+
Do: Placeholders are the family `skeleton`: a region carrying the state and, inside it, the rows' own boxes — the mark, the label line, the description line — so the answer lands without a layout shift. A placeholder line is a fraction of its row, never a length: a length wider than a phone's row overflows the page and scrolls it sideways. While items are shown and a request runs, the list carries `aria-busy` and the rows stay.
|
|
57
|
+
Don't: Draw placeholders on every request, or clear the items before every request. Reading a list back after marking one row is a refresh, not a first load.
|
|
58
|
+
Check: Open the region twice with the network throttled to a slow profile: placeholders on the first open, the previous rows and no placeholders on the second.
|
|
59
|
+
|
|
60
|
+
### AF-03 · The control that starts a request waits on it
|
|
61
|
+
|
|
62
|
+
Level: MUST
|
|
63
|
+
Applies to: any `button` member — button, icon button, toggle button — whose activation runs a request — mark as read, archive, approve, save, submit
|
|
64
|
+
Rule: From activation until the answer, the control is in its `loading` state: it refuses a second activation and shows the family's indicator.
|
|
65
|
+
Why: A control that looks idle invites a second press, and the second request is a duplicate the backend has to refuse or the reader has to undo.
|
|
66
|
+
Do: Keep, beside the data, the identity of what is in flight — the row's key for a row action, one fact for a list-wide action — and bind the control's `loading` to it. The family refuses activation on its own and names the wait for assistive technology through its loading label.
|
|
67
|
+
Don't: Disable the control by hand, hide it, or leave it as it was until the row disappears. Disabled says "not allowed"; loading says "working on it".
|
|
68
|
+
Check: Press the control with the network throttled to a slow profile: the indicator appears, a second press does nothing, the row changes once.
|
|
69
|
+
|
|
70
|
+
### AF-04 · A change of view or page draws placeholders
|
|
71
|
+
|
|
72
|
+
Level: SHOULD
|
|
73
|
+
Applies to: `tabs` that switch the content of a region, `pagination`, any filter whose answer replaces the rows rather than refreshing them
|
|
74
|
+
Rule: When the reader asks for different rows, the rows on screen are removed and placeholders stand in their place until the answer arrives.
|
|
75
|
+
Why: The rows on screen are the previous answer; leaving them under the new tab says the tab did nothing until, half a second later, it does.
|
|
76
|
+
Do: Empty the items on the change and let AF-01 draw the placeholders. Keep what does not change — the tab strip, the pager, the header — where it is.
|
|
77
|
+
Don't: Keep the old rows until the new ones arrive, or replace the whole region including the tab strip with a placeholder block.
|
|
78
|
+
Check: Switch the view with the network throttled to a slow profile: placeholders at once, the tab strip and the pager still in place, rows on the answer.
|
|
79
|
+
|
|
80
|
+
### AF-05 · An indicator may wait for the request to be slow
|
|
81
|
+
|
|
82
|
+
Level: MAY
|
|
83
|
+
Applies to: a control under AF-03 whose request usually answers under the indicator's own entrance
|
|
84
|
+
Rule: A control's indicator may be delayed by the family's loading delay, so a request that answers first shows no indicator; a region's placeholders are never delayed.
|
|
85
|
+
Why: An indicator that appears and leaves within one frame is a flicker, and a region with nothing in it is a stall from the first frame.
|
|
86
|
+
Do: Use the family's delay on a control whose request is short in the normal case and leave the control refusing activation from the first instant, which the family does regardless of the delay.
|
|
87
|
+
Don't: Delay a region's placeholders, or delay a control's refusal of activation.
|
|
88
|
+
Check: On an unthrottled network a short request shows no indicator and still ignores a second press; with the network throttled to a slow profile, the indicator appears.
|
|
89
|
+
|
|
90
|
+
### AF-06 · Placeholders are held once they are drawn
|
|
91
|
+
|
|
92
|
+
Level: SHOULD
|
|
93
|
+
Applies to: every region under AF-01, through the application's own defaults for the family
|
|
94
|
+
Rule: The application states one minimum duration for the whole product: once placeholders are painted they stay for it, even when the answer has already arrived.
|
|
95
|
+
Why: A request on a fast network answers within a few hundred milliseconds, so an undelayed region paints and is replaced inside a few frames — a blink the reader reads as a fault rather than as a wait.
|
|
96
|
+
Do: State the minimum where the application states its other family defaults, and leave the delay at nothing: AF-05 keeps a region's placeholders undelayed, and the minimum is the other half of the same decision.
|
|
97
|
+
Don't: Set the minimum per screen, or reach for a delay to stop the blink — a delay is a region showing nothing for as long as it lasts.
|
|
98
|
+
Check: On an unthrottled network, open a screen of placeholders twice: the placeholders are visible on both, and no region paints and empties within one frame.
|
|
99
|
+
|
|
100
|
+
### AF-07 · A record another actor changes offers Refresh
|
|
101
|
+
|
|
102
|
+
Level: SHOULD
|
|
103
|
+
Applies to: a record page, or a list, whose data another person or process can change while it is open — an order, an invoice, a customer another writer edits
|
|
104
|
+
Rule: The page offers Refresh among the header's actions, and a refresh keeps the values on screen, marks the page busy and replaces them when the answer arrives.
|
|
105
|
+
Why: Without it the reader reloads the browser, which loses the scroll, the open tab and any draft, and a refresh that blanks the record flickers what was already read.
|
|
106
|
+
Do: Put an icon button named "Refresh" in the header's actions toolbar with its menu counterpart, in its `loading` state until the answer (AF-03); bind the page's `loading` to "reading again while a record is shown", so the body takes `aria-busy` and draws no placeholder.
|
|
107
|
+
Don't: Offer Refresh on a record nothing else writes, or clear the record and draw its skeleton on every read after the first.
|
|
108
|
+
Check: Press Refresh on a record another writer changes: the values stay, the page body is busy until the answer, and the button waits on it.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
topic: browsing
|
|
3
|
+
prefix: BR
|
|
4
|
+
triggers:
|
|
5
|
+
[
|
|
6
|
+
tree,
|
|
7
|
+
category,
|
|
8
|
+
categories,
|
|
9
|
+
hierarchy,
|
|
10
|
+
branch,
|
|
11
|
+
leaf,
|
|
12
|
+
taxonomy,
|
|
13
|
+
browse,
|
|
14
|
+
facet,
|
|
15
|
+
drill,
|
|
16
|
+
place,
|
|
17
|
+
scope,
|
|
18
|
+
breadcrumb,
|
|
19
|
+
count,
|
|
20
|
+
grain,
|
|
21
|
+
catalogue,
|
|
22
|
+
]
|
|
23
|
+
families: [tree, breadcrumb, page-header, filter-bar, data-grid]
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Browsing
|
|
27
|
+
|
|
28
|
+
How a reader moves through a set that is arranged rather than listed: a tree
|
|
29
|
+
of categories over a list of records, and one concept a backend answers for at
|
|
30
|
+
two grains. The library gives `tree` a hierarchy with a keyboard and a query,
|
|
31
|
+
`breadcrumb` a trail and `filter-bar` a tree of conditions; it does not say
|
|
32
|
+
whether a branch is a filter or a place, which of a node's two counts a row
|
|
33
|
+
draws, or how a list that reads products and a list that reads stock lead to
|
|
34
|
+
each other.
|
|
35
|
+
|
|
36
|
+
### BR-01 · A branch is the list's place, not one of its pills
|
|
37
|
+
|
|
38
|
+
Level: SHOULD
|
|
39
|
+
Applies to: a list reached by choosing a node in a `tree`, and the `filter-bar` above that list
|
|
40
|
+
Rule: The branch a reader chose is the list's heading and its `breadcrumb`; the filter bar keeps the facets and holds no pill for it.
|
|
41
|
+
Why: A place is where the reader is and a facet is what they asked for; a branch drawn as a pill reads as one condition among many, and the way back out of it becomes a chip to delete rather than a trail to walk.
|
|
42
|
+
Do: Name the list after the branch, put the trail from the catalogue's root in the header's breadcrumb with every ancestor a link, and narrow the query from the place rather than from the bar. The way out is the trail's first crumb, which names the whole set.
|
|
43
|
+
Don't: Write the branch into the bar's tree, or leave the list titled after the resource while its rows come from one branch.
|
|
44
|
+
Check: Open a branch from the tree: the heading is the branch's name, the trail stands above it, and the bar's pill count is unchanged.
|
|
45
|
+
|
|
46
|
+
### BR-02 · A place holds what is under it, and says how much of it there is
|
|
47
|
+
|
|
48
|
+
Level: MUST
|
|
49
|
+
Applies to: a list narrowed to a branch of a hierarchy
|
|
50
|
+
Rule: The query covers the branch and every node beneath it, and the page states how many nodes that is beside how many records they hold.
|
|
51
|
+
Why: A backend that files a record on a leaf answers nothing for the branch above it; a list that asked for the branch alone would tell a reader a whole department is empty, and a list that asks for the subtree without saying so leaves a count nobody can reconcile with the tree.
|
|
52
|
+
Do: Read the hierarchy before the rows, send the branch and its descendants as one condition, and write the sentence under the title: how many records the place holds, how many nodes it covers, and how many the whole set holds.
|
|
53
|
+
Don't: Narrow to the chosen node alone, or state a count without saying what it is counted over.
|
|
54
|
+
Check: Open a branch whose own records are none: the list is not empty and the sentence names the nodes it covers.
|
|
55
|
+
|
|
56
|
+
### BR-03 · A node's own count and its branch's count are two numbers
|
|
57
|
+
|
|
58
|
+
Level: MUST
|
|
59
|
+
Applies to: a count drawn on a `tree` node
|
|
60
|
+
Rule: The number on a row is one of the two and the screen says which; where a node holds records of its own and has nodes under it, the second number is said apart from the first.
|
|
61
|
+
Why: The two answer different questions — how big this branch is, and how much is filed at this level — and a tree that prints one of them without saying which is read as the other.
|
|
62
|
+
Do: Put the branch's whole count in the row's trailing cell, the one the family keeps at its intrinsic width, and say in the region's own words what that number counts. Where a node also holds records directly, say how many on the row's second line, which the family already draws in the muted step.
|
|
63
|
+
Don't: Draw the backend's own field without adding the descendants, or print two bare numbers on one row.
|
|
64
|
+
Check: A branch whose records are all on its leaves shows the leaves' total, not nought.
|
|
65
|
+
|
|
66
|
+
### BR-04 · One concept at two grains is two lists that name each other
|
|
67
|
+
|
|
68
|
+
Level: SHOULD
|
|
69
|
+
Applies to: a resource the backend answers for at two grains — a family header and the item that is counted, ordered and priced
|
|
70
|
+
Rule: Each grain is its own route with its own columns and its own filters, and each page names the other above its rows.
|
|
71
|
+
Why: The two grains are asked different questions and filtered by different fields; one bar cannot hold both sets of conditions, and a reader looking for what is below its reorder point is not looking for a family header.
|
|
72
|
+
Do: Give each list the filters its own resource offers, say in one sentence what the grain is and what the other one is for, and carry the reader's place from one to the other so a branch survives the move.
|
|
73
|
+
Don't: Put a view switch over one filter bar whose fields belong to one of the two, or leave the second grain reachable only through a record of the first.
|
|
74
|
+
Check: The two routes are in the navigation, each header links to the other, and a place chosen on one is still the place on the other.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
topic: builders
|
|
3
|
+
prefix: BD
|
|
4
|
+
triggers:
|
|
5
|
+
[
|
|
6
|
+
builder,
|
|
7
|
+
rule,
|
|
8
|
+
rule builder,
|
|
9
|
+
condition,
|
|
10
|
+
condition tree,
|
|
11
|
+
nested group,
|
|
12
|
+
workflow,
|
|
13
|
+
automation,
|
|
14
|
+
designer,
|
|
15
|
+
definition,
|
|
16
|
+
and,
|
|
17
|
+
or,
|
|
18
|
+
drag,
|
|
19
|
+
reorder,
|
|
20
|
+
move,
|
|
21
|
+
limit,
|
|
22
|
+
depth,
|
|
23
|
+
flow,
|
|
24
|
+
trace,
|
|
25
|
+
run history,
|
|
26
|
+
]
|
|
27
|
+
families: [segmented-control, select, multi-select, menu, inline-message, typography]
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Builders
|
|
31
|
+
|
|
32
|
+
How a screen lets a person write a rule the system runs later — a trigger, a
|
|
33
|
+
tree of conditions, the actions — and how that rule is read by someone who
|
|
34
|
+
cannot change it. The library gives `filter-bar` a builder for a filter
|
|
35
|
+
applied to a list, `menu` a place for a node's commands and the field families
|
|
36
|
+
an operand; it does not say how a stored rule reads aloud, where a limit the
|
|
37
|
+
backend enforces is said, or how a tree narrows to a phone.
|
|
38
|
+
|
|
39
|
+
### BD-01 · A definition reads as a rule before a form
|
|
40
|
+
|
|
41
|
+
Level: SHOULD
|
|
42
|
+
Applies to: a stored rule — a workflow's trigger, conditions and actions — on its record
|
|
43
|
+
Rule: The record shows the rule as it reads — When, If, Then, Otherwise — every later line of the condition tree led by its join in words and every nested group opened by its own ("any of:", "all of:"); the editor opens from that reading in the section it is read in; a reader without the permission to edit reads the rule and is not shown an editor.
|
|
44
|
+
Why: A rule shown only as a form makes a reader who cannot edit it decode disabled controls, and makes one who can open an editor to learn what the rule does.
|
|
45
|
+
Do: Set the trigger beside "When", the conditions the way a query is set — the join in a column of its own at the start of each later line, a group's children stepped in one step under the line that opens it, a guide down every level — and the actions numbered under "Then". The editor is a query parameter of the record (FM-12) and stands in the same section (FM-07).
|
|
46
|
+
Don't: Draw the editor in a read-only state for a reader, or join the conditions by colour, a border or indentation alone.
|
|
47
|
+
Check: Open a stored rule as a reader who may edit it and as one who may not: both read When, If, Then, Otherwise with every later line led by its join in words, and only the first is offered the editor.
|
|
48
|
+
|
|
49
|
+
### BD-02 · A structural limit is said before it is reached
|
|
50
|
+
|
|
51
|
+
Level: SHOULD
|
|
52
|
+
Applies to: a builder whose backend limits the tree — conditions per rule, levels of nesting
|
|
53
|
+
Rule: Under the tree the builder states what it holds and what remains of each limit, and a control that would cross a limit is disabled with the limit as its stated reason.
|
|
54
|
+
Why: A limit met only as a refusal after the save is a tree written past what the backend accepts, and a control that silently does nothing at the limit reads as a broken control.
|
|
55
|
+
Do: Write one line under the root: the conditions and levels used of their maxima, and how many more of each can be added. At a limit, the add control in the group's foot is disabled and the same item in the group's `menu` carries the limit in its `description`.
|
|
56
|
+
Don't: Let the add control stay live at the limit and answer with the backend's refusal.
|
|
57
|
+
Check: Fill a tree to one of its limits: the line under the tree says nothing remains of it, and each control that would cross it is disabled with the limit as its reason.
|
|
58
|
+
|
|
59
|
+
### BD-03 · A refusal that names no node is marked at the node before the request
|
|
60
|
+
|
|
61
|
+
Level: MUST
|
|
62
|
+
Applies to: a tree the backend validates and refuses under one field, with no path to the node
|
|
63
|
+
Rule: The builder runs the backend's own check on every change and, once a save is tried, marks each node it would refuse with its message before any request is sent; a group with nothing in it is marked from the moment it is empty.
|
|
64
|
+
Why: After the request the screen knows only that the conditions were refused, not which of fifty; before it, the same check knows the node.
|
|
65
|
+
Do: Run the backend's validation code in the browser rather than writing a second one, key its issues by the node's path, outline the node and put the message under it, and keep the form's summary for the same messages (FM-03).
|
|
66
|
+
Don't: Send the tree and translate the backend's list of messages back onto nodes, or mark every node as invalid while the reader is still writing.
|
|
67
|
+
Check: Save a tree that holds an empty group and a node past a limit: each is marked at its node with its message, and the network log holds no request.
|
|
68
|
+
|
|
69
|
+
### BD-04 · The keyboard and the pointer make the same moves
|
|
70
|
+
|
|
71
|
+
Level: SHOULD
|
|
72
|
+
Applies to: moving the nodes of a tree — up, down, into the group above, out of the group
|
|
73
|
+
Rule: Every node's menu carries its moves and its removal, the grip's drag makes the same moves, and both refuse the same places — a group into itself, a level past the limit — with the reason said.
|
|
74
|
+
Why: A drag with no menu shuts out the keyboard and a phone's scrolling finger, and two code paths with two sets of boundaries let one of them build a tree the other refuses.
|
|
75
|
+
Do: Compute every move from one function over the tree and its limits; disable a menu move that would be refused, with the reason as the item's `description`; outline the place a drag is over when it is refused and announce the reason politely; keep the focus on the moved node's menu after a keyboard move.
|
|
76
|
+
Don't: Offer dragging alone, or let the drop answer with a backend refusal.
|
|
77
|
+
Check: Move one node with the keyboard from its menu and the same node by its grip: both reach the same places, both refuse a group dropped into itself with the reason said, and after the keyboard move the focus is on the node's menu.
|
|
78
|
+
|
|
79
|
+
### BD-05 · A tree narrows by stepping in, never by scrolling
|
|
80
|
+
|
|
81
|
+
Level: SHOULD
|
|
82
|
+
Applies to: a nested builder below the width its rows' three controls need, at 570 pixels and on a phone
|
|
83
|
+
Rule: Each level steps in by one space step behind a rule, and below the width a condition's field, operator and value fit on one line the field takes its own line and the operator and the value share the next; the tree never scrolls sideways.
|
|
84
|
+
Why: A tree five levels deep that indents by a column, or keeps three controls on one line, is wider than a phone, and a builder that scrolls sideways hides the join of the level the reader is in.
|
|
85
|
+
Do: Make the builder a container and switch the row's grid on its own width (OE-09), keep the grip and the node's menu at the row's two ends with the words between them wrapping, and leave the operator's popup to the family, which keeps it inside the viewport.
|
|
86
|
+
Don't: Indent by more than one space step per level, or give the tree a horizontal scroller.
|
|
87
|
+
Check: At 390 and 360 pixels open a tree five levels deep: nothing scrolls sideways, each level steps in by one space step, and a condition's field stands on its own line.
|
|
88
|
+
|
|
89
|
+
### BD-06 · A rule's record shows what flowed through it, and the rule carries its own figures
|
|
90
|
+
|
|
91
|
+
Level: SHOULD
|
|
92
|
+
Applies to: a stored rule's record, its run history and its editor — a trigger, a decision over a condition tree, the actions in order
|
|
93
|
+
Rule: The record opens on how the rule runs — every event of a stated period split where the conditions decide, the events let through split again by how their run ended, each band as thick as the events in it, the three totals over its columns — and then the rule itself, with each step's own figure at the end of its line. A run opens on the same rule as its trace, the rail lit as far as the run went; the editor, where the container has room, sets the draft the same way beside the form.
|
|
94
|
+
Why: Read as text, the rule says what it does and not how often; read as rows, the history says what happened and not where in the rule. The split by volume answers the first question a reader of a rule brings — how much of what arrives does it act on — in one look, and the figure at the end of each line answers where.
|
|
95
|
+
Do: Draw the split as an element of the screen's own (OE-01) from the measured width, with the figures in words over it. Set the rule as a sheet of four lines — When, If, Then, Otherwise — down a rail, the figures in an end column aligned across the lines, and no card, tile or badge around a step. Narrow, stand each label over its step and the end column under it.
|
|
96
|
+
Don't: Build the flow from stat cards, tiles and tags; give a figure only by a band's thickness or a tone; keep a second rendering of the rule for the trace and the preview.
|
|
97
|
+
Check: Open a rule's record: the split of its period's events comes before the rule with its totals in words, each line of the rule ends in its own figure, a run opens on the same rule lit as far as it went, and at 390 pixels nothing scrolls sideways.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
topic: composition
|
|
3
|
+
prefix: CP
|
|
4
|
+
triggers:
|
|
5
|
+
[
|
|
6
|
+
slot,
|
|
7
|
+
part,
|
|
8
|
+
anatomy,
|
|
9
|
+
where,
|
|
10
|
+
place,
|
|
11
|
+
compose,
|
|
12
|
+
wrap,
|
|
13
|
+
body,
|
|
14
|
+
header,
|
|
15
|
+
inside,
|
|
16
|
+
beside,
|
|
17
|
+
own element,
|
|
18
|
+
custom,
|
|
19
|
+
creative,
|
|
20
|
+
bespoke,
|
|
21
|
+
no family,
|
|
22
|
+
token,
|
|
23
|
+
literal,
|
|
24
|
+
hard-coded,
|
|
25
|
+
]
|
|
26
|
+
families:
|
|
27
|
+
[
|
|
28
|
+
page-header,
|
|
29
|
+
app-shell,
|
|
30
|
+
top-navigation,
|
|
31
|
+
popover,
|
|
32
|
+
drawer,
|
|
33
|
+
dialog,
|
|
34
|
+
tabs,
|
|
35
|
+
toolbar,
|
|
36
|
+
inline,
|
|
37
|
+
stack,
|
|
38
|
+
grid,
|
|
39
|
+
typography,
|
|
40
|
+
]
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# Composition
|
|
44
|
+
|
|
45
|
+
Where a consumer's element goes in relation to a family: in a slot the family
|
|
46
|
+
names, or beside the family as a block of the consumer's own. Every family
|
|
47
|
+
publishes its anatomy — the parts it draws and the slots it leaves for the
|
|
48
|
+
consumer — and a screen that improvises a block where a slot exists loses what
|
|
49
|
+
the slot carries: a position, a sticky edge, a fold, a keyboard order.
|
|
50
|
+
|
|
51
|
+
### CP-01 · A slot the family names before a block of your own
|
|
52
|
+
|
|
53
|
+
Level: MUST
|
|
54
|
+
Applies to: any element a page places inside or beside a family — a tab strip, a toolbar, a search field, a workspace row, a footer link
|
|
55
|
+
Rule: Before writing an element next to a family, the family's anatomy is read; when it names a slot for that element, the element stands in the slot.
|
|
56
|
+
Why: The slot is where the family's promises hold — the header's tabs stay pinned, the bar's search folds, the column's header takes the row inset; a block written beside the family carries none of them and reads as a defect of the family.
|
|
57
|
+
Do: Open the reference page's anatomy table and match the element to a part described as "the slot a … sits in". Compose with the family's own members; wrap a family's part in a component of the page's own only when the host has no box.
|
|
58
|
+
Don't: Place a family's content as the first block of a body, a sibling of the family or a child of the wrong part because the markup is shorter that way.
|
|
59
|
+
Check: For every element of the page's own inside a family's root, the family's anatomy names the part it stands in; every "slot a … sits in" the page needs is used rather than imitated.
|
|
60
|
+
|
|
61
|
+
### CP-02 · Siblings of the page's own stand in a layout family
|
|
62
|
+
|
|
63
|
+
Level: MUST
|
|
64
|
+
Applies to: two or more elements a page writes beside each other with no family between them — a name and a badge, a run of tags, the lines of an address, a figure and its caption
|
|
65
|
+
Rule: Sibling elements of the page's own stand in `inline` (one row) or `stack` (one column) with a gap from the space scale, and text of the page's own takes its size from the typography family; bare inline siblings and bare text are not written.
|
|
66
|
+
Why: Two inline elements with nothing between them touch, and a badge beside a word sits on the text baseline rather than the row's centre; bare text takes the document's 16-pixel body size inside a region whose families set 13.
|
|
67
|
+
Do: Wrap the run in `inline` (centre-aligned by default, a two-step gap for marks beside a word) and each line of a block in `stack`; give every span of the page's own the typography family's `size`. The families' own parts — a list item's label, a cell's value, a description — already carry their size and gap and need none of this.
|
|
68
|
+
Don't: Write a name and a `badge` run together as text in a cell, a `description-list` value holding three tags with nothing between them, or address lines as plain spans.
|
|
69
|
+
Check: Every element of the page's own that has a sibling is a child of `inline` or `stack`; no bare span inside a family's part carries the body size.
|
|
70
|
+
|
|
71
|
+
### CP-03 · A track that holds a family states a zero minimum
|
|
72
|
+
|
|
73
|
+
Level: MUST
|
|
74
|
+
Applies to: a `grid` or a flex line of the page's own holding a family that scrolls its own content — `table`, `data-grid`, `scroll-area`
|
|
75
|
+
Rule: The track the family stands in is written with a zero minimum (`minmax(0, 1fr)`, or `min-inline-size: 0` on a flex child), so the family's scroller is what scrolls.
|
|
76
|
+
Why: A track is `auto` by default and a grid item's minimum is its content, so a table wider than the page stretches the track and pushes every sibling — the figures, the header — out of the frame, instead of scrolling inside the scroller the family already draws.
|
|
77
|
+
Do: State the single column as `minmax(0, 1fr)` on the region that holds the listing, and let the family's `scroll` part take the overflow.
|
|
78
|
+
Don't: Rely on the family to constrain itself: it cannot measure a parent that grows with it.
|
|
79
|
+
Check: At 1280 with the widest tab open, the page's scroll width equals the viewport and the family's own scroller is the only thing wider than its box.
|
|
80
|
+
|
|
81
|
+
### CP-04 · Where no family answers the problem, the screen composes its own
|
|
82
|
+
|
|
83
|
+
Level: MAY
|
|
84
|
+
Applies to: a problem no published family covers — two versions of one value side by side, a step of a lifecycle drawn on a row, a figure with its own shape, a summary that is neither a list nor a table
|
|
85
|
+
Rule: When the families' anatomies have been read and none of them answers the problem, the screen writes an element of its own rather than bending a family into a shape it does not have.
|
|
86
|
+
Why: A family used against its anatomy reads as a defect of the library — a `list-item` made to hold a chart, a `badge` made to hold a sentence — and the screen still does not answer the problem; a screen that draws only what a family draws cannot answer anything the library has not met yet.
|
|
87
|
+
Do: Read the anatomies first, and name the one that came closest and what it does not carry (CP-01 is the earlier question, not a veto). Then compose: the layout families for the arrangement (`inline`, `stack`, `grid`), the families that do fit for the parts they own, the typography family for every word, and the theme's tokens for every value the element paints (CP-05). The element takes one of three shapes, and each is legitimate — a block in the screen's template, a component of the screen's own named after what it shows, or a component the module's screens share — and it carries the state attributes the vocabulary already has, so it reads as part of the system. A screen whose problem is dense is expected to reach this rule: a flagship screen composed only of untouched families is usually a screen that gave up on its problem. Add a ledger row when a family would plausibly have covered the case: that row is what grows the library.
|
|
88
|
+
Don't: Reach for the nearest family and override its stylesheet; copy a family's parts into markup of your own; or leave the problem half-answered because no family draws it.
|
|
89
|
+
Check: For every element of the page's own, the family that came closest is named in a comment or a ledger row, and what it does not carry is named as a **part of its anatomy** — not as an impression of its examples. A claim that a family "does not do this" which its anatomy table contradicts is the defect; the approval chain was written by hand beside `timeline`, whose `rail`, `marker` and `connector` are exactly that shape, and the hand-drawn thread broke on the first step that carried a comment.
|
|
90
|
+
|
|
91
|
+
### CP-05 · An element of the page's own is built from the theme's tokens
|
|
92
|
+
|
|
93
|
+
Level: MUST
|
|
94
|
+
Applies to: any CSS a screen writes for an element of its own, under CP-04 or beside a family
|
|
95
|
+
Rule: Every value the element paints comes from the theme — space, type, ink, border, radius, elevation and motion — where the theme has a token for it; a literal is written only beside a comment that says what decided it and why no token covers it.
|
|
96
|
+
Why: A literal is what makes a screen look adjacent to the product rather than part of it: it ignores the scheme switch, the three densities and the theme's own retuning, and it is the first thing that drifts when the theme moves.
|
|
97
|
+
Do: Read `--slotted-space-*` for every gap and inset, `--slotted-text-*` and `--slotted-surface-*` for ink and ground, the typography family's `size` for every word, and the published measures of the family the element stands in — `--slotted-app-shell-row-inset` inside the navigation column, the control heights beside a control. Some values have no token and never will: a measure a column is capped at, a container query's step, an optical nudge of a pixel, the geometry of a shape the screen draws. Write those, in rem where the value scales with the type and in pixels where it does not, and put the reason beside them — the check reads that comment and passes. The escape is for a value the theme does not hold, not for a value it does.
|
|
98
|
+
Don't: Write a hex colour, a font size or a gap the theme already holds a token for, and do not read another family's private custom property. A literal with no reason beside it is the defect this rule is about; a reasoned one is a decision the next reader can weigh.
|
|
99
|
+
Check: Read every stylesheet of the screen, skipping media and container queries, hairlines and any line whose comment gives its reason: no colour, type size or gap remains that a theme token covers.
|
|
100
|
+
|
|
101
|
+
### CP-06 · A state the family already draws is not drawn again
|
|
102
|
+
|
|
103
|
+
Level: SHOULD
|
|
104
|
+
Applies to: the axes a screen passes to a family — `variant`, `tone`, `size`, `current`, `complete` — and any mark a screen adds beside a part the family fills
|
|
105
|
+
Rule: A screen states a state once. Where the family has a state for it, the screen sets that state and adds nothing louder beside it.
|
|
106
|
+
Why: The second statement is never the same size as the first: `current` on a `timeline` item draws a target, and the `solid` `variant` over it adds a halo two rules wide that crowds the title; a `badge` beside a description that already says "Waiting on this role" says it twice at two weights.
|
|
107
|
+
Do: Set the state the family names, read what it draws, and stop. Where an axis is passed as well, the screen can say what that axis adds which the state does not already carry.
|
|
108
|
+
Don't: Reach for the loudest variant to make a state visible, or add a mark beside a part that already carries the words.
|
|
109
|
+
Check: For every axis a screen passes to a family, the screen can name what it adds beyond the family's own state.
|
|
110
|
+
|
|
111
|
+
### CP-07 · A page's width is its type's
|
|
112
|
+
|
|
113
|
+
Level: SHOULD
|
|
114
|
+
Applies to: every `page`
|
|
115
|
+
Rule: A list page is `bleed` with `fill`; a record, a dashboard and an overview keep the default `wide`; a create or edit form is `content`; a page of preferences read as prose is `measure`.
|
|
116
|
+
Why: The page's width is the one measure its header and its body share, and a form capped in its own stylesheet ran its body to 736 pixels under a header of 1280, so the two end edges did not meet.
|
|
117
|
+
Do: Set the width on the page and nowhere else; a form with a table of lines keeps `content` and lets the table's columns truncate.
|
|
118
|
+
Don't: Cap a form's body with a width of the screen's own, or leave a form at `wide` so its fields run the length of a monitor.
|
|
119
|
+
Check: On every form the header's end edge and the body's end edge are the same line.
|
|
120
|
+
|
|
121
|
+
### CP-08 · A control that changes mode keeps every box where it was
|
|
122
|
+
|
|
123
|
+
Level: MUST
|
|
124
|
+
Applies to: a segmented control, a tab or a toggle that switches what one region holds — a reply or an internal note, one view or another — and the text beside it
|
|
125
|
+
Rule: Switching the mode changes the words and the values in the region, never the size, the line count or the position of any box in it or around it; text whose length differs between modes stands on one line, cut with an ellipsis and read whole from its title.
|
|
126
|
+
Why: The line beside Reply and Internal note was longer in one mode and wrapped under the control at narrow widths, so every switch moved the composer and the conversation above it under the reader's eyes and pointer.
|
|
127
|
+
Do: Give the varying text `flex: 1 1 0`, `min-inline-size: 0` and a one-line cut, write the two texts to similar lengths, and keep a control's label set fixed across modes.
|
|
128
|
+
Don't: Let a mode add or remove a line, a hint or a control that changes the region's height, or let a row wrap differently in one mode than in the other.
|
|
129
|
+
Check: At 1280, 570 and 390 pixels, switch the mode and read the bounding box of each box in the region: every top and height is the same in both modes.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
topic: conversations
|
|
3
|
+
prefix: CV
|
|
4
|
+
triggers:
|
|
5
|
+
[
|
|
6
|
+
conversation,
|
|
7
|
+
thread,
|
|
8
|
+
message,
|
|
9
|
+
reply,
|
|
10
|
+
composer,
|
|
11
|
+
internal note,
|
|
12
|
+
ticket,
|
|
13
|
+
inbox,
|
|
14
|
+
sla,
|
|
15
|
+
clock,
|
|
16
|
+
business hours,
|
|
17
|
+
queue,
|
|
18
|
+
]
|
|
19
|
+
families: [message, message-scroller, prompt-composer, filter-bar, tabs, page-header]
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Conversations
|
|
23
|
+
|
|
24
|
+
How a record that is a conversation with a clock is read and answered: a
|
|
25
|
+
support ticket, and anything else whose body is messages. The library gives
|
|
26
|
+
`message` a thread row, `message-scroller` a log that follows new rows and
|
|
27
|
+
`prompt-composer` a field that sends; it does not say where the composer
|
|
28
|
+
stands when the thread is long, how a service-level clock is said, or how an
|
|
29
|
+
inbox's queues and its filters stay one thing.
|
|
30
|
+
|
|
31
|
+
### CV-01 · The composer stays in reach of a scrolled conversation
|
|
32
|
+
|
|
33
|
+
Level: MUST
|
|
34
|
+
Applies to: a record whose body is a conversation the reader answers
|
|
35
|
+
Rule: From 600 pixels the conversation fills its region and scrolls on its own with the composer under it, and the record's context scrolls on its own beside it or behind a tab of the header; below 600 pixels the page scrolls, the composer is stuck to the bottom of the screen, and the record opens on its last message.
|
|
36
|
+
Why: A thread of 120 messages scrolled with the page put the composer 120 messages away from the line it answers, and a context that scrolled with the thread left the customer and the clocks behind.
|
|
37
|
+
Do: Give the thread a `message-scroller` that opens at its `end`, keep the composer in the row under it, and draw the context as its own scrolling column; on a phone, make the composer's row sticky on the page's ground.
|
|
38
|
+
Don't: Put the composer at the end of the page's flow, or let the context scroll away with the thread.
|
|
39
|
+
Check: Open a conversation of more than one screen of messages at 1280 and at 390 pixels: it opens on its last message, and the composer is on screen without a scroll at both widths.
|
|
40
|
+
|
|
41
|
+
### CV-02 · A service-level clock is said in business time, with the hour it falls due
|
|
42
|
+
|
|
43
|
+
Level: SHOULD
|
|
44
|
+
Applies to: a clock counted in business hours — a first response, a resolution
|
|
45
|
+
Rule: A running clock says the business time left and the hour it falls due in the organization's zone, a passed one by how much and when it was due, a paused one that it is stopped and why; a queue's row says the span alone, and no clock is ever said in raw minutes.
|
|
46
|
+
Why: "1 h 20 min left" read at 19:00 is tomorrow morning: the span alone hides that the time outside business hours does not count, and the hour alone hides how much work time is left.
|
|
47
|
+
Do: Take the remaining minutes from the backend, say them in minutes, hours and business days of the organization's own length, and add the due hour; state the policy once beside the clocks, with the business hours.
|
|
48
|
+
Don't: Recompute a clock in the client, or let a colour alone say that a clock is at risk or passed.
|
|
49
|
+
Check: Read a running, a passed and a paused clock: the first names its due hour, the second how much it passed by, the third that it is stopped and why; none is a count of minutes above 59.
|
|
50
|
+
|
|
51
|
+
### CV-03 · A queue is a view of the bar, never a second filter
|
|
52
|
+
|
|
53
|
+
Level: SHOULD
|
|
54
|
+
Applies to: a list worked as named queues — mine, unassigned, at risk, escalated
|
|
55
|
+
Rule: Each queue is a view of the list's `filter-bar`, a tree of the bar's own fields, labelled with the count of the rows it opens on; choosing it writes its pills, and editing a pill leaves a view the bar marks as changed.
|
|
56
|
+
Why: A queue picker beside the bar's pills is two answers to "what am I looking at", and they disagree the moment the reader edits a pill.
|
|
57
|
+
Do: Hand the queues to the bar as its `preset` views, count each with the list's own total for its tree, and let a door open a queue by naming it in the address.
|
|
58
|
+
Don't: Keep the chosen queue in a state of its own, or count a queue with a figure the list does not reproduce.
|
|
59
|
+
Check: Choose each queue: the bar shows its pills and the list's total equals the queue's count; remove one pill: the bar marks the view as changed.
|