@wazir-dev/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (629) hide show
  1. package/AGENTS.md +111 -0
  2. package/CHANGELOG.md +14 -0
  3. package/CONTRIBUTING.md +101 -0
  4. package/LICENSE +21 -0
  5. package/README.md +314 -0
  6. package/assets/composition-engine.mmd +34 -0
  7. package/assets/demo-script.sh +17 -0
  8. package/assets/logo-dark.svg +14 -0
  9. package/assets/logo.svg +14 -0
  10. package/assets/pipeline.mmd +39 -0
  11. package/assets/record-demo.sh +51 -0
  12. package/docs/README.md +51 -0
  13. package/docs/adapters/context-mode.md +60 -0
  14. package/docs/concepts/architecture.md +87 -0
  15. package/docs/concepts/artifact-model.md +60 -0
  16. package/docs/concepts/composition-engine.md +36 -0
  17. package/docs/concepts/indexing-and-recall.md +160 -0
  18. package/docs/concepts/observability.md +41 -0
  19. package/docs/concepts/roles-and-workflows.md +59 -0
  20. package/docs/concepts/terminology-policy.md +27 -0
  21. package/docs/getting-started/01-installation.md +78 -0
  22. package/docs/getting-started/02-first-run.md +102 -0
  23. package/docs/getting-started/03-adding-to-project.md +15 -0
  24. package/docs/getting-started/04-host-setup.md +15 -0
  25. package/docs/guides/ci-integration.md +15 -0
  26. package/docs/guides/creating-skills.md +15 -0
  27. package/docs/guides/expertise-module-authoring.md +15 -0
  28. package/docs/guides/hook-development.md +15 -0
  29. package/docs/guides/memory-and-learnings.md +34 -0
  30. package/docs/guides/multi-host-export.md +15 -0
  31. package/docs/guides/troubleshooting.md +101 -0
  32. package/docs/guides/writing-custom-roles.md +15 -0
  33. package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
  34. package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
  35. package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
  36. package/docs/readmes/INDEX.md +99 -0
  37. package/docs/readmes/features/expertise/README.md +171 -0
  38. package/docs/readmes/features/exports/README.md +222 -0
  39. package/docs/readmes/features/hooks/README.md +103 -0
  40. package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
  41. package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
  42. package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
  43. package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
  44. package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
  45. package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
  46. package/docs/readmes/features/hooks/session-start.md +119 -0
  47. package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
  48. package/docs/readmes/features/roles/README.md +157 -0
  49. package/docs/readmes/features/roles/clarifier.md +152 -0
  50. package/docs/readmes/features/roles/content-author.md +190 -0
  51. package/docs/readmes/features/roles/designer.md +193 -0
  52. package/docs/readmes/features/roles/executor.md +184 -0
  53. package/docs/readmes/features/roles/learner.md +210 -0
  54. package/docs/readmes/features/roles/planner.md +182 -0
  55. package/docs/readmes/features/roles/researcher.md +164 -0
  56. package/docs/readmes/features/roles/reviewer.md +184 -0
  57. package/docs/readmes/features/roles/specifier.md +162 -0
  58. package/docs/readmes/features/roles/verifier.md +215 -0
  59. package/docs/readmes/features/schemas/README.md +178 -0
  60. package/docs/readmes/features/skills/README.md +63 -0
  61. package/docs/readmes/features/skills/brainstorming.md +96 -0
  62. package/docs/readmes/features/skills/debugging.md +148 -0
  63. package/docs/readmes/features/skills/design.md +120 -0
  64. package/docs/readmes/features/skills/prepare-next.md +109 -0
  65. package/docs/readmes/features/skills/run-audit.md +159 -0
  66. package/docs/readmes/features/skills/scan-project.md +109 -0
  67. package/docs/readmes/features/skills/self-audit.md +176 -0
  68. package/docs/readmes/features/skills/tdd.md +137 -0
  69. package/docs/readmes/features/skills/using-skills.md +92 -0
  70. package/docs/readmes/features/skills/verification.md +120 -0
  71. package/docs/readmes/features/skills/writing-plans.md +104 -0
  72. package/docs/readmes/features/tooling/README.md +320 -0
  73. package/docs/readmes/features/workflows/README.md +186 -0
  74. package/docs/readmes/features/workflows/author.md +181 -0
  75. package/docs/readmes/features/workflows/clarify.md +154 -0
  76. package/docs/readmes/features/workflows/design-review.md +171 -0
  77. package/docs/readmes/features/workflows/design.md +169 -0
  78. package/docs/readmes/features/workflows/discover.md +162 -0
  79. package/docs/readmes/features/workflows/execute.md +173 -0
  80. package/docs/readmes/features/workflows/learn.md +167 -0
  81. package/docs/readmes/features/workflows/plan-review.md +165 -0
  82. package/docs/readmes/features/workflows/plan.md +170 -0
  83. package/docs/readmes/features/workflows/prepare-next.md +167 -0
  84. package/docs/readmes/features/workflows/review.md +169 -0
  85. package/docs/readmes/features/workflows/run-audit.md +191 -0
  86. package/docs/readmes/features/workflows/spec-challenge.md +159 -0
  87. package/docs/readmes/features/workflows/specify.md +160 -0
  88. package/docs/readmes/features/workflows/verify.md +177 -0
  89. package/docs/readmes/packages/README.md +50 -0
  90. package/docs/readmes/packages/ajv.md +117 -0
  91. package/docs/readmes/packages/context-mode.md +118 -0
  92. package/docs/readmes/packages/gray-matter.md +116 -0
  93. package/docs/readmes/packages/node-test.md +137 -0
  94. package/docs/readmes/packages/yaml.md +112 -0
  95. package/docs/reference/configuration-reference.md +159 -0
  96. package/docs/reference/expertise-index.md +52 -0
  97. package/docs/reference/git-flow.md +43 -0
  98. package/docs/reference/hooks.md +87 -0
  99. package/docs/reference/host-exports.md +50 -0
  100. package/docs/reference/launch-checklist.md +172 -0
  101. package/docs/reference/marketplace-listings.md +76 -0
  102. package/docs/reference/release-process.md +34 -0
  103. package/docs/reference/roles-reference.md +77 -0
  104. package/docs/reference/skills.md +33 -0
  105. package/docs/reference/templates.md +29 -0
  106. package/docs/reference/tooling-cli.md +94 -0
  107. package/docs/truth-claims.yaml +222 -0
  108. package/expertise/PROGRESS.md +63 -0
  109. package/expertise/README.md +18 -0
  110. package/expertise/antipatterns/PROGRESS.md +56 -0
  111. package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
  112. package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
  113. package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
  114. package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
  115. package/expertise/antipatterns/backend/index.md +24 -0
  116. package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
  117. package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
  118. package/expertise/antipatterns/code/async-antipatterns.md +622 -0
  119. package/expertise/antipatterns/code/code-smells.md +1186 -0
  120. package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
  121. package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
  122. package/expertise/antipatterns/code/index.md +27 -0
  123. package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
  124. package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
  125. package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
  126. package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
  127. package/expertise/antipatterns/design/dark-patterns.md +1121 -0
  128. package/expertise/antipatterns/design/index.md +22 -0
  129. package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
  130. package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
  131. package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
  132. package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
  133. package/expertise/antipatterns/frontend/index.md +23 -0
  134. package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
  135. package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
  136. package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
  137. package/expertise/antipatterns/index.md +31 -0
  138. package/expertise/antipatterns/performance/index.md +20 -0
  139. package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
  140. package/expertise/antipatterns/performance/premature-optimization.md +623 -0
  141. package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
  142. package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
  143. package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
  144. package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
  145. package/expertise/antipatterns/process/index.md +23 -0
  146. package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
  147. package/expertise/antipatterns/security/index.md +20 -0
  148. package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
  149. package/expertise/antipatterns/security/security-theater.md +843 -0
  150. package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
  151. package/expertise/architecture/PROGRESS.md +70 -0
  152. package/expertise/architecture/data/caching-architecture.md +671 -0
  153. package/expertise/architecture/data/data-consistency.md +574 -0
  154. package/expertise/architecture/data/data-modeling.md +536 -0
  155. package/expertise/architecture/data/event-streams-and-queues.md +634 -0
  156. package/expertise/architecture/data/index.md +25 -0
  157. package/expertise/architecture/data/search-architecture.md +663 -0
  158. package/expertise/architecture/data/sql-vs-nosql.md +708 -0
  159. package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
  160. package/expertise/architecture/decisions/build-vs-buy.md +616 -0
  161. package/expertise/architecture/decisions/index.md +23 -0
  162. package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
  163. package/expertise/architecture/decisions/technology-selection.md +616 -0
  164. package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
  165. package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
  166. package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
  167. package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
  168. package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
  169. package/expertise/architecture/distributed/index.md +25 -0
  170. package/expertise/architecture/distributed/saga-pattern.md +797 -0
  171. package/expertise/architecture/foundations/architectural-thinking.md +460 -0
  172. package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
  173. package/expertise/architecture/foundations/design-principles-solid.md +649 -0
  174. package/expertise/architecture/foundations/domain-driven-design.md +719 -0
  175. package/expertise/architecture/foundations/index.md +25 -0
  176. package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
  177. package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
  178. package/expertise/architecture/index.md +34 -0
  179. package/expertise/architecture/integration/api-design-graphql.md +638 -0
  180. package/expertise/architecture/integration/api-design-grpc.md +804 -0
  181. package/expertise/architecture/integration/api-design-rest.md +892 -0
  182. package/expertise/architecture/integration/index.md +25 -0
  183. package/expertise/architecture/integration/third-party-integration.md +795 -0
  184. package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
  185. package/expertise/architecture/integration/websockets-realtime.md +791 -0
  186. package/expertise/architecture/mobile-architecture/index.md +22 -0
  187. package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
  188. package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
  189. package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
  190. package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
  191. package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
  192. package/expertise/architecture/patterns/event-driven.md +797 -0
  193. package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
  194. package/expertise/architecture/patterns/index.md +27 -0
  195. package/expertise/architecture/patterns/layered-architecture.md +736 -0
  196. package/expertise/architecture/patterns/microservices.md +753 -0
  197. package/expertise/architecture/patterns/modular-monolith.md +692 -0
  198. package/expertise/architecture/patterns/monolith.md +626 -0
  199. package/expertise/architecture/patterns/plugin-architecture.md +735 -0
  200. package/expertise/architecture/patterns/serverless.md +780 -0
  201. package/expertise/architecture/scaling/database-scaling.md +615 -0
  202. package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
  203. package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
  204. package/expertise/architecture/scaling/index.md +24 -0
  205. package/expertise/architecture/scaling/multi-tenancy.md +800 -0
  206. package/expertise/architecture/scaling/stateless-design.md +787 -0
  207. package/expertise/backend/embedded-firmware.md +625 -0
  208. package/expertise/backend/go.md +853 -0
  209. package/expertise/backend/index.md +24 -0
  210. package/expertise/backend/java-spring.md +448 -0
  211. package/expertise/backend/node-typescript.md +625 -0
  212. package/expertise/backend/python-fastapi.md +724 -0
  213. package/expertise/backend/rust.md +458 -0
  214. package/expertise/backend/solidity.md +711 -0
  215. package/expertise/composition-map.yaml +443 -0
  216. package/expertise/content/foundations/content-modeling.md +395 -0
  217. package/expertise/content/foundations/editorial-standards.md +449 -0
  218. package/expertise/content/foundations/index.md +24 -0
  219. package/expertise/content/foundations/microcopy.md +455 -0
  220. package/expertise/content/foundations/terminology-governance.md +509 -0
  221. package/expertise/content/index.md +34 -0
  222. package/expertise/content/patterns/accessibility-copy.md +518 -0
  223. package/expertise/content/patterns/index.md +24 -0
  224. package/expertise/content/patterns/notification-content.md +433 -0
  225. package/expertise/content/patterns/sample-content.md +486 -0
  226. package/expertise/content/patterns/state-copy.md +439 -0
  227. package/expertise/design/PROGRESS.md +58 -0
  228. package/expertise/design/disciplines/dark-mode-theming.md +577 -0
  229. package/expertise/design/disciplines/design-systems.md +595 -0
  230. package/expertise/design/disciplines/index.md +25 -0
  231. package/expertise/design/disciplines/information-architecture.md +800 -0
  232. package/expertise/design/disciplines/interaction-design.md +788 -0
  233. package/expertise/design/disciplines/responsive-design.md +552 -0
  234. package/expertise/design/disciplines/usability-testing.md +516 -0
  235. package/expertise/design/disciplines/user-research.md +792 -0
  236. package/expertise/design/foundations/accessibility-design.md +796 -0
  237. package/expertise/design/foundations/color-theory.md +797 -0
  238. package/expertise/design/foundations/iconography.md +795 -0
  239. package/expertise/design/foundations/index.md +26 -0
  240. package/expertise/design/foundations/motion-and-animation.md +653 -0
  241. package/expertise/design/foundations/rtl-design.md +585 -0
  242. package/expertise/design/foundations/spacing-and-layout.md +607 -0
  243. package/expertise/design/foundations/typography.md +800 -0
  244. package/expertise/design/foundations/visual-hierarchy.md +761 -0
  245. package/expertise/design/index.md +32 -0
  246. package/expertise/design/patterns/authentication-flows.md +474 -0
  247. package/expertise/design/patterns/content-consumption.md +789 -0
  248. package/expertise/design/patterns/data-display.md +618 -0
  249. package/expertise/design/patterns/e-commerce.md +1494 -0
  250. package/expertise/design/patterns/feedback-and-states.md +642 -0
  251. package/expertise/design/patterns/forms-and-input.md +819 -0
  252. package/expertise/design/patterns/gamification.md +801 -0
  253. package/expertise/design/patterns/index.md +31 -0
  254. package/expertise/design/patterns/microinteractions.md +449 -0
  255. package/expertise/design/patterns/navigation.md +800 -0
  256. package/expertise/design/patterns/notifications.md +705 -0
  257. package/expertise/design/patterns/onboarding.md +700 -0
  258. package/expertise/design/patterns/search-and-filter.md +601 -0
  259. package/expertise/design/patterns/settings-and-preferences.md +768 -0
  260. package/expertise/design/patterns/social-and-community.md +748 -0
  261. package/expertise/design/platforms/desktop-native.md +612 -0
  262. package/expertise/design/platforms/index.md +25 -0
  263. package/expertise/design/platforms/mobile-android.md +825 -0
  264. package/expertise/design/platforms/mobile-cross-platform.md +983 -0
  265. package/expertise/design/platforms/mobile-ios.md +699 -0
  266. package/expertise/design/platforms/tablet.md +794 -0
  267. package/expertise/design/platforms/web-dashboard.md +790 -0
  268. package/expertise/design/platforms/web-responsive.md +550 -0
  269. package/expertise/design/psychology/behavioral-nudges.md +449 -0
  270. package/expertise/design/psychology/cognitive-load.md +1191 -0
  271. package/expertise/design/psychology/error-psychology.md +778 -0
  272. package/expertise/design/psychology/index.md +22 -0
  273. package/expertise/design/psychology/persuasive-design.md +736 -0
  274. package/expertise/design/psychology/user-mental-models.md +623 -0
  275. package/expertise/design/tooling/open-pencil.md +266 -0
  276. package/expertise/frontend/angular.md +1073 -0
  277. package/expertise/frontend/desktop-electron.md +546 -0
  278. package/expertise/frontend/flutter.md +782 -0
  279. package/expertise/frontend/index.md +27 -0
  280. package/expertise/frontend/native-android.md +409 -0
  281. package/expertise/frontend/native-ios.md +490 -0
  282. package/expertise/frontend/react-native.md +1160 -0
  283. package/expertise/frontend/react.md +808 -0
  284. package/expertise/frontend/vue.md +1089 -0
  285. package/expertise/humanize/domain-rules-code.md +79 -0
  286. package/expertise/humanize/domain-rules-content.md +67 -0
  287. package/expertise/humanize/domain-rules-technical-docs.md +56 -0
  288. package/expertise/humanize/index.md +35 -0
  289. package/expertise/humanize/self-audit-checklist.md +87 -0
  290. package/expertise/humanize/sentence-patterns.md +218 -0
  291. package/expertise/humanize/vocabulary-blacklist.md +105 -0
  292. package/expertise/i18n/PROGRESS.md +65 -0
  293. package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
  294. package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
  295. package/expertise/i18n/advanced/complex-scripts.md +30 -0
  296. package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
  297. package/expertise/i18n/advanced/testing-i18n.md +28 -0
  298. package/expertise/i18n/content/content-adaptation.md +23 -0
  299. package/expertise/i18n/content/locale-specific-formatting.md +23 -0
  300. package/expertise/i18n/content/machine-translation-integration.md +28 -0
  301. package/expertise/i18n/content/translation-management.md +29 -0
  302. package/expertise/i18n/foundations/date-time-calendars.md +67 -0
  303. package/expertise/i18n/foundations/i18n-architecture.md +272 -0
  304. package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
  305. package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
  306. package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
  307. package/expertise/i18n/foundations/string-externalization.md +236 -0
  308. package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
  309. package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
  310. package/expertise/i18n/index.md +38 -0
  311. package/expertise/i18n/platform/backend-i18n.md +31 -0
  312. package/expertise/i18n/platform/flutter-i18n.md +148 -0
  313. package/expertise/i18n/platform/native-android-i18n.md +36 -0
  314. package/expertise/i18n/platform/native-ios-i18n.md +36 -0
  315. package/expertise/i18n/platform/react-i18n.md +103 -0
  316. package/expertise/i18n/platform/web-css-i18n.md +81 -0
  317. package/expertise/i18n/rtl/arabic-specific.md +175 -0
  318. package/expertise/i18n/rtl/hebrew-specific.md +149 -0
  319. package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
  320. package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
  321. package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
  322. package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
  323. package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
  324. package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
  325. package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
  326. package/expertise/i18n/rtl/rtl-typography.md +160 -0
  327. package/expertise/index.md +113 -0
  328. package/expertise/index.yaml +216 -0
  329. package/expertise/infrastructure/cloud-aws.md +597 -0
  330. package/expertise/infrastructure/cloud-gcp.md +599 -0
  331. package/expertise/infrastructure/cybersecurity.md +816 -0
  332. package/expertise/infrastructure/database-mongodb.md +447 -0
  333. package/expertise/infrastructure/database-postgres.md +400 -0
  334. package/expertise/infrastructure/devops-cicd.md +787 -0
  335. package/expertise/infrastructure/index.md +27 -0
  336. package/expertise/performance/PROGRESS.md +50 -0
  337. package/expertise/performance/backend/api-latency.md +1204 -0
  338. package/expertise/performance/backend/background-jobs.md +506 -0
  339. package/expertise/performance/backend/connection-pooling.md +1209 -0
  340. package/expertise/performance/backend/database-query-optimization.md +515 -0
  341. package/expertise/performance/backend/index.md +23 -0
  342. package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
  343. package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
  344. package/expertise/performance/foundations/caching-strategies.md +489 -0
  345. package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
  346. package/expertise/performance/foundations/index.md +24 -0
  347. package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
  348. package/expertise/performance/foundations/memory-management.md +964 -0
  349. package/expertise/performance/foundations/performance-budgets.md +1314 -0
  350. package/expertise/performance/index.md +31 -0
  351. package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
  352. package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
  353. package/expertise/performance/infrastructure/index.md +22 -0
  354. package/expertise/performance/infrastructure/load-balancing.md +1081 -0
  355. package/expertise/performance/infrastructure/observability.md +1079 -0
  356. package/expertise/performance/mobile/index.md +23 -0
  357. package/expertise/performance/mobile/mobile-animations.md +544 -0
  358. package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
  359. package/expertise/performance/mobile/mobile-network.md +452 -0
  360. package/expertise/performance/mobile/mobile-rendering.md +599 -0
  361. package/expertise/performance/mobile/mobile-startup-time.md +505 -0
  362. package/expertise/performance/platform-specific/flutter-performance.md +647 -0
  363. package/expertise/performance/platform-specific/index.md +22 -0
  364. package/expertise/performance/platform-specific/node-performance.md +1307 -0
  365. package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
  366. package/expertise/performance/platform-specific/react-performance.md +1403 -0
  367. package/expertise/performance/web/bundle-optimization.md +1239 -0
  368. package/expertise/performance/web/image-and-media.md +636 -0
  369. package/expertise/performance/web/index.md +24 -0
  370. package/expertise/performance/web/network-optimization.md +1133 -0
  371. package/expertise/performance/web/rendering-performance.md +1098 -0
  372. package/expertise/performance/web/ssr-and-hydration.md +918 -0
  373. package/expertise/performance/web/web-vitals.md +1374 -0
  374. package/expertise/quality/accessibility.md +985 -0
  375. package/expertise/quality/evidence-based-verification.md +499 -0
  376. package/expertise/quality/index.md +24 -0
  377. package/expertise/quality/ml-model-audit.md +614 -0
  378. package/expertise/quality/performance.md +600 -0
  379. package/expertise/quality/testing-api.md +891 -0
  380. package/expertise/quality/testing-mobile.md +496 -0
  381. package/expertise/quality/testing-web.md +849 -0
  382. package/expertise/security/PROGRESS.md +54 -0
  383. package/expertise/security/agentic-identity.md +540 -0
  384. package/expertise/security/compliance-frameworks.md +601 -0
  385. package/expertise/security/data/data-encryption.md +364 -0
  386. package/expertise/security/data/data-privacy-gdpr.md +692 -0
  387. package/expertise/security/data/database-security.md +1171 -0
  388. package/expertise/security/data/index.md +22 -0
  389. package/expertise/security/data/pii-handling.md +531 -0
  390. package/expertise/security/foundations/authentication.md +1041 -0
  391. package/expertise/security/foundations/authorization.md +603 -0
  392. package/expertise/security/foundations/cryptography.md +1001 -0
  393. package/expertise/security/foundations/index.md +25 -0
  394. package/expertise/security/foundations/owasp-top-10.md +1354 -0
  395. package/expertise/security/foundations/secrets-management.md +1217 -0
  396. package/expertise/security/foundations/secure-sdlc.md +700 -0
  397. package/expertise/security/foundations/supply-chain-security.md +698 -0
  398. package/expertise/security/index.md +31 -0
  399. package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
  400. package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
  401. package/expertise/security/infrastructure/container-security.md +721 -0
  402. package/expertise/security/infrastructure/incident-response.md +1295 -0
  403. package/expertise/security/infrastructure/index.md +24 -0
  404. package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
  405. package/expertise/security/infrastructure/network-security.md +1337 -0
  406. package/expertise/security/mobile/index.md +23 -0
  407. package/expertise/security/mobile/mobile-android-security.md +1218 -0
  408. package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
  409. package/expertise/security/mobile/mobile-data-storage.md +1265 -0
  410. package/expertise/security/mobile/mobile-ios-security.md +1401 -0
  411. package/expertise/security/mobile/mobile-network-security.md +1520 -0
  412. package/expertise/security/smart-contract-security.md +594 -0
  413. package/expertise/security/testing/index.md +22 -0
  414. package/expertise/security/testing/penetration-testing.md +1258 -0
  415. package/expertise/security/testing/security-code-review.md +1765 -0
  416. package/expertise/security/testing/threat-modeling.md +1074 -0
  417. package/expertise/security/testing/vulnerability-scanning.md +1062 -0
  418. package/expertise/security/web/api-security.md +586 -0
  419. package/expertise/security/web/cors-and-headers.md +433 -0
  420. package/expertise/security/web/csrf.md +562 -0
  421. package/expertise/security/web/file-upload.md +1477 -0
  422. package/expertise/security/web/index.md +25 -0
  423. package/expertise/security/web/injection.md +1375 -0
  424. package/expertise/security/web/session-management.md +1101 -0
  425. package/expertise/security/web/xss.md +1158 -0
  426. package/exports/README.md +17 -0
  427. package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
  428. package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
  429. package/exports/hosts/claude/.claude/agents/designer.md +55 -0
  430. package/exports/hosts/claude/.claude/agents/executor.md +55 -0
  431. package/exports/hosts/claude/.claude/agents/learner.md +51 -0
  432. package/exports/hosts/claude/.claude/agents/planner.md +53 -0
  433. package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
  434. package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
  435. package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
  436. package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
  437. package/exports/hosts/claude/.claude/commands/author.md +42 -0
  438. package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
  439. package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
  440. package/exports/hosts/claude/.claude/commands/design.md +44 -0
  441. package/exports/hosts/claude/.claude/commands/discover.md +37 -0
  442. package/exports/hosts/claude/.claude/commands/execute.md +48 -0
  443. package/exports/hosts/claude/.claude/commands/learn.md +38 -0
  444. package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
  445. package/exports/hosts/claude/.claude/commands/plan.md +39 -0
  446. package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
  447. package/exports/hosts/claude/.claude/commands/review.md +40 -0
  448. package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
  449. package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
  450. package/exports/hosts/claude/.claude/commands/specify.md +38 -0
  451. package/exports/hosts/claude/.claude/commands/verify.md +37 -0
  452. package/exports/hosts/claude/.claude/settings.json +34 -0
  453. package/exports/hosts/claude/CLAUDE.md +19 -0
  454. package/exports/hosts/claude/export.manifest.json +38 -0
  455. package/exports/hosts/claude/host-package.json +67 -0
  456. package/exports/hosts/codex/AGENTS.md +19 -0
  457. package/exports/hosts/codex/export.manifest.json +38 -0
  458. package/exports/hosts/codex/host-package.json +41 -0
  459. package/exports/hosts/cursor/.cursor/hooks.json +16 -0
  460. package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
  461. package/exports/hosts/cursor/export.manifest.json +38 -0
  462. package/exports/hosts/cursor/host-package.json +42 -0
  463. package/exports/hosts/gemini/GEMINI.md +19 -0
  464. package/exports/hosts/gemini/export.manifest.json +38 -0
  465. package/exports/hosts/gemini/host-package.json +41 -0
  466. package/hooks/README.md +18 -0
  467. package/hooks/definitions/loop_cap_guard.yaml +21 -0
  468. package/hooks/definitions/post_tool_capture.yaml +24 -0
  469. package/hooks/definitions/pre_compact_summary.yaml +19 -0
  470. package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
  471. package/hooks/definitions/protected_path_write_guard.yaml +19 -0
  472. package/hooks/definitions/session_start.yaml +19 -0
  473. package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
  474. package/hooks/loop-cap-guard +17 -0
  475. package/hooks/post-tool-lint +36 -0
  476. package/hooks/protected-path-write-guard +17 -0
  477. package/hooks/session-start +41 -0
  478. package/llms-full.txt +2355 -0
  479. package/llms.txt +43 -0
  480. package/package.json +79 -0
  481. package/roles/README.md +20 -0
  482. package/roles/clarifier.md +42 -0
  483. package/roles/content-author.md +63 -0
  484. package/roles/designer.md +55 -0
  485. package/roles/executor.md +55 -0
  486. package/roles/learner.md +51 -0
  487. package/roles/planner.md +53 -0
  488. package/roles/researcher.md +43 -0
  489. package/roles/reviewer.md +54 -0
  490. package/roles/specifier.md +47 -0
  491. package/roles/verifier.md +71 -0
  492. package/schemas/README.md +24 -0
  493. package/schemas/accepted-learning.schema.json +20 -0
  494. package/schemas/author-artifact.schema.json +156 -0
  495. package/schemas/clarification.schema.json +19 -0
  496. package/schemas/design-artifact.schema.json +80 -0
  497. package/schemas/docs-claim.schema.json +18 -0
  498. package/schemas/export-manifest.schema.json +20 -0
  499. package/schemas/hook.schema.json +67 -0
  500. package/schemas/host-export-package.schema.json +18 -0
  501. package/schemas/implementation-plan.schema.json +19 -0
  502. package/schemas/proposed-learning.schema.json +19 -0
  503. package/schemas/research.schema.json +18 -0
  504. package/schemas/review.schema.json +29 -0
  505. package/schemas/run-manifest.schema.json +18 -0
  506. package/schemas/spec-challenge.schema.json +18 -0
  507. package/schemas/spec.schema.json +20 -0
  508. package/schemas/usage.schema.json +102 -0
  509. package/schemas/verification-proof.schema.json +29 -0
  510. package/schemas/wazir-manifest.schema.json +173 -0
  511. package/skills/README.md +40 -0
  512. package/skills/brainstorming/SKILL.md +77 -0
  513. package/skills/debugging/SKILL.md +50 -0
  514. package/skills/design/SKILL.md +61 -0
  515. package/skills/dispatching-parallel-agents/SKILL.md +128 -0
  516. package/skills/executing-plans/SKILL.md +70 -0
  517. package/skills/finishing-a-development-branch/SKILL.md +169 -0
  518. package/skills/humanize/SKILL.md +123 -0
  519. package/skills/init-pipeline/SKILL.md +124 -0
  520. package/skills/prepare-next/SKILL.md +20 -0
  521. package/skills/receiving-code-review/SKILL.md +123 -0
  522. package/skills/requesting-code-review/SKILL.md +105 -0
  523. package/skills/requesting-code-review/code-reviewer.md +108 -0
  524. package/skills/run-audit/SKILL.md +197 -0
  525. package/skills/scan-project/SKILL.md +41 -0
  526. package/skills/self-audit/SKILL.md +153 -0
  527. package/skills/subagent-driven-development/SKILL.md +154 -0
  528. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  529. package/skills/subagent-driven-development/implementer-prompt.md +102 -0
  530. package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  531. package/skills/tdd/SKILL.md +23 -0
  532. package/skills/using-git-worktrees/SKILL.md +163 -0
  533. package/skills/using-skills/SKILL.md +95 -0
  534. package/skills/verification/SKILL.md +22 -0
  535. package/skills/wazir/SKILL.md +463 -0
  536. package/skills/writing-plans/SKILL.md +30 -0
  537. package/skills/writing-skills/SKILL.md +157 -0
  538. package/skills/writing-skills/anthropic-best-practices.md +122 -0
  539. package/skills/writing-skills/persuasion-principles.md +50 -0
  540. package/templates/README.md +20 -0
  541. package/templates/artifacts/README.md +10 -0
  542. package/templates/artifacts/accepted-learning.md +19 -0
  543. package/templates/artifacts/accepted-learning.template.json +12 -0
  544. package/templates/artifacts/author.md +74 -0
  545. package/templates/artifacts/author.template.json +19 -0
  546. package/templates/artifacts/clarification.md +21 -0
  547. package/templates/artifacts/clarification.template.json +12 -0
  548. package/templates/artifacts/execute-notes.md +19 -0
  549. package/templates/artifacts/implementation-plan.md +21 -0
  550. package/templates/artifacts/implementation-plan.template.json +11 -0
  551. package/templates/artifacts/learning-proposal.md +19 -0
  552. package/templates/artifacts/next-run-handoff.md +21 -0
  553. package/templates/artifacts/plan-review.md +19 -0
  554. package/templates/artifacts/proposed-learning.template.json +12 -0
  555. package/templates/artifacts/research.md +21 -0
  556. package/templates/artifacts/research.template.json +12 -0
  557. package/templates/artifacts/review-findings.md +19 -0
  558. package/templates/artifacts/review.template.json +11 -0
  559. package/templates/artifacts/run-manifest.template.json +8 -0
  560. package/templates/artifacts/spec-challenge.md +19 -0
  561. package/templates/artifacts/spec-challenge.template.json +11 -0
  562. package/templates/artifacts/spec.md +21 -0
  563. package/templates/artifacts/spec.template.json +12 -0
  564. package/templates/artifacts/verification-proof.md +19 -0
  565. package/templates/artifacts/verification-proof.template.json +11 -0
  566. package/templates/examples/accepted-learning.example.json +14 -0
  567. package/templates/examples/author.example.json +152 -0
  568. package/templates/examples/clarification.example.json +15 -0
  569. package/templates/examples/docs-claim.example.json +8 -0
  570. package/templates/examples/export-manifest.example.json +7 -0
  571. package/templates/examples/host-export-package.example.json +11 -0
  572. package/templates/examples/implementation-plan.example.json +17 -0
  573. package/templates/examples/proposed-learning.example.json +13 -0
  574. package/templates/examples/research.example.json +15 -0
  575. package/templates/examples/research.example.md +6 -0
  576. package/templates/examples/review.example.json +17 -0
  577. package/templates/examples/run-manifest.example.json +9 -0
  578. package/templates/examples/spec-challenge.example.json +14 -0
  579. package/templates/examples/spec.example.json +21 -0
  580. package/templates/examples/verification-proof.example.json +21 -0
  581. package/templates/examples/wazir-manifest.example.yaml +65 -0
  582. package/templates/task-definition-schema.md +99 -0
  583. package/tooling/README.md +20 -0
  584. package/tooling/src/adapters/context-mode.js +50 -0
  585. package/tooling/src/capture/command.js +376 -0
  586. package/tooling/src/capture/store.js +99 -0
  587. package/tooling/src/capture/usage.js +270 -0
  588. package/tooling/src/checks/branches.js +50 -0
  589. package/tooling/src/checks/brand-truth.js +110 -0
  590. package/tooling/src/checks/changelog.js +231 -0
  591. package/tooling/src/checks/command-registry.js +36 -0
  592. package/tooling/src/checks/commits.js +102 -0
  593. package/tooling/src/checks/docs-drift.js +103 -0
  594. package/tooling/src/checks/docs-truth.js +201 -0
  595. package/tooling/src/checks/runtime-surface.js +156 -0
  596. package/tooling/src/cli.js +116 -0
  597. package/tooling/src/command-options.js +56 -0
  598. package/tooling/src/commands/validate.js +320 -0
  599. package/tooling/src/doctor/command.js +91 -0
  600. package/tooling/src/export/command.js +77 -0
  601. package/tooling/src/export/compiler.js +498 -0
  602. package/tooling/src/guards/loop-cap-guard.js +52 -0
  603. package/tooling/src/guards/protected-path-write-guard.js +67 -0
  604. package/tooling/src/index/command.js +152 -0
  605. package/tooling/src/index/storage.js +1061 -0
  606. package/tooling/src/index/summarizers.js +261 -0
  607. package/tooling/src/loaders.js +18 -0
  608. package/tooling/src/project-root.js +22 -0
  609. package/tooling/src/recall/command.js +225 -0
  610. package/tooling/src/schema-validator.js +30 -0
  611. package/tooling/src/state-root.js +40 -0
  612. package/tooling/src/status/command.js +71 -0
  613. package/wazir.manifest.yaml +135 -0
  614. package/workflows/README.md +19 -0
  615. package/workflows/author.md +42 -0
  616. package/workflows/clarify.md +38 -0
  617. package/workflows/design-review.md +46 -0
  618. package/workflows/design.md +44 -0
  619. package/workflows/discover.md +37 -0
  620. package/workflows/execute.md +48 -0
  621. package/workflows/learn.md +38 -0
  622. package/workflows/plan-review.md +42 -0
  623. package/workflows/plan.md +39 -0
  624. package/workflows/prepare-next.md +37 -0
  625. package/workflows/review.md +40 -0
  626. package/workflows/run-audit.md +41 -0
  627. package/workflows/spec-challenge.md +41 -0
  628. package/workflows/specify.md +38 -0
  629. package/workflows/verify.md +37 -0
@@ -0,0 +1,1374 @@
1
+ # Web Vitals — Performance Expertise Module
2
+
3
+ > Core Web Vitals (LCP, INP, CLS) are Google's user-centric performance metrics that directly impact search rankings, user engagement, and conversion rates. They measure loading performance, interactivity, and visual stability — the three pillars of user-perceived web performance.
4
+
5
+ > **Impact:** Critical
6
+ > **Applies to:** Web
7
+ > **Key metrics:** Largest Contentful Paint (LCP), Interaction to Next Paint (INP), Cumulative Layout Shift (CLS)
8
+
9
+ ---
10
+
11
+ ## Why This Matters
12
+
13
+ ### Search Ranking Impact
14
+
15
+ Core Web Vitals are a confirmed Google ranking signal since June 2021. Google evaluates
16
+ pages using the 75th percentile of real-user data collected via the Chrome User Experience
17
+ Report (CrUX). Pages failing CWV thresholds lose ranking eligibility for Top Stories
18
+ carousels and are disadvantaged in competitive SERPs. Sites have reported moving up 5-10
19
+ positions for competitive keywords solely by improving performance metrics.
20
+
21
+ As of 2025, only 48% of mobile websites and 56% of desktop websites pass all three Core
22
+ Web Vitals — meaning more than half the web still fails these thresholds, representing a
23
+ significant competitive opportunity.
24
+
25
+ ### Real Business Case Studies
26
+
27
+ | Company | Optimization | Result |
28
+ |---------|-------------|--------|
29
+ | **Vodafone** | 31% LCP improvement | 8% more sales, 15% better lead-to-visit rate, 11% better cart-to-visit rate |
30
+ | **Swappie** | 55% LCP improvement, 91% CLS improvement | 42% jump in mobile revenue |
31
+ | **Rakuten** | Full CWV optimization | 33% higher conversion rate, 53% higher revenue per visitor |
32
+ | **T-Mobile** | Page speed overhaul | 20% fewer in-site issues, 60% increase in visit-to-order rate |
33
+ | **Google Flights** | Added `fetchpriority="high"` to hero image | 700ms LCP improvement |
34
+ | **Etsy** | Priority Hints on LCP elements | 4% LCP improvement (up to 20-30% in lab) |
35
+
36
+ **Source:** web.dev/case-studies/vodafone, web.dev/case-studies/vitals-business-impact
37
+
38
+ ### The Conversion-Speed Relationship
39
+
40
+ Every 100ms improvement in page load time can boost conversion rates by up to 7%. A
41
+ healthcare provider that optimized mobile CWV scores saw a 43% increase in mobile
42
+ conversion rates. These numbers compound: for an e-commerce site doing $10M/year, a 1-second
43
+ LCP improvement could translate to $700K+ in additional revenue.
44
+
45
+ ### FID to INP Transition (March 2024)
46
+
47
+ In March 2024, Google officially replaced First Input Delay (FID) with Interaction to Next
48
+ Paint (INP). This was significant because:
49
+
50
+ - **FID** only measured the delay of the *first* interaction and ignored processing time
51
+ - **INP** captures *every* interaction (clicks, taps, key presses) throughout the full page lifecycle
52
+ - **INP** reports the worst interaction at the 75th percentile, making it far harder to game
53
+ - The transition caused a ~5 percentage point drop in mobile CWV pass rates because sites
54
+ that appeared responsive under FID had slow later interactions that INP now captures
55
+
56
+ ---
57
+
58
+ ## Performance Budgets & Targets
59
+
60
+ ### Official Thresholds
61
+
62
+ | Metric | Good | Needs Improvement | Poor |
63
+ |--------|------|-------------------|------|
64
+ | **LCP** | < 2.5s | 2.5s - 4.0s | > 4.0s |
65
+ | **INP** | < 200ms | 200ms - 500ms | > 500ms |
66
+ | **CLS** | < 0.1 | 0.1 - 0.25 | > 0.25 |
67
+
68
+ ### How Thresholds Are Evaluated
69
+
70
+ Google uses the **75th percentile** (p75) of page visits over a rolling 28-day window.
71
+ This means 75% of your visitors must experience a "Good" score for the page to pass. This
72
+ is more demanding than a median — the bottom quartile of visits drags your score down.
73
+
74
+ ### Mobile vs Desktop Differences
75
+
76
+ Mobile consistently scores worse than desktop due to:
77
+ - **CPU:** Mobile processors take 3-5x longer to parse and execute JavaScript
78
+ - **Network:** Cellular connections add 50-300ms of latency vs broadband
79
+ - **Rendering:** Smaller viewports mean different LCP elements (often text instead of images)
80
+ - **2025 data:** 48% mobile pass rate vs 56% desktop pass rate
81
+
82
+ ### Per-Metric Pass Rates (2025 Web Almanac)
83
+
84
+ | Metric | Mobile Good | Desktop Good | Mobile Poor |
85
+ |--------|------------|-------------|-------------|
86
+ | **LCP** | ~60% | ~70% | ~15% |
87
+ | **INP** | ~77% | ~85% | ~6% |
88
+ | **CLS** | ~72% | ~78% | ~11% |
89
+
90
+ INP has the highest pass rate individually, but it is the most commonly *failed* metric
91
+ when combined — 43% of sites that fail CWV fail specifically on INP.
92
+
93
+ ### Setting Internal Targets
94
+
95
+ Do not target the threshold boundary. Set internal budgets at 70-80% of the "Good"
96
+ threshold to provide a safety margin for traffic spikes and edge cases:
97
+
98
+ | Metric | Recommended Internal Budget |
99
+ |--------|---------------------------|
100
+ | **LCP** | < 1.8s (28% buffer from 2.5s) |
101
+ | **INP** | < 150ms (25% buffer from 200ms) |
102
+ | **CLS** | < 0.05 (50% buffer from 0.1) |
103
+
104
+ ---
105
+
106
+ ## Measurement & Profiling
107
+
108
+ ### Lab Tools (Synthetic / Controlled)
109
+
110
+ | Tool | What It Measures | Use Case |
111
+ |------|-----------------|----------|
112
+ | **Lighthouse** | LCP, CLS, TBT (proxy for INP) | Development-time audits, CI/CD gates |
113
+ | **Chrome DevTools Performance Panel** | LCP, CLS, INP, long tasks, layout shifts | Debugging specific interactions |
114
+ | **WebPageTest** | LCP, CLS, TBT, filmstrip, waterfall | Deep network/rendering analysis |
115
+ | **PageSpeed Insights** | Lab + Field data combined | Quick assessment with CrUX overlay |
116
+
117
+ **Important:** Lab tools cannot measure INP directly (no real user interactions). Total
118
+ Blocking Time (TBT) is used as a lab proxy — TBT correlates with INP at ~0.8 correlation.
119
+
120
+ ### Field Tools (Real User Monitoring)
121
+
122
+ | Tool | Data Source | Granularity | Update Frequency |
123
+ |------|------------|------------|-----------------|
124
+ | **CrUX (Chrome UX Report)** | Real Chrome users | Origin + URL level | Daily (API), Monthly (BigQuery) |
125
+ | **Search Console CWV Report** | CrUX data | URL groups | Rolling 28-day window |
126
+ | **PageSpeed Insights** | CrUX + Lighthouse | Per-URL | On-demand |
127
+ | **RUM providers** (SpeedCurve, DebugBear, Calibre) | Your users specifically | Per-page, per-segment | Real-time |
128
+
129
+ ### JavaScript API: web-vitals Library (v5)
130
+
131
+ The `web-vitals` library (maintained by Google Chrome team) is the standard way to collect
132
+ CWV data from real users. It uses the `PerformanceObserver` API with the `buffered` flag,
133
+ meaning it captures metrics even if loaded late.
134
+
135
+ #### Installation and Basic Usage
136
+
137
+ ```bash
138
+ npm install web-vitals
139
+ ```
140
+
141
+ ```javascript
142
+ // Basic usage — collect all three Core Web Vitals
143
+ import { onLCP, onINP, onCLS } from 'web-vitals';
144
+
145
+ function sendToAnalytics(metric) {
146
+ const body = JSON.stringify({
147
+ name: metric.name, // "LCP", "INP", or "CLS"
148
+ value: metric.value, // The metric value
149
+ rating: metric.rating, // "good", "needs-improvement", or "poor"
150
+ delta: metric.delta, // Change since last report
151
+ id: metric.id, // Unique ID for this metric instance
152
+ navigationType: metric.navigationType, // "navigate", "reload", "back-forward", etc.
153
+ });
154
+
155
+ // Use sendBeacon for reliability during page unload
156
+ if (navigator.sendBeacon) {
157
+ navigator.sendBeacon('/analytics', body);
158
+ } else {
159
+ fetch('/analytics', { body, method: 'POST', keepalive: true });
160
+ }
161
+ }
162
+
163
+ onLCP(sendToAnalytics);
164
+ onINP(sendToAnalytics);
165
+ onCLS(sendToAnalytics);
166
+ ```
167
+
168
+ #### Attribution Build (for debugging root causes)
169
+
170
+ ```javascript
171
+ // Attribution build adds diagnostic data to each metric
172
+ import { onLCP, onINP, onCLS } from 'web-vitals/attribution';
173
+
174
+ onLCP((metric) => {
175
+ console.log('LCP element:', metric.attribution.element);
176
+ console.log('LCP resource URL:', metric.attribution.url);
177
+ console.log('Time to first byte:', metric.attribution.timeToFirstByte);
178
+ console.log('Resource load delay:', metric.attribution.resourceLoadDelay);
179
+ console.log('Resource load time:', metric.attribution.resourceLoadTime);
180
+ console.log('Element render delay:', metric.attribution.elementRenderDelay);
181
+ });
182
+
183
+ onINP((metric) => {
184
+ console.log('INP event type:', metric.attribution.interactionType);
185
+ console.log('INP target:', metric.attribution.interactionTarget);
186
+ console.log('Input delay:', metric.attribution.inputDelay);
187
+ console.log('Processing duration:', metric.attribution.processingDuration);
188
+ console.log('Presentation delay:', metric.attribution.presentationDelay);
189
+ });
190
+
191
+ onCLS((metric) => {
192
+ console.log('Largest shift target:', metric.attribution.largestShiftTarget);
193
+ console.log('Largest shift value:', metric.attribution.largestShiftValue);
194
+ console.log('Largest shift time:', metric.attribution.largestShiftTime);
195
+ });
196
+ ```
197
+
198
+ #### CDN Usage (No Build Step)
199
+
200
+ ```html
201
+ <script type="module">
202
+ import { onCLS, onINP, onLCP } from 'https://unpkg.com/web-vitals@5?module';
203
+ onCLS(console.log);
204
+ onINP(console.log);
205
+ onLCP(console.log);
206
+ </script>
207
+ ```
208
+
209
+ **Critical rule:** Never call `onLCP()`, `onINP()`, or `onCLS()` more than once per page
210
+ load. Each call creates a `PerformanceObserver` instance and event listeners for the
211
+ lifetime of the page — calling them repeatedly causes memory leaks.
212
+
213
+ ### CrUX API for Automated Monitoring
214
+
215
+ ```javascript
216
+ // Query CrUX API for origin-level data
217
+ const API_KEY = 'YOUR_API_KEY';
218
+ const url = `https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=${API_KEY}`;
219
+
220
+ const response = await fetch(url, {
221
+ method: 'POST',
222
+ body: JSON.stringify({
223
+ origin: 'https://example.com',
224
+ formFactor: 'PHONE',
225
+ metrics: [
226
+ 'largest_contentful_paint',
227
+ 'interaction_to_next_paint',
228
+ 'cumulative_layout_shift'
229
+ ]
230
+ })
231
+ });
232
+
233
+ const data = await response.json();
234
+ // data.record.metrics.largest_contentful_paint.percentiles.p75
235
+ // data.record.metrics.largest_contentful_paint.histogram[0].density (good %)
236
+ ```
237
+
238
+ CrUX data is also available in BigQuery (`chrome-ux-report` project) for large-scale
239
+ analysis across millions of origins, updated monthly on the second Tuesday after collection.
240
+
241
+ ---
242
+
243
+ ## Common Bottlenecks
244
+
245
+ ### LCP Bottlenecks
246
+
247
+ LCP measures when the largest content element (image, video poster, text block, or
248
+ background image) becomes visible in the viewport.
249
+
250
+ | Bottleneck | Impact | Frequency |
251
+ |-----------|--------|-----------|
252
+ | **Slow server response (high TTFB)** | Delays everything; a 1s TTFB makes 2.5s LCP nearly impossible | ~35% of poor LCP pages |
253
+ | **Render-blocking CSS/JS** | Browser cannot render until critical CSS is parsed and blocking JS executes | ~30% of poor LCP pages |
254
+ | **Slow resource load time** | Large unoptimized images, no CDN, no compression | ~25% of poor LCP pages |
255
+ | **Client-side rendering** | Content not in HTML; must download, parse, and execute JS first | ~20% of SPA pages |
256
+ | **Resource discovery delay** | LCP image referenced in CSS or loaded via JS, invisible to preload scanner | ~15% of poor LCP pages |
257
+ | **Lazy loading above-the-fold images** | `loading="lazy"` on LCP image delays its fetch until layout | Common anti-pattern |
258
+ | **No image optimization** | Serving 2MB PNG instead of 200KB WebP/AVIF | Very common |
259
+ | **Unoptimized web fonts blocking text** | Text is the LCP element but invisible during font load (FOIT) | ~10% of text-LCP pages |
260
+
261
+ ### INP Bottlenecks
262
+
263
+ INP measures the latency from user input to the next visual update. It has three phases:
264
+ **input delay** (waiting for main thread), **processing time** (event handler execution),
265
+ and **presentation delay** (rendering after handler completes).
266
+
267
+ | Bottleneck | Phase Affected | Impact |
268
+ |-----------|---------------|--------|
269
+ | **Long tasks (>50ms) on main thread** | Input delay | User taps during a long task; response waits for task completion |
270
+ | **Excessive JavaScript execution** | Processing time | Event handlers do too much synchronous work |
271
+ | **Layout thrashing** | Processing time | Repeated read/write cycles force synchronous layout recalculations |
272
+ | **Large DOM size (>1500 nodes)** | Presentation delay | Browser takes longer to recalculate styles and paint after changes |
273
+ | **Heavy third-party scripts** | Input delay | Analytics, ads, chat widgets block the main thread |
274
+ | **Forced synchronous layout** | Processing time | Reading `offsetHeight` after writing to DOM triggers immediate reflow |
275
+ | **Unoptimized React re-renders** | Processing time | State changes triggering large subtree re-renders |
276
+ | **No task yielding** | Input delay | Long-running computations never yield to let interactions through |
277
+
278
+ **Key stat:** Fewer than 25% of websites keep task duration below the recommended 50ms
279
+ threshold (HTTP Archive 2025 Web Almanac).
280
+
281
+ ### CLS Bottlenecks
282
+
283
+ CLS measures unexpected layout shifts during the page lifecycle, scored by impact fraction
284
+ times distance fraction.
285
+
286
+ | Bottleneck | Share of CLS Issues | Impact |
287
+ |-----------|-------------------|--------|
288
+ | **Images/videos without dimensions** | ~60% | Browser allocates 0px then reflows when media loads |
289
+ | **Web fonts causing text reflow (FOIT/FOUT)** | ~25% | Fallback font metrics differ from web font, text reflows |
290
+ | **Dynamically injected content** | ~15% | Ads, banners, cookie notices push content down |
291
+ | **Late-loading CSS** | Common | Styles apply after content renders, causing shifts |
292
+ | **Animations not using `transform`** | Moderate | Animating `top`/`left`/`width`/`height` causes layout shifts |
293
+ | **Async-loaded embeds without placeholders** | Moderate | iframes, maps, social widgets have unknown dimensions |
294
+
295
+ ---
296
+
297
+ ## Optimization Patterns
298
+
299
+ ### LCP Optimization
300
+
301
+ #### 1. Eliminate Resource Discovery Delay
302
+
303
+ The LCP image must be discoverable in the HTML source by the browser's preload scanner.
304
+ If it is referenced only in CSS (`background-image`) or loaded via JavaScript, the browser
305
+ cannot start fetching it until those resources are parsed.
306
+
307
+ ```html
308
+ <!-- BEFORE: Image only discoverable after CSS parses (adds 200-800ms) -->
309
+ <div class="hero" style="background-image: url('/hero.jpg')"></div>
310
+
311
+ <!-- AFTER: Image discoverable immediately by preload scanner -->
312
+ <link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
313
+ <img src="/hero.webp" alt="Hero" fetchpriority="high"
314
+ width="1200" height="600" decoding="async">
315
+ ```
316
+
317
+ **Measured impact:** Google Flights saw a 700ms LCP improvement from adding
318
+ `fetchpriority="high"` alone. Google's own tests improved LCP from 2.6s to 1.9s.
319
+
320
+ #### 2. Optimize Server Response Time
321
+
322
+ ```nginx
323
+ # nginx: Enable gzip + brotli compression
324
+ gzip on;
325
+ gzip_types text/html text/css application/javascript image/svg+xml;
326
+
327
+ # Enable brotli (requires ngx_brotli module)
328
+ brotli on;
329
+ brotli_types text/html text/css application/javascript image/svg+xml;
330
+
331
+ # Set aggressive caching for immutable assets
332
+ location /assets/ {
333
+ expires 1y;
334
+ add_header Cache-Control "public, immutable";
335
+ }
336
+ ```
337
+
338
+ **Trade-off:** Brotli compression achieves 15-25% smaller files than gzip but requires
339
+ more CPU for compression. Use brotli for static assets (pre-compressed) and gzip for
340
+ dynamic content.
341
+
342
+ #### 3. Responsive Images with Modern Formats
343
+
344
+ ```html
345
+ <!-- BEFORE: Single 2MB PNG for all devices -->
346
+ <img src="/hero.png" alt="Hero">
347
+
348
+ <!-- AFTER: Format negotiation + responsive sizing -->
349
+ <picture>
350
+ <source srcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w"
351
+ sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 1200px"
352
+ type="image/avif">
353
+ <source srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
354
+ sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 1200px"
355
+ type="image/webp">
356
+ <img src="/hero-800.jpg" alt="Hero" width="1200" height="600"
357
+ fetchpriority="high" decoding="async">
358
+ </picture>
359
+ ```
360
+
361
+ **Measured savings:**
362
+ - AVIF: 50-70% smaller than JPEG at equivalent quality
363
+ - WebP: 25-35% smaller than JPEG
364
+ - On a 4G mobile connection, a 200KB WebP loads in ~400ms vs 800ms for a 500KB JPEG
365
+
366
+ **Trade-off:** AVIF encoding is 10-20x slower than JPEG. Pre-generate at build time or
367
+ use an image CDN (Cloudinary, imgix, Cloudflare Images) for on-the-fly conversion.
368
+
369
+ #### 4. Inline Critical CSS, Defer the Rest
370
+
371
+ ```html
372
+ <head>
373
+ <!-- Inline only above-the-fold CSS (target: <14KB to fit in first TCP round-trip) -->
374
+ <style>
375
+ /* Critical CSS extracted by tools like critters or critical */
376
+ .hero { display: flex; align-items: center; min-height: 60vh; }
377
+ .hero img { width: 100%; height: auto; }
378
+ nav { display: flex; gap: 1rem; padding: 1rem; }
379
+ </style>
380
+
381
+ <!-- Defer non-critical CSS -->
382
+ <link rel="preload" href="/styles.css" as="style"
383
+ onload="this.onload=null;this.rel='stylesheet'">
384
+ <noscript><link rel="stylesheet" href="/styles.css"></noscript>
385
+ </head>
386
+ ```
387
+
388
+ **Measured impact:** Inlining critical CSS typically reduces LCP by 300-800ms on 3G
389
+ connections by eliminating a render-blocking round-trip.
390
+
391
+ **Trade-off:** Inlined CSS is not cached separately. If critical CSS exceeds ~14KB, it
392
+ no longer fits in the initial TCP congestion window and loses its advantage. Use tools
393
+ like `critters` (Webpack/Vite plugin) to automate extraction.
394
+
395
+ ### INP Optimization
396
+
397
+ #### 1. Break Up Long Tasks with `scheduler.yield()`
398
+
399
+ ```javascript
400
+ // BEFORE: Single long task blocks main thread for 300ms+
401
+ function processLargeDataset(items) {
402
+ for (const item of items) {
403
+ heavyComputation(item); // 5ms per item, 1000 items = 5000ms blocked
404
+ updateDOM(item);
405
+ }
406
+ }
407
+
408
+ // AFTER: Yield to main thread, allowing interactions to process
409
+ async function processLargeDataset(items) {
410
+ const BATCH_SIZE = 50; // ~250ms per batch, then yield
411
+ for (let i = 0; i < items.length; i += BATCH_SIZE) {
412
+ const batch = items.slice(i, i + BATCH_SIZE);
413
+ for (const item of batch) {
414
+ heavyComputation(item);
415
+ updateDOM(item);
416
+ }
417
+ // Yield to main thread — interactions can now be processed
418
+ await scheduler.yield();
419
+ }
420
+ }
421
+ ```
422
+
423
+ **Key advantage of `scheduler.yield()` over `setTimeout(0)`:** `scheduler.yield()`
424
+ schedules the continuation at the *front* of the task queue, not the back. This means
425
+ your remaining work runs immediately after pending user interactions, rather than after
426
+ all other queued tasks. Result: same INP improvement with less total processing delay.
427
+
428
+ **Browser support:** Chrome 129+ (September 2024). For older browsers, use a polyfill:
429
+
430
+ ```javascript
431
+ // Polyfill for browsers without scheduler.yield
432
+ async function yieldToMain() {
433
+ if ('scheduler' in globalThis && 'yield' in scheduler) {
434
+ return scheduler.yield();
435
+ }
436
+ return new Promise(resolve => setTimeout(resolve, 0));
437
+ }
438
+ ```
439
+
440
+ #### 2. Debounce and Throttle Expensive Handlers
441
+
442
+ ```javascript
443
+ // BEFORE: Scroll handler fires 60+ times/second, each triggering layout
444
+ window.addEventListener('scroll', () => {
445
+ const rect = element.getBoundingClientRect(); // forces layout
446
+ element.style.transform = `translateY(${rect.top}px)`; // triggers paint
447
+ });
448
+
449
+ // AFTER: Use requestAnimationFrame to batch to paint cycle
450
+ let ticking = false;
451
+ window.addEventListener('scroll', () => {
452
+ if (!ticking) {
453
+ requestAnimationFrame(() => {
454
+ const rect = element.getBoundingClientRect();
455
+ element.style.transform = `translateY(${rect.top}px)`;
456
+ ticking = false;
457
+ });
458
+ ticking = true;
459
+ }
460
+ });
461
+ ```
462
+
463
+ **Measured impact:** Reducing scroll handler frequency from 60/s to 1/frame typically
464
+ cuts INP for scroll-triggered interactions by 40-70%.
465
+
466
+ #### 3. Use `content-visibility` for Off-Screen Content
467
+
468
+ ```css
469
+ /* Browser skips rendering for off-screen sections entirely */
470
+ .below-fold-section {
471
+ content-visibility: auto;
472
+ contain-intrinsic-size: auto 500px; /* estimated height to prevent CLS */
473
+ }
474
+ ```
475
+
476
+ **Measured impact:** Chrome team reported rendering time reductions of 50-70% for pages
477
+ with many off-screen sections. The `contain-intrinsic-size` prevents CLS by reserving
478
+ space.
479
+
480
+ **Trade-off:** `content-visibility: auto` can cause `find-in-page` (Ctrl+F) to miss
481
+ content in some edge cases. Also, any element with `content-visibility: auto` that becomes
482
+ visible will trigger a rendering cost at that moment.
483
+
484
+ #### 4. Move Heavy Computation to Web Workers
485
+
486
+ ```javascript
487
+ // BEFORE: JSON parsing blocks main thread for 200ms+
488
+ const data = JSON.parse(hugeJsonString); // blocks INP
489
+
490
+ // AFTER: Offload to Web Worker
491
+ // worker.js
492
+ self.onmessage = (e) => {
493
+ const data = JSON.parse(e.data);
494
+ self.postMessage(data);
495
+ };
496
+
497
+ // main.js
498
+ const worker = new Worker('/worker.js');
499
+ worker.postMessage(hugeJsonString);
500
+ worker.onmessage = (e) => {
501
+ renderData(e.data); // Only DOM update on main thread
502
+ };
503
+ ```
504
+
505
+ **Trade-off:** Web Workers have no DOM access and data transfer via `postMessage` has
506
+ serialization cost. Use `Transferable` objects (ArrayBuffer) for large binary data to
507
+ avoid the copy overhead. Structured clone for objects typically adds 1-5ms per MB.
508
+
509
+ ### CLS Optimization
510
+
511
+ #### 1. Always Set Explicit Dimensions on Media
512
+
513
+ ```html
514
+ <!-- BEFORE: No dimensions — browser allocates 0px, then shifts -->
515
+ <img src="/photo.jpg" alt="Product">
516
+
517
+ <!-- AFTER: Explicit width/height lets browser reserve space -->
518
+ <img src="/photo.jpg" alt="Product" width="800" height="600"
519
+ loading="lazy" decoding="async">
520
+
521
+ <!-- AFTER (modern CSS approach): aspect-ratio for responsive images -->
522
+ <style>
523
+ .responsive-img {
524
+ width: 100%;
525
+ height: auto;
526
+ aspect-ratio: 4 / 3; /* Browser reserves correct space before load */
527
+ }
528
+ </style>
529
+ <img class="responsive-img" src="/photo.jpg" alt="Product"
530
+ loading="lazy" decoding="async">
531
+ ```
532
+
533
+ **Measured impact:** Adding `width`/`height` attributes to images eliminates ~60% of all
534
+ CLS issues across the web.
535
+
536
+ #### 2. Optimize Font Loading to Prevent Text Reflow
537
+
538
+ ```css
539
+ /* Option A: font-display: swap (shows text immediately with fallback) */
540
+ @font-face {
541
+ font-family: 'CustomFont';
542
+ src: url('/fonts/custom.woff2') format('woff2');
543
+ font-display: swap;
544
+ /* Risk: fallback → custom font swap causes CLS if metrics differ */
545
+ }
546
+
547
+ /* Option B: font-display: optional (best for CLS, may not show custom font) */
548
+ @font-face {
549
+ font-family: 'CustomFont';
550
+ src: url('/fonts/custom.woff2') format('woff2');
551
+ font-display: optional;
552
+ /* Font only used if already cached; zero CLS guaranteed */
553
+ }
554
+
555
+ /* Option C: Size-adjust fallback (best of both worlds) */
556
+ @font-face {
557
+ font-family: 'CustomFont Fallback';
558
+ src: local('Arial');
559
+ size-adjust: 105.2%; /* Match custom font metrics */
560
+ ascent-override: 95%;
561
+ descent-override: 22%;
562
+ line-gap-override: 0%;
563
+ }
564
+
565
+ body {
566
+ font-family: 'CustomFont', 'CustomFont Fallback', sans-serif;
567
+ }
568
+ ```
569
+
570
+ **Preload critical fonts:**
571
+ ```html
572
+ <link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin>
573
+ ```
574
+
575
+ **Trade-offs:**
576
+ - `font-display: swap` — zero FOIT but may cause CLS on text reflow (~0.02-0.08 CLS)
577
+ - `font-display: optional` — zero CLS but first-time visitors may not see custom font
578
+ - `size-adjust` fallback — near-zero CLS with custom font display, but requires per-font metric tuning
579
+
580
+ #### 3. Reserve Space for Dynamic Content
581
+
582
+ ```html
583
+ <!-- BEFORE: Ad loads and pushes content down -->
584
+ <div class="content">
585
+ <p>Article text here...</p>
586
+ <div id="ad-slot"></div> <!-- 0px until ad loads -->
587
+ <p>More content...</p>
588
+ </div>
589
+
590
+ <!-- AFTER: Reserve space with min-height -->
591
+ <div class="content">
592
+ <p>Article text here...</p>
593
+ <div id="ad-slot" style="min-height: 250px; background: #f0f0f0;">
594
+ <!-- 250px reserved for standard IAB medium rectangle ad -->
595
+ </div>
596
+ <p>More content...</p>
597
+ </div>
598
+ ```
599
+
600
+ **For cookie banners and notification bars:**
601
+ ```css
602
+ /* Reserve space at top/bottom for banners that push content */
603
+ .cookie-banner-placeholder {
604
+ min-height: 80px; /* match your banner height */
605
+ }
606
+
607
+ /* OR: Use overlay positioning (no layout shift) */
608
+ .cookie-banner {
609
+ position: fixed;
610
+ bottom: 0;
611
+ left: 0;
612
+ right: 0;
613
+ z-index: 1000;
614
+ /* Fixed positioning does NOT cause layout shifts */
615
+ }
616
+ ```
617
+
618
+ #### 4. Use CSS `transform` for Animations
619
+
620
+ ```css
621
+ /* BEFORE: Animating layout properties causes CLS */
622
+ .slide-in {
623
+ animation: slideIn 0.3s ease;
624
+ }
625
+ @keyframes slideIn {
626
+ from { margin-left: -100%; } /* triggers layout shift */
627
+ to { margin-left: 0; }
628
+ }
629
+
630
+ /* AFTER: transform animations do not cause layout shifts */
631
+ .slide-in {
632
+ animation: slideIn 0.3s ease;
633
+ }
634
+ @keyframes slideIn {
635
+ from { transform: translateX(-100%); } /* compositor-only, no CLS */
636
+ to { transform: translateX(0); }
637
+ }
638
+ ```
639
+
640
+ **Rule:** Only `transform` and `opacity` animations are compositor-only and CLS-free.
641
+ Animating `top`, `left`, `width`, `height`, `margin`, or `padding` triggers layout and
642
+ contributes to CLS.
643
+
644
+ ---
645
+
646
+ ## Anti-Patterns
647
+
648
+ ### 1. Lazy Loading the LCP Image
649
+ Adding `loading="lazy"` to the largest above-the-fold image delays its fetch until the
650
+ browser determines it is near the viewport — after layout. This adds 200-1000ms to LCP.
651
+ **Fix:** Never lazy-load above-the-fold images. Use `fetchpriority="high"` instead.
652
+
653
+ ### 2. Preloading Everything
654
+ Excessive `<link rel="preload">` tags cause bandwidth contention, delaying the resources
655
+ that actually matter. Chrome limits effective preloads to ~6 concurrent connections per
656
+ origin. **Fix:** Preload only the LCP image and critical font (1-2 preloads max).
657
+
658
+ ### 3. Third-Party Script Tags in `<head>`
659
+ Synchronous third-party scripts (analytics, tag managers, chat widgets) block rendering.
660
+ A single 100KB synchronous script on 3G adds 500ms+ to LCP.
661
+ **Fix:** Load all third-party scripts with `async` or `defer`. Use `requestIdleCallback`
662
+ or `setTimeout` for non-critical scripts.
663
+
664
+ ### 4. Using `display: none` Instead of `content-visibility`
665
+ `display: none` still parses and constructs the DOM for hidden elements. For large
666
+ off-screen sections, this wastes CPU during interactions.
667
+ **Fix:** Use `content-visibility: auto` for sections below the fold.
668
+
669
+ ### 5. Placeholder Spinners That Cause Layout Shift
670
+ Replacing a spinner (50px tall) with actual content (300px tall) causes a 250px shift.
671
+ **Fix:** Use skeleton screens that match the final content dimensions.
672
+
673
+ ### 6. Importing Entire Libraries for Small Features
674
+ `import _ from 'lodash'` pulls in ~72KB minified for functions like `debounce` (0.3KB).
675
+ **Fix:** Use `import debounce from 'lodash-es/debounce'` or native alternatives.
676
+
677
+ ### 7. Unoptimized `useEffect` in React Causing INP Issues
678
+ ```javascript
679
+ // ANTI-PATTERN: useEffect with missing dependency causes re-render storms
680
+ useEffect(() => {
681
+ setData(transform(rawData)); // triggers re-render every time rawData ref changes
682
+ }); // missing dependency array = runs every render
683
+ ```
684
+ **Fix:** Use `useMemo` for expensive transforms, add correct dependency arrays.
685
+
686
+ ### 8. Client-Side Rendering for Content-Heavy Pages
687
+ SPAs that fetch data after hydration add 1-3 seconds to LCP because the browser must:
688
+ download JS bundle > parse > execute > fetch data > render. Each step is sequential.
689
+ **Fix:** Use SSR/SSG for content-heavy pages, CSR only for authenticated dashboards.
690
+
691
+ ### 9. Not Setting `decoding="async"` on Non-LCP Images
692
+ Without `decoding="async"`, image decoding can block the main thread for 10-50ms per
693
+ large image, accumulating into INP-impacting long tasks.
694
+ **Fix:** Add `decoding="async"` to all images except the LCP image.
695
+
696
+ ### 10. Using `@import` in CSS Files
697
+ CSS `@import` creates sequential request chains. Each `@import` adds a full round-trip:
698
+ ```css
699
+ /* style.css — ANTI-PATTERN */
700
+ @import url('reset.css'); /* fetch 1: 200ms */
701
+ @import url('layout.css'); /* fetch 2: waits for fetch 1, +200ms */
702
+ @import url('theme.css'); /* fetch 3: waits for fetch 2, +200ms */
703
+ /* Total: 600ms of sequential fetching */
704
+ ```
705
+ **Fix:** Use `<link>` tags in HTML (fetched in parallel) or a CSS bundler.
706
+
707
+ ### 11. Not Using a CDN for Static Assets
708
+ Serving images from a single origin server adds 50-300ms of latency for distant users.
709
+ A CDN reduces this to 5-20ms by serving from edge nodes.
710
+ **Fix:** Use a CDN for all static assets. Cost: ~$0.01-0.08/GB, trivial for most sites.
711
+
712
+ ### 12. Dynamic `import()` on the Critical Path
713
+ ```javascript
714
+ // ANTI-PATTERN: Dynamic import delays the hero component render
715
+ const HeroSection = lazy(() => import('./HeroSection'));
716
+ // This adds a network round-trip to LCP
717
+ ```
718
+ **Fix:** Statically import above-the-fold components. Use dynamic `import()` only for
719
+ below-the-fold or interaction-triggered components.
720
+
721
+ ---
722
+
723
+ ## Architecture-Level Decisions
724
+
725
+ ### Rendering Strategy Impact on LCP
726
+
727
+ | Strategy | Typical LCP | Pros | Cons |
728
+ |----------|------------|------|------|
729
+ | **SSR** | 1.0-2.0s | Content in first HTML response; best LCP | Server CPU cost; TTFB depends on server speed |
730
+ | **SSG** | 0.5-1.5s | Pre-built HTML; fastest TTFB from CDN | Not suitable for dynamic/personalized content |
731
+ | **CSR (SPA)** | 2.5-5.0s | Rich interactivity; simpler deployment | Worst LCP; requires JS to render any content |
732
+ | **Streaming SSR** | 0.8-1.8s | Progressive HTML delivery; good LCP + INP | Complexity; requires framework support |
733
+ | **Islands Architecture** | 0.6-1.5s | Minimal JS; near-SSG LCP with targeted interactivity | Newer pattern; limited framework support |
734
+ | **RSC (React Server Components)** | 0.8-1.8s | Zero client JS for server components; streaming | React ecosystem only; learning curve |
735
+
736
+ **Key finding:** Switching from CSR to SSR/SSG typically cuts LCP by 40-60% (web.dev).
737
+
738
+ ### Streaming HTML
739
+
740
+ Streaming SSR sends HTML chunks as they are generated, rather than waiting for the entire
741
+ page to render on the server. This improves both TTFB and LCP:
742
+
743
+ ```
744
+ Traditional SSR: [Server renders 2s] → [Send HTML] → [Browser paints at 2.5s]
745
+ Streaming SSR: [Server starts] → [Send <head> at 100ms] → [Send hero at 300ms] → [Browser paints at 800ms]
746
+ ```
747
+
748
+ **Framework support:**
749
+ - Next.js: Built-in with App Router (`loading.js` for streaming boundaries)
750
+ - Remix: Native streaming with `defer()` loaders
751
+ - SvelteKit: Streaming by default in SSR mode
752
+ - Astro: Streaming SSR via adapter configuration
753
+
754
+ ### Islands Architecture (Astro, Fresh, Marko)
755
+
756
+ Islands isolate interactive components into "islands" of JavaScript within a sea of
757
+ static HTML. The browser only downloads and hydrates JavaScript for interactive components.
758
+
759
+ ```
760
+ Traditional SPA: [Download 500KB JS] → [Parse] → [Hydrate entire page] → [Interactive]
761
+ Islands: [Static HTML paints immediately] → [Download 50KB JS for 2 islands] → [Hydrate only islands]
762
+ ```
763
+
764
+ **Impact on CWV:**
765
+ - **LCP:** Near-SSG performance because HTML is static
766
+ - **INP:** Better because less JavaScript means fewer long tasks during hydration
767
+ - **CLS:** Better because static HTML has stable layout from first paint
768
+
769
+ **Trade-off:** Islands architecture requires decomposing your UI into static vs interactive
770
+ parts at the component level, which adds architectural complexity. Not all UI patterns map
771
+ cleanly to this model (e.g., highly interactive dashboards).
772
+
773
+ ### Progressive Enhancement for INP
774
+
775
+ Build core functionality in HTML/CSS, enhance with JavaScript. If a form works without
776
+ JS (via standard `<form>` submission), then JS failures or slow loads do not block
777
+ interactions. This architectural choice guarantees zero INP for core flows before JS loads.
778
+
779
+ ---
780
+
781
+ ## Testing & Regression Prevention
782
+
783
+ ### Lighthouse CI Setup
784
+
785
+ ```yaml
786
+ # .github/workflows/lighthouse-ci.yml
787
+ name: Lighthouse CI
788
+ on: [pull_request]
789
+
790
+ jobs:
791
+ lighthouse:
792
+ runs-on: ubuntu-latest
793
+ steps:
794
+ - uses: actions/checkout@v4
795
+ - uses: actions/setup-node@v4
796
+ with:
797
+ node-version: 20
798
+
799
+ - name: Install & Build
800
+ run: npm ci && npm run build
801
+
802
+ - name: Run Lighthouse CI
803
+ uses: treosh/lighthouse-ci-action@v12
804
+ with:
805
+ configPath: ./lighthouserc.json
806
+ uploadArtifacts: true
807
+ ```
808
+
809
+ ### Lighthouse CI Configuration with Budgets
810
+
811
+ ```json
812
+ // lighthouserc.json
813
+ {
814
+ "ci": {
815
+ "collect": {
816
+ "url": [
817
+ "http://localhost:3000/",
818
+ "http://localhost:3000/products",
819
+ "http://localhost:3000/checkout"
820
+ ],
821
+ "numberOfRuns": 3,
822
+ "startServerCommand": "npm run start"
823
+ },
824
+ "assert": {
825
+ "assertions": {
826
+ "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
827
+ "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
828
+ "total-blocking-time": ["error", { "maxNumericValue": 300 }],
829
+ "interactive": ["warn", { "maxNumericValue": 3500 }],
830
+ "categories:performance": ["error", { "minScore": 0.9 }]
831
+ }
832
+ },
833
+ "upload": {
834
+ "target": "temporary-public-storage"
835
+ }
836
+ }
837
+ }
838
+ ```
839
+
840
+ ### Performance Budget File
841
+
842
+ ```json
843
+ // budget.json (for Lighthouse --budget-path)
844
+ [
845
+ {
846
+ "path": "/*",
847
+ "timings": [
848
+ { "metric": "largest-contentful-paint", "budget": 2500 },
849
+ { "metric": "cumulative-layout-shift", "budget": 0.1 },
850
+ { "metric": "total-blocking-time", "budget": 300 }
851
+ ],
852
+ "resourceSizes": [
853
+ { "resourceType": "script", "budget": 300 },
854
+ { "resourceType": "image", "budget": 500 },
855
+ { "resourceType": "stylesheet", "budget": 100 },
856
+ { "resourceType": "total", "budget": 1000 }
857
+ ],
858
+ "resourceCounts": [
859
+ { "resourceType": "third-party", "budget": 10 },
860
+ { "resourceType": "script", "budget": 15 }
861
+ ]
862
+ }
863
+ ]
864
+ ```
865
+
866
+ ### CrUX API Monitoring Script
867
+
868
+ ```javascript
869
+ // scripts/check-cwv.mjs — Run daily in CI or cron
870
+ const API_KEY = process.env.CRUX_API_KEY;
871
+ const ORIGIN = 'https://yoursite.com';
872
+
873
+ async function checkCWV() {
874
+ const res = await fetch(
875
+ `https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=${API_KEY}`,
876
+ {
877
+ method: 'POST',
878
+ body: JSON.stringify({ origin: ORIGIN, formFactor: 'PHONE' }),
879
+ }
880
+ );
881
+ const data = await res.json();
882
+ const metrics = data.record.metrics;
883
+
884
+ const checks = [
885
+ { name: 'LCP', value: metrics.largest_contentful_paint.percentiles.p75, threshold: 2500 },
886
+ { name: 'INP', value: metrics.interaction_to_next_paint.percentiles.p75, threshold: 200 },
887
+ { name: 'CLS', value: metrics.cumulative_layout_shift.percentiles.p75, threshold: 0.1 },
888
+ ];
889
+
890
+ let failed = false;
891
+ for (const check of checks) {
892
+ const status = check.value <= check.threshold ? 'PASS' : 'FAIL';
893
+ console.log(`${check.name}: ${check.value} (threshold: ${check.threshold}) [${status}]`);
894
+ if (status === 'FAIL') failed = true;
895
+ }
896
+
897
+ if (failed) {
898
+ // Send alert via Slack/PagerDuty/email
899
+ console.error('Core Web Vitals regression detected!');
900
+ process.exit(1);
901
+ }
902
+ }
903
+
904
+ checkCWV();
905
+ ```
906
+
907
+ ### Automated Regression Alerts
908
+
909
+ Set up monitoring at three levels:
910
+ 1. **CI/CD (Lab):** Lighthouse CI on every PR — catches regressions before merge
911
+ 2. **Daily CrUX check (Field):** Script above — catches real-user regressions within 28 days
912
+ 3. **Real-time RUM (Field):** web-vitals library sending to your analytics — catches regressions within minutes
913
+
914
+ **Alert thresholds recommendation:**
915
+ - Warn when p75 exceeds 80% of the "Good" threshold (LCP > 2.0s, INP > 160ms, CLS > 0.08)
916
+ - Alert/block when p75 exceeds the "Good" threshold (LCP > 2.5s, INP > 200ms, CLS > 0.1)
917
+
918
+ ---
919
+
920
+ ## Decision Trees
921
+
922
+ ### "My LCP is Slow" Decision Tree
923
+
924
+ ```
925
+ LCP > 2.5s?
926
+ ├── Check TTFB first (DevTools → Network → document request)
927
+ │ ├── TTFB > 800ms → Server-side issue
928
+ │ │ ├── Slow database queries → Add caching layer (Redis/CDN)
929
+ │ │ ├── No CDN → Add CDN (reduces TTFB by 100-500ms)
930
+ │ │ └── Dynamic page on every request → Switch to SSG or ISR
931
+ │ └── TTFB < 800ms → Client-side issue
932
+ │ ├── What is the LCP element? (DevTools → Performance → LCP marker)
933
+ │ │ ├── Image
934
+ │ │ │ ├── Is it lazy-loaded? → Remove loading="lazy"
935
+ │ │ │ ├── Is it in CSS background-image? → Move to <img> tag + preload
936
+ │ │ │ ├── Is fetchpriority="high" set? → Add it
937
+ │ │ │ ├── Is image > 200KB? → Convert to WebP/AVIF, resize
938
+ │ │ │ └── Is image preloaded? → Add <link rel="preload">
939
+ │ │ ├── Text block
940
+ │ │ │ ├── Font blocking render? → Add font-display: optional
941
+ │ │ │ ├── CSS render-blocking? → Inline critical CSS
942
+ │ │ │ └── JS rendering content? → Switch to SSR
943
+ │ │ └── Video
944
+ │ │ ├── Add poster attribute with optimized image
945
+ │ │ └── Preload poster image
946
+ │ └── Check render-blocking resources (Lighthouse audit)
947
+ │ ├── Blocking CSS > 50KB → Extract + inline critical CSS
948
+ │ ├── Blocking JS in <head> → Add defer attribute
949
+ │ └── Third-party scripts blocking → Load async or defer to after LCP
950
+ ```
951
+
952
+ ### "I Have Layout Shifts" Decision Tree
953
+
954
+ ```
955
+ CLS > 0.1?
956
+ ├── Identify shifting elements (DevTools → Performance → Layout Shift clusters)
957
+ │ ├── Images/videos shifting?
958
+ │ │ ├── Missing width/height attributes → Add explicit dimensions
959
+ │ │ ├── Responsive but no aspect-ratio → Add CSS aspect-ratio
960
+ │ │ └── Container resizing → Set min-height on container
961
+ │ ├── Text shifting (font swap)?
962
+ │ │ ├── Large CLS from font → Use font-display: optional
963
+ │ │ ├── Moderate CLS from font → Use size-adjust on fallback
964
+ │ │ └── Slow font load → Preload critical font files
965
+ │ ├── Ad/embed shifting?
966
+ │ │ ├── No reserved space → Add min-height to ad container
967
+ │ │ ├── Ad sizes vary → Use the most common ad size as min-height
968
+ │ │ └── Consider sticky/overlay ad formats (no layout shift)
969
+ │ ├── Dynamically injected content?
970
+ │ │ ├── Cookie banner → Use position: fixed (no CLS)
971
+ │ │ ├── Notification bar → Reserve space with min-height
972
+ │ │ └── Lazy-loaded components → Use skeleton with matching dimensions
973
+ │ └── CSS animations?
974
+ │ ├── Using top/left/margin → Switch to transform: translate()
975
+ │ └── Using width/height → Switch to transform: scale()
976
+ ```
977
+
978
+ ### "INP is High" Decision Tree
979
+
980
+ ```
981
+ INP > 200ms?
982
+ ├── Which phase is slow? (web-vitals attribution build)
983
+ │ ├── Input Delay > 100ms (waiting for main thread)
984
+ │ │ ├── Long tasks visible? (DevTools → Performance → Long Tasks)
985
+ │ │ │ ├── Third-party scripts → Defer to after interaction, use Partytown
986
+ │ │ │ ├── Heavy initialization → Use requestIdleCallback or lazy init
987
+ │ │ │ └── Large JS bundle → Code-split, tree-shake, reduce bundle
988
+ │ │ └── Frequent timer/interval callbacks → Reduce frequency, use rAF
989
+ │ ├── Processing Duration > 100ms (event handler too slow)
990
+ │ │ ├── Layout thrashing? → Batch DOM reads before writes
991
+ │ │ ├── Expensive computation? → Move to Web Worker
992
+ │ │ ├── Large React re-render? → Use memo/useMemo, virtualize lists
993
+ │ │ └── Synchronous API calls? → Should never block event handler
994
+ │ └── Presentation Delay > 100ms (rendering after handler)
995
+ │ ├── Large DOM (>1500 nodes) → Reduce DOM size, virtualize
996
+ │ ├── Complex CSS selectors → Simplify, use BEM/utility classes
997
+ │ └── Many style recalculations → Use CSS containment (contain: layout)
998
+ ├── Is it a specific interaction type?
999
+ │ ├── Click handlers → Check for synchronous work in handler
1000
+ │ ├── Key press handlers → Check for synchronous search/filter
1001
+ │ └── Scroll-triggered → Use passive listeners, throttle with rAF
1002
+ ```
1003
+
1004
+ ---
1005
+
1006
+ ## Code Examples
1007
+
1008
+ ### 1. Complete Web Vitals Monitoring Setup
1009
+
1010
+ ```javascript
1011
+ // lib/vitals.js — Production-ready CWV monitoring
1012
+ import { onLCP, onINP, onCLS } from 'web-vitals/attribution';
1013
+
1014
+ const ANALYTICS_ENDPOINT = '/api/vitals';
1015
+ const queue = [];
1016
+ let flushTimer = null;
1017
+
1018
+ function enqueue(metric) {
1019
+ queue.push({
1020
+ name: metric.name,
1021
+ value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
1022
+ rating: metric.rating,
1023
+ delta: metric.delta,
1024
+ id: metric.id,
1025
+ page: window.location.pathname,
1026
+ navigationType: metric.navigationType,
1027
+ // Attribution data for debugging
1028
+ ...(metric.attribution && {
1029
+ debug: getDebugInfo(metric),
1030
+ }),
1031
+ });
1032
+
1033
+ // Batch send: flush after 5 seconds or 10 metrics
1034
+ clearTimeout(flushTimer);
1035
+ if (queue.length >= 10) {
1036
+ flush();
1037
+ } else {
1038
+ flushTimer = setTimeout(flush, 5000);
1039
+ }
1040
+ }
1041
+
1042
+ function getDebugInfo(metric) {
1043
+ if (metric.name === 'LCP') {
1044
+ return {
1045
+ element: metric.attribution.element,
1046
+ url: metric.attribution.url,
1047
+ ttfb: metric.attribution.timeToFirstByte,
1048
+ loadDelay: metric.attribution.resourceLoadDelay,
1049
+ loadTime: metric.attribution.resourceLoadTime,
1050
+ renderDelay: metric.attribution.elementRenderDelay,
1051
+ };
1052
+ }
1053
+ if (metric.name === 'INP') {
1054
+ return {
1055
+ eventType: metric.attribution.interactionType,
1056
+ target: metric.attribution.interactionTarget,
1057
+ inputDelay: metric.attribution.inputDelay,
1058
+ processingTime: metric.attribution.processingDuration,
1059
+ presentationDelay: metric.attribution.presentationDelay,
1060
+ };
1061
+ }
1062
+ if (metric.name === 'CLS') {
1063
+ return {
1064
+ largestTarget: metric.attribution.largestShiftTarget,
1065
+ largestValue: metric.attribution.largestShiftValue,
1066
+ };
1067
+ }
1068
+ }
1069
+
1070
+ function flush() {
1071
+ if (queue.length === 0) return;
1072
+ const body = JSON.stringify(queue.splice(0));
1073
+ if (navigator.sendBeacon) {
1074
+ navigator.sendBeacon(ANALYTICS_ENDPOINT, body);
1075
+ } else {
1076
+ fetch(ANALYTICS_ENDPOINT, { body, method: 'POST', keepalive: true });
1077
+ }
1078
+ }
1079
+
1080
+ // Register listeners — call ONCE per page load
1081
+ onLCP(enqueue);
1082
+ onINP(enqueue);
1083
+ onCLS(enqueue);
1084
+
1085
+ // Ensure final flush on page hide
1086
+ document.addEventListener('visibilitychange', () => {
1087
+ if (document.visibilityState === 'hidden') flush();
1088
+ });
1089
+ ```
1090
+
1091
+ ### 2. LCP Preload with Dynamic Detection
1092
+
1093
+ ```javascript
1094
+ // Detect and preload the LCP image dynamically based on viewport
1095
+ // Place in <head> as inline script for earliest execution
1096
+ (function() {
1097
+ const mq = window.matchMedia('(max-width: 768px)');
1098
+ const link = document.createElement('link');
1099
+ link.rel = 'preload';
1100
+ link.as = 'image';
1101
+ link.fetchPriority = 'high';
1102
+
1103
+ if (mq.matches) {
1104
+ link.href = '/images/hero-mobile.webp';
1105
+ link.type = 'image/webp';
1106
+ } else {
1107
+ link.href = '/images/hero-desktop.webp';
1108
+ link.type = 'image/webp';
1109
+ }
1110
+
1111
+ document.head.appendChild(link);
1112
+ })();
1113
+ ```
1114
+
1115
+ ### 3. Skeleton Screen That Prevents CLS
1116
+
1117
+ ```html
1118
+ <!-- Skeleton matches final content dimensions exactly -->
1119
+ <div class="product-card" style="width: 300px; min-height: 420px;">
1120
+ <div class="skeleton-image" style="aspect-ratio: 4/3; background: #e0e0e0;"></div>
1121
+ <div class="skeleton-text" style="height: 24px; margin: 12px 0; background: #e0e0e0; border-radius: 4px;"></div>
1122
+ <div class="skeleton-text" style="height: 16px; width: 60%; background: #e0e0e0; border-radius: 4px;"></div>
1123
+ <div class="skeleton-price" style="height: 28px; width: 40%; margin-top: 8px; background: #e0e0e0; border-radius: 4px;"></div>
1124
+ </div>
1125
+
1126
+ <style>
1127
+ .product-card [class^="skeleton-"] {
1128
+ animation: pulse 1.5s ease-in-out infinite;
1129
+ }
1130
+ @keyframes pulse {
1131
+ 0%, 100% { opacity: 1; }
1132
+ 50% { opacity: 0.5; }
1133
+ }
1134
+ </style>
1135
+ ```
1136
+
1137
+ ### 4. Yielding Pattern with Time-Slicing for INP
1138
+
1139
+ ```javascript
1140
+ // Time-based yielding — more adaptive than fixed batch sizes
1141
+ async function processItems(items, processItem, maxBlockTime = 50) {
1142
+ let deadline = performance.now() + maxBlockTime;
1143
+
1144
+ for (let i = 0; i < items.length; i++) {
1145
+ processItem(items[i]);
1146
+
1147
+ if (performance.now() >= deadline) {
1148
+ // Yield to main thread for pending interactions
1149
+ await scheduler.yield?.() ?? new Promise(r => setTimeout(r, 0));
1150
+ deadline = performance.now() + maxBlockTime;
1151
+ }
1152
+ }
1153
+ }
1154
+
1155
+ // Usage
1156
+ await processItems(
1157
+ thousandProducts,
1158
+ (product) => renderProductCard(product),
1159
+ 50 // yield every 50ms to stay under long-task threshold
1160
+ );
1161
+ ```
1162
+
1163
+ ### 5. Responsive Image Component (React)
1164
+
1165
+ ```jsx
1166
+ // components/OptimizedImage.jsx
1167
+ function OptimizedImage({
1168
+ src,
1169
+ alt,
1170
+ width,
1171
+ height,
1172
+ isLCP = false,
1173
+ sizes = '100vw',
1174
+ }) {
1175
+ // Generate srcset for multiple widths
1176
+ const widths = [400, 800, 1200, 1600];
1177
+ const avifSrcSet = widths.map(w => `${src}?format=avif&w=${w} ${w}w`).join(', ');
1178
+ const webpSrcSet = widths.map(w => `${src}?format=webp&w=${w} ${w}w`).join(', ');
1179
+
1180
+ return (
1181
+ <picture>
1182
+ <source srcSet={avifSrcSet} sizes={sizes} type="image/avif" />
1183
+ <source srcSet={webpSrcSet} sizes={sizes} type="image/webp" />
1184
+ <img
1185
+ src={`${src}?format=webp&w=800`}
1186
+ alt={alt}
1187
+ width={width}
1188
+ height={height}
1189
+ sizes={sizes}
1190
+ loading={isLCP ? 'eager' : 'lazy'}
1191
+ decoding={isLCP ? 'sync' : 'async'}
1192
+ fetchPriority={isLCP ? 'high' : 'auto'}
1193
+ style={{ width: '100%', height: 'auto', aspectRatio: `${width}/${height}` }}
1194
+ />
1195
+ </picture>
1196
+ );
1197
+ }
1198
+
1199
+ // Usage
1200
+ <OptimizedImage
1201
+ src="/api/images/hero"
1202
+ alt="Product showcase"
1203
+ width={1200}
1204
+ height={600}
1205
+ isLCP={true}
1206
+ sizes="(max-width: 768px) 100vw, 1200px"
1207
+ />
1208
+ ```
1209
+
1210
+ ### 6. Third-Party Script Loading Strategy
1211
+
1212
+ ```html
1213
+ <!-- Priority 1: Critical (inline in <head>) -->
1214
+ <!-- Only truly critical CSS and no JS in head -->
1215
+
1216
+ <!-- Priority 2: High (async, in <head>) — needed for page functionality -->
1217
+ <script async src="/js/app.bundle.js"></script>
1218
+
1219
+ <!-- Priority 3: Medium (defer) — needed but not for initial render -->
1220
+ <script defer src="https://www.googletagmanager.com/gtag/js?id=GA_ID"></script>
1221
+
1222
+ <!-- Priority 4: Low — load after page is interactive -->
1223
+ <script>
1224
+ // Load non-critical third-party scripts after page load
1225
+ window.addEventListener('load', () => {
1226
+ setTimeout(() => {
1227
+ // Chat widget
1228
+ const chat = document.createElement('script');
1229
+ chat.src = 'https://widget.chat-service.com/loader.js';
1230
+ chat.async = true;
1231
+ document.body.appendChild(chat);
1232
+
1233
+ // Social sharing buttons
1234
+ const social = document.createElement('script');
1235
+ social.src = 'https://platform.twitter.com/widgets.js';
1236
+ social.async = true;
1237
+ document.body.appendChild(social);
1238
+ }, 3000); // 3 second delay after load
1239
+ });
1240
+ </script>
1241
+ ```
1242
+
1243
+ ### 7. Next.js App Router Configuration for Optimal CWV
1244
+
1245
+ ```tsx
1246
+ // app/layout.tsx — Root layout with CWV optimizations
1247
+ import { Inter } from 'next/font/google';
1248
+
1249
+ // Next.js automatically optimizes Google Fonts:
1250
+ // - Self-hosts the font files (no external request)
1251
+ // - Generates size-adjust CSS for zero-CLS font loading
1252
+ const inter = Inter({
1253
+ subsets: ['latin'],
1254
+ display: 'swap', // Show text immediately
1255
+ preload: true, // Preload font files
1256
+ fallback: ['system-ui'], // Fallback while loading
1257
+ });
1258
+
1259
+ export default function RootLayout({ children }) {
1260
+ return (
1261
+ <html lang="en" className={inter.className}>
1262
+ <head>
1263
+ {/* Preconnect to critical third-party origins */}
1264
+ <link rel="preconnect" href="https://cdn.yoursite.com" />
1265
+ <link rel="dns-prefetch" href="https://analytics.yoursite.com" />
1266
+ </head>
1267
+ <body>{children}</body>
1268
+ </html>
1269
+ );
1270
+ }
1271
+
1272
+ // app/page.tsx — Page with optimized LCP
1273
+ import Image from 'next/image';
1274
+
1275
+ export default function HomePage() {
1276
+ return (
1277
+ <main>
1278
+ {/* Next.js Image component handles:
1279
+ - Automatic WebP/AVIF format negotiation
1280
+ - Responsive srcset generation
1281
+ - Lazy loading (except when priority=true)
1282
+ - Width/height for CLS prevention */}
1283
+ <Image
1284
+ src="/hero.jpg"
1285
+ alt="Hero"
1286
+ width={1200}
1287
+ height={600}
1288
+ priority // Sets fetchpriority="high" + no lazy loading
1289
+ sizes="100vw"
1290
+ quality={80}
1291
+ />
1292
+ </main>
1293
+ );
1294
+ }
1295
+ ```
1296
+
1297
+ ### 8. Layout Shift Debugging Overlay
1298
+
1299
+ ```javascript
1300
+ // Debug tool: Visualize layout shifts in real-time during development
1301
+ if (process.env.NODE_ENV === 'development') {
1302
+ const observer = new PerformanceObserver((list) => {
1303
+ for (const entry of list.getEntries()) {
1304
+ if (!entry.hadRecentInput) { // Only unexpected shifts
1305
+ for (const source of entry.sources || []) {
1306
+ const el = source.node;
1307
+ if (el) {
1308
+ // Highlight shifting element with red border
1309
+ el.style.outline = '3px solid red';
1310
+ el.setAttribute('data-cls-value', entry.value.toFixed(4));
1311
+ console.warn('Layout shift detected:', {
1312
+ element: el,
1313
+ value: entry.value.toFixed(4),
1314
+ previousRect: source.previousRect,
1315
+ currentRect: source.currentRect,
1316
+ });
1317
+ // Remove highlight after 2 seconds
1318
+ setTimeout(() => { el.style.outline = ''; }, 2000);
1319
+ }
1320
+ }
1321
+ }
1322
+ }
1323
+ });
1324
+ observer.observe({ type: 'layout-shift', buffered: true });
1325
+ }
1326
+ ```
1327
+
1328
+ ---
1329
+
1330
+ ## Quick Reference
1331
+
1332
+ ### Metric Thresholds
1333
+
1334
+ | Metric | Good | Acceptable | Needs Work | Percentile Used |
1335
+ |--------|------|------------|------------|----------------|
1336
+ | **LCP** | < 2.5s | 2.5s - 4.0s | > 4.0s | 75th (p75) |
1337
+ | **INP** | < 200ms | 200ms - 500ms | > 500ms | 75th (p75) |
1338
+ | **CLS** | < 0.1 | 0.1 - 0.25 | > 0.25 | 75th (p75) |
1339
+
1340
+ ### Top 3 Fixes Per Metric
1341
+
1342
+ | Metric | Fix #1 | Fix #2 | Fix #3 |
1343
+ |--------|--------|--------|--------|
1344
+ | **LCP** | Add `fetchpriority="high"` to LCP image (~700ms gain) | Use WebP/AVIF images (50-70% size reduction) | Inline critical CSS (300-800ms gain on 3G) |
1345
+ | **INP** | Break long tasks with `scheduler.yield()` | Move computation to Web Workers | Use `content-visibility: auto` for off-screen DOM |
1346
+ | **CLS** | Add `width`/`height` to all images (fixes ~60% of CLS) | Use `font-display: optional` or `size-adjust` | Reserve space for ads with `min-height` |
1347
+
1348
+ ### Measurement Cheat Sheet
1349
+
1350
+ | Need | Tool | Command / URL |
1351
+ |------|------|--------------|
1352
+ | Quick check | PageSpeed Insights | pagespeed.web.dev |
1353
+ | Lab audit | Lighthouse CLI | `npx lighthouse https://url --output html` |
1354
+ | Field data (per-URL) | CrUX API | `POST chromeuxreport.googleapis.com/v1/records:queryRecord` |
1355
+ | Field data (bulk) | BigQuery | `SELECT * FROM chrome-ux-report.all.YYYYMM` |
1356
+ | CI/CD gate | Lighthouse CI | `npx @lhci/cli autorun --config=lighthouserc.json` |
1357
+ | Real-time RUM | web-vitals library | `npm install web-vitals` |
1358
+ | Debug shifts | DevTools | Performance tab > "Layout Shifts" track |
1359
+ | Debug INP | DevTools | Performance tab > "Interactions" track |
1360
+
1361
+ ### Resource Size Budgets
1362
+
1363
+ | Resource Type | Recommended Budget | Why |
1364
+ |--------------|-------------------|-----|
1365
+ | Total page weight | < 1MB (mobile), < 2MB (desktop) | 1MB on 3G takes ~5s to download |
1366
+ | JavaScript (total) | < 300KB compressed | JS is byte-for-byte the most expensive resource |
1367
+ | CSS (total) | < 100KB compressed | Render-blocking; affects LCP directly |
1368
+ | LCP image | < 200KB | Must load within the 2.5s budget |
1369
+ | Fonts | < 100KB total | Each font file adds a blocking request |
1370
+ | Third-party scripts | < 10 scripts, < 200KB total | Each script competes for main thread |
1371
+
1372
+ ---
1373
+
1374
+ *Researched: 2026-03-08 | Sources: [web.dev/articles/lcp](https://web.dev/articles/lcp), [web.dev/articles/inp](https://web.dev/articles/inp), [web.dev/articles/cls](https://web.dev/articles/cls), [web.dev/articles/optimize-lcp](https://web.dev/articles/optimize-lcp), [web.dev/case-studies/vodafone](https://web.dev/case-studies/vodafone), [web.dev/case-studies/vitals-business-impact](https://web.dev/case-studies/vitals-business-impact), [web.dev/articles/fetch-priority](https://web.dev/articles/fetch-priority), [web.dev/articles/optimize-long-tasks](https://web.dev/articles/optimize-long-tasks), [developer.chrome.com/docs/crux](https://developer.chrome.com/docs/crux/guides/crux-api), [github.com/GoogleChrome/web-vitals](https://github.com/GoogleChrome/web-vitals), [addyosmani.com/blog/fetch-priority](https://addyosmani.com/blog/fetch-priority/), [developer.mozilla.org/en-US/blog/fix-image-lcp](https://developer.mozilla.org/en-US/blog/fix-image-lcp/), [nitropack.io/blog/interaction-to-next-paint-inp](https://nitropack.io/blog/interaction-to-next-paint-inp/), [developer.chrome.com/blog/use-scheduler-yield](https://developer.chrome.com/blog/use-scheduler-yield), [HTTP Archive 2025 Web Almanac](https://almanac.httparchive.org)*