Skip to content

refactor: JS cleanup, color tokens, legacy design port & a11y#9

Merged
maithuy2610 merged 36 commits into
mainfrom
refactor/js-constants-icons
May 24, 2026
Merged

refactor: JS cleanup, color tokens, legacy design port & a11y#9
maithuy2610 merged 36 commits into
mainfrom
refactor/js-constants-icons

Conversation

@maithuy2610

Copy link
Copy Markdown
Collaborator

Tổng quan

Đợt refactor & nâng cấp lớn cho landing page: port lại thiết kế legacy, chuẩn hoá JS, hệ thống màu/token, a11y, analytics và build pipeline.

Thay đổi chính

Thiết kế & nội dung

  • Port lại 4 section legacy + thay tên 4 feature; thay toàn bộ section bằng thiết kế legacy
  • Hero map, feature scrapbook, planner refinements
  • Đổi font handwriting sang Dancing Script + tinh chỉnh background section
  • Lưu trữ bản thiết kế landing page trước khi refactor WebP (docs/legacy/)

JS (REQ-01/02/03/06/07/08/10/11)

  • Bọc app-links & menu trong IIFE + guard DOM-ready
  • Cache DOM refs khi boot, dùng Set để check group (planner)
  • Tách navigateTo cho carousel, null-check chặt chẽ
  • Gỡ nhánh chết (count-up, hero-rotate)
  • Khôi phục carousel legacy + harden khởi tạo Lucide

Màu sắc & a11y

  • Áp dụng hệ thống color token + sửa contrast theo WCAG, gộp primary color token
  • Sửa ARIA cho carousel dots (role=group + aria-current)
  • Sửa skip-link a11y, single accordion icon cho FAQ

Build & analytics

  • Single-source GA/iOS/APK URLs qua placeholder trong package.json
  • Tách partial dùng chung: head-top / head-assets / nav-legal
  • GA4 tracking + app-link attribution + privacy compliance
  • Phục vụ ảnh preview dạng WebP (~8.4 MB tiết kiệm mỗi lần load trang)

Test & tooling

  • Test hành vi mobile flow của planner (submit/loading/back/resize)
  • Test app-links
  • baseline-capture.js cho Playwright snapshot

Test

  • npm run build (idempotency được enforce qua pre-commit husky)
  • Visual + unit tests trong tests/

🤖 Generated with Claude Code

maithuy-cmyk and others added 30 commits May 23, 2026 15:37
- archive/index.html + archive/index 2.html: orphan backups, not referenced anywhere
- .hoangsa/sessions/{chore,design,docs,feat,feature,fix,refactor}/: 223 files of old audit/spec history, no longer needed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Wrap 8 preview-* <img> tags in <picture> with WEBP source + PNG fallback
  (same pattern as hero banner)
- Add 8 untracked preview-*.webp files that were already on disk
- Restore App Store URL + data-app-link attributes in src/index.src.html
  (previously diverged from built file — commit 3803013 fixed built only)

Image weights:
  preview-user-profile  3,466 KB PNG → 310 KB WEBP  (-92%)
  preview-newsfeed      2,700 KB PNG → 328 KB WEBP  (-88%)
  preview-venue-detail  2,174 KB PNG → 320 KB WEBP  (-86%)
  preview-plan-list       980 KB PNG → 158 KB WEBP  (-84%)
  preview-AI-review       418 KB PNG → 143 KB WEBP  (-66%)
  + 3 more

Also: removed 10 macOS Finder " 2" duplicate files (logo 2.jpg,
preview-* 2.webp, ga 2.js — ~2.2 MB clutter, none referenced).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mpliance

- All 4 pages: load Google Analytics 4 via assets/js/ga.js + GTM
- CSP updated to allow GA/GTM domains across src/*.src.html
- privacy.html: rewrite section 7 to disclose GA usage (compliance)
- assets/js/app-links.js: support data-app-link-placement attribute
  for tracking which CTA was clicked (hero/final_cta)
- tests/app-links.test.js: behavior tests for analytics emission
- package.json: add test:analytics script
- README: document analytics attribute conventions and test:analytics

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures baseline screenshots of index.html across 4 viewports, records
overflow/lazy-load gaps and basic nav info — used when re-baselining
visual regression tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Append to existing lint-staged hook:
- Run `npm run build` to regenerate HTML/CSS from src/
- If working tree drifts from staged content → block commit with
  clear error message pointing to the right workflow

Catches two anti-patterns:
1. Direct edits to root *.html files (README rule violation)
2. Forgetting to rebuild after changing src/*.src.html

Also: .gitignore was ignoring entire .husky/ directory, blocking
contributors from adding hooks. Narrow to just .husky/_/ (Husky v9's
generated dir) so committed hooks like pre-commit can be modified
without -f.

Tested:
- Clean state → exit 0 (commit proceeds)
- `echo ... >> index.html && git add index.html && git commit` →
  exit 1 with explanatory message, husky blocks commit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Save a frozen snapshot of the earlier index.html design (3,295 lines,
captured 2026-05-23) as design reference. The legacy version contains
sections + a Polaroid loop + a Mobile Sticky Bar that the current
src/index.src.html doesn't have — kept here so future redesigns can
reference layout/copy choices.

Not built or served — pure reference. README explains how to use it
without copying anti-patterns (Tailwind CDN, inline tailwind.config,
Lucide CDN, arbitrary hex colors) into the live source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port from docs/legacy/index-2026-05-23.html into src/index.src.html:
- Problem + Vision section (id="problem") — timeline 7h/8h30/9h30 with colored cards
- Interactive Planner (id="planner", static demo) — age + vibe + weather filters mock
- Polaroid loop "How It Works" (id="how-it-works-loop") — 3 polaroid cards + outcome band
- Mobile Sticky Bar (id="sticky-bar") with dismiss functionality

Update 4 feature names (F3–F6) in feature grid:
- "Thông tin chi tiết" → "Chi tiết địa điểm"
- "Lưu địa điểm yêu thích" → "Lập kế hoạch cuối tuần"
- "Chia sẻ khoảnh khắc" → "Chỉ đường thông minh"
- "Đánh giá từ phụ huynh" → "Review từ phụ huynh"
+ updated SVG icons + descriptions to match new intent.

Reorder: move Testimonials to AFTER Polaroid loop (Hero → Stats → Problem
→ Planner → Features → Phone → Polaroid → Testimonials → FAQ → Download)
to match legacy narrative flow.

Dependencies added:
- Lucide CDN (https://unpkg.com/lucide@latest) for ~18 icons used by ported sections
- Google Fonts (Nunito, Caveat) for Polaroid typography
- New assets/css/polaroid.css (~80 lines) — Polaroid rotate/hover/tape styling
- New assets/js/sticky.js — dismiss handler
- CSP nới: script-src cho phép unpkg.com; style-src cho phép fonts.googleapis.com;
  font-src cho phép fonts.gstatic.com

Trade-off: ported sections use iOS green arbitrary hex (#34C759 / #24A94B /
#CFF5DA) while existing sections use Tailwind primary token (#22c55e).
Accepted intentionally to ship fast; unify in follow-up.

Spec: .hoangsa/sessions/feat/port-legacy-sections/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wholesale port of body content from docs/legacy/index-2026-05-23.html
into src/index.src.html, replacing existing src section designs with
the legacy versions. All 12 legacy sections now present:

- Header (legacy nav with iOS green gradient CTA, mobile menu)
- Hero (legacy split layout with stats badge)
- Problem + Vision (already ported in prior commit)
- Interactive Planner (already ported)
- Feature Grid (replaced cards entirely, not just names)
- Phone Carousel (legacy coverflow markup with data-carousel-* attrs)
- Polaroid loop "How It Works" (already ported)
- Community Proof / Testimonials (replaced with legacy version)
- FAQ (replaced)
- Download CTA (replaced)
- Footer (replaced partials/footer.html with legacy 187-line version)
- Mobile Sticky Bar (already ported)

JS dependency fixes:
- menu.js: rename selector menu-toggle → mobile-menu-btn to match legacy header
- src markup: rename .phone-slide → .phone-item + add id=carousel-prev/next
  so existing carousel.js coverflow engine picks up legacy phone images

Stripped during port:
- Inline <style> blocks (already extracted to assets/css/polaroid.css)
- Inline <script> blocks except JSON-LD (we have separate JS modules)
- Tailwind CDN script (compiled CSS via Tailwind CLI is sufficient)
- Inline tailwind.config block (tokens live in tailwind.config.js)

Page now matches legacy design end-to-end. Visual snapshots will need
wholesale re-baselining.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…acy design)

Match legacy design:
- favicon: assets/images/favicon.png → assets/images/logo.jpg
- add apple-touch-icon: assets/images/app-icon-160.png

Applied to all 4 src files (index, privacy, terms, community-standards)
for consistency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 UI bugs reported after wholesale section port:

1. Phone carousel broken — src's coverflow carousel.js (.phone-item +
   id="carousel-next/prev") doesn't match legacy markup (.phone-slide +
   data-offset attribute + data-carousel-* attrs). Fix:
   - Revert .phone-item rename → .phone-slide in src markup
   - Replace assets/css/carousel.css with full legacy CSS (17 rules
     incl. data-offset positioning + nav buttons + dots + 2 media
     queries for tablet/mobile breakpoints)
   - Replace assets/js/carousel.js with legacy initPhoneCarousel
     (data-offset based, autoplay, title sync, keyboard nav)

2. Lucide icons not rendering reliably — single-shot DOMContentLoaded
   call could miss if CDN was still loading. Fix:
   - Add defer to Lucide script
   - Replace single createIcons() call with retry loop: up to 20
     attempts × 150ms = 3s ceiling, exits early once window.lucide
     is available

3. Polaroid font + sticky bar — verified working in code (CSP allows
   fonts.gstatic.com for Caveat/Nunito; footer has pb-[80px] lg:pb-0
   for sticky bar clearance). No changes needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restore the "Tính năng độc quyền" interactive planner and remaining
hero/scroll micro-interactions that were dropped when the legacy
single-file landing was modularized.

- assets/js/planner.js: toggle groups, PLANNER_VENUES data, render +
  fallback banner, reset handler (ported from legacy inline script)
- assets/js/micro.js: scroll progress bar, nav active-section
  highlight, hero count-up, hero rotating suggestion, scroll reveal
- assets/js/menu.js: fix #year footer (was #copyright-year), add
  aria-label on mobile menu toggle
- assets/js/sticky.js: scroll-based show/hide via .show class
- src/input.css: data-toggle active states, .venue-card,
  #scroll-progress, nav a.is-active, .sticky-bar/.show, .reveal,
  reduce-motion query
- src/index.src.html: wire planner.js + micro.js script tags
- eslint.config.js: allow browser globals (IntersectionObserver,
  requestAnimationFrame)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
planner.js builds venue cards via template strings containing Tailwind
classes (sm:w-[38%], min-h-[160px], h-44, bg-[#F0FAF3], etc.). These
were purged by the JIT because the content config only scanned HTML
and SVG. Adding ./assets/js/**/*.js restores the missing card image
column and color tokens so the planner cards match the legacy design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FAQ <details> items rendered both the plus and minus icons stacked
because no CSS keyed icon visibility to the open state. Hide .faq-minus
by default and swap plus/minus on details[open]. CSS-only; markup
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Checkpoint of in-progress cleanup-and-webp work (webp favicon, src/built
HTML cleanup, tailwind config) before branching off for the planner
mobile-flow feature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Clip horizontal overflow at 360 px from two sources:
- #experience: belt-and-suspenders clip for coverflow side phones
- #problem: decorative blur circle (-right-10 absolute) extended 24 px
  past the right viewport edge, causing scrollWidth > clientWidth

Uses overflow-x: clip (not hidden) on both sections to avoid creating
scroll containers that could flatten .phone-carousel's 3D perspective.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… comments

- src/input.css: drop dead .venue-card.is-hidden and [data-group=city] rules (REQ-04)
- src/index.src.html: skip-link now uses sr-only focus pattern (REQ-05); sequential section numbers (REQ-09)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ull check (REQ-07, REQ-11)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-02, REQ-06)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ination

#carousel-dots was role=tablist with non-tab button children and aria-selected
on plain buttons (2 critical axe violations: aria-allowed-attr ×8, aria-required-children).
Dots are pagination, not tabs (slides aren't tabpanels), so use role=group +
aria-current instead. Fixes index a11y test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
maithuy-cmyk and others added 6 commits May 24, 2026 11:06
…holders (REQ-01)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ls (REQ-03)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ld-injected (REQ-02)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eaks

- Add `hand: Dancing Script` font family; swap Caveat -> Dancing Script (link + config)
- Replace inline font-family styles with font-hand utility + size/weight classes
- Adjust section backgrounds (features, experience, how-it-works)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add 14 named color tokens to tailwind.config.js (zinc neutrals, brand greens, semantic) — keep hand font
- Tokenize ~440 arbitrary [#hex] utilities in index.src.html, planner.js, hero-map.js (15 genuine one-offs kept: map marker, price/type badges, dark bezel, SVG illustration)
- Fix white-on-green CTAs -> primary-deep #157A36 (AA 5.43:1)
- Bright-green text: h2 accents -> primary-dark (AA large 3:1), icons/hover -> primary-deep; logo stays bright (exempt)
- Sync input.css neutral hex to zinc; collapse duplicate #2BA64A

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r tokens

- Swap remaining Caveat → Dancing Script (notes, polaroid captions)
- Collapse primary-deep/primary-dark usages to primary across CTAs, FAQ, badges
- Replace planner step icons with numbered badges

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented May 24, 2026

Copy link
Copy Markdown

Deploy Preview for splendid-speculoos-fd7e51 ready!

Name Link
🔨 Latest commit 1fb6411
🔍 Latest deploy log https://app.netlify.com/projects/splendid-speculoos-fd7e51/deploys/6a128927ab0ec70008fd4517
😎 Deploy Preview https://deploy-preview-9--splendid-speculoos-fd7e51.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@maithuy2610
maithuy2610 merged commit 455f6b2 into main May 24, 2026
4 of 6 checks passed
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