haltija 1.1.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/LICENSE +190 -0
- package/README.md +220 -0
- package/bin/build-bookmarklet.ts +107 -0
- package/bin/cli-subcommand.mjs +537 -0
- package/bin/format-events.mjs +125 -0
- package/bin/format-test.mjs +183 -0
- package/bin/format-tree.mjs +165 -0
- package/bin/hj.mjs +59 -0
- package/bin/mcp-setup.mjs +288 -0
- package/bin/server.ts +9 -0
- package/bin/tosijs-dev.mjs +591 -0
- package/bin/tosijs-dev.ts +74 -0
- package/dist/client.js +387 -0
- package/dist/component.js +6685 -0
- package/dist/index.js +10201 -0
- package/dist/server.js +9847 -0
- package/docs/CI-INTEGRATION.md +230 -0
- package/docs/EXECUTIVE-SUMMARY.md +213 -0
- package/docs/README.md +67 -0
- package/docs/REST-API.md +123 -0
- package/docs/ROADMAP.md +591 -0
- package/docs/UX-CRIMES.md +599 -0
- package/docs/agent-prompt.md +139 -0
- package/docs/getting-started/app.md +96 -0
- package/docs/getting-started/playground.md +75 -0
- package/docs/getting-started/service.md +96 -0
- package/docs/recipes.md +245 -0
- package/haltija-icon.svg +79 -0
- package/package.json +68 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
# The Haltija Criminal Code (v1.0)
|
|
2
|
+
|
|
3
|
+
A comprehensive database of UX anti-patterns that Haltija can detect programmatically.
|
|
4
|
+
This turns the agent into a **"Compliance Officer"** and **"Linting Tool for Psychology"**.
|
|
5
|
+
|
|
6
|
+
> "Make your web app feel like a native app."
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Class 1: Interaction Integrity (The "UX Historian" Crimes)
|
|
11
|
+
|
|
12
|
+
*Violations of 40 years of established OS/GUI conventions. These happen when designers reinvent standard controls in a vacuum.*
|
|
13
|
+
|
|
14
|
+
### 1. The Conflated Tree View
|
|
15
|
+
|
|
16
|
+
**The Crime:** Clicking a parent node selects it *and* expands it simultaneously, or selection is impossible without expansion.
|
|
17
|
+
|
|
18
|
+
**The OS Standard:** Selection (Click) and Expansion (Triangle/Arrow) are distinct actions.
|
|
19
|
+
|
|
20
|
+
**Haltija Signature:**
|
|
21
|
+
- Click parent row
|
|
22
|
+
- Check if `aria-expanded` toggles *and* `aria-selected` toggles instantly
|
|
23
|
+
- Verify Arrow Keys (`Right` to expand, `Left` to collapse) function correctly
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
### 2. The Disclosure Heresy
|
|
28
|
+
|
|
29
|
+
**The Crime:** An arrow pointing **UP** to indicate an open/expanded state (because "the drawer pulls up"), violating the reading flow convention.
|
|
30
|
+
|
|
31
|
+
**The OS Standard:**
|
|
32
|
+
- **Right (▶)** = Closed/Next ("content is this way")
|
|
33
|
+
- **Down (▼)** = Open ("content flows below")
|
|
34
|
+
|
|
35
|
+
**The Philosophy:** Designers in a vacuum think "the drawer pulls up, so arrow points up." But users are in **Reading Mode** (following flow), not **Mechanical Mode** (operating a lever).
|
|
36
|
+
|
|
37
|
+
**Haltija Signature:**
|
|
38
|
+
- Compare icon rotation/SVG path between `aria-expanded="true"` and `false`
|
|
39
|
+
- Detect 180° rotation (Down → Up) on inline items
|
|
40
|
+
- *Exception:* Bottom-of-screen drawers may legitimately use Up
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### 3. The Ambiguous Toggle
|
|
45
|
+
|
|
46
|
+
**The Crime:** A switch that relies solely on color (Green/Grey) or position (Left/Right) without text labels like "On/Off".
|
|
47
|
+
|
|
48
|
+
**The Issue:** Does "Green" mean it *is* on, or that clicking it *turns* it on? (State vs. Action confusion)
|
|
49
|
+
|
|
50
|
+
**Haltija Signature:**
|
|
51
|
+
- Detect `role="switch"` or checkbox styled as toggle
|
|
52
|
+
- Check for adjacent `<label>` or internal text
|
|
53
|
+
- If text is missing → **Crime**
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### 4. The Scrollbar Lie (Gaslighting Scroll)
|
|
58
|
+
|
|
59
|
+
**The Crime:** Virtual scrolling that jumps, changes the scrollbar thumb size while dragging, or loses position when scrolling up.
|
|
60
|
+
|
|
61
|
+
**The OS Standard:** Scrollbars act as a fixed map of the content. 10% scroll movement = 10% content movement.
|
|
62
|
+
|
|
63
|
+
**Haltija Signature:**
|
|
64
|
+
- Measure `scrollHeight` change during scroll events
|
|
65
|
+
- If `scrollHeight` varies by >10% without explicit "Load More" action → **Crime**
|
|
66
|
+
- Monitor scroll-up: if `scrollTop` jumps by >100px without user input → **Crime**
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### 5. Multi-Select Amnesia
|
|
71
|
+
|
|
72
|
+
**The Crime:** A list that requires individual clicking of 50 items because `Shift+Click` (Range) and `Cmd/Ctrl+Click` (Toggle) are ignored.
|
|
73
|
+
|
|
74
|
+
**Haltija Signature:**
|
|
75
|
+
- Click Item 1
|
|
76
|
+
- Hold Shift, Click Item 5
|
|
77
|
+
- Check selection count
|
|
78
|
+
- If < 5 → **Crime**
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### 6. Infinite Scroll in Data Tables
|
|
83
|
+
|
|
84
|
+
**The Crime:** Using infinite scroll in enterprise data grids because "pagination is old school."
|
|
85
|
+
|
|
86
|
+
**The Reality:** In data grids, position matters. Users need to know "I am roughly 50% through the alphabet." Infinite scroll destroys the scrollbar's utility as a "Map."
|
|
87
|
+
|
|
88
|
+
**Haltija Signature:**
|
|
89
|
+
- Detect a `<table>` or `role="grid"`
|
|
90
|
+
- Scroll to bottom - does it load more rows?
|
|
91
|
+
- **Verdict:** "Data Integrity Risk. Infinite scroll prevents footer access and destroys spatial memory."
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Class 2: Dark Patterns & "Conversion Optimization"
|
|
96
|
+
|
|
97
|
+
*Deliberate hostility designed to trick the user or prevent them from leaving.*
|
|
98
|
+
|
|
99
|
+
### 7. The Trapdoor Cancel
|
|
100
|
+
|
|
101
|
+
**The Crime:** The "Save" button is a massive, high-contrast primary button. The "Cancel" button is a tiny, grey text link (10-12px), hidden in a corner.
|
|
102
|
+
|
|
103
|
+
**The Philosophy:** Usability is about helping the user make the *correct* decision, not just the *business* decision.
|
|
104
|
+
|
|
105
|
+
**Haltija Signature:**
|
|
106
|
+
- Identify the Form Actions container
|
|
107
|
+
- Compare Primary vs. Secondary action
|
|
108
|
+
- **Heuristic:** If Secondary Action is:
|
|
109
|
+
- `<a>` instead of `<button>`, OR
|
|
110
|
+
- font-size < 80% of Primary, OR
|
|
111
|
+
- contrast ratio < 3:1
|
|
112
|
+
→ **Crime**
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### 8. The Roach Motel
|
|
117
|
+
|
|
118
|
+
**The Crime:** Signup takes 1 click. Cancellation requires a phone call, chat with a bot, or >3 levels of menu navigation.
|
|
119
|
+
|
|
120
|
+
**Haltija Signature:**
|
|
121
|
+
- Navigate to "Settings"
|
|
122
|
+
- Search DOM for "Cancel," "Delete," "Close Account"
|
|
123
|
+
- If count is 0 or link is deep in nested divs → **Crime**
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### 9. Confirmshaming
|
|
128
|
+
|
|
129
|
+
**The Crime:** "No, I prefer to pay full price" or "No, I don't like saving money."
|
|
130
|
+
|
|
131
|
+
**Haltija Signature:**
|
|
132
|
+
- Analyze negative action text sentiment
|
|
133
|
+
- Detect manipulative phrasing (keywords: "dumb," "lose," "prefer to pay," "miss out")
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### 10. The Phantom Shield (Cookie Wall)
|
|
138
|
+
|
|
139
|
+
**The Crime:** Transparent or invisible layers (cookie banners) blocking clicks on the main UI, making the site feel broken/frozen.
|
|
140
|
+
|
|
141
|
+
**Haltija Signature:**
|
|
142
|
+
1. **Click Intercept Test:**
|
|
143
|
+
- Fire `click` at center of viewport (e.g., Login button)
|
|
144
|
+
- Check which element actually received the event
|
|
145
|
+
- If target is `div.cookie-wrapper` instead of intended button → **Critical Crime**
|
|
146
|
+
|
|
147
|
+
2. **Invisible Wall Heuristic:**
|
|
148
|
+
- Find elements with `position: fixed` covering >80% of viewport
|
|
149
|
+
- Check: `background-color: transparent` AND `pointer-events: auto`?
|
|
150
|
+
- Report: "Invisible High-Z Element blocking interactions. Site appears frozen."
|
|
151
|
+
|
|
152
|
+
**Severity:** Critical (Site Unusable)
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Class 3: Internationalization (i18n) Hostility
|
|
157
|
+
|
|
158
|
+
*Design that assumes every user is in the US, speaks English, and has a US keyboard.*
|
|
159
|
+
|
|
160
|
+
### 11. The Buried Country
|
|
161
|
+
|
|
162
|
+
**The Crime:** "United States" is at the top. "Afghanistan" is second. "Finland" is item #64. No search/typeahead.
|
|
163
|
+
|
|
164
|
+
**Haltija Signature:**
|
|
165
|
+
- Open dropdown
|
|
166
|
+
- Check index of current user's locale country
|
|
167
|
+
- If index > 10 and list is > 50 → **Crime**
|
|
168
|
+
- Test typeahead: focus list, send keystrokes "F", "I"
|
|
169
|
+
- *Pass:* Selection jumps to "Fiji" then "Finland"
|
|
170
|
+
- *Fail:* Nothing happens, or jumps to "France" (ignoring "I")
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### 12. Zip Code Jail
|
|
175
|
+
|
|
176
|
+
**The Crime:** Rejecting alphanumeric postal codes (UK: `EC1A 1BB`, Canada: `M5V 2H1`) because the regex expects 5 digits.
|
|
177
|
+
|
|
178
|
+
**Haltija Signature:**
|
|
179
|
+
- Input `EC1A 1BB` into postal field
|
|
180
|
+
- Check for validation error
|
|
181
|
+
- If error → **Crime: "Globalization Fail"**
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### 13. The Phone Number Pedant
|
|
186
|
+
|
|
187
|
+
**The Crime:** "Invalid format. Do not use dashes." / "Invalid format. You must use dashes."
|
|
188
|
+
|
|
189
|
+
**The Fix:** Strip non-digits on the backend! Don't make the user do data entry work.
|
|
190
|
+
|
|
191
|
+
**Haltija Signature:**
|
|
192
|
+
- Fuzz input with:
|
|
193
|
+
- `+358 40 1234567` (Standard International)
|
|
194
|
+
- `040-123-4567` (Local formatted)
|
|
195
|
+
- `0401234567` (Raw)
|
|
196
|
+
- If *any* valid format triggers error → **Crime**
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 14. The Polygloat Menu
|
|
201
|
+
|
|
202
|
+
**The Crime:** Listing languages in English ("German", "Japanese") instead of endonyms ("Deutsch", "日本語").
|
|
203
|
+
|
|
204
|
+
**The Problem:** Non-English speakers can't read English! They're looking for their language *in their language*.
|
|
205
|
+
|
|
206
|
+
**Haltija Signature:**
|
|
207
|
+
- Scan language menu
|
|
208
|
+
- Regex check: Does "Spanish" match `Español`? "German" match `Deutsch`?
|
|
209
|
+
- If text is purely ASCII English names for non-English languages → **Crime**
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### 15. The Cookie Wall (EU Blocker)
|
|
214
|
+
|
|
215
|
+
**The Crime:** Cookie modal so poorly implemented it's impossible to click "Login" because an invisible `div` covers the screen, or the "Reject" button is off-canvas on mobile.
|
|
216
|
+
|
|
217
|
+
**Haltija Signature:**
|
|
218
|
+
- Element: Fixed position `div` with high `z-index`, keywords "Cookie", "Consent"
|
|
219
|
+
- Attempt to click primary action *before* interacting with banner
|
|
220
|
+
- Check if click intercepted
|
|
221
|
+
- **GDPR Check:** Is there a `button` with text "Reject" or "Necessary Only"?
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Class 4: Forms from Hell
|
|
226
|
+
|
|
227
|
+
*Friction points that destroy data entry speed.*
|
|
228
|
+
|
|
229
|
+
### 16. The Paste Blockade
|
|
230
|
+
|
|
231
|
+
**The Crime:** Blocking `paste` on password or account number fields.
|
|
232
|
+
|
|
233
|
+
**Haltija Signature:**
|
|
234
|
+
- Attempt `paste` event
|
|
235
|
+
- Check `defaultPrevented`
|
|
236
|
+
- If blocked → **Crime**
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### 17. The Amnesiac Form
|
|
241
|
+
|
|
242
|
+
**The Crime:** Submitting the form with one error wipes all other valid fields.
|
|
243
|
+
|
|
244
|
+
**Haltija Signature:**
|
|
245
|
+
- Fill form completely
|
|
246
|
+
- Intentionally break one field
|
|
247
|
+
- Submit
|
|
248
|
+
- Check if other fields retained values
|
|
249
|
+
- If wiped → **Crime**
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### 18. The Label Lie
|
|
254
|
+
|
|
255
|
+
**The Crime:** Using `placeholder` text as the only label. It vanishes when you type, forcing reliance on memory.
|
|
256
|
+
|
|
257
|
+
**Haltija Signature:**
|
|
258
|
+
- Check input for `<label>` element, `aria-label`, or `aria-labelledby`
|
|
259
|
+
- If all missing → **Crime**
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Class 5: Accessibility & Mobile Hostility
|
|
264
|
+
|
|
265
|
+
*Basic exclusions of users with disabilities or touch interfaces.*
|
|
266
|
+
|
|
267
|
+
### 19. The "Div" Button
|
|
268
|
+
|
|
269
|
+
**The Crime:** `<div>` or `<span>` with `onClick` but no `role="button"` and no `tabindex`. Invisible to screen readers and keyboard users.
|
|
270
|
+
|
|
271
|
+
**Haltija Signature:**
|
|
272
|
+
- Query elements with click listeners
|
|
273
|
+
- Check tag name
|
|
274
|
+
- If generic container without proper ARIA → **Crime**
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### 20. The Tiny Tap
|
|
279
|
+
|
|
280
|
+
**The Crime:** Interactive elements smaller than 44x44px.
|
|
281
|
+
|
|
282
|
+
**Haltija Signature:**
|
|
283
|
+
- Compute `boundingClientRect` of all `<a>` and `<button>` elements
|
|
284
|
+
- Flag any < 44px in either dimension
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### 21. Focus Trap / Ghost Focus
|
|
289
|
+
|
|
290
|
+
**The Crime:**
|
|
291
|
+
- Opening a modal does not trap focus inside it, OR
|
|
292
|
+
- Closing modal resets focus to `<body>` instead of the trigger button
|
|
293
|
+
|
|
294
|
+
**Haltija Signature:**
|
|
295
|
+
- Open modal
|
|
296
|
+
- Press `Tab` repeatedly
|
|
297
|
+
- If focus leaves modal → **Crime**
|
|
298
|
+
- Close modal
|
|
299
|
+
- Check `document.activeElement`
|
|
300
|
+
- If not the original trigger → **Crime**
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Class 6: Volatility (The "Feed" Crimes)
|
|
305
|
+
|
|
306
|
+
*Violating object permanence and user intent.*
|
|
307
|
+
|
|
308
|
+
### 22. Feed Roulette
|
|
309
|
+
|
|
310
|
+
**The Crime:** Accidental refresh or "back" button wipes the content you were looking at, replacing it with a new algorithmic feed.
|
|
311
|
+
|
|
312
|
+
**The Philosophy:** Prioritizes "Freshness" (Engagement) over "Utility" (Retrieval). Treats user attention as disposable.
|
|
313
|
+
|
|
314
|
+
**Haltija Signature:**
|
|
315
|
+
1. `scanVisibleContent()` → Store IDs of first 5 items
|
|
316
|
+
2. Trigger `reload` or click "Home"
|
|
317
|
+
3. `scanVisibleContent()` again
|
|
318
|
+
4. If < 50% of original items present → **Crime: "Volatile Content Environment"**
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### 23. The Gaslighting Scroll (Bi-Directional Virtualization)
|
|
323
|
+
|
|
324
|
+
**The Crime:** You're 500 items deep. You scroll up to check something you saw 30 seconds ago. The browser jumps, content re-orders, you can't find your place.
|
|
325
|
+
|
|
326
|
+
**The Philosophy:** Humans rely on spatial memory ("It was up and to the left"). Virtual scrolling breaks this map.
|
|
327
|
+
|
|
328
|
+
**Haltija Signature:**
|
|
329
|
+
1. Scroll down 50 viewport heights
|
|
330
|
+
2. Scroll up 10 viewport heights quickly
|
|
331
|
+
3. Monitor `scroll` events vs `visualViewport` offset
|
|
332
|
+
4. If scroll position jumps (>100px without user input) → **Crime**
|
|
333
|
+
5. If `layout-shift` score spikes during scroll-up → **Crime**
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
### 24. Deceptive Loading (Skeleton Lie)
|
|
338
|
+
|
|
339
|
+
**The Crime:** Skeleton screen shows a layout, but final content shifts positions significantly (CLS) or buttons remain disabled after appearing ready.
|
|
340
|
+
|
|
341
|
+
**The Philosophy:** The UI promises interaction ("I am loading right here") but delivers a moving target.
|
|
342
|
+
|
|
343
|
+
**Haltija Signature:**
|
|
344
|
+
- Compare `boundingClientRect` of skeleton elements vs. final content
|
|
345
|
+
- If shift > 10px → **Crime: "Deceptive Loading State"**
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Implementation: The "Rap Sheet"
|
|
350
|
+
|
|
351
|
+
When Haltija runs a test plan, output a **Criminal Record** alongside functional results:
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
Summary:
|
|
355
|
+
- Functional: PASSED (Login works)
|
|
356
|
+
- UX Audit: FAILED
|
|
357
|
+
|
|
358
|
+
Crimes Detected:
|
|
359
|
+
1. FELONY: Paste Blockade on Password Field
|
|
360
|
+
2. MISDEMEANOR: Tiny Tap Target on "Forgot Password" link (12px height)
|
|
361
|
+
3. INTERACTION VIOLATION: Country picker lacks typeahead
|
|
362
|
+
4. DARK PATTERN: Cancel button is 10px grey text link
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
This gives engineers/designers **exact ammunition** for the next meeting.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Why Agents Hate These Even More Than Humans
|
|
370
|
+
|
|
371
|
+
To an AI Agent, **The Feed is Kryptonite.**
|
|
372
|
+
|
|
373
|
+
- Agents act linearly: "I saw a shoe I liked. I will scroll back up to buy it."
|
|
374
|
+
- If scrolling up makes the shoe vanish (Virtual Scroll) or reloading changes inventory (Feed Roulette), the Agent fails.
|
|
375
|
+
|
|
376
|
+
**The Haltija Advantage:**
|
|
377
|
+
Because Haltija acts as a **Context History Stream** (Semantic Events), it solves this:
|
|
378
|
+
|
|
379
|
+
- **The Site:** Deletes DOM node for "Post #101" because you scrolled past it
|
|
380
|
+
- **Haltija:** Remembers "Post #101" was seen at `timestamp: 10:05`
|
|
381
|
+
- **The Feature:** When Agent asks "Where is that post?", Haltija says: *"The site deleted it (Virtual Scroll), but I have the data. Don't scroll up; use Search instead."*
|
|
382
|
+
|
|
383
|
+
You are building **the memory that the modern web tries to erase**.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Class 7: Mobile Hostility
|
|
388
|
+
|
|
389
|
+
*Crimes committed by developers who only test on a 27-inch monitor with a mouse.*
|
|
390
|
+
|
|
391
|
+
### 25. The Pinch-to-Zoom Lock
|
|
392
|
+
|
|
393
|
+
**The Crime:** Disabling zoom because it "messes up the design." This actively prevents users with vision impairments (or anyone in bright sunlight) from reading your content.
|
|
394
|
+
|
|
395
|
+
**Haltija Signature:**
|
|
396
|
+
- Scan `<meta name="viewport">`
|
|
397
|
+
- Look for `user-scalable=no` or `maximum-scale=1.0`
|
|
398
|
+
|
|
399
|
+
**Report:** "Viewport Violation: Zoom disabled. Accessibility failure for low-vision users."
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
### 26. The Hover Trap
|
|
404
|
+
|
|
405
|
+
**The Crime:** Hiding critical actions (like "Edit" or "Delete" buttons) or navigation sub-menus behind a `:hover` state.
|
|
406
|
+
|
|
407
|
+
**The Reality:** There is no hover on touchscreens. The user has to awkwardly tap once to see the menu (which might interpret the tap as a click and navigate away immediately).
|
|
408
|
+
|
|
409
|
+
**Haltija Signature:**
|
|
410
|
+
- Identify elements with `mouseover` or `:hover` listeners that change visibility of child elements
|
|
411
|
+
- **Heuristic:** Can the Agent access the child element via a single `click` or `focus` event without triggering navigation?
|
|
412
|
+
|
|
413
|
+
**Report:** "Touchscreen Exclusion: Critical UI hidden behind Hover state. Inaccessible on mobile."
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
### 27. The Keyboard Eclipse
|
|
418
|
+
|
|
419
|
+
**The Crime:** Tapping an input field brings up the on-screen keyboard, which completely covers the input field you are typing in.
|
|
420
|
+
|
|
421
|
+
**Haltija Signature:**
|
|
422
|
+
- **Simulation:** Set viewport height to 50% (simulating keyboard)
|
|
423
|
+
- **Action:** Focus an input in the bottom half of the page
|
|
424
|
+
- **Check:** Is the input element's `bottom` coordinate > visible viewport height? Does the page fail to scroll it into view?
|
|
425
|
+
|
|
426
|
+
**Report:** "Input Occlusion: Active field hidden by virtual keyboard."
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### 28. The Fat Finger Minefield
|
|
431
|
+
|
|
432
|
+
**The Crime:** Placing two actionable elements (e.g., "Save" and "Cancel", or list items) closer than 8px apart.
|
|
433
|
+
|
|
434
|
+
**Haltija Signature:**
|
|
435
|
+
- Scan all `button`, `a`, `input`
|
|
436
|
+
- Compute bounding boxes
|
|
437
|
+
- **Check:** If distance between edges of two interactive targets is < 8px → **Crime**
|
|
438
|
+
|
|
439
|
+
**Report:** "Touch Target Risk: Interactive elements too close. High probability of accidental clicks."
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
### 29. The Horizontal Scroll of Death
|
|
444
|
+
|
|
445
|
+
**The Crime:** A single element (like a long URL or code block) is wider than the mobile viewport, causing the *entire page* to wobble left and right.
|
|
446
|
+
|
|
447
|
+
**Haltija Signature:**
|
|
448
|
+
- Set viewport width to 375px (iPhone SE)
|
|
449
|
+
- **Check:** Is `document.body.scrollWidth` > `window.innerWidth`?
|
|
450
|
+
|
|
451
|
+
**Report:** "Layout Failure: Page overflows horizontally. Causes unstable scrolling."
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Class 8: Accessibility Atrocities
|
|
456
|
+
|
|
457
|
+
*Basic exclusions that are often illegal and always unethical.*
|
|
458
|
+
|
|
459
|
+
### 30. The Silent Image
|
|
460
|
+
|
|
461
|
+
**The Crime:** Meaningful images (not decorative) missing `alt` text. Screen readers just say "Image" or read the filename "DSC_0043.jpg".
|
|
462
|
+
|
|
463
|
+
**Haltija Signature:**
|
|
464
|
+
- Query `img:not([alt])`
|
|
465
|
+
- Filter out `role="presentation"` or empty `alt=""` (if decorative)
|
|
466
|
+
|
|
467
|
+
**Report:** "Blind Spot: Meaningful image missing alternative text."
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
### 31. The Color-Only Signal
|
|
472
|
+
|
|
473
|
+
**The Crime:** Using *only* red text or a red border to indicate an error, with no icon or text label explaining "Invalid".
|
|
474
|
+
|
|
475
|
+
**The Reality:** Colorblind users cannot see the error.
|
|
476
|
+
|
|
477
|
+
**Haltija Signature:**
|
|
478
|
+
- Trigger form error
|
|
479
|
+
- **Check:** Did a new text node appear or did `aria-invalid` toggle?
|
|
480
|
+
- **Failure:** If only CSS `color` or `border-color` changed → **Crime**
|
|
481
|
+
|
|
482
|
+
**Report:** "Sensory Reliance: Error state conveyed solely by color. Invisible to colorblind users."
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
### 32. The Low Contrast Grey
|
|
487
|
+
|
|
488
|
+
**The Crime:** Light grey text on a white background (e.g., `#999` on `#fff`). The designer thinks it looks "clean." Users think it looks "invisible."
|
|
489
|
+
|
|
490
|
+
**Haltija Signature:**
|
|
491
|
+
- **Heuristic:** Compute background color and foreground color of all text nodes
|
|
492
|
+
- **Math:** Calculate luminosity ratio
|
|
493
|
+
- If < 4.5:1 (WCAG AA) for normal text → **Crime**
|
|
494
|
+
|
|
495
|
+
**Report:** "Legibility Failure: Text contrast too low (Ratio X:1)."
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
### 33. The Headings Out of Order
|
|
500
|
+
|
|
501
|
+
**The Crime:** Using `<h3>` because you wanted "medium sized font," skipping `<h1>` or `<h2>`.
|
|
502
|
+
|
|
503
|
+
**The Reality:** Screen reader users navigate by headings. A broken hierarchy makes the page structure unintelligible.
|
|
504
|
+
|
|
505
|
+
**Haltija Signature:**
|
|
506
|
+
- Scan heading tags
|
|
507
|
+
- Check order (e.g., `h4` appears before `h1`, or `h3` is a direct child of `h1` skipping `h2`)
|
|
508
|
+
|
|
509
|
+
**Report:** "Structural Chaos: Semantic heading hierarchy broken."
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
### 34. The Autoplay Assault
|
|
514
|
+
|
|
515
|
+
**The Crime:** Video or audio that plays automatically on page load.
|
|
516
|
+
|
|
517
|
+
**The Reality:** This overrides screen reader audio, disorienting blind users, and triggers anxiety/migraines in neurodiverse users.
|
|
518
|
+
|
|
519
|
+
**Haltija Signature:**
|
|
520
|
+
- Check `<video>` or `<audio>` tags for `autoplay` attribute
|
|
521
|
+
- Check if `muted` is missing
|
|
522
|
+
|
|
523
|
+
**Report:** "Hostile Media: Unsolicited autoplay detected."
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
### 35. The No Skip Link
|
|
528
|
+
|
|
529
|
+
**The Crime:** Forcing keyboard users to tab through 50 navigation links on *every single page* before they can reach the content.
|
|
530
|
+
|
|
531
|
+
**Haltija Signature:**
|
|
532
|
+
- **Action:** Press `Tab` from page load
|
|
533
|
+
- **Check:** Is one of the first 3 focusable elements a link to `#main` or `main`?
|
|
534
|
+
|
|
535
|
+
**Report:** "Navigation Fatigue: Missing 'Skip to Content' link."
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Implementation Strategy: The Audit Flag
|
|
540
|
+
|
|
541
|
+
You don't want this running on every fast iteration loop (it generates too much noise).
|
|
542
|
+
|
|
543
|
+
**Add a flag to the Haltija CLI:**
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
# Run functional tests only
|
|
547
|
+
npx haltija test
|
|
548
|
+
|
|
549
|
+
# Run with full Criminal Investigation
|
|
550
|
+
npx haltija test --audit
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
When `--audit` is active, the Agent gets the "Criminal Code" system prompt and the Haltija widget activates its "Sensors" (Contrast checker, Layout shift observer, etc.).
|
|
554
|
+
|
|
555
|
+
This allows teams to separate:
|
|
556
|
+
- **"Did we break the build?"** (Functional)
|
|
557
|
+
- **"Did we ruin the product?"** (Audit)
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## The Value Proposition
|
|
562
|
+
|
|
563
|
+
You aren't just selling a "Tester." You are selling a **Compliance Officer**.
|
|
564
|
+
|
|
565
|
+
If a company uses Haltija, they aren't just checking if the login button works. They're checking:
|
|
566
|
+
|
|
567
|
+
- "Did we alienate our European customers today?"
|
|
568
|
+
- "Did we break the site for Canada?"
|
|
569
|
+
- "Is our mobile menu usable by humans?"
|
|
570
|
+
|
|
571
|
+
This turns **JSON Test Results** into a **Business Risk Report**. That's worth 10x what a Selenium script is worth.
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## Severity Levels
|
|
576
|
+
|
|
577
|
+
| Level | Description | Example |
|
|
578
|
+
|-------|-------------|---------|
|
|
579
|
+
| **Critical** | Site unusable | Phantom Shield blocking all clicks |
|
|
580
|
+
| **Felony** | Major user harm | Paste Blockade on passwords |
|
|
581
|
+
| **Misdemeanor** | Friction/annoyance | Tiny tap targets |
|
|
582
|
+
| **Violation** | Best practice breach | Missing form labels |
|
|
583
|
+
| **Warning** | Potential issue | Country picker lacks typeahead |
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Future Additions (Parking Lot)
|
|
588
|
+
|
|
589
|
+
From original roadmap, not yet codified:
|
|
590
|
+
|
|
591
|
+
- **CAPTCHA nightmares** - inaccessible, unsolvable
|
|
592
|
+
- **Hamburger menus hiding critical actions** - primary nav buried
|
|
593
|
+
- **No skip links** - keyboard users trapped in header
|
|
594
|
+
- **Hover-dependent UI on mobile** - tooltips/menus that require hover
|
|
595
|
+
- **Pinch-zoom disabled** - viewport meta crimes
|
|
596
|
+
- **Auto-formatting that fights input** - phone fields that add/remove chars while typing
|
|
597
|
+
- **Duplicate country entries** - "UK" vs "Great Britain" vs "United Kingdom"
|
|
598
|
+
- **Language selector buried** - in footer? hamburger? settings? random flag icon?
|
|
599
|
+
- **Missing lang attribute** - wrong language auto-detected, no way to fix
|