bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
18 KiB
18 KiB
Design Audit — quiXzoom × Landvex
Date: 2026-06-20
Auditor: Bernt (Principal Product Designer / Design Systems Lead)
Scope: quiXzoom.com + Landvex.com — production-grade responsive, accessibility, consistency, enterprise-readiness
AUDIT HEADER
| Property | quiXzoom | Landvex |
|---|---|---|
| Stack | Vanilla CSS + Vanilla JS | Vanilla CSS + Vanilla JS |
| Stylesheet | /styles.css (single file) | Inline <style> (no external CSS file) |
| Framework | None | None |
| CSS approach | Custom properties + BEM-like classes | Custom properties + utility classes |
| Overflow elements (390px) | 3 real + 1 false positive | 15+ (footer = 2859px wide) |
| Overflow elements (1440px) | 0 | 2859px footer |
| Mobile-menu bug | #mobile-menu visible on desktop |
N/A |
| Social link icons | ❌ quiXzoom: raw URLs | ✅ Landvex: SVG icons |
Totals:
- 🔴 Critical: 4
- 🟠 Major: 6
- 🟡 Minor: 4
🔴 CRITICAL DEFECTS
C-001 — #mobile-menu visible on desktop (quiXzoom)
- URL: https://www.quixzoom.com/
- Viewport: 1440px desktop
- Selector:
#mobile-menu - Computed CSS:
display: block; height: 26px; position: static; top: 69px - Rotorsak:
#mobile-menulacks adisplay: nonerule at viewport > 768px. The element renders as a second nav row below the sticky header, showing all nav links as unstyled plain text. - Visual impact: Desktop nav appears broken — two rows, second row shows raw link text without styling.
Fix:
/* In styles.css — add to desktop breakpoint (already exists for .hamburger) */
@media (min-width: 769px) {
#mobile-menu {
display: none !important;
}
}
C-002 — Hamburger button 4×4px rendered on desktop (quiXzoom)
- URL: https://www.quixzoom.com/
- Viewport: 1440px desktop
- Selector:
button.hamburger - Computed CSS:
display: block; width: 4px; height: 4px - Rotorsak:
.hamburgeris not hidden at desktop breakpoints. It renders as a 4×4px invisible dot in the nav right area — a non-functional touch target that confuses layout.
Fix:
@media (min-width: 769px) {
.hamburger {
display: none;
}
}
C-003 — .footer-links 2859px overflow (Landvex)
- URL: https://www.landvex.com/
- Viewport: All viewports (780px measured, same at 390px and 1440px)
- Selector:
.footer-links - Computed CSS:
display: flex; flex-wrap: nowrap; gap: 24px; width: auto - Rotorsak: 34
<a>children in a singleflex nowraprow. Total width: 34 × ~50px + 33 × 24px gap ≈ 2492px. Causes entire page horizontal scroll.
Fix:
.footer-links {
flex-wrap: wrap;
gap: 12px 24px; /* row-gap 12px, column-gap 24px */
}
C-004 — .footer-legal-links overflow on mobile (quiXzoom)
- URL: https://www.quixzoom.com/
- Viewport: 390px mobile (right: 476px, 86px outside viewport)
- Selector:
.footer-legal-links - Computed CSS:
display: flex; flex-wrap: nowrap; gap: 20px; width: auto - Rotorsak: 7 link items × ~50px + 6 × 20px gap = 470px in a 390px viewport. No wrapping.
Fix:
.footer-legal-links {
flex-wrap: wrap;
gap: 8px 16px;
justify-content: center;
}
🟠 MAJOR DEFECTS
M-001 — quiXzoom social footer links = raw URLs (quiXzoom)
- URL: https://www.quixzoom.com/
- Viewport: All
- Selector:
footer a[href*="linkedin"], footer a[href*="twitter"], footer a[href*="instagram"] - Computed CSS: N/A — content issue
- Rotorsak: Social links render as bare
https://www.linkedin.com/company/quixzoomtext — no icon, no label. Landvex has SVG icons for the same links. Cross-site inconsistency + enterprise credibility failure.
Fix (HTML — quiXzoom footer social links):
<!-- Replace bare <a href="https://www.linkedin.com/company/quixzoom">https://...</a> with: -->
<a href="https://www.linkedin.com/company/quixzoom" aria-label="quiXzoom on LinkedIn" rel="noopener noreferrer" target="_blank">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/>
<rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/>
</svg>
<span class="sr-only">LinkedIn</span>
</a>
<a href="https://x.com/quixzoom" aria-label="quiXzoom on X" rel="noopener noreferrer" target="_blank">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.743l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
<span class="sr-only">X (Twitter)</span>
</a>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
M-002 — Landvex desktop nav has no navigation links
- URL: https://www.landvex.com/
- Viewport: 1440px desktop
- Selector:
nav(Landvex) - Computed CSS: Nav renders logo + "Get in touch" CTA only — no navigation links visible
- Rotorsak: Navigation items are not present in the rendered nav HTML on desktop. Either rendered only in a hidden mobile menu (not verified), or missing entirely from the template.
Fix: Add primary navigation links to Landvex desktop nav:
<nav>
<a href="/" class="nav-logo">LandveX</a>
<ul class="nav-links">
<li><a href="/methodology/">Methodology</a></li>
<li><a href="/verticals/">Solutions</a></li>
<li><a href="/cities/">Cities</a></li>
<li><a href="/about/">About</a></li>
</ul>
<a href="/contact/" class="nav-cta">Get in touch</a>
</nav>
@media (min-width: 769px) {
.nav-links {
display: flex;
gap: 32px;
list-style: none;
}
}
M-003 — Stat rows do not wrap at < 480px (quiXzoom)
- URL: https://www.quixzoom.com/
- Viewport: 390px mobile
- Selector:
.hstatparent container (hero stats: "12–80 / 20+ / 0%") - Rotorsak: Stat items in flex row, no wrap defined. At 390px the 3-column row is borderline; at 320px it breaks.
Fix:
.hero-stats, /* or whichever parent wraps .hstat elements */
[class*="stat-row"] {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 16px;
}
M-004 — .community-pipeline-badge white-space:nowrap on mobile (quiXzoom)
- URL: https://www.quixzoom.com/
- Viewport: 390px mobile
- Selector:
.community-pipeline-badge - Computed CSS:
white-space: nowrap; display: block; width: 315px - Rotorsak: Badge text "Field observation → Mission → Verified data" is
nowrapand 315px wide. At 320px this overflows. Parent.community-pipelineis 343px — also exceeds 320px viewport.
Fix:
.community-pipeline-badge {
white-space: normal;
overflow-wrap: anywhere;
}
.community-pipeline {
max-width: 100%;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
M-005 — Focus styles absent on interactive elements (both sites)
- URL: https://www.quixzoom.com/ + https://www.landvex.com/
- Viewport: All
- Selector:
a, button, input, select, textarea(all interactive elements) - Computed CSS at :focus-visible:
outline-width: 0px; box-shadow: none - Rotorsak: No
:focus-visiblestyles defined. WCAG 2.2 SC 2.4.11 (Focus Appearance) requires visible focus indicators.
Fix:
/* Add to both sites' stylesheets */
:focus-visible {
outline: 2px solid #0066FF;
outline-offset: 2px;
border-radius: 4px;
}
/* Suppress for mouse users only */
:focus:not(:focus-visible) {
outline: none;
}
M-006 — Footer 34-link flat list — no semantic grouping (Landvex)
- URL: https://www.landvex.com/
- Viewport: All
- Selector:
.footer-links - Rotorsak: 34 links in a single flat
<div>with no grouping, no<nav>landmark, no section headers. Enterprise footers require grouped columns (Legal / Product / Company / Cities).
Fix (HTML structure):
<footer>
<div class="footer-grid">
<nav aria-label="Legal" class="footer-col">
<h3 class="footer-col-title">Legal</h3>
<a href="/privacy/">Privacy</a>
<a href="/terms/">Terms</a>
<a href="/cookie-policy/">Cookies</a>
<a href="/dpa/">DPA</a>
<a href="/compliance/">Compliance</a>
</nav>
<nav aria-label="Product" class="footer-col">
<h3 class="footer-col-title">Product</h3>
<a href="/methodology/">Methodology</a>
<a href="/data-quality/">Data Quality</a>
<a href="/coverage/">Coverage</a>
<a href="/sla/">SLA</a>
</nav>
<nav aria-label="Cities" class="footer-col">
<h3 class="footer-col-title">Cities</h3>
<a href="/cities/stockholm/">Stockholm</a>
<!-- etc -->
</nav>
<nav aria-label="Company" class="footer-col">
<h3 class="footer-col-title">Company</h3>
<a href="/about/">About</a>
<a href="/careers/">Careers</a>
<a href="/blog/">Insights</a>
<a href="/press/">Press</a>
</nav>
</div>
</footer>
.footer-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 40px;
}
.footer-col {
display: flex;
flex-direction: column;
gap: 8px;
}
.footer-col-title {
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-muted, #6B6B6B);
margin-bottom: 8px;
}
🟡 MINOR DEFECTS
m-001 — html,body missing overflow-x: clip safety net (both sites)
- URL: Both
- Rotorsak: After root-cause fixes,
overflow-x: clipshould be added as final protection.
Fix (add AFTER root causes fixed above):
html,
body {
overflow-x: clip;
}
m-002 — Milestone chain label mismatch (quiXzoom)
- URL: https://www.quixzoom.com/
- Rotorsak: DOM contains
.community-pipeline-badgewith text "10 → 25 → 50 → 100 → 250 → 500" (not "10K" as described in PDF). The text body references "World Builder at 10,000" — the badge does not show full chain.
Fix: Badge should show abbreviated chain or scroll on mobile:
.community-pipeline-badge {
white-space: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scroll-snap-type: x mandatory;
max-width: 100%;
}
m-003 — Viewport scrollbar width causes 15px layout shift (quiXzoom 1440px)
- URL: https://www.quixzoom.com/
- Viewport: 1440px
- Rotorsak:
document.documentElement.scrollWidth: 1425vswindow.innerWidth: 1440— 15px difference = scrollbar width eating into layout. Container usesmax-width: 1160px + 24px padding= 1208px. Not a real overflow, but can cause CLS.
Fix:
.container {
padding-inline: max(24px, calc((100vw - 1160px) / 2));
}
/* Or use scrollbar-gutter to reserve space */
html {
scrollbar-gutter: stable;
}
m-004 — Hero sub-text references old wording (quiXzoom)
- URL: https://www.quixzoom.com/
- Selector:
.hero-sub(p tag in hero) - Computed content: "Earn QX Credits per approved submission. Weekly payouts — no platform fee. Launching August 1, 2026."
- Rotorsak: "Weekly payouts" is the old wording — was updated in index.html but may not have been invalidated in CDN cache.
Fix: CDN cache invalidation for / and verify live origin serves updated copy.
ACCESSIBILITY REPORT (WCAG 2.2 AA)
A-001 — No :focus-visible on any interactive element (CRITICAL)
- WCAG SC: 2.4.11 Focus Appearance (AA, new in 2.2)
- Selector: All
a, button, input, select, textarea - Fix: See M-005 above
A-002 — Hamburger button has no accessible label (quiXzoom)
- Selector:
button.hamburger - Issue: No
aria-label, no visible text, noaria-expanded - Fix:
<button class="hamburger" aria-label="Open navigation" aria-expanded="false" aria-controls="mobile-menu">
<!-- SVG icon -->
</button>
// Toggle aria-expanded on click
document.querySelector('.hamburger').addEventListener('click', function() {
const expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', String(!expanded));
});
A-003 — <main> id="main-content" present, skip-link present (quiXzoom) ✅
- Skip-to-main-content link exists (
top: -100px, activated on focus). Correct.
A-004 — Touch targets ≥ 44×44px
- Hamburger: 38×32px (quiXzoom) — below WCAG 2.2 minimum 44×44px
- Fix:
.hamburger {
min-width: 44px;
min-height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
CONSISTENCY MATRIX
| Element | quiXzoom homepage | quiXzoom /earn/ | Landvex homepage | Status |
|---|---|---|---|---|
| Nav: desktop links | ✅ (.nav-links visible) | Unknown | ❌ No nav links | INCONSISTENT |
| Nav: mobile hamburger | ✅ 38×32px | Unknown | Unknown | — |
| Footer: social icons | ❌ Raw URLs | Unknown | ✅ SVG icons | INCONSISTENT |
| Footer: legal links | 7 links, flex nowrap | Unknown | 34 links, flex nowrap | SAME BUG |
| Footer: copyright | Not extracted | Unknown | "© 2026 Landvex Inc" | — |
| Credits value (hero) | 12–80 (fixed) | 12–80 | N/A | CONSISTENT ✅ |
| Currency | USD ($28 floating card) | Unknown | N/A | OK ✅ |
| Dark theme bg | #0a0a0a | Unknown | #0a0a0a | CONSISTENT ✅ |
| Accent blue | #0066FF | Unknown | #0066FF | CONSISTENT ✅ |
DESIGN TOKEN SYSTEM
/* =============================================
SHARED TOKEN SYSTEM — quiXzoom + Landvex
Apply to :root in both sites' stylesheets
============================================= */
:root {
/* === COLORS === */
--color-bg-primary: #0a0a0a;
--color-bg-secondary: #111111;
--color-bg-surface: #181818;
--color-bg-white: #FFFFFF;
--color-bg-off: #F7F7F5;
--color-accent: #0066FF;
--color-accent-dark: #0052CC;
--color-accent-pale: rgba(0, 102, 255, 0.07);
--color-success: #00C853;
--color-warning: #FF9800;
--color-error: #FF3B30;
--color-text-primary: #FFFFFF;
--color-text-body: #3a3a3a;
--color-text-muted: #6B6B6B;
--color-text-dim: #888888;
--color-text-dark: #0a0a0a;
--color-border: rgba(255, 255, 255, 0.07);
--color-border-light: rgba(0, 0, 0, 0.1);
/* === TYPOGRAPHY === */
--font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Helvetica Neue', sans-serif;
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.375rem; /* 22px */
--text-2xl: 1.75rem; /* 28px */
--text-3xl: 2.25rem; /* 36px */
--text-4xl: 3rem; /* 48px */
--text-hero: clamp(2.75rem, 6vw, 5rem);
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-extrabold: 800;
--font-weight-black: 900;
--line-height-tight: 1.1;
--line-height-snug: 1.3;
--line-height-normal: 1.65;
--line-height-relaxed: 1.8;
--measure: 65ch; /* max line length for body text */
/* === SPACING (4px base) === */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-16: 64px;
--space-20: 80px;
--space-24: 96px;
/* === BORDER RADIUS === */
--radius-sm: 8px;
--radius-md: 14px;
--radius-lg: 24px;
--radius-xl: 32px;
--radius-full: 100px;
/* === SHADOWS === */
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
--shadow-md: 0 8px 24px rgba(0, 0, 0, 0.12);
--shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.14);
--shadow-hover: 0 16px 48px rgba(0, 0, 0, 0.14);
/* === BREAKPOINTS (use in @media queries) === */
/* --bp-xs: 320px */
/* --bp-sm: 390px */
/* --bp-md: 768px */
/* --bp-lg: 1024px */
/* --bp-xl: 1280px */
/* --bp-2xl: 1440px */
/* --bp-3xl: 1920px */
/* === LAYOUT === */
--container-max: 1160px;
--container-pad: 24px;
--nav-height: 68px;
--section-pad: 96px;
/* === TRANSITIONS === */
--transition-base: 0.22s cubic-bezier(0.25, 0.46, 0.45, 0.94);
--transition-fast: 0.15s ease;
}
IMPLEMENTATION ROADMAP
Priority 1 — Overflow & Layout Crashes (fix immediately)
| ID | Fix | File | Est |
|---|---|---|---|
| C-001 | Hide #mobile-menu on desktop |
quiXzoom/styles.css | 2 min |
| C-002 | Hide .hamburger on desktop |
quiXzoom/styles.css | 1 min |
| C-003 | flex-wrap: wrap on .footer-links |
Landvex/index.html inline style | 1 min |
| C-004 | flex-wrap: wrap on .footer-legal-links |
quiXzoom/styles.css | 1 min |
| m-001 | Add overflow-x: clip to html/body |
Both | 1 min |
Total: ~6 minutes of CSS edits.
Priority 2 — Cross-site inconsistencies
| ID | Fix | File | Est |
|---|---|---|---|
| M-001 | Replace raw social URLs with SVG icons | quiXzoom footer | 20 min |
| M-002 | Add desktop nav links to Landvex | Landvex/index.html | 30 min |
| M-006 | Restructure Landvex footer into columns | Landvex/index.html | 45 min |
Priority 3 — Accessibility failures (WCAG 2.2 AA)
| ID | Fix | File | Est |
|---|---|---|---|
| A-001/M-005 | Add :focus-visible styles globally |
Both stylesheets | 5 min |
| A-002 | Add aria-label + aria-expanded to hamburger |
quiXzoom/index.html | 5 min |
| A-004 | Expand hamburger touch target to 44×44px | quiXzoom/styles.css | 2 min |
Priority 4 — Enterprise polish
| ID | Fix | Est |
|---|---|---|
| M-003 | Stat rows → auto-fit grid | 10 min |
| M-004 | Community pipeline badge white-space fix | 5 min |
| m-002/m-003 | Scrollbar gutter + layout polish | 10 min |
| Token system | Apply CSS variables to both sites | 2h |
| Footer columns | Landvex grouped footer nav | 45 min |
GLOBAL SAFETY NET (add last, after all root causes fixed)
/* Add to BOTH sites — last rule in stylesheet */
html,
body {
overflow-x: clip;
}
Note: overflow-x: clip (not hidden) preserves sticky positioning while preventing horizontal scroll. Apply only after root causes are fixed — never as primary solution.