deckhearth/.convoys/scanner-desktop-layout/brief-2-scanner-tips.md
varutasu 938c161a26
feat(scanner): add desktop workstation layout (#165)
Give /scanner a md+ camera, live match inspector, and history strip
(with device picker, batch scan, and tips) without regressing the
mobile immersive checkout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 17:21:23 -05:00

79 lines
2.9 KiB
Markdown

---
convoy: scanner-desktop-layout
brief_number: 2
depends_on: []
recommended_model: composer-2.5-fast
model_tier: fast
files:
- components/scanner/ScannerTips.js
- test/components/ScannerTips.test.js
cross_brief_commitments:
- brief: 6
description: |
Brief 6 mounts `<ScannerTips />` in the desktop page header row (md+ only)
beside "Card Scanner" title. This brief ships the self-contained modal +
trigger button; no page wiring here.
---
# Brief 2: Scanner Tips modal
## Goal (1 sentence)
Ship a desktop Scanner Tips control that opens a glass `<Modal>` with five convoy-authored tips.
## Files in scope (do not edit anything else)
- `components/scanner/ScannerTips.js`
- `test/components/ScannerTips.test.js`
## Conventions to follow
- Use `Modal` + `Button` from `components/ui` — no custom scrim (`ui-and-theming.mdc`).
- Glass panel recipe: `--glass-surface-low` via `GlassSurface` inside modal body if needed.
- Copy locked (Design direction + IA):
| # | Tip |
| --- | --- |
| 1 | **Good lighting** — avoid glare on foil cards. |
| 2 | **Fill the frame** with one card; keep corners visible. |
| 3 | **Hold still** until Auto-detect locks the match. |
| 4 | **Use Batch Scan** for a pile of photos from your gallery. |
| 5 | **Switch camera** if the image is dark or mirrored. |
- Modal title: **Scanner Tips**. Trigger: ghost `Button` with lightbulb SVG + label **Scanner Tips** (visible text for 2.5.3 Label in Name).
- Dismiss: close control, Esc, scrim click — standard `<Modal>` behavior.
- No hex in `.js`; tokens only.
- Test pattern: `test/components/ScannerCheckoutSheet.test.js` (vitest + jsdom + RTL).
## Implementation shape
```js
export default function ScannerTips({ className = '' }) {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="ghost" onClick={() => setOpen(true)} /* lightbulb + Scanner Tips */ />
<Modal open={open} onClose={() => setOpen(false)} title="Scanner Tips" size="md">
<ol className="space-y-4 text-sm" style={{ color: 'var(--text-secondary)' }}>
{/* five <li> with <strong> lead + em dash body */}
</ol>
</Modal>
</>
);
}
```
Component is self-contained — no props required v1.
## Acceptance criteria
- [ ] Trigger shows lightbulb icon + **Scanner Tips** visible label
- [ ] Modal lists exactly five tips with locked copy
- [ ] Uses `<Modal>` primitive (no `fixed inset-0 bg-black` shell)
- [ ] Opening/closing traps focus per existing Modal behavior
- [ ] tests: trigger opens modal, all five tips visible, close dismisses
- [ ] no scope expansion (do not edit files outside `files:` above)
## Rationale (≤3 sentences)
Tips are a standalone surface (C3) with no queue or camera dependencies. Shipping the modal in isolation lets Brief 6 wire it into the header without blocking other parallel work. Five tips exceed popover length — Modal is Design-locked.