Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-2475Done

Sweep `--el-text-faint` off ACTIVE informational text — every site the scanner rules a violation, and its faint arm turned on repo-wide

Carved out of MOTIR-2459 on 2026-08-08, which was sized 5 points / 90 minutes for "build the guard AND fix every violation" before anyone had the defect count. The count is now measured and it is a backlog, so 2459 keeps the SCANNER and this card carries the --el-text-faint sweep it enables.

Repo: motir-core. One PR.

What the scanner measured, on origin/main at 0d5279da

Over app/**, components/**, lib/** and packages/design-system/src/** (1703 files), text-(--el-text-faint) lands on 230 sites:

verdictsiteswhat it means
decorative81aria-hidden, a labelled role="img", or a labelled control whose content is glyphs only — legitimate
disabled17a disabled / aria-disabled element, or the disabled branch of a ternary — WCAG 1.4.3 exempts it
violation122active informational text at 2.37–2.61:1
unattributable10a bare class constant the scanner cannot attach to an element, so nothing can show it is either of the above

132 defects across 82 files. The card this was carved from cited 262 occurrences / ~184 "text-ish" and warned the heuristic was untrustworthy; it was — the honest split is above, and the 10 unattributable constants are a shape neither number contained.

Why the shipped a11y sweeps are green with 132 defects on main

Crossed the violation files against the routes tests/e2e/shell-a11y*.spec.ts actually visits: 108 of the 134 files carrying either defect sit on routes the sweeps never load at all — the whole app/(public) tree (docs, explore, p/[identifier]), (onboarding), /backlog, /triage, /settings/account, /settings/organization, components/planning. The 26 that DO sit on a swept route survive because the sweep loads them EMPTY (a fresh user, a fresh project), so the faint-carrying branches — a "Showing N of M" tail, a populated row's timestamp, an open menu's section label — never render. A green sweep is evidence about the fixture, not about the ink.

The fix, and why one substitution covers the sweep

--el-text-secondary clears AA on all four measured surfaces in both themes (6.18–6.80:1), so it is the only replacement that cannot be wrong without knowing the surface — and motir-core/CLAUDE.md § The token map already says to reach for it when in doubt. So the sweep is: every violation takes --el-text-secondary, except where the element is genuinely a glyph, which is fixed by MARKING it (aria-hidden, a labelled role="img") rather than by re-inking it. The 10 unattributable constants are fixed by moving the class onto the element that renders it, or by marking that element.

Acceptance criteria

  • scanSource (from MOTIR-2459) reports zero violation and zero unattributable faint findings across the scanned tree — asserted as a derivation over the scanned file set, never against a frozen count of 132, so the assertion survives the tree growing.
  • A new tests/theme/inkContrastLint.test.ts runs that scan repo-wide and FAILS the build on any faint violation, in the mould of tests/theme/swapLayerLint.test.ts — and it asserts the scanned file set is real and non-empty first (notes.html #195), naming at least one file per scanned root.
  • No allowlist, no exclusion, no per-file exemption. Every violation is fixed by moving the ink or by marking the element decorative; an element that is genuinely a glyph gains aria-hidden (or a labelled role="img") so the guard can see what a reader already assumed.
  • The guard's muted arm stays off in this PR — it is a separate sweep with a separate blind spot, and turning it on here would make the diff unreviewable.
  • Every string the sweep re-inks keeps its existing size / weight / tracking; this changes the ink only.
  • pnpm lint, pnpm typecheck, pnpm prettier and next build pass, and every component test that asserts on a re-inked class is updated in the same PR.
  • The PR body states the final counts by verdict and names the routes the a11y sweeps do NOT cover, so the next reader inherits the coverage gap rather than re-deriving it.

Context refs

  • tests/theme/inkContrastScan.ts — the scanner this card consumes (MOTIR-2459).
  • tests/theme/swapLayerLint.test.ts — the repo-wide token-lint mould to follow.
  • motir-core/CLAUDE.md § The token map — the measured contrast table and the two-legitimate-jobs rule.
  • tests/e2e/shell-a11y.spec.tsSHELL_ROUTES, the swept set.
  • MOTIR-2455 — the incident that measured the tokens.