Cache strategy: cache headers, content-hash cache-busting for CSS/JS/images, image dimension and filename rules, image lazy-loading.
Part of the requirements index. Section IDs (02-§N.M) are stable and cited from code; they do not encode the file path.
The site must use browser-native loading hints to improve perceived performance and reduce layout shift. No new client-side JavaScript is required.
content-img) produced by the build must include
loading="lazy", except for images in the first content section (which is
above the fold on mobile and may contain the LCP element). loading="lazy" — they are
above the fold on mobile and lazy-loading them delays the Largest Contentful
Paint. hero-img) must NOT have loading="lazy" — it is
above the fold and must load immediately. <head> of the homepage must include a <link rel="preload" as="image">
element whose href matches the hero image source. The path must not be
hardcoded — it is derived from the hero image extracted at build time. <img> tag must include fetchpriority="high". nav.js script tag must include the defer attribute on all pages. This
breaks the critical request chain (HTML → CSS → JS) identified by Lighthouse,
allowing the browser to discover and preload the script during HTML
parsing. The build-time markdown converter (convertMarkdown() and inlineHtml() in
render-index.js) supports only a limited subset of markdown. Content authors
write standard markdown (including tables) that the converter silently mangles.
Replace the hand-rolled converter with the marked library.
marked as the markdown-to-HTML converter. marked must be a production dependency (build-time only; no client-side
JS change). headingOffset parameter must shift all heading levels
(e.g. ## Foo with offset 1 becomes <h3>), capped at h6. collapsible is true, each ##-level section
(after offset) must be wrapped in a
<details class="accordion"><summary>…</summary>…</details>
element. Content before the first ## must not be wrapped. class="content-img" and
loading="lazy". YYYY-MM-DD string using Intl.DateTimeFormat.formatToParts, not
toLocaleDateString. .camps-row div must anchor on </ul> and <script>
explicitly. src extraction regex must not assume attribute
order. <p> removal regex must tolerate optional
whitespace inside the <p> element. Every <img> element in the rendered HTML must have explicit width and
height attributes. This reserves layout space before the image loads,
preventing Cumulative Layout Shift (CLS).
Images whose display size is constant (not responsive) must have hardcoded
width and height attributes matching their CSS display dimensions:
.testimonial-img): width="60" height="60". .hero-social-link img): width="32" height="32". .rss-icon): dimensions matching the image’s natural aspect
ratio at the CSS display height. .camp-fb-logo): dimensions matching the
image’s natural aspect ratio at the CSS display size. .hero-img) must have width and height attributes
reflecting its natural pixel dimensions. content-img) must
have width and height attributes set to the source image’s natural
pixel dimensions, read at build time. local.yaml must have width
and height attributes set to their natural pixel dimensions, read at
build time. width and height attributes must not change the rendered
appearance of any image. Existing CSS rules control display
size. The site must serve static assets with appropriate Cache-Control headers
to reduce repeat-visit load times. Cache rules are delivered via an Apache
.htaccess file in the site root.
.webp, .png, .jpg, .ico): Cache-Control: max-age=31536000
(1 year). Cache-Control: max-age=604800 (1 week). Cache-Control: no-cache (always revalidate). .htaccess file must live at source/static/.htaccess in the source
tree. source/static/.htaccess to public/.htaccess
during the build step. fs.copyFileSync() call — not the
copyFlattened() helper which operates on source/assets/. .htaccess is for the static site root only. The existing
api/.htaccess (PHP routing) must not be modified. All image files in source/content/images/ must have descriptive,
human-readable filenames that follow a consistent naming convention. This
makes markdown editing easier for non-technical contributors and aligns
filenames with their natural alt-text descriptions.
source/content/*.md) must
point to the renamed file. image_path in source/data/local.yaml must point to the
renamed file. render-index.js,
render.js) must point to the renamed files. [alt="..."] must be updated if the corresponding
alt-text changes. HTML files are served with Cache-Control: no-cache and always revalidate,
but CSS is cached for up to one week (02-§67.2). When a deploy changes CSS
selectors or styles, browsers may serve stale CSS against fresh HTML,
causing visual regressions. The build must append a content-based hash to
the CSS URL so that any CSS change forces a cache miss.
public/style.css and compute a short content hash. href="style.css" in all HTML files
under public/ with href="style.css?v=<hash>". build.js. style.css presence must continue to
pass. HTML files are served with Cache-Control: no-cache and always revalidate,
but JS is cached for up to one week (02-§67.2). When a deploy changes
client-side JavaScript, browsers may serve stale scripts against fresh HTML,
causing broken behaviour. The build must append a content-based hash to JS
URLs so that any JS change forces a cache miss while unchanged files continue
to be served from cache.
The build appends a deterministic 8-character MD5-based content hash to every
<script src="…"> reference in public/ HTML so JS URLs become
src="<file>.js?v=<hash>", with identical JS content always producing the
same hash. See 03-architecture/ci-and-deploy.md §27 "Asset Cache-Busting" for the
canonical mechanism (shared with CSS and image cache-busting).
build.js. Images are cached for up to one year (02-§67.1). When an image is replaced with new content but the same filename, browsers may serve the old version indefinitely. The build must append a content-based hash to image URLs in HTML so that changed images force a cache miss while unchanged images continue to be served from cache.
The build appends a deterministic 8-character MD5-based content hash to every
<img src="…"> reference in public/ HTML so image URLs become
src="<file>.<ext>?v=<hash>" (where ext is webp, png, jpg, jpeg,
or ico), with identical image content always producing the same hash. See
03-architecture/ci-and-deploy.md §27 "Asset Cache-Busting" for the canonical mechanism
(shared with CSS and JS cache-busting).
build.js. Section 78 covers src attributes in <img> tags. Images also appear in
href attributes (<link rel="preload">, <link rel="icon">,
<link rel="apple-touch-icon">) and in the PWA manifest
(app.webmanifest). These references must receive the same content-based
hash so that the browser treats them as identical URLs and avoids
redundant downloads.
href="<file>.<ext>" (where ext is webp, png, jpg, jpeg, or ico) in all
HTML files under public/ with
href="<file>.<ext>?v=<hash>". "src": "<file>.<ext>" (same extensions) in
app.webmanifest under public/ with
"src": "<file>.<ext>?v=<hash>". src cache-busting to ensure consistency. href must match the corresponding <img src> URL exactly
(including query string) so that the browser can match the preloaded
resource.