@setsquare/review-sdk 0.0.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 (181) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +57 -0
  3. package/README.md +338 -0
  4. package/THIRD-PARTY-NOTICES.md +61 -0
  5. package/dist/autoscan.d.ts +25 -0
  6. package/dist/autoscan.d.ts.map +1 -0
  7. package/dist/autoscan.js +332 -0
  8. package/dist/autoscan.js.map +1 -0
  9. package/dist/axe.d.ts +96 -0
  10. package/dist/axe.d.ts.map +1 -0
  11. package/dist/axe.js +210 -0
  12. package/dist/axe.js.map +1 -0
  13. package/dist/bin/setsquare.d.ts +3 -0
  14. package/dist/bin/setsquare.d.ts.map +1 -0
  15. package/dist/bin/setsquare.js +21 -0
  16. package/dist/bin/setsquare.js.map +1 -0
  17. package/dist/brand.d.ts +84 -0
  18. package/dist/brand.d.ts.map +1 -0
  19. package/dist/brand.js +335 -0
  20. package/dist/brand.js.map +1 -0
  21. package/dist/checks/dynamic.d.ts +57 -0
  22. package/dist/checks/dynamic.d.ts.map +1 -0
  23. package/dist/checks/dynamic.js +184 -0
  24. package/dist/checks/dynamic.js.map +1 -0
  25. package/dist/checks/rule-pack.d.ts +34 -0
  26. package/dist/checks/rule-pack.d.ts.map +1 -0
  27. package/dist/checks/rule-pack.js +270 -0
  28. package/dist/checks/rule-pack.js.map +1 -0
  29. package/dist/checks/versions.d.ts +28 -0
  30. package/dist/checks/versions.d.ts.map +1 -0
  31. package/dist/checks/versions.js +45 -0
  32. package/dist/checks/versions.js.map +1 -0
  33. package/dist/ci.d.ts +37 -0
  34. package/dist/ci.d.ts.map +1 -0
  35. package/dist/ci.js +199 -0
  36. package/dist/ci.js.map +1 -0
  37. package/dist/contract.d.ts +334 -0
  38. package/dist/contract.d.ts.map +1 -0
  39. package/dist/contract.js +35 -0
  40. package/dist/contract.js.map +1 -0
  41. package/dist/dedupe.d.ts +79 -0
  42. package/dist/dedupe.d.ts.map +1 -0
  43. package/dist/dedupe.js +102 -0
  44. package/dist/dedupe.js.map +1 -0
  45. package/dist/document.d.ts +29 -0
  46. package/dist/document.d.ts.map +1 -0
  47. package/dist/document.js +94 -0
  48. package/dist/document.js.map +1 -0
  49. package/dist/envelope.d.ts +124 -0
  50. package/dist/envelope.d.ts.map +1 -0
  51. package/dist/envelope.js +337 -0
  52. package/dist/envelope.js.map +1 -0
  53. package/dist/escape.d.ts +14 -0
  54. package/dist/escape.d.ts.map +1 -0
  55. package/dist/escape.js +20 -0
  56. package/dist/escape.js.map +1 -0
  57. package/dist/findings.d.ts +47 -0
  58. package/dist/findings.d.ts.map +1 -0
  59. package/dist/findings.js +229 -0
  60. package/dist/findings.js.map +1 -0
  61. package/dist/fixture.d.ts +7 -0
  62. package/dist/fixture.d.ts.map +1 -0
  63. package/dist/fixture.js +237 -0
  64. package/dist/fixture.js.map +1 -0
  65. package/dist/group.d.ts +214 -0
  66. package/dist/group.d.ts.map +1 -0
  67. package/dist/group.js +709 -0
  68. package/dist/group.js.map +1 -0
  69. package/dist/html-report.d.ts +68 -0
  70. package/dist/html-report.d.ts.map +1 -0
  71. package/dist/html-report.js +649 -0
  72. package/dist/html-report.js.map +1 -0
  73. package/dist/human.d.ts +65 -0
  74. package/dist/human.d.ts.map +1 -0
  75. package/dist/human.js +111 -0
  76. package/dist/human.js.map +1 -0
  77. package/dist/index.d.ts +34 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +33 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/infer.d.ts +65 -0
  82. package/dist/infer.d.ts.map +1 -0
  83. package/dist/infer.js +67 -0
  84. package/dist/infer.js.map +1 -0
  85. package/dist/insights.d.ts +140 -0
  86. package/dist/insights.d.ts.map +1 -0
  87. package/dist/insights.js +483 -0
  88. package/dist/insights.js.map +1 -0
  89. package/dist/install.d.ts +8 -0
  90. package/dist/install.d.ts.map +1 -0
  91. package/dist/install.js +114 -0
  92. package/dist/install.js.map +1 -0
  93. package/dist/links.d.ts +45 -0
  94. package/dist/links.d.ts.map +1 -0
  95. package/dist/links.js +45 -0
  96. package/dist/links.js.map +1 -0
  97. package/dist/mask.d.ts +85 -0
  98. package/dist/mask.d.ts.map +1 -0
  99. package/dist/mask.js +98 -0
  100. package/dist/mask.js.map +1 -0
  101. package/dist/observe.d.ts +51 -0
  102. package/dist/observe.d.ts.map +1 -0
  103. package/dist/observe.js +233 -0
  104. package/dist/observe.js.map +1 -0
  105. package/dist/options.d.ts +249 -0
  106. package/dist/options.d.ts.map +1 -0
  107. package/dist/options.js +185 -0
  108. package/dist/options.js.map +1 -0
  109. package/dist/output.d.ts +113 -0
  110. package/dist/output.d.ts.map +1 -0
  111. package/dist/output.js +231 -0
  112. package/dist/output.js.map +1 -0
  113. package/dist/perf.d.ts +11 -0
  114. package/dist/perf.d.ts.map +1 -0
  115. package/dist/perf.js +150 -0
  116. package/dist/perf.js.map +1 -0
  117. package/dist/reading-level.d.ts +73 -0
  118. package/dist/reading-level.d.ts.map +1 -0
  119. package/dist/reading-level.js +189 -0
  120. package/dist/reading-level.js.map +1 -0
  121. package/dist/reporter.d.ts +94 -0
  122. package/dist/reporter.d.ts.map +1 -0
  123. package/dist/reporter.js +709 -0
  124. package/dist/reporter.js.map +1 -0
  125. package/dist/rules.d.ts +55 -0
  126. package/dist/rules.d.ts.map +1 -0
  127. package/dist/rules.js +77 -0
  128. package/dist/rules.js.map +1 -0
  129. package/dist/runtime.d.ts +32 -0
  130. package/dist/runtime.d.ts.map +1 -0
  131. package/dist/runtime.js +67 -0
  132. package/dist/runtime.js.map +1 -0
  133. package/dist/sanitise.d.ts +116 -0
  134. package/dist/sanitise.d.ts.map +1 -0
  135. package/dist/sanitise.js +247 -0
  136. package/dist/sanitise.js.map +1 -0
  137. package/dist/scan.d.ts +60 -0
  138. package/dist/scan.d.ts.map +1 -0
  139. package/dist/scan.js +246 -0
  140. package/dist/scan.js.map +1 -0
  141. package/dist/screens.d.ts +98 -0
  142. package/dist/screens.d.ts.map +1 -0
  143. package/dist/screens.js +301 -0
  144. package/dist/screens.js.map +1 -0
  145. package/dist/settle.d.ts +65 -0
  146. package/dist/settle.d.ts.map +1 -0
  147. package/dist/settle.js +162 -0
  148. package/dist/settle.js.map +1 -0
  149. package/dist/shots.d.ts +109 -0
  150. package/dist/shots.d.ts.map +1 -0
  151. package/dist/shots.js +113 -0
  152. package/dist/shots.js.map +1 -0
  153. package/dist/style.d.ts +47 -0
  154. package/dist/style.d.ts.map +1 -0
  155. package/dist/style.js +154 -0
  156. package/dist/style.js.map +1 -0
  157. package/dist/suite.d.ts +218 -0
  158. package/dist/suite.d.ts.map +1 -0
  159. package/dist/suite.js +219 -0
  160. package/dist/suite.js.map +1 -0
  161. package/dist/summary.d.ts +268 -0
  162. package/dist/summary.d.ts.map +1 -0
  163. package/dist/summary.js +1308 -0
  164. package/dist/summary.js.map +1 -0
  165. package/dist/unscannable.d.ts +75 -0
  166. package/dist/unscannable.d.ts.map +1 -0
  167. package/dist/unscannable.js +111 -0
  168. package/dist/unscannable.js.map +1 -0
  169. package/dist/versions.d.ts +13 -0
  170. package/dist/versions.d.ts.map +1 -0
  171. package/dist/versions.js +39 -0
  172. package/dist/versions.js.map +1 -0
  173. package/dist/wcag-map.d.ts +19 -0
  174. package/dist/wcag-map.d.ts.map +1 -0
  175. package/dist/wcag-map.js +52 -0
  176. package/dist/wcag-map.js.map +1 -0
  177. package/dist/with-setsquare.d.ts +26 -0
  178. package/dist/with-setsquare.d.ts.map +1 -0
  179. package/dist/with-setsquare.js +70 -0
  180. package/dist/with-setsquare.js.map +1 -0
  181. package/package.json +81 -0
package/dist/axe.js ADDED
@@ -0,0 +1,210 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { createRequire } from 'node:module';
3
+ import { readFileSync } from 'node:fs';
4
+ import { rulePackSource } from './checks/rule-pack.js';
5
+ import { tagsFor } from './rules.js';
6
+ /**
7
+ * Running axe inside the page.
8
+ *
9
+ * The one place axe-core is loaded and executed, so that the version we pin, the
10
+ * rules we resolve and the digest we publish are all derived from the same
11
+ * injection rather than from three places that could disagree.
12
+ */
13
+ const require = createRequire(import.meta.url);
14
+ /**
15
+ * Read once per process, not once per scan.
16
+ *
17
+ * axe-core is a large file and a suite scans hundreds of times. Reading it from
18
+ * disk on every scan would be a measurable share of the cost the SDK adds to a
19
+ * customer's run — and run time is the thing P1a is watching most closely.
20
+ */
21
+ let cachedSource;
22
+ function axeSource() {
23
+ cachedSource ??= readFileSync(require.resolve('axe-core'), 'utf8');
24
+ return cachedSource;
25
+ }
26
+ /** Where our axe instance lives, deliberately NOT `window.axe`. */
27
+ const SETSQUARE_AXE = '__setsquareAxe';
28
+ /**
29
+ * How long to wait for a customer's own axe run to finish before starting ours.
30
+ *
31
+ * Short, because this is politeness rather than correctness — our instance can
32
+ * run regardless. Long enough to cover a typical scan of a real page, which is
33
+ * a few hundred milliseconds.
34
+ */
35
+ const YIELD_BUDGET_MS = 3000;
36
+ /**
37
+ * Inject axe and run it against the page's current state.
38
+ *
39
+ * Injection happens per scan rather than per page, because a navigation wipes
40
+ * the document and any previously injected script with it. Re-injecting is
41
+ * cheap next to the run itself.
42
+ *
43
+ * Main frame only, and that is a stated limitation rather than an oversight:
44
+ * axe reports cross-origin iframes as `incomplete` unless it is injected into
45
+ * each frame, which needs same-origin access the SDK cannot assume. Reporting
46
+ * them as incomplete is the honest outcome — "we could not check this" — and it
47
+ * reaches the payload as such rather than as a pass.
48
+ *
49
+ * ## Why this does not use `window.axe`
50
+ *
51
+ * Because it did, and it broke a customer's suite.
52
+ *
53
+ * The reference implementation runs `@axe-core/playwright` in its own specs. Both
54
+ * it and the original version of this function loaded axe onto the shared
55
+ * `window.axe`, so an auto-scan firing while one of their scans was in flight hit
56
+ * axe-core's own guard — *"Axe is already running"* — and failed two of THEIR
57
+ * tests, which then passed on retry. The SDK had changed a verdict in the suite
58
+ * of the customer profile most likely to adopt it: the one already running axe.
59
+ *
60
+ * The fix is that our instance is never reachable as `window.axe`. Axe's UMD
61
+ * bundle assigns itself to the global, so the injection captures it, files it
62
+ * under our own key, and puts back whatever was there before — including
63
+ * `undefined`, so a page with no axe of its own is left with no axe of its own.
64
+ * A separate instance carries its own `_running` flag, so neither side can ever
65
+ * trip the other's guard.
66
+ *
67
+ * Politeness on top of correctness: if the customer's axe IS mid-run we wait for
68
+ * it, up to a short budget, rather than traversing the same DOM at the same time.
69
+ * Two concurrent runs are no longer an ERROR, but they are still two full tree
70
+ * walks competing for one main thread, and the customer's run is the one with a
71
+ * person waiting on it.
72
+ */
73
+ export async function runAxe(page, policy, timeoutMs) {
74
+ const tags = tagsFor(policy);
75
+ /*
76
+ * Setsquare's own AAA rule pack rides in with the injection. Configured on
77
+ * OUR instance only, and only when the target selects AAA tags — at AA the
78
+ * pack's rules would never run anyway (runOnly is tag-scoped), so not
79
+ * configuring them keeps the AA instance byte-identical to what it was.
80
+ */
81
+ const pack = policy.target === 'AAA' ? rulePackSource() : '';
82
+ await page.evaluate(([source, key, packSource]) => {
83
+ const global = window;
84
+ // Already injected on this document, and a navigation would have wiped it.
85
+ if (global[key])
86
+ return;
87
+ const theirs = global['axe'];
88
+ const hadTheirs = 'axe' in global;
89
+ // eslint-disable-next-line no-eval
90
+ (0, eval)(source);
91
+ global[key] = global['axe'];
92
+ /*
93
+ * Restore, including restoring ABSENCE. Leaving our axe on the global would
94
+ * be indistinguishable from the customer having installed it themselves —
95
+ * and any of their code that feature-detects `window.axe` would silently
96
+ * change behaviour because we were watching.
97
+ */
98
+ if (hadTheirs)
99
+ global['axe'] = theirs;
100
+ else
101
+ delete global['axe'];
102
+ if (packSource) {
103
+ // eslint-disable-next-line no-eval
104
+ (0, eval)(packSource)(global[key]);
105
+ }
106
+ }, [axeSource(), SETSQUARE_AXE, pack]);
107
+ const run = page.evaluate(async ([runTags, key, budget]) => {
108
+ const global = window;
109
+ const running = () => Boolean(global['axe']?._running);
110
+ // Yield to the customer's own scan. Bounded, and it proceeds either way:
111
+ // a stuck flag must never mean our evidence silently goes missing.
112
+ const deadline = Date.now() + budget;
113
+ while (running() && Date.now() < deadline) {
114
+ await new Promise((resolve) => setTimeout(resolve, 50));
115
+ }
116
+ /*
117
+ * Scoped to the conformance rule set rather than axe's defaults.
118
+ *
119
+ * Axe's default run mixes WCAG conformance rules with 30 advisory
120
+ * best-practice ones and leaves AAA off entirely, which is the wrong shape
121
+ * for an evidence product in both directions at once: it would report advice
122
+ * as failure while omitting a level a customer might be committed to.
123
+ * `runOnly` makes the assessed set an explicit, stated decision.
124
+ */
125
+ const axe = global[key];
126
+ return await axe.run({ runOnly: { type: 'tag', values: runTags } });
127
+ }, [tags, SETSQUARE_AXE, YIELD_BUDGET_MS]);
128
+ if (timeoutMs === undefined)
129
+ return await run;
130
+ /*
131
+ * A hard ceiling on how long a scan may hold a customer's test.
132
+ *
133
+ * The SDK's first promise is that it never fails a build, and an unbounded axe
134
+ * run breaks it in the most confusing way available: their test times out,
135
+ * blames itself, and nothing points at us. On the reference implementation's
136
+ * drag-and-drop builder surfaces — thousands of nodes, an axe pass of their own
137
+ * already running — that is not hypothetical.
138
+ *
139
+ * Abandoning rather than cancelling, deliberately. There is no way to stop an
140
+ * evaluation already running in the page, so the run finishes on its own and
141
+ * its result is discarded. That wastes work, which is the correct trade: the
142
+ * alternative is holding a test hostage to a scan nobody is waiting for.
143
+ *
144
+ * `undefined` rather than an empty result, because a scan that did not finish
145
+ * must never be recorded as a scan that found nothing.
146
+ */
147
+ let timer;
148
+ try {
149
+ return await Promise.race([
150
+ run,
151
+ new Promise((resolve) => {
152
+ timer = setTimeout(() => resolve(undefined), timeoutMs);
153
+ }),
154
+ ]);
155
+ }
156
+ finally {
157
+ if (timer)
158
+ clearTimeout(timer);
159
+ // Never leave the evaluation's rejection unhandled: the page may close while
160
+ // an abandoned run is still in flight, and an unhandled rejection in a
161
+ // customer's worker looks like their bug.
162
+ void run.catch(() => undefined);
163
+ }
164
+ }
165
+ /** The axe build that produced a result, read from the run rather than assumed. */
166
+ export function engineVersion(results) {
167
+ return results.testEngine?.version;
168
+ }
169
+ /**
170
+ * A digest of the rules that actually ran.
171
+ *
172
+ * Evidence claims are relative to a ruleset, so "this finding did not appear last
173
+ * week" is only meaningful if the same rules were evaluated both times. The
174
+ * digest is what makes that checkable rather than assumed.
175
+ *
176
+ * Computed from the union of every outcome bucket — violations, incompletes,
177
+ * passes and inapplicable — because that union IS the resolved rule set. axe
178
+ * ships 105 rules and evaluates ~89 by default; the remainder are experimental or
179
+ * off by default and never appear in any bucket. Digesting `getRules()` instead
180
+ * would name rules that never ran, and would not change when the default set did.
181
+ *
182
+ * Sorted before hashing so two runs of the same configuration agree regardless of
183
+ * the order axe happened to report in.
184
+ */
185
+ export function rulesetDigest(axeVersion, results, tags = [],
186
+ /**
187
+ * Setsquare's own checks, when any are enabled — versioned ids like
188
+ * `setsquare-reading-level@1`. Evidence claims are relative to everything that
189
+ * assessed the page, not only axe, so two runs that differ in enabled setsquare
190
+ * checks must not share a digest. Empty (the default) leaves the digest
191
+ * byte-identical to what pre-check SDKs produced.
192
+ */
193
+ setsquareChecks = []) {
194
+ const ruleIds = new Set();
195
+ for (const bucket of [
196
+ results.violations,
197
+ results.incomplete,
198
+ results.passes,
199
+ results.inapplicable,
200
+ ]) {
201
+ for (const rule of bucket)
202
+ ruleIds.add(rule.id);
203
+ }
204
+ const setsquareLine = setsquareChecks.length > 0 ? `\nsetsquare:${[...setsquareChecks].sort().join(',')}` : '';
205
+ const digest = createHash('sha256')
206
+ .update(`axe-core@${axeVersion}\ntags:${[...tags].sort().join(',')}\nrules:${[...ruleIds].sort().join(',')}${setsquareLine}`, 'utf8')
207
+ .digest('hex');
208
+ return `sha256:${digest}`;
209
+ }
210
+ //# sourceMappingURL=axe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"axe.js","sourceRoot":"","sources":["../src/axe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=setsquare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setsquare.d.ts","sourceRoot":"","sources":["../../src/bin/setsquare.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `setsquare` binary, owned here and implemented in `@setsquare/review-cli`.
4
+ *
5
+ * This shim exists because of a measured failure of the packaging split
6
+ * (decision 9). The plan was that `@setsquare/review-sdk` depends on
7
+ * `@setsquare/review-cli`, so one install line links `setsquare` — and that is true on npm
8
+ * and **false on pnpm**, which only links binaries for DIRECT dependencies.
9
+ * Tested, not assumed: a clean pnpm install of the SDK alone produced a
10
+ * `node_modules/.bin` with no `setsquare` in it, so the quickstart's second command
11
+ * would have failed for every pnpm user, including the reference implementation.
12
+ *
13
+ * Declaring the bin here fixes that without giving up what the split was for.
14
+ * The CLI still owns the implementation — the audit, its adapters, and whatever
15
+ * parser it grows — and none of it enters the test process's import graph,
16
+ * because this runs as its own process. The SDK owns the NAME; the CLI owns the
17
+ * CODE.
18
+ */
19
+ await import('@setsquare/review-cli/bin');
20
+ export {};
21
+ //# sourceMappingURL=setsquare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setsquare.js","sourceRoot":"","sources":["../../src/bin/setsquare.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The Setsquare mark and palette, for the artifacts the SDK writes.
3
+ *
4
+ * The report and the contact sheet are the only Setsquare surfaces most
5
+ * customers see before they see the platform, and until now they looked like
6
+ * neither it nor each other: an ad-hoc grey palette invented in
7
+ * `html-report.ts` and a second one in `screens.ts`. This module is the one
8
+ * place either of them gets a colour or a logo from.
9
+ *
10
+ * ## Why the values are copied rather than imported
11
+ *
12
+ * The palette below is a copy of `apps/portal/app/tokens.css`. It has to be: that
13
+ * app is proprietary, is not published, and is not a dependency of this package,
14
+ * so importing it would break the build for every consumer. It was also a licence
15
+ * problem while the published packages were ELv2 and could not take a
16
+ * proprietary dependency; D3 moved everything published to Apache-2.0, so that
17
+ * half of the argument is gone and the build half is the whole reason. A copy nobody
18
+ * checks is a copy that drifts, and drift here is invisible — the report would
19
+ * simply stop matching the product. `brand.test.ts` reads `tokens.css` and
20
+ * fails when the two disagree, which is the same guard `style.ts` uses for its
21
+ * CLI twin.
22
+ *
23
+ * ## Why only verified pairs are used
24
+ *
25
+ * Every value here carries the contrast ratio `tokens.css` measured for it.
26
+ * Nothing in this file invents a tint: a status colour is used either as text
27
+ * on a surface or as a background under its own `-ink`, because those are the
28
+ * pairs that were checked. The moment a new pair is needed it needs measuring,
29
+ * not estimating — this file is consumed by an accessibility report, and a
30
+ * report that fails its own bar is worth nothing.
31
+ *
32
+ * ## The marks are raster, deliberately for now
33
+ *
34
+ * PNG rather than SVG because these are the settled artwork and redrawing the
35
+ * geometry by hand is not this module's job — see
36
+ * `apps/portal/public/brand/README.md`, which records the provenance and the
37
+ * outstanding vector work. They are inlined as data URIs because the report is
38
+ * a single file a customer moves, mails and attaches; a file reference would
39
+ * break the first time it left its directory.
40
+ *
41
+ * Two marks, not one, for the reason the brand README gives: no single colour
42
+ * is 7:1 against both a light and a dark surface, so each theme has its own.
43
+ */
44
+ /**
45
+ * Fill `#005698` — 7.54:1 on `#ffffff`, 6.74:1 on `#f2f2f2`.
46
+ *
47
+ * DRIFTED, and knowingly. This raster fill was the light accent until the
48
+ * monochrome sheet re-derived it to `#005392`; the PNG still carries the old
49
+ * value, so the mark is no longer the accent and measures below 7:1 on the raised
50
+ * surface. It is used at 44px as a masthead logo — non-text, so 1.4.11's 3:1 is
51
+ * the applicable floor and it clears that comfortably — which is why this is
52
+ * recorded rather than treated as a release blocker.
53
+ *
54
+ * The fix is the vector redraw, not a recolour: `currentColor` removes the copy
55
+ * that can drift at all. Until then, note that `public/brand/README.md`'s claim
56
+ * that the fill is "checked, not assumed" is not true — nothing asserts it, which
57
+ * is precisely why this went unnoticed.
58
+ */
59
+ export declare const MARK_LIGHT: string;
60
+ /** Fill `#6bb5fe` — 8.04:1 on the dark surface, 7.02:1 on the dark raised surface. */
61
+ export declare const MARK_DARK: string;
62
+ /**
63
+ * The palette, as custom properties, light with a dark override.
64
+ *
65
+ * Role names, never colour names — `--ss-danger`, not `--ss-red` — so the day
66
+ * danger becomes amber there is one line to change. The `ss-` prefix is short
67
+ * for the CSS custom-property namespace only; everything a customer types is
68
+ * still `setsquare` in full.
69
+ */
70
+ export declare const BRAND_CSS = "\n :root {\n /* Surfaces. The base is now the LIGHTER of the two, inverted from the\n previous palette, so the raised step is the worse ground here. */\n --ss-surface: #ffffff;\n --ss-surface-raised: #f2f2f2;\n\n /* Text, measured against --ss-surface-raised (the worse case in this theme) */\n --ss-ink: #000000; /* 18.76:1 raised, 21.00:1 surface */\n --ss-ink-muted: #4f4f4f; /* 7.32:1 raised, 8.19:1 surface */\n\n --ss-border: #8c8c8c; /* 3.00:1 raised \u2014 non-text, meets 1.4.11 */\n --ss-grid-line: #d4d4d4; /* 1.32:1 raised \u2014 decorative only */\n\n --ss-accent: #005392; /* 7.07:1 raised, 7.92:1 surface */\n --ss-accent-ink: #ffffff; /* on accent \u2014 7.92:1 */\n\n /* Status roles. Used as text on a surface, or as a fill under their -ink. */\n --ss-danger: #a70019; /* 7.06:1 raised, 7.90:1 surface */\n --ss-danger-ink: #ffffff; /* on danger \u2014 7.90:1 */\n --ss-success: #005f29; /* 7.03:1 raised, 7.87:1 surface */\n --ss-success-ink: #ffffff;/* on success \u2014 7.87:1 */\n --ss-warning: #704a00; /* 7.03:1 raised, 7.87:1 surface */\n --ss-warning-ink: #ffffff;/* on warning \u2014 7.87:1 */\n\n --ss-code-bg: #f2f2f2;\n --ss-mark: url('data:image/png;base64,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');\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n --ss-surface: #0a0a0a;\n --ss-surface-raised: #1a1a1a;\n\n /* Measured against --ss-surface-raised, the worse case here. The chromatic\n values are unchanged; the grounds got darker, so every one of them GAINED\n headroom (7.0x -> 8.0x) rather than needing re-derivation. */\n --ss-ink: #ffffff; /* 17.40:1 raised, 19.80:1 surface */\n --ss-ink-muted: #a8a8a8; /* 7.32:1 raised, 8.33:1 surface */\n\n --ss-border: #676767; /* 3.08:1 raised */\n --ss-grid-line: #333333; /* 1.38:1 raised \u2014 decorative only */\n\n --ss-accent: #6bb5fe; /* 8.01:1 raised, 9.11:1 surface */\n --ss-accent-ink: #0b0e11; /* on accent \u2014 8.91:1 */\n\n --ss-danger: #fe928a; /* 8.03:1 raised, 9.14:1 surface */\n --ss-danger-ink: #0b0e11; /* on danger \u2014 8.93:1 */\n --ss-success: #62c47b; /* 8.04:1 raised, 9.15:1 surface */\n --ss-success-ink: #0b0e11;/* on success \u2014 8.94:1 */\n --ss-warning: #e9a228; /* 8.01:1 raised, 9.11:1 surface */\n --ss-warning-ink: #0b0e11;/* on warning \u2014 8.91:1 */\n\n --ss-code-bg: #1a1a1a;\n --ss-mark: url('data:image/png;base64,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');\n }\n }\n";
71
+ /**
72
+ * The masthead: mark plus wordmark, shared by both artifacts.
73
+ *
74
+ * The mark is a background image on an empty element with an accessible name
75
+ * rather than an `<img>`, because the two themes need two files and CSS is the
76
+ * only place that choice can be made without shipping both into the DOM.
77
+ * `role="img"` plus `aria-label` keeps it announced; a decorative logo beside a
78
+ * visible product name would be the other defensible call, but the report is
79
+ * often read alone and the name matters.
80
+ */
81
+ export declare const masthead: (title: string, subtitle: string) => string;
82
+ /** Masthead styling, kept beside the markup that needs it. */
83
+ export declare const MASTHEAD_CSS = "\n .masthead { display: flex; align-items: center; gap: 1rem; margin: 0 0 1.5rem; }\n .masthead h1 { margin: 0; }\n .masthead p { margin: .25rem 0 0; }\n .mark {\n flex: 0 0 auto; width: 44px; height: 44px;\n background: var(--ss-mark) center / contain no-repeat;\n }\n @media (max-width: 30rem) { .mark { width: 32px; height: 32px; } }\n";
84
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,UAAU,QA2EiD,CAAC;AAEzE,sFAAsF;AACtF,eAAO,MAAM,SAAS,QAyGsB,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,4mmBAyDrB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,EAAE,UAAU,MAAM,KAAG,MAO/C,CAAC;AAEb,8DAA8D;AAC9D,eAAO,MAAM,YAAY,mWASxB,CAAC"}