Website
The Next.js landing page in website/
The website/ folder holds the CleanLens marketing landing page and the
documentation pages that render this docs/ folder. It is a separate
application: it does not import the extension's code, has its own
package.json and lockfile, and is not part of the extension package.
Stack
| Piece | Version / choice |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, Tailwind CSS 4 |
| Internationalization | i18next + react-i18next, accept-language |
| 3D background | Three.js |
| Animation | GSAP (@gsap/react) |
| Docs rendering | marked (Markdown), shiki (code highlighting), github-slugger (heading ids) |
| Fonts | Alexandria, IBM Plex Sans Arabic, IBM Plex Sans, JetBrains Mono (via next/font/google) |
| Node | ≥ 20.9 |
Running it
cd website
npm install
npm run dev # http://localhost:3000 → redirects to /ar, /en or /tr
npm run build
npm start
npm run lintnext.config.ts sets turbopack.root to the website/ folder, because the
extension's own lockfile one level up would otherwise confuse Next.js about the
project root.
Structure
website/
├── app/
│ ├── [locale]/
│ │ ├── layout.tsx fonts, <html lang/dir>, metadata, theme bootstrap
│ │ ├── page.tsx renders HomePageContent for the locale
│ │ ├── docs/page.tsx docs home (cards per group)
│ │ ├── docs/[slug]/page.tsx one doc page
│ │ └── globals.css
│ ├── i18n/
│ │ ├── settings.js languages, fallback, RTL list, cookie name
│ │ ├── index.js server-side getTranslation()
│ │ └── locales/{ar,en,tr}/translation.json
│ └── icon.png
├── components/
│ ├── ForPages/Home/ one folder per page section (see below)
│ ├── ForPages/Docs/ DocsShell, DocsSidebar, DocArticle, DocToc, docs.css
│ └── Global/ Navbar, LanguageSwitcher, Footer, ThemeToggle,
│ BackToTop, CopyCommandButton, RichText, Icons
├── lib/constants.ts Marketplace URL, extension id, install command,
│ EXTENSION_VERSION, theme storage key
├── lib/docs.ts reads and renders docs/*.md (build time)
├── proxy.ts locale redirect (Next.js 16 "proxy", formerly middleware)
└── public/images/Page sections
components/ForPages/Home/HomePageContent.tsx renders, in order:
| Section | Folder | Content |
|---|---|---|
| Background | Scene/ | Animated 3D Git-graph scene (Three.js) |
| Hero | Hero/ | Title, typing install command, tilt card |
| Vibe coding | VibeCoding/ | Demo of findings on AI-generated code |
| Problem | Problem/ | Why blame-based attribution is unfair (legacy-file example) |
| Pipeline | Pipeline/ | The analysis steps |
| Scoring | Scoring/ | The score formula, sample developer card |
| Rules | Rules/ | The 14 rules |
| Usage | Usage/ | Extension vs. CLI tabs |
| Audience | Audience/ | Who it is for |
| Privacy | Privacy/ | Local-only guarantees |
| Limits | Limits/ | Honest limitations |
| Roadmap | Roadmap/ | Planned work |
| Final CTA | FinalCta/ | Install call-to-action |
Motion/ScrollAnimations.tsx adds GSAP scroll effects.
Documentation pages
The navbar's Documentation button opens /<locale>/docs. The pages are
built from the repository's docs/ folder at build time (fully static).
| Route | Content |
|---|---|
/<locale>/docs | Docs home: a highlighted card for the customization guide, then one card per doc, grouped like docs/README.md |
/<locale>/docs/<slug> | One doc: sidebar, article, "On this page", previous/next |
The slug is the file name without its number: 08-scoring.md → /en/docs/scoring.
Where the content comes from (lib/docs.ts):
- The list, order, groups, titles and summaries are parsed from the tables in
docs/README.md(ordocs/<locale>/README.md), so adding a row there adds a page. /ar/...readsdocs/ar/,/tr/...readsdocs/tr/,/en/...readsdocs/. A missing translation falls back to English (shown left-to-right).- The
docs/folder is found next towebsite/whether the server is started fromwebsite/or from the repository root. SetCLEANLENS_DOCS_DIRto override. Deployments must include thedocs/folder at build time.
Rendering:
- Headings get ids with
github-slugger; translated headings reuse the English ids by position (see 15). - Links to other docs become site links; links to repository files (source code, top-level README) are shown as plain text because the repository is private.
- Code blocks are highlighted with
shikiin light and dark themes and have a Copy button. Tables scroll horizontally on small screens. Blockquotes render as callouts.
Interface: sidebar with search (press / or Ctrl/Cmd+K; matches titles,
summaries and section headings), an "On this page" list that follows the scroll,
previous/next links, a mobile drawer below 960 px, and right-to-left layout for
Arabic. UI labels are in docs.* keys of the translation files.
Languages
- Supported: Arabic (
ar, default and fallback, RTL), English (en), Turkish (tr). - All text lives in
app/i18n/locales/<lng>/translation.json. dirFor(lng)setsdir="rtl"for Arabic.- Rich text in translations uses tags mapped in
components/Global/RichText/RichText.tsx:<hl>(highlight),<code>,<kbd>,<mk>(Marketplace link), and others.
Locale routing (proxy.ts)
For any path without a locale prefix, the visitor is redirected to a locale
chosen from, in order: the i18next cookie → the Accept-Language header →
Arabic. When a page is visited under a locale, that locale is saved in the cookie.
Static assets (_next, images, assets, favicon.ico, …) are not redirected.
Adding a language
- Add the code to
languagesinapp/i18n/settings.js(and tortlLanguagesif RTL). - Create
app/i18n/locales/<code>/translation.jsonwith the same keys asen. - Add its labels to
LABELSandSHORTincomponents/Global/Navbar/LanguageSwitcher.tsx(the switcher lists every entry oflanguages). - Add the font subset in
app/[locale]/layout.tsxif the script needs one.
Theme
Light is the default. The visitor's choice is stored in localStorage under
cleanlens-theme (THEME_KEY) and applied by the layout before paint, to avoid
a flash of the wrong theme.
Release checklist for the site
- Update
EXTENSION_VERSIONinlib/constants.tsto matchpackage.json. - If rules, scoring or limitations changed, update the matching sections in all three translation files.