Three single-file HTML apps ported to Makepad 2.0 (dev b41e740) script_mod! DSL: - rider (ride.html): 21 unit + 9 integration + 9 headless UI tests - koboyo (koboyo.html): 29 + 14 + 8 - insurance (insurance.html): 28 + 14 + 8 All 140 tests green. Includes headless-Linux platform patch, full README, setup script, and three brutal codebase assessments with execution plans.
21 KiB
InsurePay ("nigig-pay" / insurance.html) — Brutal Professional Assessment
Artifact reviewed: /home/user/insurance.html — 998 lines, 56.8 KB, single
file containing HTML + 700 lines of bespoke CSS + ~150 lines of inline JS.
Reviewer verdict: a visually polished design prototype that is
unshippable as a product in its current form. Roughly a third of the UI is
inert decoration, the interaction layer has real correctness bugs, there is no
test of any kind, and it embeds realistic PII in public markup.
Scorecard (0–10, strictly as production code):
| Axis | Score | One-liner |
|---|---|---|
| Architecture | 3.5 | Single global script, DOM-as-state, stringly-typed routing |
| Code quality | 4.0 | Readable but duplicated, dead code, zero tests/tooling |
| Bugs / correctness | 4.0 | Gesture math, dead affordances, unreachable code paths |
| Performance | 6.0 | Self-contained & light, but backdrop-filter stacking and filter animation are GPU traps |
| Design / UX | 6.5 | Genuinely pretty; confusing IA, 40% interactive-looking elements do nothing |
| Security / privacy | 3.0 | No auth model, realistic PII hardcoded, no CSP; currently low attack surface only because nothing is wired |
| Accessibility | 2.5 | user-scalable=no, 10px labels, no focus management, no aria-live, no keyboard sheet control |
| Overall | 4.2 | Demo-grade. Not production-grade. |
Credit where due (one paragraph, then we get harsh): the file is fully self-contained with zero dependencies and zero network cost; icons are inline SVG (no icon-font garbage); the gesture code at least attempts real physics (velocity, clamps, tap vs. drag disambiguation); sectional CSS comments are disciplined; and hit targets are mostly ≥38 px. That ends the good news.
1. Architecture
A1 — DOM is the state machine. Screen visibility, chip selection, sheet
mode, and toast message are all encoded as DOM classes (classList.toggle)
with data-go/data-toast/data-stab strings as the only "API". The only
actual state variable in 150 lines of JS is sheetMode
(insurance.html:925). There is no model, no store, no events, no types.
Every behavior mutation has to know which classes to flip. This is why the
Makepad port had to rebuild the whole thing as model.rs — the original has
no extractable logic.
A2 — Stringly-typed routing with silent failure. go(name)
(:885) looks up scr-<name>; a typo in any data-go attribute
(data-go="policy" instead of policies) fails silently — no console
error, no fallback. There are 14 data-go sites and nothing validates them.
A3 — No history/routing integration. The four "screens" are div-switching. The browser Back button exits the app entirely; there are no URLs, no deep links, no refresh-safe state. For an app with auth and payments (renewals), this alone disqualifies it.
A4 — Content is hardcoded in markup. Agent names/roles, policy numbers, prices ($214/$112), claim amounts (+$234.00), support email — all baked into HTML. There is no data layer, not even a JSON block. Any business change requires editing markup; there is no seam where an API would attach.
A5 — Four duplicated component families. The bell button is copy-pasted
4× (identical 5-line SVG), the back chevron 3×, the "↗" arrow 5×, the renew
pill 2×, avatar stacks 2×. No <symbol>/sprite, no template, no JS component.
Change the bell → edit 4 places. This is copy-paste-driven development.
A6 — Global scope pollution. The script has no IIFE/module wrapper:
screens, tabs, go, showToast, toast, sheetMode, dragging, vy,
moved et al. are all globals. Any third-party script collides silently.
No 'use strict', no JSDoc types, no lint config, no package.json, no build,
no source control hygiene visible (no version, no license, no author).
A7 — Error handling: zero. grab.setPointerCapture(e.pointerId) (:951)
can throw NotFoundError in synthetic/edge environments; no try/catch anywhere
in the file. One thrown pointer error bricks the sheet gesture for the session.
A8 — Decision ambiguity encoded as three same-named sections.
"Quick Actions" exists on Home (2 rows), on Policies (2 policy cards), and in
the FAB sheet (4 tiles) — three different datasets under one label. "File a
Claim" appears 3 ways with three different outcomes (+$234.00 reward
row, "Claim registration opened ✓", "Claim form opened…"). Same action,
different behavior depending on where you tap — an IA defect masquerading as
richness.
2. Code quality
- Q1. Dead CSS and JS.
.hide{display:none!important}(:36) is defined and never used. The FABclicktoggle (:937: closeSheet()branch) is unreachable code (see B1). The dots pager (.dots) purports to page a carousel that doesn't exist. - Q2. ~40% of interactive-style elements are inert. Concrete list: bell ×4,
filter button, hero-arrow, both bare "View All" links (policies/buy),
mini-handle, avatar, portrait, both QA rows, the agents duo-card, the
search input (accepts typing, filters nothing), all four
.sec-links on the subpages. Users will tap things that look tappable and get nothing. Either wire them or delete them — currently it's a mock wearing a product's clothes. - Q3. Magic values everywhere. px-only sizing (no rem/em — ignores user font preferences), un-tokenized spacing/radii, z-index 60/70/80/90/95 held together by hope, gesture thresholds (−26, −12, 36, 18, 0.2, 0.28, 140, 8) inline with no names.
- Q4.
transition: allanti-pattern on chips and action tiles (.chip,.act) — animates every property including ones you didn't mean to. - Q5. Cryptic class names:
.tb,.dc,.qa,.idh,.bc,.stk,.ag-row,.sup-row— maintenance tax on every future edit. - Q6. No tests. Nothing. Not a gesture unit test, not a snapshot. The gesture thresholds are tunable constants whose regression would be invisible until a user complains.
- Q7. Localization-hostile. Hardcoded English,
10/02/25ambiguous date,$hardcoded,white-space:nowraptoasts that will overflow in German, physical (non-logical) CSS properties block RTL.
3. Bugs (ranked by severity)
- B1 (High) — FAB can never close the sheet; half its code is dead.
fabBtnlives inside.tabbar(:777-797). Opening the sheet adds.ghostto the tabbar →pointer-events:none(:283). SosheetMode==='closed' ? openSheet() : closeSheet()(:937) can only ever take the open branch; the close branch is unreachable. The FAB pretends to be a toggle and isn't one. - B2 (High) — Flick detection windows are wrong. Velocity uses only the
last two pointermove samples (
vy = (cy-ly)/max(1, now-lt),:959). A fast drag that decelerates at release registers vy≈0 and snaps back; a slow drag with an accidental final twitch counts as a fling. Industry standard is a trailing ~100 ms sample window. Users will experience random sheet snaps. - B3 (Medium) — Sub-tab state is silently reset on every expansion.
setMode('expanded')forcesswitchStab('actions')(:932). User on "Agents" collapses to peek (tab hidden but state kept), re-expands → yanked back to "Actions". Collapse keeps state, expand discards it — inconsistent. - B4 (Medium) — Chips filter nothing. The benefits chips only toggle
.on(:905-911). Featured/Health/Motor switch while the entire screen (drive card, health tip) stays identical. It is a fake control. - B5 (Medium) —
user-scalable=no, maximum-scale=1(:5). WCAG 1.4.4 / 1.4.10 violation; low-vision users cannot zoom. This is disqualifying for an insurance app that will be used by older demographics. - B6 (Medium) — Fake QR code. The ID card "QR" (
<div class="qr">) is a static rect pattern encoding nothing. A user scanning their own insurance ID and getting "no data" is a trust incident. At minimum an insurance ID card must encode policy deep-link or signed token; as-is it's a real-looking credential that does nothing — worse than no QR. - B7 (Medium) — Toast is not announced. No
role="status"/aria-liveon#toast; screen-reader users never hear "Policy renewal started ✓". Also hardcodedwhite-space:nowrapwill overflow the pill on 320 px devices or longer localized strings. - B8 (Low) — Sheet keyboard/escape handling absent. No Esc-to-close, no
focus trap, focus stays behind the backdrop; keyboard users tab into a
blurred background.
.backdropis a<div>with a click listener — not keyboard-activatable. - B9 (Low) — Drag threshold tuned for mouse, not thumb. 26–36 px on a ~0.35 mm/px phone screen is ~9 mm — double the comfort threshold for flick gestures; combined with B2 users get missed or accidental expansions.
- B10 (Low) —
Hero renewpill says "Renew Now" and the toast confirms a renewal with no confirmation step, no amount, no terms. A one-tap financial commitment with zero friction text. Fine for a mock; a lawsuit in production. - B11 (Low) — Screens preserve scroll position across navigation.
go()never resetsscrollTop, so returning to a long screen restores a scrolled view with no status indication. Minor but sloppy. - B12 (Low) — Time is hardcoded "9:41" and the bell red dot has no unread-state backing. Cosmetic, but it signals the app's data plumbing is imaginary.
4. Performance
- P1 — backdrop-filter stacking. Five separate
backdrop-filterregions (hero-ic blur 4, batt-tag blur 6, dc-glass blur 6, tabbar blur 14, sheet backdrop blur 4) sit over scrollable content. On mid/low Android this is the single largest GPU cost; scroll jank is guaranteed when the sheet is open over blurred content. - P2 — Animating
filter: blur(3px)on.tabbar.ghost(:283) — filter animation is paint-every-frame, not composited; on a full-width bar this is a visible stutter. The same effect is free as pre-rendered opacity+translate. - P3 — Massive shadows.
0 34px 90px,0 22px 46px, multi-layer card shadows repaint on any transform of their elements. Real risk during the sheet's transform animation on low-end devices (verified pattern on mobile WebKit). - P4 — Redundant gradients. Hero, drive card, ID card, FAB each stack 2–3
radial + linear gradients as backgrounds. Beautiful; also 4 large
background-sizerasterizations. Cacheable but adds first-paint cost on weak GPUs. - P5 — No
will-change/containment hints on.sheetor.screens; the.screenswitch depends ondisplaytoggling which forces full layout+paint of 1000-node pages on every tab switch. For 4 pages it's tolerable; at 8 it won't be. - P6 — Positives: zero external requests, no fonts, no images, ~10 KB gzipped. First paint is fast. This is why it feels snappy in a demo.
- P7 — Pointermove handler does
performance.now()+ style writes per event — fine — but norequestAnimationFramethrottle; at 120 Hz screens style writes run 2× frame rate. Trivial fix.
5. Security & privacy
- S1 — Realistic PII in public markup. Full names (Rahul Sharma, Alex Mandes, Sarah Kimani, David Otieno, Grace Wanjiru), DOB (24 Feb 2001), ID number 326547624, policy CA326547624, plate MP04CY9999, residence. If any of these map to real humans, this file is a data-protection incident (Kenya DPA 2019 / GDPR-class). Even if synthetic, nothing in the file says so.
- S2 — No auth/session concept. An app that displays ID cards, policy numbers, and takes "renewal" actions has zero authentication surface. As a mock: understandable. As a product blueprint: where is it?
- S3 — No CSP/SRI/referrer-meta. Currently self-hosted so risk is latent,
but the day someone hot-links a script or style, there's no defense in
depth.
textContentfor toasts is XSS-safe today (:897) — the classic regression is someone "improving" it toinnerHTMLfor bold text. - S4 — Sensitive actions lack confirmation & audit trail. Renewal, claim registration, ID download: one tap, toast, done. No step-up auth, no idempotency, no server request at all (obviously) — meaning the file currently teaches the wrong interaction contract to anyone building the backend from it.
- S5 — Secrets/privacy in URLs to be designed later — but note the QR (B6) will likely tempt someone to encode raw policy IDs client-side. Needs a signed, short-lived token design from the start.
- S6 — Input handling is unstructured. The search input has no validation/sanitization conventions; when wired to an API this becomes the injection vector of choice. Establish encoding-at-boundary now.
6. Design / UX (and a11y)
- D1 — Visual craft is real. Consistent radius scale (13→30), one strong brand color, disciplined soft-shadow language, coherent purple/navy/light hierarchy. It looks expensive. (This is what's fooling stakeholders into thinking it's nearly-done.)
- D2 — Identity confusion. Hero says Car Insurance; sections push Health & Wellness, Quick Actions reward "+$234.00" for claims, Benefits pushes driving score + heart rate. The app doesn't know what it is: car insurance, health insurance, or a wellness-rewards program. Pick one north star per surface.
- D3 — FAB mega-menu buries support two gestures deep (FAB → expand → Support stab) while "Message Now" exists on Policies — two competing support entries with different states.
- D4 — Emoji-as-iconography. 🧔🏻👩🏼📄🔔💬🛡 render inconsistently across platforms (tofu on some Androids, inconsistent skin tones). For a financial app this reads as unprofessional; replace with a single icon font/SVG set.
- D5 — Contrast failures.
.lblrgba(255,255,255,.62) on purple (~2.9:1),#a6a7b3placeholder,#9a9ba810.5 px tab labels,#cfcfd9dots — multiple sub-4.5:1 ratios at 10–12 px sizes. Insurance users skew older; this is an exclusion shipped as a style. - D6 — Home indicator / island are fake device chrome painted by the app
(
.island,.home-ind). Inside a real device in-app WebView these double up with the OS chrome. Kill them outside the demo frame. - D7 — No loading/empty/error states anywhere. The file only knows the happy path. First product question that will be asked: "what does this screen show while policies load?" — there's no answer in this code.
- D8 — Motion is un-governed: no
prefers-reduced-motionquery; the fadeUp + sheet + blur + toast stack all animate regardless of vestibular settings.
7. Execution plan — "finally improve InsurePay"
Rough effort for 1 senior FE + 0.5 QA, assuming it must become a real maintainable web app. If any phase's goal shifts (e.g., the product stays a prototype), that's a scope decision — write it down explicitly instead of letting it happen by accident.
Phase 0 — Foundations & hygiene (≈3–4 days) — do not skip
| # | Task | Acceptance criteria |
|---|---|---|
| 0.1 | Split the monolith: index.html + styles.css (+ tokens.css) + app.js as ES module; add package.json (Vite or no-build ESM), lint (ESLint+stylelint), formatting (Prettier), git baseline |
npm run dev/build/lint green; zero globals (verify via window enumeration in CI) |
| 0.2 | Extract design tokens → CSS custom properties for all spacing/radii/font sizes; px→rem at 16 px base; z-index scale z.base/z.overlay/z.sheet/z.toast |
No magic numbers outside tokens; rem audit passes |
| 0.3 | SVG sprite: one <symbol> per icon, <use> everywhere (SVG currently duplicated 14×); delete emoji icons |
Single bell definition; zero emoji in chrome |
| 0.4 | Kill dead code: .hide, dots pager, fake .island/.home-ind, inert controls → wire or delete each; decision table for all 22 data-toast and 14 data-go sites |
Interactive-inventory doc: every tappable element has a named behavior |
| 0.5 | Fix viewport meta (drop user-scalable=no/maximum-scale); add prefers-reduced-motion gate for fadeUp/sheet/toast animations |
axe-core: 0 critical |
| 0.6 | Toast: role="status" aria-live="polite", max-width wrap, queue (2s) instead of replace |
SR announces; no clipping at 320 px |
Phase 1 — Behavior correctness (≈4–5 days)
| # | Task | Acceptance criteria |
|---|---|---|
| 1.1 | State store: one module owning {screen, sheetMode, sheetTab, chip, toast}; DOM becomes pure render. Keep the exact gesture constants as named, unit-tested exports — the Makepad port's model.rs is the reference implementation; port it back to JS/TS 1:1 |
Store drives all class flips; go('typo') throws |
| 1.2 | Rewrite the gesture engine: trailing 100 ms velocity window, rAF-throttled moves, guarded setPointerCapture, touch-comfort thresholds (~48 px / velocity-weighted), tap tolerance on both axes |
95% of scripted gesture traces resolve as intended (test corpus) |
| 1.3 | Fix B1: move FAB out of the ghosted tabbar (or un-ghost it); restore real toggle; add Esc-to-close, backdrop focus trap, focus return | Keyboard-only walkthrough passes |
| 1.4 | Fix B3: preserve sub-tab across collapse/expand, or make the reset an explicit product decision (note in spec) | Behavior documented + tested |
| 1.5 | Fix B4: chips actually filter a benefits dataset (extract benefits rows from markup into data/benefits.json); search bar filters catalog |
Typing "health" narrows visible rows |
| 1.6 | Routing: #/policies-style hash router with browser back support + scroll restore reset (B11) |
Refresh on #/buy renders Buy; back works |
| 1.7 | One canonical Claims journey (merge the 3 divergent entries, IA decision per D2/D3) | One "File a Claim" path with one outcome |
Phase 2 — Tests & quality gates (≈3–4 days, overlaps Phase 1)
| # | Task | Acceptance criteria |
|---|---|---|
| 2.1 | Unit tests (Vitest) for: router, store transitions, gesture math corpus (reuse the 28 test cases from makepad-ports/insurance/src/model.rs — they encode the same truth), chip filtering, toast queue |
≥80% branch coverage on logic modules |
| 2.2 | E2E (Playwright): boot smoke, tab circuit, FAB open/expand/close (mouse drag + touch emulation), chip select, toast TTL | All green in CI, trace/video on failure |
| 2.3 | a11y gate: axe-core in CI on all 4 screens; contrast sweep (fixes D5): raise .lbl/placeholder/tab-label to ≥4.5:1 or document exception |
axe 0 critical/serious |
| 2.4 | Visual regression: screenshot 4 screens + sheet states (peek/expanded × 3 tabs) against golden PNGs | PR-blocker on diffs |
| 2.5 | Performance budget in CI: Lighthouse mobile ≥90; forbid new backdrop-filter & filter: animations without waiver (replaces P1–P3 with stacked alpha/opacity pre-composites) |
Budgets enforced by CI |
Phase 3 — Product hardening (≈2–6 weeks, domain-dependent)
| # | Task | Notes |
|---|---|---|
| 3.1 | AuthN/Z gate (OIDC), step-up for renewals/claims/ID download (B10, S2, S4) | Idempotent POSTs + confirmation sheets with amounts |
| 3.2 | Real QR: signed short-lived token → verification endpoint (B6, S5) | Never raw policy IDs in the QR |
| 3.3 | PII audit & synthetic-data policy (S1): replace all names/IDs with clearly-marked fixtures (EXAMPLE-prefixed); provenance review for agent names |
Sign-off from whoever owns data compliance |
| 3.4 | i18n extraction (react-intl/paraglide), logical CSS properties for RTL, locale-safe date/currency | en + sw (Swahili) as first locales given user base |
| 3.5 | Security headers: CSP (script-src 'self'), referrer-policy, permissions-policy; SRI for any future external asset | Observed via scanner |
| 3.6 | API layer: typed contracts for policies/claims/agents/support; loading/empty/error states for every screen (D7) | MSW-mocked contract tests |
| 3.7 | Decide the native story: the Makepad 2.0 port already exists and is fully tested (makepad-ports/insurance/ — 28 unit + 14 integration + 8 UI tests green). Either (a) keep web-only and treat model.rs as the executable spec to port back, or (b) adopt the port as the native shell and drive both from one JSON content contract |
The port's model.rs is currently the most correct description of this app's behavior that exists anywhere — use it |
Phase 4 — Observability (post-launch guardrails, ≈2 days)
Analytics events on nav/toast/CTA (privacy-reviewed), RUM for gesture jank and tab-switch paint time, error reporting wired to CI releases, feature flags for the staged rollout.
Definition of done (product-level)
- Every interactive element either works or is gone (D2 inventory signs off).
npm run build && npm test && axe && lhcigreen on every PR.- No PII-looking strings in the repo outside
tests/fixtures/. - Gesture behavior documented + identical in web tests and the Makepad port's model tests.
- A real flow (renew or claim) completes against a mock API with loading, error, and confirmation states.
Bottom line
InsurePay's HTML is a beautifully-executed demo of ~20% of an app. It has no logic worth salvaging as code — its value is (a) the visual system and (b) the interaction vocabulary (sheet physics, tab model, toast idiom). Both are already captured in a testable form in the Makepad port. Treat the HTML as the spec, not the product: rebuild on Phase 0–1 foundations, gate with Phase 2, and only then let it call itself an insurance app.