Skip to content

Extension UI/UX modernization + PWA polish (tokens, popup redesign, Shadow DOM, iOS Safari verified)#114

Open
BSalaeddin wants to merge 206 commits into
mainfrom
dev
Open

Extension UI/UX modernization + PWA polish (tokens, popup redesign, Shadow DOM, iOS Safari verified)#114
BSalaeddin wants to merge 206 commits into
mainfrom
dev

Conversation

@BSalaeddin

Copy link
Copy Markdown
Collaborator

What

Full UI/UX modernization of the browser extension + PWA install surface, brand preserved (caramel orange #ea6925, warm neutrals).

  • Design tokensassets/tokens.css: single-source --cm-* palette/radius/shadow/motion/type system with a full dark-mode palette (prefers-color-scheme); enforced by tests/ext-tokens.test.mjs (raw brand hexes banned outside tokens.css across all extension stylesheets).
  • Popup redesign — wave/glassmorphism era removed; fluid min(420px,100vw) shell, brand accent bar, skeleton loading, horizontal SVG wordmark, real settings button, full dark mode. Auth a11y: associated labels, autocomplete, password show/hide, role=alert errors, aria-live toast. Badges moved from inline hex styles to tier classes (test-pinned).
  • Content UI → Shadow DOM — injected prompt/modals render in shadow roots (host-page !important hardening deleted); the three host IDs stay in light DOM so store-detect.js contracts hold (test-pinned). Focus traps in both modals, 100dvh overlays, unified card language. Fixes Firefox content-UI styling (its manifest ships no CSS array; positioning now inline on hosts).
  • PWA — manifest colors were off-brand navy/purple → brand orange/cream; maskable 512 icon, 3 shortcuts, appleWebApp + light/dark themeColor, apple-touch-icon, display_override spec fix.
  • Budgets — content-scripts 98→102 KB, popup 34→38 KB (Shadow-DOM plumbing + a11y/SVG weight; documented in CLAUDE.md).

Verification

  • 36/36 extension unit tests; size-limit, prettier, oxlint, strict tsc all green.
  • WebKit (Safari-engine) rendering matrix: 320/375/430/420px × light/dark, zero console errors, no overflow, ≥40px tap targets.
  • Real Xcode 26.2 safari-web-extension-converter: only warning is identity (OAuth already degrades to email/password); iOS + macOS targets build.
  • Verified in real iOS Safari (iPhone 16e sim, iOS 26): extension enabled programmatically, popup renders correctly light+dark over a live store page. Automation codified in scripts/ios-sim-enable-and-verify.sh (plist state write + idb fallback).
  • Two-agent pre-merge review (correctness + public-repo hygiene/leak audit): SAFE TO MERGE / CLEAN TO PUBLISH; both findings fixed (settings-gear visibility in the signed-in coupons view + test pin, comment drift).

🤖 Generated with Claude Code

BSalaeddin and others added 30 commits April 14, 2026 04:03
…ctor dispatch (qOne/qAll), HARD GATE 2 contract fields supported, dev-mode cache bypass, restriction warning chips in popup
… taken before apply — was treating permanently-mounted error containers (aria-live regions, empty banners) as 'error present', causing the loop to never stop on valid coupons
…d — fixes 'no savings' on aria-live status regions
…tes without priceContainer (logos.com) need stickiness check, not error-region-text checks alone
…rop state — '✓ Coupon Applied' headline + 'Discount visible in your cart' instead of misleading 'No Savings This Time' on sites without priceContainer
…ow OR error text) — replaces fixed 1.2s sleep that caused next attempt to mis-attribute prior code's slow-rendered row on logos.com et al
…put+apply — partial configs caused try-loop to stack invalid coupons
…ble=false

christianbook.com has full 5-field xpath contract but selectors live
on the post-login checkout page, not the cart entry_url. Extension
cannot reach them without driving the multi-step quick-checkout
flow. Honor the extension_compatible=false verdict (set by agent
HARD validation or manual review) so these stores drop out of the
extension's effective list while keeping the API verifier path
operational.
Some Shopify themes (paragonsports and similar) render two
.alert.alert--error containers — a hidden empty placeholder and a real
one that fills with text. qOne returned the placeholder and missed
the actual rejection message, so the extension thought every code
applied successfully when in fact every code was being rejected by
the API silently.

New _firstVisibleErrorEl helper iterates all selector matches and
returns the first visible non-empty one. Both snapshotErrorState
(baseline) and detectCouponError now use it. Verified live: the
selector .alert.alert--error has 2 matches on paragonsports — the
hidden empty one and a visible one reading 'Discount code cannot be
applied to your cart'. Old code missed it entirely.
Cycle-9 fix only updated detectCouponError + snapshotErrorState. The smart
cart-signal waiter (waitForCartSignal) was still using qOne(errorIndicator)
directly, which on multi-error-container sites latches to the empty
placeholder forever. The waiter would never trip the 'errored' early-exit
and would burn the full 4s timeout on every rejected code instead of
returning fast.

Switching to _firstVisibleErrorEl matches the rest of the cycle-9
plumbing. paragonsports-class sites now exit the waiter as soon as the
visible non-empty error renders, instead of waiting for the timeout.
Some Shopify themes (paragonsports-class) gate the discount apply on
pointerdown rather than click. A bare .click() fires the click handler
but not the pointer chain, so the site's own JS skips it.

Now the extension dispatches the full sequence pointerdown -> pointerup
-> mousedown -> mouseup -> click before the keydown Enter fallback.
Matches what a real user click produces.

Sites that genuinely require event.isTrusted=true (olaplex,
paleoonthego) still won't accept synthetic events - those are correctly
flagged extension_compatible:false by the agent's HARD VALIDATION 2
which now uses the same probe sequence to avoid false-flagging sites
that pass on pointer events but fail on bare .click().
…4s→10s — Polaris checkouts respond in 5-8s; pd→pu→md→mu→click broke React handlers
…d/unverified/not-valid) on page, SSR, and extension
…tatus filter + include pending in store lists
…es domain-validate, search-supported via couponsSql, align extension api-key header
…es substring false-positives, fixes hyphen-prefixed checkout hosts)
…pon data in render (accurate data-code, no markup injection)
…ns (no emoji), responsive modals, system type"

This reverts commit 28a2423.
BSalaeddin and others added 30 commits July 15, 2026 23:31
…ops framework-managed NODE_ENV; drift gate counts commented assignments
…t, Integration (DB); note compose-build is path-filtered
…ation, dead/dup CSS purge, token-discipline test
…traps, dvh overlays, host-id contract kept; size budgets 102/38 KB
…wraps so badges can't crush the title at 320px
… state write + idb fallback), pulled from the Mac Phase-4 run
…w (+test pin), 98->102KB comment drift, sim script UDID arg required, tap.py cleanup
…DOM content UI, PWA polish, iOS Safari verification)
…sh, dark parity, a11y focus states, reduced-motion support
UI/UX: site-wide visual refinement (landing, marketing, auth)
…ht (idle-mounted, desktop-only, shared ticket module)
…n, CSS spinner in providers, lazy-boundary test (-375KB home)
…logos, unoptimized hero wordmark, right-size 415/298/139KB PNGs to <10KB
…WebGL) + honest 15s nav URL timeouts — kills the nav flake class
… deterministic e2e (PR #117)

UI/UX: split hero — copy left, interactive 3D coupon right
…nder the model, inline CTAs, clean-glass ticket (drop stub), centered full-viewport loader, remove 2nd welcome section
…ner 3D model, centered loader, drop 2nd section (PR #118)

Landing hero refinements: stats-as-coupons, inline CTAs, cleaner 3D model, centered loader, drop 2nd section
feat(landing): hero stats as three same-size 3D coupon models
feat(landing): stat coupons float like the main model with in-canvas 3D text
fix(landing): hero clip bleed, legible 3D stat text, warm dark surfaces, themed body bg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants