@ericrisco/rsc 3.3.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/manifest.json +43 -22
- package/package.json +1 -1
- package/scripts/lib/default-skill-floor.js +5 -1
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/retired-skills.js +4 -0
- package/skills/design/SKILL.md +4 -3
- package/skills/design/evals/cases.yaml +2 -2
- package/skills/react/SKILL.md +2 -1
- package/skills/ux/SKILL.md +149 -0
- package/skills/ux/THIRD_PARTY.md +32 -0
- package/skills/ux/evals/cases.yaml +40 -0
- package/skills/ux/references/flows.md +124 -0
- package/skills/ux/upstream/UPSTREAM.json +59 -0
- package/skills/ux/upstream/ux-designer/GUIDE.md +180 -0
- package/skills/ux/upstream/ux-designer/LICENSE +21 -0
- package/skills/ux/upstream/ux-designer/references/01-core-principles.md +251 -0
- package/skills/ux/upstream/ux-designer/references/02-laws-of-ux.md +330 -0
- package/skills/ux/upstream/ux-designer/references/03-accessibility.md +411 -0
- package/skills/ux/upstream/ux-designer/references/04-visual-design.md +433 -0
- package/skills/ux/upstream/ux-designer/references/05-information-architecture.md +432 -0
- package/skills/ux/upstream/ux-designer/references/06-interaction-design.md +495 -0
- package/skills/ux/upstream/ux-designer/references/07-forms-and-inputs.md +491 -0
- package/skills/ux/upstream/ux-designer/references/08-mobile-ux.md +539 -0
- package/skills/ux/upstream/ux-designer/references/09-ux-writing.md +493 -0
- package/skills/ux/upstream/ux-designer/references/10-user-research.md +533 -0
- package/skills/ux/upstream/ux-designer/references/11-design-systems.md +618 -0
- package/skills/ux/upstream/ux-designer/references/12a-presence-awareness.md +395 -0
- package/skills/ux/upstream/ux-designer/references/12b-conflict-resolution-sync.md +420 -0
- package/skills/ux/upstream/ux-designer/references/13a-canvas-navigation.md +388 -0
- package/skills/ux/upstream/ux-designer/references/13b-canvas-objects-performance.md +559 -0
- package/skills/ux/upstream/ux-designer/references/14-ai-ux-patterns.md +511 -0
- package/skills/ux/upstream/ux-designer/references/15-ethical-design.md +419 -0
- package/skills/ux/upstream/ux-designer/references/16-onboarding.md +430 -0
- package/skills/ux/upstream/ux-designer/references/17-notifications.md +461 -0
- package/skills/ux/upstream/ux-designer/references/18-data-visualization.md +453 -0
- package/skills/ux/upstream/ux-designer/references/19-search-ux.md +479 -0
- package/skills/ux/upstream/ux-designer/references/20-emotional-design.md +382 -0
- package/skills/ux/upstream/ux-designer/references/21-data-tables.md +629 -0
- package/skills/ux/upstream/ux-designer/references/22-performance-ux.md +626 -0
- package/skills/ux/upstream/ux-designer/references/23-internationalization.md +347 -0
- package/skills/ux/upstream/ux-designer/references/24-voice-and-multimodal.md +308 -0
- package/skills/ux/upstream/ux-designer/references/25a-barcode-camera-scanning.md +342 -0
- package/skills/ux/upstream/ux-designer/references/25b-barcode-scanner-workflows.md +412 -0
- package/skills/ux/upstream/ux-designer/references/25c-barcode-display.md +277 -0
- package/skills/ux/upstream/ux-usability-foundations/GUIDE.md +285 -0
- package/skills/ux/upstream/ux-usability-foundations/LICENSE +21 -0
- package/skills/ux/upstream/ux-usability-foundations/references/anti-patterns.md +267 -0
- package/skills/ux/upstream/ux-usability-foundations/references/checklists.md +130 -0
- package/skills/ux/upstream/ux-usability-foundations/references/decision-prompts.md +340 -0
- package/skills/ux/upstream/ux-usability-foundations/references/principle-cards.md +449 -0
- package/targets/clone-bootstrap.mjs +1 -1
- package/skills/ui-engineering/SKILL.md +0 -113
- package/skills/ui-engineering/evals/cases.yaml +0 -38
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# Camera Barcode & QR Scanning
|
|
2
|
+
|
|
3
|
+
Scanning with a phone or laptop camera in consumer and prosumer apps: product lookup,
|
|
4
|
+
adding items, filling a form from a code, joining Wi-Fi, following a QR link, redeeming
|
|
5
|
+
a voucher. The camera is an **accelerator for typing, never the only path**. Most scanner
|
|
6
|
+
UX failures come from the states around the decode: permissions, aiming, feedback, and
|
|
7
|
+
what happens after a hit. The decode itself is rarely the problem.
|
|
8
|
+
|
|
9
|
+
> Dedicated scanner hardware and scan-driven work apps: [25b-barcode-scanner-workflows.md](25b-barcode-scanner-workflows.md).
|
|
10
|
+
> Showing a code for someone else to scan: [25c-barcode-display.md](25c-barcode-display.md).
|
|
11
|
+
> Symbology primer (in 25b), short version: enable only the formats you expect (QR for links,
|
|
12
|
+
> EAN/UPC for retail products, DataMatrix for pharma/GS1). Fewer formats scan faster and
|
|
13
|
+
> misread less. GS1 barcodes need parsing, and the app must verify check digits.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
- When to Offer Scanning
|
|
20
|
+
- Entry Points & System Scanners
|
|
21
|
+
- Camera Permission
|
|
22
|
+
- The Viewfinder
|
|
23
|
+
- Live States & Guidance
|
|
24
|
+
- Hard Conditions: Light, Focus, Zoom
|
|
25
|
+
- Multiple Codes & Continuous Scanning
|
|
26
|
+
- Feedback: Visual, Haptic, Sound
|
|
27
|
+
- After the Scan: Auto-Act vs. Confirm
|
|
28
|
+
- QR Safety (Quishing)
|
|
29
|
+
- Scanning from an Image
|
|
30
|
+
- Accessibility
|
|
31
|
+
- Web Specifics
|
|
32
|
+
- Platform Notes
|
|
33
|
+
- Quick Checklist
|
|
34
|
+
- Common Mistakes
|
|
35
|
+
- Sources
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## When to Offer Scanning
|
|
40
|
+
|
|
41
|
+
- Offer scanning where it replaces typing a long or error-prone value: product lookup, a
|
|
42
|
+
form field, event details, Wi-Fi, a contact (Material "Barcode scanning" pattern).
|
|
43
|
+
- **Always pair it with manual entry.** Scanning disappears when permission is denied, the
|
|
44
|
+
device has no camera (desktop web), the code is damaged, Google Play services are
|
|
45
|
+
missing, or the browser lacks support. Android's permission guidance says to promote
|
|
46
|
+
alternatives when a feature is unavailable.
|
|
47
|
+
- **Hide scan entry points on devices that can't scan** (iOS `DataScannerViewController.isSupported`
|
|
48
|
+
requires an A12 chip or later, and a desktop may have no camera).
|
|
49
|
+
- Don't ask people to scan a code shown **on the same phone**. Give them a link instead, or
|
|
50
|
+
scan-from-image (NN/g).
|
|
51
|
+
|
|
52
|
+
## Entry Points & System Scanners
|
|
53
|
+
|
|
54
|
+
- Put the entry point **where the value is needed**: a scan icon inside a search field, a
|
|
55
|
+
"Scan" button beside a form field. A dedicated tab only makes sense for scan-centric apps.
|
|
56
|
+
This also puts the permission request in context.
|
|
57
|
+
- **Where results go** (Material):
|
|
58
|
+
|
|
59
|
+
| Task | After a hit |
|
|
60
|
+
|---|---|
|
|
61
|
+
| Browse products in a store | Stay in the scanner, result in a bottom sheet over the camera |
|
|
62
|
+
| Add a contact / save something | Stay until saved, via a bottom sheet or full-screen dialog |
|
|
63
|
+
| Complete a form | Return to the form with the fields filled in |
|
|
64
|
+
|
|
65
|
+
- **You may not need a scanner at all.** The iOS Camera app, the Control Center "Scan Code"
|
|
66
|
+
control, the Android camera, the Quick Settings "Scan QR code" tile, and Google Lens all read
|
|
67
|
+
QR codes. If your codes are HTTPS URLs set up as Universal Links / App Links, the system
|
|
68
|
+
scanner opens your app directly.
|
|
69
|
+
- NCSC (UK) and the FBI advise consumers to use the phone's built-in scanner and not download
|
|
70
|
+
scanner apps. Don't make "download our app to scan" a prerequisite for a generic QR.
|
|
71
|
+
- First run: a one-screen animated hint showing how to hold the device, plus a Help entry
|
|
72
|
+
(angle, distance, light). Not a multi-step tour.
|
|
73
|
+
|
|
74
|
+
## Camera Permission
|
|
75
|
+
|
|
76
|
+
- Ask **when the user taps Scan**, never at launch (Apple HIG, Android).
|
|
77
|
+
- Purpose string: a specific, complete sentence ("Scan barcodes to add products to your
|
|
78
|
+
list"), not "needed for a better experience".
|
|
79
|
+
- **Pre-permission screens differ by platform.** Apple's HIG says a single "Continue"
|
|
80
|
+
button that leads to the system alert, with no cancel and no "Allow" label. Android says
|
|
81
|
+
to include a "No thanks" that keeps the app usable. Don't unify them.
|
|
82
|
+
- **Denied state** (Android guidance, which applies everywhere):
|
|
83
|
+
- Don't block the UI with a full-screen warning, and don't nag.
|
|
84
|
+
- Explain the loss where results would appear, and offer manual entry and "Choose from photos".
|
|
85
|
+
- After a permanent denial (Android: denied twice), deep-link to Settings.
|
|
86
|
+
- **Check before opening the camera:** iOS `isAvailable` is false when access is denied
|
|
87
|
+
or restricted (Screen Time). On the web, `navigator.permissions.query({name:'camera'})`
|
|
88
|
+
(Chrome, Safari 16+, Firefox 132+) lets you show recovery steps instead of failing.
|
|
89
|
+
- **Permission-free options:**
|
|
90
|
+
|
|
91
|
+
| Platform | Option | Trade-off |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| Android | **Google Code Scanner** (ML Kit): no camera permission; Google Play services runs the camera | No custom UI; needs GMS (not Huawei or de-Googled devices); module may download on first use. Has `allowManualInput` and `enableAutoZoom` options |
|
|
94
|
+
| Android / iOS | Photo Picker / PHPicker + still-image decode | Not live; no media permission |
|
|
95
|
+
| iOS | System Camera / Control Center handoff | Only for URL codes; no in-app live scanner without permission |
|
|
96
|
+
| Web | `<input type="file" accept="image/*" capture="environment">` | A still photo, not live; desktop shows a file picker |
|
|
97
|
+
|
|
98
|
+
## The Viewfinder
|
|
99
|
+
|
|
100
|
+
- **Full-bleed camera, controls on the edges.** Put exit, torch, and Help in a top bar with a
|
|
101
|
+
scrim, keep overlays translucent, and leave the centre clear (Material).
|
|
102
|
+
- **Frame shaped for the expected code:** wide for 1D, square for QR. A single-code frame
|
|
103
|
+
also tells the user that one code is read at a time.
|
|
104
|
+
- **Region of interest:** decode only inside the frame. A smaller region with fewer formats
|
|
105
|
+
processes faster (vendor docs; iOS `regionOfInterest`).
|
|
106
|
+
- **Size the frame so the lens can focus.** On Pro iPhones a small code that fills a big
|
|
107
|
+
frame pulls the phone inside the minimum focus distance (12–15 cm), and the image blurs.
|
|
108
|
+
Apple's fix (WWDC21) is to apply zoom automatically (`minimumFocusDistance`) so people
|
|
109
|
+
hold the phone farther away.
|
|
110
|
+
- Don't lock orientation. Decoders read codes at any rotation (ML Kit), and WCAG 1.3.4
|
|
111
|
+
applies on the web.
|
|
112
|
+
- **Inline or mini scanners** (a small camera preview docked over a list, as in self-checkout
|
|
113
|
+
or stock counting) keep the context visible. Full-screen suits one-off scans.
|
|
114
|
+
|
|
115
|
+
## Live States & Guidance
|
|
116
|
+
|
|
117
|
+
Material's pattern is **sense → recognize → communicate**:
|
|
118
|
+
|
|
119
|
+
| State | Frame | Text |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| Sensing | Pulsing border | "Point your camera at a barcode" |
|
|
122
|
+
| Too small / far | Partial border | "Move closer" (or "Move back", see focus above) |
|
|
123
|
+
| Detected | Border turns solid; **camera feed pauses** | — |
|
|
124
|
+
| Looking up | Progress indicator across the frame | "Searching…" |
|
|
125
|
+
| Error | Border turns dotted (not only red) | **Banner** with the reason and actions (Retry, Help) |
|
|
126
|
+
|
|
127
|
+
- **Pause the feed on detection** so the user doesn't scan a second code by accident.
|
|
128
|
+
- **Tooltips describe the state only.** No action verbs in non-actionable hints, and errors
|
|
129
|
+
never go in a tooltip (Material).
|
|
130
|
+
- **Debounce 1D misreads:** act only after the same value has decoded several frames in a
|
|
131
|
+
row (ML Kit).
|
|
132
|
+
- **Design the post-decode failures:** a code from an unrelated source ("not a product
|
|
133
|
+
code"), no network during lookup, a product not in the database. Each needs manual
|
|
134
|
+
search and a retry.
|
|
135
|
+
- **No-detection hint:** no primary source gives a timing. Show help ("Add light", "Hold
|
|
136
|
+
steady", "Try manual entry") after roughly 5–10 s without a detection, as a heuristic.
|
|
137
|
+
- **Idle camera:** pausing after ~8–10 s of inactivity saves battery (vendor advice). Pause
|
|
138
|
+
with a one-tap "Resume scanning" and never discard state (WCAG 2.2.1 on the web).
|
|
139
|
+
|
|
140
|
+
## Hard Conditions: Light, Focus, Zoom
|
|
141
|
+
|
|
142
|
+
- **Torch toggle** is always in the top bar when the camera supports it. Hide it otherwise
|
|
143
|
+
(desktop webcams, Firefox): check `track.getCapabilities().torch`.
|
|
144
|
+
- **Help copy** (Google Pixel's wording): hold steady, add light, avoid glare, clean the lens,
|
|
145
|
+
keep the whole code visible. For **glare on curved or shiny packaging**: tilt or rotate the
|
|
146
|
+
item.
|
|
147
|
+
- **Tiny codes:** use a higher-accuracy mode (VisionKit `qualityLevel = .accurate`) and zoom.
|
|
148
|
+
For small or dense codes, use camera input of 1280×720–1920×1080. ML Kit needs the
|
|
149
|
+
smallest module to cover about 2 camera pixels or more, and an EAN-13 about 190 px wide.
|
|
150
|
+
- **Zoom:** VisionKit supports pinch-to-zoom by default, and Google Code Scanner and ML Kit
|
|
151
|
+
offer auto-zoom for distant codes. If you implement pinch zoom yourself, add **zoom
|
|
152
|
+
buttons or a slider** as the single-pointer alternative (WCAG 2.5.1).
|
|
153
|
+
- **Damaged codes** fall back to manual entry. Some vendor SDKs add OCR of the printed digits.
|
|
154
|
+
|
|
155
|
+
## Multiple Codes & Continuous Scanning
|
|
156
|
+
|
|
157
|
+
- **Single mode** (the default): pick the code nearest the centre, and let a **tap choose a
|
|
158
|
+
different one** (VisionKit behaviour).
|
|
159
|
+
- **Multi mode:** highlight every detected code with overlays that track movement (keep
|
|
160
|
+
high-frame-rate tracking on), and tap to act on one. ML Kit returns at most 10 codes
|
|
161
|
+
per frame.
|
|
162
|
+
- **Continuous or batch** (scanning a shelf, a cart): keep the camera running, list results
|
|
163
|
+
as they arrive, show a running count, and allow undo of the last item.
|
|
164
|
+
- **Duplicate suppression** has no standard. Vendor defaults range from 0.5 to 3 s, or "until
|
|
165
|
+
the code leaves the frame". Single-scan flows should simply pause after a hit.
|
|
166
|
+
|
|
167
|
+
## Feedback: Visual, Haptic, Sound
|
|
168
|
+
|
|
169
|
+
- **Visual first:** the frame locks, the feed freezes, and the result appears. Use shape as
|
|
170
|
+
well as colour for success and error.
|
|
171
|
+
- **Haptics:** iOS `UINotificationFeedbackGenerator` `.success` / `.error`. Android
|
|
172
|
+
`HapticFeedbackConstants.CONFIRM` / `REJECT`. Don't reuse one pattern for opposite
|
|
173
|
+
meanings, and prefer no haptics to buzzy ones (Android). Apple warns that haptics must not
|
|
174
|
+
disrupt the camera, so fire them **after** the frame is captured.
|
|
175
|
+
- **Sound:** no platform guideline says on or off. Vendor SDKs default to on. A short beep is
|
|
176
|
+
fine if it respects silent mode, is toggleable, and is never the only signal. On iOS,
|
|
177
|
+
play it with the **`.ambient`** audio session. The default `soloAmbient` stops the
|
|
178
|
+
user's music.
|
|
179
|
+
- **Web:** `navigator.vibrate` does nothing on iOS and Firefox Android, so never rely on it.
|
|
180
|
+
Create or resume the `AudioContext` inside the "Start scanning" tap, so later beeps pass
|
|
181
|
+
autoplay rules.
|
|
182
|
+
|
|
183
|
+
## After the Scan: Auto-Act vs. Confirm
|
|
184
|
+
|
|
185
|
+
| Code | Action |
|
|
186
|
+
|---|---|
|
|
187
|
+
| Your own app's payload or a known product ID | Auto-act: look it up and show the result sheet |
|
|
188
|
+
| Arbitrary URL | **Preview, then the user taps to open** (as the iOS and Android system scanners do) |
|
|
189
|
+
| Structured payload (Wi-Fi, contact, calendar, phone, SMS, geo) | Type-specific preview (SSID, name, number), then a confirm action |
|
|
190
|
+
| Login / "link a device" QR | Say exactly what happens ("Sign in on a new device: Chrome on Windows, Berlin") and require explicit confirmation |
|
|
191
|
+
| Payment QR | Show merchant name and amount before confirming ([25c](25c-barcode-display.md)) |
|
|
192
|
+
|
|
193
|
+
ML Kit and VisionKit already parse the common structured types. Use their typed results
|
|
194
|
+
rather than re-parsing strings.
|
|
195
|
+
|
|
196
|
+
## QR Safety (Quishing)
|
|
197
|
+
|
|
198
|
+
Attackers put stickers over real codes (parking, restaurants), send QR codes in email to
|
|
199
|
+
get past link scanners, and use "link a device" codes to hijack messaging accounts (CISA,
|
|
200
|
+
Nov 2025). Risk is highest for codes in email and in open public spaces (NCSC UK). The
|
|
201
|
+
"Gone Quishing" field study found 67% of participants handed over Google or Facebook
|
|
202
|
+
credentials after a QR pretext.
|
|
203
|
+
|
|
204
|
+
When your app opens scanned URLs:
|
|
205
|
+
- **Show a URL preview before opening.** Follow Chromium's URL display rules:
|
|
206
|
+
- Highlight the **registrable domain** (eTLD+1).
|
|
207
|
+
- Elide long hostnames **from the front** so the real domain stays visible.
|
|
208
|
+
- Never show `user:pass@`.
|
|
209
|
+
- Show punycode (`xn--`) for confusable internationalized domains.
|
|
210
|
+
- Flag link shorteners, non-HTTPS URLs, and domains that don't match the context (a
|
|
211
|
+
"parking" code going to an unrelated domain).
|
|
212
|
+
- Don't auto-open app installs or payment pages from arbitrary QR codes. The FBI says to
|
|
213
|
+
install apps from the store and type known payment URLs.
|
|
214
|
+
- For account linking or login by QR, see the table above and
|
|
215
|
+
[25c](25c-barcode-display.md#qr-login--device-pairing).
|
|
216
|
+
|
|
217
|
+
## Scanning from an Image
|
|
218
|
+
|
|
219
|
+
Codes arrive in screenshots, PDFs, and emails, and a phone can't point its camera at its
|
|
220
|
+
own screen.
|
|
221
|
+
- Put **"Choose from photos"** in the scanner and in the permission-denied state.
|
|
222
|
+
- iOS: PHPicker (no library permission) + VisionKit `ImageAnalyzer`. Android: Photo Picker
|
|
223
|
+
(no media permission) + ML Kit on a still image. Web: `BarcodeDetector.detect()` or the
|
|
224
|
+
polyfill on an image or blob.
|
|
225
|
+
- "No code found in this image" offers a retry with another image and manual entry.
|
|
226
|
+
|
|
227
|
+
## Accessibility
|
|
228
|
+
|
|
229
|
+
- **Visual aiming excludes blind users.** Proven research patterns (Smith-Kettlewell BLaDE):
|
|
230
|
+
- Audio cues where **volume = distance** and **tone continuity = centring**.
|
|
231
|
+
- Feedback about **one code at a time**.
|
|
232
|
+
- A target size of 60–80% of the frame for 1D codes.
|
|
233
|
+
- Seeing AI and Google Lookout use beeps or spoken guidance and **auto-capture** with no
|
|
234
|
+
shutter button.
|
|
235
|
+
- **Auto-capture** also helps users with motor impairments. Never require a tap while aiming.
|
|
236
|
+
- **Announce the result.** On iOS, post a `UIAccessibility` announcement. On Android,
|
|
237
|
+
`announceForAccessibility` is **deprecated since API 36**: move focus to the result, or use
|
|
238
|
+
a live region or pane title instead.
|
|
239
|
+
- Label the torch, zoom, exit, and manual-entry controls. Make the targets large and reachable
|
|
240
|
+
one-handed.
|
|
241
|
+
- Success and error use shape and text, not just colour (Material switches the frame from a
|
|
242
|
+
solid to a dotted stroke).
|
|
243
|
+
- Zoom has a single-pointer alternative (WCAG 2.5.1). No short camera timeouts without a
|
|
244
|
+
resume option (2.2.1).
|
|
245
|
+
|
|
246
|
+
## Web Specifics
|
|
247
|
+
|
|
248
|
+
- **`BarcodeDetector` is not Baseline** (MDN compat data, Sept 2026):
|
|
249
|
+
|
|
250
|
+
| Browser | Support |
|
|
251
|
+
|---|---|
|
|
252
|
+
| Chrome Android | Yes |
|
|
253
|
+
| Chrome / Edge desktop | macOS and ChromeOS only, **not Windows or Linux** |
|
|
254
|
+
| Safari (macOS, iOS) | Behind a flag only; broken on iOS 18+ (WebKit bug 281848) |
|
|
255
|
+
| Firefox | No |
|
|
256
|
+
|
|
257
|
+
All iOS browsers use WebKit, so there's effectively no native detector on iOS web.
|
|
258
|
+
- **Fallback:** the `barcode-detector` polyfill (on zxing-wasm / ZXing-C++). It only
|
|
259
|
+
registers when native support is missing. Feature-detect `'BarcodeDetector' in window`
|
|
260
|
+
**and** check `getSupportedFormats()` for your formats. Load the WASM **lazily** when the
|
|
261
|
+
scanner opens, and self-host the `.wasm` for CSP or offline use. ZXing Java and
|
|
262
|
+
`@zxing/library` are in maintenance mode.
|
|
263
|
+
- **HTTPS only.** `navigator.mediaDevices` is undefined on insecure origins. In iframes,
|
|
264
|
+
add `allow="camera"`.
|
|
265
|
+
- **Camera constraints:** use `facingMode: 'environment'`, not `{exact: ...}`, which throws
|
|
266
|
+
on laptops. Feature-detect `torch`, `zoom`, and `focusMode` through `getCapabilities()`.
|
|
267
|
+
Torch works in iOS Safari only from about iOS 17, despite older claims.
|
|
268
|
+
- **Map `getUserMedia` errors to UI:**
|
|
269
|
+
|
|
270
|
+
| Error | Meaning |
|
|
271
|
+
|---|---|
|
|
272
|
+
| `NotAllowedError` | Denied or policy-blocked. Show recovery steps |
|
|
273
|
+
| `NotFoundError` | No camera. Hide scan, show manual entry |
|
|
274
|
+
| `NotReadableError` | Camera in use by another app |
|
|
275
|
+
| `OverconstrainedError` | Constraints too strict. Retry with looser ones |
|
|
276
|
+
|
|
277
|
+
- **Stop the tracks** (`track.stop()`) when the scanner closes or pauses, so the camera
|
|
278
|
+
indicator goes off.
|
|
279
|
+
- **iOS Home Screen web apps** historically forget the camera decision and re-prompt. Since
|
|
280
|
+
iOS 26, *every* site added to the Home Screen opens as a web app by default, so plan for
|
|
281
|
+
repeated prompts with brief in-context priming each time.
|
|
282
|
+
- **Hybrid apps:** WKWebView needs `requestMediaCapturePermissionFor` (iOS 15+), or users see
|
|
283
|
+
a web prompt on top of the native one. Android WebView needs `onPermissionRequest` plus
|
|
284
|
+
the app's CAMERA permission. Many in-app browsers fail silently here.
|
|
285
|
+
|
|
286
|
+
## Platform Notes
|
|
287
|
+
|
|
288
|
+
- **iOS — VisionKit `DataScannerViewController`** (iOS 16+, A12+):
|
|
289
|
+
- Built in: guidance labels ("Slow down"), tap-to-focus, pinch-to-zoom, tap-to-select, and
|
|
290
|
+
optional highlighting.
|
|
291
|
+
- Restrict `recognizedDataTypes` to the symbologies you need, and pick a `qualityLevel`.
|
|
292
|
+
- For full control, use AVFoundation `AVCaptureMetadataOutput` (see the AVCamBarcode sample
|
|
293
|
+
for focus-distance zoom).
|
|
294
|
+
- **Android — ML Kit** (on-device, offline): Codabar, Code 39/93/128, EAN, ITF, UPC, Aztec,
|
|
295
|
+
DataMatrix, PDF417, QR. Use CameraX `STRATEGY_KEEP_ONLY_LATEST`, ≤ 2 MP input, and throttle
|
|
296
|
+
the detector. **Google Code Scanner** gives a no-permission, no-custom-UI alternative.
|
|
297
|
+
- **Web:** native `BarcodeDetector` where available, the zxing-wasm polyfill elsewhere. See
|
|
298
|
+
above.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Quick Checklist
|
|
303
|
+
|
|
304
|
+
- [ ] Manual entry and "Choose from photos" beside every scan entry point
|
|
305
|
+
- [ ] Scan entry hidden when the device can't scan
|
|
306
|
+
- [ ] Permission asked on tap, with a specific purpose string; denied state is non-blocking and links to Settings
|
|
307
|
+
- [ ] Frame matches the code shape; torch, exit, and Help in the top bar
|
|
308
|
+
- [ ] Only the needed symbologies enabled; region of interest set
|
|
309
|
+
- [ ] Sensing → detected → lookup → error states designed; feed pauses on a hit
|
|
310
|
+
- [ ] Errors in a banner with a reason and actions, not a tooltip; not colour-only
|
|
311
|
+
- [ ] Focus-distance zoom on iOS; zoom buttons if custom pinch
|
|
312
|
+
- [ ] Haptic after capture; beep toggleable, respects silent mode (`.ambient` on iOS)
|
|
313
|
+
- [ ] Arbitrary URLs previewed (registrable domain highlighted) before opening
|
|
314
|
+
- [ ] Login and device-link codes state what they authorize and require confirmation
|
|
315
|
+
- [ ] Result announced to screen readers; auto-capture without a shutter tap
|
|
316
|
+
- [ ] Web: feature-detect `BarcodeDetector` + formats, lazy WASM fallback, HTTPS, tracks stopped on close
|
|
317
|
+
|
|
318
|
+
## Common Mistakes
|
|
319
|
+
|
|
320
|
+
- Scan as the only way to enter a code
|
|
321
|
+
- Asking for camera permission at app launch
|
|
322
|
+
- A permission-denied dead end with no manual path
|
|
323
|
+
- Auto-opening any URL a QR code contains
|
|
324
|
+
- Leaving the camera live after a hit, so a second code gets scanned by mistake
|
|
325
|
+
- "Move closer" hints on phones where closer means out of focus
|
|
326
|
+
- Errors shown as a red tint or a tooltip only
|
|
327
|
+
- A beep that stops the user's music
|
|
328
|
+
- Relying on `navigator.vibrate` or native `BarcodeDetector` on iOS
|
|
329
|
+
- Loading a large WASM decoder on page load instead of when the scanner opens
|
|
330
|
+
- Enabling every symbology "just in case"
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Sources
|
|
335
|
+
|
|
336
|
+
- [Material Design (M2) — Barcode scanning pattern](https://m2.material.io/design/machine-learning/barcode-scanning.html)
|
|
337
|
+
- [Apple VisionKit — DataScannerViewController](https://developer.apple.com/documentation/visionkit/datascannerviewcontroller) · [WWDC22 — Capture machine-readable codes and text with VisionKit](https://developer.apple.com/videos/play/wwdc2022/10025/) · [WWDC21 — What's new in camera capture](https://developer.apple.com/videos/play/wwdc2021/10047/) · [HIG — Privacy](https://developer.apple.com/design/human-interface-guidelines/privacy) · [HIG — Playing haptics](https://developer.apple.com/design/human-interface-guidelines/playing-haptics) · [AVAudioSession ambient](https://developer.apple.com/documentation/avfaudio/avaudiosession/category-swift.struct/ambient) · [iPhone — Scan a QR code](https://support.apple.com/guide/iphone/scan-a-qr-code-iphe8bda8762/ios)
|
|
338
|
+
- [ML Kit barcode scanning](https://developers.google.com/ml-kit/vision/barcode-scanning) · [Android guide](https://developers.google.com/ml-kit/vision/barcode-scanning/android) · [Google Code Scanner](https://developers.google.com/ml-kit/vision/barcode-scanning/code-scanner) · [Android — Request permissions](https://developer.android.com/training/permissions/requesting) · [HapticFeedbackConstants](https://developer.android.com/reference/android/view/HapticFeedbackConstants) · [Photo Picker](https://developer.android.com/training/data-storage/shared/photo-picker)
|
|
339
|
+
- [MDN — BarcodeDetector](https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector) · [WICG Shape Detection API](https://wicg.github.io/shape-detection-api/) · [WebKit bug 281848](https://bugs.webkit.org/show_bug.cgi?id=281848) · [barcode-detector polyfill](https://github.com/Sec-ant/barcode-detector) · [MDN — getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) · [W3C MediaStream Image Capture](https://www.w3.org/TR/image-capture/) · [WebKit — Safari 26.0 features](https://webkit.org/blog/17333/webkit-features-in-safari-26-0/)
|
|
340
|
+
- [FTC — QR code scams](https://consumer.ftc.gov/consumer-alerts/2023/12/scammers-hide-harmful-links-qr-codes-steal-your-information) · [FBI IC3 PSA](https://www.ic3.gov/PSA/2022/PSA220118) · [NCSC UK — QR codes: what's the real risk?](https://www.ncsc.gov.uk/blog-post/qr-codes-whats-real-risk) · [NCSC Ireland — QR phishing](https://www.ncsc.gov.ie/pdfs/Quick_Guide_QR_Code_Phishing_Scams.pdf) · [CISA — messaging app targeting (Nov 2025)](https://www.cisa.gov/news-events/alerts/2025/11/24/spyware-allows-cyber-threat-actors-target-users-messaging-applications) · [Chromium URL display guidelines](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/url_display_guidelines/url_display_guidelines.md) · ["Gone Quishing" (arXiv 2204.04086)](https://arxiv.org/abs/2204.04086)
|
|
341
|
+
- [Smith-Kettlewell BLaDE](https://www.ski.org/project/blade/) · [Google Lookout](https://support.google.com/accessibility/android/answer/9031274) · [NN/g — QR code guidelines](https://www.nngroup.com/articles/qr-code-guidelines/)
|
|
342
|
+
- [WCAG 2.2 — 1.3.4 Orientation](https://www.w3.org/WAI/WCAG22/Understanding/orientation.html) · [2.5.1 Pointer Gestures](https://www.w3.org/WAI/WCAG22/Understanding/pointer-gestures.html) · [2.2.1 Timing Adjustable](https://www.w3.org/WAI/WCAG22/Understanding/timing-adjustable.html)
|