@effect.healthcare/messenger 0.3.1 → 0.4.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.
@@ -1,921 +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
- ```
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
+ ```