80 lines
2.9 KiB
Markdown
80 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.
|