---
trigger: always_on
related_: [reexamine_resilience, reexamine_polish, reexamine_process]
---

# Re-examine: Performance

## Progressive disclosure
- Don't render 500 items at once — show 8-12 per group with "Show more"
- Collapsed sections: don't render hidden children, use deferred rendering on expand
- Store raw data in `window._data` or `data-items` attribute, render on demand via `insertAdjacentHTML`

## Smart refresh
- Polling/interval refreshes: hash the data before re-rendering
- `simpleHash(JSON.stringify(data))` compared to `_lastDataHash` — skip if unchanged
- Avoids DOM thrashing on 5s intervals when nothing changed
- Use `requestAnimationFrame` for batched DOM updates

## Lazy loading
- Images below the fold: `loading="lazy"` attribute
- Heavy components: dynamic import / code splitting
- IntersectionObserver for triggering loads when elements enter viewport
- Placeholder (skeleton or blur) until loaded

## String concatenation
- Build HTML with array push + join: `const h = []; h.push('<div>'); return h.join('')`
- Avoids O(n^2) string concatenation in loops
- Template literals are fine for small chunks; arrays for large dynamic HTML

## CSS performance
- Use CSS custom properties (`:root { --gap: 1rem }`) instead of repeated values
- Avoid `*` selectors in complex rules
- `will-change: transform` only on elements that actually animate
- `contain: content` on independent card/section containers

## Measurement
- Profile before optimizing — don't guess where the bottleneck is
- Check for layout thrashing: read → write → read → write cycles
- `getComputedStyle()` calls force synchronous layout — cache results
