Ethereum.org is a primary online resource for the Ethereum community.
# CLAUDE.md - Ethereum.org Website
## Project Overview
This is the official Ethereum.org website - a Next.js application that serves as the primary educational and community hub for Ethereum. The site is built with modern web technologies and focuses on accessibility, internationalization, and performance.
## Technology Stack
### Core Framework
- **Next.js 16+** - React framework with App Router (Turbopack is the default bundler; webpack available via `pnpm dev:webpack` / `pnpm build:webpack`)
- **React 19** - UI library
- **TypeScript 5.5+** - Type safety and development experience
- **Tailwind CSS 4+** - Utility-first CSS framework (CSS-first config in `src/styles/global.css`)
### Key Dependencies
- **next-intl 4+** - Internationalization (i18n) with 25 languages
- **next-mdx-remote 5.0+** - MDX content processing
- **Motion 12+** (`motion` package, formerly Framer Motion) - Animations and transitions; import from `motion/react`
- **Radix UI** - Accessible component primitives
- **shadcn/ui** - Component library built on Radix UI
- **Recharts** - Data visualization
- **Viem/Wagmi** - Ethereum blockchain integration
### Development & Testing
- **Storybook 10+** - Component development and testing
- **Chromatic** - Visual regression testing
- **ESLint** - Code linting with custom rules
- **Prettier** - Code formatting
- **Husky** - Git hooks
- **PNPM** - Package manager
## Project Structure
- **app/** - Next.js App Router pages
- **[locale]/** - Internationalized routes
- **src/**
- **components/** - React components
- **ui/** - Design system components
- **icons/** - SVG icon components
- **data/** - Static data and configurations
- **hooks/** - Custom React hooks
- **i18n/** - Internationalization config
- **intl/** - Translation files (25 languages)
- **layouts/** - Page layout components
- **lib/** - Utility functions and types
- **constants.ts** - App constants
- **types.ts** - TypeScript type definitions
- **utils/** - Utility functions
- **styles/** - Global styles and design tokens
- **public/** - Static assets
- **content/** - Markdown content files
- **images/** - Image assets
- **docs/** - Development documentation
- **solutions/** - Documented solutions to past problems, organized by category with YAML frontmatter (module, tags, problem_type)
## Code Conventions
### File Naming
- **Components**: kebab-case (e.g., `button-group.tsx`)
- **Utilities**: camelCase (e.g., `cn.ts`, `relativePath.ts`)
- **Pages**: kebab-case following Next.js conventions
- **Assets**: kebab-case (e.g., `eth-logo.png`)
### TypeScript Patterns
- Use `interface` for object shapes, `type` for unions/intersections
- Prefer explicit typing over `any` (ESLint enforces `fixToUnknown`)
- **NEVER leave unused variables or parameters** - ESLint `unused-imports/no-unused-vars` will fail the Netlify build. The only allowed unused arg pattern is a single underscore `_`. Do NOT use `_prefixedNames` (e.g., `_foo`) - either use the variable or remove it from the signature entirely.
- Use generic constraints for reusable components
- Export types from dedicated files in `@/lib/types`
### Styling Conventions
- **Primary approach**: Tailwind CSS utility classes
- **Component variants**: Use `tailwind-variants` (`tv`) for new and refactored work. Existing `class-variance-authority` (`cva`) components don't need bulk migration -- swap to `tv` opportunistically when you're already touching the component for another reason.
- **Dynamic classes**: Use `cn()` utility (clsx + tailwind-merge)
- **Custom properties**: CSS variables in `src/styles/` for theme values
- **Responsive design**: Mobile-first approach
For UI work, see the **`design-system` skill** at `.claude/skills/design-system/`. It's the canonical knowledge base for component choices, design tokens, RTL/i18n, server/client boundaries, and the "use a variant, not a new component" pattern.
## Development Workflows
### Available Scripts
```bash
# Development
pnpm dev # Start development server (Turbopack)
pnpm dev:webpack # Start development server with webpack
pnpm build # Build for production (Turbopack)
pnpm build:webpack # Build for production with webpack
pnpm start # Start production server
# Code Quality
pnpm lint # Run ESLint
pnpm lint:fix # Fix ESLint issues
pnpm type-check # TypeScript type checking
pnpm format # Format with Prettier
# Testing
pnpm test:unit # Playwright unit tests (unit project)
pnpm test:e2e # Playwright end-to-end tests
pnpm test:visual # Playwright + Chromatic full-page visual tests
# Storybook
pnpm storybook # Start Storybook dev server
pnpm build-storybook # Build Storybook
pnpm chromatic # Storybook Chromatic (needs CHROMATIC_STORYBOOK_TOKEN)
# Content Management
pnpm lint:md # Lint English markdown content
pnpm lint:md:fix # Auto-fix header IDs and duplicates
```
### Testing Strategy
- **Unit Testing**: Playwright `unit` project (`pnpm test:unit`) — heaviest coverage is the intl-pipeline sanitizer
- **E2E Testing**: Playwright (`pnpm test:e2e`), see `docs/e2e-testing.md`
- **Visual Testing**: Storybook + Chromatic for component regression; Playwright + Chromatic for full pages (see the `page-visual-tests` skill)
- **Type Safety**: TypeScript strict mode enabled
- **Linting**: ESLint with custom rules for imports and TypeScript
## Content Management
### Internationalization
- **25 languages** supported (canonical list: `i18n.config.json`); **RTL support** for Arabic, Urdu
- JSON UI strings in `src/intl/[locale]/`; translated markdown content in `public/content/translations/[locale]/`
- Non-English markdown is propagated by the **intl-pipeline** (`src/scripts/intl-pipeline/`, entry `main.ts`). **Do not hand-propagate English changes into non-English files** -- let the pipeline run, or trigger `intl-pipeline.yml` with `stamp_only: true` if manifests must catch up urgently (e.g. unblocking a build). Hand-fixing a translation error is fine when the English side hasn't moved, since the manifest mapping stays valid. Spec: `tests/specs/PIPELINE-SPEC.md`.
- Glossary: base URL from `GLOSSARY_API_URL` env var; default in `src/scripts/intl-pipeline/config.ts`. ETHGlossary is authoritative for Ethereum term translations.
For pipeline mechanics, recovery, manifests, ETHGlossary integration, and the `intl/pending-{base}` orchestration model, see the **`intl-pipeline` skill** at `.claude/skills/intl-pipeline/`. For translation-quality review (scoring rubric, language-group rules, ETHGlossary-as-authority policy, multi-agent role split), see the **`intl-review` skill** at `.claude/skills/intl-review/`.
### Markdown Content
- Educational content stored in `public/content/`
- Processed with `next-mdx-remote`
- Custom MDX components for rich content
- Automatic table of contents generation
- **All h1-h4 headings require a custom `{#lower-kebab-id}`** -- enforced by markdownlint via pre-commit hook. Run `pnpm lint:md:fix` to auto-add missing IDs. Config: `.markdownlint-cli2.jsonc`, custom rules: `.markdownlint-rules/`
### Asset Management
- Images optimized with Next.js Image component
- SVGs loaded as React components via `@svgr/webpack`
- Static assets served from `public/`
- Placeholder generation for images
## SEO & Meta
- Sitemap generation in `app/sitemap.ts`
- Meta tags and Open Graph optimization
- Structured data for search engines
- Security headers (X-Frame-Options: DENY)
## Development Guidelines
### When Working on Features
1. **Check existing patterns** - Look at similar components first
2. **Prioritize Server Components** - Use App Router and Server Components when possible
3. **Follow import order** - ESLint will enforce, but be proactive
4. **Use TypeScript strictly** - No `any` types, prefer `unknown`
5. **Test in Storybook** - Create stories for new components (filename pattern: `.stories.tsx`)
6. **Consider i18n** - All user-facing text should be translatable. Server components: `getTranslations` and `getLocale` from `next-intl/server`. Client components: `useTranslations` from `next-intl`, one namespace-bound function per namespace - to access a second namespace, bind another function (e.g. `const tCommon = useTranslations("common")`) rather than reaching across namespaces
7. **Mobile-first** - Design for mobile, enhance for desktop
8. **Accessibility** - Use Radix primitives, semantic HTML
9. **Use locale-aware formatting wrappers** - Use `numberFormat()` from `src/lib/utils/numbers.ts` instead of `new Intl.NumberFormat()`, and `dateTimeFormat()` from `src/lib/utils/date.ts` instead of `new Intl.DateTimeFormat()` / `.toLocaleDateString()` / `.toLocaleTimeString()`. Both enforce correct numbering systems and calendar for Urdu and Arabic locales.
### Component Development
1. Create component in appropriate `src/components/` subdirectory
- Use `src/components/ui` for shadcn components or pure UI components
2. Add TypeScript types and proper props interface
3. Accept `ref` as a regular prop when needed (React 19); `forwardRef` only survives in legacy components
4. Add a Storybook story (see below)
5. Export from appropriate index file
6. Update documentation if adding new patterns
### Storybook stories
**Location**: `__stories__/` for flat directories (`ui/`, `styles/`), co-located for foldered components. Story globs are recursive, so nesting works. **Filenames are kebab-case** (`app-card.stories.tsx`) even inside a PascalCase component directory -- enforced by the test below.
**Title**: `Section / Group / Name`. The section is derived from the file path -- not a judgment call:
| Path | Section |
| ------------------------------------- | --------------- |
| `src/styles/**` | `Design System` |
| `src/components/ui/**` | `UI` |
| `src/components/**` (everything else) | `Components` |
| `src/layouts/**` | `Layouts` |
| `app/**` | `Pages` |
Use a second-level group only when an established one fits (Actions, Forms, Layout, Navigation, Overlays, Data Display, Cards, Heroes, Site Chrome, Content, Data Viz, Features). **Don't invent a group for a single component** -- a flat `Components / Morpher` is correct and preferred. `tests/unit/storybook/story-titles.spec.ts` enforces the section prefix, explicit titles, and title uniqueness (Storybook silently merges duplicate titles into one sidebar entry).
**Don't add a story for a deprecated component or variant.** A rendered example reads as an endorsement whatever the caption says, so the showcase should only contain things that are available to use -- note the deprecation in prose instead (see `HR`). A component whose future is merely undecided can keep its story with a status blurb (see `Carousel`).
Add `tags: ["autodocs"]`. Write the `parameters.docs.description.component` blurb for someone deciding whether to use the thing -- constraints and gotchas, not a restatement of the props table.
**A green build does not mean a story renders.** `pnpm build-storybook` compiles stories without executing them, and every `UI /` story sets `chromatic: { disableSnapshot: true }`, so neither CI signal catches a story that throws. Open new stories in `pnpm storybook` before pushing.
Async server components render via a `next-intl/server` shim (`.storybook/next-intl-server.tsx`). Components calling `usePathname` need `parameters.nextjs.navigation.pathname`. New i18n namespaces must be added to the `ns` array in `.storybook/next-intl.ts`, or strings render as raw key tails.
### Content Updates
1. Markdown files go in `public/content/`
2. Images in `public/images/` with descriptive names
3. Translation strings in appropriate `src/intl/` JSON files
4. Data files in `src/data/` with TypeScript types
### Type-Safe Chain Names
This project enforces type-safe chain names via TypeScript. When working with layer 2 networks or wallet data:
**Critical Files:**
- `src/data/chains.ts` - Canonical source of all chain names (auto-updated weekly)
- `src/lib/types.ts` - Defines `ChainName` type derived from chains.ts
- `src/data/networks/networks.ts` - Uses `chainName: ChainName`
- `src/data/wallets/wallet-data.ts` - Uses `supported_chains: ChainName[]`
**Rules:**
1. **Always look up exact names** - Before adding `chainName` or `supported_chains`, search `chains.ts` for the exact `name` value
2. **Names are case-sensitive and exact** - e.g., use `"Zircuit Mainnet"` not `"Zircuit"`, use `"OP Mainnet"` not `"Optimism"`
3. **Run type checking** - Use `pnpm type-check` to verify chain names are valid before committing
4. **Non-EVM chains** - For Starknet and other non-EVM chains, use `NonEVMChainName` type
**Common Mistakes:**
- Using informal names: `"Optimism"` should be `"OP Mainnet"`
- Missing "Mainnet" suffix: `"Zircuit"` should be `"Zircuit Mainnet"`
- Wrong casing: `"zksync Mainnet"` should be `"zkSync Mainnet"`
## Key Dependencies to Know
### UI & Styling
- `@radix-ui/*` - Accessible component primitives
- `tailwind-variants` - Component variant patterns
- `motion` - Animation library (formerly framer-motion; import from `motion/react`)
- `lucide-react` - Icon library
### Content & Data
- `gray-matter` - Frontmatter parsing
- `recharts` - Data visualization
### Ethereum Integration
- `viem` - Ethereum library
- `wagmi` - React hooks for Ethereum
- `@rainbow-me/rainbowkit` - Wallet connection
## A/B Testing
The site uses a GDPR-compliant, cookie-less A/B testing system built on the Flags SDK precompute pattern, integrated with Matomo. The proxy assigns variants at the edge (header fingerprinting) and rewrites to signed, statically prerendered variant pages under `ab-code/[code]/` — experiments never force dynamic rendering. Experiment status, weights, and scheduling live in the Matomo dashboard (no deploys needed). Only the default locale is tested; other locales always get the original.
Key gotcha: **variants are matched by array index, not names** — the `variants` array order must match the Matomo experiment order exactly, and the `testKey` must match the Matomo experiment name exactly.
Full guide (architecture, step-by-step recipe, env vars): `docs/ab-testing.md`. Code: `proxy.ts`, `src/lib/ab-testing/`, `src/components/AB/`, `app/[locale]/ab-code/`.
## Deployment
- **Platform**: Netlify (config in `netlify.toml`)
- **Next.js Integration**: Uses `@netlify/plugin-nextjs` for seamless Netlify and Next.js compatibility
- **Monitoring**: Matomo analytics integration
## Internal Infrastructure
The following external-looking services are managed by the ethereum.org team:
- **`s3-dcl1.ethquokkaops.io`** — S3-compatible object storage for app screenshots and media. Used by the data layer to serve images for the `/dapps` and app listing pages. Downtime here means broken images on the live site.
- **Netlify Blobs** (`@netlify/blobs`) — Key-value store used by the data layer to cache API responses. Accessed via `src/data-layer/storage.ts`.