@loadbare/app 0.8.0 → 0.8.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.
Files changed (47) hide show
  1. package/dist/build/skills-cli.d.ts +12 -0
  2. package/dist/build/skills-cli.d.ts.map +1 -0
  3. package/dist/build/skills-cli.js +81 -0
  4. package/dist/build/skills-cli.js.map +1 -0
  5. package/dist/build/skills.d.ts +47 -0
  6. package/dist/build/skills.d.ts.map +1 -0
  7. package/dist/build/skills.js +124 -0
  8. package/dist/build/skills.js.map +1 -0
  9. package/dist/core/lb-constants.d.ts +2 -0
  10. package/dist/core/lb-constants.d.ts.map +1 -1
  11. package/dist/core/lb-constants.js +18 -4
  12. package/dist/core/lb-constants.js.map +1 -1
  13. package/dist/hub/lb-apply.d.ts +10 -0
  14. package/dist/hub/lb-apply.d.ts.map +1 -1
  15. package/dist/hub/lb-apply.js +15 -1
  16. package/dist/hub/lb-apply.js.map +1 -1
  17. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  18. package/dist/hub/lb-hub.browser.js +116 -35
  19. package/dist/hub/lb-hub.browser.js.map +1 -1
  20. package/dist/server/lb-express.d.ts +13 -5
  21. package/dist/server/lb-express.d.ts.map +1 -1
  22. package/dist/server/lb-express.js +26 -7
  23. package/dist/server/lb-express.js.map +1 -1
  24. package/dist/server/lb-server.d.ts +3 -0
  25. package/dist/server/lb-server.d.ts.map +1 -1
  26. package/dist/server/lb-server.js.map +1 -1
  27. package/docs/TECHREF-1.0.md +93 -22
  28. package/docs/comparison.md +8 -7
  29. package/docs/reference/chrome.md +5 -3
  30. package/docs/reference/data-binding.md +28 -0
  31. package/docs/reference/server.md +11 -0
  32. package/docs/reference/widgets.md +8 -4
  33. package/docs/roadmap.md +16 -0
  34. package/docs/theory.md +8 -1
  35. package/docs/tutorials/010-pages-and-navigation.md +2 -1
  36. package/package.json +8 -4
  37. package/skills/loadbare-app/SKILL.md +275 -0
  38. package/skills/loadbare-app/references/TECHREF-1.0.md +1260 -0
  39. package/skills/loadbare-app/references/builder.md +134 -0
  40. package/skills/loadbare-app/references/chrome.md +160 -0
  41. package/skills/loadbare-app/references/css.md +44 -0
  42. package/skills/loadbare-app/references/custom-elements.md +397 -0
  43. package/skills/loadbare-app/references/data-binding.md +485 -0
  44. package/skills/loadbare-app/references/overview.md +38 -0
  45. package/skills/loadbare-app/references/page-files.md +194 -0
  46. package/skills/loadbare-app/references/server.md +153 -0
  47. package/skills/loadbare-app/references/widgets.md +178 -0
@@ -0,0 +1,485 @@
1
+ # Data Binding
2
+
3
+ A page binds its elements to server data with four attributes, and asks the
4
+ server to change that data with one more. The developer writes all five into
5
+ the HTML; the server declares which queries it answers and which operations
6
+ it permits, and refuses anything it has not declared.
7
+
8
+ One vocabulary and one containment ladder: a list holds rows, a row holds
9
+ cells. A column is the second axis, and it is what the value of `lb-cell` or
10
+ `lb-key` always holds.
11
+
12
+ ## A page that binds data
13
+
14
+ Here is a page that binds a scalar, a list, and three requests:
15
+
16
+ ```html
17
+ <!-- src/pages/members.page.html -->
18
+ <h1>Members</h1>
19
+
20
+ <p lb-row="dues">Dues collected this year: <span lb-cell="total"></span></p>
21
+
22
+ <section lb-list="roster">
23
+ <form lb-action="lb-row-insert">
24
+ <input lb-cell="name" placeholder="Name" />
25
+ <button type="submit">Add member</button>
26
+ </form>
27
+
28
+ <ul>
29
+ <template lb-key="id">
30
+ <li>
31
+ <lb-input lb-cell="name"></lb-input>
32
+ <button lb-action="lb-row-delete">Remove</button>
33
+ </li>
34
+ </template>
35
+ </ul>
36
+ </section>
37
+ ```
38
+
39
+ The queries named here — `dues` and `roster` — and the operations the page
40
+ asks for are declared on the server; see [page files](./page-files.md).
41
+
42
+ ## Binding
43
+
44
+ | Attribute | Written by | Names |
45
+ |----------------|-------------|-------------------------------------------------------|
46
+ | `lb-list` | a developer | The set of rows a subtree displays |
47
+ | `lb-row` | a developer | The one row a subtree displays |
48
+ | `lb-key` | a developer | The column that identifies a row |
49
+ | `lb-cell` | a developer | The column an element displays |
50
+ | `lb-show` | a developer | The column that decides whether an element is present |
51
+ | `lb-key-value` | the hub | A live row's own key |
52
+ | `lb-value` | the hub | The value that landed on a cell |
53
+
54
+ A binding is scoped by ancestry. Either scope attribute scopes its DOM
55
+ children, and a nested one of either kind begins a new scope, so an element
56
+ binds to the name on its nearest ancestor carrying one, and to the row on its
57
+ nearest ancestor carrying `lb-key-value`. Nothing else establishes scope: an
58
+ element outside every scope is bound to nothing and displays nothing, and a
59
+ result never crosses into a nested scope.
60
+
61
+ Which of the two a subtree writes is not a choice about display. Cardinality
62
+ is a property of the name, so one name answers with one shape, always. A page
63
+ that shows the roster both as a set and as a single row declares two queries,
64
+ `rosterList` and `rosterRow`, and binds each with the attribute that matches
65
+ what it answers with.
66
+
67
+ Bind an element to a cell by putting `lb-cell` on the element that shows the
68
+ value. Its value is a column name and never a cell name: a cell has no name
69
+ of its own, because it is identified by its row and its column, and the row
70
+ arrives from scope. The scope's own root counts as a cell if it carries one,
71
+ which is how an `<option>` — whose content model is text — displays the value
72
+ it is.
73
+
74
+ An element that carries a scope and `lb-cell` both is a cell of the scope
75
+ around it. Its own `lb-list` or `lb-row` names what it displays, and its
76
+ ancestors name where it belongs, so a `<select lb-list="accounts"
77
+ lb-cell="account">` in a row displays the accounts and holds that row's
78
+ `account`. The value lands on it and is gathered from it; the cells inside
79
+ it are the accounts', and neither.
80
+
81
+ Name the same query on more than one subtree to display it in more than one
82
+ place. Every subtree gets the result.
83
+
84
+ One name is the hub's rather than the server's: `lb-navigation`, one row
85
+ landed on every navigation with the columns `page-label` and `page-uri`. It binds the
86
+ same way. Its name is reserved, as every value beginning with `lb-` is in
87
+ every `lb-` attribute: the server refuses a page that declares a query so
88
+ named — see [Where the page is](./chrome.md#where-the-page-is).
89
+
90
+ A key has a name and a value, and they are two attributes. `lb-key` on a row
91
+ template names the column that identifies a row: it is a property of the
92
+ list, since a list without a fixed key column is meaningless, written where
93
+ the rows land. `lb-key-value` on a row that is showing carries that row's
94
+ value of it. A developer writes the first and never the second.
95
+
96
+ ### Where a bound value lands
97
+
98
+ A value lands on a bound element one of three ways.
99
+
100
+ | Element | Receives the value as |
101
+ |----------------------------------------|-----------------------------------|
102
+ | A custom element | Its `lb-value` attribute |
103
+ | `<select>`, `<textarea>`, or `<input>` | Its `value`, and `lb-value` |
104
+ | Any other native element | Its `textContent`, and `lb-value` |
105
+
106
+ A widget owns whatever control it wraps, so it is handed the value and
107
+ renders it itself — see [Custom Elements](./custom-elements.md#code) for
108
+ observing `lb-value`. A form control shows its state as its `value`, so a
109
+ `<select>` keeps its options. Any other native element has no behavior of its
110
+ own, so its value is its text. Checkboxes, radio buttons and file inputs
111
+ receive nothing, not even `lb-value`, and the hub reports it to the console.
112
+
113
+ Nothing an application writes ever sets `lb-value`. It is written by
114
+ Loadbare and read by a widget or a stylesheet.
115
+
116
+ ### Lists
117
+
118
+ Write `lb-list` on any element, and a `<template>` inside it carrying
119
+ `lb-key`. The hub clones that template once per row, fills each clone, and
120
+ reconciles what is showing against what arrived. No widget is involved, and
121
+ none is needed.
122
+
123
+ An array is the whole set, so it decides membership and order: a row whose
124
+ key did not arrive is gone. A patch touches only the rows it names and leaves
125
+ every other row's contents and position alone.
126
+
127
+ A widget enters only where the rows need scaffolding or placement that only
128
+ it can decide — a `<select>` that builds an `<optgroup>` per distinct value, a
129
+ table that sections and sorts. Such a widget carries `lb-list` itself and
130
+ implements one or both of two optional methods; see
131
+ [Decorating a list](./custom-elements.md#decorating-a-list) and
132
+ [The Basic Widget Library](./widgets.md).
133
+
134
+ A scope with `lb-list` and no row template displays nothing, and that is not
135
+ an error. It is bound to the list without showing it, which is what an insert
136
+ form naming the list it adds a row to already is.
137
+
138
+ ## Requests
139
+
140
+ One attribute turns an interaction into a request. `lb-action` names what the
141
+ server is asked for: an action the page declared, or one of Loadbare's
142
+ reserved names, which are the CRUD operations.
143
+
144
+ One attribute name, one wire field, one set of values. The request field
145
+ `action` carries this attribute's value verbatim, so nothing is translated
146
+ between the markup and the server, and the CRUD key is the same value with
147
+ the prefix stripped and the rest camel-cased.
148
+
149
+ | Written | Asks for | Carries |
150
+ |-------------------------------------------|--------------|-------------------------------|
151
+ | `lb-action="name"` | That action | The scope, and what is in it |
152
+ | `lb-action="lb-row-delete"` | `rowDelete` | `list`, `key` |
153
+ | `lb-action="lb-row-insert"` on a `<form>` | `rowInsert` | `list`, `values` |
154
+ | `lb-action="lb-row-update"` on a `<form>` | `rowUpdate` | `list`, `key`, `values` |
155
+ | `lb-action="lb-row-update"` on a widget cell | `rowUpdate` | `list`, `key`, `values` of one cell |
156
+
157
+ All three operations are list operations. Each needs a key, and a key exists
158
+ only on a live row the hub stamped inside a list, so a single-row scope is
159
+ read-only and a declared action is the only thing it can send. An application
160
+ that wants a writable single row declares a list that answers with one row.
161
+
162
+ A name beginning with `lb-` is reserved, in `lb-action` and in either scope
163
+ attribute alike. The server refuses a page that declares an action or a query
164
+ so named, which is what lets a reserved name be added later without colliding
165
+ with one an application already uses. That reservation is also the whole of
166
+ the wire discriminant: a value beginning with `lb-` is an operation, and
167
+ anything else is a name the page declared.
168
+
169
+ The hub sends a request from a native element on the element's own event: a
170
+ form on submit, anything else on click. An insert or update clicked from a
171
+ button reads the form or row the button is in (see [Forms](#forms)). To
172
+ commit one cell as it changes, put `lb-row-update` on a widget cell instead
173
+ (see [Committing one cell](#committing-one-cell)).
174
+
175
+ Declare every action on the server, and permit every operation on its list. A
176
+ name the page has not declared, and an operation a list does not permit, are
177
+ refused; see
178
+ [requests, actions, CRUD](./page-files.md#requests-actions-crud).
179
+
180
+ ### Actions
181
+
182
+ Write `lb-action` on a button to ask the server to do something that is not
183
+ one of the three CRUD operations:
184
+
185
+ ```html
186
+ <button lb-action="mailRoster">Mail the roster</button>
187
+ ```
188
+
189
+ An action carries whatever binding is in scope at the element — `list` or
190
+ `row`, whichever scoped it, plus `key` and `cell` — and nothing else. There is
191
+ no argument list. A button carries no value, so the server computes the whole
192
+ of the new state and the page displays only what came back.
193
+
194
+ Write `lb-action` on a widget to have the widget decide what performing the
195
+ action means. Loadbare turns a click into a request for a native element
196
+ only, and leaves a widget to send its own — a `<select>` performs its action
197
+ on change, not on click.
198
+
199
+ ### Deleting a row
200
+
201
+ ```html
202
+ <button lb-action="lb-row-delete">Remove</button>
203
+ ```
204
+
205
+ `lb-row-delete` needs nothing declared. The row's `lb-list` and `lb-key-value`
206
+ are already in scope, and they are all the server needs to know which row is
207
+ meant and whether the list permits deleting it.
208
+
209
+ The hub sends it from a native element. A widget sends its own request.
210
+
211
+ ### Forms
212
+
213
+ A `<form>` performs its `lb-action` on submit. `lb-row-insert` and `lb-row-update`
214
+ both gather every `lb-cell` of the form into one values map, read from
215
+ the control each cell is or wraps. A cell inside a scope nested in the form
216
+ is that scope's and is not gathered, the same way a value landing on the
217
+ form's row does not reach it. They differ in one thing: `lb-row-update`
218
+ also carries the key of the row it is inside, and `lb-row-insert` carries none,
219
+ because there is no row yet. A declared name on a form sends that action on
220
+ submit, carrying the binding and no values.
221
+
222
+ Put an `lb-row-update` form inside the row it edits, so it has that row's key
223
+ from the same ancestor a delete button reads:
224
+
225
+ ```html
226
+ <template lb-key="id">
227
+ <li>
228
+ <form lb-action="lb-row-update">
229
+ <input lb-cell="name" />
230
+ <button type="submit">Save</button>
231
+ </form>
232
+ </li>
233
+ </template>
234
+ ```
235
+
236
+ When an `lb-row-insert` succeeds, the hub resets every control it gathered
237
+ from to its default, as `form.reset()` would, so the form is ready for the
238
+ next entry. A failed insert leaves the entry for the user to correct, and a
239
+ control the user changed while the request was in flight keeps the change.
240
+ An `lb-row-update` resets nothing: the row it sent lands back on its cells.
241
+
242
+ Give every `lb-cell` in a form a control to read. A cell that is neither an
243
+ `<input>`, `<select>`, or `<textarea>` nor wraps one is left out of the
244
+ values map, and a form with no cell to read sends nothing.
245
+
246
+ An insert or update gathers the row it belongs to: the nearest `<form>`,
247
+ `<tr>` or live row around the element that sends it, inside its scope. A
248
+ button submits its form the same way, whatever else sits beside it.
249
+
250
+ A form cannot go around a table row's controls, so there the row is the
251
+ form. Put the action on a button in the row, and it inserts every cell in
252
+ the row, wherever in the row the button and the cells are:
253
+
254
+ ```html
255
+ <table lb-list="roster">
256
+ <tbody>
257
+ <tr>
258
+ <td><input lb-cell="name" /></td>
259
+ <td>
260
+ <input lb-cell="role" />
261
+ <button lb-action="lb-row-insert">Add</button>
262
+ </td>
263
+ </tr>
264
+ </tbody>
265
+ </table>
266
+ ```
267
+
268
+ An `lb-row-update` button in a live row saves that row and no other, and a
269
+ form nested in a row gathers only the form. Anywhere else, write the form:
270
+ cells and a button in a `<div>` belong to no row, so the hub refuses the
271
+ request and says to put them in a `<form>`.
272
+
273
+ Put these two actions on a form or a button, not on an element that holds
274
+ the cells. A click into one of its inputs would send the row, so the hub
275
+ refuses it. A widget may dispatch either from any element, and the hub
276
+ gathers the same way; see
277
+ [Sending a request](./custom-elements.md#sending-a-request).
278
+
279
+ ### Committing one cell
280
+
281
+ Put `lb-row-update` on a widget that carries `lb-cell` to save that one cell
282
+ whenever it changes. The shipped `<lb-input>` sends it on `change`:
283
+
284
+ ```html
285
+ <template lb-key="id">
286
+ <tr>
287
+ <td><lb-input lb-cell="name" lb-action="lb-row-update"></lb-input></td>
288
+ <td><lb-input lb-cell="note" lb-action="lb-row-update"></lb-input></td>
289
+ </tr>
290
+ </template>
291
+ ```
292
+
293
+ An element carrying `lb-cell` is a record of one cell, the way a control has
294
+ a value and a form has values. Its update carries `values` holding that cell
295
+ alone, so an edit in one input never sends the other. It reaches the same
296
+ `rowUpdate` a form does. SQL has one UPDATE whether it sets one column or
297
+ many, and the application writes one handler for both.
298
+
299
+ The widget decides when the cell has changed. A native `<input>` carrying
300
+ `lb-cell` and `lb-row-update` has no such moment, since a click into it
301
+ would send it, so the hub refuses it and says so.
302
+
303
+ Leave `lb-action` off a widget inside an `lb-row-insert` or `lb-row-update`
304
+ form. The form reads every `lb-cell` in it on submit, so a widget that also
305
+ sent its own would write the same edit twice.
306
+
307
+ ## Query parms
308
+
309
+ A control that narrows what the page shows writes its value into the query
310
+ string instead of sending a request. `lb-query-parm` names the parm:
311
+
312
+ ```html
313
+ <select lb-query-parm="team">
314
+ <option value="">Every team</option>
315
+ <option value="Engines">Engines</option>
316
+ </select>
317
+ ```
318
+
319
+ On `change` the hub sets that one parm in the URL, leaving every other parm
320
+ alone, and takes it out when the value is empty. The write replaces the
321
+ current history entry; add `lb-query-parm-push` to push one instead. The page
322
+ then loads at the new URL, exactly as a cold load of it would, without
323
+ replacing its DOM.
324
+
325
+ After every load the hub lands each parm on the control that writes it, and
326
+ an absent parm lands empty, so the control shows what the address bar says.
327
+
328
+ A control that writes a query parm sends no request, and one that also
329
+ carries `lb-action` has that request refused.
330
+
331
+ The server reads the parms off `req.query` in `contextFor`; see
332
+ [the Express server](./server.md#database-layer). See
333
+ [Query parms](./TECHREF-1.0.md#query-parms) for the whole rule.
334
+
335
+ ## Conditional rendering
336
+
337
+ Loadbare ships static HTML and hydrates elements that are already in the
338
+ document. There is no `if`, and none is needed: write every possibility into
339
+ the page, and let a column decide which of them is present.
340
+
341
+ Write `lb-show` on an element, naming the column that decides it:
342
+
343
+ ```html
344
+ <template lb-key="id">
345
+ <tr>
346
+ <td lb-cell="name"></td>
347
+ <td><button lb-action="lb-row-delete" lb-show="removable">Remove</button></td>
348
+ </tr>
349
+ </template>
350
+ ```
351
+
352
+ ```sql
353
+ (ledger_count = 0 AND system_behavior IS NULL) AS removable
354
+ ```
355
+
356
+ A value of `null` or `false` takes the element out of the page, and any other
357
+ value puts it back. The hub never reads a string, so `"false"` is a value like
358
+ any other: have the query answer with a boolean or a null. A row that does not
359
+ carry the column leaves the element as it is, so a query that answers with
360
+ whole rows returns the column in every row.
361
+
362
+ `lb-show` binds the way `lb-cell` does, to the row on its nearest scoped
363
+ ancestor. On an element that is itself a scope, the column belongs to the
364
+ row around it, so this picker takes its choices from `groups` and whether it
365
+ is present from the account row:
366
+
367
+ ```html
368
+ <select lb-list="groups" lb-cell="group_id" lb-show="group_choice">
369
+ ```
370
+
371
+ An element may show a column and be decided by it, which shows a note only
372
+ when there is one:
373
+
374
+ ```html
375
+ <span lb-cell="note" lb-show="note"></span>
376
+ ```
377
+
378
+ An element that is not present cannot be clicked, but that is presentation:
379
+ the server still refuses what a request may not do.
380
+
381
+ ### Where an absent element is
382
+
383
+ An element whose column is off is moved into a `<template lb-show>` that
384
+ stands where it stood, and moved back out when the column turns on. The
385
+ developer never writes that template.
386
+
387
+ - Nothing renders it, whatever a stylesheet says, because a template's content
388
+ is not its children.
389
+ - It cannot be focused or clicked, assistive technology does not announce it,
390
+ and a form does not gather it.
391
+ - It is moved, never rebuilt, so a widget keeps its instance and a control
392
+ keeps what was typed into it.
393
+ - Values keep landing on it, and on every cell and scope inside it, while it
394
+ is away, so it returns current.
395
+
396
+ The builder ships every `lb-show` element already inside its template, so
397
+ nothing conditional shows until its row has landed.
398
+
399
+ A condition never changes the structure of a page. An absent element keeps
400
+ its place among its siblings, so a position selector (`:first-child`,
401
+ `:nth-child`, `:empty`, `+`, `~`) counts its template as a sibling. A selector
402
+ by tag, class or attribute is unaffected. Only a list changes a page's
403
+ structure.
404
+
405
+ A condition that is only a style is a class on an element that is present or
406
+ not:
407
+
408
+ ```html
409
+ <span lb-show="out_of_balance" class="danger">Out of balance</span>
410
+ ```
411
+
412
+ These are build errors:
413
+
414
+ - `lb-show` on a row template's root. A row that should not show is left out
415
+ by the query.
416
+ - `lb-show` with no row around it: outside every scope, on a scope with none
417
+ around it, or in a list scope outside its row template, where nothing lands.
418
+ - `lb-show` on a `<template>`.
419
+
420
+ A condition that is not data, such as a collapsed section or an open menu,
421
+ has no column. Use `<details>`, a stylesheet, or a widget. Every cell still
422
+ carries the value that landed on it as `lb-value`, for a widget to read or a
423
+ stylesheet to select on.
424
+
425
+ ### An empty list
426
+
427
+ The hub stamps every list scope with `lb-row-count`, the number of rows it is
428
+ showing. It is the one conditional a page cannot be sent, because the server
429
+ answers with rows and says nothing about how many survived. It makes an empty
430
+ list a stylesheet rule rather than code anywhere:
431
+
432
+ ```html
433
+ <div lb-list="roster">
434
+ <ul>
435
+ <template lb-key="id">
436
+ <li lb-cell="name"></li>
437
+ </template>
438
+ </ul>
439
+ <p class="roster-empty">No members yet.</p>
440
+ </div>
441
+ ```
442
+
443
+ ```css
444
+ .roster-empty {
445
+ display: none;
446
+ }
447
+ [lb-row-count="0"] .roster-empty {
448
+ display: revert;
449
+ }
450
+ ```
451
+
452
+ The scope is a `<div>` here rather than the `<ul>`, so that the empty message
453
+ is inside it and the same rule can reach both.
454
+
455
+ ## Request state
456
+
457
+ Loadbare stamps two attributes on the element a request came from — the
458
+ button, the form, or the widget itself:
459
+
460
+ | Attribute | Means |
461
+ |-------------------|-------------------------------------------|
462
+ | `lb-pending` | The request is in flight |
463
+ | `lb-error` | The last request from this element failed |
464
+
465
+ `lb-pending` is set when the request goes out and removed when it
466
+ settles. `lb-error` is set on a failed response, a network failure, or
467
+ a timeout alike, and cleared when that element sends its next request.
468
+
469
+ Neither one carries any meaning beyond the fact it states. Dim a pending
470
+ button in a stylesheet, or have a widget watch its own attributes and
471
+ disable itself. An application that styles neither behaves correctly and
472
+ shows nothing.
473
+
474
+ A native button or form pressed again while it carries `lb-pending` is
475
+ ignored, so a pending one is already disabled and the stylesheet only shows
476
+ it. A widget is not held back, since one that sends on change must send its
477
+ latest value. The hub also sets `aria-busy="true"` for as long as
478
+ `lb-pending` is present.
479
+
480
+ ## Sending a request from a widget
481
+
482
+ A widget can build and dispatch a request itself instead of carrying one of
483
+ the attributes above — which is what `<lb-input>` does, and what a widget
484
+ carrying `lb-action` must do. See
485
+ [Custom Elements](./custom-elements.md#code).
@@ -0,0 +1,38 @@
1
+ # The Elements of a Loadbare Application
2
+
3
+ This reference is organized into four groups that reflect how an application is
4
+ put together: the shell it lives in, the build that assembles it, the pages it
5
+ serves, and the widgets those pages are made of.
6
+
7
+ ## The application shell
8
+
9
+ | Topic | Description |
10
+ |--------------------------------------------------|-----------------------------------------------------|
11
+ | [`chrome.html`](./chrome.md) | The HTML document w/head, body, banner, main, etc. |
12
+ | [The Express server](./server.md) | Serves static assets and handles data channel calls |
13
+ | [The database layer](./server.md#database-layer) | Where the app's database access fits |
14
+
15
+ ## Building the app
16
+
17
+ | Topic | Description |
18
+ |----------------------------------------------------------|-------------------------------------------|
19
+ | [`imports.ts`](./builder.md#widgets-from-packages) | The packages this app takes widgets from |
20
+ | [`loadbare-app-build`](./builder.md#running-the-builder) | Configuring the build command |
21
+ | [CSS](./css.md) | How the builder packages css |
22
+
23
+ ## Application Pages
24
+
25
+ | Topic | Description |
26
+ |---------------------------------------------------------|--------------------------------|
27
+ | [`<name>.page.html`](./page-files.md#html) | The page's HTML |
28
+ | [`<name>.queries.ts`](./page-files.md#queries) | The data the page displays |
29
+ | [`<name>.requests.ts`](./page-files.md#requests-actions-crud) | Data Channel handlers |
30
+ | [Data Binding](./data-binding.md) | Connecting HTML to server data |
31
+
32
+ ## Widgets
33
+
34
+ | Topic | Description |
35
+ |------------------------------------------------|---------------------------------|
36
+ | [What a widget is](./custom-elements.md) | An HTML file, a script, or both |
37
+ | [`<tag-name>.html`](./custom-elements.md#html) | The markup the tag expands into |
38
+ | [`<tag-name>.ts`](./custom-elements.md#code) | The class the tag registers |