@bongos/core 1.24.80 → 1.24.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +125 -110
- package/docs/adr/0402-helm-is-the-halls-look-and-the-hall-is-dark-only.md +142 -0
- package/docs/adr/README.md +1 -0
- package/docs/copy-inventory.md +26 -31
- package/docs/copy-registry.json +29 -86
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +1 -3
- package/docs/page-readings.json +94 -94
- package/modules/hall-ui/public/ai-studio.html +5 -3
- package/modules/hall-ui/public/ai-studio.states.json +1 -0
- package/modules/hall-ui/public/atlas.html +3 -3
- package/modules/hall-ui/public/atlas.states.json +1 -0
- package/modules/hall-ui/public/blockers.html +3 -3
- package/modules/hall-ui/public/board-room.html +3 -3
- package/modules/hall-ui/public/board-room.states.json +1 -0
- package/modules/hall-ui/public/city.css +1 -3
- package/modules/hall-ui/public/city.html +5 -8
- package/modules/hall-ui/public/city.js +10 -55
- package/modules/hall-ui/public/city.states.json +7 -32
- package/modules/hall-ui/public/collab.html +3 -3
- package/modules/hall-ui/public/collab.states.json +1 -0
- package/modules/hall-ui/public/commands.html +3 -3
- package/modules/hall-ui/public/commands.states.json +1 -0
- package/modules/hall-ui/public/controls.css +63 -11
- package/modules/hall-ui/public/copy-desk.html +3 -3
- package/modules/hall-ui/public/copy-desk.states.json +1 -0
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/deploy.states.json +1 -0
- package/modules/hall-ui/public/goal-map.html +3 -3
- package/modules/hall-ui/public/goals.html +3 -3
- package/modules/hall-ui/public/goals.states.json +1 -0
- package/modules/hall-ui/public/government.html +3 -3
- package/modules/hall-ui/public/government.states.json +1 -0
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/idea.states.json +1 -0
- package/modules/hall-ui/public/ideas.html +3 -3
- package/modules/hall-ui/public/ideas.states.json +1 -0
- package/modules/hall-ui/public/index-founding.states.json +1 -0
- package/modules/hall-ui/public/index-genesis-decide.states.json +1 -0
- package/modules/hall-ui/public/index-genesis-demo.states.json +1 -0
- package/modules/hall-ui/public/index-genesis.states.json +1 -0
- package/modules/hall-ui/public/index.html +4 -4
- package/modules/hall-ui/public/index.states.json +1 -0
- package/modules/hall-ui/public/market-watch-item.html +3 -3
- package/modules/hall-ui/public/market-watch-item.states.json +1 -0
- package/modules/hall-ui/public/market-watch-rules.html +3 -3
- package/modules/hall-ui/public/market-watch-rules.states.json +1 -0
- package/modules/hall-ui/public/market-watch.html +3 -3
- package/modules/hall-ui/public/market-watch.states.json +1 -0
- package/modules/hall-ui/public/modules.html +3 -3
- package/modules/hall-ui/public/modules.states.json +1 -0
- package/modules/hall-ui/public/money.html +3 -3
- package/modules/hall-ui/public/money.states.json +1 -0
- package/modules/hall-ui/public/my-agents.html +3 -3
- package/modules/hall-ui/public/my-agents.states.json +1 -0
- package/modules/hall-ui/public/primer.html +3 -3
- package/modules/hall-ui/public/primer.states.json +1 -0
- package/modules/hall-ui/public/profile.html +3 -3
- package/modules/hall-ui/public/profile.states.json +1 -0
- package/modules/hall-ui/public/project-settings.html +3 -3
- package/modules/hall-ui/public/roster.html +3 -3
- package/modules/hall-ui/public/roster.states.json +1 -0
- package/modules/hall-ui/public/settings.html +3 -3
- package/modules/hall-ui/public/settings.states.json +1 -0
- package/modules/hall-ui/public/shell-game.css +87 -42
- package/modules/hall-ui/public/shell-game.js +41 -8
- package/modules/hall-ui/public/shell.js +8 -40
- package/modules/hall-ui/public/street-card.states.json +1 -0
- package/modules/hall-ui/public/street.css +1 -5
- package/modules/hall-ui/public/street.html +5 -9
- package/modules/hall-ui/public/street.js +8 -59
- package/modules/hall-ui/public/street.states.json +7 -32
- package/modules/hall-ui/public/studio.html +3 -3
- package/modules/hall-ui/public/studio.states.json +1 -0
- package/modules/hall-ui/public/style.css +124 -65
- package/modules/hall-ui/public/task.html +3 -3
- package/modules/hall-ui/public/task.states.json +1 -0
- package/modules/hall-ui/public/thinking.html +3 -3
- package/modules/hall-ui/public/thinking.states.json +1 -0
- package/modules/hall-ui/public/tweak-editor.html +3 -3
- package/modules/hall-ui/public/tweak-editor.states.json +1 -0
- package/modules/hall-ui/public/watch.html +3 -3
- package/modules/hall-ui/public/watch.states.json +1 -0
- package/modules/hall-ui/public/work-founding.states.json +1 -0
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/hall-ui/public/work.states.json +1 -0
- package/modules/hall-ui/records/start-screen.md +6 -2
- package/modules/hall-ui/specimens/components.states.json +2 -1
- package/modules/ui-design/public/brand.html +2 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/hall-preview/README.md +2 -3
- package/src/bongos/serve-internal.js +7 -2
- package/src/hall-helm.js +79 -0
- package/src/module-api.js +1 -1
- package/tests/design_reference.mjs +1 -1
- package/tests/hall_builders_page_pass.mjs +2 -1
- package/tests/hall_city.mjs +15 -29
- package/tests/hall_controls.mjs +52 -4
- package/tests/hall_helm.mjs +187 -0
- package/tests/hall_record_world.mjs +4 -2
- package/tests/hall_start_screen.mjs +65 -2
- package/tests/hall_street.mjs +13 -26
- package/tests/hall_topbar_320.mjs +4 -2
- package/tests/nav_permission_atoms.mjs +1 -1
|
@@ -45,13 +45,13 @@
|
|
|
45
45
|
<h2 class="scroll__h" id="ref-colour-h">Colour</h2>
|
|
46
46
|
<p class="scroll__lede">The thirteen colours of the branding pack. An instance replaces them in its own pack; in dark mode only the neutrals flip, and the accent, sea and terracotta stay the pack’s.</p>
|
|
47
47
|
|
|
48
|
-
<ul class="ref-list" role="list"><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--bg)" aria-hidden="true"></span><code class="ref-name">--bg</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#edecee</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#
|
|
48
|
+
<ul class="ref-list" role="list"><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--bg)" aria-hidden="true"></span><code class="ref-name">--bg</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#edecee</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#050d16</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--bg-card)" aria-hidden="true"></span><code class="ref-name">--bg-card</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#fbfafc</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#0a1622</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--bg-deep)" aria-hidden="true"></span><code class="ref-name">--bg-deep</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#e2e1e4</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#050d16</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--bg-frame)" aria-hidden="true"></span><code class="ref-name">--bg-frame</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#f6f5f7</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#07111b</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--ink)" aria-hidden="true"></span><code class="ref-name">--ink</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#17181a</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#ffffff</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--ink-soft)" aria-hidden="true"></span><code class="ref-name">--ink-soft</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#55565a</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#c8f1f7</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--ink-faint)" aria-hidden="true"></span><code class="ref-name">--ink-faint</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#65656a</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#a9cdd4</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--rule)" aria-hidden="true"></span><code class="ref-name">--rule</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#c6c6c8</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(200, 241, 247, .4)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--rule-soft)" aria-hidden="true"></span><code class="ref-name">--rule-soft</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#dcdbdd</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(200, 241, 247, .2)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--accent)" aria-hidden="true"></span><code class="ref-name">--accent</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#fb9569</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>same as light</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--accent-deep)" aria-hidden="true"></span><code class="ref-name">--accent-deep</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#a3481a</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>same as light</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--sea)" aria-hidden="true"></span><code class="ref-name">--sea</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#5b6fd0</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>same as light</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--terracotta)" aria-hidden="true"></span><code class="ref-name">--terracotta</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#c9512c</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>same as light</code></span></li></ul>
|
|
49
49
|
<h3 class="ref-sub">The status vocabulary</h3>
|
|
50
50
|
<p class="ref-note">Fixed, not taken from the pack, and the one exception to the fifteen tokens: a status is always a word as well as a colour.</p>
|
|
51
51
|
<ul class="ref-list" role="list"><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--ok)" aria-hidden="true"></span><code class="ref-name">--ok</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#187444</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#4ade80</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--ok-weak)" aria-hidden="true"></span><code class="ref-name">--ok-weak</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, #187444 12%, var(--bg-card))</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, #4ade80 14%, var(--bg-card))</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--warn)" aria-hidden="true"></span><code class="ref-name">--warn</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#895c00</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#fbbf24</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--warn-weak)" aria-hidden="true"></span><code class="ref-name">--warn-weak</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, #895c00 14%, var(--bg-card))</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, #fbbf24 14%, var(--bg-card))</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--danger)" aria-hidden="true"></span><code class="ref-name">--danger</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#b42318</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#f87171</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--danger-weak)" aria-hidden="true"></span><code class="ref-name">--danger-weak</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, #b42318 10%, var(--bg-card))</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, #f87171 14%, var(--bg-card))</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--info)" aria-hidden="true"></span><code class="ref-name">--info</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--sea) 86%, #000000)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--sea) 80%, #ffffff)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--info-weak)" aria-hidden="true"></span><code class="ref-name">--info-weak</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--info) 9%, var(--bg-card))</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--info) 14%, var(--bg-card))</code></span></li></ul>
|
|
52
52
|
<h3 class="ref-sub">Derived colours</h3>
|
|
53
53
|
<p class="ref-note">Mixed from the thirteen, never typed as a new colour. Black and white with alpha appear only as a scrim, a shadow or glass.</p>
|
|
54
|
-
<ul class="ref-list" role="list"><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--glass)" aria-hidden="true"></span><code class="ref-name">--glass</code><span class="ref-val"><span class="ref-val__k">light</span> <code>rgba(23, 24, 26, .05)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(
|
|
54
|
+
<ul class="ref-list" role="list"><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--glass)" aria-hidden="true"></span><code class="ref-name">--glass</code><span class="ref-val"><span class="ref-val__k">light</span> <code>rgba(23, 24, 26, .05)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(200, 241, 247, .1)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--glass-strong)" aria-hidden="true"></span><code class="ref-name">--glass-strong</code><span class="ref-val"><span class="ref-val__k">light</span> <code>rgba(23, 24, 26, .08)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(200, 241, 247, .16)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--glass-edge)" aria-hidden="true"></span><code class="ref-name">--glass-edge</code><span class="ref-val"><span class="ref-val__k">light</span> <code>rgba(23, 24, 26, .12)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(200, 241, 247, .2)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--void)" aria-hidden="true"></span><code class="ref-name">--void</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#050508</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#050d16</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--void-ink)" aria-hidden="true"></span><code class="ref-name">--void-ink</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#ffffff</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>var(--ink)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--void-edge)" aria-hidden="true"></span><code class="ref-name">--void-edge</code><span class="ref-val"><span class="ref-val__k">light</span> <code>rgba(23, 24, 26, .12)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(200, 241, 247, .4)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--skeleton)" aria-hidden="true"></span><code class="ref-name">--skeleton</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--ink) 10%, var(--bg-card))</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--ink) 10%, var(--bg-card))</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--accent-ink)" aria-hidden="true"></span><code class="ref-name">--accent-ink</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--accent) 45%, #240e00)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>var(--accent)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--accent-lift)" aria-hidden="true"></span><code class="ref-name">--accent-lift</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--accent) 58%, #ffffff)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--accent) 58%, #ffffff)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--accent-tint)" aria-hidden="true"></span><code class="ref-name">--accent-tint</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--accent) 30%, #fffdf9)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--accent) 22%, #050d16)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--accent-on)" aria-hidden="true"></span><code class="ref-name">--accent-on</code><span class="ref-val"><span class="ref-val__k">light</span> <code>#2a1305</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>#2a1305</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--focus)" aria-hidden="true"></span><code class="ref-name">--focus</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--accent-deep) 82%, #3a1400)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>var(--accent)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--scrim)" aria-hidden="true"></span><code class="ref-name">--scrim</code><span class="ref-val"><span class="ref-val__k">light</span> <code>rgba(0, 0, 0, .55)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>rgba(0, 0, 0, .55)</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--surface-2)" aria-hidden="true"></span><code class="ref-name">--surface-2</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--ink) 5%, var(--bg-card))</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--ink) 6%, var(--bg-card))</code></span></li><li class="ref-row ref-row--colour"><span class="ref-swatch" style="background: var(--night-glass)" aria-hidden="true"></span><code class="ref-name">--night-glass</code><span class="ref-val"><span class="ref-val__k">light</span> <code>color-mix(in srgb, var(--bg) 72%, transparent)</code></span><span class="ref-val"><span class="ref-val__k">dark</span> <code>color-mix(in srgb, var(--bg) 72%, transparent)</code></span></li></ul>
|
|
55
55
|
</section>
|
|
56
56
|
<section class="scroll ref-sec" id="ref-type" aria-labelledby="ref-type-h">
|
|
57
57
|
<h2 class="scroll__h" id="ref-type-h">Type</h2>
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.81",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.81",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.81",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11583,5 +11583,11 @@
|
|
|
11583
11583
|
"id": "1005141",
|
|
11584
11584
|
"text": "The builders' crafts now carry their game names: Engineers are Artisans, Ideators join the Artists, and Patron is the third character; old links and old records keep working."
|
|
11585
11585
|
}
|
|
11586
|
+
],
|
|
11587
|
+
"1.24.81": [
|
|
11588
|
+
{
|
|
11589
|
+
"id": "1005192",
|
|
11590
|
+
"text": "The hall now wears the new Helm look: deep night panels, ice-blue text, wide capital titles and cut-corner buttons, all from one shared design system. The start and load screens are rebuilt to match, and the hall is dark only."
|
|
11591
|
+
}
|
|
11586
11592
|
]
|
|
11587
11593
|
}
|
|
@@ -135,15 +135,14 @@ overlay). A concept no table holds is not drawn: never add sample data for one.
|
|
|
135
135
|
|
|
136
136
|
## The evidence matrix (per page-redesign ship)
|
|
137
137
|
|
|
138
|
-
Screenshot every changed page at **{
|
|
138
|
+
Screenshot every changed page at **{1280w, 390w}** (the hall is dark only since task 1005192, ADR 0402), plus one
|
|
139
139
|
instance-pack run (`--brand config/branding.json`) proving the theming contract
|
|
140
140
|
survives. Run the harness with **`--fixture-me`** for these — the pinned snapshot
|
|
141
141
|
is what makes two runs comparable. With the Playwright MCP (`npx playwright install chrome` once):
|
|
142
142
|
|
|
143
143
|
1. `browser_navigate` → the page. `browser_console_messages` must show **0 errors**.
|
|
144
144
|
2. 1280w: `browser_resize 1280×900` → `browser_take_screenshot` (fullPage for tall pages).
|
|
145
|
-
3.
|
|
146
|
-
4. 390w: `browser_resize 390×844` → light + dark screenshots.
|
|
145
|
+
3. 390w: `browser_resize 390×844` → screenshot.
|
|
147
146
|
|
|
148
147
|
## Seam assertions (copy-paste `browser_evaluate` snippets)
|
|
149
148
|
|
|
@@ -45,6 +45,7 @@ const { buildInfo } = require('../build-info');
|
|
|
45
45
|
const { buildBongosDownloads, installScriptReader } = require('../bongos-downloads');
|
|
46
46
|
const { clientBranding, branding, applyBrandConditionals } = require('../branding');
|
|
47
47
|
const { themeMarkup } = require('../theme-markup');
|
|
48
|
+
const { applyHelm } = require('../hall-helm');
|
|
48
49
|
const { linkPreviewTags } = require('../link-preview');
|
|
49
50
|
const { prelaunchGate } = require('./prelaunch-gate');
|
|
50
51
|
const { platformVisibilityGate } = require('./platform-visibility-gate');
|
|
@@ -126,8 +127,12 @@ function injectBrandingGlobal(html, brand = clientBranding(), mods = clientModul
|
|
|
126
127
|
// §4), injected the same way as __BRANDING__ so hall/status scripts can hide
|
|
127
128
|
// UI sections for off modules synchronously (no fetch, no race). R60 wires the
|
|
128
129
|
// actual section-hiding; this exposes the data. The link-preview card (task 1005091) rides along.
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
// A HALL page (one that links the hall's sheet) is pinned dark and, unless its pack
|
|
131
|
+
// chose faces of its own, takes the Helm faces (task 1005192, ADR 0402, src/hall-helm.js).
|
|
132
|
+
// Every other page comes back from applyHelm untouched.
|
|
133
|
+
const helm = applyHelm(applyBrandTokens(html, brand), brand.theme);
|
|
134
|
+
const stamped = helm.html;
|
|
135
|
+
const tag = `${clientBundleScriptTag()}<script>window.__BRANDING__=${jsonForScript(brand)};window.__MODULES__=${jsonForScript(mods)};</script>${linkPreviewTags(stamped, brand, safePublicOrigin())}${themeMarkup(brand.theme)}${helm.tail}`;
|
|
131
136
|
return stamped.includes('</head>') ? stamped.replace('</head>', `${tag}</head>`) : tag + stamped;
|
|
132
137
|
}
|
|
133
138
|
|
package/src/hall-helm.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/hall-helm.js — Helm, the look the builders' hall wears (task 1005192, ADR 0402).
|
|
2
|
+
//
|
|
3
|
+
// The hall is the game: everything after sign-in. It is dark only, and it reads in
|
|
4
|
+
// the two Helm faces unless its pack chose faces of its own. Both are decided HERE,
|
|
5
|
+
// at serve time, for one reason each:
|
|
6
|
+
//
|
|
7
|
+
// * DARK ONLY. A fixed html[data-theme="dark"] in the served markup selects the
|
|
8
|
+
// hall sheet's dark tier before first paint, with nothing to flash and nothing
|
|
9
|
+
// for the OS preference to reach (the ADR 0204 move, on the hall's own
|
|
10
|
+
// attribute). shell.js sets the same value again for a page read without it.
|
|
11
|
+
// * THE FACES. The pack splices --font-display / --font-body as a plain :root
|
|
12
|
+
// after every sheet, for every surface, so the landing and the hall would
|
|
13
|
+
// always read the same face. The hall sheet carries the Helm stacks as its own
|
|
14
|
+
// tokens (--helm-display / --helm-body) and hands them to the two font tokens
|
|
15
|
+
// under html[data-faces="helm"], which outranks the pack's :root. This file
|
|
16
|
+
// stamps that attribute, and links the two families, only for a hall whose
|
|
17
|
+
// pack still names the platform's default faces. A pack that names its own
|
|
18
|
+
// (config/branding.json, or a look from the style library with faces of its
|
|
19
|
+
// own, ADR 0358) is never stamped and keeps them.
|
|
20
|
+
//
|
|
21
|
+
// A hall page is recognised by what makes it one: it links the hall's sheet. So one
|
|
22
|
+
// seam (serve-internal.js injectBrandingGlobal) covers the server, the preview
|
|
23
|
+
// harness and the design kit's stub, and the landing, the hub and the status page,
|
|
24
|
+
// which never link that sheet, are never touched. PURE given `defaults`.
|
|
25
|
+
'use strict';
|
|
26
|
+
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { escapeHtml: esc } = require('./html-escape');
|
|
30
|
+
|
|
31
|
+
// Barlow Condensed (the name voice) and Barlow (everything said to you), swapped in.
|
|
32
|
+
const HELM_FONTS_HREF = 'https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;500;600&family=Barlow:wght@400;500&display=swap';
|
|
33
|
+
const HALL_SHEET_RE = /<link\b[^>]*\bhref="\/builders\/style\.css[^"]*"/i;
|
|
34
|
+
const HTML_TAG_RE = /<html\b([^>]*)>/i;
|
|
35
|
+
|
|
36
|
+
function isHallPage(html) {
|
|
37
|
+
return HALL_SHEET_RE.test(String(html || ''));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The platform's default faces: the neutral pack's two stacks, read once. A hall
|
|
41
|
+
// whose resolved pack still carries exactly these has not chosen faces.
|
|
42
|
+
let _defaults = null;
|
|
43
|
+
function defaultFaces() {
|
|
44
|
+
if (_defaults) return _defaults;
|
|
45
|
+
try {
|
|
46
|
+
const { resolveCoreRoot } = require('./instance-config'); // eslint-disable-line global-require
|
|
47
|
+
const pack = JSON.parse(fs.readFileSync(path.join(resolveCoreRoot(), 'config', 'branding.neutral.json'), 'utf8'));
|
|
48
|
+
const fonts = (pack.theme && pack.theme.ui && pack.theme.ui.fonts) || {};
|
|
49
|
+
_defaults = { display: String(fonts.display || ''), body: String(fonts.body || '') };
|
|
50
|
+
} catch { _defaults = { display: '', body: '' }; }
|
|
51
|
+
return _defaults;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Whether this hall takes the Helm faces: its pack names the default ones (or none).
|
|
55
|
+
function wearsHelmFaces(theme, defaults = defaultFaces()) {
|
|
56
|
+
const fonts = (theme && theme.fonts) || {};
|
|
57
|
+
const same = (mine, theirs) => !mine || mine === theirs;
|
|
58
|
+
return same(fonts.display, defaults.display) && same(fonts.body, defaults.body);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Set one attribute on the document's <html> tag, replacing a value already there.
|
|
62
|
+
function stampHtml(html, name, value) {
|
|
63
|
+
return html.replace(HTML_TAG_RE, (tag, attrs) => {
|
|
64
|
+
const rest = attrs.replace(new RegExp(`\\s${name}(?:="[^"]*")?`, 'i'), '');
|
|
65
|
+
return `<html${rest} ${name}="${esc(value)}">`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The whole transform for one served page: a page that does not link the hall's
|
|
70
|
+
// sheet comes back untouched. `tail` is markup for the end of <head>.
|
|
71
|
+
function applyHelm(html, theme, defaults = defaultFaces()) {
|
|
72
|
+
if (!isHallPage(html)) return { html, tail: '' };
|
|
73
|
+
let out = stampHtml(html, 'data-theme', 'dark');
|
|
74
|
+
if (!wearsHelmFaces(theme, defaults)) return { html: out, tail: '' };
|
|
75
|
+
out = stampHtml(out, 'data-faces', 'helm');
|
|
76
|
+
return { html: out, tail: `<link rel="stylesheet" href="${esc(HELM_FONTS_HREF)}">` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { applyHelm, isHallPage, wearsHelmFaces, stampHtml, HELM_FONTS_HREF };
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.81'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -76,7 +76,7 @@ test('every pack colour shows its light value and its dark value; the status voc
|
|
|
76
76
|
assert.ok(r.includes(`light</span> <code>${light}</code>`), `${prop} light ${light}`);
|
|
77
77
|
assert.match(r, /dark<\/span> <code>[^<]+<\/code>/, `${prop} has a dark value`);
|
|
78
78
|
}
|
|
79
|
-
assert.ok(rowOf(PAGE, '--bg').includes('dark</span> <code>#
|
|
79
|
+
assert.ok(rowOf(PAGE, '--bg').includes('dark</span> <code>#050d16</code>'), 'the dark ground is read from the hall\'s dark tier: the Helm night (task 1005192, ADR 0402)');
|
|
80
80
|
assert.ok(rowOf(PAGE, '--ok').includes('light</span> <code>#187444</code>') && rowOf(PAGE, '--ok').includes('dark</span> <code>#4ade80</code>'));
|
|
81
81
|
for (const s of ['--warn', '--danger', '--info', '--ok-weak', '--warn-weak', '--danger-weak', '--info-weak']) rowOf(PAGE, s);
|
|
82
82
|
});
|
|
@@ -152,7 +152,8 @@ test('A: the quiet fill is a defined variant, not an inline colour', () => {
|
|
|
152
152
|
const m = css.match(/([^{}]*\.landing__btn--quiet[\s,][^{}]*)\{([^{}]*)\}/);
|
|
153
153
|
assert.ok(m, 'controls.css defines .landing__btn--quiet');
|
|
154
154
|
const block = m[2];
|
|
155
|
-
|
|
155
|
+
// Helm (task 1005192, ADR 0402): the ghost takes the hall's press fill, and nothing without the hall's tier
|
|
156
|
+
assert.match(block, /background:\s*var\(--press-fill, transparent\)/, 'the quiet variant is the ghost');
|
|
156
157
|
assert.deepEqual(block.match(/#[0-9a-f]{3,8}\b/gi) || [], [], 'the variant carries no literal colour');
|
|
157
158
|
// Same geometry as the request button means it inherits .landing__btn's
|
|
158
159
|
// min-height and padding rather than restating them.
|
package/tests/hall_city.mjs
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
// the ring road, or a task with no building;
|
|
13
13
|
// * the line under the button saying anything but the docket's own line;
|
|
14
14
|
// * the start governing button going anywhere but the street;
|
|
15
|
-
// *
|
|
16
|
-
//
|
|
15
|
+
// * a light/dark switch coming back: the hall is dark only (task 1005192,
|
|
16
|
+
// ADR 0402), so the city draws its night faces and reads no stored choice;
|
|
17
17
|
// * motion that escapes the Kill Switch, or docket text reaching innerHTML.
|
|
18
18
|
//
|
|
19
19
|
// Run: node tests/hall_city.mjs
|
|
@@ -263,7 +263,7 @@ test('parcels ride only the roads of plots that hold a district, and leave throu
|
|
|
263
263
|
|
|
264
264
|
// ── the page ────────────────────────────────────────────────────────────────
|
|
265
265
|
|
|
266
|
-
test('the page holds only the wordmark, the
|
|
266
|
+
test('the page holds only the wordmark, the city, one door and one line, and no light/dark switch', () => {
|
|
267
267
|
const html = read('city.html').replace(/<!--[\s\S]*?-->/g, ''); // the markup, not the prose about it
|
|
268
268
|
assert.equal((html.match(/<header\b/g) || []).length, 1);
|
|
269
269
|
assert.equal((html.match(/<main\b/g) || []).length, 1);
|
|
@@ -271,7 +271,8 @@ test('the page holds only the wordmark, the switch, the city, one button and one
|
|
|
271
271
|
assert.match(html, /<a class="city-start" id="cityStart" href="#decide"/);
|
|
272
272
|
assert.equal((html.match(/<a\b/g) || []).length, 3, 'one door, and the two faint archived links to the street');
|
|
273
273
|
assert.ok(html.includes('<p class="city-archived">Archived: <a href="/builders/street">Street</a> & <a href="/builders/street#building=charter">Building Level Views</a>.</p>'), 'the street is archived behind two faint links');
|
|
274
|
-
assert.equal((html.match(/<button\b/g) || []).length,
|
|
274
|
+
assert.equal((html.match(/<button\b/g) || []).length, 0, 'no switch: the hall is dark only (task 1005192, ADR 0402)');
|
|
275
|
+
assert.doesNotMatch(html, /cityMode/);
|
|
275
276
|
assert.match(html, /class="city-mark">\{\{productName\}\}</);
|
|
276
277
|
assert.equal(City.STREET_HREF, '/builders/street');
|
|
277
278
|
});
|
|
@@ -346,7 +347,7 @@ function fakeDom() {
|
|
|
346
347
|
return el;
|
|
347
348
|
};
|
|
348
349
|
const html = mk('html');
|
|
349
|
-
for (const id of ['
|
|
350
|
+
for (const id of ['cityStage', 'cityFlat', 'cityBlds', 'cityParcels', 'cityPins', 'cityLine', 'cityStart']) mk('x').setAttribute('id', id);
|
|
350
351
|
const document = {
|
|
351
352
|
documentElement: html,
|
|
352
353
|
getElementById: (id) => byId.get(id) || null,
|
|
@@ -421,33 +422,18 @@ test('a failed docket read draws no pins and says the docket could not be read',
|
|
|
421
422
|
assert.equal(p.byId.get('cityLine').textContent, City.FALLBACK_LINE);
|
|
422
423
|
});
|
|
423
424
|
|
|
424
|
-
test('the
|
|
425
|
-
const p = await bootPage({ feeds: FEEDS([]), stored: 'light', osDark:
|
|
426
|
-
assert.equal(p.html.attrs['data-theme'], '
|
|
427
|
-
assert.equal(p.
|
|
428
|
-
|
|
429
|
-
p.byId.get('cityMode').fire('click');
|
|
430
|
-
assert.equal(p.store.get('hall-theme'), 'dark');
|
|
431
|
-
assert.equal(p.html.attrs['data-theme'], 'dark');
|
|
432
|
-
assert.equal(p.byId.get('cityMode').attrs['aria-label'], 'Switch to light mode');
|
|
433
|
-
assert.notEqual(JSON.stringify(p.byId.get('cityFlat').children[0].attrs), before, 'the city redraws in the new mode');
|
|
425
|
+
test('the city is dark only: no stored choice and no OS setting reaches it (task 1005192, ADR 0402)', async () => {
|
|
426
|
+
const p = await bootPage({ feeds: FEEDS([]), stored: 'light', osDark: false, shell: { applyTheme() { throw new Error('the city no longer drives the shell'); } } });
|
|
427
|
+
assert.equal(p.html.attrs['data-theme'], 'dark', 'a stored light choice and a light OS are both ignored');
|
|
428
|
+
assert.equal(p.store.get('hall-theme'), 'light', 'and the old preference is never written');
|
|
429
|
+
assert.equal(p.observers.length, 0, 'nothing flips the theme, so nothing watches it');
|
|
434
430
|
const q = await bootPage({ feeds: FEEDS([]), stored: null, osDark: true });
|
|
435
|
-
assert.equal(q.
|
|
431
|
+
assert.equal(JSON.stringify(q.byId.get('cityFlat').children[0].attrs), JSON.stringify(p.byId.get('cityFlat').children[0].attrs), 'one drawing whatever was stored');
|
|
432
|
+
const src = read('city.js');
|
|
433
|
+
assert.doesNotMatch(src, /hall-theme|localStorage|prefers-color-scheme/, 'city.js reads no theme preference');
|
|
436
434
|
});
|
|
437
435
|
|
|
438
|
-
|
|
439
|
-
let shellPaints = 0;
|
|
440
|
-
const p = await bootPage({ feeds: FEEDS([]), stored: 'light', osDark: true, shell: { applyTheme() { shellPaints++; } } });
|
|
441
|
-
assert.equal(p.observers.length, 1, 'the page watches html[data-theme]');
|
|
442
|
-
p.byId.get('cityMode').fire('click');
|
|
443
|
-
assert.equal(shellPaints, 1, 'this switch repaints the shell\'s');
|
|
444
|
-
const before = JSON.stringify(p.byId.get('cityFlat').children[0].attrs);
|
|
445
|
-
p.store.set('hall-theme', 'light'); p.html.setAttribute('data-theme', 'light'); // the shell's switch
|
|
446
|
-
p.observers[0].fn();
|
|
447
|
-
assert.equal(p.byId.get('cityMode').attrs['aria-label'], 'Switch to dark mode', 'this switch follows the shell\'s');
|
|
448
|
-
assert.notEqual(JSON.stringify(p.byId.get('cityFlat').children[0].attrs), before, 'and the city redraws in that mode');
|
|
449
|
-
assert.equal(shellPaints, 1, 'following the shell does not echo back to it');
|
|
450
|
-
});
|
|
436
|
+
|
|
451
437
|
|
|
452
438
|
test('the artboard fits the stage the hall chrome leaves, not the window (task 1004544)', async () => {
|
|
453
439
|
const p = await bootPage({ feeds: FEEDS([]), stage: { clientWidth: 688, clientHeight: 900 } });
|
package/tests/hall_controls.mjs
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
// button (it loads later) without re-drawing it;
|
|
10
10
|
// * THE SIZES — primary is DESIGN.md's 46–48px door, the row size and the
|
|
11
11
|
// text button keep the 24px floor, the field clears it;
|
|
12
|
+
// * THE HELM SHAPE AND VOICE (task 1005192, ADR 0402) — in the hall a button
|
|
13
|
+
// is the cut corner in the name voice, every piece of it a hall-tier token
|
|
14
|
+
// spent with the old pill/lowercase as its fallback (the status page links
|
|
15
|
+
// this sheet without the hall's tier), and a cut button's focus ring is
|
|
16
|
+
// drawn inside the shape, because a clip-path clips an outline;
|
|
12
17
|
// * CONTRAST, MEASURED — the button's ink on its fill (rest AND hover), the
|
|
13
18
|
// ghost's and the text button's ink on both grounds, the field's text and
|
|
14
19
|
// its edge (3:1, WCAG 1.4.11), in both modes, resolved from the real
|
|
@@ -144,11 +149,11 @@ test('style.css imports controls.css right after the shared token sheet', () =>
|
|
|
144
149
|
|
|
145
150
|
// ── 2. the sizes ─────────────────────────────────────────────────────────────
|
|
146
151
|
|
|
147
|
-
test('primary is the 46–48px
|
|
152
|
+
test('primary is the 46–48px door; the row size and the text button keep the 24px floor', () => {
|
|
148
153
|
const base = sharedRule('.btn');
|
|
149
154
|
const h = px(prop(base, 'min-height'));
|
|
150
155
|
assert.ok(h >= 46 && h <= 48, `.btn is ${h}px — DESIGN.md's door is 46–48px`);
|
|
151
|
-
assert.equal(prop(base, 'border-radius'), 'var(--radius-pill)', 'pill
|
|
156
|
+
assert.equal(prop(base, 'border-radius'), 'var(--press-radius, var(--radius-pill))', 'the hall\'s corner, and the pill where the hall\'s tier is not loaded');
|
|
152
157
|
assert.match(prop(base, 'border'), /transparent$/, 'the edge is reserved, so a modifier that sets only its colour draws');
|
|
153
158
|
for (const sel of ['.btn--sm', '.btn--text']) {
|
|
154
159
|
assert.ok(px(prop(sharedWith(sel, 'min-height'), 'min-height')) >= 24, `${sel} keeps 24px of height`);
|
|
@@ -203,6 +208,49 @@ test('no hall sheet puts white ink on an accent fill', () => {
|
|
|
203
208
|
const PRIMARY = ['.btn', '.btn-accent', '.landing__btn', '.task__claim', '.setup-gate__save', '.td-btn--primary', '.pbtn--save', '.craft-save'];
|
|
204
209
|
const GHOST = ['.btn--ghost', '.btn-ghost', '.landing__btn--quiet', '.td-btn', '.wip__release', '.pbtn', '.settings-btn'];
|
|
205
210
|
const SMALL = ['.btn--sm', '.btn-ghost', '.task__claim', '.td-btn', '.wip__release'];
|
|
211
|
+
// the ghost's fill: the hall's press fill, and nothing at all without the hall's tier
|
|
212
|
+
const GHOST_FILL = 'var(--press-fill, transparent)';
|
|
213
|
+
|
|
214
|
+
// ── 3b. the Helm shape and voice (task 1005192, ADR 0402) ───────────────────
|
|
215
|
+
|
|
216
|
+
test('in the hall a button is the cut corner in the name voice, and without the hall\'s tier it is the pill it was', () => {
|
|
217
|
+
const base = sharedRule('.btn');
|
|
218
|
+
// every Helm piece is a token with the old look as its fallback
|
|
219
|
+
assert.equal(prop(base, 'clip-path'), 'var(--cut-path, none)');
|
|
220
|
+
assert.equal(prop(base, 'font-family'), 'var(--name-face, var(--font-body))');
|
|
221
|
+
assert.equal(prop(base, 'font-weight'), 'var(--name-weight, 500)');
|
|
222
|
+
assert.equal(prop(base, 'letter-spacing'), 'var(--name-space-tight, normal)');
|
|
223
|
+
assert.equal(prop(base, 'text-transform'), 'var(--name-case, lowercase)');
|
|
224
|
+
assert.equal(prop(sharedWith('.btn--sm', 'clip-path'), 'clip-path'), 'var(--cut-path-sm, none)', 'the row size takes the smaller cut');
|
|
225
|
+
// the tokens are the hall tier's, and the status page (which links this sheet) defines none of them
|
|
226
|
+
const status = fs.readFileSync(path.join(ROOT, 'modules', 'status-ui', 'public', 'style.css'), 'utf8');
|
|
227
|
+
for (const name of ['--cut-path', '--cut-path-sm', '--press-radius', '--press-fill', '--press-focus', '--name-face', '--name-weight', '--name-space-tight', '--name-case']) {
|
|
228
|
+
assert.ok(root[name], `${name} is in the hall's token block`);
|
|
229
|
+
assert.ok(!new RegExp(`${name}\\s*:`).test(status), `the status page does not define ${name}, so the fallback stands there`);
|
|
230
|
+
}
|
|
231
|
+
assert.equal(root['--cut-path'], 'polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut))', 'top-left and bottom-right are cut');
|
|
232
|
+
assert.equal(root['--name-case'], 'uppercase');
|
|
233
|
+
// a link in a sentence is not a cut control and does not shout
|
|
234
|
+
const text = sharedWith('.btn--text', 'clip-path');
|
|
235
|
+
assert.equal(prop(text, 'clip-path'), 'none');
|
|
236
|
+
assert.equal(prop(text, 'text-transform'), 'none');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('a cut button keeps a visible focus ring: inset, two-tone, 3:1 on its own fill', () => {
|
|
240
|
+
// clip-path clips the outline and the outer halo, so the base rule hands the shell's
|
|
241
|
+
// :focus-visible rule an inset ring instead; the text button (not cut) hands it back
|
|
242
|
+
assert.equal(prop(sharedRule('.btn'), '--focus-ring'), 'var(--press-focus)');
|
|
243
|
+
assert.equal(prop(sharedWith('.btn--text', '--focus-ring'), '--focus-ring'), 'initial');
|
|
244
|
+
const shell = rulesOf(styleSrc).find((x) => x.sels.length === 1 && x.sels[0] === ':focus-visible');
|
|
245
|
+
assert.ok(shell, 'the shell\'s ring rule');
|
|
246
|
+
assert.match(prop(shell.body, 'box-shadow'), /^var\(--focus-ring, /, 'the shell\'s ring spends what a cut control hands in');
|
|
247
|
+
assert.equal(root['--press-focus'], 'inset 0 0 0 2px var(--focus), inset 0 0 0 4px var(--bg)', 'the focus colour on the edge, then a 2px band of the ground');
|
|
248
|
+
// one of the two tones reads against each fill a cut button paints (WCAG 1.4.11, 3:1), in the mode the hall renders
|
|
249
|
+
for (const [what, fill] of [['primary', 'var(--accent)'], ['primary hover', 'var(--accent-lift)'], ['ghost on the page', onGround(GHOST_FILL, 'var(--bg)', 'dark')], ['ghost on a panel', onGround(GHOST_FILL, 'var(--bg-card)', 'dark')], ['danger', 'var(--danger)']]) {
|
|
250
|
+
const best = Math.max(contrast('var(--focus)', fill, 'dark'), contrast('var(--bg)', fill, 'dark'));
|
|
251
|
+
assert.ok(best >= 3, `dark ${what}: the ring's better tone is ${best.toFixed(2)}:1 on the fill`);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
206
254
|
|
|
207
255
|
test('every older button name sits in the shared rule it means', () => {
|
|
208
256
|
const fill = (sel) => {
|
|
@@ -210,11 +258,11 @@ test('every older button name sits in the shared rule it means', () => {
|
|
|
210
258
|
return r.length ? prop(r[0].body, 'background') : null;
|
|
211
259
|
};
|
|
212
260
|
for (const sel of PRIMARY) assert.equal(fill(sel), 'var(--accent)', `${sel} is a primary door`);
|
|
213
|
-
for (const sel of GHOST) assert.equal(fill(sel),
|
|
261
|
+
for (const sel of GHOST) assert.equal(fill(sel), GHOST_FILL, `${sel} is a ghost`);
|
|
214
262
|
for (const sel of SMALL) assert.equal(prop(sharedWith(sel, 'min-height'), 'min-height'), '30px', `${sel} is the row size`);
|
|
215
263
|
// and the primary-on-a-ghost-base names come AFTER the ghost rule, or the ghost wins on order
|
|
216
264
|
const list = rulesOf(controls);
|
|
217
|
-
const ghostAt = list.findIndex((x) => x.sels.includes('.td-btn') && prop(x.body, 'background') ===
|
|
265
|
+
const ghostAt = list.findIndex((x) => x.sels.includes('.td-btn') && prop(x.body, 'background') === GHOST_FILL);
|
|
218
266
|
const primaryAt = list.findIndex((x) => x.sels.includes('.td-btn--primary') && prop(x.body, 'background'));
|
|
219
267
|
assert.ok(primaryAt > ghostAt, '.td-btn--primary follows .td-btn, so the shared order paints it');
|
|
220
268
|
});
|