@echelon-foundry/design-system 0.2.0 → 0.3.0-main.216.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.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -0
  3. package/dist/all.css +5537 -841
  4. package/dist/assessment.css +55 -12
  5. package/dist/build-metrics.json +181 -5
  6. package/dist/components.css +2965 -36
  7. package/dist/foundations.css +90 -0
  8. package/dist/marketing/forma-all.css +8162 -0
  9. package/dist/marketing/forma-echelon-marketing.css +2109 -0
  10. package/dist/marketing/forma-marketing-theme-echelon.css +163 -0
  11. package/dist/marketing/forma-marketing.css +1947 -0
  12. package/dist/marketing/forma-marketing.manifest.json +52 -0
  13. package/dist/marketing/forma-marketing.sha256 +4 -0
  14. package/dist/marketing.css +1198 -0
  15. package/dist/patterns/active-filter-summary.html +10 -0
  16. package/dist/patterns/attention-path.html +29 -0
  17. package/dist/patterns/badge.html +9 -0
  18. package/dist/patterns/callout.html +23 -0
  19. package/dist/patterns/card-grid.html +25 -0
  20. package/dist/patterns/character-grid-3270.html +39 -0
  21. package/dist/patterns/character-grid-field.html +21 -0
  22. package/dist/patterns/character-grid-keys.html +20 -0
  23. package/dist/patterns/character-grid-reveal.html +16 -0
  24. package/dist/patterns/character-grid-selection.html +30 -0
  25. package/dist/patterns/character-grid-status.html +11 -0
  26. package/dist/patterns/character-grid-workflow.html +147 -0
  27. package/dist/patterns/character-grid.html +16 -0
  28. package/dist/patterns/cluster.html +5 -0
  29. package/dist/patterns/code-sample.html +9 -0
  30. package/dist/patterns/command-group.html +6 -0
  31. package/dist/patterns/comparison-grid.html +7 -0
  32. package/dist/patterns/comparison-pairs.html +13 -0
  33. package/dist/patterns/cta.html +12 -0
  34. package/dist/patterns/dense-ledger.html +7 -0
  35. package/dist/patterns/diagnostic-status.html +7 -0
  36. package/dist/patterns/diagram.html +49 -0
  37. package/dist/patterns/documentation-layout.html +18 -0
  38. package/dist/patterns/emphasis-budget.html +21 -0
  39. package/dist/patterns/entry-index.html +16 -0
  40. package/dist/patterns/evidence-relationship.html +1 -0
  41. package/dist/patterns/facts.html +7 -0
  42. package/dist/patterns/fault-banner.html +19 -0
  43. package/dist/patterns/fault-blocking.html +25 -0
  44. package/dist/patterns/fault-details.html +11 -0
  45. package/dist/patterns/fault-inline.html +17 -0
  46. package/dist/patterns/fault-notification.html +20 -0
  47. package/dist/patterns/fault-reference.html +5 -0
  48. package/dist/patterns/fault-summary.html +20 -0
  49. package/dist/patterns/fault.html +17 -0
  50. package/dist/patterns/feed-item.html +12 -0
  51. package/dist/patterns/focus-stage.html +4 -0
  52. package/dist/patterns/frame.html +4 -0
  53. package/dist/patterns/freshness.html +1 -0
  54. package/dist/patterns/handoff-summary.html +1 -0
  55. package/dist/patterns/hero.html +17 -0
  56. package/dist/patterns/hierarchy-tree.html +11 -0
  57. package/dist/patterns/identifier.html +9 -0
  58. package/dist/patterns/landmark-region.html +4 -0
  59. package/dist/patterns/lane-board.html +10 -0
  60. package/dist/patterns/marketing-shell.html +40 -0
  61. package/dist/patterns/measure.html +4 -0
  62. package/dist/patterns/metric-value-change.html +9 -0
  63. package/dist/patterns/mosaic.html +6 -0
  64. package/dist/patterns/navigation-shell.html +8 -0
  65. package/dist/patterns/overflow-command-disclosure.html +6 -0
  66. package/dist/patterns/overview-disclosure.html +12 -0
  67. package/dist/patterns/path-summary.html +8 -0
  68. package/dist/patterns/priority-stack.html +4 -0
  69. package/dist/patterns/progress-bar.html +15 -0
  70. package/dist/patterns/prose.html +9 -0
  71. package/dist/patterns/rail.html +4 -0
  72. package/dist/patterns/recovery-actions.html +5 -0
  73. package/dist/patterns/relationship-index.html +8 -0
  74. package/dist/patterns/reorder-states.html +31 -0
  75. package/dist/patterns/resizable-split-pane.html +11 -0
  76. package/dist/patterns/responsive-grid.html +5 -0
  77. package/dist/patterns/scope-trail.html +1 -0
  78. package/dist/patterns/scroll-progress.html +6 -0
  79. package/dist/patterns/section-heading.html +12 -0
  80. package/dist/patterns/security-blockers.html +3 -0
  81. package/dist/patterns/security-evidence-age.html +1 -0
  82. package/dist/patterns/security-evidence-chain.html +2 -0
  83. package/dist/patterns/security-exception.html +2 -0
  84. package/dist/patterns/security-invariant-result.html +1 -0
  85. package/dist/patterns/security-posture.html +22 -0
  86. package/dist/patterns/security-state-matrix.html +5 -0
  87. package/dist/patterns/security-trend.html +1 -0
  88. package/dist/patterns/security-unknown.html +2 -0
  89. package/dist/patterns/sidebar.html +4 -0
  90. package/dist/patterns/site-footer.html +12 -0
  91. package/dist/patterns/site-header.html +18 -0
  92. package/dist/patterns/spatial-canvas.html +13 -0
  93. package/dist/patterns/spinner.html +4 -0
  94. package/dist/patterns/split-pane.html +4 -0
  95. package/dist/patterns/stack.html +5 -0
  96. package/dist/patterns/state-survivability.html +19 -0
  97. package/dist/patterns/steps.html +8 -0
  98. package/dist/patterns/switcher.html +4 -0
  99. package/dist/patterns/tooltip.html +48 -0
  100. package/dist/patterns/verification-frame.html +22 -0
  101. package/dist/patterns/workspace-shell.html +6 -0
  102. package/dist/tokens.css +436 -1
  103. package/docs/BRANDING.md +6 -1
  104. package/docs/MARKETING-SITES.md +321 -0
  105. package/figma/component-contracts.json +1544 -0
  106. package/package.json +34 -8
  107. package/patterns/active-filter-summary.html +10 -0
  108. package/patterns/aegis-faults.manifest.md +81 -0
  109. package/patterns/attention-path.html +29 -0
  110. package/patterns/badge.html +9 -0
  111. package/patterns/callout.html +23 -0
  112. package/patterns/card-grid.html +25 -0
  113. package/patterns/character-grid-3270.html +39 -0
  114. package/patterns/character-grid-field.html +21 -0
  115. package/patterns/character-grid-keys.html +20 -0
  116. package/patterns/character-grid-reveal.html +16 -0
  117. package/patterns/character-grid-selection.html +30 -0
  118. package/patterns/character-grid-status.html +11 -0
  119. package/patterns/character-grid-workflow.html +147 -0
  120. package/patterns/character-grid.html +16 -0
  121. package/patterns/cluster.html +5 -0
  122. package/patterns/code-sample.html +9 -0
  123. package/patterns/command-group.html +6 -0
  124. package/patterns/comparison-grid.html +7 -0
  125. package/patterns/comparison-pairs.html +13 -0
  126. package/patterns/cta.html +12 -0
  127. package/patterns/dense-ledger.html +7 -0
  128. package/patterns/diagnostic-status.html +7 -0
  129. package/patterns/diagram.html +49 -0
  130. package/patterns/documentation-layout.html +18 -0
  131. package/patterns/emphasis-budget.html +21 -0
  132. package/patterns/entry-index.html +16 -0
  133. package/patterns/evidence-relationship.html +1 -0
  134. package/patterns/facts.html +7 -0
  135. package/patterns/fault-banner.html +19 -0
  136. package/patterns/fault-blocking.html +25 -0
  137. package/patterns/fault-details.html +11 -0
  138. package/patterns/fault-inline.html +17 -0
  139. package/patterns/fault-notification.html +20 -0
  140. package/patterns/fault-reference.html +5 -0
  141. package/patterns/fault-summary.html +20 -0
  142. package/patterns/fault.html +17 -0
  143. package/patterns/feed-item.html +12 -0
  144. package/patterns/focus-stage.html +4 -0
  145. package/patterns/frame.html +4 -0
  146. package/patterns/freshness.html +1 -0
  147. package/patterns/handoff-summary.html +1 -0
  148. package/patterns/hero.html +17 -0
  149. package/patterns/hierarchy-tree.html +11 -0
  150. package/patterns/identifier.html +9 -0
  151. package/patterns/landmark-region.html +4 -0
  152. package/patterns/lane-board.html +10 -0
  153. package/patterns/manifest.md +2 -0
  154. package/patterns/marketing-shell.html +40 -0
  155. package/patterns/measure.html +4 -0
  156. package/patterns/metric-value-change.html +9 -0
  157. package/patterns/mosaic.html +6 -0
  158. package/patterns/navigation-shell.html +8 -0
  159. package/patterns/overflow-command-disclosure.html +6 -0
  160. package/patterns/overview-disclosure.html +12 -0
  161. package/patterns/path-summary.html +8 -0
  162. package/patterns/priority-stack.html +4 -0
  163. package/patterns/progress-bar.html +15 -0
  164. package/patterns/prose.html +9 -0
  165. package/patterns/rail.html +4 -0
  166. package/patterns/recovery-actions.html +5 -0
  167. package/patterns/relationship-index.html +8 -0
  168. package/patterns/reorder-states.html +31 -0
  169. package/patterns/resizable-split-pane.html +11 -0
  170. package/patterns/responsive-grid.html +5 -0
  171. package/patterns/scope-trail.html +1 -0
  172. package/patterns/scroll-progress.html +6 -0
  173. package/patterns/section-heading.html +12 -0
  174. package/patterns/security-blockers.html +3 -0
  175. package/patterns/security-evidence-age.html +1 -0
  176. package/patterns/security-evidence-chain.html +2 -0
  177. package/patterns/security-exception.html +2 -0
  178. package/patterns/security-invariant-result.html +1 -0
  179. package/patterns/security-posture.html +22 -0
  180. package/patterns/security-posture.manifest.md +20 -0
  181. package/patterns/security-state-matrix.html +5 -0
  182. package/patterns/security-trend.html +1 -0
  183. package/patterns/security-unknown.html +2 -0
  184. package/patterns/sidebar.html +4 -0
  185. package/patterns/site-footer.html +12 -0
  186. package/patterns/site-header.html +18 -0
  187. package/patterns/spatial-canvas.html +13 -0
  188. package/patterns/spinner.html +4 -0
  189. package/patterns/split-pane.html +4 -0
  190. package/patterns/stack.html +5 -0
  191. package/patterns/state-survivability.html +19 -0
  192. package/patterns/steps.html +8 -0
  193. package/patterns/switcher.html +4 -0
  194. package/patterns/tooltip.html +48 -0
  195. package/patterns/verification-frame.html +22 -0
  196. package/patterns/workspace-shell.html +6 -0
  197. package/schemas/brand-manifest.schema.json +17 -3
  198. package/tokens/echelon.tokens.json +2801 -0
@@ -0,0 +1,321 @@
1
+ # Building an Echelon marketing site with Forma
2
+
3
+ This guide is for agents and engineers who create or migrate an Echelon
4
+ marketing site, for example Dokimos, Ordo, Praxis, Limen, Vigila, Tutoris, or
5
+ the main Echelon Foundry site.
6
+
7
+ - Rules: [`requirements/MARKETING-PRESENTATION.md`](../requirements/MARKETING-PRESENTATION.md)
8
+ - Distribution decision: [ADR-0003](decisions/ADR-0003-marketing-presentation-distribution.md)
9
+ - Working reference: [`examples/echelon-marketing-site`](../examples/echelon-marketing-site)
10
+ - Migrating an existing site: [`docs/marketing/MIGRATION-CONTRACT.md`](marketing/MIGRATION-CONTRACT.md)
11
+
12
+ ## 1. How do I make a new Echelon marketing site?
13
+
14
+ 1. Create `forma.lock` (see question 2) and install the pinned assets.
15
+ 2. Start every page from the MarketingShell (`patterns/marketing-shell.html`).
16
+ 3. Compose each page from Forma components:
17
+ - hero;
18
+ - section heading;
19
+ - card grid;
20
+ - facts;
21
+ - entry index;
22
+ - steps;
23
+ - badges;
24
+ - CTA;
25
+ - prose;
26
+ - callout;
27
+ - code sample;
28
+ - documentation layout.
29
+ 4. Write content, not CSS. Add `site.css` only for what question 5 allows.
30
+ 5. Add the checks from question 8 to CI and deploy with question 9.
31
+
32
+ ### Minimal new site
33
+
34
+ ```text
35
+ my-site/
36
+ forma.lock
37
+ index.html
38
+ site.css # optional; often empty
39
+ .github/workflows/deploy-pages.yml
40
+ ```
41
+
42
+ `forma.lock`:
43
+
44
+ ```text
45
+ forma 0.3.0
46
+ repository kemiller2002/forma
47
+ destination assets/forma
48
+ asset forma-echelon-marketing.css sha256:<filled in by install.sh --update>
49
+ ```
50
+
51
+ `index.html`:
52
+
53
+ ```html
54
+ <!doctype html>
55
+ <html lang="en" data-ef-theme="light">
56
+ <head>
57
+ <meta charset="utf-8">
58
+ <meta name="viewport" content="width=device-width, initial-scale=1">
59
+ <title>Product | Echelon Foundry</title>
60
+ <link rel="preconnect" href="https://fonts.googleapis.com">
61
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
62
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Manrope:wght@400;500;600;700&family=Newsreader:opsz,wght@6..72,500&display=swap">
63
+ <link rel="stylesheet" href="/assets/forma/forma-echelon-marketing.css">
64
+ </head>
65
+ <body class="ef-site" data-ef-layout="product">
66
+ <a class="ef-skip-link" href="#main-content">Skip to main content</a>
67
+ <header class="ef-site-header">
68
+ <div class="ef-site-header__inner">
69
+ <div class="ef-site-header__identity">
70
+ <a class="ef-site-header__brand" href="/"><span class="ef-site-header__mark" aria-hidden="true">PX</span><span class="ef-site-header__name">Echelon / Product</span></a>
71
+ </div>
72
+ <nav class="ef-site-nav" aria-label="Primary">
73
+ <ul class="ef-site-nav__list">
74
+ <li><a class="ef-site-nav__link" href="/" aria-current="page">Overview</a></li>
75
+ <li><a class="ef-site-nav__link" href="/docs/">Documentation</a></li>
76
+ <li><a class="ef-site-nav__link" data-ef-variant="action" href="/start/">Get started</a></li>
77
+ </ul>
78
+ </nav>
79
+ </div>
80
+ </header>
81
+ <main class="ef-site__main" id="main-content" tabindex="-1">
82
+ <section class="ef-hero" aria-labelledby="hero-title">
83
+ <div class="ef-hero__content">
84
+ <p class="ef-eyebrow">Product</p>
85
+ <h1 class="ef-hero__title" id="hero-title">One clear promise</h1>
86
+ <p class="ef-lead">What it does and for whom.</p>
87
+ <div class="ef-actions"><a class="ef-button" data-ef-variant="primary" href="/start/">Get started</a></div>
88
+ </div>
89
+ </section>
90
+ </main>
91
+ <footer class="ef-site-footer" data-ef-tone="inverse">
92
+ <div class="ef-site-footer__inner">
93
+ <p class="ef-site-footer__note"><span>&copy; 2026 Echelon Foundry</span></p>
94
+ </div>
95
+ </footer>
96
+ </body>
97
+ </html>
98
+ ```
99
+
100
+ ### Callouts in long-form content
101
+
102
+ Most prose needs no callout. Use `.ef-callout` (`patterns/callout.html`) only when a reader must notice one of four kinds of supporting material:
103
+
104
+ | `data-ef-callout` | Use for | Element |
105
+ |---|---|---|
106
+ | `note` | Context the reader can skip without losing the argument | `aside` |
107
+ | `caution` | A consequence to weigh before acting on the surrounding text | `aside` |
108
+ | `evidence` | Quoted or cited source material, with its source | `figure` holding a `blockquote` and a `figcaption` |
109
+ | `decision` | A settled outcome that later sections refer to | `aside` |
110
+
111
+ Every callout starts with a visible `.ef-callout__label` naming its kind, and the container is named by that label (`aria-labelledby`). The label is the primary cue. Each kind also has its own rule pattern, so kinds stay distinct in grayscale, forced colors and print. Do not use a callout for decoration, pull quotes or ordinary emphasis: use `<strong>`, a `blockquote` or a heading instead. Callouts have no runtime and no state. Static Markdown pipelines can emit the markup directly.
112
+
113
+ ## 2. How do I consume Forma?
114
+
115
+ Pin a release. Never copy Forma CSS into the site, and never link Forma `main`
116
+ or the Forma documentation site.
117
+
118
+ ```bash
119
+ # once, and on every deliberate upgrade: download, verify against the
120
+ # release checksums, and record each file's sha256 in forma.lock
121
+ curl -fsSLO https://raw.githubusercontent.com/kemiller2002/forma/v0.3.0/actions/install-presentation/install.sh
122
+ bash install.sh --update
123
+
124
+ # every build: install exactly what the lock pins, or fail
125
+ bash install.sh
126
+ ```
127
+
128
+ The installer needs only bash, curl, and `sha256sum` or `shasum`. It does not
129
+ need Node, npm, or .NET.
130
+
131
+ - `--source DIR` installs from a local directory, such as a Forma checkout's
132
+ `dist/marketing` or an offline cache.
133
+ - Commit `forma.lock`. Do not commit the installed `assets/forma/` directory:
134
+ add it to `.gitignore`, because the build recreates it.
135
+
136
+ Sites that already use npm may instead depend on
137
+ `@echelon-foundry/design-system` at an exact version and copy
138
+ `node_modules/@echelon-foundry/design-system/dist/marketing/forma-echelon-marketing.css`
139
+ at build time. The lock file is still the recommended record, because it
140
+ carries the checksum.
141
+
142
+ Available assets for each release:
143
+
144
+ | Asset | Use |
145
+ |---|---|
146
+ | `forma-echelon-marketing.css` | Default for Echelon sites: generic layer plus Echelon theme |
147
+ | `forma-marketing.css` + `forma-marketing-theme-echelon.css` | The same content split in two, for sites that load the theme separately |
148
+ | `forma-all.css` | Adds Forma application patterns (data grid, dense ledger, status lozenge, provenance trail); load it instead of `forma-marketing.css` when needed |
149
+ | `forma-marketing.manifest.json`, `forma-marketing.sha256` | Version, sizes, sources, checksums |
150
+
151
+ ## 3. How do I select the Echelon Marketing Theme?
152
+
153
+ Load `forma-echelon-marketing.css`; it already contains the theme. On `<html>`:
154
+
155
+ - Set `data-ef-theme="light"` to match the reference site, which is light
156
+ only.
157
+ - Set `data-ef-theme="dark"` for the validated dark map.
158
+ - Omit the attribute to follow the operating-system preference.
159
+
160
+ For product identity, retarget only the identity tokens, and only to other
161
+ Forma tokens:
162
+
163
+ ```css
164
+ /* site.css */
165
+ :root {
166
+ --ef-color-accent-primary: var(--ef-color-accent-secondary);
167
+ --ef-color-accent-hover: var(--ef-color-text-primary);
168
+ }
169
+ ```
170
+
171
+ A product that needs its own validated palette gets a brand manifest in Forma
172
+ (`brands/<product>.brand.json`), including the optional extended roles.
173
+ Apply it with `data-ef-brand="<product>"`. The BrandCompiler rejects any role
174
+ pair below its contrast floor.
175
+
176
+ ## 4. How do I use the Marketing Shell?
177
+
178
+ `.ef-site` on `<body>` is the shell. Its structure is fixed and appears in
179
+ this order:
180
+
181
+ 1. `.ef-skip-link`: first element in `<body>`, targeting `#main-content`.
182
+ 2. `header.ef-site-header` > `.ef-site-header__inner`, containing:
183
+ - `.ef-site-header__identity`: brand link with `__mark` (`aria-hidden`)
184
+ and `__name`, plus an optional `__tagline`;
185
+ - `nav.ef-site-nav[aria-label="Primary"]` > `ul.ef-site-nav__list` >
186
+ `a.ef-site-nav__link`. Mark exactly one link `aria-current="page"`. At
187
+ most one link uses `data-ef-variant="action"`.
188
+ 3. `main.ef-site__main#main-content[tabindex="-1"]`, which holds:
189
+ - `section.ef-hero` first;
190
+ - `section.ef-section` regions;
191
+ - `.ef-cta`.
192
+ 4. `footer.ef-site-footer[data-ef-tone="inverse"]` > `.ef-site-footer__inner`.
193
+
194
+ Page layouts compose inside `main`:
195
+
196
+ - **Marketing page**: hero → sections → CTA. Specimen: `index.html`.
197
+ - **Product page**: hero with status badges and facts → capability card grid
198
+ → install steps with a code sample → CTA. Specimen: `product.html`.
199
+ - **Documentation page**: `.ef-doc` with `__nav`, `__content` (with
200
+ `.ef-prose`), and optional `__aside`. Specimen: `docs.html`.
201
+
202
+ Set `data-ef-layout="marketing|product|documentation"` on `.ef-site` to
203
+ declare the page type.
204
+
205
+ Surfaces use `data-ef-tone="surface|elevated|inverse"`. Text, link, label,
206
+ and rule colors adapt to the tone automatically. Use one
207
+ `data-ef-variant="primary"` action per hero, section, or CTA.
208
+
209
+ ## 5. What belongs in local CSS?
210
+
211
+ Only the following:
212
+
213
+ - identity imagery, such as illustrations or a product mark image;
214
+ - presentation unique to the site's content, such as charts, diagrams,
215
+ interactive demos, and bespoke figures;
216
+ - identity retargets from the allowlist (question 3);
217
+ - experiments pending promotion, each preceded by
218
+ `/* forma-exception: GAP-MKT-xx or issue URL */`.
219
+
220
+ Keep local class names out of the `ef-` namespace. Use a site prefix such as
221
+ `dk-` or `site-`.
222
+
223
+ ## 6. What must go back into Forma?
224
+
225
+ Anything another Echelon site could use, and anything that would otherwise
226
+ redefine:
227
+
228
+ - global typography, containers, or the spacing scale;
229
+ - buttons, navigation, cards, or the footer;
230
+ - the responsive system, focus behavior, or section composition.
231
+
232
+ The policy checker rejects these locally. Record the need as a Forma
233
+ capability gap and add it to Forma. Once it is released, upgrade and delete
234
+ the local rule.
235
+
236
+ ## 7. How do I upgrade Forma?
237
+
238
+ 1. Read the release notes for every version between yours and the target.
239
+ 2. Edit `forma.lock` to the new version, then run `bash install.sh --update`.
240
+ The installer verifies the downloads against the release checksums and
241
+ records the new hashes.
242
+ 3. Run the site checks (question 8). Review changed patterns you use:
243
+ `patterns/*.html` at the new tag.
244
+ 4. Commit `forma.lock`, plus any markup changes the release notes require, as
245
+ one reviewable change.
246
+
247
+ Nothing upgrades automatically. Forma `main` never reaches a site.
248
+
249
+ ## 8. How do I test the site before deployment?
250
+
251
+ Run these checks in CI:
252
+
253
+ - **Install:** `bash install.sh`. It fails on any drift.
254
+ - **Local-CSS policy:** use `kemiller2002/forma/actions/check-site-css@v<version>`
255
+ with `files: site.css`, or run
256
+ `dotnet run --project <forma>/tools/SiteCssPolicy/SiteCssPolicy.fsproj -- --forma assets/forma/forma-echelon-marketing.css site.css`.
257
+ - **Structure:** at least the checks in Forma's
258
+ `tests/marketing-structure.test.mjs`:
259
+ - skip link first;
260
+ - one `h1`;
261
+ - no skipped heading levels;
262
+ - one `aria-current` link;
263
+ - at most one primary action per region;
264
+ - only Forma or declared local classes.
265
+ - **Browser:** at 1280, 768, 390, and 320 px, check for:
266
+ - no horizontal overflow;
267
+ - axe WCAG 2.2 A/AA clean;
268
+ - visible keyboard focus;
269
+ - 44px targets.
270
+
271
+ Forma's `tests/browser/marketing.spec.mjs` is the template.
272
+
273
+ ## 9. How does GitHub Pages deployment obtain the correct Forma assets?
274
+
275
+ The deploy workflow installs exactly what the lock pins, before the site
276
+ build:
277
+
278
+ ```yaml
279
+ jobs:
280
+ build:
281
+ runs-on: ubuntu-latest
282
+ steps:
283
+ - uses: actions/checkout@v4
284
+ - uses: kemiller2002/forma/actions/install-presentation@v0.3.0
285
+ with:
286
+ lock: forma.lock
287
+ - uses: kemiller2002/forma/actions/check-site-css@v0.3.0
288
+ with:
289
+ files: site.css
290
+ - run: ./build.sh # the site's own build; copies assets/forma into the output
291
+ - uses: actions/configure-pages@v5
292
+ - uses: actions/upload-pages-artifact@v3
293
+ with:
294
+ path: dist
295
+ ```
296
+
297
+ Keep the `@v<version>` of the Forma actions equal to the version in
298
+ `forma.lock`. The download URL is the immutable release asset
299
+ `https://github.com/kemiller2002/forma/releases/download/v<version>/<asset>`.
300
+
301
+ ## 10. How do I propose a new reusable marketing component?
302
+
303
+ 1. Check first whether an existing component, tone, variant, or layout can
304
+ already express it:
305
+ - `patterns/`
306
+ - `requirements/COMPONENT-CATALOG.md`
307
+ - `catalog/layouts.json`
308
+ 2. Open a Forma issue that describes the relationship it must preserve, not
309
+ just its appearance:
310
+ - which sites need it;
311
+ - narrow-screen behavior;
312
+ - accessibility semantics;
313
+ - Visual Engineering basis.
314
+ 3. Add it in Forma:
315
+ - pattern HTML;
316
+ - CSS in `src/marketing/` using role tokens only;
317
+ - metadata in `tools/component-meta.mjs`;
318
+ - tests (structure, browser, axe, 320 px);
319
+ - a gap entry closed in `requirements/MARKETING-PRESENTATION.md`.
320
+ 4. Release it. The sites then upgrade (question 7) and remove any local
321
+ stand-in.