Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
112 lines
8.0 KiB
Markdown
112 lines
8.0 KiB
Markdown
# Teknav Improvement Plan — Performance, SEO depth, Trust
|
|
|
|
## Context
|
|
|
|
Teknav has a mature, high-performing SEO/content engine (an article ranks #1 on Google above Wikipedia). The remaining growth leverage is **not** more features — it's (1) shipping far less JavaScript to readers, (2) conservative, additive SEO depth that won't risk current rankings, and (3) light trust/footer polish on top of footer links the user already added.
|
|
|
|
Investigation confirmed the core problem: the reader SPA ships **one undivided app bundle**. `vite.config.js:42` `manualChunks` only splits `node_modules`. There is **zero** route- or component-level splitting (`React.lazy`/`Suspense` appear nowhere). Concretely:
|
|
- `teknav-diagrams.jsx` imports `three` and is **statically imported by every page** (`teknav-home.jsx:5`, `teknav-articles.jsx:9`, `teknav-pages.jsx:14`, `teknav-en.jsx:5`, `teknav-admin.jsx:9`) → three.js downloads on the homepage for every visitor.
|
|
- `src/components/knowledge-graph.jsx` imports `d3` (also heavy).
|
|
- `teknav-admin.jsx` (full CMS + editor) is statically imported in `teknav-app.jsx:13` → admin code ships to every reader.
|
|
|
|
Goal: cut reader bundle weight (better LCP on Iranian mobile networks → better engagement and rankings) without touching the zero-CDN rule, RTL, or the static-prerender pipeline.
|
|
|
|
Sequencing decision (from user): **measure first**. Web push is **out of scope**. SEO changes must be additive and non-destructive.
|
|
|
|
---
|
|
|
|
## Phase 0 — Baseline measurement (do first, no code changes)
|
|
|
|
Establish numbers so we fix what hurts and can prove the gain.
|
|
- Build prod: `npm run build`, then `npm run preview` (port 4173).
|
|
- Bundle baseline: inspect `dist/assets/*.js` sizes (sourcemaps already on, `vite.config.js:26`); record entry size and which chunks contain `three`/`d3`/admin. (Optional: add a one-off `rollup-plugin-visualizer` run locally — do **not** commit it.)
|
|
- Lighthouse (mobile, throttled) on `/`, a `/article/<slug>`, and `/` again warm — capture LCP, TBT, total JS transfer.
|
|
- Record results at top of this file as the "before" row.
|
|
|
|
**Gate:** the splitting work below targets the chunks Phase 0 proves are largest. If `three`/admin are not in the entry, re-scope.
|
|
|
|
---
|
|
|
|
## Phase 1 — Performance / bundle (primary work)
|
|
|
|
### 1a. Lazy-load the heavy visualization components
|
|
Create thin lazy wrappers so the 5 existing import sites change minimally and `three`/`d3` move into on-demand chunks:
|
|
- New `src/components/lazy-diagram.jsx`:
|
|
```jsx
|
|
import { lazy, Suspense } from 'react';
|
|
const DiagramRenderer = lazy(() =>
|
|
import('./teknav-diagrams.jsx').then(m => ({ default: m.DiagramRenderer })));
|
|
export function LazyDiagram(props) {
|
|
return <Suspense fallback={<div className="tek-diagram-skeleton" aria-hidden="true" />}>
|
|
<DiagramRenderer {...props} /></Suspense>;
|
|
}
|
|
```
|
|
(Note: `teknav-diagrams.jsx` exports `DiagramRenderer` as a **named** export — wrapper maps it to `default`.)
|
|
- Replace the 5 static `import { DiagramRenderer }` sites (`teknav-home.jsx`, `teknav-articles.jsx`, `teknav-pages.jsx`, `teknav-en.jsx`, `teknav-admin.jsx`) with `LazyDiagram`.
|
|
- Same pattern for `knowledge-graph.jsx` (d3) wherever it's mounted.
|
|
- Add a small `.tek-diagram-skeleton` style to `src/styles/global.css` reusing existing tokens (no new animation primitives — reuse the shared keyframes per CLAUDE.md).
|
|
|
|
### 1b. Route-split admin/editor out of the reader path
|
|
- In `teknav-app.jsx`, convert the heavy, reader-irrelevant routes to `React.lazy`: `AdminPanel`, `WriterWorkspace` (both from `teknav-admin.jsx:13`), and `LoginPage`. Keep reader-critical pages (`HomePage`, `ArticleDetail`) eagerly imported to avoid hydration jank.
|
|
- Wrap `<main>{renderPage()}</main>` (`teknav-app.jsx:195`) in a single `<Suspense fallback={...}>`.
|
|
- This removes the entire CMS/editor graph from the default reader download.
|
|
|
|
### 1c. manualChunks hygiene
|
|
- Extend `manualChunks` (`vite.config.js:42`) so `three` and `d3` get their own named chunks (`vendor-three`, `vendor-d3`). Once 1a makes them dynamic imports, Rollup splits them automatically; the named chunks just keep caching stable across deploys.
|
|
|
|
### 1d. Below-the-fold deferral on article page (measurement-gated)
|
|
- In `teknav-articles.jsx`, lazy-mount non-critical widgets (comments, Q&A, related, semantic suggestions) so they don't block first render. Only do the ones Phase 0 shows are costly.
|
|
|
|
### 1e. Font delivery (small, safe)
|
|
- Audit `@font-face` in `src/styles/global.css`: ensure `font-display: swap` and `<link rel="preload">` for the primary Vazirmatn/PelakFA weights only. Self-hosted already (zero-CDN preserved). No subsetting unless Phase 0 flags fonts as a major cost.
|
|
|
|
---
|
|
|
|
## Phase 2 — SEO depth (conservative, additive only)
|
|
|
|
Premise from user: SEO already works — only add what helps and **cannot** ruin current results. So: no changes to existing article HTML, schema types, canonical/hreflang rules, or sitemap structure (all already correct; stories/compare/directory confirmed present in `generate-seo.mjs`).
|
|
|
|
Candidate additive wins (each independently revertible; pick after Phase 0/audit):
|
|
- **Related-article internal links at the component level** (render-time in `teknav-articles.jsx`), NOT by mutating stored content HTML — strengthens topic-cluster interlinking with zero risk to existing pages. Verify whether a related block already exists before adding.
|
|
- **Breadcrumb JSON-LD** via `src/lib/seo.jsx` on article/category/glossary pages if not already emitted (purely additive structured data).
|
|
- Re-run `npm run check:seo` as a gate after any content-adjacent change (CLAUDE.md invariant).
|
|
- **Explicitly NOT extending `link-engine.ts`** to rewrite published content inline (risk of altering ranked pages). Leave glossary autolinking as-is.
|
|
|
|
If the audit shows nothing safe to add, Phase 2 is intentionally a no-op — that is an acceptable outcome.
|
|
|
|
---
|
|
|
|
## Phase 3 — Trust / footer (light)
|
|
|
|
User already added the footer links. Scope here is only:
|
|
- Verify each footer link resolves in `renderPage` (`teknav-app.jsx:146`) — report any that 404.
|
|
- Optionally add an **Editorial Guidelines** (اصول تحریریه) page using the existing `LegalPage`/`LegalSection` pattern (`teknav-pages.jsx`) + route, since it's the highest-value missing E-E-A-T page for tech content. Confirm with user before building.
|
|
|
|
---
|
|
|
|
## Files to modify (summary)
|
|
|
|
| File | Change |
|
|
|---|---|
|
|
| `src/components/lazy-diagram.jsx` (new) | Lazy wrapper for `DiagramRenderer` |
|
|
| `teknav-home/articles/pages/en/admin.jsx` | Swap static `DiagramRenderer` → `LazyDiagram` |
|
|
| `src/components/knowledge-graph.jsx` consumers | Lazy-mount d3 graph |
|
|
| `teknav-app.jsx` | `React.lazy` admin/writer/login + `<Suspense>` wrap |
|
|
| `vite.config.js` | `vendor-three` / `vendor-d3` manualChunks |
|
|
| `teknav-articles.jsx` | Defer below-the-fold widgets; optional related-links block |
|
|
| `src/styles/global.css` | Diagram skeleton style; font-display/preload audit |
|
|
| `src/lib/seo.jsx` | Breadcrumb JSON-LD (if absent) |
|
|
| `teknav-pages.jsx` + `teknav-app.jsx` | (Optional) Editorial Guidelines page + route |
|
|
|
|
## Constraints honored
|
|
Zero CDN (no new external assets); Persian-first RTL; reuse `global.css` keyframes/utilities; never edit `dist/*` by hand; static-prerender pipeline unchanged (diagrams are already client-rendered, so lazy-loading is no SEO regression).
|
|
|
|
## Verification (end-to-end)
|
|
1. `npm run build` succeeds; `check-no-cdn` and `check-responsive` pass.
|
|
2. Confirm split chunks exist: `three`/`d3`/admin no longer in the main entry `dist/assets/*.js` (compare to Phase 0 baseline).
|
|
3. `npm run preview`: load `/` and an `/article/<slug>` — diagrams still render after hydration; no console errors; Suspense fallbacks behave.
|
|
4. Re-run Lighthouse mobile on the same 3 URLs; record "after" row and verify LCP/TBT/JS-transfer improvement.
|
|
5. `npm run check:seo` clean; spot-check a prerendered `dist/article/<slug>/index.html` still has its content + schema.
|
|
6. `npm run compress-dist` produces `.gz`/`.br` for the new chunks (runs in pipeline).
|
|
7. Visit each footer link; confirm no 404s.
|