Website Performance - listenrightmeow/signal-driven-development GitHub Wiki
The SDD website targets sub-second First Contentful Paint (FCP) on 4G connections. Performance is a core requirement, not an optimization pass.
| Metric | Target |
|---|---|
| First Contentful Paint | < 1.2s |
| Largest Contentful Paint | < 1.2s |
| First Input Delay | < 50ms |
| Cumulative Layout Shift | < 0.05 |
| Lighthouse Performance | >= 95 |
| Lighthouse Accessibility | >= 95 |
| Lighthouse Best Practices | >= 95 |
| Lighthouse SEO | >= 95 |
| Budget | Limit | Contents |
|---|---|---|
| Critical path | < 30KB | Fully styled above-the-fold HTML + inlined CSS |
| Total | < 150KB | All deferred CSS, interactive JS, font files |
Bundle budgets are enforced in CI via scripts/check-bundle-budget.mjs.
All above-the-fold CSS is inlined in <head>. This includes every style needed to render the hero section pixel-complete: custom properties, typography, colors, backgrounds, convergence visualization, stats bar, nav, badges, buttons.
src/styles/
├── critical.css # 496 lines — inlined above-the-fold
├── tokens.css # 88 lines — design system tokens (part of critical)
└── walkthrough.css # 345 lines — deferred below-the-fold
Zero render-blocking <link rel="stylesheet"> tags in <head>. The inlined CSS is the only CSS before first paint.
Below-the-fold CSS loads via preload pattern:
<link rel="preload" href="walkthrough.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="walkthrough.css"></noscript>Strategy: Explicit FOUT (Flash of Unstyled Text). System fonts render immediately. Web fonts swap in when available.
Fonts:
- Outfit (body) -- weights 300, 400, 500, 600, 700 as separate files. Only 400 and 600 in critical path.
- JetBrains Mono (code) -- weights 400 and 500 only.
Rules:
-
font-display: swapon all@font-facedeclarations - Self-hosted, subset to Latin + Latin Extended (no Google Fonts CDN)
- Fonts are not preloaded -- they load via CSS cascade after deferred stylesheet applies
-
size-adjust,ascent-override,descent-overrideto minimize CLS on swap
System fallback stacks:
- Body:
'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif - Code:
'JetBrains Mono', 'SF Mono', 'Cascadia Code', 'Consolas', monospace
Every image uses <picture> elements with:
- Format negotiation -- AVIF > WebP > JPEG/PNG fallback
-
DPI-specific
srcset-- 1x, 2x, 3x variants - LQIP (Low-Quality Image Placeholder) -- 32px wide, JPEG quality 20, ~200-500 bytes
Progressive loading flow:
- LQIP renders immediately with
filter: blur(10px) - Full-quality image loads in background
- When loaded, opacity fade (200ms) replaces placeholder
- Blur filter removed
Build pipeline: Source images stored as high-res originals. Astro/sharp generates all variants at build time. No runtime image processing.
- Astro island architecture -- JS only ships for interactive components
- Zero JS by default -- static pages have no JavaScript
- Deferred loading -- interactive islands hydrate after first paint
- JS-required components: Walkthrough, LineageTimeline only