@effect.healthcare/messenger 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +88 -0
- package/.claude/context/composition.md +921 -0
- package/.claude/context/data.md +1036 -0
- package/.claude/context/event.md +686 -0
- package/.claude/context/gesture.md +614 -0
- package/.claude/context/operation.md +596 -0
- package/.claude/context/state.md +864 -0
- package/.claude/context/testing.md +876 -0
- package/.claude/context/validation.md +350 -0
- package/.claude/context/workflow.md +608 -0
- package/.claude/settings.local.json +11 -0
- package/.mcp.json +12 -0
- package/LICENSE +144 -0
- package/README.md +98 -0
- package/dist/.gitkeep +1 -0
- package/dist/component.style.css +6 -0
- package/dist/component.template.html +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/messenger.d.ts +181 -0
- package/dist/messenger.js +295 -0
- package/dist/messenger.meta.d.ts +117 -0
- package/dist/messenger.meta.js +79 -0
- package/dist/messenger.style.css +6 -0
- package/dist/messenger.template.html +1 -0
- package/dist/messenger.validation.d.ts +15 -0
- package/dist/messenger.validation.js +22 -0
- package/package.json +55 -0
- package/src/index.ts +16 -0
- package/src/messenger.meta.ts +131 -0
- package/src/messenger.ts +347 -0
- package/src/messenger.validation.ts +25 -0
- package/web-dev-server.config.mjs +8 -0
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
# Composition
|
|
2
|
+
|
|
3
|
+
This document defines an implementation policy for **component composition**, then illustrates how specifications are implemented by applying this policy.
|
|
4
|
+
|
|
5
|
+
This document is intentionally **composition** only, but part of a set of implementation policies which together define the policies which should be followed to transform specifications into an implementation which is predictable, maintainable, and production-ready at scale.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# How to Read This Policy
|
|
10
|
+
|
|
11
|
+
Rules in this policy fall into three strengths. Read each rule with its strength in mind:
|
|
12
|
+
|
|
13
|
+
- **Principles** are non-negotiable. They derive from framework correctness or TDD discipline and hold for every component. A conflict with a principle is a defect.
|
|
14
|
+
- **Templates** are invariant skeletons with explicit variation points. The skeleton applies to every component; specific steps activate or deactivate based on the component's declared classification (e.g., attribute-backed vs non-attribute, readonly vs writable, payload-bearing vs payload-free). Variation is part of the template, not a deviation from it.
|
|
15
|
+
- **Conventions** are canonical defaults that keep implementations readable across components. A second component may deviate when its shape genuinely warrants it; the deviation should be explained at review time, not forbidden by the policy.
|
|
16
|
+
|
|
17
|
+
When applying this policy to a new component, classify each rule before reconciling conflicts. A conflict with a principle is a defect; a conflict with a template means a variation point needs to be made explicit; a conflict with a convention is a documented deviation.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# 1) Composition Contract Must Be Declared in the Specification
|
|
22
|
+
|
|
23
|
+
Each component specification must declare its composition contract under `Composition`.
|
|
24
|
+
|
|
25
|
+
`Composition` must be an object whose property names are the canonical composition part names.
|
|
26
|
+
|
|
27
|
+
Part names must follow CSS-selector notation: `{type}.{class}` when the part has a class, or just `{type}` when the type alone is sufficient to identify the part. This convention keeps part names directly traceable to both the realized template element and the CSS selectors that target it.
|
|
28
|
+
|
|
29
|
+
Each composition definition must include:
|
|
30
|
+
|
|
31
|
+
- `description`
|
|
32
|
+
- `type`
|
|
33
|
+
|
|
34
|
+
A composition definition may also include:
|
|
35
|
+
|
|
36
|
+
- `contains`
|
|
37
|
+
- `id`
|
|
38
|
+
- `class`
|
|
39
|
+
- `cached`
|
|
40
|
+
|
|
41
|
+
Interpretation:
|
|
42
|
+
|
|
43
|
+
- `description` defines the semantic purpose of the structural part
|
|
44
|
+
- `type` defines the structural or element kind used to realize that part
|
|
45
|
+
- `contains` defines the direct child parts of that structural part in the composition contract
|
|
46
|
+
- `id` and `class` define structural identity where relevant to template realization
|
|
47
|
+
- if `contains` is absent, the part is treated as a leaf part in the declared composition
|
|
48
|
+
- if `cached` is `true`, the implementation must create a cached element reference for that part on the component instance
|
|
49
|
+
- if `cached` is absent or `false`, the part is realized in the template but does not require a cached runtime reference
|
|
50
|
+
|
|
51
|
+
Framework-injected template infrastructure such as stylesheet link elements is managed through component configuration and must not be declared as a composition part.
|
|
52
|
+
|
|
53
|
+
Example specification:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
"Composition": {
|
|
57
|
+
"template": {
|
|
58
|
+
"description": "The HTML template for the component.",
|
|
59
|
+
"contains": ["div.icon"],
|
|
60
|
+
"type": "template",
|
|
61
|
+
"id": "pin-button"
|
|
62
|
+
},
|
|
63
|
+
"div.icon": {
|
|
64
|
+
"description": "The main container of the component.",
|
|
65
|
+
"contains": ["svg.pinned", "svg.unpinned"],
|
|
66
|
+
"type": "div",
|
|
67
|
+
"class": "icon",
|
|
68
|
+
"cached": true
|
|
69
|
+
},
|
|
70
|
+
"svg.pinned": {
|
|
71
|
+
"description": "The SVG icon used for the pinned mode.",
|
|
72
|
+
"type": "svg",
|
|
73
|
+
"class": "pinned"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This defines the public structural contract that the implementation must honor.
|
|
79
|
+
|
|
80
|
+
# 2) Composition Must Describe Structure, Not Behavior
|
|
81
|
+
|
|
82
|
+
This framework distinguishes:
|
|
83
|
+
|
|
84
|
+
- `Composition`: the set of structural parts that make up the component and their containment relationships
|
|
85
|
+
- `State`: the set of component-owned runtime conditions
|
|
86
|
+
- `Data`: the set of component-owned content values
|
|
87
|
+
- `Operations`: the set of callable public actions exposed by the component
|
|
88
|
+
- `Events`: the set of observable semantic outcomes exposed by the component
|
|
89
|
+
- `Gestures`: the set of interaction inputs recognized by the component
|
|
90
|
+
|
|
91
|
+
Policy:
|
|
92
|
+
|
|
93
|
+
- composition describes what the component is structurally made of
|
|
94
|
+
- composition defines internal parts and their parent-child relationships
|
|
95
|
+
- composition may identify structural identity such as `id` or `class` where relevant to template realization
|
|
96
|
+
- composition must not be used to declare runtime condition
|
|
97
|
+
- composition must not be used to declare component-owned data
|
|
98
|
+
- composition must not be used to declare callable behavior
|
|
99
|
+
- composition must not be used to declare interaction input
|
|
100
|
+
- composition must not be used to declare semantic outcome
|
|
101
|
+
- composition must not be used to declare presentation attributes such as `fill`, `height`, `width`, or `viewBox` — these belong to CSS or the template
|
|
102
|
+
|
|
103
|
+
Examples of valid composition entries:
|
|
104
|
+
|
|
105
|
+
- `template`
|
|
106
|
+
- `div.icon`
|
|
107
|
+
- `div.panel`
|
|
108
|
+
- `span.label`
|
|
109
|
+
- `input.name`
|
|
110
|
+
- `svg.pinned`
|
|
111
|
+
- `svg.unpinned`
|
|
112
|
+
|
|
113
|
+
Examples that must not be modeled as composition:
|
|
114
|
+
|
|
115
|
+
- `visibility`
|
|
116
|
+
- `status`
|
|
117
|
+
- `mode`
|
|
118
|
+
- `metadata` as data content
|
|
119
|
+
- `toggle`
|
|
120
|
+
- `save`
|
|
121
|
+
- `click`
|
|
122
|
+
- `hover`
|
|
123
|
+
- `onpin`
|
|
124
|
+
- `onshow`
|
|
125
|
+
|
|
126
|
+
Composition therefore defines the structural contract that other policies depend on, but it does not redefine their concerns.
|
|
127
|
+
|
|
128
|
+
Examples:
|
|
129
|
+
|
|
130
|
+
- composition may declare that a `span.label` element exists
|
|
131
|
+
- data may define the canonical value rendered into that label
|
|
132
|
+
- state may determine when that label is shown or hidden
|
|
133
|
+
- gestures may target a structural part such as `div.icon` or `button.edit`
|
|
134
|
+
- operations may update data or state that affects the rendered structure
|
|
135
|
+
- events may report semantic outcomes produced through those other authoritative paths
|
|
136
|
+
|
|
137
|
+
# 3) Composition Must Be Realized in `<component>.template.html`
|
|
138
|
+
|
|
139
|
+
Declared composition must be realized concretely in `<component>.template.html`.
|
|
140
|
+
|
|
141
|
+
The template is the authoritative structural realization of the composition contract.
|
|
142
|
+
|
|
143
|
+
Policy:
|
|
144
|
+
|
|
145
|
+
- every declared composition part must be realized in the component template
|
|
146
|
+
- the template must preserve the declared parent-child containment relationships from `Composition`
|
|
147
|
+
- structural identity declared in the specification, such as `id` or `class`, must be realized in the template where applicable
|
|
148
|
+
- the template is the authoritative source of internal component structure
|
|
149
|
+
- runtime code, styling, gesture wiring, and DOM synchronization must depend on the realized template structure rather than redefining structure independently
|
|
150
|
+
- composition must not be realized through ad hoc manual DOM construction in component logic when the framework template mechanism is the intended structural path
|
|
151
|
+
|
|
152
|
+
Part names trace directly to template elements and CSS selectors:
|
|
153
|
+
|
|
154
|
+
| Part name | Template element | CSS selector |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `template` | `<template id="pin-button">` | — |
|
|
157
|
+
| `div.icon` | `<div class="icon">` | `.icon` |
|
|
158
|
+
| `svg.pinned` | `<svg class="pinned">` | `.pinned` or `svg.pinned` |
|
|
159
|
+
| `svg.unpinned` | `<svg class="unpinned">` | `.unpinned` or `svg.unpinned` |
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
|
|
163
|
+
```html
|
|
164
|
+
<template id="pin-button">
|
|
165
|
+
<div class="icon">
|
|
166
|
+
<svg class="pinned">...</svg>
|
|
167
|
+
<svg class="unpinned">...</svg>
|
|
168
|
+
</div>
|
|
169
|
+
</template>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The implementation therefore follows this structural flow:
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
Composition specification
|
|
176
|
+
↓
|
|
177
|
+
`<component>.template.html`
|
|
178
|
+
↓
|
|
179
|
+
realized internal DOM structure
|
|
180
|
+
↓
|
|
181
|
+
dependent runtime references / CSS targeting / gesture wiring
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The template is where the declared composition becomes actual component structure. Other implementation layers may depend on that structure, but they must not become competing sources of structural truth.
|
|
185
|
+
|
|
186
|
+
# 4) Root Template Identity Must Be Predictably Transformed Across Layers
|
|
187
|
+
|
|
188
|
+
The root template identity must remain consistent across the specification, metadata, configuration, and template.
|
|
189
|
+
|
|
190
|
+
This framework uses that identity to keep the composition contract traceable across implementation layers.
|
|
191
|
+
|
|
192
|
+
Policy:
|
|
193
|
+
|
|
194
|
+
- the specification must declare the root template identity as part of the `template` composition entry
|
|
195
|
+
- metadata must define the canonical template identity through `Tag`
|
|
196
|
+
- component configuration must use that canonical `Tag` value as the template id
|
|
197
|
+
- `<component>.template.html` must realize that same identity in the root `<template>` element
|
|
198
|
+
- root template identity must remain predictably aligned across all composition layers
|
|
199
|
+
- root template identity must not drift between specification, metadata, configuration, and template
|
|
200
|
+
|
|
201
|
+
Transformation pattern:
|
|
202
|
+
|
|
203
|
+
| Layer | Example |
|
|
204
|
+
|---|---|
|
|
205
|
+
| Specification | `Composition.template.id = "pin-button"` |
|
|
206
|
+
| Metadata | `Tag = "pin-button"` |
|
|
207
|
+
| Configuration | `template: { id: Tag }` |
|
|
208
|
+
| Template | `<template id="pin-button">` |
|
|
209
|
+
|
|
210
|
+
Example metadata and configuration pattern:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
export const Tag = "pin-button" as const;
|
|
214
|
+
|
|
215
|
+
export const configuration = {
|
|
216
|
+
url: import.meta.url,
|
|
217
|
+
template: {
|
|
218
|
+
id: Tag,
|
|
219
|
+
},
|
|
220
|
+
css: {
|
|
221
|
+
name: "pin.style.css",
|
|
222
|
+
},
|
|
223
|
+
} as const;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Example template:
|
|
227
|
+
|
|
228
|
+
```html
|
|
229
|
+
<template id="pin-button">
|
|
230
|
+
<div class="icon">...</div>
|
|
231
|
+
</template>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This predictable transformation keeps the root composition identity directly traceable back to the specification and prevents naming drift across implementation layers.
|
|
235
|
+
|
|
236
|
+
# 5) Runtime-Relevant Composition Parts Must Be Cached as Explicit Element References
|
|
237
|
+
|
|
238
|
+
Any composition part declared with `cached: true` must be cached on the component instance as an explicit element reference.
|
|
239
|
+
|
|
240
|
+
This cached structure is the component's runtime access layer for realized composition. It is derived from the template structure, but it does not need to reproduce the full composition tree.
|
|
241
|
+
|
|
242
|
+
Policy:
|
|
243
|
+
|
|
244
|
+
- any composition part declared with `cached: true` must have a cached element reference on the component instance
|
|
245
|
+
- cached references must be derived from the realized template structure
|
|
246
|
+
- cached references form the runtime access vocabulary for internal structural parts used by the component
|
|
247
|
+
- cached references must be explicit rather than relying on repeated ad hoc selector queries at the point of use
|
|
248
|
+
- the cache structure does not need to mirror the full composition nesting hierarchy
|
|
249
|
+
- a flat cache is valid when it preserves clear and stable traceability back to the declared composition and realized template
|
|
250
|
+
|
|
251
|
+
Example pattern:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
protected elements: { icon: HTMLDivElement | null } = { icon: null };
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Example with multiple cached parts:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
protected elements = {
|
|
261
|
+
label: null as HTMLElement | null,
|
|
262
|
+
name: null as HTMLInputElement | null,
|
|
263
|
+
edit: null as HTMLElement | null,
|
|
264
|
+
save: null as HTMLElement | null,
|
|
265
|
+
cancel: null as HTMLElement | null,
|
|
266
|
+
};
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Benefits:
|
|
270
|
+
|
|
271
|
+
- prevents repeated querying
|
|
272
|
+
- makes runtime dependencies explicit
|
|
273
|
+
- keeps gesture wiring, DOM updates, and internal synchronization traceable to composition
|
|
274
|
+
- avoids forcing the runtime cache to duplicate the full composition tree when that would add structural noise without improving clarity
|
|
275
|
+
|
|
276
|
+
The composition contract defines the full structure. The runtime cache defines only the internal structural references that runtime behavior depends on, as declared by `cached: true` in the specification.
|
|
277
|
+
|
|
278
|
+
# 6) Cached Element Reference Names Must Prefer the Deepest Unambiguous Composition Name
|
|
279
|
+
|
|
280
|
+
When a composition part is cached for runtime use, its cached reference name should be derived from the class portion of its CSS-selector part name.
|
|
281
|
+
|
|
282
|
+
This keeps cached references compact while preserving traceability back to the composition contract and realized template.
|
|
283
|
+
|
|
284
|
+
Policy:
|
|
285
|
+
|
|
286
|
+
- cached element reference names must be derived from declared composition parts
|
|
287
|
+
- the preferred cached name is the class portion of the part name when it uniquely identifies the runtime-relevant part within the cache
|
|
288
|
+
- full structural path naming is not required when the class portion is already unambiguous
|
|
289
|
+
- if two or more runtime-relevant parts would produce the same cached name, the implementation must add enough qualification to make the names distinct
|
|
290
|
+
- cached reference naming must remain stable and predictable for the declared composition
|
|
291
|
+
- cached reference names should remain as small as possible while still being unambiguous
|
|
292
|
+
|
|
293
|
+
Preferred pattern:
|
|
294
|
+
|
|
295
|
+
- composition part: `div.icon`
|
|
296
|
+
- template realization: `<div class="icon">`
|
|
297
|
+
- runtime cache: `elements.icon`
|
|
298
|
+
|
|
299
|
+
Also valid when qualification is required:
|
|
300
|
+
|
|
301
|
+
- composition part: `span.title` inside `div.header`
|
|
302
|
+
- composition part: `span.title` inside `div.dialog`
|
|
303
|
+
- possible runtime cache: `elements.headerTitle`, `elements.dialogTitle`
|
|
304
|
+
|
|
305
|
+
Examples:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
protected elements: { icon: HTMLDivElement | null } = { icon: null };
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
protected elements = {
|
|
313
|
+
label: null as HTMLElement | null,
|
|
314
|
+
name: null as HTMLInputElement | null,
|
|
315
|
+
edit: null as HTMLElement | null,
|
|
316
|
+
save: null as HTMLElement | null,
|
|
317
|
+
cancel: null as HTMLElement | null,
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
In these examples:
|
|
322
|
+
|
|
323
|
+
- `elements.icon` is derived from part `div.icon` — the class `icon` is the cached name
|
|
324
|
+
- `elements.label` is derived from part `span.label` — the class `label` is the cached name
|
|
325
|
+
- `elements.panel.metadata.label` is not required because `label` is already unambiguous
|
|
326
|
+
|
|
327
|
+
The composition contract defines the full structural hierarchy. Cached element reference names define the smallest unambiguous runtime vocabulary derived from that hierarchy.
|
|
328
|
+
|
|
329
|
+
# 7) Only Composition Parts Declared with `cached: true` Need Cached References
|
|
330
|
+
|
|
331
|
+
Not every declared composition part requires a cached runtime reference.
|
|
332
|
+
|
|
333
|
+
The composition contract may describe more structure than runtime logic needs to access directly.
|
|
334
|
+
|
|
335
|
+
Policy:
|
|
336
|
+
|
|
337
|
+
- only composition parts declared with `cached: true` must be cached as explicit element references
|
|
338
|
+
- a composition part should be declared with `cached: true` when component logic reads from it, writes to it, attaches listeners to it, or otherwise depends on it as an explicit DOM target
|
|
339
|
+
- purely structural or presentational descendants do not require `cached: true` when runtime logic does not access them directly
|
|
340
|
+
- the absence of `cached: true` does not mean a composition part is absent from the structural contract; it means the part is not part of the component's runtime access layer
|
|
341
|
+
- cached references should be limited to the smallest set of structural parts that runtime behavior actually depends on
|
|
342
|
+
|
|
343
|
+
In `Pin`:
|
|
344
|
+
|
|
345
|
+
- `div.icon` is declared with `cached: true` because runtime gesture listeners attach to it
|
|
346
|
+
- `svg.pinned` does not require `cached: true` when it participates only in structural rendering and CSS-driven visual state
|
|
347
|
+
- `svg.unpinned` does not require `cached: true` when it participates only in structural rendering and CSS-driven visual state
|
|
348
|
+
|
|
349
|
+
In `Title`:
|
|
350
|
+
|
|
351
|
+
- `span.label` is declared with `cached: true` because runtime logic writes text content to it
|
|
352
|
+
- `input.name` is declared with `cached: true` because runtime logic reads, writes, and focuses the input
|
|
353
|
+
- `button.edit`, `button.save`, and `button.cancel` are declared with `cached: true` because runtime gesture listeners attach to them
|
|
354
|
+
- higher structural containers such as `div.panel`, `div.metadata`, and `div.tasks` do not require `cached: true` when runtime logic does not directly depend on them
|
|
355
|
+
|
|
356
|
+
Examples:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
protected elements: { icon: HTMLDivElement | null } = { icon: null };
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
protected elements = {
|
|
364
|
+
label: null as HTMLElement | null,
|
|
365
|
+
name: null as HTMLInputElement | null,
|
|
366
|
+
edit: null as HTMLElement | null,
|
|
367
|
+
save: null as HTMLElement | null,
|
|
368
|
+
cancel: null as HTMLElement | null,
|
|
369
|
+
};
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The composition specification defines the full internal structure. The element cache defines only the `cached: true` subset of that structure.
|
|
373
|
+
|
|
374
|
+
# 8) Composition Must Provide Stable Structural Targets for Other Policies
|
|
375
|
+
|
|
376
|
+
Composition is the structural foundation that other implementation policies depend on.
|
|
377
|
+
|
|
378
|
+
Composition does not redefine state, data, operations, events, or gestures. Instead, it provides the stable internal structure that those policies use when they interact with the realized component DOM.
|
|
379
|
+
|
|
380
|
+
Policy:
|
|
381
|
+
|
|
382
|
+
- composition must provide stable internal structural targets for runtime concerns defined by other policies
|
|
383
|
+
- gesture wiring must attach to structural targets realized through composition
|
|
384
|
+
- state-driven styling must target structural parts realized through composition
|
|
385
|
+
- data and state synchronization may update structural parts realized through composition
|
|
386
|
+
- runtime logic must depend on the realized composition structure rather than inventing separate structural targets ad hoc
|
|
387
|
+
- composition remains the authoritative structural contract even when other policies depend on parts of that structure
|
|
388
|
+
|
|
389
|
+
Example dependency flow:
|
|
390
|
+
|
|
391
|
+
```text
|
|
392
|
+
Composition
|
|
393
|
+
↓
|
|
394
|
+
template structure
|
|
395
|
+
↓
|
|
396
|
+
cached runtime references
|
|
397
|
+
↓
|
|
398
|
+
gesture wiring / DOM synchronization / CSS targeting
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
In `Pin`:
|
|
402
|
+
|
|
403
|
+
- composition defines `div.icon`, `svg.pinned`, and `svg.unpinned`
|
|
404
|
+
- the template realizes those parts
|
|
405
|
+
- the component caches `elements.icon` as the runtime gesture target
|
|
406
|
+
- gestures attach to `elements.icon`
|
|
407
|
+
- CSS targets `.icon`, `.pinned`, and `.unpinned`
|
|
408
|
+
- reflected state such as `status` and `visibility` changes how those structural parts are presented
|
|
409
|
+
|
|
410
|
+
In `Title`:
|
|
411
|
+
|
|
412
|
+
- composition defines structural parts such as `span.label`, `input.name`, `button.edit`, `button.save`, and `button.cancel`
|
|
413
|
+
- the template realizes those parts
|
|
414
|
+
- the component caches the `cached: true` subset in `elements`
|
|
415
|
+
- gesture wiring attaches to `button.edit`, `button.save`, `button.cancel`, and `input.name`
|
|
416
|
+
- DOM synchronization writes to `span.label` and `input.name`
|
|
417
|
+
- reflected state such as `mode`, `visibility`, and `content` changes how those structural parts are presented
|
|
418
|
+
|
|
419
|
+
Composition therefore supplies the structural targets that other policies rely on, while those other policies remain authoritative for behavior, mutation, dispatch, and interaction semantics.
|
|
420
|
+
|
|
421
|
+
# 9) Composition Should Prefer Structural Minimalism
|
|
422
|
+
|
|
423
|
+
A component should declare the smallest internal structure necessary to realize its public contract and support its dependent implementation policies.
|
|
424
|
+
|
|
425
|
+
Composition is a structural contract, not a complete serialization of incidental markup.
|
|
426
|
+
|
|
427
|
+
Policy:
|
|
428
|
+
|
|
429
|
+
- composition should include structural parts that are semantically meaningful to the component implementation
|
|
430
|
+
- composition should include structural parts that other policies depend on for runtime targeting, DOM synchronization, or CSS/state-driven presentation
|
|
431
|
+
- composition should avoid declaring incidental markup that carries no meaningful structural role in the component contract
|
|
432
|
+
- composition should avoid unnecessary wrapper layers when they do not contribute structural meaning
|
|
433
|
+
- composition should remain small enough to stay traceable across specification, template, runtime references, and dependent policies
|
|
434
|
+
- composition minimalism must not remove structural parts that are required by runtime logic, styling, or the declared structural contract
|
|
435
|
+
- framework-injected template infrastructure such as stylesheet link elements is not a declared composition part
|
|
436
|
+
|
|
437
|
+
Examples of composition parts that are often worth declaring:
|
|
438
|
+
|
|
439
|
+
- root template parts
|
|
440
|
+
- interactive containers
|
|
441
|
+
- named display surfaces
|
|
442
|
+
- editable form surfaces
|
|
443
|
+
- task or action targets
|
|
444
|
+
- structural variants that state-driven styling depends on
|
|
445
|
+
|
|
446
|
+
Examples of structure that may remain implicit unless it carries contract value:
|
|
447
|
+
|
|
448
|
+
- incidental wrapper elements with no runtime or styling role
|
|
449
|
+
- leaf SVG internals such as `path` when no other policy depends on them directly
|
|
450
|
+
- markup introduced only for formatting convenience without structural meaning
|
|
451
|
+
- framework-injected infrastructure managed through component configuration
|
|
452
|
+
|
|
453
|
+
In `Pin`, the composition remains intentionally small:
|
|
454
|
+
|
|
455
|
+
- one root `template`
|
|
456
|
+
- one interactive container `div.icon`
|
|
457
|
+
- two visual variants `svg.pinned` and `svg.unpinned`
|
|
458
|
+
|
|
459
|
+
In `Title`, the composition may be broader because more runtime and styling concerns depend on internal structure, but it should still stop at the level where structural parts remain meaningful to the component contract.
|
|
460
|
+
|
|
461
|
+
Composition should therefore be as small as possible, but no smaller than the structure required to keep the component's contract explicit and its dependent policies traceable.
|
|
462
|
+
|
|
463
|
+
# 10) Composition Must Separate Structural Parts from Visual Styling
|
|
464
|
+
|
|
465
|
+
Composition defines structure. CSS defines presentation.
|
|
466
|
+
|
|
467
|
+
A composition entry may identify structural parts that styling depends on, but composition itself must not become a styling contract.
|
|
468
|
+
|
|
469
|
+
Policy:
|
|
470
|
+
|
|
471
|
+
- composition must define the existence and structural role of internal parts
|
|
472
|
+
- composition may identify structural identity such as `class` or `id` when that identity is part of template realization
|
|
473
|
+
- composition must not define presentation rules, visual appearance, or styling behavior
|
|
474
|
+
- composition must not declare presentation attributes such as `fill`, `height`, `width`, `viewBox`, or `stroke` — these belong to CSS or the template
|
|
475
|
+
- CSS must remain the authoritative layer for presentation concerns
|
|
476
|
+
- state-reflective styling may target composition parts, but the styling logic itself belongs to CSS rather than to composition
|
|
477
|
+
- composition may declare that a structural part exists; CSS may define how that part appears in different states or interaction conditions
|
|
478
|
+
|
|
479
|
+
Examples:
|
|
480
|
+
|
|
481
|
+
- composition says that a `div.icon` part exists
|
|
482
|
+
- CSS says how `.icon` looks
|
|
483
|
+
- composition says that `svg.pinned` and `svg.unpinned` exist
|
|
484
|
+
- CSS says when `.pinned` and `.unpinned` are shown or hidden
|
|
485
|
+
- composition says that `span.label`, `input.name`, `button.edit`, `button.save`, and `button.cancel` exist
|
|
486
|
+
- CSS says how those parts are laid out and how state such as `mode`, `content`, or `visibility` affects presentation
|
|
487
|
+
|
|
488
|
+
Example CSS:
|
|
489
|
+
|
|
490
|
+
```css
|
|
491
|
+
:host([status="pinned"]) .pinned {
|
|
492
|
+
display: inline-block;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
:host([status="pinned"]) .unpinned {
|
|
496
|
+
display: none;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
:host([mode="view"]) .label {
|
|
500
|
+
display: block;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
:host([mode="view"]) .name {
|
|
504
|
+
display: none;
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Composition therefore provides the stable structural targets that styling depends on, while CSS remains the authoritative implementation layer for presentation.
|
|
509
|
+
|
|
510
|
+
# 11) Composition Must Be Realized Through the Framework Template Mechanism
|
|
511
|
+
|
|
512
|
+
Composition must be implemented through the framework's template-loading mechanism rather than through ad hoc DOM construction in component logic.
|
|
513
|
+
|
|
514
|
+
The framework template mechanism is the authoritative implementation path for realizing declared component composition.
|
|
515
|
+
|
|
516
|
+
Policy:
|
|
517
|
+
|
|
518
|
+
- declared composition must be realized through `<component>.template.html`
|
|
519
|
+
- the component class must use the framework template-loading mechanism to realize that template
|
|
520
|
+
- component logic must not become a competing source of structural truth by manually constructing the declared internal DOM tree
|
|
521
|
+
- runtime logic may cache, read, update, or attach listeners to realized structural parts, but it should depend on the realized template rather than constructing those parts imperatively
|
|
522
|
+
- composition remains declarative when structure is defined in the template and loaded through the framework mechanism
|
|
523
|
+
|
|
524
|
+
Example pattern:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
public static Template = new Template(import.meta.url);
|
|
528
|
+
|
|
529
|
+
constructor() {
|
|
530
|
+
super(configuration);
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Example configuration:
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
export const configuration = {
|
|
538
|
+
url: import.meta.url,
|
|
539
|
+
template: {
|
|
540
|
+
id: Tag,
|
|
541
|
+
},
|
|
542
|
+
css: {
|
|
543
|
+
name: "pin.style.css",
|
|
544
|
+
},
|
|
545
|
+
} as const;
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
This ensures:
|
|
549
|
+
|
|
550
|
+
- composition remains declarative
|
|
551
|
+
- the template remains the canonical source of internal structure
|
|
552
|
+
- component logic remains focused on runtime behavior rather than DOM construction
|
|
553
|
+
- the implementation stays traceable from composition specification to template realization to runtime access
|
|
554
|
+
|
|
555
|
+
The composition flow is therefore:
|
|
556
|
+
|
|
557
|
+
```text
|
|
558
|
+
Composition specification
|
|
559
|
+
↓
|
|
560
|
+
`<component>.template.html`
|
|
561
|
+
↓
|
|
562
|
+
framework template-loading mechanism
|
|
563
|
+
↓
|
|
564
|
+
realized internal DOM structure
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
The framework template mechanism is the authoritative path by which declared composition becomes actual component structure.
|
|
568
|
+
|
|
569
|
+
# 12) Composition Changes Must Be Treated as Contract Changes
|
|
570
|
+
|
|
571
|
+
If a declared composition part is referenced by runtime logic, styling, or other framework policies, changing that part is a contract-level change.
|
|
572
|
+
|
|
573
|
+
Composition is the authoritative structural contract. Changes to that contract must be reflected consistently across all dependent implementation layers.
|
|
574
|
+
|
|
575
|
+
Policy:
|
|
576
|
+
|
|
577
|
+
- renaming, removing, reparenting, or changing the structural identity of a declared composition part must be treated as a composition contract change
|
|
578
|
+
- a composition contract change must be reflected consistently in the specification, template, runtime cache, styling, and any dependent policy implementations
|
|
579
|
+
- runtime references must not continue to depend on outdated structural names or selectors after a composition contract change
|
|
580
|
+
- styling must not continue to target outdated structural identities after a composition contract change
|
|
581
|
+
- gesture wiring, DOM synchronization, and other runtime logic must remain aligned with the updated realized composition
|
|
582
|
+
- composition changes must preserve cross-layer traceability after the update
|
|
583
|
+
|
|
584
|
+
Examples of contract-impacting changes:
|
|
585
|
+
|
|
586
|
+
- renaming `div.icon` to `button.icon`
|
|
587
|
+
- changing `.icon` to a different structural identity without updating runtime references
|
|
588
|
+
- removing `svg.pinned` while CSS still depends on `.pinned`
|
|
589
|
+
- renaming `span.label` to `span.title` while runtime logic still writes to `elements.label`
|
|
590
|
+
- changing the structure so that `button.edit`, `button.save`, or `button.cancel` no longer match their cached references or listener targets
|
|
591
|
+
|
|
592
|
+
Examples of layers that may require coordinated updates:
|
|
593
|
+
|
|
594
|
+
- `Composition` in the specification
|
|
595
|
+
- `<component>.template.html`
|
|
596
|
+
- cached runtime references in `elements`
|
|
597
|
+
- selector-based styling in `<component>.style.css`
|
|
598
|
+
- gesture listener wiring and DOM synchronization in `<component>.ts`
|
|
599
|
+
|
|
600
|
+
Composition changes therefore must be treated with the same discipline as changes to state contracts, data contracts, event contracts, operation contracts, or gesture contracts: dependent implementation layers must be updated together so the component remains predictable and internally consistent.
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
# Policy Applied to Specification
|
|
605
|
+
|
|
606
|
+
Specification fragment:
|
|
607
|
+
|
|
608
|
+
```json
|
|
609
|
+
{
|
|
610
|
+
"Composition": {
|
|
611
|
+
"template": {
|
|
612
|
+
"description": "The HTML template for the pin component.",
|
|
613
|
+
"contains": ["div.icon"],
|
|
614
|
+
"type": "template",
|
|
615
|
+
"id": "pin-button"
|
|
616
|
+
},
|
|
617
|
+
"div.icon": {
|
|
618
|
+
"description": "The main container of the pin component, styled as a button.",
|
|
619
|
+
"contains": ["svg.pinned", "svg.unpinned"],
|
|
620
|
+
"type": "div",
|
|
621
|
+
"class": "icon",
|
|
622
|
+
"cached": true
|
|
623
|
+
},
|
|
624
|
+
"svg.pinned": {
|
|
625
|
+
"description": "The SVG icon used for the pinned mode.",
|
|
626
|
+
"type": "svg",
|
|
627
|
+
"class": "pinned"
|
|
628
|
+
},
|
|
629
|
+
"svg.unpinned": {
|
|
630
|
+
"description": "The SVG icon used for the unpinned mode.",
|
|
631
|
+
"type": "svg",
|
|
632
|
+
"class": "unpinned"
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
How this specification is implemented:
|
|
639
|
+
|
|
640
|
+
- every declared composition part is realized in `<component>.template.html`
|
|
641
|
+
- the root template identity is predictably transformed across specification, metadata, configuration, and template
|
|
642
|
+
- the framework template mechanism is the authoritative realization path for declared composition
|
|
643
|
+
- only the composition parts declared with `cached: true` are cached as explicit element references
|
|
644
|
+
- cached element reference names are derived from the class portion of the CSS-selector part name
|
|
645
|
+
- other policies such as gestures, state-reflective styling, and runtime DOM synchronization depend on the realized composition structure, but do not redefine it
|
|
646
|
+
|
|
647
|
+
Applied to `Pin`:
|
|
648
|
+
|
|
649
|
+
- `template`
|
|
650
|
+
- declared in the specification with `id: "pin-button"`
|
|
651
|
+
- realized by `<template id="pin-button">`
|
|
652
|
+
- mapped through `Tag = "pin-button"`
|
|
653
|
+
- loaded through component configuration with `template: { id: Tag }`
|
|
654
|
+
- `div.icon`
|
|
655
|
+
- declared as a structural child of `template` with `cached: true`
|
|
656
|
+
- realized by `<div class="icon">`
|
|
657
|
+
- cached as `elements.icon` — the class `icon` is the cached name
|
|
658
|
+
- used as the runtime gesture target
|
|
659
|
+
- `svg.pinned`
|
|
660
|
+
- declared as a structural child of `div.icon`
|
|
661
|
+
- realized by `<svg class="pinned">`
|
|
662
|
+
- not declared with `cached: true` — participates in CSS-controlled visual state rendering only
|
|
663
|
+
- `svg.unpinned`
|
|
664
|
+
- declared as a structural child of `div.icon`
|
|
665
|
+
- realized by `<svg class="unpinned">`
|
|
666
|
+
- not declared with `cached: true` — participates in CSS-controlled visual state rendering only
|
|
667
|
+
|
|
668
|
+
Implementation:
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
export const Tag = "pin-button" as const;
|
|
672
|
+
|
|
673
|
+
export const configuration = {
|
|
674
|
+
url: import.meta.url,
|
|
675
|
+
template: {
|
|
676
|
+
id: Tag,
|
|
677
|
+
},
|
|
678
|
+
css: {
|
|
679
|
+
name: "pin.style.css",
|
|
680
|
+
},
|
|
681
|
+
} as const;
|
|
682
|
+
|
|
683
|
+
export class Pin extends Component {
|
|
684
|
+
protected elements: { icon: HTMLDivElement | null } = { icon: null };
|
|
685
|
+
|
|
686
|
+
public static Template = new Template(import.meta.url);
|
|
687
|
+
|
|
688
|
+
constructor() {
|
|
689
|
+
super(configuration);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Template:
|
|
695
|
+
|
|
696
|
+
```html
|
|
697
|
+
<template id="pin-button">
|
|
698
|
+
<div class="icon">
|
|
699
|
+
<svg class="pinned">...</svg>
|
|
700
|
+
<svg class="unpinned">...</svg>
|
|
701
|
+
</div>
|
|
702
|
+
</template>
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
How the implementation applies the policy:
|
|
706
|
+
|
|
707
|
+
- the specification defines the structural contract under `Composition`
|
|
708
|
+
- part names follow CSS-selector notation — each name traces directly to its template element and CSS selector
|
|
709
|
+
- the root template identity is declared in the specification and predictably transformed into metadata, configuration, and template realization
|
|
710
|
+
- `<component>.template.html` is the authoritative structural realization of the declared composition
|
|
711
|
+
- the framework template-loading mechanism realizes that declared structure without requiring manual DOM construction in component logic
|
|
712
|
+
- `elements.icon` is the only cached reference, derived from the class of `div.icon`, because it is the only part declared with `cached: true`
|
|
713
|
+
- `svg.pinned` and `svg.unpinned` remain part of the declared and realized composition even though they are not cached
|
|
714
|
+
- gesture wiring and CSS styling depend on the realized structural parts defined by composition
|
|
715
|
+
|
|
716
|
+
The important transformation is therefore:
|
|
717
|
+
|
|
718
|
+
```text
|
|
719
|
+
Composition specification
|
|
720
|
+
↓
|
|
721
|
+
template identity mapping
|
|
722
|
+
↓
|
|
723
|
+
`<component>.template.html`
|
|
724
|
+
↓
|
|
725
|
+
framework template-loading mechanism
|
|
726
|
+
↓
|
|
727
|
+
realized internal DOM structure
|
|
728
|
+
↓
|
|
729
|
+
cached: true references / CSS targeting / gesture wiring
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
# Policy Checklist
|
|
735
|
+
|
|
736
|
+
A compliant component must:
|
|
737
|
+
|
|
738
|
+
1. Declare composition under `Composition` in the specification.
|
|
739
|
+
2. Define each composition part using the required specification contract.
|
|
740
|
+
3. Name composition parts using CSS-selector notation — `{type}.{class}` when a class distinguishes the part, or `{type}` when the type alone is sufficient.
|
|
741
|
+
4. Distinguish composition from state, data, operations, events, and gestures.
|
|
742
|
+
5. Avoid declaring presentation attributes such as `fill`, `height`, `width`, or `viewBox` in composition — these belong to CSS or the template.
|
|
743
|
+
6. Avoid declaring framework-injected infrastructure such as stylesheet link elements as composition parts.
|
|
744
|
+
7. Realize every declared composition part in `<component>.template.html`.
|
|
745
|
+
8. Preserve declared parent-child containment relationships in the realized template.
|
|
746
|
+
9. Keep root template identity predictably aligned across specification, metadata, configuration, and template.
|
|
747
|
+
10. Use the framework template-loading mechanism as the authoritative realization path for declared composition.
|
|
748
|
+
11. Avoid manual component-side DOM construction as a competing source of structural truth for declared composition.
|
|
749
|
+
12. Declare `cached: true` on every composition part that runtime logic reads from, writes to, attaches listeners to, or otherwise depends on as an explicit DOM target.
|
|
750
|
+
13. Cache every composition part declared with `cached: true` as an explicit element reference on the component instance.
|
|
751
|
+
14. Derive cached element reference names from the class portion of the CSS-selector part name.
|
|
752
|
+
15. Add qualification to cached element reference names only when needed to avoid ambiguity.
|
|
753
|
+
16. Cache only the `cached: true` subset of declared composition.
|
|
754
|
+
17. Treat uncached composition parts as still part of the structural contract when they are realized in the template.
|
|
755
|
+
18. Provide stable structural targets for gesture wiring, DOM synchronization, and CSS/state-reflective styling.
|
|
756
|
+
19. Keep runtime logic dependent on realized composition rather than inventing ad hoc structural targets.
|
|
757
|
+
20. Prefer structural minimalism in the declared composition.
|
|
758
|
+
21. Avoid declaring incidental markup that carries no meaningful contract value.
|
|
759
|
+
22. Keep composition separate from visual styling concerns.
|
|
760
|
+
23. Keep CSS as the authoritative layer for presentation.
|
|
761
|
+
24. Treat renames, removals, reparenting, or structural identity changes of declared composition parts as contract changes.
|
|
762
|
+
25. Reflect composition contract changes consistently across specification, template, runtime cache, styling, and dependent policy implementations.
|
|
763
|
+
26. Preserve cross-layer traceability from composition specification to template realization to runtime access.
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
# Component Composition Specification Schema
|
|
768
|
+
|
|
769
|
+
JSON Schema:
|
|
770
|
+
|
|
771
|
+
```json
|
|
772
|
+
{
|
|
773
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
774
|
+
"$id": "https://scalable.software/schema/component-composition.schema.json",
|
|
775
|
+
"title": "Component Composition",
|
|
776
|
+
"description": "Schema for the Composition object in a component specification.",
|
|
777
|
+
"type": "object",
|
|
778
|
+
"propertyNames": {
|
|
779
|
+
"type": "string",
|
|
780
|
+
"minLength": 1,
|
|
781
|
+
"description": "Canonical composition part name in CSS-selector notation. Use {type}.{class} when a class distinguishes the part (for example: div.icon, svg.pinned, span.label, button.save), or {type} when the type alone is sufficient (for example: template)."
|
|
782
|
+
},
|
|
783
|
+
"additionalProperties": {
|
|
784
|
+
"type": "object",
|
|
785
|
+
"description": "A composition part definition.",
|
|
786
|
+
"required": ["description", "type"],
|
|
787
|
+
"additionalProperties": false,
|
|
788
|
+
"properties": {
|
|
789
|
+
"description": {
|
|
790
|
+
"type": "string",
|
|
791
|
+
"minLength": 1,
|
|
792
|
+
"description": "Human-readable description of the structural part."
|
|
793
|
+
},
|
|
794
|
+
"type": {
|
|
795
|
+
"type": "string",
|
|
796
|
+
"minLength": 1,
|
|
797
|
+
"description": "Structural or element kind used to realize this part, for example: template, div, span, input, button, svg."
|
|
798
|
+
},
|
|
799
|
+
"contains": {
|
|
800
|
+
"type": "array",
|
|
801
|
+
"description": "Ordered list of direct child composition parts contained by this structural part.",
|
|
802
|
+
"minItems": 1,
|
|
803
|
+
"items": {
|
|
804
|
+
"type": "string",
|
|
805
|
+
"minLength": 1,
|
|
806
|
+
"description": "Canonical composition part name of a direct child."
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
"id": {
|
|
810
|
+
"type": "string",
|
|
811
|
+
"minLength": 1,
|
|
812
|
+
"description": "Structural identity used when the realized template part is identified by id."
|
|
813
|
+
},
|
|
814
|
+
"class": {
|
|
815
|
+
"type": "string",
|
|
816
|
+
"minLength": 1,
|
|
817
|
+
"description": "Structural identity used when the realized template part is identified by class."
|
|
818
|
+
},
|
|
819
|
+
"cached": {
|
|
820
|
+
"type": "boolean",
|
|
821
|
+
"description": "When true, the implementation must create a cached element reference for this part on the component instance. Declare cached: true on every part that runtime logic reads from, writes to, or attaches listeners to."
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
Example JSON:
|
|
829
|
+
|
|
830
|
+
```json
|
|
831
|
+
{
|
|
832
|
+
"Composition": {
|
|
833
|
+
"template": {
|
|
834
|
+
"description": "The HTML template for the pin component.",
|
|
835
|
+
"contains": ["div.icon"],
|
|
836
|
+
"type": "template",
|
|
837
|
+
"id": "pin-button"
|
|
838
|
+
},
|
|
839
|
+
"div.icon": {
|
|
840
|
+
"description": "The main container of the pin component, styled as a button.",
|
|
841
|
+
"contains": ["svg.pinned", "svg.unpinned"],
|
|
842
|
+
"type": "div",
|
|
843
|
+
"class": "icon",
|
|
844
|
+
"cached": true
|
|
845
|
+
},
|
|
846
|
+
"svg.pinned": {
|
|
847
|
+
"description": "The SVG icon used for the pinned mode.",
|
|
848
|
+
"type": "svg",
|
|
849
|
+
"class": "pinned"
|
|
850
|
+
},
|
|
851
|
+
"svg.unpinned": {
|
|
852
|
+
"description": "The SVG icon used for the unpinned mode.",
|
|
853
|
+
"type": "svg",
|
|
854
|
+
"class": "unpinned"
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
Example JSON with a richer component:
|
|
861
|
+
|
|
862
|
+
```json
|
|
863
|
+
{
|
|
864
|
+
"Composition": {
|
|
865
|
+
"template": {
|
|
866
|
+
"description": "The HTML template for the title component.",
|
|
867
|
+
"contains": ["div.panel"],
|
|
868
|
+
"type": "template",
|
|
869
|
+
"id": "app-title"
|
|
870
|
+
},
|
|
871
|
+
"div.panel": {
|
|
872
|
+
"description": "The root panel of the title component.",
|
|
873
|
+
"contains": ["div.metadata", "div.tasks"],
|
|
874
|
+
"type": "div",
|
|
875
|
+
"class": "panel"
|
|
876
|
+
},
|
|
877
|
+
"div.metadata": {
|
|
878
|
+
"description": "The metadata display and editing region.",
|
|
879
|
+
"contains": ["span.label", "input.name"],
|
|
880
|
+
"type": "div",
|
|
881
|
+
"class": "metadata"
|
|
882
|
+
},
|
|
883
|
+
"span.label": {
|
|
884
|
+
"description": "The label used to display the current title.",
|
|
885
|
+
"type": "span",
|
|
886
|
+
"class": "label",
|
|
887
|
+
"cached": true
|
|
888
|
+
},
|
|
889
|
+
"input.name": {
|
|
890
|
+
"description": "The input used to edit the current title.",
|
|
891
|
+
"type": "input",
|
|
892
|
+
"class": "name",
|
|
893
|
+
"cached": true
|
|
894
|
+
},
|
|
895
|
+
"div.tasks": {
|
|
896
|
+
"description": "The task action container.",
|
|
897
|
+
"contains": ["button.edit", "button.cancel", "button.save"],
|
|
898
|
+
"type": "div",
|
|
899
|
+
"class": "tasks"
|
|
900
|
+
},
|
|
901
|
+
"button.edit": {
|
|
902
|
+
"description": "The button used to enter edit mode.",
|
|
903
|
+
"type": "button",
|
|
904
|
+
"class": "edit",
|
|
905
|
+
"cached": true
|
|
906
|
+
},
|
|
907
|
+
"button.cancel": {
|
|
908
|
+
"description": "The button used to cancel editing.",
|
|
909
|
+
"type": "button",
|
|
910
|
+
"class": "cancel",
|
|
911
|
+
"cached": true
|
|
912
|
+
},
|
|
913
|
+
"button.save": {
|
|
914
|
+
"description": "The button used to save editing.",
|
|
915
|
+
"type": "button",
|
|
916
|
+
"class": "save",
|
|
917
|
+
"cached": true
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
```
|