@loadbare/app 0.9.0 → 0.10.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 (80) hide show
  1. package/README.md +3 -3
  2. package/dist/build/assemble.d.ts.map +1 -1
  3. package/dist/build/assemble.js +74 -66
  4. package/dist/build/assemble.js.map +1 -1
  5. package/dist/build/expand.d.ts.map +1 -1
  6. package/dist/build/expand.js +20 -19
  7. package/dist/build/expand.js.map +1 -1
  8. package/dist/build/locations.d.ts +2 -3
  9. package/dist/build/locations.d.ts.map +1 -1
  10. package/dist/build/locations.js +2 -3
  11. package/dist/build/locations.js.map +1 -1
  12. package/dist/build/pages.d.ts +3 -4
  13. package/dist/build/pages.d.ts.map +1 -1
  14. package/dist/build/pages.js +3 -4
  15. package/dist/build/pages.js.map +1 -1
  16. package/dist/core/lb-constants.d.ts +25 -24
  17. package/dist/core/lb-constants.d.ts.map +1 -1
  18. package/dist/core/lb-constants.js +95 -168
  19. package/dist/core/lb-constants.js.map +1 -1
  20. package/dist/core/lb-types.d.ts +64 -77
  21. package/dist/core/lb-types.d.ts.map +1 -1
  22. package/dist/core/lb-types.js +40 -7
  23. package/dist/core/lb-types.js.map +1 -1
  24. package/dist/hub/lb-apply.d.ts +47 -37
  25. package/dist/hub/lb-apply.d.ts.map +1 -1
  26. package/dist/hub/lb-apply.js +174 -193
  27. package/dist/hub/lb-apply.js.map +1 -1
  28. package/dist/hub/lb-hub.browser.d.ts +1 -1
  29. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  30. package/dist/hub/lb-hub.browser.js +411 -449
  31. package/dist/hub/lb-hub.browser.js.map +1 -1
  32. package/dist/server/lb-express.d.ts +5 -5
  33. package/dist/server/lb-express.d.ts.map +1 -1
  34. package/dist/server/lb-express.js +35 -66
  35. package/dist/server/lb-express.js.map +1 -1
  36. package/dist/server/lb-server.d.ts +77 -135
  37. package/dist/server/lb-server.d.ts.map +1 -1
  38. package/dist/server/lb-server.js +132 -79
  39. package/dist/server/lb-server.js.map +1 -1
  40. package/docs/TECHREF-1.0.md +861 -587
  41. package/docs/comparison.md +222 -185
  42. package/docs/prior-art.md +15 -14
  43. package/docs/reference/builder.md +9 -3
  44. package/docs/reference/chrome.md +107 -56
  45. package/docs/reference/custom-elements.md +199 -173
  46. package/docs/reference/data-binding.md +374 -374
  47. package/docs/reference/overview.md +12 -10
  48. package/docs/reference/page-files.md +135 -99
  49. package/docs/reference/server.md +2 -2
  50. package/docs/reference/widgets.md +104 -110
  51. package/docs/roadmap.md +32 -39
  52. package/docs/terms-of-art.md +57 -0
  53. package/docs/testing.md +97 -68
  54. package/docs/theory.md +92 -58
  55. package/docs/tutorials/010-pages-and-navigation.md +20 -12
  56. package/docs/tutorials/020-css.md +6 -3
  57. package/docs/tutorials/030-html-decomposition.md +9 -7
  58. package/docs/tutorials/040-displaying-data.md +30 -13
  59. package/docs/tutorials/{050-actions.md → 050-requests.md} +25 -15
  60. package/docs/tutorials/060-custom-element-code.md +17 -16
  61. package/docs/tutorials/065-conditional-rendering.md +34 -23
  62. package/docs/tutorials/070-displaying-a-list.md +29 -21
  63. package/docs/tutorials/072-inserting-into-a-list.md +24 -16
  64. package/docs/tutorials/074-deleting-from-a-list.md +9 -7
  65. package/docs/tutorials/076-updating-a-list-item.md +11 -10
  66. package/docs/tutorials/080-widget-requests.md +71 -43
  67. package/docs/tutorials/090-using-widget-libraries.md +22 -22
  68. package/package.json +1 -1
  69. package/skills/loadbare-app/SKILL.md +178 -122
  70. package/skills/loadbare-app/references/TECHREF-1.0.md +861 -587
  71. package/skills/loadbare-app/references/builder.md +9 -3
  72. package/skills/loadbare-app/references/chrome.md +107 -56
  73. package/skills/loadbare-app/references/custom-elements.md +199 -173
  74. package/skills/loadbare-app/references/data-binding.md +374 -374
  75. package/skills/loadbare-app/references/overview.md +12 -10
  76. package/skills/loadbare-app/references/page-files.md +135 -99
  77. package/skills/loadbare-app/references/server.md +2 -2
  78. package/skills/loadbare-app/references/widgets.md +104 -110
  79. package/docs/analysis-accidental-complexity.md +0 -149
  80. package/docs/analysis-closed-set.md +0 -210
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: loadbare-app
3
- description: Build server-bound web applications with Loadbare/app. Covers the file conventions the builder finds by name (`chrome.html`, `*.page.html`, `*.queries.ts`, `*.requests.ts`, `imports.ts`, `<tag>.html`, `<tag>.browser.ts`); the `lb-*` attribute vocabulary that binds HTML to server data and sends requests (`lb-list`, `lb-row`, `lb-cell`, `lb-key`, `lb-show`, `lb-action`); query parms in the URL with `lb-query-parm`; build-time widget expansion with `exp-*` parameters, `lb-slot` and `lb-template`; custom element code; the Express wiring with `hubRoutes`; and the `@loadbare/widgets` library. Use whenever a task involves `@loadbare/app`, `@loadbare/widgets`, `loadbare-app-build`, an `lb-` attribute, or a page, query, request or widget file in a Loadbare application. The model is not React, htmx or REST, and cannot be inferred from them.
3
+ description: Build server-bound web applications with Loadbare/app. Covers the file conventions the builder finds by name (`chrome.html`, `*.page.html`, `*.queries.ts`, `*.requests.ts`, `imports.ts`, `<tag>.html`, `<tag>.browser.ts`); the `lb-*` attribute vocabulary that lands query rows on HTML and issues requests (`lb-query`, `lb-column`, `lb-show`, `lb-request`); queries declared with `row(key, run)` and `rows(key, run)`; the URL as the hub's own query `lb-url`, with `lb-url-link`, `lb-url-push` and `lb-url-unknown`; build-time expansion with `exp-*` parameters, `lb-exp-slot` and `lb-exp-template`; custom elements as form-associated controls and row hosts; the Express wiring with `hubRoutes`; and the `@loadbare/widgets` library. Use whenever a task involves `@loadbare/app`, `@loadbare/widgets`, `loadbare-app-build`, an `lb-` attribute, or a page, query, request or custom element file in a Loadbare application. The model is not React, htmx or REST, and cannot be inferred from them.
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  package: "@loadbare/app"
@@ -11,17 +11,19 @@ metadata:
11
11
 
12
12
  Loadbare/app builds a whole application into one HTML document, one client
13
13
  script and one stylesheet. The server answers named queries with rows and
14
- performs named requests; the browser lands those rows on elements that name
15
- them. No component renders anything, and no page fetches anything.
14
+ runs named requests; the hub in the browser lands those rows on the elements
15
+ that name them. No component renders anything, and no page fetches
16
+ anything.
16
17
 
17
- The wire is relational. A name answers with one row or a set of rows, a
18
- row holds cells, and a cell holds one value. Hold on to that: most wrong
19
- designs come from sending a shape a relational answer cannot have.
18
+ Everything is a query. A query is a name for rows, of kind `row` or `rows`,
19
+ with a key column. A row holds columns, and a column holds one value. The
20
+ URL itself is a query, `lb-url`, that the hub serves. Hold on to that: most
21
+ wrong designs come from sending a shape a relational answer cannot have.
20
22
 
21
23
  ## Build every change before handing it over
22
24
 
23
- `loadbare-app-build` is the checker. It expands every page and widget and
24
- refuses what it cannot ship, naming the tag and the file:
25
+ `loadbare-app-build` is the checker. It expands every page and custom
26
+ element and refuses what it cannot ship, naming the tag and the file:
25
27
 
26
28
  ```bash
27
29
  loadbare-app-build --src src --out dist
@@ -40,9 +42,9 @@ tsc --noEmit
40
42
  `dist/pages.ts` imports page files with their `.ts` extensions, so the
41
43
  application's `tsconfig.json` needs `allowImportingTsExtensions`.
42
44
 
43
- **Write, build, fix, repeat until it is clean.** Then start the server:
44
- `createHub` refuses a query that answers with the wrong shape and a page
45
- that declares a reserved name, and those refusals only appear at run time.
45
+ Write, build, fix, and repeat until the build is clean. Then start the
46
+ server: `createHub` refuses a page that declares a reserved name at startup,
47
+ and the server console reports a query that answers with the wrong kind.
46
48
  Restart the server after adding or changing a `.queries.ts` or
47
49
  `.requests.ts` file.
48
50
 
@@ -58,9 +60,9 @@ src/
58
60
  pages/
59
61
  index.page.html the page for /
60
62
  members.page.html the page for /members
61
- members.queries.ts what members displays
62
- members.requests.ts what members may be asked to do
63
- widgets/
63
+ members.queries.ts the queries members shows
64
+ members.requests.ts the requests members answers
65
+ elements/
64
66
  note-card.html the markup <note-card> expands into
65
67
  visit-count.browser.ts the class <visit-count> registers
66
68
  server.ts
@@ -74,18 +76,22 @@ A page is an HTML fragment that lands in `<main>`:
74
76
 
75
77
  ```html
76
78
  <!-- src/pages/members.page.html -->
77
- <p lb-row="dues">Collected this year: <span lb-cell="total"></span></p>
79
+ <title>Members</title>
80
+ <h1>Members</h1>
78
81
 
79
- <section lb-list="roster">
80
- <form lb-action="lb-row-insert">
81
- <input lb-cell="name" />
82
- <button type="submit">Add</button>
82
+ <p lb-query="dues">Dues collected this year: <span lb-column="total"></span></p>
83
+
84
+ <section lb-query="roster">
85
+ <form lb-request="lb-row-insert">
86
+ <input lb-column="name" placeholder="Name" />
87
+ <button type="submit">Add member</button>
83
88
  </form>
89
+
84
90
  <ul>
85
- <template lb-key="id">
91
+ <template>
86
92
  <li>
87
- <span lb-cell="name"></span>
88
- <button lb-action="lb-row-delete">Remove</button>
93
+ <input lb-column="name" lb-request="lb-row-update" />
94
+ <button lb-request="lb-row-delete">Remove</button>
89
95
  </li>
90
96
  </template>
91
97
  </ul>
@@ -94,11 +100,11 @@ A page is an HTML fragment that lands in `<main>`:
94
100
 
95
101
  ```ts
96
102
  // src/pages/members.queries.ts
97
- import { list, row, type Queries } from "@loadbare/app/server";
103
+ import { row, rows, type Queries } from "@loadbare/app/server";
98
104
 
99
105
  export const queries: Queries = {
100
- dues: row((ctx) => ctx.db.duesTotal()),
101
- roster: list((ctx) => ctx.db.members()),
106
+ dues: row("year", (ctx) => ctx.db.duesTotal()),
107
+ roster: rows("id", (ctx) => ctx.db.members()),
102
108
  };
103
109
  ```
104
110
 
@@ -115,6 +121,12 @@ export const requests: Requests = {
115
121
  }),
116
122
  refresh: ["dues"],
117
123
  },
124
+ rowUpdate: {
125
+ run: async (ctx, { key, values }) => ({
126
+ roster: patch({ rows: [await ctx.db.updateMember(key, values)] }),
127
+ }),
128
+ refresh: [],
129
+ },
118
130
  rowDelete: {
119
131
  run: async (ctx, { key }) => {
120
132
  await ctx.db.removeMember(key);
@@ -133,122 +145,166 @@ Re-read this list when a build fails or a design will not fit. Each item is
133
145
  a place a reasonable instinct from React, Vue, htmx or REST produces an
134
146
  error or a dead end.
135
147
 
148
+ **The server declares every query's kind and key.** Declare each query with
149
+ `row(key, run)` or `rows(key, run)`, key column first. The markup names the
150
+ query with `lb-query` and the columns it shows with `lb-column`, and repeats
151
+ neither the kind nor the key. A page that needs the same data as one row
152
+ and as a set declares two queries. Every query has a key; an aggregate row
153
+ answers with a constant one.
154
+
136
155
  **There is no template language.** No `{#if}`, no `v-for`, no `map()`. A
137
- list is an element carrying `lb-list` with a `<template lb-key="...">`
138
- inside it, and the hub clones the template once per row. A condition is
139
- `lb-show="<column>"`: write every possibility into the page and let a column
140
- decide which is present. The column must be a boolean or null; the string
141
- `"false"` counts as present.
156
+ set of rows is an element carrying `lb-query` with a `<template>` inside it,
157
+ the row template, which the hub clones once per row. A `row` query with no
158
+ row template lands on the element itself. A `rows` query with no row
159
+ template lands nothing, which is what an insert form naming its query is. A
160
+ condition is `lb-show="<column>"`: write every possibility into the page and
161
+ let a column decide which is present. The column is a boolean or null; the
162
+ string `"false"` counts as present.
142
163
 
143
164
  **`{{placeholder}}` is build time only.** It reads an `exp-` attribute on
144
165
  the tag, never data. Write it as an entire attribute value or an entire
145
166
  text node. Name parameters in lowercase with hyphens: HTML lowercases
146
167
  `exp-inputClass` before expansion sees it.
147
168
 
148
- **Scope comes from ancestry, not props.** An `lb-cell` binds to the row on
149
- its nearest ancestor carrying `lb-row` or a live list row. An element
150
- outside every scope displays nothing. A value of `lb-cell` is a column name.
169
+ **Position comes from ancestry, not props.** `lb-column` and `lb-show` read
170
+ from the nearest ancestor row: a live row, or an element a `row` query
171
+ landed on. A nested `lb-query` begins a new query. An element with
172
+ `lb-column` and no ancestor row receives nothing, and the hub still gathers
173
+ from it.
174
+
175
+ **A column never holds rows.** Master-detail is a `row` query and a `rows`
176
+ query under two names. Many masters with their details is one `rows` query
177
+ of joined rows, grouped for display by a custom element's `lbPlaceRow`. A
178
+ query nested inside another query's row template receives the same rows in
179
+ every outer row; use it for a picker, never for per-row detail.
180
+
181
+ **Every request has one shape.** `lb-request` names the request, and the
182
+ hub sends `{ name, query, key, values }` as present: `query` from the
183
+ nearest ancestor `lb-query`, `key` from the nearest ancestor row of that
184
+ query, and `values` gathered from controls by their `lb-column`.
185
+
186
+ **An element commits on its own event.** A form on `submit`, a control on
187
+ `change`, anything else on `click`. A click on interactive content inside
188
+ the element (a link, a button, a control, a label) belongs to that content
189
+ and commits nothing. A submit button never commits on click;
190
+ on submit, the submitter's `lb-request` wins over the form's. A native
191
+ `<input lb-column="name" lb-request="lb-row-update">` saves that one column
192
+ on `change`. Inside a form, leave `lb-request` off the controls and put it
193
+ on the form.
194
+
195
+ **Gathering follows the form, the live row, or the form owner.** An element
196
+ with `lb-column` gathers its own value alone. A `<form>` gathers every
197
+ control whose form owner it is. An element in a live row gathers that live
198
+ row. Anything else gathers its form owner's controls. A `<form>` cannot
199
+ wrap a `<tr>`, so an insert row in a table puts its controls in a form
200
+ elsewhere with the HTML `form` attribute. Checkboxes, radio buttons and
201
+ file inputs are not controls here: they receive no value and are not
202
+ gathered.
203
+
204
+ **Three requests are Loadbare's.** `lb-row-insert` needs `query` and
205
+ `values`, `lb-row-update` needs `query`, `key` and `values`, and
206
+ `lb-row-delete` needs `query` and `key`. The page permits each under
207
+ `crud`, keyed by query name, as `rowInsert`, `rowUpdate` and `rowDelete`.
208
+ Each works on a `row` query as well as a `rows` query, since the element a
209
+ `row` lands on carries its key. Every other request name is a declared
210
+ request, under `handlers`. Anything undeclared is refused.
211
+
212
+ **Write `rowUpdate` as a partial update.** A form sends every control it
213
+ owns; a single control sends its one column. Set the columns `values`
214
+ names, leave the rest alone, and check the names against the columns the
215
+ page may edit.
151
216
 
152
- **One name, one cardinality.** Declare every query with `row()` or
153
- `list()`. A page that needs the same data as a row and as a set declares
154
- two queries.
217
+ **Return a patch when the change has a known extent.** `patch({ rows })`
218
+ for rows added or edited, `patch({ drop })` for keys removed, with
219
+ `refresh: []`. List a query in `refresh` only when its membership or order
220
+ changed in a way the handler cannot name.
155
221
 
156
- **A cell never holds rows.** Master-detail is a row and a list under two
157
- names. Many masters with their details is one list of joined rows, grouped
158
- for display by a widget's `lbPlaceRow`. A list nested inside another list's
159
- rows receives the same rows in every outer row; use it for a picker, never
160
- for per-row detail.
222
+ **Format values in the query.** The hub does no type conversion. What a
223
+ number, date or null looks like is decided on the server.
161
224
 
162
225
  **A URL's path names a page, never a resource.** Do not design
163
- `/accounts/42`. A row is addressed by `list` and `key`, taken from where the
164
- element sits.
226
+ `/accounts/42`. A row is addressed by `query` and `key`, taken from where
227
+ the element sits.
165
228
 
166
229
  **The query string is what the page has on screen.** Which record a page
167
230
  shows, a filter, a date range: each is a query parm,
168
231
  `/accounts?acct=23&from=2026-09-09`, so a reload, a bookmark or a mailed link
169
- shows the same thing. A control writes one with `lb-query-parm="acct"`, which
170
- replaces the history entry, reloads the page at the new URL, and sends no
171
- request. A link writes several with an ordinary `lb-nav-link` href. Queries
172
- still take no arguments: `contextFor(req, parms)` puts parms onto `ctx`, and
173
- a query reads them there. Read them from `parms`, never `req.query`, because
174
- `contextFor` may be called twice for one request. A parm is user input, so
175
- validate it where you read it. Do not keep a selection in server state set
176
- by an action; that forces `refresh: []` and a hand-written re-answer of
177
- everything the selection touches.
178
-
179
- **When a server response changes the query parms.** After an insert, only
180
- the server knows the new key. After a delete, only the server knows the parm
181
- should go. Have `run` return `queryParms({ acct: String(id) })`, or
182
- `queryParms({ acct: "" })` to remove the parm. The page loads at the new
183
- query string in the same round trip, and the history entry is replaced to
184
- match. This is Post/Redirect/Get without the redirect: the path never
185
- changes, and there is no second request. Do not reach for a redirect, a
186
- hand-written refresh of every query, or server state holding the selection.
187
- See [refresh and patch](references/page-files.md#refresh-and-patch).
232
+ shows the same thing. The hub serves the query `lb-url`, of kind `row`,
233
+ keyed by `lb-path`, with `lb-page-label`, `lb-page-unknown`, and one column
234
+ per query parm. A control inside `lb-query="lb-url"` carrying
235
+ `lb-request="lb-row-update"` writes its parm; the hub replaces the history
236
+ entry, or pushes one when the control carries `lb-url-push`, and reloads the
237
+ page's queries with no request to the server. An `<a lb-url-link>` moves
238
+ to its `href`. Queries still take no arguments: `contextFor(req, parms)`
239
+ puts parms onto `ctx`, and a query reads them there. Read them from
240
+ `parms`, never `req.query`, because `contextFor` may be called twice for one
241
+ request. A parm is user input, so validate it where it is read. Do not
242
+ keep a selection in server state set by a handler.
243
+
244
+ **A handler moves the URL with `url()`.** After an insert, only the server
245
+ knows the new key; after a delete, only the server knows the parm should
246
+ go. Have `run` return `url({ acct: String(id) })`, or `url({ acct: "" })`
247
+ to remove the parm, or `url({ "lb-path": "/accounts", acct: "5" })` to
248
+ enter another page with only the parms named. The page loads at the new URL
249
+ in the same round trip. This is Post/Redirect/Get without the redirect. See
250
+ [moving the URL](references/page-files.md#moving-the-url).
251
+
252
+ **A page names itself with `<title>`.** The builder lifts a page file's
253
+ `<title>` and the hub shows it as `lb-page-label` and as the document title.
254
+ A page without one is labeled by its stub.
188
255
 
189
256
  **Loadbare is for applications, not sites.** Every route is answered with
190
257
  `app.html`, and a path that names no page is found in the browser, not
191
- answered with a 404. That is the design, not a defect to report.
258
+ answered with a 404. The hub opens a `<dialog lb-url-unknown
259
+ lb-query="lb-url">` in the chrome for it.
192
260
 
193
261
  **There are no endpoints to write.** `hubRoutes(hub, contextFor)` is the
194
- whole data channel. Declare every action under `actions` and every
195
- permitted operation under `crud`; anything undeclared is refused.
196
-
197
- **All three CRUD operations are list operations.** Each needs a key, and a
198
- key exists only on a live row inside `lb-list`. An `lb-row` scope is
199
- read-only; give a writable single row a list that answers with one row.
200
-
201
- **Write `rowUpdate` as a partial update.** A form sends every cell it
202
- holds; a widget carrying `lb-cell` and `lb-action="lb-row-update"` sends its
203
- one cell. Set the columns `values` names, leave the rest alone, and check
204
- the names against the columns the page may edit.
205
-
206
- **Put `lb-row-insert` and `lb-row-update` on a `<form>` or a button.** The
207
- hub gathers the nearest `<form>`, `<tr>` or live row. Cells in a bare
208
- `<div>` belong to no row and the request is refused. A native `<input>`
209
- carrying `lb-row-update` is refused; commit one cell with a widget such as
210
- `<lb-input>`. Inside a form, leave `lb-action` off the widgets.
211
-
212
- **Return a patch when the change has a known extent.** `patch({ rows })`
213
- for rows added or edited, `patch({ drop })` for keys removed, with
214
- `refresh: []`. List a query in `refresh` only when its membership or order
215
- changed in a way the operation cannot name.
216
-
217
- **Format values in the query.** The hub does no type conversion. What a
218
- number, date or null looks like is decided on the server.
219
-
220
- **Checkboxes, radio buttons and file inputs are not bound.** They receive
221
- no value and are not gathered. This is an open item, not a mistake in the
222
- page.
223
-
224
- **`lb-` belongs to Loadbare.** Invent no `lb-` attribute, and name no
225
- query or action with the prefix. Import attribute names in widget code from
226
- `@loadbare/app/constants`; never write them as string literals.
227
-
228
- **Name a widget script `<tag>.browser.ts`.** A plain `<tag>.ts` stays on
229
- the server and the tag goes unregistered.
230
-
231
- **Light DOM, global CSS.** No shadow root, no scoping. Style empty lists
232
- with `[lb-row-count="0"]`, pending requests with `[lb-pending]`, and failed
233
- ones with `[lb-error]`.
262
+ whole data channel.
263
+
264
+ **The developer writes `lb-*` attributes in markup, and script never
265
+ assigns them.** The builder refuses any `lb-` attribute other than
266
+ `lb-query`, `lb-column`, `lb-show`, `lb-request`, `lb-url-link`,
267
+ `lb-url-push` and `lb-url-unknown`. Everything else with the prefix is a
268
+ stamp the hub or the builder writes.
269
+
270
+ **`lb-` belongs to Loadbare.** The application should never name anything
271
+ with the `lb-` prefix anywhere: no attribute, query, column, request, custom
272
+ element, event, or custom element method beginning `lb`. Import attribute
273
+ names in custom element code from `@loadbare/app/constants`; never write
274
+ them as string literals.
275
+
276
+ **A custom element takes part as a control.** A form-associated custom
277
+ element (`static formAssociated = true`) with a `value` property that fires
278
+ `change` is a control: the hub sets its `value`, gathers it, and commits it
279
+ on `change`. Any other custom element keeps its content and receives the
280
+ column as its `lb-column-value` attribute.
281
+
282
+ **Name a custom element script `<tag>.browser.ts`.** A plain `<tag>.ts`
283
+ stays on the server and the tag goes unregistered.
284
+
285
+ **Light DOM, global CSS.** No shadow root, no scoping. Style an empty
286
+ query with `[lb-query-row-count="0"]`, a request in flight with
287
+ `[lb-request-pending]`, a failed one with `[lb-request-error]`, and a value
288
+ with `[lb-column-value="overdue"]`.
234
289
 
235
290
  **Host at the origin root.** The hub reaches its route by absolute path, so
236
291
  a subpath such as `example.com/myapp/` does not work.
237
292
 
238
- ## Widgets
293
+ ## Custom elements
239
294
 
240
- A widget is `<tag>.html`, `<tag>.browser.ts`, or both, and a tag with
241
- neither is a build error. The HTML is expanded at build time into the tag's
242
- children; the class is an ordinary custom element with no base class.
295
+ A custom element is `<tag>.html`, `<tag>.browser.ts`, or both, and a tag
296
+ with neither is a build error. The HTML is expanded at build time into the
297
+ tag's children; the class is an ordinary custom element with no base class.
243
298
 
244
- Reach for a widget only when plain HTML cannot do the job. A list, a form
245
- and a condition need none. A widget exists to wrap a control that decides
246
- its own moment to send (`<lb-input>` on `change`), or to place and scaffold
247
- rows (`lbPlaceRow`, `lbRowsLanded`).
299
+ Reach for one only when plain HTML cannot do the job. A set of rows, a form
300
+ and a condition need none. A custom element exists to be a control of its
301
+ own, or to place and scaffold rows through the two hooks the hub calls,
302
+ `lbPlaceRow` and `lbRowsLanded` (the `RowsHost` interface).
248
303
 
249
304
  Before writing one, check `@loadbare/widgets`: `lb-input`, `lb-select`,
250
- `lb-options`, `lb-table`, `lb-picker`, `lb-unknown-page`. Install it and
251
- list it in `src/imports.ts`:
305
+ `lb-options`, `lb-picker`, `lb-table`, `lb-unknown-page`. The first four
306
+ are form-associated controls that carry `lb-column` and `lb-request`
307
+ themselves. Install the package and list it in `src/imports.ts`:
252
308
 
253
309
  ```ts
254
310
  export default ["@loadbare/widgets"];
@@ -263,15 +319,15 @@ with the package, so no network is needed.
263
319
 
264
320
  - [`references/overview.md`](references/overview.md) — the map of the
265
321
  reference, by part of the application.
266
- - [`references/data-binding.md`](references/data-binding.md) — every `lb-`
267
- attribute, requests, forms, query parms, conditions and request state.
268
- Start here for anything in a page.
322
+ - [`references/data-binding.md`](references/data-binding.md) — landing,
323
+ gathering, requests, conditions and request state. Start here for
324
+ anything in a page.
269
325
  - [`references/page-files.md`](references/page-files.md) — queries,
270
- `onPageEnter`, actions, CRUD, refresh and patch.
326
+ `onPageEnter`, handlers, `crud`, refresh and patch, and `url()`.
271
327
  - [`references/custom-elements.md`](references/custom-elements.md) —
272
- expansion, parameters, slots, destinations, and widget code.
273
- - [`references/chrome.md`](references/chrome.md) — the chrome,
274
- navigation and `lb-navigation`.
328
+ expansion, parameters, slots, destinations, and custom element code.
329
+ - [`references/chrome.md`](references/chrome.md) — the chrome, and the URL
330
+ as the query `lb-url`.
275
331
  - [`references/server.md`](references/server.md) — the Express server and
276
332
  the request context.
277
333
  - [`references/builder.md`](references/builder.md) — the builder's options,
@@ -282,5 +338,5 @@ with the package, so no network is needed.
282
338
  - [`references/TECHREF-1.0.md`](references/TECHREF-1.0.md) — the reserved
283
339
  names, what a URL names and
284
340
  [query parms](references/TECHREF-1.0.md#query-parms), and the open items
285
- that block 1.0. Read it before designing around
286
- something the other references do not mention.
341
+ that block 1.0. Read it before designing around something the other
342
+ references do not mention.