Skip to content

Replace the pad QR card's size toggle with a resize handle (128px to 1024px) #465

Closed

Description

@HMarzban

Summary

The pad QR card (#377) has a size toggle. On hover a blurred veil covers the code, and a centred button doubles it from 128px to 256px. The maintainer asked to replace it:

"I want to remove that feature, and add another similar feature, the feature I want is the indicator that display (show up) when user mouse hover the qrcode and let the user to resize freely. so let's run team of UI and UX and see what is the best solution to have this feature. let the user to control the size of qrcode. just remember to add a minimum and maximume size."

This issue replaces the toggle with one resize handle on the card's bottom-left corner. It changes the size part of the #377 rulings of 2026-10-05. The position, room and chat-bound rulings stay. The maintainer ruling of 2026-10-07 settles the handle look, hover, range, click and reset.

Where

Paths are under apps/webapp/src/ unless shown in full.

  • apps/webapp/src/components/pages/document/components/PadQrCode.tsx (52 lines):
    • :17-18: isLarge state.
    • :34-37: w-32 or w-[clamp(8rem,calc(100cqh-5.25rem),16rem)], with a --motion-panel width tween.
    • :40-48: the data-testid="pad-qr-size" button with aria-pressed and <QrVeil>.
    • :24-27: the size container above --chat-panel-height, the top-3.5 / Find placement, and the 13.5rem show gate. These stay.
  • apps/webapp/src/components/ui/QrVeil.tsx stays. The Share dialog's Present button uses it (components/TipTap/pad-title-section/PresentQrCode.tsx:75-77).
  • apps/webapp/src/components/icons/registry.ts:68 (the LuMinimize2 import) and :197 (minimize2): used only by this card, so both go. maximize2 stays, because chat media uses it.
  • apps/webapp/cypress/e2e/document/pad-qr-toggle.cy.ts:87-103 tests the toggle.
  • Precedents to copy, not import:
    • components/chatroom/hooks/useChatPaneGrabber.ts:35-66: element-local pointer handlers with setPointerCapture.
    • components/pages/document/components/chat/ChatPane.tsx:21-45: role="slider" with Arrow, Home and End keys.

Design

Remove. Delete the veil button, isLarge, the QrVeil import and Icons.minimize2. At rest nothing covers the code, as today. The card body has no click action.

Handle

  • One handle on the bottom-left corner. The card is pinned top-right, so it grows down and left. The code stays square.

  • Put it inside the pad-qr-code wrapper, as a sibling of the role="img" panel. The panel is overflow-hidden, so a child would be clipped.

  • The hit area is at least 24×24px (WCAG 2.5.8), centred on the corner. It is transparent, so it may lie over modules without hurting a scan.

  • The visible grip (option A, ruled 2026-10-07) is two short parallel diagonal strokes in --qr-ink, like a mirrored text-area resize grip. Draw it as an inline aria-hidden <svg> in PadQrCode.tsx; add no Icons entry. The strokes stay inside the white quiet zone and inside the panel's rounded-box corner, and never touch a QR module. At 128px the quiet zone is about 12.5px (read from a mock, not measured).

  • Shown with opacity, never display: none, so keyboard focus can reveal it:

    • when the card is hovered;
    • when the handle has focus;
    • during a drag.

    Hover is a deliberate exception to the parity rule (ruling 2026-10-07): the card cannot be selected. Fade with --motion-overlay-in and motion-reduce:transition-none.

  • Cursor: nesw-resize. During a drag, the document body also gets that cursor and user-select: none, as the TOC sash does.

Drag

  • On a primary-button pointerdown: preventDefault(), setPointerCapture, and record the start point and the start size (the painted size, see Bounds). The handle has touch-action: none.
  • On move: dx = startX - clientX and dy = clientY - startY. delta is whichever has the larger absolute value, with its sign, so a drag right or up alone also shrinks. Then size = clamp(MIN, start + delta, cap), rounded to whole px.
  • Resize live. Turn the width tween off during the drag.
  • pointerup and pointercancel both end the drag, as the chat grabber does (ChatPane.tsx:32-33). Clear the body cursor and user-select on drag end and on unmount, as the TOC sash does (components/pages/document/hooks/useTocResize.tsx:110-113, :298). A Private seal or a pad switch can unmount the card mid-drag.

No-drag paths (WCAG 2.5.7 and 2.1.1)

  • Click. A press is a click when neither |dx| nor |dy| reaches 8px, as in the chat grabber (TAP_PX, useChatPaneGrabber.ts:12, :55). Decide it in the pointerup handler, as the grabber does (:75-78), with no onClick. When the painted size is MIN, it goes to the cap. Otherwise it goes back to MIN.
  • Keys. role="slider", tabIndex={0}, aria-label="Resize QR code", aria-valuemin={MIN}, aria-valuemax={MAX}, aria-valuenow, aria-valuetext="{n} pixels".
    • ArrowUp and ArrowRight grow by 16px. ArrowDown and ArrowLeft shrink by 16px.
    • Home goes to MIN, End goes to the cap.
    • Keys and clicks keep the --motion-panel width tween (motion-safe).
  • Focus ring: focus-visible:ring-2 focus-visible:ring-primary focus-visible:outline-none.

Bounds

  • MIN = 128 (8rem), the default and today's size. MAX = 636, ruled by the maintainer. Both are module constants in PadQrCode.tsx.
  • cap = Math.min(MAX, container.clientHeight - 84). container is a ref on the size container (:24); 84px is 5.25rem, so say so in a comment. Read it on each pointerdown, key press and click. Drag, keys and click start from the painted size, Math.min(size, cap).
  • QrCode takes no style prop, so set the width through a CSS variable. Put style={{ '--pad-qr-size': ${size}px } as React.CSSProperties} on the role="img" panel, and give QrCode the class w-[min(var(--pad-qr-size),calc(100cqh-5.25rem))]. Add no prop to QrCode. The old 16rem ceiling goes away. When docked chat grows or Find opens, the painted code shrinks; when room returns, it grows back to the chosen size.
  • At large sizes the card may cover the page (ruling 2026-10-07). Do not add a width gate.
  • The 13.5rem show gate is unchanged. If MIN ever changes, the gate moves with it.

State. One useState<number> in the card. Hiding the card unmounts it, so the size resets to 128, as #377 ruled. No store and no localStorage. All of it stays in PadQrCode.tsx: no new hook, no ui/ file and no export. Do not reuse ui/ResizeHandle.tsx; it is a mouse-only edge sash with role="separator" and no value.

Out of scope

  • The Share dialog's QR and Present view.
  • Persisting the size.
  • Changing the toolbar toggle (components/TipTap/toolbar/desktop/EditorToolbar.tsx:340-348) or the card position.

Acceptance criteria

  • At rest nothing covers the code. Hover or keyboard focus shows the handle on the bottom-left corner. The veil button is gone.
  • A drag resizes the code live and keeps it square. It stops at 128px and at 636px, or earlier at the room cap above docked chat. A drag right or up alone shrinks it.
  • A drag does not select text in the editor and does not move the caret.
  • A click on the handle switches between 128px and the cap.
  • Tab reaches the handle. Arrow keys step by 16px, and Home and End go to the limits. VoiceOver in Chrome on macOS reads the handle as "Resize QR code", a slider, with the value "128 pixels".
  • The handle and its focus ring are visible in light, dark and docsplus-dark-hc.
  • At 128px, with a 30-character slug, the grip strokes stay inside the white quiet zone and touch no QR module.
  • Opening docked chat or Find shrinks a large code to fit, and closing them restores it.
  • Hiding and showing the card resets it to 128px.
  • In pad-qr-toggle.cy.ts, the size block (:87-103) is replaced, and the test title at :77 no longer says "doubles on demand". At the spec's 1680×1050 viewport: hover shows the handle; realMouseDown, realMouseMove and realMouseUp drag it down and left past the limit, and the code width reaches min(636, cap); one realClick on the handle returns it to 128px. It is observed green locally (the spec is local-only). Keys stay a browser check.
  • apps/webapp/CLAUDE.md §Pad Workspace Surfaces ("Pad QR card") and the [Unreleased] QR bullet in apps/webapp/CHANGELOG.md describe the handle.
  • .cursor/docs/design-system.md §ShareModal present tag (line 954 today) drops the sentence "The pad QR card (Show a QR code for the pad from a desktop toolbar toggle #377) reuses the same QrVeil with its size icon." The QR code row (:953) stays as it is: the Share card passes quietZone={2} (PresentQrCode.tsx:76). The same section records the pad card's grip: two diagonal --qr-ink strokes in the bottom-left quiet zone, shown on hover, focus and drag. That file is locked, so the lock hook asks the maintainer.

Activity

  1. HMarzban commented on Oct 7, 2026

    @HMarzban
    CollaboratorAuthor

    Maintainer ruling, 2026-10-07

    This changes the size part of the #377 rulings of 2026-10-05.

    • Handle: one handle on the bottom-left corner, option A: a diagonal corner grip in the QR ink, inside the white quiet zone.
    • Show on hover and on keyboard focus. This is a deliberate exception to the parity rule: Notion shows handles on hover, while Google Docs and Word show them after a click selects the object. The card cannot be selected.
    • Size range: 128px to 636px. The room cap above docked chat still applies, and the card may cover the page.
    • A click on the handle switches between 128px and the cap.
    • Not remembered: hiding the card resets it to 128px.
    • Design system: the two edits to .cursor/docs/design-system.md are approved.
  2. HMarzban commented on Oct 12, 2026

    @HMarzban
    CollaboratorAuthor

    Shipped, 2026-10-10

    Deployed in run 38068638114 (e5e210d02, green).

    • 8ac7a9089: the size toggle is replaced by a corner resize handle.
    • 03ea0648a: maintainer ruling. The largest size is now 1024px, not 636px, so the room is the limit. The margin is a --qr-plate padding of clamp(12px, 4% of size, 24px).
    • 228d96114: the drag state is React state. The body cursor and text selection are cleared on drag end and on unmount.

    cypress/e2e/document/pad-qr-toggle.cy.ts passes 4/4. It was proven by sabotage: a 636px cap fails it. The title now says 1024px.

  3. changed the title [-]Replace the pad QR card's size toggle with a resize handle (128px to 636px)[/-] [+]Replace the pad QR card's size toggle with a resize handle (128px to 1024px)[/+] on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions