fix: a click target wearing a drag affordance made the controls look dead
Some checks failed
ci / check (push) Failing after 4s
Some checks failed
ci / check (push) Failing after 4s
Tier S (a fix inside a boundary; chaos d8=7 from the previous roll stands
for this continuation). Two observations from play that are ONE defect.
`play again`, `end session`, `pass` and the move buttons carried `.pick`,
which is cursor:grab. The stylesheet has .btn{cursor:pointer} BEFORE
.pick{cursor:grab}, so grab won.
A GRAB CURSOR INVITES A DRAG. A drag released over nothing posts nothing,
so the player picked up the button, let go, and the page did nothing. It
looked dead because the affordance told them to do the one thing that does
not work. Reported as two separate things -- "the button shows a hand to
pick up that it probably shouldn't" and "I can't start another game or
stop the server" -- and the first causes the second.
The click path itself was never broken: driving again->again and
done->done through the JS harness posts correctly. The logic was fine and
the invitation was wrong.
Click targets now carry `.tap` -- pointer cursor, same press affordance.
This extends CB-WP-0017's rule (interactive and inert must not look
identical) to: click and drag must not look identical either. The test
asserts both directions, because checking only that buttons lost `.pick`
would pass for a page with no affordances at all.
Registered F20 (applied) and F21.
F21 IS THE ONE I COULD NOT REPRODUCE: dragging did not work until after
the first note was saved. Ruled out the plausible mechanisms -- the
gesture logic posts correctly against the served page, the drag ghost
carries pointer-events:none so it cannot intercept the drop, and the
markup is identical before and after since the 303 re-renders the same
page from the same state. Remaining candidates are a <details> toggle
shifting layout mid-drag, a first-load timing difference, or browser-level
pointer capture. Reproducing it needs a browser, which no test here has --
the same gap F19 named. Recorded as unreproduced rather than given a
speculative fix.
And the fourth observation is confirmation, not a bug: "drawing my cards
from the deck is not implemented, I did not need to do that" is exactly
what CB-WP-0028 T04 determined and deliberately did not build. It is the
first evidence that importing the card text closed the comprehension gap
that produced the earlier click-the-deck request.
make all: exit 0. 62 render tests, 26 cb-play.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a6e7acd4cf
commit
b12725566b
5 changed files with 252 additions and 4 deletions
|
|
@ -220,6 +220,15 @@ h1,h2{font-size:1rem;margin:1.2rem 0 .4rem;color:#9cf}
|
|||
.pick{cursor:grab;user-select:none;box-shadow:0 2px 0 #0006,0 0 0 1px #5a7a inset;
|
||||
transition:transform .08s,box-shadow .08s}
|
||||
.pick:hover{box-shadow:0 3px 8px #000a,0 0 0 1px #7ca inset;transform:translateY(-1px)}
|
||||
/* CB-WP-0017's principle, one step further: a thing you CLICK and a thing
|
||||
you DRAG must not look identical either. `.pick` promises carry-me-
|
||||
somewhere; these promise press-me. They wore `.pick` and therefore a
|
||||
grab cursor, which invited a drag -- and a drag released over nothing
|
||||
posts nothing, so the button appeared dead. Reported both as the button
|
||||
showing a hand it should not, AND as being unable to start a new game. */
|
||||
.tap{cursor:pointer;user-select:none;box-shadow:0 2px 0 #0006,0 0 0 1px #a7d6 inset;
|
||||
transition:transform .08s,box-shadow .08s}
|
||||
.tap:hover{box-shadow:0 3px 8px #000a,0 0 0 1px #a7d inset;transform:translateY(-1px)}
|
||||
/* A legal destination for the thing currently held -- and ONLY for that
|
||||
thing. The set is written by Rust into data-targets; the script matches
|
||||
it and never derives it (ADR-0010 D1).
|
||||
|
|
@ -1062,7 +1071,7 @@ fn move_section(
|
|||
if !spatial {
|
||||
let _ = write!(
|
||||
s,
|
||||
"<div class=\"card btn pick\" data-drop=\"cmd-{i}\">{}</div>",
|
||||
"<div class=\"card btn tap\" data-drop=\"cmd-{i}\">{}</div>",
|
||||
esc(&format!("{c:?}"))
|
||||
);
|
||||
}
|
||||
|
|
@ -1075,7 +1084,7 @@ fn move_section(
|
|||
}
|
||||
if may_pass {
|
||||
s.push_str(
|
||||
"<div class=\"card btn pick\" data-drop=\"pass\">pass \u{2014} decline to act</div>",
|
||||
"<div class=\"card btn tap\" data-drop=\"pass\">pass \u{2014} decline to act</div>",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1336,8 +1345,8 @@ pub fn ending(
|
|||
let _ = write!(
|
||||
s,
|
||||
"<div class=\"row\">\
|
||||
<div class=\"card btn pick\" data-drop=\"again\">play again</div>\
|
||||
<div class=\"card btn pick\" data-drop=\"done\">end session \u{2014} stops the game server</div>\
|
||||
<div class=\"card btn tap\" data-drop=\"again\">play again</div>\
|
||||
<div class=\"card btn tap\" data-drop=\"done\">end session \u{2014} stops the game server</div>\
|
||||
</div>",
|
||||
);
|
||||
log_section(&mut s, log);
|
||||
|
|
|
|||
|
|
@ -1275,6 +1275,8 @@ mod played_cards {
|
|||
/// table and a `play again` pointing at a closed port.
|
||||
#[cfg(test)]
|
||||
mod ending_page {
|
||||
use cb_kernel::PlayerId;
|
||||
|
||||
use crate::{doc, jsrun};
|
||||
|
||||
fn page() -> String {
|
||||
|
|
@ -1326,6 +1328,53 @@ mod ending_page {
|
|||
);
|
||||
}
|
||||
|
||||
/// **A thing you click must not look like a thing you drag.**
|
||||
///
|
||||
/// Reported two ways at once: *"the button shows a hand to pick up
|
||||
/// that it probably shouldn't"* and *"I can't start another game"*.
|
||||
/// They are one defect — a grab cursor invites a drag, and a drag
|
||||
/// released over nothing posts nothing, so the button looks dead.
|
||||
#[test]
|
||||
fn click_targets_do_not_wear_the_drag_affordance() {
|
||||
let pages = [
|
||||
doc::ending(None, "m", "/command?t=x", &[], &[]),
|
||||
doc::document(
|
||||
&crate::testfix::view(Some(PlayerId(0))),
|
||||
&[games_ground::GroundCommand::SelectAction {
|
||||
action: games_ground::Action::Investigate,
|
||||
target: None,
|
||||
problem: Some(2),
|
||||
}],
|
||||
"/command?t=x",
|
||||
Some(PlayerId(0)),
|
||||
true,
|
||||
),
|
||||
];
|
||||
for html in &pages {
|
||||
for key in ["again", "done", "pass"] {
|
||||
let Some(i) = html.find(&format!("data-drop=\"{key}\"")) else {
|
||||
continue;
|
||||
};
|
||||
let tag = &html[html[..i].rfind('<').expect("an opening tag")..i];
|
||||
assert!(
|
||||
!tag.contains("pick"),
|
||||
"`{key}` is a click target wearing `.pick`, which is cursor:grab — \
|
||||
it invites a drag, and a drag onto nothing posts nothing"
|
||||
);
|
||||
assert!(
|
||||
tag.contains("tap"),
|
||||
"`{key}` must still look pressable: {tag}"
|
||||
);
|
||||
}
|
||||
}
|
||||
// The inverse, or this passes for a page with no affordances at
|
||||
// all: a real draggable still carries `.pick`.
|
||||
assert!(
|
||||
pages[1].contains("class=\"card act pick\""),
|
||||
"action cards must still be draggable"
|
||||
);
|
||||
}
|
||||
|
||||
/// **CB-WP-0028 T06, asserted both ways.** A test that only checked
|
||||
/// the win case would pass for a page that always says "solved".
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue