Files
novalon-website/CLAUDE.md
T
zhangxiang 611b6b87fe feat(ui): 营销站 UI/UE/UX 治理 P0→P3 收官
复评(impeccable critique)全部 Priority Issues 与 Minor Observations 落地:
- P0 数字口径:MetricsBasisNote 单一真源 + metrics-basis.test fs 防线,接入
  products/solutions/services/home/about;纯渲染端加角注,不动 DB/seed 值
- P1 转化断头路:敬请期待态 + 404 语境出口 + services 空态双出口
- P1 首页并卡:TrustSection 早鸟横幅并入 CasesSection 单张深色共创卡
- P2 CTA 治理:CONSULT_CTA_ALLOWLIST 白名单 + 逐字符扫描器,/contact CTA 全归一
- P3 polish:footer 补 服务/公司列、contact 死三元/空槽/toast、GlobalErrorTracker 幂等日志
- 文档同源:PRODUCT/CONTEXT 动效带统一 180–280ms;DESIGN.md 立 The 10px Floor;
  font-floor.test 守卫禁止再引入 <10px(保留 10/11px 有意 Bain 微标签)

Gates: type-check 0 · jest 1581/132 · lint 0e/126w · contrast 7/7 · headings 10/10
2026-09-20 09:17:07 +08:00

211 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Commands
```bash
# Development
npm run dev # Start dev server on port 3000
npm run dev:clean # Clean .next/dist then start dev server
# Build & Preview
npm run build # Build production files to dist/
npm run build:clean # Clean then build
npm run preview # Serve dist/ on port 3000 (npx serve)
# Deploy (统一发布脚本)
./scripts/deploy.sh build # 构建静态产物
./scripts/deploy.sh deploy # 构建并发布到生产服务器
./scripts/deploy.sh deploy --skip-build # 使用现有 dist/ 直接发布
./scripts/deploy.sh rollback # 回滚到最近一次远程备份
./scripts/deploy.sh status # 查看生产环境发布状态
# Linting & Type Checking
npm run lint # ESLint (configured in config/lint/.eslintrc.json)
npm run type-check # tsc --noEmit
# E2E Testing (Playwright)
npm run test # Run all E2E tests
npm run test:e2e # Same as above
npm run test:smoke # Only @smoke-tagged tests
npm run test:visual # Visual regression (Desktop Chrome)
npm run test:visual:all # Visual regression (all projects)
npm run test:visual:update # Update visual snapshots
npx playwright test --grep "test name" # Run a single E2E test by name
# Unit Testing (Jest)
npm run test:unit # Run all unit tests
npm run test:coverage # Coverage report (thresholds: 80% branches/functions/lines)
npx jest --testPathPattern="button" # Run a single test file matching pattern
# Quality Checks
npm run check:contrast # Color contrast audit
npm run check:headings # Heading hierarchy audit
npm run lighthouse # Lighthouse CI performance audit
# Database (Prisma + SQLite)
npm run db:seed # Seed the dev database
npm run db:reset # Reset database with migrations
```
## Architecture
### Tech Stack
- **Next.js 14** (App Router) with **hybrid rendering** — static pages + API routes (Note: `output: 'export'` was recently removed; the project is transitioning away from pure static export)
- **React 18**, **TypeScript 5** (strict mode with `noUncheckedIndexedAccess`)
- **Tailwind CSS 3** with design tokens exposed as CSS custom properties (all tokenized via `var()` references in `tailwind.config.js`)
- **Framer Motion** for animations, **Lucide React** for icons, **Zod** for validation
- **shadcn/ui** pattern (Radix UI + class-variance-authority + tailwind-merge)
- **Prisma** with SQLite for backend data (admin, auth, CMS)
### Path Alias
`@/` maps to `src/` — configured in both `tsconfig.json` (`paths`) and `jest.config.js` (`moduleNameMapper`).
### TypeScript Strictness
Beyond `strict: true`, the project enables:
- `noUncheckedIndexedAccess: true` — array/object index access returns `T | undefined`, requiring null checks. This is a significant constraint to be aware of when writing code.
- `noImplicitReturns`, `noFallthroughCasesInSwitch`, `noUnusedLocals`, `noUnusedParameters`
### Route Structure (App Router)
```
src/app/
├── layout.tsx # Root layout: fonts, metadata, theme, analytics, SEO schemas
├── (marketing)/ # Route group — all public marketing pages
│ ├── layout.tsx # Shared: Header + Footer + PageTransition + ErrorBoundary
│ ├── page.tsx # Home (delegates to home-content-cms.tsx)
│ ├── about/ # About page (client.tsx)
│ ├── news/ # News list + [slug] detail
│ ├── contact/ # Contact form
│ ├── products/ # Products hub (HSI model)
│ │ ├── page.tsx # Product listing (enterprise suites + standalone)
│ │ ├── [id]/ # Product detail (four-layer narrative)
│ │ ├── standalone/[id]/ # Standalone product detail
│ │ ├── erp-upgrade/ # Specific product landing pages
│ │ └── erp-upgrade-v3/
│ ├── services/ # Services list + [id] detail
│ ├── solutions/ # Solutions list + [id] detail (cross-references products)
│ ├── cases/ # Case studies
│ └── team/ # Team page
├── api/
│ ├── admin/ # Admin API
│ ├── auth/ # Authentication API
│ ├── cms/ # CMS API routes (draft mode, revalidation)
│ └── contact/ # Contact form submission
├── privacy/, terms/ # Legal pages
└── fonts/ # Local font files (Geist Sans/Mono)
```
### Archiving Convention
When replacing a page or component with a new version, move the old one to an `_archive/` subdirectory (e.g., `src/app/(marketing)/_archive/` for old homepage iterations). The archive is excluded from TypeScript compilation via `tsconfig.json`.
### Dark Mode
Dark mode uses the `data-theme="dark"` HTML attribute (not Tailwind's `dark:` class). Tailwind is configured with `darkMode: ['variant', '[data-theme="dark"] &']` — use the **variant** form, not `['selector', ...]`, because the selector form is accepted by the config but JIT emits zero `dark:` rules under Turbopack + Next.js 16.
**Three-state preference model** (`src/lib/theme.ts` is the single source of truth):
| Stored value (`localStorage['novalon-theme']`) | Meaning |
|---|---|
| `'light'` / `'dark'` | Explicit user choice — always wins over the OS |
| `'system'` / absent | Follow OS `prefers-color-scheme` (**default for new visitors**) |
- `src/app/layout.tsx` has an inline `<script>` in `<head>` that resolves the preference and sets `data-theme` **before first paint** (FOUC prevention). It must stay semantically in sync with `src/lib/theme.ts` — change both together.
- `src/components/theme/theme-toggle.tsx` cycles `跟随系统 → 浅色 → 深色`. It listens to `matchMedia('(prefers-color-scheme: dark)')` for live OS changes (only effective in the `system` state) and to `storage` events for cross-tab sync.
- `html[data-theme]` is an **output** of preference resolution — never read it back as the user's preference, or the `system` state collapses into `dark` on dark-OS machines and the cycle deadlocks.
### HSI Information Architecture
The site follows a **Hub-Spoke-Independent** model (see `CONTEXT.md` and ADR-0002):
- **Hub**: `/products` — product catalog, split into "企业套装" (6 enterprise products) and "专业产品" (standalone)
- **Spoke**: `/solutions` — industry scenarios, each recommending product combinations from the Hub
- **Independent**: Standalone products in the specialized zone have their own narrative path
### Four-Layer Narrative Model
Every detail page (product/service/solution/standalone) follows the same four-layer structure:
1. **L1 Hero**: Emotional entry with visual, title, value prop, status badge
2. **L2 Value Rationale**: Product-specific — features/benefits for products, pain-points→architecture for solutions, challenges→results for services
3. **L3 Trust Proof**: Case studies, data proofs, certifications, testimonials (currently stubbed — company is pre-launch)
4. **L4 CTA Conversion**: Primary action + secondary action + cross-recommendations
The detail components implementing this live in `src/components/detail/`:
- `detail-hero.tsx`, `detail-product-value.tsx`, `detail-trust-section.tsx`, `detail-cta-section.tsx`
- `solution-value.tsx`, `service-value.tsx` (type-specific L2 variants)
- `detail-cross-recommend.tsx` (cross-links between products↔solutions↔services)
### CMS Content Architecture
The app has a **mock CMS layer** at `src/lib/cms/` that simulates a headless CMS (no real backend — designed to be swapped with real API calls later):
- `types.ts` — ContentModel, ContentItem, ContentZone, ThemeConfig definitions
- `ContentZoneRenderer.tsx` — Renders a zone's items in grid/list/carousel layouts, delegating each item to `getItemRenderer(modelCode)` from the component registry
- `mock-home.ts` — Mock data for the homepage zones
- `component-registry.ts` — Maps model codes to React renderer components
The homepage (`home-content-cms.tsx`) initializes the CMS and fetches mock content zones (hero, stats, services, solutions, cases, news).
### Design Token System
All visual tokens are defined as **CSS custom properties** in `src/app/globals.css` (`:root` block) and mapped into Tailwind's config via `var()` references:
- **Colors**: `--color-ink` (deep charcoal #0A0E14), `--color-brand` (vermilion #C41E3A), accent colors for service-coding (blue, teal, amber, purple), functional colors (success, warning, error, info), dark-section overrides
- **Typography**: `--font-size-*`, `--line-height-*`, `--letter-spacing-*` all mapped to Tailwind's `fontSize`/`lineHeight`/`letterSpacing` scales
- **Spacing, radius, shadows** all tokenized through CSS variables
- **Transitions**: `--transition-fast/normal/slow`, `--ease-ink` (cubic-bezier), `--ease-sharp``--ease-spring*` 死令牌已于 2026-09-19 polish 删除)
- **Dark mode**: `data-theme="dark"` attribute (set via inline script before paint to prevent flash); flipped by the `[data-theme='dark']` override block in `globals.css`, which only re-maps CSS variables — components do not restyle per element
### Design DNA Framework
The site follows a three-dimensional design system (see `CONTEXT.md`):
- **Dimension 1 — Design System**: Quantifiable tokens (colors, typography, spacing, radius, shadows, motion, components)
- **Dimension 2 — Design Style**: Qualitative perception (atmosphere, visual language, composition, imagery, brand tone)
- **Dimension 3 — Visual Effects**: Scroll animations, micro-interactions, parallax, SVG effects
The **Consulting Professional** aesthetic (inspired by Accenture + Bain) is the primary skeleton. **Ink cultural elements** (水墨) are secondary decorations limited to ≤6 locations (logo, dividers, transition animations, footer texture).
**Brand red (#C41E3A) usage rule**: Every page must have ≥3 brand-red touchpoints. It must never be used as paragraph text color, as a large background, or alongside accent colors in the same card. Area ≤10%. Exception: full-bleed dark-red statement/CTA blocks use the separate `crimson-veil` (#8B1530) token — see DESIGN.md「Crimson Veil」(rule clarified 2026-09-19 critique to resolve this apparent contradiction).
**Motion design**: Animations are purposeful (not decorative), fast (150-300ms), use `ease-ink` [0.22, 1, 0.36, 1] as default easing, and stagger children by 30-60ms. No continuous looping animations except pulse-soft for skeletons. No spring easings for content entry. No animations >700ms.
### Data Layer
All structured content data is in `src/lib/constants/` as TypeScript constants — no API/database for marketing content:
- `products.ts` — 6 enterprise products (ERP, CRM, CMS, BI, SDS, OA) + standalone products (NovaVis)
- `services.ts`, `solutions.ts` — Service and solution definitions
- `cases.ts` — Case studies with industry filtering
- `navigation.ts` — Nav structure + mega dropdown data
- `company.ts`, `stats.ts`, `team.ts`, `news.ts`, `methodology.ts`
- `hero-themes.ts` — Per-product hero visual theme variants
- `cross-references.ts` — Cross-links between products/solutions/services
Each product/solution/service implements the `Product`/`Solution`/`Service` interface which includes `caseStudies[]`, `dataProofs[]`, `certifications[]`, etc. for the four-layer narrative.
### Backend Layer (Prisma + API Routes)
The project has a backend layer for admin/auth/CMS functionality:
- **Prisma** with SQLite (`prisma/dev.db`) for admin user data, auth, and CMS content management
- API routes at `src/app/api/admin/`, `auth/`, `cms/`, `contact/`
- The marketing pages use mock data from `src/lib/cms/mock-home.ts`, but the CMS API routes suggest a real CMS backend is planned
### Component Organization
```
src/components/
├── ui/ # Base: Button, Card, Input, Badge, ScrollReveal, AnimatedCounter, etc.
├── layout/ # Header, Footer, MobileTabBar, Breadcrumb, MegaDropdownMobileMenu 已删除,抽屉内置于 Header)
├── sections/ # Page section components: HeroSectionV2, ServiceGrid, CTASection, etc.
├── detail/ # Four-layer narrative: DetailHero, ProductValueSection, DetailTrustSection, etc.
├── seo/ # Structured data (OrganizationSchema, WebsiteSchema, etc.)
├── analytics/ # GA4, error tracking, cookie consent, scroll depth, outbound links
├── cms/ # CMS renderers (ContentRenderer, SectionRenderer, FieldRenderer)
├── content/ # sections.tsx, testimonials.tsx
└── providers/ # (currently empty)
```
### Testing Setup
- **Jest** (unit): Tests alongside source in `src/**/*.test.{ts,tsx}`, coverage threshold 80%. Config in `config/test/jest.config.js`. Uses `@/` path alias and ts-jest with `jsx: 'react-jsx'` transform (since tsconfig uses `'preserve'`). Run single tests with `npx jest --testPathPattern="component-name"`.
- **Playwright** (E2E): Tests in `e2e/`, config in `e2e/playwright.config.ts`. Auto-starts `npm run preview` as web server. Visual regression snapshots in `e2e/visual-snapshots/`. Three browser projects (chromium, firefox, webkit) plus dedicated visual regression projects at desktop/tablet/mobile breakpoints. Run single tests with `npx playwright test --grep "test name"`.
### Build Output
The project builds to `dist/` (configured via `distDir` in `next.config.mjs`). It is served via Nginx (see `nginx-static-production.conf`) with CDN support (`assetPrefix` respects `CDN_DOMAIN` env var). Images are unoptimized (static export limitation). Note: `output: 'export'` was recently removed from the config — the project may be moving toward a hybrid SSR + static model.
### Commit Convention
Uses Conventional Commits with commitlint (`@commitlint/config-conventional`). Husky + lint-staged enforces linting on pre-commit.
### Component Versioning History
The project has gone through multiple design iterations. Older component versions:
- `components/detail-v2/` — previous iteration (deleted per git status, migration to `detail/` completed)
- `components/detail-v3/` — another iteration (deleted, same reason)
- `home-content-v2.tsx` through `home-content-v11.tsx` — archived homepage iterations in `(marketing)/_archive/`
- The current canonical components are in `components/detail/` and `home-content-cms.tsx`