130 lines
5.9 KiB
Markdown
130 lines
5.9 KiB
Markdown
|
|
# Landing Page Refactor — Design Spec
|
|||
|
|
**Date:** 2026-03-26
|
|||
|
|
**Status:** Approved
|
|||
|
|
|
|||
|
|
## Goal
|
|||
|
|
Replace all existing marketing home components with content and styles from `texpixel-landing.html`. The UI/UX must exactly match the reference file. The home page is a marketing/broadcast page; all CTAs navigate to `/app`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## CSS Strategy
|
|||
|
|
- Extract the full `<style>` block (lines 10–1593) from `texpixel-landing.html` into `src/styles/landing.css`.
|
|||
|
|
- **Scope all rules** under a `.marketing-page` wrapper class to prevent bleed into the `/app` workspace.
|
|||
|
|
- Body-level rules (`body { background }`, `body::before` grid overlay) are converted to `.marketing-page` and `.marketing-page::before` respectively.
|
|||
|
|
- `:root` CSS variable declarations are kept as-is since landing variables use different names (`--primary`, `--bg`, etc.) from existing workspace variables (`--color-primary`, `--color-bg`). No conflict — they coexist.
|
|||
|
|
- **Do NOT import in `main.tsx`** — import directly in `MarketingLayout.tsx` via `import '../styles/landing.css'` so it only applies to marketing routes.
|
|||
|
|
- `MarketingLayout.tsx` wrapper div gets `className="marketing-page"`.
|
|||
|
|
- `index.css` Tailwind layer remains untouched.
|
|||
|
|
- Add Google Fonts to `index.html` `<head>`: Lora (serif, weights 400/600/700) and JetBrains Mono (monospace, weights 400/500). DM Sans already present.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Component Mapping
|
|||
|
|
|
|||
|
|
| Reference section | Target file | Action |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `<nav>` | `src/components/layout/MarketingNavbar.tsx` | Replace |
|
|||
|
|
| `.hero` | `src/components/home/HeroSection.tsx` | Replace |
|
|||
|
|
| `.product-suite` | `src/components/home/ProductSuiteSection.tsx` | New |
|
|||
|
|
| `.core-features` | `src/components/home/FeaturesSection.tsx` | Replace |
|
|||
|
|
| `.showcase` | `src/components/home/ShowcaseSection.tsx` | New |
|
|||
|
|
| `.user-love` | `src/components/home/TestimonialsSection.tsx` | New |
|
|||
|
|
| `.pricing` | `src/components/home/PricingSection.tsx` | Replace |
|
|||
|
|
| `.docs-seo` | `src/components/home/DocsSeoSection.tsx` | New |
|
|||
|
|
| `<footer>` | `src/components/layout/Footer.tsx` | Replace |
|
|||
|
|
|
|||
|
|
### Delete (no reference equivalent)
|
|||
|
|
- `src/components/home/HowItWorksSection.tsx`
|
|||
|
|
- `src/components/home/ContactSection.tsx`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## MarketingLayout.tsx
|
|||
|
|
- Wrap outlet in `<div className="marketing-page">` — applies scoped landing CSS
|
|||
|
|
- Render three `.glow-blob` divs (`.glow-blob-1`, `.glow-blob-2`, `.glow-blob-3`) as direct children of the `.marketing-page` wrapper — these are `position: fixed` ambient background elements visible across all marketing pages.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## HomePage.tsx
|
|||
|
|
Update to render sections in order:
|
|||
|
|
```
|
|||
|
|
HeroSection
|
|||
|
|
<div className="section-divider" />
|
|||
|
|
ProductSuiteSection
|
|||
|
|
FeaturesSection
|
|||
|
|
ShowcaseSection
|
|||
|
|
<div className="section-divider" />
|
|||
|
|
TestimonialsSection
|
|||
|
|
<div className="section-divider" />
|
|||
|
|
PricingSection
|
|||
|
|
<div className="section-divider" />
|
|||
|
|
DocsSeoSection
|
|||
|
|
```
|
|||
|
|
Section dividers are plain `<div className="section-divider" />` JSX inlined in `HomePage.tsx` — no abstraction needed.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Navbar (MarketingNavbar.tsx)
|
|||
|
|
- Sticky, height 72px, backdrop blur on scroll (existing scroll state logic kept)
|
|||
|
|
- Logo: SVG icon (lines symbol) + "TexPixel" text — **remove `font-display` Tailwind class** from logo `<span>`, replace with `style={{ fontFamily: "'Lora', serif" }}` to avoid Plus Jakarta Sans conflict
|
|||
|
|
- Nav links: Home `/`, Docs `/docs`, Blog `/blog`, Pricing `#pricing` (anchor on home only), **no Contact link** — also remove the existing `anchorLinks` `#contact` entry
|
|||
|
|
- Right side:
|
|||
|
|
- Lang switch button (existing `useLanguage` toggle)
|
|||
|
|
- User avatar/menu using `const { user, signOut } = useAuth()` — show avatar dropdown when `user !== null`; show "登录/Login" CTA button when `user === null`
|
|||
|
|
- Avatar dropdown items: "启动应用" → `/app`, then logout (calls `signOut()`). **No profile settings link** (route does not exist — omitted)
|
|||
|
|
- "Try Free" CTA button → `/app`
|
|||
|
|
- i18n: `useLanguage` for all labels
|
|||
|
|
- Remove unused `t.marketing.nav.contact` references from this component
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## JS Behaviors → React hooks/useEffect
|
|||
|
|
|
|||
|
|
### Scroll Reveal Hook
|
|||
|
|
- **Create `src/hooks/` directory** (does not exist yet)
|
|||
|
|
- Create `src/hooks/useScrollReveal.ts` — sets up a single `IntersectionObserver` targeting all `.reveal` elements, adds `.visible` class on intersection
|
|||
|
|
- Called once in `HomePage.tsx` via `useScrollReveal()`
|
|||
|
|
|
|||
|
|
### Nav Active on Scroll
|
|||
|
|
- `useEffect` in `MarketingNavbar.tsx` — watches `window.scroll`, adds `.active` class to nav link matching current section `id`
|
|||
|
|
|
|||
|
|
### Testimonial Carousel (TestimonialsSection.tsx)
|
|||
|
|
- React state: `currentPage` (0-indexed), 6 cards, 3 visible, 4 pages
|
|||
|
|
- `useEffect` auto-advances every 5s, resets on manual navigation
|
|||
|
|
- Prev/Next buttons + rendered dots
|
|||
|
|
- Window resize recalcs slide offset
|
|||
|
|
|
|||
|
|
### Typing Effect (HeroSection.tsx)
|
|||
|
|
- `useRef` on `.output-code` element
|
|||
|
|
- `useEffect` cycles through 3 LaTeX strings every 3500ms via `innerHTML` + cursor span
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## CTA Links
|
|||
|
|
- "Try TexPixel", "Try Free", "Get Started" (Free/Monthly/Quarterly plans) → `<Link to="/app">`
|
|||
|
|
- "Buy Desktop" → `<Link to="/app">` (placeholder, no separate purchase flow)
|
|||
|
|
- Doc card links → `/docs`
|
|||
|
|
- Footer blog link → `/blog`
|
|||
|
|
- Pricing anchor `#pricing` → `<a href="#pricing">`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Content / i18n
|
|||
|
|
- All text from the reference HTML is hardcoded in components (Chinese/English bilingual where reference already has it)
|
|||
|
|
- Existing `useLanguage` / `t` translations are used where keys already exist
|
|||
|
|
- New section text is **hardcoded** (not added to `translations.ts`) — the reference HTML content is the source of truth; full i18n for new sections is out of scope for this refactor
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Cleanup
|
|||
|
|
- Remove `contact` key from `marketing.nav` in `src/lib/translations.ts` (both `en` and `zh` blocks) — becomes dead code after ContactSection and `#contact` link are removed.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## What Does NOT Change
|
|||
|
|
- `src/App.tsx`, routing (`AppRouter.tsx`), auth system, workspace (`WorkspacePage`)
|
|||
|
|
- `index.css` Tailwind layer
|
|||
|
|
- Docs/Blog pages
|
|||
|
|
- `SEOHead` component usage in `HomePage.tsx`
|
|||
|
|
- `tailwind.config.js`
|