04 / WORK / CASE STUDY
Naily Storefront & Canvas Product Customizer
The consumer storefront for a press-on nail brand — shopping flow, payments and e-invoicing, four social logins, and an in-browser Canvas design editor; spanning module work and a redesign on the CRA site, and design-debt cleanup on its Next.js 15 successor.
- TYPE
- FRONTEND
- SCOPE
- In production · contributor → refactor lead
- STACK
- REACT · NEXTJS · CANVAS · ANTD · STYLED-COMPONENTS · PLAYWRIGHT
- LINKS
- Internal project (private)
01 / PROBLEM
Naily sells custom press-on nails, and what customers need is not a form — it is “upload a photo of your nails → edit the design → preview the result live”, which demands an in-browser Canvas editor with ten independently designable nails. Around it sits a full e-commerce SPA that kept accreting features: cart, checkout, Taiwan e-invoicing, ECPay payments, four social logins and marketing analytics in one codebase, with duplicated components and clashing styles growing in the gaps. An early-2026 redesign cut the five-step customization flow to three and unified the visual language; the storefront then moved to Next.js 15 App Router for SSR and SEO, followed immediately by a systematic design-token debt cleanup.
02 / CONSTRAINTS
- A commercial brand project; the codebase is private. The CRA site had five contributors — this page keeps my share and others’ work explicit.
- Performance and debt work had to stay frontend-only, with no new backend resources: bundle trimming, CSS-variable consolidation, image compression.
- The Next.js build keeps auth in localStorage, but middleware runs on the Edge Runtime, which cannot read it — centralized login redirects were impossible, so protection devolves to each page’s client component.
- The Next.js repo has three test files and no CI: large-scale dead-code deletion is gated by full-repo grep re-verification plus a clean build, not a test suite.
03 / ARCHITECTURE
The CRA site: a React 18 SPA with 72 routes under react-router v7, 56 components code-split via React.lazy; state is deliberately four Contexts plus 37 service files, no Redux — most state is one shopper’s flow, not cross-module. A single Axios entry point owns JWT refresh, json-bigint precision and guest-mode fallbacks; GA4/GTM/Pixel instrumentation is defined and validated by a ~2,500-line event schema; tests are 572 Jest cases plus 33 Playwright E2E specs on a daily GitHub Actions schedule.
The Next.js 15 build: migrated from CRA to the App Router for SSR/SSG — 53 page routes, 55 feature modules, 352 component files, deployed standalone under PM2. A post-migration sitewide inventory exposed the real debt: the design-token file referenced by only 5% of files, four styling systems coexisting (Ant Design, react-bootstrap, styled-components, CSS Modules), and 3,463 hard-coded hex colors in CSS — the starting line of the ongoing debt-cleanup phase.
04 / RESPONSIBILITIES
Two periods, two roles, kept distinct. On the CRA site — five contributors — I account for 136 of 629 commits (21.6%): building the internal admin modules in May–July 2025 (plans, users, products and inventory, orders, payments and invoicing), then serving as the main developer of the early-2026 redesign (the Canvas editor and three-step custom flow, homepage performance, CSS debt, the incognito-mode audit). The Next.js design-debt cleanup is solo: all 141 commits are mine (July 2026, five focused days), eight of them tagged as paired with Claude.
05 / CHALLENGES → SOLUTIONS
CHALLENGE
The old customization flow ran five steps — length itself is a conversion risk — and the old architecture had no draft saving, no reuse of past uploads, no API retry: an interrupted session started over from scratch.
SOLUTION
Redesigned to three steps (spec → design → preview), delivering nine modules at once: 30-second draft autosave, reusable upload history, API retry, six nail-shape SVG renderers, ten independently designed nails, image editing, dual-view preview and a share panel. The build split the nine modules across eight parallel AI coding agents in one pass (one commit: 77 files, 25,804 lines), with me doing the integration acceptance — and it still took two more polish commits to reach shippable quality.
CHALLENGE
Three token files in the Next.js build each declared the same CSS custom properties with different values — which one won depended entirely on webpack chunk load order. One symptom: --font-size-lg through 5xl all flattened to 1rem, silently collapsing every heading size on the site.
SOLUTION
Instead of surface alignment, first traced which values were actually winning, then converged on those: z-index kept the unified system already depended on by 15+ components in a self-consistent order, with the other two files re-pointed to it; font, radius and shadow tokens now forward to the consolidated file — same names, one value. Every token was then verified identical across all chunk combinations in the build output; hard-coded hex colors fell from 3,463 to 2,131 (−38%), branch still in progress.
CHALLENGE
The repo had piled up duplicated and unreferenced CSS and assets (Cart.module.css coexisting with Cart2025.module.css, and so on), yet held only three test files — nothing guaranteed that deleting a file would not break a page.
SOLUTION
The deletion gate became “full-repo grep re-verification of zero imports, plus one clean build”: round one removed 80 confirmed-dead files; the current branch removes another 111, a net ~44k lines down (in progress). The method works but explicitly does not scale and cannot see dynamic imports — a tracked limitation, not a hidden risk.
CHALLENGE
Incognito mode in Safari and others restricts or disables localStorage outright, and the site called the raw API in many places — device identity, guest order tracking, data sync — so one DOMException took whole features like guest order lookup down.
SOLUTION
A sitewide audit flagged 19 issues and established safeStorage — a wrapper with an in-memory fallback — as the fix standard. As of this inventory, 50 files have migrated and 36 (including core-severity files the audit named) have not: audit done, remediation in progress, recorded as-is.
06 / SYSTEM FACTS
72
routes (56 lazy-loaded)
25,804
lines shipped in one commit
605
tests (572 Jest + 33 E2E)
-38%
hard-coded colors (3,463→2,131)
07 / LESSONS
Massively parallel generation buys a skeleton, not consistency: after eight agents produced 25k lines in one pass, it still took two polish rounds and a later token-and-icon consolidation to converge on one style. Next time the shared design tokens and component conventions get locked before the work is split across agents, not merged after.
CSS load order is an implicit contract: with three files declaring the same tokens at different values, the winner was decided by chunk order, not by anyone’s intent. Next time a single token source is enforced from day one, with lint blocking raw hex and duplicate declarations — instead of cleaning up 3,463 instances later.
An audit not followed by scheduled execution is just a document: the incognito audit listed 19 issues and a phased plan, and half a year later 36 files remain unmigrated. Either the findings go into the sprint on the spot, or the state is honestly “known and unfixed” — the document must never impersonate the fix.
NEXT
NKUST Alumni Association Platform