Skip to content

Improve matching component look and feel - #1323

Open
rfd131 wants to merge 2 commits into
RunestoneInteractive:mainfrom
rfd131:improve-matching-ui
Open

Improve matching component look and feel#1323
rfd131 wants to merge 2 commits into
RunestoneInteractive:mainfrom
rfd131:improve-matching-ui

Conversation

@rfd131

@rfd131 rfd131 commented Jul 26, 2026

Copy link
Copy Markdown

What this does

Enhances the look and feel of the matching component and addresses several interaction bugs found along the way. No changes to grading logic, event logging semantics, or the stored answer format.

Visual changes

  • Curved connections with visible ports. Lines are now gentle S-curves (cubic beziers with rounded caps) instead of straight <line> elements, and each box shows a small port dot on its inner edge, making the "draw a line from here to there" affordance discoverable.
  • Tighter layout. The workspace is capped at 780px so the two columns sit near each other instead of being pinned to opposite edges of a 1000px area. Boxes in a column share a uniform width; premises are left-aligned (sentences read better ragged-right), responses stay centered.
  • Lighter box styling. Theme-variable background, 1px border, 8px radius, subtle shadow, and hover states — replacing the gray fill / heavy black border look.
  • Grading feedback on the boxes. After Check Me, the boxes at each end of a connection are tinted green/red (via the existing parsons theme variables) in addition to the line coloring, so results are legible at a glance.
  • Readable score panel. The Score/Correct/Incorrect/Missing debug-style block is now a score badge plus a one-line "N correct · N incorrect · N missing" summary. The feedback block only renders when the author actually supplied feedback text (previously an empty red band appeared for exercises with <feedback></feedback>).
  • Empty state. The connections panel shows "No connections yet. Drag between boxes to connect them." instead of a bare Connections: header.
  • Dark mode verified against both :root.dark-mode and [data-theme="dark"] variable sets.

Bug fixes

  • Releasing a drag over empty space threw an uncaught TypeError (the old code dereferenced endBox.dataset unconditionally). Because the throw happened before cleanup, the document-level pointermove/pointerup listeners also leaked. Now guarded, and cleanup always runs.
  • matching_connection events were logged even when no connection was made (release on empty space, duplicate connection, same-column attempt). createPermanentLine now returns a boolean and the event is logged only on success.
  • Connecting two boxes in the same column popped a blocking alert(). It now flashes the boxes briefly and announces the problem via the existing aria-live region.
  • Stale correct/incorrect marks (lines and boxes) are cleared whenever the user changes their connections after grading, instead of lingering next to the new answer.
  • Removed a leftover console.log, the always-true if (e.ctrlKey || e.metaKey || true) wrapper, and CSS that leaked outside the component (bare button, body, h2, and * selectors — the button rules were restyling unrelated buttons on the page).

Screenshots

book-before book-after

Testing

  • timed_matching.js only touches connList.style.display, unaffected by the refactor.

Visual redesign of the matching component:
- Draw connections as S-curved bezier paths with rounded caps instead of
  straight lines, and mark the attachment points with port dots on the
  inner edge of each box so the connect-a-line affordance is visible.
- Tighten the workspace (780px) so the two columns sit closer together;
  boxes in a column share a uniform width and premises are left-aligned.
- Softer box styling (theme background, 1px border, radius, subtle
  shadow) with hover states, replacing the gray heavy-border look.
- Show graded results on the boxes themselves (green/red fill via the
  parsons theme variables) in addition to coloring the lines.
- Replace the debug-style score readout with a score badge plus a
  'N correct / N incorrect / N missing' summary line, and only render
  the feedback block when the author actually provided feedback text.
- Give the empty connections panel a helpful hint instead of a bare
  'Connections:' header.

Interaction fixes made along the way:
- Releasing a drag on empty space no longer throws (the old code
  dereferenced endBox.dataset unconditionally), which also leaked the
  document-level pointer listeners it failed to remove.
- matching_connection events are only logged when a connection is
  actually created, and the leftover console.log is gone.
- Connecting two boxes in the same column flashes the boxes and reports
  the problem via the aria-live region instead of a blocking alert().
- Stale correct/incorrect marks are cleared when the user changes their
  connections after grading.
- Removed the always-true (e.ctrlKey || e.metaKey || true) condition and
  the CSS rules that leaked out of the component (bare button, body, h2,
  and * selectors).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rfd131
rfd131 marked this pull request as ready for review July 26, 2026 14:15
@rfd131
rfd131 requested a review from bnmnetp as a code owner July 26, 2026 14:15
@bnmnetp
bnmnetp requested a review from Copilot July 26, 2026 14:17
@bnmnetp

bnmnetp commented Jul 26, 2026

Copy link
Copy Markdown
Member

Thanks for this! I'll check it out today or tomorrow.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refreshes the Runestone matching component’s UI (curved connector paths, ports, layout, and results display) while also addressing several interaction/logging bugs during connection creation and grading feedback rendering.

Changes:

  • Switch connector rendering from straight SVG <line> elements to curved <path> beziers, with updated styling and a “temp” drag state.
  • Improve UX and accessibility: visible ports, empty-state messaging, non-blocking invalid-connection feedback (flash + aria-live), and a more compact score display.
  • Fix interaction issues: prevent logging “matching_connection” on failed/invalid connections and clear stale grading marks when connections change.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
bases/rsptx/interactives/runestone/matching/js/matching.js Refactors connection creation/rendering (paths + endpoints), improves invalid/empty states, adjusts grading visuals, and tightens event logging/cleanup.
bases/rsptx/interactives/runestone/matching/css/matching.css Restyles the component (scoped rules, ports, curved line styling, results badge, and control button scoping) and adds responsive tweaks.
Comments suppressed due to low confidence (1)

bases/rsptx/interactives/runestone/matching/js/matching.js:672

  • updateTempLine also hard-codes getRightBoxCenter(this.startBox), so the drag line continues to come from the wrong edge when starting from a right-column (role="drop") box. This should use the same port-selection logic as permanent lines (left edge for drop, right edge for drag).
        e.preventDefault();
        if (!this.startBox || !this.tempLine) return;
        const from = this.getRightBoxCenter(this.startBox);
        const containerRect = this.workspace.getBoundingClientRect();
        const to = {
            x: e.clientX - containerRect.left,
            y: e.clientY - containerRect.top,
        };
        this.setLineEndpoints(this.tempLine, from, to);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +605 to +610
e.preventDefault();
this.startBox = box;
const from = this.getRightBoxCenter(this.startBox);
this.tempLine = this.createLineElement(from, from);
this.tempLine.classList.add("temp");
this.svg.appendChild(this.tempLine);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not due to your PR, it looks like it has been in place for a while. But it would be worth fixing.

@bnmnetp

bnmnetp commented Jul 26, 2026

Copy link
Copy Markdown
Member

If I select a box on the left, I have to tab through all of the connectors before I get to the boxes on the right. I wonder if we could position the next selected object to be the top box on the right after something is selected. If nothing is selected on the left then we should still tab through connectors as the user may want to delete one.

- Anchor the temporary drag line to the box's port edge (left edge for
  right-column boxes) via a new getPortCenter helper, instead of always
  using the right edge. The bezier control points now also pull toward
  the other endpoint, so a line dragged leftward curves correctly.
- When a box is selected with Enter, move focus to the top box of the
  opposite column so the user doesn't tab through the rest of the column
  and every connection line to reach a match; with nothing selected,
  natural tab order still visits the lines so they can be deleted.
  Escape cancels a selection and returns focus to the selected box.
- Update the help modal and aria-live announcements to describe the new
  keyboard flow.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@rfd131

rfd131 commented Jul 26, 2026

Copy link
Copy Markdown
Author

Thanks! Here are the updates:

The temp line now anchors to the port edge of whichever box the drag starts from.

For keyboard use: selecting a box with Enter now moves focus to the top of the opposite column, requiring fewer tabs; escape cancels a selection. Help screen also updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants