Files
boc/reports/design-audit-2026-06-20.md
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- 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
2026-06-29 16:24:48 +00:00

18 KiB
Raw Blame History

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-menu lacks a display: none rule 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: .hamburger is 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;
  }
}

  • 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 single flex nowrap row. 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 */
}

  • 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


  • 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/quixzoom text — 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;
}

  • 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: .hstat parent container (hero stats: "1280 / 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 nowrap and 315px wide. At 320px this overflows. Parent .community-pipeline is 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-visible styles 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;
}

  • 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: clip should 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-badge with 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: 1425 vs window.innerWidth: 1440 — 15px difference = scrollbar width eating into layout. Container uses max-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, no aria-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));
});
  • 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) 1280 (fixed) 1280 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.