fix(as-S2): legibility — nothing fades, labels 11px, sentences 12px
From the anti-slop run (design-dev, 2026-09-28). Faded is not legible: opacity divides whatever contrast a line had. - Review arrows: the chip under the thin chevron is 82% dense, not 60%; the glyph now clears 7:1 over a white stage by colour (was 3.84:1), and reads at pixel level where the detector sampled a 2.9:1 median. - Filmstrip numbers, the marks' state stamp and the inline ask's state tag are labels at 11px (were 9.5 / 10.5 / 10.5px). - The Desk's section rules, the board note and the bench note are sentences at 12px. - Retired benches: no opacity; the link and URL take --text-muted. - Embed chrome: answered-ask details and the "recorded:" line inherit the host's text colour at full strength (the embed cannot know the host's palette); the notes placeholder inherits it at 75%, not the UA grey. Folded from the heid bug-hunt: the embed's ask title no longer fades either (Q9), and the legibility claims are also held on the browser's COMPUTED style (tests/test_antislop_s2_browser.py): a stylesheet grep cannot see a later rule in the cascade (font-size:1px, color:transparent, filter:grayscale, a placeholder at opacity:0); the browser can. Folded after the first gate run: the flagged tray's number, the tile's "flagged" stamp and compare's A/B badge were still under the 11px label floor; they take --size-micro too. Two film-number rows are re-anchored on the .film-ord selector: the tray's line is now identical to it, and the runner mutates the first match. Contract: as_antislop S2. Falsifiers: antislop.toml 34/34 proved (S1+S2); all 12 tables 314/314 proved on this tree.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""S2 of the anti-slop fix slices, read off the browser's COMPUTED style.
|
||||
|
||||
The string tests in test_antislop.py read the stylesheet; a later rule in the
|
||||
cascade (`font-size:1px`, `color:transparent`, `filter:grayscale`, a placeholder
|
||||
at `opacity:0`) passes them and is still illegible (heid bug-hunt guard grid).
|
||||
These open the pages in the real uvicorn + Chromium harness and read what the
|
||||
browser will actually paint. Skips, never fails, when no usable Chromium exists.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
||||
from test_flow_browser import _png, browser, live # noqa: E402,F401 (fixtures)
|
||||
|
||||
WIDE = {"width": 1280, "height": 800}
|
||||
|
||||
|
||||
def _seed(root: pathlib.Path):
|
||||
from booth.benches import set_bench_state, upsert_bench
|
||||
from booth.links import LINKS_FILE
|
||||
from booth.marks import answer_pick, declare_pick, marks_for
|
||||
g = root / "g"
|
||||
g.mkdir()
|
||||
for n in ("a.png", "b.png"):
|
||||
(g / n).write_bytes(_png(64, 48))
|
||||
from booth.marks import set_flag
|
||||
set_flag(g, "a.png", True) # the tray's number, the tile's stamp
|
||||
declare_pick(g, "p1", {"prompt": "Which?", "options": ["North", "South"]})
|
||||
answer_pick(g, "p1", "North", who="127.0.0.1")
|
||||
r = root / "r"
|
||||
r.mkdir()
|
||||
(r / "index.html").write_text("<!doctype html><title>r</title><body><h1>R</h1><p>body</p></body>")
|
||||
declare_pick(r, "p2", {"title": "Round one", "prompt": "Which gate?", "notes": True,
|
||||
"options": [{"id": "n", "label": "North", "detail": "the gate by the road"},
|
||||
{"id": "s", "label": "South", "detail": "the loading dock"}]})
|
||||
answer_pick(r, "p2", marks_for(r)[0].options[0]["id"], who="127.0.0.1")
|
||||
board = root / "links"
|
||||
board.mkdir()
|
||||
(board / LINKS_FILE).write_text("- [x](http://x.example/) <sub>· infra-ops · 2026-09-06 23:35</sub>\n")
|
||||
bench, _ = upsert_bench(root, "http://10.100.10.50:7788/talk", "talk demo", "booth")
|
||||
set_bench_state(root, bench.id, "retired")
|
||||
|
||||
|
||||
def _size(page, sel):
|
||||
return page.locator(sel).first.evaluate("e => parseFloat(getComputedStyle(e).fontSize)")
|
||||
|
||||
|
||||
def test_labels_and_sentences_render_at_their_floors(browser, live):
|
||||
base, root = live
|
||||
_seed(root)
|
||||
page = browser.new_page(viewport=WIDE)
|
||||
checks = [("/b/g/view?f=a.png", ".film-ord", 11), ("/b/g/marks", ".mark-state", 11), ("/", ".desk-rule", 12),
|
||||
("/b/links/", ".board-note", 12), ("/b/links/", ".bench-note", 12)]
|
||||
for url, sel, floor in checks:
|
||||
page.goto(base + url, wait_until="load")
|
||||
assert _size(page, sel) >= floor, (url, sel, _size(page, sel))
|
||||
# folded after the S2 gate: three labels the report's list did not name
|
||||
for url, sel in (("/b/g/", ".tray-ord"), ("/b/g/compare?a=a.png&b=b.png", ".film-ab")):
|
||||
page.goto(base + url, wait_until="load")
|
||||
assert _size(page, sel) >= 11, (url, sel, _size(page, sel))
|
||||
page.goto(base + "/b/g/", wait_until="load")
|
||||
stamp = page.locator(".item.is-flagged").first.evaluate(
|
||||
"e => parseFloat(getComputedStyle(e, '::before').fontSize)")
|
||||
assert stamp >= 11, ("the tile's 'flagged' stamp", stamp)
|
||||
page.goto(base + "/b/r/", wait_until="load")
|
||||
page.wait_for_selector(".bk-ask-tag")
|
||||
assert _size(page, ".bk-ask-tag") >= 11
|
||||
page.close()
|
||||
|
||||
|
||||
_FADE = """e => { const cs = getComputedStyle(e), parent = getComputedStyle(e.parentElement);
|
||||
const a = c => { const m = c.match(/rgba?\\(([^)]+)\\)/); if (!m) return 1; const p = m[1].split(',');
|
||||
return p.length > 3 ? parseFloat(p[3]) : 1; };
|
||||
return {opacity: parseFloat(cs.opacity), filter: cs.filter, alpha: a(cs.color),
|
||||
color: cs.color, parent: parent.color}; }"""
|
||||
|
||||
|
||||
def test_nothing_fades(browser, live):
|
||||
base, root = live
|
||||
_seed(root)
|
||||
page = browser.new_page(viewport=WIDE)
|
||||
page.goto(base + "/b/r/", wait_until="load")
|
||||
page.wait_for_selector(".bk-ask-det")
|
||||
for sel in (".bk-ask-det", ".bk-ask-was", ".bk-ask-title"):
|
||||
got = page.locator(sel).first.evaluate(_FADE)
|
||||
assert got["opacity"] == 1 and got["filter"] == "none" and got["alpha"] == 1, (sel, got)
|
||||
det = page.locator(".bk-ask-det").first.evaluate(_FADE)
|
||||
assert det["color"] == det["parent"], ("details take the host's own colour", det)
|
||||
ph = page.locator(".bk-ask-notes").first.evaluate("""e => { const p = getComputedStyle(e, '::placeholder');
|
||||
return {opacity: parseFloat(p.opacity), color: p.color, field: getComputedStyle(e).color}; }""")
|
||||
assert ph["opacity"] >= 0.7 and ph["color"] == ph["field"], ph
|
||||
page.goto(base + "/b/links/", wait_until="load")
|
||||
row = page.locator(".bench-row.is-retired").first.evaluate(_FADE)
|
||||
assert row["opacity"] == 1 and row["filter"] == "none", row
|
||||
page.close()
|
||||
Reference in New Issue
Block a user