Files
teknav-bak/app/NEXT-PLAN-O8.md
T
arsam e9cd12dda6 Import remaining project documentation and reference notes
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
2026-09-18 23:33:04 +03:30

8.0 KiB

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:
    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.


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.