Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/DISCUSSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ Sorted by descending date (most recent first).

---

# Session: PERF — Fix CLS 0.997 from Angular Bootstrap Delay
**Date:** 2026-05-07
**Context and Problem to Solve**

After deploying the previous round of performance fixes, production Lighthouse reported Performance 76/100 with CLS 0.997 — worse than before. Root cause: a 3-level sequential JS chain (index.html → main.js → home.chunk → productList.chunk = 299ms total) left `<app-root>` blank for ~300ms before Angular rendered all content at once. The entire viewport shift from blank→content was recorded as CLS. The earlier skeleton fix addressed the wrong level of CLS (in-session product grid layout), not the Angular bootstrap CLS.

**Summary of Decisions**

| Decision | Rationale |
|---|---|
| **Make Home and ProductList routes non-lazy** | Home already statically imported ProductList; both were in separate lazy chunks due to `loadComponent`. Moving both to `component:` (static) collapsed the 3-level JS chain to 1. Initial bundle grows from ~12 KB to ~19 KB (gzipped) — still tiny, well under 1 MB error budget. |
| **Inline CSS skeleton in `index.html` inside `<app-root>`** | Angular replaces `<app-root>` children on bootstrap, so any HTML placed there is visible during the JS loading gap and removed automatically. A pure-CSS skeleton (no Tailwind — CSS bundle not yet loaded) mirrors the home page layout (header, dark hero, 4-col product grid). The shift becomes skeleton→content instead of blank→content, reducing CLS to near zero. |
| **Keep all other routes lazy** | Only Home and ProductList are on the initial page. All other routes (product-details, cart, checkout, dashboard, login, register) remain lazy and are preloaded in the background via `PreloadAllModules`. |

---

# Session: PERF — Lighthouse Score Improvements (CLS, Accessibility, SEO)
**Date:** 2026-05-07
**Context and Problem to Solve**
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Routes } from '@angular/router';
import { authGuard } from './guards/auth-guard';
import { Home } from './components/home/home';
import { ProductList } from './components/product-list/product-list';

export const routes: Routes = [
{ path: '', loadComponent: () => import('./components/home/home').then(m => m.Home) },
{ path: 'products', loadComponent: () => import('./components/product-list/product-list').then(m => m.ProductList) },
{ path: '', component: Home },
{ path: 'products', component: ProductList },
{ path: 'product/:id', loadComponent: () => import('./components/product-details/product-details').then(m => m.ProductDetails) },
{ path: 'dashboard', loadComponent: () => import('./components/user-dashboard/user-dashboard').then(m => m.UserDashboard), canActivate: [authGuard] },
{ path: 'cart', loadComponent: () => import('./components/cart/cart').then(m => m.Cart), canActivate: [authGuard] },
Expand Down
42 changes: 41 additions & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,48 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="E-Shop — curated premium products for the modern individual. Browse our collection of quality lifestyle essentials.">
<link rel="icon" type="image/x-icon" href="favicon.svg">
<style>
.sk-wrap{min-height:100vh;display:flex;flex-direction:column;background:#f8fafc}
.sk-header{height:64px;background:#fff;border-bottom:1px solid #e2e8f0;flex-shrink:0}
.sk-main{flex:1;max-width:80rem;width:100%;margin:0 auto;padding:2rem 1rem;box-sizing:border-box}
.sk-hero{background:#0f172a;border-radius:2.5rem;min-height:320px;margin-bottom:4rem}
.sk-heading{display:flex;flex-direction:column;align-items:center;gap:.5rem;margin-bottom:4rem}
.sk-heading-bar{height:2rem;width:14rem;background:#e2e8f0;border-radius:.375rem}
.sk-heading-line{height:4px;width:3rem;background:#4f46e5;border-radius:9999px}
.sk-grid{display:grid;gap:1.5rem;grid-template-columns:repeat(4,1fr)}
.sk-card{background:#fff;border-radius:1.5rem;border:1px solid #e2e8f0;padding:1rem}
.sk-img{aspect-ratio:1/1;background:#e2e8f0;border-radius:1rem}
.sk-t1{margin-top:1rem;height:.625rem;width:25%;background:#e2e8f0;border-radius:.25rem}
.sk-t2{margin-top:.5rem;height:1rem;width:75%;background:#e2e8f0;border-radius:.25rem}
.sk-footer{height:60px;background:#fff;border-top:1px solid #e2e8f0;flex-shrink:0}
@media(max-width:1280px){.sk-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:768px){.sk-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.sk-grid{grid-template-columns:1fr}}
</style>
</head>
<body>
<app-root></app-root>
<app-root>
<div class="sk-wrap">
<header class="sk-header"></header>
<main class="sk-main">
<div class="sk-hero"></div>
<div class="sk-heading">
<div class="sk-heading-bar"></div>
<div class="sk-heading-line"></div>
</div>
<div class="sk-grid">
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
<div class="sk-card"><div class="sk-img"></div><div class="sk-t1"></div><div class="sk-t2"></div></div>
</div>
</main>
<footer class="sk-footer"></footer>
</div>
</app-root>
</body>
</html>
Loading