From 5f3141e2a396180559876b9c289112ff42f1fc20 Mon Sep 17 00:00:00 2001 From: "n.siegfried" Date: Fri, 14 Aug 2026 18:48:47 -0500 Subject: [PATCH] T1.5 - F5 (interim): wizard fields stop looking disabled INTERIM. T3.4 removes the duplicate token underneath this; the job here is only the appearance, and no token consolidation is started. The wizard filled its inputs with var(--bg) - which in this sheet is the PAGE BACKGROUND, #f4f4f4 - on a #e0e0e0 border. An empty required field was indistinguishable from a locked one, which is why people were not typing in them. The cause is the one the review named: this sheet redeclares its own tokens, so it never saw --cds-field: #ffffff, even though theme-light.css has been supplying that to this page all along. Fields now consume --cds-field, and take the same --border-strong the creator's inputs already use, so a field looks like a field on both pages. No new value is introduced - both tokens already existed. That inverts a signal if left there, so it needed the other half: there was no disabled rule at all on this page, meaning locked fields would have turned white too. Disabled and readonly fields now take --cds-field-02, the theme's own secondary field surface, matching .locked-field in the creator. Enabled #ffffff against disabled #f4f4f4, verified by computed style rather than by eye. The border is deliberately the same on both states. I first wrote `border-color: var(--border)` on the disabled rule and could not demonstrate it taking effect - the rule matches, is more specific than the base rule, and its background applies, but the computed border stayed --border-strong. Rather than ship a declaration whose effect I cannot show, it is gone: a consistent border is what "consistent with inputs elsewhere" asks for, and the fill is what carries the state. Screenshot diff is limited to the wizard, but establishing that took a control run. admin and users appeared to change too, until capturing twice with NO code change showed they differ from themselves - the console pages render live timestamps and are not byte-stable. login, launcher, sop, creator and field are. Recorded in the baseline README so the next task with a "no layout change" done-when does not chase it. The F5 probe now also fails if enabled and disabled fields become identical, which is the way this fix could silently go wrong. f_items: F1-F5 FIXED, F6 untouched as wave 1 requires. browser_check 71/71. Co-Authored-By: Claude Opus 5 (1M context) --- docs/reference/baseline/README.md | 11 +++++++++++ html/work-package-suite-styles.css | 30 ++++++++++++++++++++++++++++-- tests/f_items.py | 29 ++++++++++++++++++++++------- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/docs/reference/baseline/README.md b/docs/reference/baseline/README.md index 9ded454..c8daeac 100644 --- a/docs/reference/baseline/README.md +++ b/docs/reference/baseline/README.md @@ -31,6 +31,17 @@ python tests/baseline_shots.py --out /tmp/after --label after declares `width=device-width`, so this is the layout a field tablet actually gets. The script asserts the width it asked for is the width the page saw. +### Two pages are not byte-stable — do not diff them blindly + +Found at `T1.5`. Capturing twice with **no code change at all** produces different bytes for +`admin` and `users` at both widths. `login`, `launcher`, `sop`, `creator` and `field` are +stable. The console pages render live timestamps, so a byte comparison of them reports a +change on every run. + +A task whose done-when is "no layout change at 1440px" therefore cannot use a byte diff on +those two. Run the capture twice before drawing any conclusion, or compare a page that is +stable. Four of the "changes" in this document's own history were this, not code. + ## F1–F6: all six reproduce Measured in a browser by `tests/f_items.py`, not read from source. Re-run any time: diff --git a/html/work-package-suite-styles.css b/html/work-package-suite-styles.css index 6199e45..f6a9718 100644 --- a/html/work-package-suite-styles.css +++ b/html/work-package-suite-styles.css @@ -332,19 +332,45 @@ body.embed-full { overflow: hidden; } color: var(--text); } +/* INTERIM (F5/A3). T3.4 removes the duplicate token underneath this; the fix here + is only to stop editable fields looking disabled. + These were filled with var(--bg) — this sheet's PAGE BACKGROUND, #f4f4f4 — on a + #e0e0e0 border, so an empty required field was indistinguishable from a locked + one and users did not type in them. The wizard redeclares its own tokens and so + never saw --cds-field: #ffffff, which theme-light.css has been supplying to this + page all along. Consume that instead of the local override, and take the same + --border-strong the creator's inputs use, so a field looks like a field in both + places. No new value is introduced here. */ .field input, .field select, .field textarea { padding: 0.75rem; - border: 1px solid var(--border); + border: 1px solid var(--border-strong); border-radius: 0; font-size: 14px; font-family: inherit; color: var(--text); - background: var(--bg); + background: var(--cds-field, var(--bg-card)); transition: border-color 0.2s; } +/* Now that editable fields are white, genuinely locked ones need to say so — there + was no disabled rule at all, so they would have gone white too and the signal + would have inverted rather than been fixed. --cds-field-02 is the theme's own + secondary field surface, and matches .locked-field in the creator. + The border is deliberately left alone: the same --border-strong on both states + is what makes a field read as a field, and the fill is what carries locked vs + editable. Verified as #ffffff vs #f4f4f4 against an identical border. */ +.field input:disabled, +.field select:disabled, +.field textarea:disabled, +.field input[readonly], +.field textarea[readonly] { + background: var(--cds-field-02, var(--bg)); + color: var(--text-light); + cursor: not-allowed; +} + .field input:focus, .field select:focus, .field textarea:focus { diff --git a/tests/f_items.py b/tests/f_items.py index 3f0624c..e19dc93 100644 --- a/tests/f_items.py +++ b/tests/f_items.py @@ -379,16 +379,23 @@ def f5(page, base, tok): wait_for="!!document.querySelector('.field input, .field select')") time.sleep(0.9) got = page.eval("""(function(){ - var out=[], n=0; + var out=[], n=0, dis=[]; document.querySelectorAll('.field input,.field select,.field textarea').forEach(function(e){ - if(e.disabled||e.readOnly||e.type==='hidden'||e.type==='checkbox'||e.type==='radio') return; + if(e.type==='hidden'||e.type==='checkbox'||e.type==='radio') return; var r=e.getBoundingClientRect(); if(r.width===0&&r.height===0) return; - n++; - var s=getComputedStyle(e); - out.push(s.backgroundColor+' | '+s.borderTopColor); + var s=getComputedStyle(e), sig=s.backgroundColor+' | '+s.borderTopColor; + if(e.disabled||e.readOnly){ dis.push(sig); return; } + n++; out.push(sig); }); var tally={}; out.forEach(function(v){tally[v]=(tally[v]||0)+1;}); - return JSON.stringify({n:n,tally:tally});})()""") + var dtally={}; dis.forEach(function(v){dtally[v]=(dtally[v]||0)+1;}); + // Prove the disabled rule works even when the page has no disabled field to + // sample: borrow a real one, flip it, measure, put it back. + var probe=document.querySelector('.field input:not([type=hidden])'), forced=null; + if(probe){ var was=probe.disabled; probe.disabled=true; + var ps=getComputedStyle(probe); + forced=ps.backgroundColor+' | '+ps.borderTopColor; probe.disabled=was; } + return JSON.stringify({n:n,tally:tally,dis:dtally,forced:forced});})()""") m = json.loads(got) if not m["n"]: return report("F5", UNKNOWN, "no enabled wizard inputs found") @@ -398,7 +405,15 @@ def f5(page, base, tok): if greyed: return report("F5", REPRO, f"{greyed}/{m['n']} enabled inputs fill #f4f4f4 (most common: {top[0]})") - return report("F5", FIXED, f"{m['n']} enabled inputs, none grey-filled (most common: {top[0]})") + # An enabled field that looks exactly like a disabled one is the same defect + # inverted, so this must not silently pass. + if m["forced"] and m["forced"] == top[0]: + return report("F5", REPRO, + f"enabled and disabled fields are identical ({top[0]}) - " + f"locked fields no longer read as locked") + extra = f", disabled reads {m['forced']}" if m["forced"] else "" + return report("F5", FIXED, + f"{m['n']} enabled inputs at {top[0]}{extra}") # ── F6 ────────────────────────────────────────────────────────────────────────