You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(standard): add §18 accessibility as opt-in mode layer — v1.33
Accessibility was three unenforceable lines in §13, a chapter scoped to
graphical applications, while the estate is overwhelmingly Rust CLI/TUI.
§18 makes it a first-class, auditable chapter covering CLI, TUI, and GUI.
Accessible mode is mandatory for developers to implement and optional for
users to activate. The Steelbore theme is untouched and remains the sole
default; two additive sibling variants join the §11.1 registry.
- §18 (new): activation precedence (--accessible / SPACECRAFT_A11Y /
config / hints), CLI+TUI rules, TUI linear mode, GUI bridges
(AccessKit for Rust; GtkAccessible / Semantics / QAccessible),
verification, and remediation tracking for existing projects.
- §11.1.1 (new): steelbore-high-contrast (only accent and error shift,
to #7FAEDC and #FF8080; all six tokens >=7:1 on Void Navy) and
steelbore-mono (4-bit ANSI). Void Navy remains the background in every
variant.
- §11: the AA guarantee covers foreground-on-Void-Navy only. 9 of 10
inter-token pairs fall below 3:1 (Amber on Red Oxide is 1.13:1), so
text on a palette-colored fill now requires its own verification.
- §10: bindings must be user-remappable; NVDA/Orca/VoiceOver modifier
chords must not be captured.
- §13: WCAG 2.1 AA -> WCAG 2.2 AA; EN 301 549 clause 11 (non-web
software) adopted as the anchor for CLI/TUI.
- §16: new §18 gate; §10 and §13 gates extended.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|**2. Environment**|`SPACECRAFT_A11Y=1` / `SPACECRAFT_A11Y=0` — the umbrella-wide environment variable |
1431
+
|**3. Configuration**|`[accessibility] enabled = true` in the project’s config file |
1432
+
|**4. Auto-detect hints**|`TERM=dumb`, `NO_COLOR`, or `GTK_MODULES` containing `gail:atk`|
1433
+
1434
+
- A hint (source 4) **may** enable accessible mode, but must never be
1435
+
inferred from an ambiguous signal. An explicit `--no-accessible` or
1436
+
`SPACECRAFT_A11Y=0` always wins, at every level.
1437
+
1438
+
- Unset at every source ⇒ **standard `Steelbore` rendering, unchanged**.
1439
+
Silence is never read as consent to change the default presentation.
1440
+
1441
+
- The resolved state and the source that decided it must be reported
1442
+
under `--verbose`, so a user can tell why the mode is on or off.
1443
+
1444
+
- The toggle is a **single switch** governing every behavior in §18.2
1445
+
and §18.3 together. Per-feature accessibility flags fragment the
1446
+
contract and are not a substitute.
1447
+
1448
+
## §18.2 — CLI & TUI Requirements
1449
+
1450
+
**The constraint that shapes everything here:** a terminal has no
1451
+
accessibility tree. There is no ARIA, there are no roles, and there are
1452
+
no live regions. A screen reader reads the emulator’s character grid, so
1453
+
a redraw-based interface produces re-reads and speech loops rather than
1454
+
useful speech. Accessibility cannot be delegated to the TUI framework —
1455
+
no terminal UI library provides it — so the application must supply a
1456
+
linear fallback itself.
1457
+
1458
+
### §18.2.1 — Rules that apply in every mode
1459
+
1460
+
These are not gated behind the toggle. They are correctness requirements
1461
+
for all output, always:
1462
+
1463
+
-**Color is never the sole carrier of meaning.** Every colored status
1464
+
carries a text tag: `[OK]`, `[ERROR]`, `[WARN]`, `[INFO]`. A red line
1465
+
that says only "failed to connect" is non-compliant;
1466
+
`[ERROR] failed to connect` is compliant.
1467
+
1468
+
-**No text on colored fills** unless that specific pair is verified per
1469
+
§11 — palette tokens are verified against Void Navy, not against each
1470
+
other.
1471
+
1472
+
-**Diagnostics go to `stderr`**, results to `stdout`, and the two are
1473
+
never interleaved into one visual block.
1474
+
1475
+
-**`NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`, and `TERM=dumb`** are honored
1476
+
with the precedence already defined by the `spacecraft-cli-standard`
1477
+
skill.
1478
+
1479
+
### §18.2.2 — Rules that apply in accessible mode
1480
+
1481
+
| Requirement | Rule |
1482
+
|----|----|
1483
+
|**No animation**| Spinners, marquees, blinking text, and progress animations are replaced by a single static line carrying monotonic progress (`Working… 40%`), rewritten at most once per second |
1484
+
|**No decorative art**| ASCII art, banners, box-drawing decoration, and figlet headers are suppressed. Where the art is *informational* (a diagram, a chart), emit an equivalent text description instead — do not simply drop it |
1485
+
|**Linear output**| Output is append-only and reads correctly top-to-bottom. Blank lines separate logical sections so a screen reader can navigate by paragraph |
1486
+
|**Tabular fallback**| Any table offers a non-columnar rendering — one `field: value` pair per line — since column alignment conveys nothing through speech |
1487
+
|**Prompt legibility**| Interactive prompts state the question, the available choices, and the default in plain text before awaiting input. Prompts that rely on cursor positioning or redraw to convey state are non-compliant |
1488
+
1489
+
### §18.2.3 — TUI linear mode
1490
+
1491
+
Any full-screen TUI MUST additionally provide a **non-redraw,
1492
+
append-only stream mode**, reachable through the same §18.1 toggle. In
1493
+
linear mode the application writes new state as new lines rather than
1494
+
repainting regions, and does not take over the alternate screen buffer.
1495
+
1496
+
Where a TUI would otherwise be the only way to perform an operation, an
1497
+
equivalent **non-interactive CLI path** must exist — flags plus `--json`
1498
+
output — so the operation remains scriptable and reachable without
1499
+
navigating a visual grid. Per §8, this path must be documented in the
1500
+
project’s Texinfo manual alongside the interactive one.
1501
+
1502
+
## §18.3 — GUI Requirements
1503
+
1504
+
Graphical applications expose an accessibility tree to the platform’s
1505
+
assistive-technology API. Which bridge to use depends on how the UI is
1506
+
drawn:
1507
+
1508
+
| UI stack | Required bridge |
1509
+
|----|----|
1510
+
|**Rust, custom-drawn UI**|**AccessKit** (Apache-2.0) — one API over UI Automation (Windows), NSAccessibility (macOS), and AT-SPI (Linux). Already integrated in egui, Slint, Bevy, Freya, Xilem, and winit |
1511
+
|**GTK 4**|`GtkAccessible` — WAI-ARIA roles and states surfaced over AT-SPI |
1512
+
|**Flutter**|`Semantics` widgets and `SemanticsRole`|
1513
+
|**Qt**|`QAccessible`|
1514
+
1515
+
Custom-drawn widgets are the failure case to watch: a canvas-rendered
1516
+
control is invisible to assistive technology unless the application
1517
+
publishes its role and state explicitly. This is precisely the gap
1518
+
AccessKit exists to close, which is why it is required rather than
1519
+
merely suggested for Rust GUI work (§3.1 already makes Rust the
1520
+
preferred language).
1521
+
1522
+
**Requirements:**
1523
+
1524
+
- Every interactive element carries an explicit accessible **name** and
1525
+
**role**. Purely decorative elements are explicitly marked decorative
1526
+
so they are skipped rather than read as noise.
1527
+
1528
+
- State changes that matter to the user are **announced**, not merely
1529
+
repainted.
1530
+
1531
+
- System preferences for **reduced motion** and **high contrast** are
1532
+
honored — these are read from the platform, independently of the §18.1
1533
+
toggle, because the user has already expressed the preference
1534
+
system-wide.
1535
+
1536
+
- Keyboard reachability and focus visibility follow §10.
1537
+
1538
+
## §18.4 — Verification & Remediation
1539
+
1540
+
**Verification.** An accessibility claim is not satisfied by inspection.
1541
+
Before a release:
1542
+
1543
+
-**CLI/TUI:** pipe the accessible-mode output through a speech
1544
+
synthesizer (e.g. `espeak-ng`) and confirm it is comprehensible when
1545
+
heard rather than seen. Confirm the non-interactive path completes the
1546
+
same operations as the interactive one.
1547
+
1548
+
-**GUI:** exercise the application with a real screen reader — Orca on
1549
+
Linux, NVDA on Windows, VoiceOver on macOS — and confirm every
1550
+
interactive element announces a name and a role.
1551
+
1552
+
-**Contrast:** measure the actual pairings used, not just
1553
+
foreground-on-background, and record the ratios (§11).
1554
+
1555
+
-**Keyboard:** complete every primary task without a pointing device.
1556
+
1557
+
**Remediation for existing projects.** §18 applies to every project
1558
+
immediately on adoption of v1.33, which means projects predating it are
1559
+
non-compliant until retrofitted. This is stated plainly rather than
1560
+
softened into a recommendation. Until a project conforms, it MUST carry
1561
+
a **dated remediation entry** in `PROJECTS.md` recording its current
1562
+
accessibility state and the intended remediation. An absent entry is a
1563
+
compliance failure in its own right — a project may be unfinished, but
1564
+
it may not be silently unfinished.
1565
+
1566
+
————————————————————————
1567
+
1307
1568
# §16 — Compliance Checklist (Audit Gate)
1308
1569
1309
1570
Before finalising **any** Spacecraft Software artifact, mentally verify:
0 commit comments