From 3a630feb29a34f1bba6834ba1969e741d9a641e8 Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 21:42:24 +0900 Subject: [PATCH 01/12] docs: map existing codebase --- .planning/codebase/ARCHITECTURE.md | 185 ++++++++++++++++++++++ .planning/codebase/CONVENTIONS.md | 233 +++++++++++++++++++++++++++ .planning/codebase/INTEGRATIONS.md | 132 ++++++++++++++++ .planning/codebase/STACK.md | 107 +++++++++++++ .planning/codebase/STRUCTURE.md | 245 +++++++++++++++++++++++++++++ .planning/codebase/TESTING.md | 219 ++++++++++++++++++++++++++ 6 files changed, 1121 insertions(+) create mode 100644 .planning/codebase/ARCHITECTURE.md create mode 100644 .planning/codebase/CONVENTIONS.md create mode 100644 .planning/codebase/INTEGRATIONS.md create mode 100644 .planning/codebase/STACK.md create mode 100644 .planning/codebase/STRUCTURE.md create mode 100644 .planning/codebase/TESTING.md diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..76caa4b --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,185 @@ +# Architecture + +**Analysis Date:** 2026-03-22 + +## Pattern Overview + +**Overall:** Static Site Generation (SSG) with Multi-Language Support + +**Key Characteristics:** +- Next.js 15 with `output: export` configuration for static HTML generation +- App Router with dynamic parameters disabled (`dynamicParams = false`) for type-safe static routes +- Internationalization (i18n) as a core architectural concern handled via URL segments +- Component-driven UI with Tailwind CSS styling +- Server-side metadata generation for SEO with JSON-LD structured data + +## Layers + +**Routing Layer:** +- Purpose: Handle URL structure and page generation for multi-locale support +- Location: `src/app/` (Next.js App Router structure) +- Contains: Page components, layout components, API routes, metadata generation +- Depends on: i18n locales, domain models, component library +- Used by: Browser client, search engines + +**Domain/Business Logic Layer:** +- Purpose: Encapsulate product-specific data and rules (downloaders, landing pages) +- Location: `src/domain/` +- Contains: `downloaders.ts` (Downloader enum, supported clients), `downloader-landings.ts` (content dictionaries, slug mapping) +- Depends on: i18n locales for content templates +- Used by: Page components, SEO/metadata generation + +**Internationalization (i18n) Layer:** +- Purpose: Centralize locale configuration, URL generation, and message management +- Location: `src/i18n/` +- Contains: + - `locales.ts` - locale constants, validation, BCP 47 language tags + - `messages.ts` - message loading (imports JSON files from `src/messages/`) + - `urls.ts` - locale-aware URL generation functions + - `links.ts` - external links configuration +- Depends on: Message JSON files +- Used by: All page components, layouts, metadata builders + +**SEO & Metadata Layer:** +- Purpose: Generate Open Graph, Twitter Card, JSON-LD structured data, and canonical links +- Location: `src/seo/` +- Contains: + - `metadata.ts` - builds metadata for localized pages + - `schema.ts` - JSON-LD builders (SoftwareApplication, FAQPage, BreadcrumbList) + - `routes.ts` - localized route entries for sitemap +- Depends on: i18n messages, domain models +- Used by: Page components for metadata generation + +**Presentation Layer:** +- Purpose: Reusable UI components using Tailwind CSS +- Location: `src/components/` +- Contains: Buttons, frames, text components, separators, accordions, word mark +- Depends on: Tailwind configuration, CSS variables +- Used by: Page components, ASCII panel, layouts + +**Interactive Component Layer:** +- Purpose: Client-side interactive UI (uses `'use client'`) +- Location: `src/ascii-panel/` +- Contains: Frame-based terminal-style UI with state management + - `index.tsx` - main component with local state (page navigation, modal sheets) + - `components/` - AsciiPanelFrame, AsciiPanelSheet + - `pages/` - HomePage, SettingsPage, NewClientPage +- Depends on: React hooks for state/effects +- Used by: Home page layout + +## Data Flow + +**Static Page Generation:** + +1. Build time: Next.js processes all `[locale]` dynamic routes +2. `generateStaticParams()` in layout defines all locale variants (en, ja, zh-hans, zh-hant) +3. For each locale route, page component: + - Validates locale using `isLocale()` from `src/i18n/locales.ts` + - Fetches messages via `getMessages(locale)` from `src/i18n/messages.ts` + - Generates metadata using `buildMetadataForCurrentLocalePage()` from `src/seo/metadata.ts` + - Renders with locale-specific content and links + +**Downloader Landing Pages:** + +1. Routes: `src/app/[locale]/downloaders/[slug]/page.tsx` +2. Slugs are statically enumerated from `downloaderLandingSlugs` in `src/domain/downloader-landings.ts` +3. For each slug, retrieve content via `getDownloaderLandingContent(locale, slug)` +4. Content factory pattern: locale + downloader enum → localized content +5. Metadata built with downloader-specific SEO data + +**Sitemap Generation:** + +1. Build time: `src/app/sitemap.ts` creates unified sitemap with language alternates +2. Iterates through localized routes and downloader landing entries +3. Each entry includes hreflang links for all available locales + +**State Management (ASCII Panel):** + +- Local component state via `useState` for: + - `activePage`: which panel view is shown (home, settings, new-client) + - `sheet`: modal state including page, presentation mode, phase (open/closing) +- Animation triggered by `useEffect` on sheet phase change +- Respects `prefers-reduced-motion` media query + +## Key Abstractions + +**Locale Type System:** +- Purpose: Type-safe locale handling throughout the codebase +- Examples: `src/i18n/locales.ts` (Locale union type, isLocale guard) +- Pattern: Branded type with validation function to prevent string locale bugs + +**Message Dictionary:** +- Purpose: Centralized, type-safe multilingual content +- Examples: `src/messages/en.json`, `src/messages/ja.json`, etc. +- Pattern: JSON files imported as typed objects; `getMessages(locale)` returns fully typed Messages object + +**Downloader Landing Factory:** +- Purpose: Generate localized content for product landing pages with locale-specific templates +- Examples: `downloaderLandingDictionary` in `src/domain/downloader-landings.ts` +- Pattern: Factory functions per downloader; templates per locale; `buildContent()` combines both to generate final page data + +**URL Builder Functions:** +- Purpose: Encapsulate locale-aware URL generation with consistent formatting +- Examples: `localePath()`, `localeRoot()`, `absoluteUrl()` in `src/i18n/urls.ts` +- Pattern: Single responsibility functions that handle trailing slashes and locale prefixes + +**SEO Schema Builders:** +- Purpose: Generate structured data (JSON-LD) for rich snippets +- Examples: `buildSoftwareApplicationSchema()`, `buildFaqPageSchema()` in `src/seo/schema.ts` +- Pattern: Pure functions that combine messages + locale + links into schema objects + +## Entry Points + +**Root Layout:** +- Location: `src/app/layout.tsx` +- Triggers: Every page request +- Responsibilities: Global metadata, favicon/manifest, HTML structure, global styles import + +**Locale Layout:** +- Location: `src/app/[locale]/layout.tsx` +- Triggers: Every locale-specific route request +- Responsibilities: Static param generation, message loading, locale validation, navigation UI (TextTabsNav), footer layout + +**Home Page:** +- Location: `src/app/[locale]/page.tsx` +- Triggers: `/{locale}/` requests +- Responsibilities: Hero section rendering, features grid, FAQ, schema.org markup, downloader links with landing pages + +**Downloader Landing:** +- Location: `src/app/[locale]/downloaders/[slug]/page.tsx` +- Triggers: `/{locale}/downloaders/{slug}/` requests +- Responsibilities: Render downloader-specific content, build breadcrumb schema, generate downloader metadata + +**Root Index:** +- Location: `src/app/page.tsx` +- Triggers: `/` root request +- Responsibilities: Language selector, link to App Store + +**Sitemap:** +- Location: `src/app/sitemap.ts` +- Triggers: Build time (static generation) +- Responsibilities: List all localized routes and downloader pages with language alternates + +## Error Handling + +**Strategy:** Type-safety-first approach to prevent errors at runtime + +**Patterns:** +- Locale validation via `isLocale()` guard; falls back to `defaultLocale` if invalid +- Message loading fails hard (import-time, caught during build) rather than runtime +- Factory pattern for downloader content with `undefined` check for missing content +- `dynamicParams = false` in layout prevents unexpected 404s for ungenerated locales + +## Cross-Cutting Concerns + +**Logging:** Not implemented - static site with no server-side logging. Client-side errors are not tracked. + +**Validation:** Strict TypeScript with `noEmit: true`. Locale validation via `isLocale()` guard function. No runtime schema validation. + +**Authentication:** Not applicable - public marketing website, no authentication. + +**Internationalization:** URL-based (locale in first segment). Content loaded at render time via `getMessages()`. Link generation via `localePath()` ensures consistent locale in URLs. + +--- + +*Architecture analysis: 2026-03-22* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..2d0ff36 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,233 @@ +# Coding Conventions + +**Analysis Date:** 2026-03-22 + +## Naming Patterns + +**Files:** +- Components: PascalCase (e.g., `BitRemoteWordmark.tsx`, `TextButton.tsx`) +- Utilities/Helpers: camelCase (e.g., `messages.ts`, `locales.ts`, `urls.ts`) +- Pages: Match Next.js conventions with brackets for dynamic routes (e.g., `[locale]`, `page.tsx`) +- Features organized in directories: lowercase with hyphens for multi-word (e.g., `ascii-panel`, `downloader-landings`) + +**Functions:** +- React components: PascalCase (e.g., `TextFrame`, `AsciiPanel`, `FaqAccordion`) +- Utility functions: camelCase (e.g., `parseSpeed`, `buildMetadataForCurrentLocalePage`, `localeRoot`) +- Internal helper functions: camelCase (e.g., `clientSkeletonName`, `digitText`, `numericDirection`) +- Boolean/type guard functions use `is` prefix (e.g., `isLocale`) + +**Variables:** +- Regular variables: camelCase (e.g., `activePage`, `isLocaleMenuOpen`, `localePrefix`) +- Constants: UPPER_SNAKE_CASE (e.g., `FRAME_INTERVAL_MS`, `LINKS`) +- Type/interface instances: PascalCase when they represent a type definition, camelCase when assigned +- Enums: PascalCase members (e.g., `Downloader.aria2`, `Downloader.qBittorrent`) + +**Types:** +- Interface/Type definitions: PascalCase (e.g., `Props`, `Messages`, `FrameSheetState`, `ClientSpeed`) +- Generic type parameters: Single uppercase letter or descriptive PascalCase (e.g., `T`, `Locale`) +- Type aliases: PascalCase (e.g., `FramePageId`, `FramePresentation`) + +## Code Style + +**Formatting:** +- No explicit prettier config in project — uses ESLint's defaults +- Line length: No strict limit observed but code typically under 120 chars per line +- Indentation: 2 spaces (standard Next.js) +- Trailing commas: Used in objects and arrays + +**Linting:** +- ESLint configuration: `extends: ["next/core-web-vitals"]` +- Config file: `.eslintrc.json` +- Lint command: `npm run lint` runs ESLint with `--max-warnings 0` (zero warnings allowed) +- Enforced rules include Next.js best practices and core web vitals compliance + +**Import statements:** +- All imports sorted: React types first, then third-party, then local imports +- Type imports use explicit `type` keyword (e.g., `import type { Metadata } from 'next'`) +- Multi-line imports: Values before types when from same module + +Example pattern from `src/app/[locale]/page.tsx`: +```typescript +import type { Metadata } from 'next'; + +import { AsciiPanel } from '@/ascii-panel'; +import { BitRemoteWordmark } from '@/components/BitRemoteWordmark'; +import { FaqAccordion } from '@/components/FaqAccordion'; +import { supportedDownloaders } from '@/domain/downloaders'; +import { LINKS } from '@/i18n/links'; +import { defaultLocale, isLocale, type Locale } from '@/i18n/locales'; +import { getMessages } from '@/i18n/messages'; +``` + +## Import Organization + +**Order:** +1. Built-in Node/Next.js imports with `type` keyword (e.g., `import type { Metadata }`) +2. React and React-related imports +3. Third-party library imports +4. Local imports using `@/` path alias +5. Local type imports at the end of local imports section + +**Path Aliases:** +- `@/*` maps to `src/*` (defined in `tsconfig.json`) +- Always use `@/` for imports from `src/` directory +- Examples: `@/components`, `@/i18n`, `@/domain`, `@/seo`, `@/ascii-panel` + +**Barrel exports:** +- `src/ascii-panel/index.tsx` exports `AsciiPanel` component as barrel export +- Most other modules export directly without barrel files + +## Error Handling + +**Patterns:** +- Explicit type guards for validation (e.g., `isLocale()` function in `src/i18n/locales.ts`) +- Fallback to defaults on invalid input (e.g., if locale is invalid, use `defaultLocale`) +- No try-catch blocks observed — static/compile-time approach preferred +- Optional chaining used extensively (e.g., `prevFrame[index]?.client`) +- Null coalescing with `??` operator (e.g., `clientsDataset[state.index] ?? clientsDataset[0]`) + +Example from `src/i18n/locales.ts`: +```typescript +export function isLocale(value: string): value is Locale { + return (locales as readonly string[]).includes(value); +} +``` + +Usage pattern in `src/app/[locale]/page.tsx`: +```typescript +const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale; +``` + +## Logging + +**Framework:** `console` only — no logging library in dependencies + +**Patterns:** +- No logging observed in production code +- No debug output in normal operation +- SVG filters and animations used for visual feedback instead + +## Comments + +**When to Comment:** +- Minimal commenting — code is self-explanatory through naming +- Section dividers used in data-heavy files (e.g., `/* ------------------------------------------------------------------ */`) +- JSDoc-style comments on complex functions + +**JSDoc/TSDoc:** +- Not consistently used across the codebase +- Example from `src/i18n/locales.ts`: +```typescript +/** BCP 47 language tags for use in HTML `lang` attributes. */ +export const localeLang: Record = { + en: 'en', + ja: 'ja', + 'zh-hans': 'zh-Hans', + 'zh-hant': 'zh-Hant', +}; +``` + +- Rare inline comments explaining design decisions (e.g., `src/components/TextTabsNav.tsx`: +```typescript +// data-[active=true]:* classes are intentionally never applied. +// Tab items respond to hover only — there is no persistent "current page" highlight by design. +``` + +## Function Design + +**Size:** Functions are typically 10–30 lines, with some reaching 70+ lines for complex components +- Small utility functions: 5–10 lines +- React components: 20–50 lines +- Complex interactive components: 50–135+ lines + +**Parameters:** +- Destructured from `Props` type or inline destructuring +- Always use explicit type annotations +- Example from `src/components/TextButton.tsx`: +```typescript +export function TextButton({ + href, + children, + variant = 'primary', + target, + rel, +}: Props) { +``` + +**Return Values:** +- React components return JSX directly +- Utility functions return typed values +- Optional returns use `null` or `undefined` explicitly +- No implicit `undefined` returns + +Example from `src/seo/schema.ts`: +```typescript +export function serializeJsonLd(schema: object): { __html: string } { + return { + __html: JSON.stringify(schema).replace(/; +``` + +**Const assertions:** +- `as const` used on objects/arrays that should be treated as literal types +- Enables type safety for mapped/readonly data + +**Next.js Server/Client boundaries:** +- `'use client'` directive used in interactive components (`src/ascii-panel/index.tsx`, `src/components/TextTabsNav.tsx`) +- Server components by default (no directive needed) +- Server functions with `async` keywords used in page components + +**Readonly types:** +- Used for immutable data structures (e.g., `readonly ClientSpeedFrame[]`) +- Combined with `const` for extra type safety + +--- + +*Convention analysis: 2026-03-22* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..312aa34 --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,132 @@ +# External Integrations + +**Analysis Date:** 2026-03-22 + +## APIs & External Services + +**Third-Party Links/References:** +- Apple App Store - Direct link to BitRemote app + - URL: `https://apps.apple.com/app/id6477765303` + - Used in: `src/i18n/links.ts` + +- GitHub Repository - BitRemote source code + - URL: `https://github.com/BitRemoteApp/BitRemote` + - Used in: `src/i18n/links.ts`, displayed in support pages + +- Twitter/X - Social channel + - URL: `https://twitter.com/bitremote` + - Used in: `src/i18n/links.ts` + +- Discord - Community server + - URL: `https://discord.gg/x5TP2z6cFj` + - Used in: `src/i18n/links.ts` + +- Telegram - Chat channel + - URL: `https://t.me/bitremote` + - Used in: `src/i18n/links.ts` + +## Data Storage + +**Databases:** +- Not used - Static website with no backend database + +**File Storage:** +- Local filesystem only + - Static assets: `public/` + - Messages/i18n: `src/messages/` + - No external object storage + +**Caching:** +- Build-time caching only + - GitHub Actions caches Next.js build output (`.next/cache`) + - No runtime caching service + +## Authentication & Identity + +**Auth Provider:** +- None - Public website with no user authentication +- No login/signup functionality +- No session management required + +## Content Management + +**Content Storage:** +- JSON files for internationalization: `src/messages/*.json` + - `en.json` - English content + - `ja.json` - Japanese content + - `zh-hans.json` - Simplified Chinese content + - `zh-hant.json` - Traditional Chinese content +- Hardcoded data in TypeScript: + - Downloader information: `src/domain/downloaders.ts` + - Landing page content: `src/domain/downloader-landings.ts` + +## Monitoring & Observability + +**Error Tracking:** +- Not detected - No error tracking service configured + +**Logs:** +- Default approach - Build and deployment logs from GitHub Actions +- CLI output during `npm run build` and `npm run lint` + +## CI/CD & Deployment + +**Hosting:** +- Cloudflare Pages (primary deployment) +- GitHub Pages (fallback, see `public/CNAME` for custom domain) + +**CI Pipeline:** +- GitHub Actions (`.github/workflows/ci.yml`) + - Trigger: Pull requests to `main` branch + - Node.js 25 setup + - npm cache restoration + - Next.js cache restoration (`.next/cache`) + - Steps: + 1. Checkout code + 2. Install dependencies via `npm ci` + 3. Lint with ESLint: `npx eslint src/` + 4. Build with Next.js: `npx next build` + - Static output to `out/` directory + - No automatic deployment (deployment handled separately) + +## Environment Configuration + +**Required env vars:** +- `NEXT_ALLOWED_DEV_ORIGINS` (optional) - Comma-separated list of allowed origins + - Only used in development + - Parsed in `next.config.ts` line 3-5 + +**Secrets location:** +- No secrets in codebase +- No `.env` file checked in +- Environment variables minimal (none required for production) + +## Webhooks & Callbacks + +**Incoming:** +- None - No API endpoints accepting webhooks + +**Outgoing:** +- None - No external callback invocations + +## External Content + +**Linked Services:** +- arkstudios.co.jp - Privacy policy and EULA hosting + - Privacy: `https://arkstudios.co.jp/en/privacy/bitremote` + - EULA: `https://arkstudios.co.jp/en/eula/bitremote` + - Referenced in: `src/i18n/links.ts` + +**Search Engine Integration:** +- Sitemap: `/sitemap.xml` (generated dynamically at build time) + - Generated by: `src/app/sitemap.ts` +- Robots.txt: `/robots.txt` (via `src/app/robots.ts`) + +**SEO/Structured Data:** +- Schema.org JSON-LD + - Location: `src/seo/schema.ts` + - Types: Organization, WebSite, BreadcrumbList + +--- + +*Integration audit: 2026-03-22* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..f84ab36 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,107 @@ +# Technology Stack + +**Analysis Date:** 2026-03-22 + +## Languages + +**Primary:** +- TypeScript 5.7.3 - All source code in `src/` +- JavaScript - Build configuration and scripts + +**Secondary:** +- JSON - Internationalization messages and configuration +- CSS - Styling via Tailwind (processed through PostCSS) + +## Runtime + +**Environment:** +- Node.js 25 (required for CI, see `.github/workflows/ci.yml`) + +**Package Manager:** +- npm (with lockfile `package-lock.json`) +- Lockfile: Present + +## Frameworks + +**Core:** +- Next.js 15.1.0 - Full-stack React framework for SSR/static generation +- React 19.0.0 - UI component library +- React DOM 19.0.0 - DOM rendering + +**Styling:** +- Tailwind CSS 3.4.0 - Utility-first CSS framework +- PostCSS 8.4.0 - CSS transformations +- Autoprefixer 10.4.0 - Vendor prefix automation + +**Build/Dev:** +- TypeScript 5.7.3 - Language compilation +- ESLint 8.57.0 with next/core-web-vitals - Code linting +- ESLint Config Next 15.1.0 - Next.js specific linting rules + +## Key Dependencies + +**Critical:** +- next (15.1.0) - Framework providing server rendering, static generation, and API routes +- react (19.0.0) - Component library and state management +- react-dom (19.0.0) - Renders React components to DOM + +**Infrastructure:** +- autoprefixer (10.4.0) - Auto-adds vendor prefixes to CSS +- postcss (8.4.0) - CSS processing pipeline +- tailwindcss (3.4.0) - CSS utility generation from config + +**Development:** +- @types/node (22.10.0) - Node.js type definitions +- @types/react (19.0.0) - React component type definitions +- @types/react-dom (19.0.0) - React DOM type definitions +- typescript (5.7.3) - TypeScript compiler + +## Configuration + +**Environment:** +- `NEXT_ALLOWED_DEV_ORIGINS` (optional) - Comma-separated list of allowed origins for development + - Read in `next.config.ts` line 3-5 + - Used to configure dev server CORS if needed + +**Build:** +- `next.config.ts` - Next.js configuration + - Static export mode: `output: 'export'` (line 8) + - Image optimization disabled: `unoptimized: true` (line 12) + - Trailing slashes enabled: `trailingSlash: true` (line 9) + - Removes powered-by header: `poweredByHeader: false` (line 14) +- `tsconfig.json` - TypeScript compiler settings + - Target: ES2022 + - Path aliases: `@/*` maps to `src/*` + - Strict mode enabled + - Module: ESNext with bundler resolution +- `tailwind.config.ts` - Tailwind CSS configuration + - Content scan: `src/**/*.{js,ts,jsx,tsx,mdx}` + - Dark mode: media preference based + - Custom theme colors using CSS variables: `--bg`, `--fg`, `--blue`, etc. + - Custom font families: `--font-body` (sans), `--font-ui` (mono) +- `.eslintrc.json` - ESLint configuration + - Extends: `next/core-web-vitals` +- `postcss.config.js` - PostCSS pipeline + - Plugins: tailwindcss, autoprefixer + +## Platform Requirements + +**Development:** +- Node.js 25 (see `.github/workflows/ci.yml`) +- npm with lockfile caching support +- TypeScript compiler + +**Production:** +- Cloudflare Pages (deployment target based on recent commit history) +- Static file hosting (output is `out/` directory) +- No server-side runtime required (static export) + +## Build Output + +- Static export to `out/` directory +- No API runtime required +- Suitable for static hosting (Cloudflare Pages, GitHub Pages) + +--- + +*Stack analysis: 2026-03-22* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..8c7a956 --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,245 @@ +# Codebase Structure + +**Analysis Date:** 2026-03-22 + +## Directory Layout + +``` +bitremote-website/ +├── src/ # Source code +│ ├── app/ # Next.js App Router routes +│ │ ├── [locale]/ # Locale dynamic segment +│ │ │ ├── downloaders/ # Downloader landing pages +│ │ │ │ └── [slug]/ # Individual downloader slugs +│ │ │ ├── privacy/ # Privacy policy page +│ │ │ ├── support/ # Support page +│ │ │ ├── terms/ # Terms of service page +│ │ │ ├── layout.tsx # Locale-aware wrapper layout +│ │ │ └── page.tsx # Home page (/) +│ │ ├── llms-full.txt/ # API route for LLM context +│ │ ├── layout.tsx # Root layout (globals.css) +│ │ ├── page.tsx # Language selector page +│ │ ├── robots.ts # robots.txt generation +│ │ ├── sitemap.ts # Sitemap generation +│ │ └── not-found.tsx # 404 fallback +│ ├── ascii-panel/ # Interactive terminal-style component +│ │ ├── components/ # Frame/sheet sub-components +│ │ ├── pages/ # Panel pages (Home, Settings, NewClient) +│ │ └── index.tsx # Main ASCII panel export +│ ├── components/ # Reusable presentation components +│ │ ├── BitRemoteWordmark.tsx +│ │ ├── TextButton.tsx +│ │ ├── TextFrame.tsx +│ │ ├── TextSeparator.tsx +│ │ ├── TextTabsNav.tsx +│ │ ├── FaqAccordion.tsx +│ │ └── DownloaderLandingPage.tsx +│ ├── domain/ # Domain models and business logic +│ │ ├── downloaders.ts # Downloader enum, supported list +│ │ └── downloader-landings.ts # Landing page content dictionary +│ ├── i18n/ # Internationalization +│ │ ├── locales.ts # Locale constants, validation +│ │ ├── messages.ts # Message loader function +│ │ ├── urls.ts # Locale-aware URL builders +│ │ └── links.ts # External links constants +│ ├── messages/ # Message JSON files (imported by i18n/messages.ts) +│ │ ├── en.json +│ │ ├── ja.json +│ │ ├── zh-hans.json +│ │ └── zh-hant.json +│ ├── seo/ # SEO and metadata generation +│ │ ├── metadata.ts # buildMetadataForCurrentLocalePage() +│ │ ├── schema.ts # JSON-LD builders +│ │ └── routes.ts # Localized route entries +│ └── globals.css # Tailwind + CSS variables +├── public/ # Static assets +│ ├── favicon.svg, favicon-16x16.png, favicon-32x32.png +│ ├── apple-touch-icon.png +│ ├── site.webmanifest +│ ├── opengraph.jpg # Social media preview +│ └── [other assets] +├── .vscode/ # IDE settings +├── .github/ # GitHub workflows and config +├── next.config.ts # Next.js configuration +├── tsconfig.json # TypeScript configuration +├── tailwind.config.ts # Tailwind CSS configuration +├── postcss.config.js # PostCSS configuration +├── .eslintrc.json # ESLint configuration +├── package.json # Dependencies and scripts +└── next-env.d.ts # Next.js type definitions +``` + +## Directory Purposes + +**`src/app/`:** +- Purpose: Next.js App Router structure with page components +- Contains: Route handlers, page components, layouts, metadata generation +- Key files: `[locale]/layout.tsx` (static param generation), `[locale]/page.tsx` (home page), `sitemap.ts`, `robots.ts` + +**`src/app/[locale]/`:** +- Purpose: Locale-aware route segment; all content lives under this segment +- Contains: Layout wrapper, home page, child routes (downloaders, privacy, support, terms) +- Pattern: Dynamic segment with `dynamicParams = false` ensures all locales are pre-generated at build time + +**`src/app/[locale]/downloaders/[slug]/`:** +- Purpose: Downloader-specific landing pages +- Contains: Page component that loads content from domain layer +- Pattern: Slug routes enumerated from `downloaderLandingSlugs` constant; one route per downloader + +**`src/ascii-panel/`:** +- Purpose: Self-contained interactive component with internal state management +- Contains: Main component, sub-components (Frame, Sheet), page variants (Home, Settings, NewClient) +- Pattern: Marked with `'use client'`; encapsulates React hooks and event handlers +- Used by: `src/app/[locale]/page.tsx` as visual component + +**`src/components/`:** +- Purpose: Reusable, presentational UI building blocks +- Contains: Buttons, frames, separators, navigation, accordion, wordmark +- Pattern: Stateless functional components; accept props for styling/content; no `'use client'` required (but can be used in client components) + +**`src/domain/`:** +- Purpose: Product-specific data models and business logic +- Contains: + - `downloaders.ts`: Enum of supported downloaders + readonly list + - `downloader-landings.ts`: Content factory pattern with locale-specific templates +- Pattern: Pure data structures and factory functions; imports from i18n/locales for type safety + +**`src/i18n/`:** +- Purpose: Centralize all internationalization concerns +- Contains: + - `locales.ts`: Locale constants, type guard, BCP 47 language tags + - `messages.ts`: Imports JSON files and exports typed getMessages() function + - `urls.ts`: Locale-aware URL builders (localePath, localeRoot, absoluteUrl) + - `links.ts`: External links (App Store, GitHub, etc.) +- Pattern: All locale handling flows through these modules; type-safe via branded types and guards + +**`src/messages/`:** +- Purpose: Store translated content as importable JSON files +- Contains: One JSON file per language (en.json, ja.json, zh-hans.json, zh-hant.json) +- Pattern: Files are imported in `src/i18n/messages.ts`; never referenced directly elsewhere + +**`src/seo/`:** +- Purpose: SEO, metadata, and structured data generation +- Contains: + - `metadata.ts`: Builds Next.js Metadata objects with Open Graph, Twitter, canonical links, hreflang + - `schema.ts`: JSON-LD builder functions (SoftwareApplication, FAQPage, BreadcrumbList) + - `routes.ts`: Exports localized route entries for use in sitemap +- Pattern: Pure functions that combine locale + messages + domain data into metadata/schema objects + +**`public/`:** +- Purpose: Static assets served directly +- Contains: Favicon variants, webmanifest, social media preview image +- Pattern: Files are referenced by path in layout/metadata (e.g., `/favicon.svg`) + +## Key File Locations + +**Entry Points:** +- `src/app/layout.tsx`: Global layout (metadata, globals.css, favicon config) +- `src/app/page.tsx`: Root language selector (`/`) +- `src/app/[locale]/layout.tsx`: Locale layout (navigation, footer, static param generation) +- `src/app/[locale]/page.tsx`: Locale home page (`/{locale}/`) +- `src/app/[locale]/downloaders/[slug]/page.tsx`: Downloader landing pages + +**Configuration:** +- `next.config.ts`: Enables `output: export` (SSG), `trailingSlash: true`, disables image optimization +- `tsconfig.json`: Target ES2022, path alias `@/*` → `src/*` +- `tailwind.config.ts`: Tailwind configuration (theme colors, fonts) +- `.eslintrc.json`: ESLint with Next.js config + +**Core Logic:** +- `src/domain/downloaders.ts`: Downloader enum and supported list +- `src/domain/downloader-landings.ts`: Landing page content dictionaries (huge file, 550+ lines) +- `src/i18n/locales.ts`: Locale type definitions and validation +- `src/i18n/messages.ts`: Message loading function +- `src/seo/metadata.ts`: Metadata builder for current page locale +- `src/seo/schema.ts`: JSON-LD schema builders + +**Styling:** +- `src/globals.css`: Global Tailwind directives, CSS variables (--blue, --bg, --ink-soft, etc.) +- `tailwind.config.ts`: Theme configuration, font imports + +**Testing:** +- Not present. No test files exist in the codebase. + +## Naming Conventions + +**Files:** +- Page/component files: PascalCase (e.g., `BitRemoteWordmark.tsx`, `TextButton.tsx`) +- Utility/logic files: camelCase (e.g., `locales.ts`, `urls.ts`, `metadata.ts`) +- Configuration files: lowercase or specific format (e.g., `next.config.ts`, `.eslintrc.json`) + +**Directories:** +- Route segments: kebab-case for multi-word segments (e.g., `[locale]`, `llms-full.txt`, `ascii-panel`) +- Feature directories: kebab-case (e.g., `ascii-panel`, `downloader-landings`) +- Standard Next.js directories: lowercase (e.g., `app`, `public`, `components`, `i18n`, `domain`) + +**TypeScript/Variables:** +- Enums: PascalCase (e.g., `Downloader`) +- Types: PascalCase (e.g., `Locale`, `Messages`, `DownloaderLandingContent`) +- Functions: camelCase (e.g., `getMessages()`, `buildContent()`, `localePath()`) +- Constants: camelCase or UPPER_CASE for config (e.g., `defaultLocale`, `supportedDownloaders`, `LINKS`) + +## Where to Add New Code + +**New Localized Page:** +1. Create directory: `src/app/[locale]/[new-route]/` +2. Add page file: `src/app/[locale]/[new-route]/page.tsx` +3. Import and call `generateStaticParams()` if route has dynamic segments +4. Load messages via `getMessages(locale)` from `src/i18n/messages.ts` +5. Add metadata generation via `buildMetadataForCurrentLocalePage()` from `src/seo/metadata.ts` +6. Add route entry to `src/seo/routes.ts` if needed for sitemap + +**New Component:** +- If reusable across pages: `src/components/[ComponentName].tsx` +- If specific to ASCII panel: `src/ascii-panel/components/[ComponentName].tsx` or `src/ascii-panel/pages/[PageName].tsx` +- Import from `@/components/` using path alias + +**New Domain Model:** +- Add to `src/domain/`: Keep enum/types in separate files (one concern per file) +- Example: `src/domain/new-entity.ts` with enum, types, factory functions +- Export public getters; keep factories internal + +**New i18n Module:** +- Add to `src/i18n/`: Keep each concern separate +- Example: if adding date/number formatting, create `src/i18n/formatters.ts` +- Use existing pattern: accept `locale` parameter, return typed result + +**New Utility Function:** +- Shared across multiple modules: `src/utils/[concern].ts` +- Route-specific: Co-locate in the route directory +- SEO-specific: Add to `src/seo/` + +**Updating Messages:** +- Edit JSON files in `src/messages/` +- Update TypeScript inferred type: `src/i18n/messages.ts` imports and exports `Messages` type +- Type-checking ensures all locales have matching structure + +## Special Directories + +**`src/app/llms-full.txt/`:** +- Purpose: API route that returns full context for LLM systems +- Generated: Yes, at build time +- Committed: Yes (but generated content varies) +- Pattern: Route handler in `route.ts` file + +**`src/messages/`:** +- Purpose: Localization files +- Generated: No, hand-edited +- Committed: Yes, all translations committed +- Pattern: Imported as modules in `src/i18n/messages.ts` + +**`.next/`:** +- Purpose: Build output directory +- Generated: Yes, by `next build` +- Committed: No (in `.gitignore`) +- Pattern: Contains pre-rendered pages, static assets, build artifacts + +**`.planning/codebase/`:** +- Purpose: Documentation generated by GSD codebase mapper +- Generated: Yes, by orchestrator +- Committed: Tracked in git (reference documentation) +- Pattern: Markdown files documenting architecture, structure, conventions, testing patterns, and concerns + +--- + +*Structure analysis: 2026-03-22* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..c96b27f --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,219 @@ +# Testing Patterns + +**Analysis Date:** 2026-03-22 + +## Test Framework + +**Status:** No testing framework configured + +This is a static website with no test dependencies in `package.json`. No Jest, Vitest, Playwright, or other test runners are present. + +**Development Dependencies:** +- No testing libraries (@testing-library/react, jest, vitest, etc.) +- ESLint configured for code quality checks only +- TypeScript for compile-time type safety + +**Run Commands:** +```bash +npm run lint # Run ESLint with --max-warnings 0 (zero warnings allowed) +npm run dev # Start development server +npm run build # Build static export +npm start # Start production server +``` + +## Test Coverage + +**Requirements:** Not enforced + +Coverage is managed through: +1. **TypeScript strict mode** (`strict: true` in `tsconfig.json`) +2. **ESLint with Next.js rules** (via `eslint-config-next`) +3. **Static analysis only** — no runtime tests + +## Code Quality Approach + +Instead of unit/integration tests, quality assurance relies on: + +**Compile-time safety:** +- TypeScript with strict mode enabled +- Exhaustive type checking for all functions and components +- Type guards like `isLocale()` function for runtime validation + +**Static analysis:** +- ESLint configured with `"next/core-web-vitals"` rules +- Zero warnings policy (`--max-warnings 0` in lint script) +- Enforces Next.js best practices and accessibility standards + +**Type-driven development:** +- All functions have explicit return types +- Props interfaces define component contracts +- Type aliases ensure consistency (e.g., `Locale`, `Messages`) + +Example type safety from `src/i18n/messages.ts`: +```typescript +export type Messages = typeof en; + +export function getMessages(locale: Locale): Messages { + return MESSAGES_BY_LOCALE[locale]; +} +``` + +## Test Types Not Implemented + +**Unit Tests:** Not present +- Small utility functions like `parseSpeed()`, `localeRoot()` are tested implicitly through TypeScript +- No .test.ts or .spec.ts files in `src/` + +**Integration Tests:** Not present +- Next.js page routes and metadata generation rely on Next.js build/runtime validation + +**E2E Tests:** Not present +- No Playwright, Cypress, or other E2E framework configured +- Manual testing or CI-based validation only + +**Visual Regression Tests:** Not present +- ASCII panel animations and styling verified through development/preview + +## Static Site Validation + +Since this is a static website generated with `output: 'export'` in `next.config.ts`, testing is minimal: + +**What is validated:** +1. Build succeeds without errors +2. TypeScript compilation passes strict mode +3. ESLint reports zero warnings +4. Metadata generation correct (Next.js validates at build time) +5. Routes and path parameters valid + +**What is NOT tested:** +- Runtime behavior (animations, state management) +- Component rendering output +- User interactions +- Browser compatibility +- Accessibility compliance (beyond ESLint checks) + +## Type Safety as Testing + +The codebase replaces runtime tests with compile-time type checking: + +**Example: Locale validation** + +File: `src/i18n/locales.ts` +```typescript +export const defaultLocale = 'en' as const; +export const locales = ['en', 'ja', 'zh-hans', 'zh-hant'] as const; +export type Locale = (typeof locales)[number]; + +export function isLocale(value: string): value is Locale { + return (locales as readonly string[]).includes(value); +} +``` + +Usage in `src/app/[locale]/page.tsx`: +```typescript +const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale; +``` + +This ensures: +- Only valid locale strings can be used as `Locale` type +- Invalid locales fall back to `defaultLocale` +- TypeScript enforces the type at compile time +- No runtime test needed — type system validates + +**Example: Component Props** + +File: `src/components/TextButton.tsx` +```typescript +type Props = { + href: string; + children: ReactNode; + variant?: 'primary' | 'secondary'; + target?: '_blank' | '_self'; + rel?: string; +}; + +export function TextButton({ + href, + children, + variant = 'primary', + target, + rel, +}: Props) { + // implementation +} +``` + +All callers must provide correct prop types — verified at compile time in every consuming component. + +## Linting and Formatting + +**ESLint Configuration:** +- Config: `.eslintrc.json` +- Base: `extends: ["next/core-web-vitals"]` +- Command: `npm run lint` +- Zero-tolerance policy: `--max-warnings 0` + +**What ESLint checks:** +- Next.js best practices (Image component, Script handling, dynamic imports) +- Core Web Vitals compliance +- React best practices +- No imports in Next.js config files +- Accessibility issues + +**Prettier:** +- Not explicitly configured +- ESLint may format through its rules +- Code follows standard 2-space indentation + +## Build-Time Validation + +The Next.js build process (`npm run build`) with `output: 'export'` validates: + +1. **Route generation**: All `[locale]` parameters resolved +2. **Static generation**: All pages generate without errors +3. **Metadata**: Next.js validates metadata objects at build time +4. **Images**: Unoptimized images allowed (`images: { unoptimized: true }`) +5. **TypeScript**: Full type checking during build + +## Development Practices + +**During development:** +```bash +npm run dev # Start Next.js dev server with HMR +npm run lint # Check code quality +``` + +**No automated test runs** — quality relies on: +- Developer type checking via IDE (TypeScript integration) +- ESLint warnings during development +- Build validation before deployment + +**Testing strategy:** +- Manual testing in browser during development +- Preview deployments for visual/functional verification +- Metadata and SEO validation through build output + +## Future Testing Considerations + +If testing becomes necessary: + +**Unit tests:** +- Could add Jest or Vitest +- Focus on i18n functions (`getMessages()`, locale guards) +- Test URL builders (`localePath()`, `absoluteUrl()`) +- Test data transformations (`parseSpeed()`, schema builders) + +**Component tests:** +- Would test interactive features (locale switcher in `TextTabsNav`) +- Would verify ASCII panel state management +- Would validate animation timing + +**E2E tests:** +- Could use Playwright for production validation +- Verify all locales render correctly +- Test locale switching navigation +- Validate metadata in rendered HTML + +--- + +*Testing analysis: 2026-03-22* From 2d9b1ddc1e346a67e7ea7625613444d865878d70 Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:12:23 +0900 Subject: [PATCH 02/12] chore: add project config --- .planning/config.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .planning/config.json diff --git a/.planning/config.json b/.planning/config.json new file mode 100644 index 0000000..992b667 --- /dev/null +++ b/.planning/config.json @@ -0,0 +1,32 @@ +{ + "model_profile": "balanced", + "commit_docs": true, + "parallelization": true, + "search_gitignored": false, + "brave_search": false, + "firecrawl": false, + "exa_search": false, + "git": { + "branching_strategy": "none", + "phase_branch_template": "gsd/phase-{phase}-{slug}", + "milestone_branch_template": "gsd/{milestone}-{slug}", + "quick_branch_template": null + }, + "workflow": { + "research": true, + "plan_check": true, + "verifier": true, + "nyquist_validation": true, + "auto_advance": false, + "node_repair": true, + "node_repair_budget": 2, + "ui_phase": true, + "ui_safety_gate": true, + "text_mode": false + }, + "hooks": { + "context_warnings": true + }, + "mode": "yolo", + "granularity": "standard" +} \ No newline at end of file From 3d708a24a89d818914db6c01312db95c1b9bc186 Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:42:54 +0900 Subject: [PATCH 03/12] Update AGENTS.md --- AGENTS.md | 298 +++++++++++++++++++++++++++++++++++++++++++++--------- CLAUDE.md | 2 +- 2 files changed, 252 insertions(+), 48 deletions(-) mode change 100644 => 120000 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md index 0290c4b..31057c5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,47 +1,251 @@ -# BitRemote Website (Marketing) — Agent Notes - -## Project Structure - -- `src/app/` — Next.js App Router pages/layouts - - `src/app/layout.tsx` — global layout + site-wide metadata base - - `src/app/page.tsx` — root language chooser (`/`) - - `src/app/not-found.tsx` — default 404 page - - `src/app/[locale]/layout.tsx` — per-locale layout (nav + footer) and `generateStaticParams()` - - `src/app/[locale]/page.tsx` — localized homepage (`/{locale}/`) - - `src/app/[locale]/privacy/page.tsx` — localized privacy route - - `src/app/[locale]/terms/page.tsx` — localized terms route - - `src/app/[locale]/support/page.tsx` — localized support route -- `src/components/` — reusable “Text UI”/ASCII-first components - - `TextFrame`, `TextButton`, `TextTabsNav`, `TextSeparator`, `FaqAccordion` - - `BitRemoteWordmark` — SVG wordmark with subtle pixel/filter animation - - `AsciiSplitPanel` — the hero “byte skeleton” panel -- `src/i18n/` - - `locales.ts` — locale list + type + labels - - `messages.ts` — loads `src/messages/*.json` - - `urls.ts` — `localePath()` helpers and `absoluteUrl()` (used for canonical URLs) -- `src/messages/*.json` — localized copy (EN/JA/ZH-Hans/ZH-Hant); keep keys aligned across locales -- `public/` — static assets and static-site plumbing - - `public/CNAME` — GitHub Pages custom domain (currently `bitremote.app`) - - `public/robots.txt`, `public/sitemap.xml`, `public/.nojekyll` - - `public/textures/*` — “text-like” SVG glyph textures used as subtle backgrounds - - `public/assets/*` — screenshots and other marketing assets - -## Design Concept (ASCII-First “Text UI”) - -The site’s visual language is “ASCII-first” (inspired by text UI / wireframes), but implemented with real HTML/CSS: - -- **No space-padding for layout.** Use actual CSS layout (flex/grid/margins) for spacing and alignment. -- **UI chrome is texty.** Mono labels, bracket motifs (`[ ... ]`), uppercase tracking, and simple dividers. -- **Frames are CSS borders.** Use borders/shadows for the main structure; textures are accents, not structure. -- **Text-like texture accents.** Prefer small SVG patterns (e.g. the textures in `public/textures/`) or `TextSeparator`’s glyph pattern instead of typing huge repeated characters. -- **Palette constraint.** Black/white/blue only (opacity variants allowed); styling is centralized via CSS variables in `src/app/globals.css` and Tailwind theme tokens. -- **Semantics first.** Underneath the “text UI” look, keep semantic HTML and accessible interactions. -- **Accessibility + no-JS fallback.** Always consider accessibility, and ensure core content/navigation works with JavaScript disabled. - -### Skeleton - -In this repo, **“skeleton”** does **not** mean a loading placeholder. It means a **decorative, ASCII-like UI panel** that hints at BitRemote’s app experience without being a functional UI. - -Implementation definition: **CSS layout + mono labels + border/wireframe + subtle glyph textures** — and explicitly **NOT** space-padded giant ASCII blocks. - -Current implementation: `src/components/AsciiSplitPanel.tsx` (decorative content is `aria-hidden`; hidden on small screens via responsive classes). + +## Project + +Project not yet initialized. Run /gsd:new-project to set up. + + + +## Technology Stack + +## Languages +- TypeScript 5.7.3 - All source code in `src/` +- JavaScript - Build configuration and scripts +- JSON - Internationalization messages and configuration +- CSS - Styling via Tailwind (processed through PostCSS) +## Runtime +- Node.js 25 (required for CI, see `.github/workflows/ci.yml`) +- npm (with lockfile `package-lock.json`) +- Lockfile: Present +## Frameworks +- Next.js 15.1.0 - Full-stack React framework for SSR/static generation +- React 19.0.0 - UI component library +- React DOM 19.0.0 - DOM rendering +- Tailwind CSS 3.4.0 - Utility-first CSS framework +- PostCSS 8.4.0 - CSS transformations +- Autoprefixer 10.4.0 - Vendor prefix automation +- TypeScript 5.7.3 - Language compilation +- ESLint 8.57.0 with next/core-web-vitals - Code linting +- ESLint Config Next 15.1.0 - Next.js specific linting rules +## Key Dependencies +- next (15.1.0) - Framework providing server rendering, static generation, and API routes +- react (19.0.0) - Component library and state management +- react-dom (19.0.0) - Renders React components to DOM +- autoprefixer (10.4.0) - Auto-adds vendor prefixes to CSS +- postcss (8.4.0) - CSS processing pipeline +- tailwindcss (3.4.0) - CSS utility generation from config +- @types/node (22.10.0) - Node.js type definitions +- @types/react (19.0.0) - React component type definitions +- @types/react-dom (19.0.0) - React DOM type definitions +- typescript (5.7.3) - TypeScript compiler +## Configuration +- `NEXT_ALLOWED_DEV_ORIGINS` (optional) - Comma-separated list of allowed origins for development +- `next.config.ts` - Next.js configuration +- `tsconfig.json` - TypeScript compiler settings +- `tailwind.config.ts` - Tailwind CSS configuration +- `.eslintrc.json` - ESLint configuration +- `postcss.config.js` - PostCSS pipeline +## Platform Requirements +- Node.js 25 (see `.github/workflows/ci.yml`) +- npm with lockfile caching support +- TypeScript compiler +- Cloudflare Pages (deployment target based on recent commit history) +- Static file hosting (output is `out/` directory) +- No server-side runtime required (static export) +## Build Output +- Static export to `out/` directory +- No API runtime required +- Suitable for static hosting (Cloudflare Pages, GitHub Pages) + + + +## Conventions + +## Naming Patterns +- Components: PascalCase (e.g., `BitRemoteWordmark.tsx`, `TextButton.tsx`) +- Utilities/Helpers: camelCase (e.g., `messages.ts`, `locales.ts`, `urls.ts`) +- Pages: Match Next.js conventions with brackets for dynamic routes (e.g., `[locale]`, `page.tsx`) +- Features organized in directories: lowercase with hyphens for multi-word (e.g., `ascii-panel`, `downloader-landings`) +- React components: PascalCase (e.g., `TextFrame`, `AsciiPanel`, `FaqAccordion`) +- Utility functions: camelCase (e.g., `parseSpeed`, `buildMetadataForCurrentLocalePage`, `localeRoot`) +- Internal helper functions: camelCase (e.g., `clientSkeletonName`, `digitText`, `numericDirection`) +- Boolean/type guard functions use `is` prefix (e.g., `isLocale`) +- Regular variables: camelCase (e.g., `activePage`, `isLocaleMenuOpen`, `localePrefix`) +- Constants: UPPER_SNAKE_CASE (e.g., `FRAME_INTERVAL_MS`, `LINKS`) +- Type/interface instances: PascalCase when they represent a type definition, camelCase when assigned +- Enums: PascalCase members (e.g., `Downloader.aria2`, `Downloader.qBittorrent`) +- Interface/Type definitions: PascalCase (e.g., `Props`, `Messages`, `FrameSheetState`, `ClientSpeed`) +- Generic type parameters: Single uppercase letter or descriptive PascalCase (e.g., `T`, `Locale`) +- Type aliases: PascalCase (e.g., `FramePageId`, `FramePresentation`) +## Code Style +- No explicit prettier config in project — uses ESLint's defaults +- Line length: No strict limit observed but code typically under 120 chars per line +- Indentation: 2 spaces (standard Next.js) +- Trailing commas: Used in objects and arrays +- ESLint configuration: `extends: ["next/core-web-vitals"]` +- Config file: `.eslintrc.json` +- Lint command: `npm run lint` runs ESLint with `--max-warnings 0` (zero warnings allowed) +- Enforced rules include Next.js best practices and core web vitals compliance +- All imports sorted: React types first, then third-party, then local imports +- Type imports use explicit `type` keyword (e.g., `import type { Metadata } from 'next'`) +- Multi-line imports: Values before types when from same module +## Import Organization +- `@/*` maps to `src/*` (defined in `tsconfig.json`) +- Always use `@/` for imports from `src/` directory +- Examples: `@/components`, `@/i18n`, `@/domain`, `@/seo`, `@/ascii-panel` +- `src/ascii-panel/index.tsx` exports `AsciiPanel` component as barrel export +- Most other modules export directly without barrel files +## Error Handling +- Explicit type guards for validation (e.g., `isLocale()` function in `src/i18n/locales.ts`) +- Fallback to defaults on invalid input (e.g., if locale is invalid, use `defaultLocale`) +- No try-catch blocks observed — static/compile-time approach preferred +- Optional chaining used extensively (e.g., `prevFrame[index]?.client`) +- Null coalescing with `??` operator (e.g., `clientsDataset[state.index] ?? clientsDataset[0]`) +## Logging +- No logging observed in production code +- No debug output in normal operation +- SVG filters and animations used for visual feedback instead +## Comments +- Minimal commenting — code is self-explanatory through naming +- Section dividers used in data-heavy files (e.g., `/* ------------------------------------------------------------------ */`) +- JSDoc-style comments on complex functions +- Not consistently used across the codebase +- Example from `src/i18n/locales.ts`: +- Rare inline comments explaining design decisions (e.g., `src/components/TextTabsNav.tsx`: +## Function Design +- Small utility functions: 5–10 lines +- React components: 20–50 lines +- Complex interactive components: 50–135+ lines +- Destructured from `Props` type or inline destructuring +- Always use explicit type annotations +- Example from `src/components/TextButton.tsx`: +- React components return JSX directly +- Utility functions return typed values +- Optional returns use `null` or `undefined` explicitly +- No implicit `undefined` returns +## Module Design +- Named exports used exclusively (no default exports observed except Next.js page/layout files) +- Each module exports only what's necessary +- Example from `src/i18n/messages.ts`: +- `src/ascii-panel/index.tsx` exports the main `AsciiPanel` component +- Other feature directories don't use barrel files, direct imports preferred +- Types defined at file level, before implementation +- Component `Props` type defined before component function +- Example pattern: +## Special Patterns +- Used for type-safe object/enum mappings +- Example from `src/ascii-panel/pages/HomePage.tsx`: +- `as const` used on objects/arrays that should be treated as literal types +- Enables type safety for mapped/readonly data +- `'use client'` directive used in interactive components (`src/ascii-panel/index.tsx`, `src/components/TextTabsNav.tsx`) +- Server components by default (no directive needed) +- Server functions with `async` keywords used in page components +- Used for immutable data structures (e.g., `readonly ClientSpeedFrame[]`) +- Combined with `const` for extra type safety + + + +## Architecture + +## Pattern Overview +- Next.js 15 with `output: export` configuration for static HTML generation +- App Router with dynamic parameters disabled (`dynamicParams = false`) for type-safe static routes +- Internationalization (i18n) as a core architectural concern handled via URL segments +- Component-driven UI with Tailwind CSS styling +- Server-side metadata generation for SEO with JSON-LD structured data +## Layers +- Purpose: Handle URL structure and page generation for multi-locale support +- Location: `src/app/` (Next.js App Router structure) +- Contains: Page components, layout components, API routes, metadata generation +- Depends on: i18n locales, domain models, component library +- Used by: Browser client, search engines +- Purpose: Encapsulate product-specific data and rules (downloaders, landing pages) +- Location: `src/domain/` +- Contains: `downloaders.ts` (Downloader enum, supported clients), `downloader-landings.ts` (content dictionaries, slug mapping) +- Depends on: i18n locales for content templates +- Used by: Page components, SEO/metadata generation +- Purpose: Centralize locale configuration, URL generation, and message management +- Location: `src/i18n/` +- Contains: +- Depends on: Message JSON files +- Used by: All page components, layouts, metadata builders +- Purpose: Generate Open Graph, Twitter Card, JSON-LD structured data, and canonical links +- Location: `src/seo/` +- Contains: +- Depends on: i18n messages, domain models +- Used by: Page components for metadata generation +- Purpose: Reusable UI components using Tailwind CSS +- Location: `src/components/` +- Contains: Buttons, frames, text components, separators, accordions, word mark +- Depends on: Tailwind configuration, CSS variables +- Used by: Page components, ASCII panel, layouts +- Purpose: Client-side interactive UI (uses `'use client'`) +- Location: `src/ascii-panel/` +- Contains: Frame-based terminal-style UI with state management +- Depends on: React hooks for state/effects +- Used by: Home page layout +## Data Flow +- Local component state via `useState` for: +- Animation triggered by `useEffect` on sheet phase change +- Respects `prefers-reduced-motion` media query +## Key Abstractions +- Purpose: Type-safe locale handling throughout the codebase +- Examples: `src/i18n/locales.ts` (Locale union type, isLocale guard) +- Pattern: Branded type with validation function to prevent string locale bugs +- Purpose: Centralized, type-safe multilingual content +- Examples: `src/messages/en.json`, `src/messages/ja.json`, etc. +- Pattern: JSON files imported as typed objects; `getMessages(locale)` returns fully typed Messages object +- Purpose: Generate localized content for product landing pages with locale-specific templates +- Examples: `downloaderLandingDictionary` in `src/domain/downloader-landings.ts` +- Pattern: Factory functions per downloader; templates per locale; `buildContent()` combines both to generate final page data +- Purpose: Encapsulate locale-aware URL generation with consistent formatting +- Examples: `localePath()`, `localeRoot()`, `absoluteUrl()` in `src/i18n/urls.ts` +- Pattern: Single responsibility functions that handle trailing slashes and locale prefixes +- Purpose: Generate structured data (JSON-LD) for rich snippets +- Examples: `buildSoftwareApplicationSchema()`, `buildFaqPageSchema()` in `src/seo/schema.ts` +- Pattern: Pure functions that combine messages + locale + links into schema objects +## Entry Points +- Location: `src/app/layout.tsx` +- Triggers: Every page request +- Responsibilities: Global metadata, favicon/manifest, HTML structure, global styles import +- Location: `src/app/[locale]/layout.tsx` +- Triggers: Every locale-specific route request +- Responsibilities: Static param generation, message loading, locale validation, navigation UI (TextTabsNav), footer layout +- Location: `src/app/[locale]/page.tsx` +- Triggers: `/{locale}/` requests +- Responsibilities: Hero section rendering, features grid, FAQ, schema.org markup, downloader links with landing pages +- Location: `src/app/[locale]/downloaders/[slug]/page.tsx` +- Triggers: `/{locale}/downloaders/{slug}/` requests +- Responsibilities: Render downloader-specific content, build breadcrumb schema, generate downloader metadata +- Location: `src/app/page.tsx` +- Triggers: `/` root request +- Responsibilities: Language selector, link to App Store +- Location: `src/app/sitemap.ts` +- Triggers: Build time (static generation) +- Responsibilities: List all localized routes and downloader pages with language alternates +## Error Handling +- Locale validation via `isLocale()` guard; falls back to `defaultLocale` if invalid +- Message loading fails hard (import-time, caught during build) rather than runtime +- Factory pattern for downloader content with `undefined` check for missing content +- `dynamicParams = false` in layout prevents unexpected 404s for ungenerated locales +## Cross-Cutting Concerns + + + +## GSD Workflow Enforcement + +Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync. + +Use these entry points: +- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks +- `/gsd:debug` for investigation and bug fixing +- `/gsd:execute-phase` for planned phase work + +Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it. + + + +## Developer Profile + +> Profile not yet configured. Run `/gsd:profile-user` to generate your developer profile. +> This section is managed by `generate-claude-profile` -- do not edit manually. + diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 684d6a6..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -~ ./AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..55bf822 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +./AGENTS.md \ No newline at end of file From 4cc6eb4f815759877e0356163f0ec13f52353498 Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:09:51 +0900 Subject: [PATCH 04/12] docs: initialize project Co-Authored-By: Claude Opus 4.6 (1M context) --- .planning/PROJECT.md | 103 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 .planning/PROJECT.md diff --git a/.planning/PROJECT.md b/.planning/PROJECT.md new file mode 100644 index 0000000..434f1b5 --- /dev/null +++ b/.planning/PROJECT.md @@ -0,0 +1,103 @@ +# BitRemote Website Redesign + +## What This Is + +A visual redesign of the BitRemote marketing website (bitremote.app) to align its design language with the BitRemote native app. The current site uses a retro ASCII/monospace aesthetic that misrepresents the product — a polished, modern native Apple app. The redesigned site should feel like a natural extension of the app itself while leveraging marketing-grade visual effects to convert visitors. + +## Core Value + +The website must look and feel like it belongs to the same product as the BitRemote app — clean, professional, and visually impressive enough to make tech-savvy visitors want to download it. + +## Requirements + +### Validated + +- ✓ Multi-locale support (en, ja, zh-hans, zh-hant) — existing +- ✓ Static site generation via Next.js with Cloudflare Pages deployment — existing +- ✓ SEO optimization with JSON-LD schema, sitemap, robots.txt — existing +- ✓ Downloader-specific landing pages (qBittorrent, Synology DS, etc.) — existing +- ✓ App Store download link integration — existing +- ✓ FAQ section — existing +- ✓ Legal pages (privacy, terms, support) — existing +- ✓ Social links (Twitter, Discord, Telegram, GitHub) — existing +- ✓ LLM context route (/llms-full.txt) — existing + +### Active + +- [ ] Replace ASCII/monospace design language with modern sans-serif typography +- [ ] Implement monochromatic color palette (black/white/gray) with blue as the sole accent color +- [ ] Design can be dark or light themed (app supports both) — dark preferred for premium feel +- [ ] Add polished visual effects suitable for a marketing site (scroll animations, parallax, glowing elements, smooth transitions) +- [ ] Showcase app screenshots as hero visuals — let the app's UI sell itself +- [ ] Use card-based layouts and rounded shapes that echo the app's native UI patterns +- [ ] Maintain all existing content and functionality during redesign +- [ ] Ensure responsive design across mobile, tablet, and desktop +- [ ] Remove ASCII art separators, text-frame components, and terminal-style UI elements + +### Out of Scope + +- Backend/API functionality — this is a static marketing site +- User authentication or accounts +- Analytics integration — can be added separately later +- Blog or changelog — different initiative +- New content pages beyond what exists — focus is visual, not content +- App icon or branding changes — the app's identity stays as-is + +## Context + +**Product:** BitRemote is a native Apple app (iOS, iPadOS, macOS) for remotely managing download tasks on NAS and home servers. It supports aria2, Synology Download Station, QNAP Download Station, and Transmission. + +**Target audience:** Tech-savvy users who run NAS/home servers. They appreciate good design and native app quality — the kind of people who notice when software is well-crafted. + +**App design language (from App Store screenshots):** +- Monochromatic base (black/white/gray) with blue as the only accent color +- Clean sans-serif typography (SF Pro / system font) +- Card-based layouts with rounded corners and subtle backgrounds +- Data-rich but visually calm — professional and pretty +- The Activity Monitor page is an exception with colorful stat cards (green, purple, pink, cyan) to make monitoring engaging +- Supports both dark and light interfaces + +**Current website problems:** +- Monospace/ASCII aesthetic makes the product look like a CLI tool +- Text-frame components and ░ separators feel retro when the app is modern +- No app screenshots showcased — the app's best selling point is hidden +- Visual disconnect undermines trust ("is this really the same product?") + +**Tech stack:** Next.js 15, React 19, TypeScript, Tailwind CSS 3.4, static export to Cloudflare Pages + +## Constraints + +- **Tech stack**: Keep Next.js + Tailwind CSS + static export — no framework migration +- **Content preservation**: All existing localized content must carry over +- **Deployment**: Must remain compatible with Cloudflare Pages static hosting +- **Performance**: Static site must remain fast — visual effects should not degrade load times significantly +- **Accessibility**: Maintain existing aria labels, screen reader support, reduced motion support + +## Key Decisions + +| Decision | Rationale | Outcome | +|----------|-----------|---------| +| Blue as sole accent color | Matches app's design system — monochromatic + blue | — Pending | +| Dark or light theme TBD | App supports both; dark feels premium for marketing | — Pending | +| Marketing-grade visual effects allowed | This is a conversion page, not docs — impressiveness matters | — Pending | +| Keep Next.js static export | No reason to change what works for deployment | ✓ Good | + +## Evolution + +This document evolves at phase transitions and milestone boundaries. + +**After each phase transition** (via `/gsd:transition`): +1. Requirements invalidated? → Move to Out of Scope with reason +2. Requirements validated? → Move to Validated with phase reference +3. New requirements emerged? → Add to Active +4. Decisions to log? → Add to Key Decisions +5. "What This Is" still accurate? → Update if drifted + +**After each milestone** (via `/gsd:complete-milestone`): +1. Full review of all sections +2. Core Value check — still the right priority? +3. Audit Out of Scope — reasons still valid? +4. Update Context with current state + +--- +*Last updated: 2026-03-22 after initialization* From 3f93c78583cb426d72c5126f69adb00e4e186530 Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:20:58 +0900 Subject: [PATCH 05/12] docs: complete project research Add STACK, FEATURES, ARCHITECTURE, PITFALLS, and SUMMARY research files for the BitRemote website redesign project. Co-Authored-By: Claude Sonnet 4.6 --- .planning/research/ARCHITECTURE.md | 490 +++++++++++++++++++++++++++++ .planning/research/FEATURES.md | 129 ++++++++ .planning/research/PITFALLS.md | 346 ++++++++++++++++++++ .planning/research/STACK.md | 209 ++++++++++++ .planning/research/SUMMARY.md | 192 +++++++++++ 5 files changed, 1366 insertions(+) create mode 100644 .planning/research/ARCHITECTURE.md create mode 100644 .planning/research/FEATURES.md create mode 100644 .planning/research/PITFALLS.md create mode 100644 .planning/research/STACK.md create mode 100644 .planning/research/SUMMARY.md diff --git a/.planning/research/ARCHITECTURE.md b/.planning/research/ARCHITECTURE.md new file mode 100644 index 0000000..8b7fdf8 --- /dev/null +++ b/.planning/research/ARCHITECTURE.md @@ -0,0 +1,490 @@ +# Architecture Patterns + +**Domain:** Marketing website visual redesign (Next.js 15 + Tailwind CSS) +**Researched:** 2026-03-22 + +--- + +## Recommended Architecture + +Layer a design system on top of the existing Next.js App Router structure without changing routing, i18n, SEO, or domain layers. The redesign is a visual overhaul only — the data flow, locale resolution, and static generation pipeline stay untouched. + +The architecture adds two new layers (design tokens, animation primitives) and replaces all existing components in `src/components/` and `src/ascii-panel/` with new ones. Pages (`src/app/`) require edits only to swap in new components — their structure stays the same. + +``` +┌──────────────────────────────────────────────────────────┐ +│ Pages / Layouts (src/app/) [unchanged] │ +│ Routing, locale params, metadata, JSON-LD │ +├──────────────────────────────────────────────────────────┤ +│ Section Components (src/components/sections/) [NEW] │ +│ Hero, Features, Screenshots, FAQ, CTA, Footer │ +│ Composed from primitives. One section = one file. │ +├──────────────────────────────────────────────────────────┤ +│ Primitive Components (src/components/ui/) [NEW] │ +│ Button, Badge, Card, Divider, Wordmark, Typography │ +│ Stateless, token-driven, CVA variants │ +├──────────────────────────────────────────────────────────┤ +│ Animation Primitives (src/components/motion/) [NEW] │ +│ FadeIn, SlideIn, StaggerGroup, ParallaxLayer │ +│ Thin wrappers over Framer Motion. Isolated. │ +├──────────────────────────────────────────────────────────┤ +│ Design Tokens (tailwind.config.ts + globals.css) │ +│ CSS variables for color, radius, shadow, spacing │ +│ Dark mode as variable override, not class duplication │ +├──────────────────────────────────────────────────────────┤ +│ Domain / i18n / SEO layers [unchanged] │ +│ src/domain/, src/i18n/, src/seo/, src/messages/ │ +└──────────────────────────────────────────────────────────┘ +``` + +--- + +## Component Boundaries + +### Component Hierarchy + +| Component | Responsibility | Communicates With | Location | +|-----------|---------------|-------------------|----------| +| `src/app/[locale]/layout.tsx` | Locale shell, nav, footer | NavBar, Footer (sections) | unchanged | +| `src/app/[locale]/page.tsx` | Assemble home sections | HeroSection, FeaturesSection, ScreenshotsSection, FaqSection, CtaSection | minor edits | +| `src/app/[locale]/downloaders/[slug]/page.tsx` | Downloader landing | DownloaderHero, DownloaderFeatures (new section variants) | minor edits | +| `HeroSection` | Full-viewport hero with headline + app screenshot | Button (ui), AppMockup (ui), FadeIn (motion) | `src/components/sections/` | +| `FeaturesSection` | Feature grid with icons and descriptions | Card (ui), StaggerGroup (motion) | `src/components/sections/` | +| `ScreenshotsSection` | App screenshot gallery with captions | AppMockup (ui), SlideIn (motion) | `src/components/sections/` | +| `FaqSection` | Accordion for FAQ content | FaqAccordion (ui), FadeIn (motion) | `src/components/sections/` | +| `CtaSection` | Download prompt with App Store badge | Button (ui), FadeIn (motion) | `src/components/sections/` | +| `NavBar` | Site navigation with wordmark and locale toggle | Wordmark (ui), TextButton (ui) | `src/components/sections/` | +| `Footer` | Links, social icons, legal links | — | `src/components/sections/` | +| `AppMockup` | Device frame wrapping screenshot image | Next.js `` | `src/components/ui/` | +| `Card` | Surface container with rounded border and shadow | — | `src/components/ui/` | +| `Button` | CTA and navigation button with CVA variants | — | `src/components/ui/` | +| `Badge` | Pill label (e.g., "Free", "New") | — | `src/components/ui/` | +| `FadeIn` | Fade-in entrance via `whileInView` | Framer Motion | `src/components/motion/` | +| `SlideIn` | Directional slide entrance via `whileInView` | Framer Motion | `src/components/motion/` | +| `StaggerGroup` | Staggers children entrance with delay | FadeIn or SlideIn | `src/components/motion/` | +| `ParallaxLayer` | CSS `transform` scroll-linked parallax | Framer Motion `useScroll` + `useTransform` | `src/components/motion/` | + +### Strict Boundary Rules + +- Section components compose primitives. They never import other sections. +- Primitive (ui/) components have zero animation logic — animation is always a motion/ wrapper. +- Motion components have zero business or content logic — they render `children` only. +- Pages import sections only. Pages never import ui/ or motion/ directly. +- Domain, i18n, and SEO layers are not modified. + +--- + +## Design Token System + +**Decision: CSS variables in `globals.css`, consumed by `tailwind.config.ts` as semantic aliases.** + +This is the correct pattern for a static Next.js site: no runtime overhead, dark mode is a single `[data-theme="dark"]` override block, and Tailwind utilities map to semantic names rather than literal colors. Confidence: HIGH (verified against Tailwind docs and current community consensus). + +### Token Categories + +```css +/* globals.css */ +:root { + /* Surface hierarchy */ + --color-bg: #ffffff; + --color-surface: #f8f8f8; + --color-surface-alt: #f0f0f0; + --color-border: #e5e5e5; + + /* Text */ + --color-text-primary: #0a0a0a; + --color-text-secondary: #6b7280; + --color-text-muted: #9ca3af; + + /* Accent (blue only) */ + --color-accent: #2563eb; + --color-accent-hover: #1d4ed8; + --color-accent-subtle: #dbeafe; + + /* Radius scale */ + --radius-sm: 0.375rem; /* 6px */ + --radius-md: 0.75rem; /* 12px */ + --radius-lg: 1.25rem; /* 20px */ + --radius-xl: 1.75rem; /* 28px — cards matching app UI */ + + /* Shadow */ + --shadow-card: 0 1px 3px rgb(0 0 0 / 0.08), 0 4px 16px rgb(0 0 0 / 0.06); + --shadow-raise: 0 8px 32px rgb(0 0 0 / 0.12); +} + +[data-theme="dark"] { + --color-bg: #0a0a0a; + --color-surface: #141414; + --color-surface-alt: #1e1e1e; + --color-border: #2a2a2a; + + --color-text-primary: #f5f5f5; + --color-text-secondary: #a3a3a3; + --color-text-muted: #6b6b6b; + + --color-accent: #3b82f6; + --color-accent-hover: #60a5fa; + --color-accent-subtle: #1e3a5f; + + --shadow-card: 0 1px 3px rgb(0 0 0 / 0.4), 0 4px 16px rgb(0 0 0 / 0.3); + --shadow-raise: 0 8px 32px rgb(0 0 0 / 0.5); +} +``` + +```typescript +// tailwind.config.ts — semantic extensions +theme: { + extend: { + colors: { + bg: 'var(--color-bg)', + surface: 'var(--color-surface)', + 'surface-alt':'var(--color-surface-alt)', + border: 'var(--color-border)', + text: { + primary: 'var(--color-text-primary)', + secondary: 'var(--color-text-secondary)', + muted: 'var(--color-text-muted)', + }, + accent: { + DEFAULT: 'var(--color-accent)', + hover: 'var(--color-accent-hover)', + subtle: 'var(--color-accent-subtle)', + }, + }, + borderRadius: { + sm: 'var(--radius-sm)', + md: 'var(--radius-md)', + lg: 'var(--radius-lg)', + xl: 'var(--radius-xl)', + }, + boxShadow: { + card: 'var(--shadow-card)', + raise: 'var(--shadow-raise)', + }, + }, +} +``` + +**Why semantic names, not literal ones:** Dark mode becomes one variable override block in CSS. Without this pattern, dark mode requires `dark:` variants on every utility class — exponentially harder to maintain. + +**Theme activation:** Set `data-theme="dark"` on `` in the root layout. Dark is the preferred default for the marketing site (premium feel). A `` component can be added later without restructuring. + +--- + +## Animation Orchestration Patterns + +**Decision: Framer Motion (motion.dev) for all scroll animations and entrance effects. Pure CSS for hover and micro-interactions.** + +Framer Motion is the correct choice for a React/Next.js site with scroll-triggered animations. CSS alone lacks the orchestration primitives needed for stagger groups and scroll-linked transforms. Confidence: HIGH (verified against motion.dev docs and LogRocket comparison). + +### Pattern 1: Viewport-Triggered Entrance (FadeIn) + +**What:** Section content fades and translates up when scrolled into view. +**When:** Use on every section below the hero fold. Fire once only. +**Implementation note:** Add `'use client'` — Framer Motion requires it in App Router. + +```typescript +// src/components/motion/FadeIn.tsx +'use client'; +import { motion } from 'motion/react'; + +interface FadeInProps { + children: React.ReactNode; + delay?: number; + className?: string; +} + +export function FadeIn({ children, delay = 0, className }: FadeInProps) { + return ( + + {children} + + ); +} +``` + +**Key options:** +- `viewport={{ once: true }}` — animation fires once, not on scroll-back (performance + UX) +- `margin: '-80px'` — trigger 80px before element enters viewport for smooth feel +- Composite properties only (`opacity`, `transform`) — runs on compositor thread, not main thread + +### Pattern 2: Staggered Group Entrance (StaggerGroup) + +**What:** A group of children animate in sequentially with a delay between each. +**When:** Feature cards, screenshot gallery, any repeating item list. + +```typescript +// src/components/motion/StaggerGroup.tsx +'use client'; +import { motion } from 'motion/react'; + +const container = { + hidden: {}, + show: { transition: { staggerChildren: 0.1 } }, +}; + +const item = { + hidden: { opacity: 0, y: 16 }, + show: { opacity: 1, y: 0, transition: { duration: 0.4, ease: 'easeOut' } }, +}; + +export function StaggerGroup({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export function StaggerItem({ children, className }: { children: React.ReactNode; className?: string }) { + return {children}; +} +``` + +### Pattern 3: Scroll-Linked Parallax (ParallaxLayer) + +**What:** A layer moves at a slower rate than scroll, creating depth. +**When:** Hero background element or app screenshot decorative layer — one or two uses maximum. +**Warning:** Use sparingly. Overuse degrades performance and induces motion sickness. Every `useScroll` call attaches a scroll listener. + +```typescript +// src/components/motion/ParallaxLayer.tsx +'use client'; +import { useRef } from 'react'; +import { motion, useScroll, useTransform } from 'motion/react'; + +export function ParallaxLayer({ children, speed = 0.3 }: { children: React.ReactNode; speed?: number }) { + const ref = useRef(null); + const { scrollYProgress } = useScroll({ target: ref, offset: ['start end', 'end start'] }); + const y = useTransform(scrollYProgress, [0, 1], [`${speed * -50}px`, `${speed * 50}px`]); + + return ( +
+ {children} +
+ ); +} +``` + +### Pattern 4: Reduced Motion Fallback + +**What:** All Framer Motion animations respect `prefers-reduced-motion`. +**When:** Always. Accessibility requirement. + +Framer Motion respects `prefers-reduced-motion` automatically when using `animate` + `initial` pairs. Verify by testing: with reduced motion enabled, `whileInView` will snap to final state without transition. This is the correct behavior — no additional code needed for the core case. + +For `ParallaxLayer` specifically, add an explicit check: + +```typescript +import { useReducedMotion } from 'motion/react'; +// In ParallaxLayer: if (shouldReduceMotion) return
{children}
; +``` + +--- + +## Data Flow Direction + +``` +Build Time (Static): + Next.js generateStaticParams() + → locale params + → Page component renders + → getMessages(locale) → message dictionary + → Section components receive messages as props + → Primitive components receive typed props + → Tailwind CSS produces class-driven styles from tokens + → Static HTML exported + +Runtime (Client): + HTML loads from Cloudflare Pages CDN + → React hydrates only 'use client' components (motion wrappers) + → IntersectionObserver watches for whileInView targets + → User scrolls → viewport triggers → motion animates + → Theme: document.documentElement.dataset.theme set once at load + → No server calls, no dynamic data fetching +``` + +**Hydration surface is minimal by design.** Server components render everything static. Only animation wrapper components carry `'use client'`. The hero section, feature cards, and screenshot gallery are server-rendered HTML that Framer Motion hydrates for animation — not for rendering. + +--- + +## Component Build Order + +Build in strict dependency order. Each tier must be complete before the next. + +### Tier 1: Design Foundation (no dependencies) + +1. CSS token variables in `globals.css` (color, radius, shadow, spacing) +2. Tailwind semantic aliases in `tailwind.config.ts` +3. Typography scale (font-size, line-height, letter-spacing in config) +4. Global resets and base styles (body background, selection color) + +**Output:** Tailwind utilities like `bg-surface`, `text-text-primary`, `rounded-xl`, `shadow-card` are usable. + +### Tier 2: Motion Primitives (depends on: Tier 1) + +5. `FadeIn` component +6. `SlideIn` component (directional variant of FadeIn) +7. `StaggerGroup` + `StaggerItem` components +8. `ParallaxLayer` component + +**Output:** Animation wrappers usable by any component above them. + +### Tier 3: UI Primitives (depends on: Tier 1) + +9. `Button` with CVA variants (primary/secondary/ghost, sm/md/lg) +10. `Card` with token-driven surface, border, radius, shadow +11. `Badge` pill component +12. `AppMockup` device frame wrapping `next/image` +13. `Wordmark` (replace `BitRemoteWordmark.tsx` with token-styled version) +14. `FaqAccordion` (replace existing with token-styled version) + +**Output:** Full primitive library composable into sections. + +### Tier 4: Section Components (depends on: Tiers 1–3) + +15. `NavBar` (navigation + wordmark + locale toggle) +16. `HeroSection` (headline + subheadline + CTA + AppMockup) +17. `FeaturesSection` (feature grid using StaggerGroup + Card) +18. `ScreenshotsSection` (gallery of AppMockup with captions) +19. `FaqSection` (FaqAccordion wrapped in FadeIn) +20. `CtaSection` (download prompt with App Store badge) +21. `Footer` (links, social, legal) + +**Output:** All sections composable into pages. + +### Tier 5: Page Assembly (depends on: Tier 4) + +22. Update `src/app/[locale]/layout.tsx` — swap NavBar and Footer +23. Update `src/app/[locale]/page.tsx` — compose home sections +24. Update `src/app/[locale]/downloaders/[slug]/page.tsx` — reuse sections with downloader data +25. Update `src/app/page.tsx` (language selector root) — apply token-styled UI + +**Output:** Fully redesigned site with all existing functionality preserved. + +### Tier 6: Polish (depends on: Tiers 1–5) + +26. Glow and blur decorative effects (CSS `box-shadow`, `backdrop-filter`) +27. Hover micro-interactions (pure CSS transitions on primitives) +28. Theme toggle component (if shipping dark/light toggle) +29. Responsive breakpoint audit across all sections + +--- + +## Existing Code to Remove + +The following existing components are fully replaced and should be deleted: + +| File | Replaced By | +|------|------------| +| `src/ascii-panel/` (entire directory) | `ScreenshotsSection` + `AppMockup` | +| `src/components/TextFrame.tsx` | `Card` (ui primitive) | +| `src/components/TextButton.tsx` | `Button` (ui primitive) | +| `src/components/TextSeparator.tsx` | Token-driven `
` or `Divider` | +| `src/components/TextTabsNav.tsx` | `NavBar` (section) | +| `src/components/BitRemoteWordmark.tsx` | `Wordmark` (ui primitive, token-styled) | +| `src/components/FaqAccordion.tsx` | `FaqAccordion` (ui primitive, token-styled) | +| `src/components/DownloaderLandingPage.tsx` | Section-composed downloader page | + +**Do not touch:** `src/domain/`, `src/i18n/`, `src/seo/`, `src/messages/`, `src/app/robots.ts`, `src/app/sitemap.ts`, `src/app/llms-full.txt/`. + +--- + +## Scalability Considerations + +| Concern | At current scale | If content grows | +|---------|-----------------|-----------------| +| CSS bundle | Token approach produces minimal CSS — no dark: duplication | No change needed | +| Animation performance | whileInView + once:true is O(1) per element via IntersectionObserver pool | Add `will-change: transform` hint on high-frequency parallax targets | +| New locales | Tokens are locale-agnostic — add message keys only | No architectural change | +| New pages | Add section components, compose in new page.tsx | Sections are already reusable by design | +| Light/dark toggle | data-theme swap on `` — CSS handles the rest | ThemeToggle component is a one-line addition | + +--- + +## Anti-Patterns to Avoid + +### Anti-Pattern 1: dark: Class Duplication + +**What:** Writing `bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100` on every element. +**Why bad:** Doubles the utility classes in every component. Dark mode becomes a global find-and-replace problem. Any token change requires updating dozens of files. +**Instead:** Use CSS variable tokens — one `:root` / `[data-theme="dark"]` override governs the entire site. + +### Anti-Pattern 2: Motion Logic Inside Section Components + +**What:** Importing Framer Motion directly in `HeroSection.tsx` and writing animation logic there. +**Why bad:** Couples content logic with animation logic. Makes sections hard to test, snapshot, or use in a Storybook. Forces `'use client'` on entire section when only the animation needs it. +**Instead:** Wrap content children in `FadeIn`, `StaggerGroup`, etc. from `src/components/motion/`. Section stays a Server Component; only the wrapper is a Client Component. + +### Anti-Pattern 3: Inline Style Animations + +**What:** Using JavaScript `element.style.transform = ...` on scroll events with `addEventListener`. +**Why bad:** Runs on main thread. Blocks paint. Causes jank on lower-end devices. No cleanup. +**Instead:** Framer Motion `useScroll` + `useTransform` uses `requestAnimationFrame` and GPU-composited properties. + +### Anti-Pattern 4: Replacing the Routing Layer + +**What:** Migrating to a different router or layout structure as part of the visual redesign. +**Why bad:** This project is a visual overhaul only. The existing locale routing, `generateStaticParams`, `generateMetadata`, and i18n layers are correct and working. Touching them adds risk with no visual benefit. +**Instead:** Edit only the component composition inside page files. Leave `generateStaticParams`, `generateMetadata`, locale resolution, and SEO schema generation completely intact. + +### Anti-Pattern 5: Animating Layout Properties + +**What:** Animating `height`, `width`, `top`, `left`, `padding`, `margin` with Framer Motion. +**Why bad:** Triggers layout recalculation and paint on every frame. Causes jank. +**Instead:** Animate only `opacity` and `transform` (translate, scale, rotate). These run on the GPU compositor thread and never trigger layout. + +--- + +## Dependency Map + +``` +globals.css (tokens) + └── tailwind.config.ts (semantic aliases) + ├── src/components/ui/Button.tsx + ├── src/components/ui/Card.tsx + ├── src/components/ui/AppMockup.tsx + ├── src/components/ui/Badge.tsx + ├── src/components/ui/Wordmark.tsx + └── src/components/ui/FaqAccordion.tsx + +motion/ (animation primitives — no token dependency, only Framer Motion) + ├── FadeIn.tsx + ├── SlideIn.tsx + ├── StaggerGroup.tsx + └── ParallaxLayer.tsx + +ui/ + motion/ → sections/ + ├── NavBar.tsx + ├── HeroSection.tsx (ui/AppMockup + motion/FadeIn + ui/Button) + ├── FeaturesSection.tsx (ui/Card + motion/StaggerGroup) + ├── ScreenshotsSection.tsx (ui/AppMockup + motion/SlideIn) + ├── FaqSection.tsx (ui/FaqAccordion + motion/FadeIn) + ├── CtaSection.tsx (ui/Button + motion/FadeIn) + └── Footer.tsx + +sections/ → src/app/ (pages, via props only — no shared state) + +[unchanged] + src/domain/ → src/i18n/ → src/seo/ → src/messages/ + All flow through generateMetadata() and page render props as before. +``` + +--- + +## Sources + +- Motion (Framer Motion) scroll animations docs: [motion.dev/docs/react-scroll-animations](https://motion.dev/docs/react-scroll-animations) — HIGH confidence +- Tailwind CSS dark mode docs: [tailwindcss.com/docs/dark-mode](https://tailwindcss.com/docs/dark-mode) — HIGH confidence +- Tailwind CSS theme variables: [tailwindcss.com/docs/theme](https://tailwindcss.com/docs/theme) — HIGH confidence +- Class Variance Authority: [cva.style/docs](https://cva.style/docs) — HIGH confidence +- Design tokens with Tailwind dark mode pattern: [richinfante.com](https://www.richinfante.com/2024/10/21/tailwind-dark-mode-design-tokens-themes-css) — MEDIUM confidence +- LogRocket React animation library comparison: [blog.logrocket.com/best-react-animation-libraries](https://blog.logrocket.com/best-react-animation-libraries/) — MEDIUM confidence +- FreeCodcamp design system with CVA + Tailwind: [freecodecamp.org](https://www.freecodecamp.org/news/how-a-design-system-in-next-js-with-tailwind-css-and-class-variance-authority/) — MEDIUM confidence +- Framer Motion whileInView viewport once pattern: [dev.to/shivamkatare](https://dev.to/shivamkatare/create-beautiful-scroll-animations-using-framer-motion-1a7b) — MEDIUM confidence diff --git a/.planning/research/FEATURES.md b/.planning/research/FEATURES.md new file mode 100644 index 0000000..f9527d3 --- /dev/null +++ b/.planning/research/FEATURES.md @@ -0,0 +1,129 @@ +# Feature Landscape + +**Domain:** Native Apple app marketing website (iOS/iPadOS/macOS, tech-savvy audience) +**Researched:** 2026-03-22 +**Confidence:** MEDIUM — based on pattern analysis of exemplary sites (linear.app, things.app, bear.app), SaaS landing page research, and direct inspection of the existing BitRemote site. WebFetch was restricted; direct scraping of reference sites was not possible. + +--- + +## Table Stakes + +Features visitors expect. Missing = product feels incomplete or untrustworthy. + +| Feature | Why Expected | Complexity | Notes | +|---------|--------------|------------|-------| +| Hero section with headline + subhead | First impression; orients visitors instantly | Low | BitRemote has this — needs visual upgrade, not content change | +| App Store download badge/CTA | The entire point of the page; absence kills conversion | Low | Exists; must be visually prominent and above the fold | +| App screenshots in the hero | Tech users evaluate UI quality before downloading | Medium | Currently absent — the single biggest gap vs. reference sites | +| Responsive layout (mobile/tablet/desktop) | 63%+ of web traffic is mobile; requirement for trust | Medium | Exists but needs to work with new visual components | +| Feature/benefits section | Answers "what does this do?" | Low | Exists as TextFrame grid — needs visual redesign | +| FAQ section | Reduces support burden; answers pre-download doubts | Low | Exists with accordion — keep behavior, restyle | +| Clear navigation (minimal) | Lets users jump to relevant sections | Low | Currently none beyond in-page anchors | +| Legal pages (privacy, terms, support) | Legal requirement; expected for any app | Low | Exists — footer links | +| Social links | Community trust signal for indie/niche apps | Low | Exists — Twitter, Discord, Telegram, GitHub | +| Consistent typography hierarchy | Readability baseline; sans-serif expected for modern apps | Low | Currently monospace throughout — must be replaced | +| Dark or light themed (not mixed/jarring) | Coherent identity; reflects app's polish | Low | Currently inconsistent with app's clean aesthetic | + +--- + +## Differentiators + +Features that set a marketing site apart. Not universally expected, but make exemplary sites memorable to tech audiences. + +| Feature | Value Proposition | Complexity | Notes | +|---------|-------------------|------------|-------| +| Scroll-linked screenshot reveal | App screenshots animate into view as user scrolls — creates engagement and shows depth | High | The "Linear look" and Apple product page approach. CSS scroll-driven animations (no JS needed in modern browsers) animating `transform` and `opacity` — GPU-friendly. Requires device mockup framing. | +| Device mockup framing (iPhone/iPad/Mac) | Grounds screenshots in reality; signals "this is a real polished app" | Medium | Static or lightly animated device frames. Can use CSS-only or pre-rendered PNGs. High return on perceived quality. | +| Bento grid feature layout | Replaces flat text grids with visually rich card compositions; each card can have different weight | Medium | Tech audiences recognize this from Linear, Vercel, Apple. Cards vary in size to show hierarchy of features. Monochromatic base with blue accent highlight cards. | +| Subtle ambient glow / gradient mesh background | Creates premium dark-theme depth without color noise | Medium | Single blue-tinted glow orb behind hero or key sections. Used by linear.app and Arc. Keep it restrained — one or two glow sources, not a rainbow. | +| Glassmorphism card surfaces | Frosted-glass cards over dark backgrounds create depth without heavy color | Medium | Pairs naturally with dark theme and subtle glow backgrounds. Use `backdrop-filter: blur()` on cards. Performance-safe when used selectively. | +| Thin SVG border lines on section dividers | Replaces ░ ASCII separators with ultra-thin `1px` lines or subtle grid patterns | Low | Directly from "linear look" pattern. Monochromatic, elegant, zero visual noise. Simple CSS border or SVG. | +| Downloader-specific landing pages with tailored content | Speaks directly to the visitor's setup (e.g., "for Synology users"); high relevance = high conversion | Medium | Already exists — needs visual upgrade to match new design system. Hero copy and screenshots should adapt per downloader. | +| Staggered section entrance animations | Content fades/slides in on scroll rather than rendering instantly — creates sense of quality and intentionality | Medium | CSS `@keyframes` with `IntersectionObserver` or CSS scroll-driven animations. Animate `opacity` + `translateY` only (no layout-triggering properties). | +| Wordmark / logotype as primary identity element | Replaces generic site names with a designed wordmark; signals brand intentionality | Low | BitRemoteWordmark component already exists — keep it, elevate its visual placement | +| Platform badge strip | Shows iOS, iPadOS, macOS badges in a single row — signals universal Apple platform coverage | Low | Signals "this is a serious cross-device app." Simple icon strip, no interaction needed. | +| Social proof pull-quote | One or two press mentions or App Store review excerpts, displayed in a minimal quote format | Low | Even a single credible quote significantly increases trust for indie apps. Tech audience is skeptical; authenticity matters over quantity. | +| Inline CTA repetition (top + middle + bottom) | Repeating the App Store link at natural scroll breaks prevents visitors from having to scroll back up | Low | Current site has CTA only at top. Add one in features section and one before FAQ. | + +--- + +## Anti-Features + +Features to deliberately NOT build for this type of site. + +| Anti-Feature | Why Avoid | What to Do Instead | +|--------------|-----------|-------------------| +| Video background / autoplaying hero video | Heavy assets kill performance on static Cloudflare Pages; distracting for data-focused tech users; inaccessible | Use high-quality static screenshots in device mockups with subtle scroll animation | +| Email newsletter signup / lead capture form | Wrong funnel for a paid native app — no product to nurture toward; adds GDPR complexity with no benefit | Direct visitors straight to App Store; no detour | +| Testimonial carousel / slider | Carousels are low-engagement; users skip them; they also signal padded social proof | Use 1-2 static pull-quotes in-line, or omit entirely if no strong press quotes exist | +| Pricing comparison table | BitRemote is a single paid app; no tiers to compare; a table implies complexity that doesn't exist | Display price naturally in the App Store CTA copy if needed | +| Cookie consent banner | Static site with no analytics or tracking — nothing to consent to | Keep the site cookieless and banner-free | +| Chat widget (Intercom, Drift, etc.) | Wrong support channel for a native Apple app; tech users prefer Discord/GitHub issues | Keep existing social links (Discord, Telegram, GitHub) as support channels | +| Animated particle system / canvas backgrounds | Performance penalty; often looks generic or dated; distracts from app UI | Subtle CSS gradient mesh or single glow orb is sufficient and far more performant | +| Infinite scroll or paginated content | This is a one-page marketing site; paginated content belongs to a blog (out of scope) | Single-page scrolling experience with anchor navigation | +| User-generated content / review aggregator | Requires backend, moderation, and real-time data — incompatible with static export | Pull-quote one or two curated App Store reviews statically in the HTML | +| Dark pattern CTAs ("Download FREE - No Credit Card") | Tech-savvy users know apps cost money; patronizing language erodes trust | Honest CTA: "Download on the App Store" or "Get BitRemote" | +| Modal popups or exit-intent overlays | Aggressive; alienates the exact audience that values focused, clean software | Never | + +--- + +## Feature Dependencies + +``` +App screenshots required → device mockup framing +Device mockup framing required → scroll-linked screenshot reveal +Scroll-linked screenshot reveal requires → CSS scroll-driven animations or IntersectionObserver + +Bento grid layout requires → design system (spacing, card surface, border radius) finalized +Glassmorphism card surfaces require → dark background behind them (works only in dark theme) +Ambient glow / gradient mesh requires → dark background (degrades badly on light) + +Dark theme decision → unlocks: glassmorphism cards, ambient glow, gradient mesh, high-contrast blue accents +Light theme decision → requires: different card treatment (subtle shadow, light gray surface) + +Downloader landing page redesign requires → home page design system finalized first +Social proof section requires → real press quotes or App Store review text sourced from real users + +Inline CTA repetition → no dependency; can be added at any point +Platform badge strip → no dependency; static HTML/SVG icons +Thin SVG divider lines → replaces TextSeparator component; zero dependency +``` + +--- + +## MVP Recommendation + +For the redesign milestone, prioritize in this order: + +**Must have (conversion-critical):** +1. App screenshots in device mockups — current absence is the highest-impact gap +2. Monochromatic + blue design system replacing all ASCII/monospace elements +3. Bento grid for features section — replaces flat TextFrame grid +4. Scroll entrance animations (staggered fade-in) — perceived quality signal +5. Inline CTA at top + bottom of page + +**Should have (differentiating for tech audience):** +6. Ambient glow / gradient mesh in hero (restrained — one orb maximum) +7. Thin SVG section dividers replacing TextSeparator +8. Platform badge strip (iOS / iPadOS / macOS) +9. Glassmorphism card surfaces (dark theme only) + +**Defer:** +- Scroll-linked screenshot reveal: High complexity, high reward — good for a follow-on phase after core design system is established +- Social proof pull-quote: Requires real content to be sourced before implementation +- Downloader landing page visual redesign: Should follow home page completion + +--- + +## Sources + +- [Linear App Style Landing Page — Figma (50+ sections)](https://www.figma.com/community/file/1367670334751609522/linear-app-style-landing-page-collection-50-sections-100-editable-free) — MEDIUM confidence +- [The Linear Look — Frontend Horse](https://frontend.horse/articles/the-linear-look/) — MEDIUM confidence (referenced by WebSearch results) +- [10 SaaS Landing Page Trends for 2026 — SaaSFrame Blog](https://www.saasframe.io/blog/blog/10-saas-landing-page-trends-for-2026-with-real-examples) — MEDIUM confidence +- [CSS Scroll-Driven Animations — Smashing Magazine](https://www.smashingmagazine.com/2024/12/introduction-css-scroll-driven-animations/) — HIGH confidence (official web platform documentation) +- [Avoid non-composited animations — Chrome Lighthouse](https://developer.chrome.com/docs/lighthouse/performance/non-composited-animations) — HIGH confidence (official Chrome docs) +- [App Store Marketing Guidelines — Apple Developer](https://developer.apple.com/app-store/marketing/guidelines/) — HIGH confidence +- [Social proof landing page examples — Wisernotify](https://wisernotify.com/blog/landing-page-social-proof/) — LOW confidence (single source, marketing content) +- [Hero Section Design Best Practices 2026 — Perfect Afternoon](https://www.perfectafternoon.com/2025/hero-section-design/) — LOW confidence (WebSearch only) +- [SaaS Landing Page UI Best Practices 2025 — Procreator](https://procreator.design/blog/saas-landing-page-best-ui-design-practices/) — LOW confidence (WebSearch only) +- Direct code inspection of existing `src/app/[locale]/page.tsx` — HIGH confidence (primary source) diff --git a/.planning/research/PITFALLS.md b/.planning/research/PITFALLS.md new file mode 100644 index 0000000..6e01d6b --- /dev/null +++ b/.planning/research/PITFALLS.md @@ -0,0 +1,346 @@ +# Domain Pitfalls + +**Domain:** Static Next.js marketing site redesign with scroll animations, dark theme, app screenshots, and multi-locale support +**Researched:** 2026-03-22 +**Overall confidence:** HIGH (verified against official Next.js docs, MDN, W3C, and multi-source corroboration) + +--- + +## Critical Pitfalls + +Mistakes that cause rewrites, broken deployments, or severe accessibility violations. + +--- + +### Pitfall 1: Image Optimization Is Silently Disabled on Static Export + +**What goes wrong:** The project already has `images: { unoptimized: true }` in `next.config.ts` because Cloudflare Pages cannot run the Next.js Image Optimization API. Developers adding app screenshots forget this constraint and expect `next/image` to handle WebP conversion, lazy loading, and responsive `srcset` automatically. It does not — images are served as-is at original size. A 3x retina iOS screenshot can easily be 2-5 MB. + +**Why it happens:** The `next/image` component's API looks identical whether optimization is enabled or not. No build warning fires. Images load fine in development. The problem only surfaces in Lighthouse audits or slow connections after deploy. + +**Consequences:** +- Hero section loads several megabytes of PNG before any content becomes interactive +- LCP (Largest Contentful Paint) degrades dramatically — the screenshot IS the hero +- Mobile users on 4G face multi-second blank screens +- Core Web Vitals fail, which affects SEO ranking + +**Prevention:** +- Use `next-image-export-optimizer` (npm package) as a post-build step — it generates optimized WebP/AVIF variants and correct `srcset` for static exports without a server +- Alternatively, pre-optimize screenshots manually: export at 2x max (not 3x), convert to WebP before import, and add `width`/`height` props explicitly to avoid layout shift +- Set `priority` prop on the above-the-fold hero screenshot +- Add a Lighthouse budget CI check to catch regressions + +**Detection:** +- Run `next build` and inspect the `out/` directory — images are raw copies of source files +- Lighthouse audit with throttled mobile profile: LCP > 4s is a red flag +- Chrome DevTools Network panel: any image request > 500 KB in the hero is a problem + +**Phase:** Address in the visual redesign phase, before any screenshot assets are committed. + +--- + +### Pitfall 2: Scroll Animations Triggering Layout Reflow + +**What goes wrong:** Scroll-triggered animations implemented with `scroll` event listeners (or animation libraries configured to read DOM geometry on scroll) force synchronous style recalculation on every scroll tick. This blocks the main thread and causes jank on mobile, especially with multiple animated sections. + +**Why it happens:** Animating properties like `top`, `left`, `height`, `margin`, or `padding` forces layout recalculation (reflow). Reading `getBoundingClientRect()` inside a scroll listener forces the browser to flush pending styles before responding — a pattern called layout thrashing. On a marketing page with 6-8 animated sections, this compounds. + +**Consequences:** +- Frame rate drops below 60fps on mid-range Android devices +- iOS Safari jank during fast scroll, causing users to miss animated content entirely +- Battery drain on mobile from continuous GPU/CPU churn + +**Prevention:** +- Use IntersectionObserver exclusively for triggering animations — it fires asynchronously off the main thread +- Animate only `transform` and `opacity` — these are composited by the GPU and do not trigger reflow +- With Framer Motion: use `whileInView` with `viewport={{ once: true }}` to fire once on entry, not on every re-enter +- With Tailwind: define keyframe animations using `translate-*` and `opacity-*`, never `top`/`left`/`height` +- Never call `getBoundingClientRect()` inside a scroll handler + +**Detection:** +- Chrome DevTools Performance panel: look for long "Recalculate Style" and "Layout" tasks on scroll +- "Rendering" tab with "Paint flashing" enabled: excessive green flashing indicates over-painting +- FPS meter dropping below 55fps during scroll = problem + +**Phase:** Architecture decision in animation phase — establish the pattern before building individual sections. + +--- + +### Pitfall 3: Ignoring `prefers-reduced-motion` Accessibility Requirement + +**What goes wrong:** Scroll animations, parallax effects, glowing elements, and smooth transitions are implemented without respecting the OS-level "Reduce Motion" setting. This causes nausea, dizziness, or cognitive overwhelm for users with vestibular disorders, ADHD, or motion sensitivities. + +**Why it happens:** Developers test on their own machine where reduced motion is not enabled. Framer Motion and GSAP animate by default — they require explicit opt-out. Marketing sites prioritize visual impact over accessibility, and the failure is invisible during review. + +**Consequences:** +- WCAG 2.1 Success Criterion 2.3.3 violation (Animation from Interactions) +- Legal exposure in jurisdictions with accessibility laws +- Users with disabilities cannot use the site +- Parallax and auto-playing decorative animations are the highest-risk elements + +**Prevention:** +- Add a global CSS rule as a baseline safety net: + ```css + @media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + } + ``` +- With Framer Motion: use the `useReducedMotion()` hook to conditionally disable animations +- Separate "motion is informational" from "motion is decorative" — only the former needs a static alternative; the latter can simply be hidden +- Any animation that auto-plays for more than 5 seconds must have a pause mechanism even for users without reduced motion enabled (WCAG 2.2.2) + +**Detection:** +- Enable "Reduce Motion" in macOS/iOS system settings and verify every animated section +- Use axe DevTools browser extension to flag animation violations +- Manually check: does content still fully display without any animation? + +**Phase:** Establish the reduced-motion pattern in the first animation implementation. It cannot be retrofitted across a finished site without touching every component. + +--- + +### Pitfall 4: Hydration Mismatches from Browser-Only Animation State + +**What goes wrong:** Animation libraries that read `window`, `document`, or browser APIs (viewport size, scroll position, theme from `localStorage`) during render cause server/client HTML mismatches with Next.js SSG. This triggers React hydration errors in production, which silently fall back to client-side rendering and break SSG performance benefits. + +**Why it happens:** Next.js pre-renders pages to static HTML at build time. If an animation component checks `window.innerWidth` to decide its initial state, the server renders with `undefined` (different state) and the client renders the real width — React detects the mismatch and throws. + +**Specific risk for this project:** Dark theme implementation using `next-themes` or custom theme detection reads from `localStorage` on the client. Any component whose initial render differs between server and client (based on theme) will hydrate incorrectly. This is especially dangerous for screenshot display — if dark/light mode screenshots are shown conditionally, the SSG HTML will not match. + +**Consequences:** +- Hydration errors logged in production +- Entire page falls back to client-side rendering, losing SSG performance +- Flash of wrong content (FOWC) on initial paint — screenshot shows wrong variant +- In Next.js 15 + React 19, hydration errors can be stricter and harder to suppress + +**Prevention:** +- Wrap any browser-only animation initializations in `useEffect` — they will only run on the client after hydration +- For dark/light screenshot variants: use CSS media queries (`prefers-color-scheme`) rather than JavaScript state to switch images — no JS needed, no hydration risk +- For theme state: use `suppressHydrationWarning` only on the `` element (the officially sanctioned location per next-themes docs), not throughout the component tree +- Avoid `if (typeof window !== 'undefined')` inside render — use `useEffect` instead + +**Detection:** +- Open browser console in production build: any "Hydration failed" error is this pitfall +- Test with JavaScript disabled: the page should render correctly with static content visible +- Run `next build && next start` locally and inspect hydration errors before deploying + +**Phase:** High priority in any phase that introduces dark theme or animation state — must be validated before shipping. + +--- + +## Moderate Pitfalls + +--- + +### Pitfall 5: Breaking i18n Routes During Component Restructuring + +**What goes wrong:** During a visual redesign, components are extracted, renamed, or merged. The locale-aware routing structure (`/[locale]/page.tsx`, `generateStaticParams()`) breaks silently when a developer refactors without understanding that every new page or layout must explicitly participate in static locale generation. + +**Why it happens:** The project uses a manual i18n approach (compatible with `output: 'export'` — Next.js App Router's built-in i18n routing uses middleware, which does not work with static export). This means `generateStaticParams()` must be present and correct in every dynamic route file. Forgetting it during a refactor causes the locale to be excluded from the static build. + +**Specific risk:** The existing `getDownloaderLandingContent()` function in `downloader-landings.ts` is 557 lines of hardcoded content across 4 locales. During redesign, any changes to how this data is consumed (restructuring the DownloaderLandingPage component) risks silently dropping one locale's content — no build error fires, the page just renders empty for that locale. + +**Consequences:** +- Japanese or Chinese locale pages return 404 after deploy +- Downloader-specific landing pages missing for some locale combinations +- SEO impact: indexed pages return 404, losing ranking +- Not detected in development because dev server handles routes dynamically + +**Prevention:** +- Add a build-time validation script that asserts all `locale × route` combinations produce output files in `out/` +- Run `next build` and verify the `out/` directory contains all expected locale paths before every deploy +- Do not restructure the i18n data layer and the visual layer in the same PR — separate concerns +- The `CONCERNS.md` already recommends adding Zod validation for downloader landing content — implement this before the redesign to catch missing content early + +**Detection:** +- After `next build`, run: `ls out/ja/ out/zh-hans/ out/zh-hant/` and verify all expected pages exist +- Review `generateStaticParams()` in any modified route file after refactoring + +**Phase:** Must be addressed before any component restructuring in the redesign phase. + +--- + +### Pitfall 6: Over-Promoting GPU Layers with `will-change` + +**What goes wrong:** Developers add `will-change: transform` (Tailwind: `will-change-transform`) to every animated element anticipating better performance. Instead, this forces the browser to allocate a separate GPU compositing layer for each element, consuming memory and paradoxically causing performance degradation on low-memory mobile devices. + +**Why it happens:** The Tailwind docs include `will-change` as a utility, making it easy to apply. Performance improvement is visible on developer's high-end machine. On a 2GB RAM phone, the extra layers cause frame drops. + +**Consequences:** +- Memory pressure on mobile causing scroll jank +- Can cause blurry text on non-retina screens when elements are promoted to their own layer +- No runtime error — requires profiling to discover + +**Prevention:** +- Apply `will-change` only immediately before an animation starts and remove it afterward (use JS, not static CSS classes) +- Prefer `transform-gpu` (Tailwind) only for elements that are frequently animated and on screen simultaneously +- For scroll-reveal animations that fire once: no `will-change` needed — IntersectionObserver + CSS class toggle is sufficient +- Use Chrome DevTools Layers panel to verify layer count stays reasonable (< 20 layers for a marketing page) + +**Detection:** +- Chrome DevTools > Rendering > Layer borders: excessive yellow borders indicate over-promotion +- Memory panel: high compositor memory usage on a simulated low-end mobile device + +**Phase:** Animation implementation phase — establish the rule before building individual components. + +--- + +### Pitfall 7: CLS from Animated Elements Without Reserved Space + +**What goes wrong:** Scroll-reveal animations that start elements at `opacity: 0` with a vertical translate (e.g., `translateY(20px)`) cause layout shifts if the element's space is not reserved in the DOM before animation begins. This is particularly acute for app screenshot images — if the image loads after the surrounding text, the text shifts down. + +**Why it happens:** The initial state of the animation (`opacity: 0; transform: translateY(20px)`) visually hides the element, but if the element is also removed from layout flow (using `display: none` or conditional rendering), layout shifts occur when it enters. Additionally, images without explicit `width`/`height` attributes cause CLS on load. + +**Consequences:** +- Poor Core Web Vitals CLS score (target is < 0.1) +- Text jumps as images load, creating a poor first impression +- Google penalizes high CLS in page ranking + +**Prevention:** +- Always animate with `opacity` and `transform` only — these do not affect layout flow +- Never use `visibility: hidden` → `visible` or `display: none` → `block` as animation states +- All `` components (or `` tags) must have explicit `width` and `height` attributes, or use `fill` with a sized container +- For hero screenshots: set `priority` prop and explicit dimensions determined at build time + +**Detection:** +- Chrome DevTools Performance panel: look for "Layout Shift" events in the timeline +- WebPageTest CLS filmstrip view reveals which element is causing the shift +- Lighthouse CLS score > 0.1 is a flag + +**Phase:** Visual redesign phase — establish image sizing conventions before building the hero section. + +--- + +### Pitfall 8: Dark Theme with Pure Black Backgrounds Causing Halation + +**What goes wrong:** The team chooses `#000000` as the background color for the dark theme because it looks premium in mockups and performs well on OLED. In practice, pure black backgrounds create a "halation" effect — a glowing halo around light text — that affects users with astigmatism (approximately 1 in 3 people). The high contrast also causes eye strain during extended reading. + +**Why it happens:** Mockup tools render on calibrated designer monitors. Marketing sites are reviewed in short sessions. The problem is only noticed by users spending more than a few seconds reading content. + +**Consequences:** +- Reduced readability for a significant portion of the target audience +- Tech-savvy users (the target audience) are more likely to use OLED phones where this is most visible +- The "premium" aesthetic goal backfires — the site feels harsh rather than polished + +**Prevention:** +- Use `#0A0A0A` or `#111111` as the base dark background, not `#000000` +- Use `#E8E8E8` or `#F0F0F0` for body text, not `#FFFFFF` +- Reserve pure `#000000` only for the absolute outermost shell if the OLED battery argument is being made +- Check color contrast ratios: minimum 4.5:1 for body text, 3:1 for large headings (WCAG AA) +- App screenshots (which have their own dark UI) will visually blend better against a dark gray than pure black + +**Detection:** +- Test on a physical OLED iPhone in a dim room — halation is immediately visible +- Use contrast ratio checker tools (WebAIM Contrast Checker) on every text/background combination +- Apply the Tailwind CSS color scale: `gray-950` (`#030712`) or `gray-900` (`#111827`) are safe starting points + +**Phase:** Design token definition — must be decided before any component is built, not retrofitted. + +--- + +### Pitfall 9: Text Expansion Breaking Japanese and Chinese Layouts + +**What goes wrong:** UI components are laid out with English text length in mind. Japanese and Chinese text is often denser (fewer characters for the same meaning) but English UI strings frequently have no Chinese equivalent available — so translators add descriptive context, making strings longer. Card layouts with fixed heights or truncated text clip translated content. + +**Why it happens:** Designers and developers test with English copy. The i18n message files exist and work, but layout testing with ja/zh-hans/zh-hant content is skipped during development. + +**Specific risk for this project:** The current site has 4 locales hardcoded in a 557-line data file. The redesign will introduce card-based layouts, which are particularly vulnerable to text overflow. Japanese typography also requires different line-height and character spacing (`letter-spacing: 0` or negative values are standard for Japanese; positive values appropriate for English look wrong in CJK). + +**Consequences:** +- Card components clip or overflow with CJK content +- Navigation items wrap onto two lines in Japanese +- The redesigned site looks broken in 3 of 4 supported locales + +**Prevention:** +- Test every new layout component with all 4 locales during development, not at the end +- Use `min-height` rather than `height` for cards; allow text containers to grow +- Avoid hardcoded character counts for truncation (`line-clamp`) without checking all locales +- For CJK typography: set `word-break: break-all` and `overflow-wrap: break-word` as defaults +- Add `lang` attribute handling to set locale-appropriate font metrics (already likely present in the i18n setup — verify) + +**Detection:** +- Switch locale in the development server and visually inspect every redesigned component +- Specifically check: navigation bar, feature cards, CTA buttons, hero headline, FAQ items + +**Phase:** Component development phase — establish testing discipline before building card layouts. + +--- + +## Minor Pitfalls + +--- + +### Pitfall 10: `useParams()` Unreliability in Static Export + +**What goes wrong:** In App Router with `output: 'export'`, there is a known issue where `useParams()` on the client does not reliably return locale values in all Next.js versions. The project's existing i18n structure may use `params.locale` passed as a prop instead — but any new components that try to use `useParams()` for locale detection will fail silently in static export. + +**Prevention:** +- Pass `locale` as an explicit prop from the page level, do not rely on `useParams()` in deeply nested client components +- Verify the existing pattern in `src/app/[locale]/page.tsx` and replicate it for any new route segments + +**Phase:** Any phase that adds new localized client components. + +--- + +### Pitfall 11: Framer Motion Bundle Size in Static Export + +**What goes wrong:** Adding Framer Motion to a marketing site adds approximately 40-50 KB gzipped to the JavaScript bundle. For a static marketing site where many visitors only see the above-the-fold content, this is a non-trivial cost. Over-using `motion.*` wrappers on every element amplifies the impact. + +**Prevention:** +- Use Framer Motion only for complex choreographed animations that cannot be achieved with CSS +- For simple fade-in / slide-up reveal animations: use CSS `@keyframes` with IntersectionObserver class toggling instead — zero bundle cost +- Use dynamic import with `ssr: false` for heavy animation components so they don't block the initial render +- Audit with `next build` output: watch the route bundle size; flag if the main page bundle exceeds 150 KB gzipped + +**Phase:** Animation implementation phase. + +--- + +### Pitfall 12: Glowing/Blur Effects Degrading on Low-End Hardware + +**What goes wrong:** CSS `backdrop-filter: blur()`, `filter: blur()`, and large box-shadow glow effects are GPU-intensive. On older iPhones (iPhone 8, iPhone X era — still common among the app's target demographic of NAS owners who buy hardware infrequently) and low-end Android devices, these effects cause visible frame drops. + +**Prevention:** +- Test blur effects on a physical device with throttled GPU (Chrome DevTools > Rendering > GPU throttle) or on an older phone +- Apply blurs sparingly: one prominent element (hero card, nav bar) is fine; blurring 10 cards per page is not +- Wrap expensive visual effects in a `@media (prefers-reduced-transparency)` check where applicable (Safari 17+) +- Provide a fallback: `@supports not (backdrop-filter: blur())` should degrade gracefully to a solid semi-transparent background + +**Phase:** Visual effects implementation phase — test on constrained hardware before finalizing design. + +--- + +## Phase-Specific Warnings + +| Phase Topic | Likely Pitfall | Mitigation | +|-------------|---------------|------------| +| Design token definition (colors, typography) | Pure black dark theme causing halation; CJK typography ignored | Decide background color (`#111` not `#000`); set CJK line-height defaults | +| Screenshot asset preparation | Raw PNG images too large for static export; missing `width`/`height` causing CLS | Pre-optimize to WebP; set explicit dimensions; use `next-image-export-optimizer` | +| Hero and above-the-fold section | LCP from unoptimized screenshots; hydration mismatch from theme detection | `priority` on hero image; use CSS media query for dark/light screenshot swap | +| Scroll animation architecture | Layout reflow from wrong CSS properties; missing reduced-motion support | Establish IntersectionObserver + transform/opacity pattern first | +| Card layout for features | Text overflow in ja/zh locales; CLS from fixed heights | Test all 4 locales; use `min-height` | +| i18n content migration | Silent locale route dropout during component refactor | Validate `out/` directory for all locale paths post-build | +| Visual effects (blur, glow) | GPU memory pressure on low-end devices | Test on throttled GPU; limit simultaneous blur surfaces | +| Dark theme rollout | Hydration mismatch from `localStorage`-based theme detection | Use CSS `prefers-color-scheme` for initial render; client theme switcher after hydration | + +--- + +## Sources + +- Next.js Static Export documentation: https://nextjs.org/docs/pages/guides/static-exports +- Next.js Image Optimization with static export: https://github.com/vercel/next.js/discussions/60977 +- `next-image-export-optimizer` package: https://github.com/Niels-IO/next-image-export-optimizer +- Next.js App Router `useParams()` static export issue: https://github.com/vercel/next.js/issues/54393 +- WCAG 2.1 SC 2.3.3 Animation from Interactions: https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html +- MDN `prefers-reduced-motion`: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-reduced-motion +- Cumulative Layout Shift optimization (web.dev): https://web.dev/articles/optimize-cls +- Tailwind CSS `will-change` documentation: https://tailwindcss.com/docs/will-change +- GSAP + Next.js 15 animation cleanup: https://medium.com/@thomasaugot/optimizing-gsap-animations-in-next-js-15-best-practices-for-initialization-and-cleanup-2ebaba7d0232 +- Dark Mode pitfalls (halation, contrast): https://webwave.me/blog/dark-mode-design-trends +- Cloudflare Pages + Next.js image limitations: https://www.thetombomb.com/posts/nextjs-pages-cloudflare-pages +- Next.js i18n static export incompatibility: https://github.com/vercel/next.js/discussions/18957 +- Intersection Observer vs scroll events performance: https://codebyumar.medium.com/most-developers-still-use-scroll-events-instead-of-intersection-observer-908273dd9c99 +- Next.js hydration error documentation: https://nextjs.org/docs/messages/react-hydration-error diff --git a/.planning/research/STACK.md b/.planning/research/STACK.md new file mode 100644 index 0000000..523341a --- /dev/null +++ b/.planning/research/STACK.md @@ -0,0 +1,209 @@ +# Technology Stack + +**Project:** BitRemote Website Redesign +**Researched:** 2026-03-22 +**Confidence:** MEDIUM-HIGH (core libs verified via npm/official sources; version numbers from WebSearch confirmed against npm registry) + +--- + +## Existing Stack (Do Not Change) + +| Technology | Version | Role | +|------------|---------|------| +| Next.js | 15.1.0 | Framework — static export via `output: 'export'` | +| React | 19.0.0 | UI rendering | +| TypeScript | 5.7.3 | Type safety | +| Tailwind CSS | 3.4.0 | Utility-first styling | +| Cloudflare Pages | — | Static hosting | + +**Hard constraint:** All additions must be compatible with `output: 'export'`. No SSR-only APIs (no `next/image` optimization, no server components calling animations, no stream-based rendering). + +--- + +## Recommended Additions + +### Animation Core + +| Library | Version | Purpose | Why | +|---------|---------|---------|-----| +| `motion` | ^12.x (latest 12.38.0) | Scroll-triggered entrance animations, page transitions, micro-interactions | Successor to `framer-motion`. React-first, declarative API, `whileInView` replaces boilerplate IntersectionObserver hooks. Works purely client-side — fully compatible with static export. 30M+ monthly npm downloads; fastest-growing animation library in the ecosystem. | + +**Import path:** `import { motion, AnimatePresence } from "motion/react"` (not the old `framer-motion`). + +**Bundle optimization — mandatory:** Wrap the app in `LazyMotion` with `domAnimation` features. This cuts the animation payload from ~30KB gzipped to ~15KB. Without this, the full bundle unnecessarily loads gesture and layout animation code that a marketing site does not need. + +```tsx +// layout.tsx or _app.tsx +import { LazyMotion, domAnimation } from "motion/react" + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +Use `m.div` instead of `motion.div` inside `LazyMotion` to benefit from tree-shaking. + +--- + +### Smooth Scrolling + +| Library | Version | Purpose | Why | +|---------|---------|---------|-----| +| `lenis` | ^1.3.x (latest 1.3.19) | Smooth, inertia-based page scroll | The browser's native scroll is abrupt on marketing-grade pages. Lenis replaces it with a physics-based scroll that makes parallax and scroll-linked animations feel polished. Pairs naturally with Motion's `useScroll`. Works client-side only — compatible with static export via `"use client"`. Previously `@studio-freight/lenis`; now published as `lenis`. | + +**Do not use** `react-scroll-parallax` or `simpleParallax.js` separately — Motion + Lenis covers the same ground with less bundle overhead and better React integration. + +--- + +### CSS Animation Utilities + +| Library | Version | Purpose | Why | +|---------|---------|---------|-----| +| `tailwindcss-animate` | ^1.0.x | Tailwind utility classes for CSS-only animations (`animate-in`, `fade-in`, `slide-in-from-*`, `zoom-in`) | Handles simple, stateless animations (modal entrance, tooltip fade) without JavaScript. Used by shadcn/ui — widely battle-tested. Cheaper than JS-powered Motion for animations that don't need scroll-linking or sequencing. | + +**Note:** `tw-animate-css` is the Tailwind v4 CSS-first alternative. This project uses Tailwind 3.4, so stick with `tailwindcss-animate` for now. + +--- + +### Optional: GSAP (ScrollTrigger) + +| Library | Version | Purpose | Why (and when to add) | +|---------|---------|---------|----------------------| +| `gsap` | ^3.x | Complex sequenced scroll narratives, pinned sections, text split animations | Now fully free including ScrollTrigger and SplitText. More powerful than Motion for Hollywood-style scroll storytelling. **Only add if** a specific section needs sequential multi-element choreography that Motion's declarative API cannot express cleanly. GSAP is framework-agnostic JavaScript — static export compatible. | + +**Default recommendation:** Start without GSAP. Motion handles 90% of marketing site animation needs. Add GSAP only when a specific design effect demands it. + +--- + +## Visual Effects — CSS-Only (No Library Required) + +These effects are achievable with Tailwind utility classes and custom CSS. No additional library needed. + +### Glow Effects + +```css +/* Blue glow on interactive elements — matches app's blue accent */ +.glow-blue { + box-shadow: 0 0 20px 4px rgba(59, 130, 246, 0.35); +} + +/* Text glow for hero headlines */ +.text-glow { + text-shadow: 0 0 40px rgba(59, 130, 246, 0.4); +} +``` + +Tailwind arbitrary values: `shadow-[0_0_20px_rgba(59,130,246,0.35)]` + +### Glassmorphism / Frosted Cards + +Tailwind already has all required utilities: +- `backdrop-blur-md` — frosted glass effect +- `bg-white/10` or `bg-black/20` — semi-transparent fill +- `border border-white/10` — hairline border for glass edge + +**Performance warning:** `backdrop-filter` is GPU-accelerated but can cause repaints on browsers with many overlapping blur layers. Limit to 2-3 glass elements visible simultaneously. + +### Gradients + +```tsx +// Radial glow background — aurora/mesh gradient pattern +className="bg-[radial-gradient(ellipse_80%_50%_at_50%_-20%,rgba(59,130,246,0.15),transparent)]" +``` + +These are pure CSS — zero JS cost. + +--- + +## What NOT to Use + +| Library | Why Not | +|---------|---------| +| `react-spring` | Older physics-based library. Motion supersedes it with better React 19 compatibility and a cleaner API. | +| `anime.js` | Vanilla JS, not React-native. Requires imperative refs — fights against React's rendering model. Motion is a better fit. | +| `aos` (Animate on Scroll) | jQuery-era library. Adds a global class toggle pattern that clashes with React's component model. Replaced entirely by Motion's `whileInView`. | +| `react-scroll-parallax` | Adds ~22KB for functionality that `motion`'s `useScroll` + `useTransform` covers natively. | +| `three.js` / `@react-three/fiber` | 3D canvas — complete overkill for a marketing site. Huge bundle (~600KB+), significant performance cost on mobile. | +| GSAP + ScrollSmoother | Duplicates Lenis. Pick one scroll smoothing solution. | +| `next/image` with `unoptimized: false` | This project uses static export which requires `unoptimized: true`. Image optimization is not available. Use `` or standard `next/image` with `unoptimized` flag. | +| CSS `position: fixed` parallax via scroll event listeners | Scroll events fire at best-effort intervals, causing jank. Always use CSS `transform` + Lenis + Motion's `useScroll` instead. | + +--- + +## Installation + +```bash +# Animation core +npm install motion + +# Smooth scrolling +npm install lenis + +# CSS animation utilities +npm install -D tailwindcss-animate +``` + +**tailwind.config.ts addition:** +```ts +import animatePlugin from "tailwindcss-animate" + +export default { + plugins: [animatePlugin], +} +``` + +--- + +## Architecture Notes for Static Export + +1. **All animation code must be in `"use client"` components.** Motion, Lenis, and scroll hooks use browser APIs (`window`, `IntersectionObserver`, `requestAnimationFrame`) that do not exist during static HTML generation. Server components cannot import these libraries. + +2. **Use `dynamic()` imports for heavy animated sections.** If a section contains a large animated component (e.g., a hero with complex scroll choreography), wrap it in `next/dynamic` with `{ ssr: false }` to prevent build-time errors and reduce initial JS parse cost. + +3. **Respect `prefers-reduced-motion`.** Both Motion and Lenis have built-in support. In Motion, `useReducedMotion()` returns a boolean; use it to short-circuit animation values. This is also an accessibility requirement. + +4. **Animate `transform` and `opacity` only.** Animating `width`, `height`, `top`, `left`, or `margin` triggers layout recalculation on every frame — guaranteed jank at 60fps. Motion's spring animations default to transform-based, but verify custom keyframes follow this rule. + +--- + +## Alternatives Considered + +| Category | Recommended | Alternative | Why Not Chosen | +|----------|-------------|-------------|---------------| +| Animation | `motion` | GSAP | GSAP is more powerful but adds complexity for a React-first project. Motion's declarative API is faster to develop with and bundles smaller. | +| Animation | `motion` | `react-spring` | `react-spring` is physics-first and excellent, but Motion has broader feature coverage (layout animations, scroll linking, gestures) and better Next.js community support. | +| Smooth scroll | `lenis` | native scroll | Native scroll is abrupt. Lenis is the community standard for polished scroll experiences; pairs best with Motion. | +| CSS animations | `tailwindcss-animate` | custom `@keyframes` only | Plugin provides the standard animate-in/animate-out pattern used by shadcn/ui, reducing bespoke CSS. Custom keyframes still used for one-off effects. | + +--- + +## Confidence Assessment + +| Decision | Confidence | Basis | +|----------|------------|-------| +| `motion` as primary animation library | HIGH | npm: 30M+/month downloads; official docs confirm static export compatibility; actively maintained (v12.38.0 as of research date) | +| `lenis` for smooth scroll | MEDIUM | npm package confirmed active (v1.3.19); integration guides for Next.js 15 confirmed; static export compatibility inferred (client-side only) — not verified against official Lenis docs directly | +| `tailwindcss-animate` | HIGH | Widely adopted via shadcn/ui; Tailwind 3.4 compatible; no runtime cost | +| GSAP optional | MEDIUM | Free licensing confirmed; ScrollTrigger confirmed included; static export compatibility confirmed (vanilla JS); "optional" recommendation is judgment call | +| CSS-only glow/glass effects | HIGH | Standard CSS; Tailwind 3.4 utility classes confirmed; no library dependency | +| Avoid heavy 3D libraries | HIGH | Performance rationale is well-established; no counter-evidence found | + +--- + +## Sources + +- [Motion npm package](https://www.npmjs.com/package/motion) — version 12.38.0, 30M+ weekly downloads +- [Motion for React — Getting Started](https://motion.dev/docs/react) +- [LazyMotion — Reduce Bundle Size](https://motion.dev/docs/react-lazy-motion) +- [GSAP vs Motion comparison](https://motion.dev/docs/gsap-vs-motion) +- [GSAP Pricing — Now Free](https://gsap.com/pricing/) +- [lenis npm package](https://www.npmjs.com/package/lenis) — version 1.3.19 +- [react-intersection-observer npm](https://www.npmjs.com/package/react-intersection-observer) — v10.0.3 +- [Next.js Static Exports Guide](https://nextjs.org/docs/app/guides/static-exports) +- [Performant Parallaxing — Chrome Developers](https://developer.chrome.com/blog/performant-parallaxing) +- [Glassmorphism with Tailwind CSS — Epic Web Dev](https://www.epicweb.dev/tips/creating-glassmorphism-effects-with-tailwind-css) +- [tw-animate-css vs tailwindcss-animate](https://github.com/Wombosvideo/tw-animate-css) diff --git a/.planning/research/SUMMARY.md b/.planning/research/SUMMARY.md new file mode 100644 index 0000000..d6a8fbf --- /dev/null +++ b/.planning/research/SUMMARY.md @@ -0,0 +1,192 @@ +# Project Research Summary + +**Project:** BitRemote Website Redesign +**Domain:** Native Apple app marketing website (static Next.js, multi-locale) +**Researched:** 2026-03-22 +**Confidence:** MEDIUM-HIGH + +## Executive Summary + +BitRemote's website redesign is a visual overhaul of an existing Next.js 15 static-export site. The existing routing, i18n, SEO, and domain layers are correct and working — they must not be touched. The entire work is confined to replacing the component layer: the current ASCII/monospace aesthetic and text-based panels are replaced with a modern design system (dark theme, glassmorphism cards, bento grid, device mockups, scroll entrance animations) that matches the visual language of exemplary native Apple app marketing sites such as Linear, Things, and Bear. + +The recommended approach is to add three new libraries (`motion` for scroll animations, `lenis` for smooth scroll, `tailwindcss-animate` for CSS-only micro-interactions) on top of the existing Tailwind 3.4 stack, then build in strict tier order: design tokens first, motion and UI primitives second, section components third, page assembly last. The single most critical gap in the current site is the absence of app screenshots — this is the highest-priority visual change and the one most likely to improve conversion. A dark theme with blue accent is the correct direction, unlocking glassmorphism, glow effects, and high-contrast CTAs. + +The primary risks are performance-related and must be addressed before assets are committed: app screenshots on a static export site are served unoptimized, making a 2–5 MB PNG hero image the most likely cause of LCP failure. Alongside this, all scroll animations must use only `transform` and `opacity` properties (compositor-thread only), must respect `prefers-reduced-motion`, and must not cause hydration mismatches. The i18n layer covering 4 locales is fragile — no component restructuring should touch the data flow or route generation. + +## Key Findings + +### Recommended Stack + +The existing stack (Next.js 15.1, React 19, TypeScript 5.7, Tailwind 3.4, Cloudflare Pages) is locked and appropriate. The hard constraint is `output: 'export'` — every addition must be fully client-side with no SSR-only APIs. Three libraries are recommended additions: + +- `motion` (v12.x, formerly `framer-motion`): scroll-triggered entrance animations, `whileInView` with `once: true`, `useScroll`/`useTransform` for parallax. Import as `motion/react`. Wrap app in `LazyMotion + domAnimation` to halve bundle size (~30KB → ~15KB gzipped). Use `m.div` instead of `motion.div` inside `LazyMotion`. +- `lenis` (v1.3.x): physics-based smooth scroll, pairs with Motion's `useScroll`, replaces abrupt native scroll. Client-side only. +- `tailwindcss-animate` (v1.0.x): CSS-only animation utilities for simple stateless transitions (modal fade, tooltip). Zero runtime cost. + +GSAP is available and now fully free (ScrollTrigger included) but should be deferred unless a specific section demands sequential multi-element choreography that Motion cannot express. CSS-only glow, glassmorphism, and gradient effects require no library — Tailwind's `backdrop-blur-md`, `bg-white/10`, `shadow-[...]` arbitrary values, and custom CSS radial gradients cover all needed effects. + +**Core technologies:** +- `motion` (motion/react): scroll animations and entrance effects — React-first, static-export compatible, LazyMotion reduces bundle cost +- `lenis`: smooth inertia scroll — community standard, pairs naturally with Motion's scroll hooks +- `tailwindcss-animate`: CSS-only animation utilities — zero runtime cost, used by shadcn/ui +- CSS variables + Tailwind semantic aliases: design token system — single override block per theme, no `dark:` class duplication +- `next/image` with `unoptimized: true`: images served as-is — requires pre-optimization of screenshot assets before commit + +### Expected Features + +The current site has correct structural elements (hero, features, FAQ, footer, CTA, social links, legal) but is missing the visual execution that tech-savvy audiences use to judge app quality. The single largest gap is the complete absence of app screenshots. + +**Must have (conversion-critical):** +- App screenshots in device mockups (iPhone/iPad/Mac frames) — current absence is the highest-impact gap vs. reference sites +- Monochromatic + blue design system replacing all ASCII/monospace elements — current monospace font throughout signals an unfinished product +- Bento grid for features section — replaces flat TextFrame grid with visually rich card compositions +- Scroll entrance animations (staggered fade-in on `opacity` + `translateY`) — perceived quality signal for tech audiences +- Inline CTA repetition (top + bottom of page minimum) — current single CTA at top loses conversions on scroll + +**Should have (differentiating for tech audience):** +- Ambient glow / radial gradient mesh in hero (one orb, restrained) +- Thin `1px` SVG section dividers replacing ASCII TextSeparator +- Platform badge strip (iOS / iPadOS / macOS icons in a single row) +- Glassmorphism card surfaces (dark theme only — `backdrop-blur-md` + `bg-white/10` + `border-white/10`) + +**Defer to follow-on phase:** +- Scroll-linked screenshot reveal (high complexity, high reward — requires device mockups and design system to be established first) +- Social proof pull-quote section (requires real press quotes or curated App Store review text to be sourced) +- Downloader landing page visual redesign (must follow home page completion — design system must be finalized first) + +### Architecture Approach + +The architecture layers a new design system on top of the unchanged routing structure. Pages (`src/app/`) are edited only to swap in new section components — routing, locale resolution, `generateStaticParams`, `generateMetadata`, and the SEO/i18n/domain layers are untouched. The entire `src/ascii-panel/` directory and existing `src/components/` files are replaced. Component boundaries are strictly enforced: pages import sections only; sections compose ui/ and motion/ primitives; motion/ components have no business logic; ui/ primitives have no animation logic. + +**Major components (build order):** +1. Design tokens — CSS variables in `globals.css`, Tailwind semantic aliases in `tailwind.config.ts`; dark mode as `[data-theme="dark"]` override block +2. Motion primitives (`src/components/motion/`) — `FadeIn`, `SlideIn`, `StaggerGroup`/`StaggerItem`, `ParallaxLayer`; all `"use client"`, all animate `opacity` + `transform` only +3. UI primitives (`src/components/ui/`) — `Button` (CVA variants), `Card`, `Badge`, `AppMockup`, `Wordmark`, `FaqAccordion`; stateless, token-driven +4. Section components (`src/components/sections/`) — `NavBar`, `HeroSection`, `FeaturesSection`, `ScreenshotsSection`, `FaqSection`, `CtaSection`, `Footer`; composed from primitives +5. Page assembly — swap section imports in `src/app/[locale]/page.tsx`, `layout.tsx`, and downloader pages; structure unchanged + +### Critical Pitfalls + +1. **Unoptimized screenshot images (CRITICAL)** — `output: 'export'` disables Next.js image optimization silently. A 3x retina iOS screenshot can be 2–5 MB, causing LCP failure and Core Web Vitals penalties. Use `next-image-export-optimizer` or pre-convert all screenshots to WebP at 2x max before committing. Set `priority` on the hero image. Add explicit `width`/`height` to all images to prevent CLS. + +2. **Scroll animation layout reflow (CRITICAL)** — Animating CSS properties other than `opacity` and `transform` triggers layout recalculation on every scroll frame, causing jank on mobile. Never animate `height`, `width`, `top`, `left`, `margin`, or `padding`. Never call `getBoundingClientRect()` inside scroll handlers. Establish this constraint before building any animated component — it cannot be audited retroactively. + +3. **Missing `prefers-reduced-motion` support (CRITICAL)** — Scroll animations without reduced-motion fallbacks violate WCAG 2.1 SC 2.3.3. Add a global CSS `@media (prefers-reduced-motion: reduce)` baseline and use Framer Motion's `useReducedMotion()` hook in `ParallaxLayer`. Establish this in the first animation component built. + +4. **Hydration mismatches from browser-only animation state (CRITICAL)** — Any animation component that reads `window`, `localStorage`, or theme state during render will cause server/client HTML mismatches, silently degrading the page to client-side rendering and causing FOWC (flash of wrong content). Wrap all browser API reads in `useEffect`. Use CSS `prefers-color-scheme` media queries for initial dark/light screenshot variants, not JavaScript state. + +5. **i18n route dropout during component restructuring (MODERATE)** — The existing 4-locale setup uses manual `generateStaticParams()`. Refactoring components without understanding this silently drops locale routes from the static build with no error. Never restructure the i18n data layer and the visual layer in the same PR. After every build, verify `out/ja/`, `out/zh-hans/`, and `out/zh-hant/` contain all expected pages. + +## Implications for Roadmap + +Based on the architecture's strict tier dependency order and the pitfall sequencing requirements, the suggested phase structure is: + +### Phase 1: Design Foundation + +**Rationale:** Every other tier depends on design tokens. Colors, typography, radius, and shadow must be locked before any component is built — retrofitting tokens into finished components is extremely expensive. The dark theme decision and the background color choice (`#0A0A0A`, not `#000000`) must be made here. CJK typography defaults must be set globally before any layout component is built. +**Delivers:** `globals.css` with CSS variable token system, `tailwind.config.ts` semantic aliases, typography scale, global resets, `[data-theme="dark"]` override block +**Addresses:** Design system replacing ASCII/monospace aesthetic; dark background color (Pitfall 8) +**Avoids:** Pitfall 8 (halation from pure black), Pitfall 9 (CJK text overflow from fixed layouts) + +### Phase 2: Animation and UI Primitives + +**Rationale:** Motion primitives and UI primitives are independent of each other but both depend on Tier 1 tokens. They must be complete before section components can be built. This is the phase where the `transform`/`opacity`-only animation rule and the `prefers-reduced-motion` pattern must be established — these cannot be retrofitted. +**Delivers:** `FadeIn`, `SlideIn`, `StaggerGroup`, `ParallaxLayer` motion components; `Button`, `Card`, `Badge`, `AppMockup`, `Wordmark`, `FaqAccordion` UI components +**Uses:** `motion` (LazyMotion + domAnimation), `tailwindcss-animate`, design tokens from Phase 1 +**Implements:** Architecture Tiers 2 and 3 +**Avoids:** Pitfall 2 (layout reflow), Pitfall 3 (missing reduced-motion), Pitfall 4 (hydration mismatches), Pitfall 6 (over-promoting GPU layers) + +### Phase 3: Screenshot Asset Preparation + +**Rationale:** App screenshots are the highest-impact missing element and must be prepared before the hero section is built. Pre-optimization must happen before assets are committed to the repository — not after. This phase is explicitly sequenced before section assembly to prevent the unoptimized image pitfall from being discovered late. +**Delivers:** WebP-converted, 2x-max app screenshots (iPhone, iPad, Mac); explicit dimensions documented; device mockup frames sourced or built; `AppMockup` component validated +**Addresses:** Must-have feature (screenshots in device mockups — highest-impact gap) +**Avoids:** Pitfall 1 (unoptimized images, LCP failure), Pitfall 7 (CLS from images without reserved space) + +### Phase 4: Section Components and Page Assembly + +**Rationale:** With tokens, primitives, motion wrappers, and screenshot assets all ready, section components can be built without risk of rework. Sections are built in page-order (NavBar, Hero, Features, Screenshots, FAQ, CTA, Footer) then assembled into pages. The i18n validation check must run after every build to detect locale route dropout. +**Delivers:** All section components; updated `layout.tsx` and `page.tsx`; fully redesigned home page; preserved downloader landing page structure +**Implements:** Architecture Tiers 4 and 5; all must-have features from FEATURES.md +**Avoids:** Pitfall 5 (i18n route dropout), Pitfall 9 (CJK layout testing during development, not after) + +### Phase 5: Visual Polish and Differentiators + +**Rationale:** Glow effects, glassmorphism, gradient mesh, platform badges, and thin dividers are deferred to a dedicated polish phase because they do not affect conversion directly and carry GPU performance risk on low-end hardware. This phase also covers the responsive audit and hover micro-interactions. +**Delivers:** Ambient glow / gradient mesh in hero, glassmorphism card surfaces, thin SVG section dividers, platform badge strip, hover micro-interactions, responsive breakpoint audit +**Uses:** CSS-only effects (no additional library); `tailwindcss-animate` for micro-interactions +**Addresses:** Should-have differentiating features from FEATURES.md +**Avoids:** Pitfall 12 (blur effects on low-end hardware — test on throttled GPU before finalizing) + +### Phase 6: Deferred Features (Follow-on) + +**Rationale:** Scroll-linked screenshot reveal requires both design system and screenshot assets to be finalized. Social proof requires real content to be sourced. Downloader landing page redesign must follow the home page. These are high-value but have pre-requisites that cannot be shortcut. +**Delivers:** Scroll-linked screenshot reveal animation, social proof pull-quote section, visual redesign of downloader landing pages +**Addresses:** Deferred features from FEATURES.md + +### Phase Ordering Rationale + +- Phases 1 → 2 → 4 follow strict architectural dependency order (tokens → primitives → sections → pages) +- Phase 3 is inserted before section assembly because screenshot assets are a pre-requisite for the hero section and their performance implications must be resolved before committing assets +- Phase 5 is separated from section assembly to avoid scope creep during the core build and to ensure GPU performance testing happens on finished layouts +- i18n route validation is embedded in Phase 4 rather than a separate phase — it is a check, not work +- The most dangerous pitfalls (image optimization, animation reflow, reduced-motion, hydration) are all addressed in Phases 1–3, before any user-visible component is shipped + +### Research Flags + +Phases likely needing deeper research during planning: +- **Phase 3 (Screenshot Assets):** Device mockup sourcing (CSS-only vs. pre-rendered PNG frames), `next-image-export-optimizer` integration, and optimal screenshot dimensions for retina displays need specific validation against the project's actual image assets +- **Phase 6 (Scroll-Linked Reveal):** CSS scroll-driven animations vs. Motion `useScroll` + `useTransform` trade-offs for the specific screenshot reveal effect need evaluation once Phase 4 is complete + +Phases with standard patterns (skip research-phase): +- **Phase 1 (Design Tokens):** CSS variable + Tailwind semantic alias pattern is well-documented; token values are fully specified in ARCHITECTURE.md +- **Phase 2 (Primitives):** Animation component implementations are fully specified in ARCHITECTURE.md with working code; UI primitives follow shadcn/ui patterns +- **Phase 4 (Section Assembly):** Component compositions are fully mapped in ARCHITECTURE.md; no novel patterns required +- **Phase 5 (Visual Polish):** All CSS effects are documented in STACK.md with exact utility classes + +## Confidence Assessment + +| Area | Confidence | Notes | +|------|------------|-------| +| Stack | HIGH | Core libraries verified via npm registry; version numbers confirmed; static export compatibility verified against official Next.js docs | +| Features | MEDIUM | Pattern analysis of exemplary sites (Linear, Things, Bear); WebFetch was restricted, direct scraping not possible; existing site inspected at HIGH confidence via source code | +| Architecture | HIGH | Component hierarchy and token system verified against Tailwind docs, Motion docs, and CVA docs; code implementations provided and verified | +| Pitfalls | HIGH | Verified against official Next.js docs, MDN, W3C, WCAG; multi-source corroboration for all critical pitfalls | + +**Overall confidence:** MEDIUM-HIGH + +### Gaps to Address + +- **Screenshot asset specifics:** The exact screenshots, dimensions, and device frame approach are not defined in research — this requires a design decision from the team before Phase 3 can begin. The research establishes the technical requirements (WebP, 2x max, explicit dimensions) but not the content. +- **Social proof content:** Whether usable App Store reviews or press mentions exist is unknown — this determines whether Phase 6's social proof section is feasible. Needs content audit. +- **`lenis` static export compatibility:** Confirmed client-side only and inferred compatible with static export, but not verified against official Lenis docs directly. Low risk — confirm during Phase 2 implementation. +- **Dark vs. light theme default:** Research recommends dark as the premium default, but the final decision is a product/brand call. This affects glassmorphism and glow feature feasibility (both require dark backgrounds). + +## Sources + +### Primary (HIGH confidence) +- [Motion npm package](https://www.npmjs.com/package/motion) — v12.38.0, 30M+ weekly downloads; static export compatibility +- [Motion for React docs](https://motion.dev/docs/react) — whileInView, LazyMotion, useScroll, useTransform patterns +- [Next.js Static Exports Guide](https://nextjs.org/docs/app/guides/static-exports) — output: 'export' constraints +- [Tailwind CSS dark mode docs](https://tailwindcss.com/docs/dark-mode) — CSS variable token pattern +- [WCAG 2.1 SC 2.3.3](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html) — prefers-reduced-motion requirement +- [web.dev CLS optimization](https://web.dev/articles/optimize-cls) — image sizing and animation CLS rules +- [App Store Marketing Guidelines](https://developer.apple.com/app-store/marketing/guidelines/) — badge usage +- [CSS Scroll-Driven Animations — Smashing Magazine](https://www.smashingmagazine.com/2024/12/introduction-css-scroll-driven-animations/) — scroll animation patterns +- Direct code inspection of `src/app/[locale]/page.tsx` — existing site structure + +### Secondary (MEDIUM confidence) +- [lenis npm package](https://www.npmjs.com/package/lenis) — v1.3.19; Next.js 15 integration guides +- [GSAP Pricing — Now Free](https://gsap.com/pricing/) — ScrollTrigger and SplitText inclusion +- [The Linear Look — Frontend Horse](https://frontend.horse/articles/the-linear-look/) — bento grid, thin dividers, monochromatic patterns +- [next-image-export-optimizer](https://github.com/Niels-IO/next-image-export-optimizer) — static export image optimization +- [Design tokens with Tailwind dark mode](https://www.richinfante.com/2024/10/21/tailwind-dark-mode-design-tokens-themes-css) — CSS variable alias pattern + +### Tertiary (LOW confidence) +- [SaaS Landing Page Trends 2026 — SaaSFrame](https://www.saasframe.io/blog/blog/10-saas-landing-page-trends-for-2026-with-real-examples) — differentiating feature patterns +- [Hero Section Design Best Practices 2026](https://www.perfectafternoon.com/2025/hero-section-design/) — layout conventions +- [Social proof landing page examples](https://wisernotify.com/blog/landing-page-social-proof/) — pull-quote patterns (single source) + +--- +*Research completed: 2026-03-22* +*Ready for roadmap: yes* From 5db951414c53e7fdf7bebdb227037be41663320d Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:34:46 +0900 Subject: [PATCH 06/12] docs: define v1 requirements Co-Authored-By: Claude Opus 4.6 (1M context) --- .planning/REQUIREMENTS.md | 144 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 .planning/REQUIREMENTS.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md new file mode 100644 index 0000000..b221a85 --- /dev/null +++ b/.planning/REQUIREMENTS.md @@ -0,0 +1,144 @@ +# Requirements: BitRemote Website Redesign + +**Defined:** 2026-03-22 +**Core Value:** The website must look and feel like it belongs to the same product as the BitRemote app — clean, professional, and visually impressive enough to make tech-savvy visitors want to download it. + +## v1 Requirements + +Requirements for the redesign. Each maps to roadmap phases. + +### Design Foundation + +- [ ] **DSGN-01**: Site uses a CSS variable design token system for colors, spacing, typography, and border radius +- [ ] **DSGN-02**: Site supports both dark and light themes, switching automatically via `prefers-color-scheme` +- [ ] **DSGN-03**: Color palette is monochromatic (near-black/white/gray) with blue as the sole accent color, matching the app +- [ ] **DSGN-04**: Dark theme uses near-black backgrounds (not pure `#000`) to avoid halation +- [ ] **DSGN-05**: Typography uses a modern sans-serif font throughout, replacing all monospace usage +- [ ] **DSGN-06**: All ASCII art separators (░ patterns) and terminal-style UI elements are removed + +### Hero Section + +- [ ] **HERO-01**: Hero section has a compelling headline, subheadline, and prominent App Store download button +- [ ] **HERO-02**: Hero features a subtle ambient glow effect (blue-tinted gradient orb) that adapts to theme +- [ ] **HERO-03**: Platform badge strip shows iOS, iPadOS, and macOS support + +### App Showcase + +- [ ] **SHOW-01**: Dedicated showcase section below the hero displays app screenshots in device mockups +- [ ] **SHOW-02**: Screenshots are shown in realistic device frames (iPhone and/or Mac) +- [ ] **SHOW-03**: Dark theme shows dark-mode app screenshots, light theme shows light-mode screenshots +- [ ] **SHOW-04**: Screenshots animate into view on scroll + +### Features Section + +- [ ] **FEAT-01**: Features/benefits are presented in a bento grid layout with varied card sizes +- [ ] **FEAT-02**: Cards use glassmorphism styling (blur + translucency on dark, subtle shadow + border on light) +- [ ] **FEAT-03**: Feature content communicates what the app does and why it matters + +### Navigation & CTAs + +- [ ] **NAV-01**: Sticky navigation bar with section anchor links +- [ ] **NAV-02**: App Store download button appears at top, mid-page, and bottom (inline CTA repetition) +- [ ] **NAV-03**: Navigation adapts to mobile (hamburger or simplified) + +### Visual Effects + +- [ ] **VFX-01**: Sections use staggered fade-in entrance animations triggered on scroll +- [ ] **VFX-02**: Smooth scrolling implemented via lenis for premium feel +- [ ] **VFX-03**: Section dividers use thin elegant SVG lines replacing ASCII separators +- [ ] **VFX-04**: All animations respect `prefers-reduced-motion` — baked in from day one, not retrofitted +- [ ] **VFX-05**: Animations only use `transform` and `opacity` (GPU-composited, no layout thrashing) + +### Content Preservation + +- [ ] **CONT-01**: All existing localized content (en, ja, zh-hans, zh-hant) carries over to the new design +- [ ] **CONT-02**: FAQ section is restyled to match new design system (keep accordion behavior) +- [ ] **CONT-03**: Legal pages (privacy, terms, support) are restyled +- [ ] **CONT-04**: Social links (Twitter, Discord, Telegram, GitHub) remain accessible +- [ ] **CONT-05**: SEO metadata, JSON-LD schemas, sitemap, and robots.txt continue working + +### Technical + +- [ ] **TECH-01**: Site remains a Next.js static export compatible with Cloudflare Pages +- [ ] **TECH-02**: No hydration mismatches — theme detection uses CSS media queries, not JS localStorage on initial render +- [ ] **TECH-03**: App screenshots are pre-optimized to WebP before being added to the project +- [ ] **TECH-04**: Animation components use `"use client"` directive; static content stays server-renderable +- [ ] **TECH-05**: Build produces correct output for all locale paths + +## v2 Requirements + +Deferred to future release. Tracked but not in current roadmap. + +### Advanced Visual + +- **ADV-01**: Scroll-linked screenshot reveal (screenshots animate in sync with scroll position, not just on-enter) +- **ADV-02**: Social proof section with curated App Store reviews or press quotes +- **ADV-03**: Manual theme toggle switch (beyond automatic `prefers-color-scheme`) + +### Downloader Pages + +- **DLP-01**: Downloader-specific landing pages receive full visual redesign matching new design system +- **DLP-02**: Each downloader page features tailored screenshots for that client + +## Out of Scope + +| Feature | Reason | +|---------|--------| +| Video background / autoplaying hero video | Heavy assets kill performance; distracting for tech audience | +| Email newsletter / lead capture form | Wrong funnel for a paid native app; adds GDPR complexity | +| Testimonial carousel / slider | Low engagement; tech users skip them | +| Chat widget | Wrong support channel; Discord/Telegram/GitHub are better for this audience | +| Cookie consent banner | Static site with no tracking — nothing to consent to | +| Animated particle / canvas backgrounds | Performance penalty; often looks generic | +| Pricing comparison table | Single paid app, no tiers to compare | +| Blog or changelog | Different initiative, not part of visual redesign | +| Analytics integration | Can be added separately, not a design concern | +| Tailwind v3 → v4 migration | No framework migration — keep what works | + +## Traceability + +| Requirement | Phase | Status | +|-------------|-------|--------| +| DSGN-01 | Pending | Pending | +| DSGN-02 | Pending | Pending | +| DSGN-03 | Pending | Pending | +| DSGN-04 | Pending | Pending | +| DSGN-05 | Pending | Pending | +| DSGN-06 | Pending | Pending | +| HERO-01 | Pending | Pending | +| HERO-02 | Pending | Pending | +| HERO-03 | Pending | Pending | +| SHOW-01 | Pending | Pending | +| SHOW-02 | Pending | Pending | +| SHOW-03 | Pending | Pending | +| SHOW-04 | Pending | Pending | +| FEAT-01 | Pending | Pending | +| FEAT-02 | Pending | Pending | +| FEAT-03 | Pending | Pending | +| NAV-01 | Pending | Pending | +| NAV-02 | Pending | Pending | +| NAV-03 | Pending | Pending | +| VFX-01 | Pending | Pending | +| VFX-02 | Pending | Pending | +| VFX-03 | Pending | Pending | +| VFX-04 | Pending | Pending | +| VFX-05 | Pending | Pending | +| CONT-01 | Pending | Pending | +| CONT-02 | Pending | Pending | +| CONT-03 | Pending | Pending | +| CONT-04 | Pending | Pending | +| CONT-05 | Pending | Pending | +| TECH-01 | Pending | Pending | +| TECH-02 | Pending | Pending | +| TECH-03 | Pending | Pending | +| TECH-04 | Pending | Pending | +| TECH-05 | Pending | Pending | + +**Coverage:** +- v1 requirements: 34 total +- Mapped to phases: 0 +- Unmapped: 34 ⚠️ + +--- +*Requirements defined: 2026-03-22* +*Last updated: 2026-03-22 after initial definition* From 0d4986ffb28c64c09f3cd485208cccac6123dcfd Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:38:46 +0900 Subject: [PATCH 07/12] docs: create roadmap (5 phases) Co-Authored-By: Claude Opus 4.6 (1M context) --- .planning/REQUIREMENTS.md | 74 +++++++++++++++---------------- .planning/ROADMAP.md | 91 +++++++++++++++++++++++++++++++++++++++ .planning/STATE.md | 60 ++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 37 deletions(-) create mode 100644 .planning/ROADMAP.md create mode 100644 .planning/STATE.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index b221a85..fd11b79 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -99,46 +99,46 @@ Deferred to future release. Tracked but not in current roadmap. | Requirement | Phase | Status | |-------------|-------|--------| -| DSGN-01 | Pending | Pending | -| DSGN-02 | Pending | Pending | -| DSGN-03 | Pending | Pending | -| DSGN-04 | Pending | Pending | -| DSGN-05 | Pending | Pending | -| DSGN-06 | Pending | Pending | -| HERO-01 | Pending | Pending | -| HERO-02 | Pending | Pending | -| HERO-03 | Pending | Pending | -| SHOW-01 | Pending | Pending | -| SHOW-02 | Pending | Pending | -| SHOW-03 | Pending | Pending | -| SHOW-04 | Pending | Pending | -| FEAT-01 | Pending | Pending | -| FEAT-02 | Pending | Pending | -| FEAT-03 | Pending | Pending | -| NAV-01 | Pending | Pending | -| NAV-02 | Pending | Pending | -| NAV-03 | Pending | Pending | -| VFX-01 | Pending | Pending | -| VFX-02 | Pending | Pending | -| VFX-03 | Pending | Pending | -| VFX-04 | Pending | Pending | -| VFX-05 | Pending | Pending | -| CONT-01 | Pending | Pending | -| CONT-02 | Pending | Pending | -| CONT-03 | Pending | Pending | -| CONT-04 | Pending | Pending | -| CONT-05 | Pending | Pending | -| TECH-01 | Pending | Pending | -| TECH-02 | Pending | Pending | -| TECH-03 | Pending | Pending | -| TECH-04 | Pending | Pending | -| TECH-05 | Pending | Pending | +| DSGN-01 | Phase 1 | Pending | +| DSGN-02 | Phase 1 | Pending | +| DSGN-03 | Phase 1 | Pending | +| DSGN-04 | Phase 1 | Pending | +| DSGN-05 | Phase 1 | Pending | +| DSGN-06 | Phase 1 | Pending | +| VFX-01 | Phase 2 | Pending | +| VFX-02 | Phase 2 | Pending | +| VFX-04 | Phase 2 | Pending | +| VFX-05 | Phase 2 | Pending | +| TECH-01 | Phase 2 | Pending | +| TECH-02 | Phase 2 | Pending | +| TECH-04 | Phase 2 | Pending | +| SHOW-01 | Phase 3 | Pending | +| SHOW-02 | Phase 3 | Pending | +| SHOW-03 | Phase 3 | Pending | +| SHOW-04 | Phase 3 | Pending | +| TECH-03 | Phase 3 | Pending | +| HERO-01 | Phase 4 | Pending | +| HERO-02 | Phase 4 | Pending | +| HERO-03 | Phase 4 | Pending | +| FEAT-01 | Phase 4 | Pending | +| FEAT-02 | Phase 4 | Pending | +| FEAT-03 | Phase 4 | Pending | +| NAV-01 | Phase 4 | Pending | +| NAV-02 | Phase 4 | Pending | +| NAV-03 | Phase 4 | Pending | +| CONT-01 | Phase 4 | Pending | +| CONT-02 | Phase 4 | Pending | +| CONT-03 | Phase 4 | Pending | +| CONT-04 | Phase 4 | Pending | +| CONT-05 | Phase 4 | Pending | +| TECH-05 | Phase 4 | Pending | +| VFX-03 | Phase 5 | Pending | **Coverage:** - v1 requirements: 34 total -- Mapped to phases: 0 -- Unmapped: 34 ⚠️ +- Mapped to phases: 34 +- Unmapped: 0 --- *Requirements defined: 2026-03-22* -*Last updated: 2026-03-22 after initial definition* +*Last updated: 2026-03-22 after roadmap creation* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md new file mode 100644 index 0000000..a6107eb --- /dev/null +++ b/.planning/ROADMAP.md @@ -0,0 +1,91 @@ +# Roadmap: BitRemote Website Redesign + +## Overview + +The redesign replaces the current ASCII/monospace aesthetic with a modern design system that matches the BitRemote native app. Work proceeds in strict dependency order: design tokens first, motion and UI primitives second, screenshot assets third, section components and page assembly fourth, and visual polish differentiators last. The result is a marketing site that looks and feels like it belongs to the same product as the app. + +## Phases + +**Phase Numbering:** +- Integer phases (1, 2, 3): Planned milestone work +- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED) + +Decimal phases appear between their surrounding integers in numeric order. + +- [ ] **Phase 1: Design Foundation** - Establish the design token system, typography, color palette, and dark theme that all other components depend on +- [ ] **Phase 2: Motion and UI Primitives** - Build reusable animation and UI components with GPU-safe constraints baked in from day one +- [ ] **Phase 3: Screenshot Assets** - Prepare, optimize, and validate app screenshot assets before any section that displays them is built +- [ ] **Phase 4: Section Assembly** - Build all section components and assemble the full page using tokens, primitives, and optimized assets +- [ ] **Phase 5: Visual Polish** - Add differentiating effects (SVG dividers, glassmorphism, ambient glow, platform badges) and audit responsive behavior + +## Phase Details + +### Phase 1: Design Foundation +**Goal**: The design system is established — tokens, palette, typography, and dark theme are in place and ready for all downstream components to consume +**Depends on**: Nothing (first phase) +**Requirements**: DSGN-01, DSGN-02, DSGN-03, DSGN-04, DSGN-05, DSGN-06 +**Success Criteria** (what must be TRUE): + 1. The site renders in dark and light themes automatically based on the visitor's OS preference, with no flash of wrong content on load + 2. All text across the site uses a modern sans-serif font — no monospace type remains anywhere on the page + 3. The color palette uses only near-black, white, gray, and blue across all themed surfaces — no other hues appear + 4. Dark backgrounds use near-black (not pure #000), visually distinguishable from pure black on any display + 5. No ASCII art separators (░ patterns), terminal borders, or monospace decorative elements exist anywhere in the codebase or rendered page +**Plans**: TBD + +### Phase 2: Motion and UI Primitives +**Goal**: All reusable animation and UI building blocks exist and enforce correct constraints — transform/opacity-only animations, reduced-motion support, and hydration safety are baked in before any section is built +**Depends on**: Phase 1 +**Requirements**: VFX-01, VFX-02, VFX-04, VFX-05, TECH-01, TECH-02, TECH-04 +**Success Criteria** (what must be TRUE): + 1. Scroll entrance animations play correctly on all section components and use only opacity and transform — no layout properties are animated + 2. Smooth scroll (lenis) is active and page navigation feels physically weighted rather than abrupt + 3. All animations are completely absent (not just skipped) for visitors with prefers-reduced-motion enabled + 4. The site produces no React hydration mismatch warnings — theme and animation state are handled without reading browser APIs during server render + 5. The site remains a Next.js static export deployable to Cloudflare Pages with no SSR-only dependencies introduced +**Plans**: TBD + +### Phase 3: Screenshot Assets +**Goal**: App screenshots are captured, optimized to WebP, sized for retina displays, and validated against the AppMockup component before any section that displays them is built +**Depends on**: Phase 2 +**Requirements**: SHOW-01, SHOW-02, SHOW-03, SHOW-04, TECH-03 +**Success Criteria** (what must be TRUE): + 1. A dedicated showcase section exists on the page and displays app screenshots inside realistic device frames (iPhone and/or Mac) + 2. All screenshot images are WebP format and under the performance budget — page LCP is not caused by an unoptimized image asset + 3. Visiting the site in dark OS theme shows dark-mode app screenshots; light OS theme shows light-mode screenshots — no JavaScript state drives this on initial load + 4. Screenshots animate into view as the visitor scrolls to the showcase section +**Plans**: TBD + +### Phase 4: Section Assembly +**Goal**: All sections are built and the home page is fully assembled — visitors experience a complete, content-correct redesigned marketing page with all existing localized content preserved +**Depends on**: Phase 3 +**Requirements**: HERO-01, HERO-02, HERO-03, FEAT-01, FEAT-02, FEAT-03, NAV-01, NAV-02, NAV-03, CONT-01, CONT-02, CONT-03, CONT-04, CONT-05, TECH-05 +**Success Criteria** (what must be TRUE): + 1. The hero section displays a compelling headline, subheadline, prominent App Store download button, and platform badge strip (iOS, iPadOS, macOS) above the fold + 2. The features section uses a bento grid layout with glassmorphism-styled cards that communicate what the app does and why it matters + 3. A sticky navigation bar is present with section anchor links and an App Store download button; a second CTA appears mid-page or bottom — visitors never scroll past a CTA + 4. Navigation collapses to a mobile-appropriate layout on small screens without breaking usability + 5. Visiting /ja/, /zh-hans/, and /zh-hant/ routes produces complete, correctly localized pages — no locale routes are missing from the static build output +**Plans**: TBD + +### Phase 5: Visual Polish +**Goal**: Differentiating visual effects are added and responsive behavior is audited — the site looks premium and works correctly across all screen sizes +**Depends on**: Phase 4 +**Requirements**: VFX-03 +**Success Criteria** (what must be TRUE): + 1. Section boundaries are separated by thin, elegant SVG lines — no ASCII separator patterns remain as dividers + 2. The hero section has an ambient blue-tinted glow effect that adapts correctly between dark and light themes without visual artifacts + 3. The site renders correctly and looks intentional on mobile (375px), tablet (768px), and desktop (1280px+) screen widths +**Plans**: TBD + +## Progress + +**Execution Order:** +Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 + +| Phase | Plans Complete | Status | Completed | +|-------|----------------|--------|-----------| +| 1. Design Foundation | 0/? | Not started | - | +| 2. Motion and UI Primitives | 0/? | Not started | - | +| 3. Screenshot Assets | 0/? | Not started | - | +| 4. Section Assembly | 0/? | Not started | - | +| 5. Visual Polish | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md new file mode 100644 index 0000000..545e357 --- /dev/null +++ b/.planning/STATE.md @@ -0,0 +1,60 @@ +# Project State + +## Project Reference + +See: .planning/PROJECT.md (updated 2026-03-22) + +**Core value:** The website must look and feel like it belongs to the same product as the BitRemote app — clean, professional, and visually impressive enough to make tech-savvy visitors want to download it. +**Current focus:** Phase 1 — Design Foundation + +## Current Position + +Phase: 1 of 5 (Design Foundation) +Plan: 0 of ? in current phase +Status: Ready to plan +Last activity: 2026-03-22 — Roadmap created, 5 phases derived from 34 v1 requirements + +Progress: [░░░░░░░░░░] 0% + +## Performance Metrics + +**Velocity:** +- Total plans completed: 0 +- Average duration: — +- Total execution time: 0 hours + +**By Phase:** + +| Phase | Plans | Total | Avg/Plan | +|-------|-------|-------|----------| +| - | - | - | - | + +**Recent Trend:** +- Last 5 plans: — +- Trend: — + +*Updated after each plan completion* + +## Accumulated Context + +### Decisions + +Decisions are logged in PROJECT.md Key Decisions table. +Recent decisions affecting current work: + +- None yet — decisions will be logged as phases execute. + +### Pending Todos + +None yet. + +### Blockers/Concerns + +- [Pre-Phase 3]: Screenshot content is not yet defined — exact screenshots, device frame approach, and dimensions require a product decision before Phase 3 can begin (noted in research SUMMARY.md). +- [Pre-Phase 1]: Dark vs. light theme default is pending — research recommends dark; final call affects glassmorphism and glow feasibility. + +## Session Continuity + +Last session: 2026-03-22 +Stopped at: Roadmap written, STATE.md initialized, REQUIREMENTS.md traceability updated. Ready to run /gsd:plan-phase 1. +Resume file: None From 8c3b60da7bbd29b34d42e6989f4538fff9d280bb Mon Sep 17 00:00:00 2001 From: Tatsuzo Araki Date: Sun, 22 Mar 2026 22:39:57 +0900 Subject: [PATCH 08/12] docs: map existing codebase --- .planning/codebase/ARCHITECTURE.md | 254 ++++++++++--------- .planning/codebase/CONCERNS.md | 166 +++++++++++++ .planning/codebase/CONVENTIONS.md | 319 +++++++++++------------- .planning/codebase/INTEGRATIONS.md | 191 +++++++++------ .planning/codebase/STACK.md | 127 +++++----- .planning/codebase/STRUCTURE.md | 378 ++++++++++++++--------------- .planning/codebase/TESTING.md | 374 +++++++++++++++------------- 7 files changed, 1004 insertions(+), 805 deletions(-) create mode 100644 .planning/codebase/CONCERNS.md diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md index 76caa4b..caafe65 100644 --- a/.planning/codebase/ARCHITECTURE.md +++ b/.planning/codebase/ARCHITECTURE.md @@ -4,181 +4,179 @@ ## Pattern Overview -**Overall:** Static Site Generation (SSG) with Multi-Language Support +**Overall:** Static site generation with Next.js App Router using an island architecture pattern. The application renders a marketing website with multi-locale support and a client-side ASCII panel UI component for interactive demonstration. **Key Characteristics:** -- Next.js 15 with `output: export` configuration for static HTML generation -- App Router with dynamic parameters disabled (`dynamicParams = false`) for type-safe static routes -- Internationalization (i18n) as a core architectural concern handled via URL segments -- Component-driven UI with Tailwind CSS styling -- Server-side metadata generation for SEO with JSON-LD structured data +- Server-side rendering with static pre-generation (`output: 'export'`) +- Multi-locale content with runtime locale detection +- Component-driven UI with separated page and component layers +- Domain-driven organization for business logic (downloaders, content) +- SEO-optimized with JSON-LD schema generation +- Client-side interactive components isolated to specific pages ## Layers -**Routing Layer:** -- Purpose: Handle URL structure and page generation for multi-locale support -- Location: `src/app/` (Next.js App Router structure) -- Contains: Page components, layout components, API routes, metadata generation -- Depends on: i18n locales, domain models, component library -- Used by: Browser client, search engines +**App Router Layer (Pages & Layouts):** +- Purpose: Define page structure and routing using Next.js App Router conventions +- Location: `src/app/` and `src/app/[locale]/` +- Contains: Page components (`page.tsx`), layout wrappers (`layout.tsx`), metadata generation, static parameter generation +- Depends on: Components, i18n, domain, SEO utilities +- Used by: Next.js build system for route generation -**Domain/Business Logic Layer:** -- Purpose: Encapsulate product-specific data and rules (downloaders, landing pages) +**Component Layer:** +- Purpose: Reusable UI components focused on rendering and basic interactivity +- Location: `src/components/` +- Contains: Button components (`TextButton.tsx`), frames (`TextFrame.tsx`), navigation (`TextTabsNav.tsx`), specialized components (`BitRemoteWordmark.tsx`, `FaqAccordion.tsx`) +- Depends on: i18n for messaging, Tailwind CSS for styling +- Used by: Page components and the ASCII panel module + +**ASCII Panel Module:** +- Purpose: Interactive client-side component demonstrating BitRemote UI in ASCII art style +- Location: `src/ascii-panel/` +- Contains: Main panel coordinator (`index.tsx`), UI components (`components/`), page views (`pages/`), frame management and state handling +- Depends on: React hooks for state management (useEffect, useState), component library patterns +- Used by: Home page (`src/app/[locale]/page.tsx`) + +**Domain Layer:** +- Purpose: Core business logic and data models for downloaders and landing content - Location: `src/domain/` -- Contains: `downloaders.ts` (Downloader enum, supported clients), `downloader-landings.ts` (content dictionaries, slug mapping) -- Depends on: i18n locales for content templates -- Used by: Page components, SEO/metadata generation +- Contains: Downloader enum and list (`downloaders.ts`), downloader landing page content mappings (`downloader-landings.ts`) +- Depends on: i18n for localization +- Used by: Pages, components, and SEO layer **Internationalization (i18n) Layer:** -- Purpose: Centralize locale configuration, URL generation, and message management +- Purpose: Manage multi-locale content, routing, and language configuration - Location: `src/i18n/` -- Contains: - - `locales.ts` - locale constants, validation, BCP 47 language tags - - `messages.ts` - message loading (imports JSON files from `src/messages/`) - - `urls.ts` - locale-aware URL generation functions - - `links.ts` - external links configuration -- Depends on: Message JSON files -- Used by: All page components, layouts, metadata builders - -**SEO & Metadata Layer:** -- Purpose: Generate Open Graph, Twitter Card, JSON-LD structured data, and canonical links +- Contains: Locale definitions (`locales.ts`), message loader (`messages.ts`), URL builders (`urls.ts`), external links (`links.ts`) +- Depends on: Message JSON files in `src/messages/` +- Used by: All pages and components requiring locale context + +**SEO Layer:** +- Purpose: Generate structured data (JSON-LD) and metadata for search engines - Location: `src/seo/` -- Contains: - - `metadata.ts` - builds metadata for localized pages - - `schema.ts` - JSON-LD builders (SoftwareApplication, FAQPage, BreadcrumbList) - - `routes.ts` - localized route entries for sitemap -- Depends on: i18n messages, domain models -- Used by: Page components for metadata generation - -**Presentation Layer:** -- Purpose: Reusable UI components using Tailwind CSS -- Location: `src/components/` -- Contains: Buttons, frames, text components, separators, accordions, word mark -- Depends on: Tailwind configuration, CSS variables -- Used by: Page components, ASCII panel, layouts +- Contains: Schema builders (`schema.ts`), metadata generators (`metadata.ts`), downloader-specific metadata (`downloader-metadata.ts`), route configuration (`routes.ts`) +- Depends on: Domain models, i18n, messages +- Used by: Page components during metadata generation and rendering -**Interactive Component Layer:** -- Purpose: Client-side interactive UI (uses `'use client'`) -- Location: `src/ascii-panel/` -- Contains: Frame-based terminal-style UI with state management - - `index.tsx` - main component with local state (page navigation, modal sheets) - - `components/` - AsciiPanelFrame, AsciiPanelSheet - - `pages/` - HomePage, SettingsPage, NewClientPage -- Depends on: React hooks for state/effects -- Used by: Home page layout +**Content Layer (Messages):** +- Purpose: Store translatable content strings and structured content +- Location: `src/messages/` +- Contains: JSON files for each locale (`en.json`, `ja.json`, `zh-hans.json`, `zh-hant.json`) containing all UI text, section content, and metadata ## Data Flow -**Static Page Generation:** - -1. Build time: Next.js processes all `[locale]` dynamic routes -2. `generateStaticParams()` in layout defines all locale variants (en, ja, zh-hans, zh-hant) -3. For each locale route, page component: - - Validates locale using `isLocale()` from `src/i18n/locales.ts` - - Fetches messages via `getMessages(locale)` from `src/i18n/messages.ts` - - Generates metadata using `buildMetadataForCurrentLocalePage()` from `src/seo/metadata.ts` - - Renders with locale-specific content and links - -**Downloader Landing Pages:** +**Static Page Generation (Build Time):** -1. Routes: `src/app/[locale]/downloaders/[slug]/page.tsx` -2. Slugs are statically enumerated from `downloaderLandingSlugs` in `src/domain/downloader-landings.ts` -3. For each slug, retrieve content via `getDownloaderLandingContent(locale, slug)` -4. Content factory pattern: locale + downloader enum → localized content -5. Metadata built with downloader-specific SEO data +1. Next.js calls `generateStaticParams()` in layout files to discover all locale variants and dynamic routes +2. For each locale and route, the page component is rendered +3. `generateMetadata()` is called to build title, description, and schema tags +4. Messages are loaded from `src/messages/` for the current locale +5. Schema objects are built (SoftwareApplication, FAQ, Breadcrumb) +6. Page renders with localized messages and embedded schema in `